視頻圖像大小從標清,高清,升到超高清,編碼器標準從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);


沒有留言:
張貼留言