4412 移植x264並且YUV422轉x264
轉自http://blog.sina.com.cn/s/blog_af9acfc60101alxx.html
一、YUV422轉換規律
做視頻采集與處理,自然少不了要學會分析YUV數據。因為從采集的角度來說,一般的視頻采集芯片輸出的碼流一般都是YUV數據流的形式,而從視頻處理(例如H.264、MPEG視頻編解碼)的角度來說,也是在原始YUV碼流進行編碼和解析,所以,了解如何分析YUV數據流對於做視頻領域的人而言,至關重要。本文就是根據我的學習和了解,簡單地介紹如何分析YUV數據流。
YUV,分為三個分量,“Y”表示明亮度(Luminance或Luma),也就是灰度值;而“U”和“V” 表示的則是色度(Chrominance或Chroma),作用是描述影像色彩及飽和度,用於指定像素的顏色。
與我們熟知的RGB類似,YUV也是一種顏色編碼方法,主要用於電視系統以及模擬視頻領域,它將亮度信息(Y)與色彩信息(UV)分離,沒有UV信息一樣可以顯示完整的圖像,只不過是黑白的,這樣的設計很好地解決了彩色電視機與黑白電視的兼容問題。並且,YUV不像RGB那樣要求三個獨立的視頻信號同時傳輸,所以用YUV方式傳送占用極少的頻寬。
好了,言歸正傳,談談如何分析YUV碼流吧。YUV碼流有多種不同的格式,要分析YUV碼流,就必須搞清楚你面對的到底是哪一種格式,並且必須搞清楚這種格式的YUV采樣和分布情況。下面我將介紹幾種常用的YUV碼流格式,供大家參考。
YUV碼流的存儲格式其實與其采樣的方式密切相關,主流的采樣方式有三種,YUV4:4:4,YUV4:2:2,YUV4:2:0,關於其詳細原理,可以通過網上其它文章了解,這裏我想強調的是如何根據其采樣格式來從碼流中還原每個像素點的YUV值,因為只有正確地還原了每個像素點的YUV值,才能通過YUV與RGB的轉換公式提取出每個像素點的RGB值,然後顯示出來。
1.1 YUV格式
為了方便後面敘述,圖片的大小定 義為:w * h,寬高分別為w和h
YUV420格式
先Y,後V,中間是U。其中的Y是w * h,U和V是w/2 * (h/2)
如果w = 4,h = 2,則:
yyyy
yyyy
uu
vv
內存則是:yyyyyyyyuuvv
需要占用的內存:w * h * 3 / 2
采樣規律是:每個像素點都采樣Y,寄數行采樣1/2個U,不采樣V,偶數行采樣1/2個V,不采樣U
YUV422格式
本格式使用較為廣泛
每兩個點為一組,共占用4個字節
YUYVYUYV…
對於每一組YUYV,前面一個Y和本組中的UV組成第一個點,第二個Y和本組中的UV組成第二個點
所以,在內存中,寬高分別為w * 2、h。
如果w = 4,h = 2,則:
YUYVYUYV
YUYVYUYV
需要占用的內存:w * h * 2
UYUY422格式
本格式和YUYV422一樣,只是YUV的位置不一樣罷了
每組中YUV的排列順序為:UYUV
需要占用的內存:w * h * 2
YUV的采樣格式及每種格式中單像素所占內 存大小
YUV主要的采樣格式有YCbCr 4:2:0、YCbCr 4:2:2、YCbCr 4:1:1和 YCbCr 4:4:4。
采樣格式 單像素所占內存大小 存放的碼流
YCbCr 4:4:4 3byte Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3(4像素為例)
YCbCr 4:2:2 2byte Y0 U0 Y1 V1 Y2 U2 Y3 V3(4像素為例)
YCbCr 4:2:0 1.5byte Y0 U0 Y1 Y2 U2 Y3 Y5 V5 Y6 Y7 V7 Y8(8像素為例)
YCbCr 4:1:1 1.5byte Y0 U0 Y1 Y2 V2 Y3(4像素為例)
1.2 存儲方式
下面我用圖的形式給出常見的YUV碼流的存儲方式,並在存儲方式後面附有取樣每個像素點的YUV數據的方法,其中,Cb、Cr的含義等同於U、V。
(1) YUVY 格式 (屬於YUV422)
YUYV為YUV422采樣的存儲格式中的一種,相鄰的兩個Y共用其相鄰的兩個Cb、Cr,分析,對於像素點Y‘00、Y‘01 而言,其Cb、Cr的值均為 Cb00、Cr00,其他的像素點的YUV取值依次類推。
(2) UYVY 格式 (屬於YUV422)
UYVY格式也是YUV422采樣的存儲格式中的一種,只不過與YUYV不同的是UV的排列順序不一樣而已,還原其每個像素點的YUV值的方法與上面一樣。
(3) YUV422P(屬於YUV422)
YUV422P也屬於YUV422的一種,它是一種Plane模式,即打包模式,並不是將YUV數據交錯存儲,而是先存放所有的Y分量,然後存儲所有的U(Cb)分量,最後存儲所有的V(Cr)分量,如上圖所示。其每一個像素點的YUV值提取方法也是遵循YUV422格式的最基本提取方法,即兩個Y共用一個UV。比如,對於像素點Y‘00、Y‘01 而言,其Cb、Cr的值均為 Cb00、Cr00。
二、移植x264
首先下載對應的源文件http://ftp.videolan.org/pub/videolan/x264/snapshots/
我選擇了x264-snapshot-20181021-2245-stable.tar.bz2
tar jxvf x264-snapshot-20181021-2245-stable.tar.bz ./configure --host=arm-linux --prefix=/opt/wecam/ffmpeg --enable-shared --disable-asm
host:是要使用的平臺
prefix:是make install的目錄
enable-shared:是使能動態鏈接庫
disable-asm:是關閉匯編命令
然後需要修改config.mak文件
然後運行指令:
make
make install
然後在/opt/wecam/ffmpeg目錄下就有了對應的文件:
然後把libx264.so.155和pkgconfig目錄放到4412開發板的/lib目錄,再用指令創建軟鏈接
ln -s libx264.so.155 libx264.so
而頭文件x264.h是編譯程序時需要使用的頭文件
三、使用庫編寫YUV422轉x264應用
#include <stdio.h> #include <stdlib.h> #include "stdint.h" #include "include/x264.h" int main(int argc, char *argv[]) { int ret; int y_size; int i, j; if(argc != 3) { printf("usage: %s [source file] [dest file] \n", argv[0]); return -1; } //source file FILE *fp_src = fopen(argv[1], "rb"); FILE *fp_dst = fopen(argv[2], "wb"); //Encode 0 frame int frame_num = 50; int csp = X264_CSP_I422; //YUYV int width=640,height=480; //640*480 int iNal = 0; x264_nal_t *pNals = NULL; x264_t *pHandle = NULL; x264_picture_t *pPic_in = (x264_picture_t *)malloc(sizeof(x264_picture_t)); x264_picture_t *pPic_out = (x264_picture_t *)malloc(sizeof(x264_picture_t)); x264_param_t *pParam = (x264_param_t *)malloc(sizeof(x264_param_t)); if(fp_src == NULL || fp_dst == NULL) { printf("Error open files.\n"); return -1; } x264_param_default(pParam); pParam->i_width = width; pParam->i_height = height; pParam->i_csp = csp; x264_param_apply_profile(pParam, x264_profile_names[4]); pHandle = x264_encoder_open(pParam); x264_picture_init(pPic_out); x264_picture_alloc(pPic_in, csp, pParam->i_width, pParam->i_height); y_size = pParam->i_width * pParam->i_height; printf("w:%d h:%d\r\n",pParam->i_width,pParam->i_height); //detect frame number if(frame_num == 0) { fseek(fp_src, 0, SEEK_END); switch(csp) { case X264_CSP_I444: frame_num = ftell(fp_src)/(y_size*3); break; case X264_CSP_I420: frame_num = ftell(fp_src)/(y_size*3/2); break; case X264_CSP_I422: frame_num = ftell(fp_src)/(y_size*2); break; default: printf("Colorspace Not Support.\n"); return -1; } fseek(fp_src, 0, SEEK_SET); } printf("frame_num:%d y_size:%d\r\n",frame_num,y_size); //Loop to Encode for(i=0;i<frame_num;i++) { switch(csp) { case X264_CSP_I444: fread(pPic_in->img.plane[0], y_size, 1, fp_src); fread(pPic_in->img.plane[1], y_size, 1, fp_src); fread(pPic_in->img.plane[2], y_size, 1, fp_src); break; case X264_CSP_I420: fread(pPic_in->img.plane[0], y_size, 1, fp_src); fread(pPic_in->img.plane[1], y_size/4, 1, fp_src); fread(pPic_in->img.plane[2], y_size/4, 1, fp_src); break; case X264_CSP_I422: { int index = 0; int y_i = 0, u_i = 0, v_i = 0; for(index = 0; index < y_size*2; ) { fread(&pPic_in->img.plane[0][y_i++], 1, 1, fp_src); //Y index++; fread(&pPic_in->img.plane[1][u_i++], 1, 1, fp_src); //U index++; fread(&pPic_in->img.plane[0][y_i++], 1, 1, fp_src); //Y index++; fread(&pPic_in->img.plane[2][v_i++], 1, 1, fp_src); //V index++; } break; } default: printf("Colorspace Not Support.\n"); return -1; } pPic_in->i_pts = i; ret = x264_encoder_encode(pHandle, &pNals, &iNal, pPic_in, pPic_out); if(ret < 0) { printf("Error.\n"); return -1; } printf("Succeed encode frame: %5d\n", i); for(j=0;j<iNal;j++) { fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst); } } //flush encoder while(1) { ret = x264_encoder_encode(pHandle, &pNals, &iNal, NULL, pPic_out); if(ret == 0) break; printf("Flush 1 frame.\n"); for(j=0;j<iNal;j++) fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst); } x264_picture_clean(pPic_in); x264_encoder_close(pHandle); pHandle = NULL; free(pPic_in); free(pPic_out); free(pParam); fclose(fp_src); fclose(fp_dst); return 0; }
然後是Makefile文件
encode_h264: arm-none-linux-gnueabi-gcc encode_h264.c -o encode_h264 -L./lib/ -lx264 cp encode_h264 /home/topeet/linux/camera/x264_code clean: rm -f encode_h264
打完收工!
4412 移植x264並且YUV422轉x264