視頻圖像大小從標清,高清,升到超高清,編碼器標準從H.263,H.264升到H.265,包括目前很火的VR和AR,視頻應用對視頻處理和編解碼器的運算量要求越來越高,而GPU相比CPU更適合處理圖形圖像這樣的矩陣運算。
如何使用GPU的圖像處理能力呢,VAAPI是一個方案。
視頻加速應用程序接口 (Video Acceleration API, 縮寫為VA-API) 是一套類unix平台提供視頻硬件加速的開源庫和標準。
VAAPI免費開源,以MIT許可證發布。 MIT許可證源自麻省理工學院(Massachusetts Institute of Technology, MIT),又稱"X條款"(X License)或"X11條款"(X11 License) MIT內容與三條款BSD許可證(3-clause BSD license)內容頗為近似,但是賦予軟件被授權人更大的權利與更少的限制。有許多團體均採用MIT許可證。例如著名的ssh連接軟件PuTTY與X Windows System (X11)。 Expat, Mono開發平台庫,Ruby on Rails, Lua 5.0 onwards等等也都採用MIT授權條款。
應用層用戶可以通過VAAPI接口直接訪問加速設備驅動,使用硬件(如GPU)來加速視頻處理的應用,如視頻編碼,視頻解碼,視頻融合疊加,視頻顯示。
VAAPI是Intel發起的,之初是為Intel自己的GMA(Graphics Media Accelerator)系列GPU加速。 Intel也想用VAAPI替代已有的XvMC標準。但VAAPI發展到現在它不僅是針對Intel的視頻加速設備,其他廠商也能完全免費的通過VAAPI這個開放接口實現硬件圖形加速。
VAAPI是偏向類unix平台的,對應windows平台的技術是 Microsoft Windows DirectX Video Acceleration(DxVA)。 VAAPI可以應用在Linux, FreeBSD, Solaris和Android等類unix平台。
VAAPI當初的設計偏向於硬件加速解碼器的視頻算法如VLD, IDCT, motion compensation, deblocking。現在VAAPI不僅支持解碼,還支持主流的編碼,並且支持圖像後處理和圖像增強。隨著intel的核芯顯卡越來越強,VAAPI的功能也隨之強大。
從架構圖可知VAAPI通過GPU設備驅動實現四個模塊顯存控制,編碼,解碼,圖像處理,其中編解碼器模塊可分為同一類。
顯存控制:通過用戶態的DRM訪問內核顯存。
編解碼器:用戶態把編解碼器的指令傳給內核態驅動,內核態驅動再操作實體編解碼硬件。
圖像處理:先調用顯示接口,通過顯示接口直接調用硬件或通過調用內核Gfx模式操作硬件。
從VAAPI的架構可看出它是依賴於配套的DRM,設備驅動,內核模塊和實體硬件。以intel為例,使用VAAPI最好使用intel發布的一組套件。
/*打开VA GPU设备,并初始化*/ va_dpy = va_open_display(); va_status = vaInitialize(va_dpy, &major_ver, &minor_ver); /*查询硬件支持的H.264 profile等级*/ h264_profile = profile_list[i]; vaQueryConfigEntrypoints(va_dpy, h264_profile, entrypoints, &num_entrypoints); for (slice_entrypoint = 0; slice_entrypoint < num_entrypoints; slice_entrypoint++) { if (entrypoints[slice_entrypoint] == VAEntrypointEncSlice) { support_encode = 1; break; } } /*查询H.264 属性接口,是否支持VBR,CBR等。是否支持多slice等*/ va_status = vaGetConfigAttributes(va_dpy, h264_profile, VAEntrypointEncSlice, &attrib[0], VAConfigAttribTypeMax); CHECK_VASTATUS(va_status, "vaGetConfigAttributes"); /*在指定设备上创建H.264配置*/ va_status = vaCreateConfig(va_dpy, h264_profile, VAEntrypointEncSlice, &config_attrib[0], config_attrib_num, &config_id); CHECK_VASTATUS(va_status, "vaCreateConfig"); /* 创建源设备表面 */ /* create source surfaces */ va_status = vaCreateSurfaces(va_dpy, VA_RT_FORMAT_YUV420, frame_width_mbaligned, frame_height_mbaligned, &src_surface[0], SURFACE_NUM, NULL, 0); CHECK_VASTATUS(va_status, "vaCreateSurfaces"); /* 创建参考设备表面 */ /* create reference surfaces */ va_status = vaCreateSurfaces( va_dpy, VA_RT_FORMAT_YUV420, frame_width_mbaligned, frame_height_mbaligned, &ref_surface[0], SURFACE_NUM, NULL, 0 ); CHECK_VASTATUS(va_status, "vaCreateSurfaces"); tmp_surfaceid = calloc(2 * SURFACE_NUM, sizeof(VASurfaceID)); memcpy(tmp_surfaceid, src_surface, SURFACE_NUM * sizeof(VASurfaceID)); memcpy(tmp_surfaceid + SURFACE_NUM, ref_surface, SURFACE_NUM * sizeof(VASurfaceID)); /*创建编码器管道,因为是异构系统用管道的方式*/ /* Create a context for this encode pipe */ va_status = vaCreateContext(va_dpy, config_id, frame_width_mbaligned, frame_height_mbaligned, VA_PROGRESSIVE, tmp_surfaceid, 2 * SURFACE_NUM, &context_id); CHECK_VASTATUS(va_status, "vaCreateContext"); free(tmp_surfaceid); /*为编码申请设备上的全局指令空间 cmdbuf,控制和数据空间要分开申请,数据空间可实时申请*/ codedbuf_size = (frame_width_mbaligned * frame_height_mbaligned * 400) / (16*16); for (i = 0; i < SURFACE_NUM; i++) { va_status = vaCreateBuffer(va_dpy,context_id,VAEncCodedBufferType, codedbuf_size, 1, NULL, &coded_buf[i]); CHECK_VASTATUS(va_status,"vaCreateBuffer"); } /*装载编码数据到显存*/ upload_surface_yuv(va_dpy, surface_id, srcyuv_fourcc, frame_width, frame_height, src_Y, src_U, src_V); va_status = vaCreateBuffer(va_dpy,context_id,VAEncSliceParameterBufferType, sizeof(slice_param),1,&slice_param,&slice_param_buf); CHECK_VASTATUS(va_status,"vaCreateBuffer"); /*准备编码数据给目标表面*/ va_status = vaBeginPicture(va_dpy, context_id, src_surface[current_slot]); CHECK_VASTATUS(va_status,"vaBeginPicture"); /*推送数据给GPU,编码,完成后buffer自动释放销毁*/ va_status = vaRenderPicture(va_dpy,context_id, &slice_param_buf, 1); CHECK_VASTATUS(va_status,"vaRenderPicture"); /*让设备执行*/ va_status = vaEndPicture(va_dpy,context_id); CHECK_VASTATUS(va_status,"vaEndPicture"); /*释放资源*/ vaDestroySurfaces(va_dpy,&src_surface[0],SURFACE_NUM); vaDestroySurfaces(va_dpy,&ref_surface[0],SURFACE_NUM); for (i = 0; i < SURFACE_NUM; i++) vaDestroyBuffer(va_dpy,coded_buf[i]); vaDestroyContext(va_dpy,context_id); vaDestroyConfig(va_dpy,config_id);
沒有留言:
張貼留言