有段時間沒有寫博客了,這兩天寫起博客來竟然感覺有些興奮,仿佛找回了原來的感覺。
前一陣子在梳理以前文章的時候,發現自己雖然總結了各種視音頻應用程序,卻還缺少一個適合無視音頻背景人員學習的“最基礎”的程序。因此抽時間將以前寫過的代碼整理成了一個小項目。
這個小項目里面包含了一系列簡單的函數,可以對RGB/YUV視頻像素數據、PCM音頻采樣數據、H.264視頻碼流、AAC音頻碼流、FLV封裝格式數據、UDP/RTP協議數據進行簡單處理。這個項目的一大特點就是沒有使用任何的第三方類庫,完全借助于C語言的基本函數實現了功能。
通過對這些代碼的學習,可以讓初學者迅速掌握視音頻數據的基本格式。有關上述幾種格式的介紹可以參考文章《視音頻編解碼技術零基礎學習方法》。
從這篇文章開始打算寫6篇文章分別記錄上述6種不同類型的視音頻數據的處理方法。本文首先記錄第一部分即RGB/YUV視頻像素數據的處理方法。視頻像素數據在視頻播放器的解碼流程中的位置如下圖所示。
本文分別介紹如下幾個RGB/YUV視頻像素數據處理函數:
分離YUV420P像素數據中的Y、U、V分量
分離YUV444P像素數據中的Y、U、V分量
將YUV420P像素數據去掉顏色(變成灰度圖)
將YUV420P像素數據的亮度減半
將YUV420P像素數據的周圍加上邊框
生成YUV420P格式的灰階測試圖
計算兩個YUV420P像素數據的PSNR
分離RGB24像素數據中的R、G、B分量
將RGB24格式像素數據封裝為BMP圖像
將RGB24格式像素數據轉換為YUV420P格式像素數據
生成RGB24格式的彩條測試圖
本文中的RGB/YUV文件需要使用RGB/YUV播放器才能查看。YUV播放器種類比較多,例如YUV Player Deluxe,或者開源播放器(參考文章《修改了一個YUV/RGB播放器》)等。
函數列表
(1) 分離YUV420P像素數據中的Y、U、V分量
本程序中的函數可以將YUV420P數據中的Y、U、V三個分量分離開來并保存成三個文件。函數的代碼如下所示。
/**
- Split Y, U, V planes in YUV420P file.
- @param url Location of Input YUV file.
- @param w Width of Input YUV file.
- @param h Height of Input YUV file.
- @param num Number of frames to process.
*/
int simplest_yuv420_split(char *url, int w, int h,int num){
FILE *fp=fopen(url,"rb+");
FILE *fp1=fopen("output_420_y.y","wb+");
FILE *fp2=fopen("output_420_u.y","wb+");
FILE *fp3=fopen("output_420_v.y","wb+");
unsigned char pic=(unsigned char )malloc(wh3/2);
for(int i=0;i<num;i++){
fread(pic,1,wh3/2,fp);
//Y
fwrite(pic,1,w*h,fp1);
//U
fwrite(pic+wh,1,wh/4,fp2);
//V
fwrite(pic+wh5/4,1,w*h/4,fp3);
}
free(pic);
fclose(fp);
fclose(fp1);
fclose(fp2);
fclose(fp3);
return 0;
}
調用上面函數的方法如下所示。
- simplest_yuv420_split("lena_256x256_yuv420p.yuv",256,256,1);
從代碼可以看出,如果視頻幀的寬和高分別為w和h,那么一幀YUV420P像素數據一共占用wh3/2 Byte的數據。其中前wh Byte存儲Y,接著的wh1/4 Byte存儲U,最后wh*1/4 Byte存儲V。上述調用函數的代碼運行后,將會把一張分辨率為256x256的名稱為lena_256x256_yuv420p.yuv的YUV420P格式的像素數據文件分離成為三個文件:
output_420_y.y:純Y數據,分辨率為256x256。
output_420_u.y:純U數據,分辨率為128x128。
output_420_v.y:純V數據,分辨率為128x128。
注:本文中像素的采樣位數一律為8bit。由于1Byte=8bit,所以一個像素的一個分量的采樣值占用1Byte。
程序輸入的原圖如下所示。
程序輸出的三個文件的截圖如下圖所示。在這里需要注意輸出的U、V分量在YUV播放器中也是當做Y分量進行播放的。
(2)分離YUV444P像素數據中的Y、U、V分量
本程序中的函數可以將YUV444P數據中的Y、U、V三個分量分離開來并保存成三個文件。函數的代碼如下所示。
/**
- Split Y, U, V planes in YUV444P file.
- @param url Location of YUV file.
- @param w Width of Input YUV file.
- @param h Height of Input YUV file.
- @param num Number of frames to process.
*/
int simplest_yuv444_split(char *url, int w, int h,int num){
FILE *fp=fopen(url,"rb+");
FILE *fp1=fopen("output_444_y.y","wb+");
FILE *fp2=fopen("output_444_u.y","wb+");
FILE *fp3=fopen("output_444_v.y","wb+");
unsigned char pic=(unsigned char )malloc(wh3);
for(int i=0;i<num;i++){
fread(pic,1,w*h*3,fp);
//Y
fwrite(pic,1,w*h,fp1);
//U
fwrite(pic+w*h,1,w*h,fp2);
//V
fwrite(pic+w*h*2,1,w*h,fp3);
}
free(pic);
fclose(fp);
fclose(fp1);
fclose(fp2);
fclose(fp3);
return 0;
}
調用上面函數的方法如下所示。
simplest_yuv444_split("lena_256x256_yuv444p.yuv",256,256,1);
從代碼可以看出,如果視頻幀的寬和高分別為w和h,那么一幀YUV444P像素數據一共占用wh3 Byte的數據。其中前wh Byte存儲Y,接著的wh Byte存儲U,最后w*h Byte存儲V。上述調用函數的代碼運行后,將會把一張分辨率為256x256的名稱為lena_256x256_yuv444p.yuv的YUV444P格式的像素數據文件分離成為三個文件:
output_444_y.y:純Y數據,分辨率為256x256。
output_444_u.y:純U數據,分辨率為256x256。
output_444_v.y:純V數據,分辨率為256x256。
輸入的原圖如下所示。
輸出的三個文件的截圖如下圖所示。
(3) 將YUV420P像素數據去掉顏色(變成灰度圖)
本程序中的函數可以將YUV420P格式像素數據的彩色去掉,變成純粹的灰度圖。函數的代碼如下。
/**
Convert YUV420P file to gray picture
@param url Location of Input YUV file.
@param w Width of Input YUV file.
@param h Height of Input YUV file.
-
@param num Number of frames to process.
*/
int simplest_yuv420_gray(char *url, int w, int h,int num){
FILE *fp=fopen(url,"rb+");
FILE *fp1=fopen("output_gray.yuv","wb+");
unsigned char pic=(unsigned char )malloc(wh3/2);for(int i=0;i<num;i++){
fread(pic,1,wh3/2,fp);
//Gray
memset(pic+wh,128,wh/2);
fwrite(pic,1,wh3/2,fp1);
}free(pic);
fclose(fp);
fclose(fp1);
return 0;
}
調用上面函數的方法如下所示。
simplest_yuv420_gray("lena_256x256_yuv420p.yuv",256,256,1);
從代碼可以看出,如果想把YUV格式像素數據變成灰度圖像,只需要將U、V分量設置成128即可。這是因為U、V是圖像中的經過偏置處理的色度分量。色度分量在偏置處理前的取值范圍是-128至127,這時候的無色對應的是“0”值。經過偏置后色度分量取值變成了0至255,因而此時的無色對應的就是128了。上述調用函數的代碼運行后,將會把一張分辨率為256x256的名稱為lena_256x256_yuv420p.yuv的YUV420P格式的像素數據文件處理成名稱為output_gray.yuv的YUV420P格式的像素數據文件。輸入的原圖如下所示。
處理后的圖像如下所示。
(4)將YUV420P像素數據的亮度減半
本程序中的函數可以通過將YUV數據中的亮度分量Y的數值減半的方法,降低圖像的亮度。函數代碼如下所示。
/**
Halve Y value of YUV420P file
@param url Location of Input YUV file.
@param w Width of Input YUV file.
@param h Height of Input YUV file.
-
@param num Number of frames to process.
*/
int simplest_yuv420_halfy(char *url, int w, int h,int num){
FILE *fp=fopen(url,"rb+");
FILE *fp1=fopen("output_half.yuv","wb+");unsigned char pic=(unsigned char )malloc(wh3/2);
for(int i=0;i<num;i++){
fread(pic,1,wh3/2,fp);
//Half
for(int j=0;j<wh;j++){
unsigned char temp=pic[j]/2;
//printf("%d,\n",temp);
pic[j]=temp;
}
fwrite(pic,1,wh*3/2,fp1);
}free(pic);
fclose(fp);
fclose(fp1);return 0;
}
調用上面函數的方法如下所示。
simplest_yuv420_halfy("lena_256x256_yuv420p.yuv",256,256,1);
從代碼可以看出,如果打算將圖像的亮度減半,只要將圖像的每個像素的Y值取出來分別進行除以2的工作就可以了。圖像的每個Y值占用1 Byte,取值范圍是0至255,對應C語言中的unsigned char數據類型。上述調用函數的代碼運行后,將會把一張分辨率為256x256的名稱為lena_256x256_yuv420p.yuv的YUV420P格式的像素數據文件處理成名稱為output_half.yuv的YUV420P格式的像素數據文件。輸入的原圖如下所示。
處理后的圖像如下所示。
(5)將YUV420P像素數據的周圍加上邊框
本程序中的函數可以通過修改YUV數據中特定位置的亮度分量Y的數值,給圖像添加一個“邊框”的效果。函數代碼如下所示。
/**
Add border for YUV420P file
@param url Location of Input YUV file.
@param w Width of Input YUV file.
@param h Height of Input YUV file.
@param border Width of Border.
-
@param num Number of frames to process.
*/
int simplest_yuv420_border(char *url, int w, int h,int border,int num){
FILE *fp=fopen(url,"rb+");
FILE *fp1=fopen("output_border.yuv","wb+");unsigned char pic=(unsigned char )malloc(wh3/2);
for(int i=0;i<num;i++){
fread(pic,1,wh3/2,fp);
//Y
for(int j=0;j<h;j++){
for(int k=0;k<w;k++){
if(k<border||k>(w-border)||j<border||j>(h-border)){
pic[jw+k]=255;
//pic[jw+k]=0;
}
}
}
fwrite(pic,1,wh3/2,fp1);
}free(pic);
fclose(fp);
fclose(fp1);return 0;
}
調用上面函數的方法如下所示。
- simplest_yuv420_border("lena_256x256_yuv420p.yuv",256,256,20,1);
從代碼可以看出,圖像的邊框的寬度為border,本程序將距離圖像邊緣border范圍內的像素的亮度分量Y的取值設置成了亮度最大值255。上述調用函數的代碼運行后,將會把一張分辨率為256x256的名稱為lena_256x256_yuv420p.yuv的YUV420P格式的像素數據文件處理成名稱為output_border.yuv的YUV420P格式的像素數據文件。輸入的原圖如下所示。
處理后的圖像如下所示。
(6) 生成YUV420P格式的灰階測試圖
本程序中的函數可以生成一張YUV420P格式的灰階測試圖。函數代碼如下所示。
/**
Generate YUV420P gray scale bar.
@param width Width of Output YUV file.
@param height Height of Output YUV file.
@param ymin Max value of Y
@param ymax Min value of Y
@param barnum Number of bars
-
@param url_out Location of Output YUV file.
*/
int simplest_yuv420_graybar(int width, int height,int ymin,int ymax,int barnum,char *url_out){
int barwidth;
float lum_inc;
unsigned char lum_temp;
int uv_width,uv_height;
FILE *fp=NULL;
unsigned char *data_y=NULL;
unsigned char *data_u=NULL;
unsigned char *data_v=NULL;
int t=0,i=0,j=0;barwidth=width/barnum;
lum_inc=((float)(ymax-ymin))/((float)(barnum-1));
uv_width=width/2;
uv_height=height/2;data_y=(unsigned char )malloc(widthheight);
data_u=(unsigned char )malloc(uv_widthuv_height);
data_v=(unsigned char )malloc(uv_widthuv_height);if((fp=fopen(url_out,"wb+"))==NULL){
printf("Error: Cannot create file!");
return -1;
}//Output Info
printf("Y, U, V value from picture's left to right:\n");
for(t=0;t<(width/barwidth);t++){
lum_temp=ymin+(char)(tlum_inc);
printf("%3d, 128, 128\n",lum_temp);
}
//Gen Data
for(j=0;j<height;j++){
for(i=0;i<width;i++){
t=i/barwidth;
lum_temp=ymin+(char)(tlum_inc);
data_y[jwidth+i]=lum_temp;
}
}
for(j=0;j<uv_height;j++){
for(i=0;i<uv_width;i++){
data_u[juv_width+i]=128;
}
}
for(j=0;j<uv_height;j++){
for(i=0;i<uv_width;i++){
data_v[juv_width+i]=128;
}
}
fwrite(data_y,widthheight,1,fp);
fwrite(data_u,uv_widthuv_height,1,fp);
fwrite(data_v,uv_widthuv_height,1,fp);
fclose(fp);
free(data_y);
free(data_u);
free(data_v);
return 0;
}
調用上面函數的方法如下所示。
simplest_yuv420_graybar(640, 360,0,255,10,"graybar_640x360.yuv");
從源代碼可以看出,本程序一方面通過灰階測試圖的亮度最小值ymin,亮度最大值ymax,灰階數量barnum確定每一個灰度條中像素的亮度分量Y的取值。另一方面還要根據圖像的寬度width和圖像的高度height以及灰階數量barnum確定每一個灰度條的寬度。有了這兩方面信息之后,就可以生成相應的圖片了。上述調用函數的代碼運行后,會生成一個取值范圍從0-255,一共包含10個灰度條的YUV420P格式的測試圖。測試圖的內容如下所示。
(7)計算兩個YUV420P像素數據的PSNR
PSNR是最基本的視頻質量評價方法。本程序中的函數可以對比兩張YUV圖片中亮度分量Y的PSNR。函數的代碼如下所示。
/**
Calculate PSNR between 2 YUV420P file
@param url1 Location of first Input YUV file.
@param url2 Location of another Input YUV file.
@param w Width of Input YUV file.
@param h Height of Input YUV file.
-
@param num Number of frames to process.
*/
int simplest_yuv420_psnr(char *url1,char *url2,int w,int h,int num){
FILE *fp1=fopen(url1,"rb+");
FILE *fp2=fopen(url2,"rb+");
unsigned char *pic1=(unsigned char )malloc(wh);
unsigned char *pic2=(unsigned char )malloc(wh);for(int i=0;i<num;i++){
fread(pic1,1,wh,fp1);
fread(pic2,1,wh,fp2);double mse_sum=0,mse=0,psnr=0; for(int j=0;j<w*h;j++){ mse_sum+=pow((double)(pic1[j]-pic2[j]),2); } mse=mse_sum/(w*h); psnr=10*log10(255.0*255.0/mse); printf("%5.3f\n",psnr); fseek(fp1,w*h/2,SEEK_CUR); fseek(fp2,w*h/2,SEEK_CUR);
}
free(pic1);
free(pic2);
fclose(fp1);
fclose(fp2);
return 0;
}
調用上面函數的方法如下所示。
- simplest_yuv420_psnr("lena_256x256_yuv420p.yuv","lena_distort_256x256_yuv420p.yuv",256,256,1);
對于8bit量化的像素數據來說,PSNR的計算公式如下所示。
上述公式中mse的計算公式如下所示。
其中M,N分別為圖像的寬高,xij和yij分別為兩張圖像的每一個像素值。PSNR通常用于質量評價,就是計算受損圖像與原始圖像之間的差別,以此來評價受損圖像的質量。本程序輸入的兩張圖像的對比圖如下圖所示。其中左邊的圖像為原始圖像,右邊的圖像為受損圖像。
經過程序計算后得到的PSNR取值為26.693。PSNR取值通常情況下都在20-50的范圍內,取值越高,代表兩張圖像越接近,反映出受損圖像質量越好。
(8) 分離RGB24像素數據中的R、G、B分量
本程序中的函數可以將RGB24數據中的R、G、B三個分量分離開來并保存成三個文件。函數的代碼如下所示。
/**
- Split R, G, B planes in RGB24 file.
- @param url Location of Input RGB file.
- @param w Width of Input RGB file.
- @param h Height of Input RGB file.
- @param num Number of frames to process.
*/
int simplest_rgb24_split(char *url, int w, int h,int num){
FILE *fp=fopen(url,"rb+");
FILE *fp1=fopen("output_r.y","wb+");
FILE *fp2=fopen("output_g.y","wb+");
FILE *fp3=fopen("output_b.y","wb+");
unsigned char *pic=(unsigned char *)malloc(w*h*3);
for(int i=0;i<num;i++){
fread(pic,1,w*h*3,fp);
for(int j=0;j<w*h*3;j=j+3){
//R
fwrite(pic+j,1,1,fp1);
//G
fwrite(pic+j+1,1,1,fp2);
//B
fwrite(pic+j+2,1,1,fp3);
}
}
free(pic);
fclose(fp);
fclose(fp1);
fclose(fp2);
fclose(fp3);
return 0;
}
調用上面函數的方法如下所示。
simplest_rgb24_split("cie1931_500x500.rgb", 500, 500,1);
從代碼可以看出,與YUV420P三個分量分開存儲不同,RGB24格式的每個像素的三個分量是連續存儲的。一幀寬高分別為w、h的RGB24圖像一共占用wh3 Byte的存儲空間。RGB24格式規定首先存儲第一個像素的R、G、B,然后存儲第二個像素的R、G、B…以此類推。類似于YUV420P的存儲方式稱為Planar方式,而類似于RGB24的存儲方式稱為Packed方式。上述調用函數的代碼運行后,將會把一張分辨率為500x500的名稱為cie1931_500x500.rgb的RGB24格式的像素數據文件分離成為三個文件:
output_r.y:R數據,分辨率為256x256。
output_g.y:G數據,分辨率為256x256。
output_b.y:B數據,分辨率為256x256。
輸入的原圖是一張標準的CIE 1931色度圖。該色度圖右下為紅色,上方為綠色,左下為藍色,如下所示。
R數據圖像如下所示。
G數據圖像如下所示。
B數據圖像如下所示。
(9)將RGB24格式像素數據封裝為BMP圖像
BMP圖像內部實際上存儲的就是RGB數據。本程序實現了對RGB像素數據的封裝處理。通過本程序中的函數,可以將RGB數據封裝成為一張BMP圖像。
/**
Convert RGB24 file to BMP file
@param rgb24path Location of input RGB file.
@param width Width of input RGB file.
@param height Height of input RGB file.
-
@param url_out Location of Output BMP file.
*/
int simplest_rgb24_to_bmp(const char *rgb24path,int width,int height,const char *bmppath){
typedef struct
{
long imageSize;
long blank;
long startPosition;
}BmpHead;typedef struct
{
long Length;
long width;
long height;
unsigned short colorPlane;
unsigned short bitColor;
long zipFormat;
long realSize;
long xPels;
long yPels;
long colorUse;
long colorImportant;
}InfoHead;int i=0,j=0;
BmpHead m_BMPHeader={0};
InfoHead m_BMPInfoHeader={0};
char bfType[2]={'B','M'};
int header_size=sizeof(bfType)+sizeof(BmpHead)+sizeof(InfoHead);
unsigned char *rgb24_buffer=NULL;
FILE fp_rgb24=NULL,fp_bmp=NULL;if((fp_rgb24=fopen(rgb24path,"rb"))==NULL){
printf("Error: Cannot open input RGB24 file.\n");
return -1;
}
if((fp_bmp=fopen(bmppath,"wb"))==NULL){
printf("Error: Cannot open output BMP file.\n");
return -1;
}rgb24_buffer=(unsigned char )malloc(widthheight3);
fread(rgb24_buffer,1,widthheight*3,fp_rgb24);m_BMPHeader.imageSize=3widthheight+header_size;
m_BMPHeader.startPosition=header_size;m_BMPInfoHeader.Length=sizeof(InfoHead);
m_BMPInfoHeader.width=width;
//BMP storage pixel data in opposite direction of Y-axis (from bottom to top).
m_BMPInfoHeader.height=-height;
m_BMPInfoHeader.colorPlane=1;
m_BMPInfoHeader.bitColor=24;
m_BMPInfoHeader.realSize=3widthheight;fwrite(bfType,1,sizeof(bfType),fp_bmp);
fwrite(&m_BMPHeader,1,sizeof(m_BMPHeader),fp_bmp);
fwrite(&m_BMPInfoHeader,1,sizeof(m_BMPInfoHeader),fp_bmp);//BMP save R1|G1|B1,R2|G2|B2 as B1|G1|R1,B2|G2|R2
//It saves pixel data in Little Endian
//So we change 'R' and 'B'
for(j =0;j<height;j++){
for(i=0;i<width;i++){
char temp=rgb24_buffer[(jwidth+i)3+2];
rgb24_buffer[(jwidth+i)3+2]=rgb24_buffer[(jwidth+i)3+0];
rgb24_buffer[(jwidth+i)3+0]=temp;
}
}
fwrite(rgb24_buffer,3widthheight,1,fp_bmp);
fclose(fp_rgb24);
fclose(fp_bmp);
free(rgb24_buffer);
printf("Finish generate %s!\n",bmppath);
return 0;
return 0;
}
調用上面函數的方法如下所示。
simplest_rgb24_to_bmp("lena_256x256_rgb24.rgb",256,256,"output_lena.bmp");
通過代碼可以看出,該程序完成了主要完成了兩個工作:
1)將RGB數據前面加上文件頭。
2)將RGB數據中每個像素的“B”和“R”的位置互換。
BMP文件是由BITMAPFILEHEADER、BITMAPINFOHEADER、RGB像素數據共3個部分構成,它的結構如下圖所示。
BITMAPFILEHEADER
BITMAPINFOHEADER
RGB像素數據
其中前兩部分的結構如下所示。在寫入BMP文件頭的時候給其中的每個字段賦上合適的值就可以了。
typedef struct tagBITMAPFILEHEADER
{
unsigned short int bfType; //位圖文件的類型,必須為BM
unsigned long bfSize; //文件大小,以字節為單位
unsigned short int bfReserverd1; //位圖文件保留字,必須為0
unsigned short int bfReserverd2; //位圖文件保留字,必須為0
unsigned long bfbfOffBits; //位圖文件頭到數據的偏移量,以字節為單位
}BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER
{
long biSize; //該結構大小,字節為單位
long biWidth; //圖形寬度以象素為單位
long biHeight; //圖形高度以象素為單位
short int biPlanes; //目標設備的級別,必須為1
short int biBitcount; //顏色深度,每個象素所需要的位數
short int biCompression; //位圖的壓縮類型
long biSizeImage; //位圖的大小,以字節為單位
long biXPelsPermeter; //位圖水平分辨率,每米像素數
long biYPelsPermeter; //位圖垂直分辨率,每米像素數
long biClrUsed; //位圖實際使用的顏色表中的顏色數
long biClrImportant; //位圖顯示過程中重要的顏色數
}BITMAPINFOHEADER;
BMP采用的是小端(Little Endian)存儲方式。這種存儲方式中“RGB24”格式的像素的分量存儲的先后順序為B、G、R。由于RGB24格式存儲的順序是R、G、B,所以需要將“R”和“B”順序作一個調換再進行存儲。
下圖為輸入的RGB24格式的圖像lena_256x256_rgb24.rgb。
下圖分封裝為BMP格式后的圖像output_lena.bmp。封裝后的圖像使用普通的看圖軟件就可以查看。
(10)將RGB24格式像素數據轉換為YUV420P格式像素數據
本程序中的函數可以將RGB24格式的像素數據轉換為YUV420P格式的像素數據。函數的代碼如下所示。
unsigned char clip_value(unsigned char x,unsigned char min_val,unsigned char max_val){
if(x>max_val){
return max_val;
}else if(x<min_val){
return min_val;
}else{
return x;
}
}
//RGB to YUV420
bool RGB24_TO_YUV420(unsigned char RgbBuf,int w,int h,unsigned char yuvBuf)
{
unsigned charptrY, ptrU, ptrV, ptrRGB;
memset(yuvBuf,0,wh3/2);
ptrY = yuvBuf;
ptrU = yuvBuf + wh;
ptrV = ptrU + (wh1/4);
unsigned char y, u, v, r, g, b;
for (int j = 0; j<h;j++){
ptrRGB = RgbBuf + wj*3 ;
for (int i = 0;i<w;i++){
r = *(ptrRGB++);
g = *(ptrRGB++);
b = *(ptrRGB++);
y = (unsigned char)( ( 66 * r + 129 * g + 25 * b + 128) >> 8) + 16 ;
u = (unsigned char)( ( -38 * r - 74 * g + 112 * b + 128) >> 8) + 128 ;
v = (unsigned char)( ( 112 * r - 94 * g - 18 * b + 128) >> 8) + 128 ;
*(ptrY++) = clip_value(y,0,255);
if (j%2==0&&i%2 ==0){
*(ptrU++) =clip_value(u,0,255);
}
else{
if (i%2==0){
*(ptrV++) =clip_value(v,0,255);
}
}
}
}
return true;
}
/**
Convert RGB24 file to YUV420P file
@param url_in Location of Input RGB file.
@param w Width of Input RGB file.
@param h Height of Input RGB file.
@param num Number of frames to process.
-
@param url_out Location of Output YUV file.
*/
int simplest_rgb24_to_yuv420(char *url_in, int w, int h,int num,char *url_out){
FILE *fp=fopen(url_in,"rb+");
FILE *fp1=fopen(url_out,"wb+");unsigned char pic_rgb24=(unsigned char )malloc(wh3);
unsigned char pic_yuv420=(unsigned char )malloc(wh3/2);for(int i=0;i<num;i++){
fread(pic_rgb24,1,wh3,fp);
RGB24_TO_YUV420(pic_rgb24,w,h,pic_yuv420);
fwrite(pic_yuv420,1,wh3/2,fp1);
}free(pic_rgb24);
free(pic_yuv420);
fclose(fp);
fclose(fp1);return 0;
}
調用上面函數的方法如下所示。
simplest_rgb24_to_yuv420("lena_256x256_rgb24.rgb",256,256,1,"output_lena.yuv");
從源代碼可以看出,本程序實現了RGB到YUV的轉換公式:
Y= 0.299R+0.587G+0.114B*
U=-0.147R-0.289G+0.463B*
V= 0.615R-0.515G-0.100B*
在轉換的過程中有以下幾點需要注意:
- RGB24存儲方式是Packed,YUV420P存儲方式是Packed。
- U,V在水平和垂直方向的取樣數是Y的一半
轉換前的RGB24格式像素數據lena_256x256_rgb24.rgb的內容如下所示。
轉換后的YUV420P格式的像素數據output_lena.yuv的內容如下所示。
(11)生成RGB24格式的彩條測試圖
本程序中的函數可以生成一張RGB24格式的彩條測試圖。函數代碼如下所示。
/**
Generate RGB24 colorbar.
@param width Width of Output RGB file.
@param height Height of Output RGB file.
-
@param url_out Location of Output RGB file.
*/
int simplest_rgb24_colorbar(int width, int height,char *url_out){unsigned char *data=NULL;
int barwidth;
char filename[100]={0};
FILE *fp=NULL;
int i=0,j=0;data=(unsigned char )malloc(widthheight*3);
barwidth=width/8;if((fp=fopen(url_out,"wb+"))==NULL){
printf("Error: Cannot create file!");
return -1;
}for(j=0;j<height;j++){
for(i=0;i<width;i++){
int barnum=i/barwidth;
switch(barnum){
case 0:{
data[(jwidth+i)3+0]=255;
data[(jwidth+i)3+1]=255;
data[(jwidth+i)3+2]=255;
break;
}
case 1:{
data[(jwidth+i)3+0]=255;
data[(jwidth+i)3+1]=255;
data[(jwidth+i)3+2]=0;
break;
}
case 2:{
data[(jwidth+i)3+0]=0;
data[(jwidth+i)3+1]=255;
data[(jwidth+i)3+2]=255;
break;
}
case 3:{
data[(jwidth+i)3+0]=0;
data[(jwidth+i)3+1]=255;
data[(jwidth+i)3+2]=0;
break;
}
case 4:{
data[(jwidth+i)3+0]=255;
data[(jwidth+i)3+1]=0;
data[(jwidth+i)3+2]=255;
break;
}
case 5:{
data[(jwidth+i)3+0]=255;
data[(jwidth+i)3+1]=0;
data[(jwidth+i)3+2]=0;
break;
}
case 6:{
data[(jwidth+i)3+0]=0;
data[(jwidth+i)3+1]=0;
data[(jwidth+i)3+2]=255;break; } case 7:{ data[(j*width+i)*3+0]=0; data[(j*width+i)*3+1]=0; data[(j*width+i)*3+2]=0; break; } } }
}
fwrite(data,widthheight3,1,fp);
fclose(fp);
free(data);return 0;
}
調用上面函數的方法如下所示。
simplest_rgb24_colorbar(640, 360,"colorbar_640x360.rgb");
從源代碼可以看出,本程序循環輸出“白黃青綠品紅藍黑”8種顏色的彩條。這8種顏色的彩條的R、G、B取值如下所示。
白
|
(255, 255, 255)
黃
|
(255, 255, 0)
青
|
( 0, 255, 255)
綠
|
( 0, 255, 0)
品
|
(255, 0, 255)
紅
|
(255, 0, 0)
藍
|
( 0, 0, 255)
黑
|
( 0, 0, 0)
|
生成的圖像截圖如下所示。
下載
Simplest mediadata test
項目主頁
SourceForge:https://sourceforge.net/projects/simplest-mediadata-test/
Github:https://github.com/leixiaohua1020/simplest_mediadata_test
開源中國:http://git.oschina.net/leixiaohua1020/simplest_mediadata_test
CSDN下載地址:http://download.csdn.net/detail/leixiaohua1020/9422409
本項目包含如下幾種視音頻數據解析示例:
(1)像素數據處理程序。包含RGB和YUV像素格式處理的函數。
(2)音頻采樣數據處理程序。包含PCM音頻采樣格式處理的函數。
(3)H.264碼流分析程序。可以分離并解析NALU。
(4)AAC碼流分析程序。可以分離并解析ADTS幀。
(5)FLV封裝格式分析程序。可以將FLV中的MP3音頻碼流分離出來。
(6)UDP-RTP協議分析程序。可以將分析UDP/RTP/MPEG-TS數據包。
雷霄驊 (Lei Xiaohua)
leixiaohua1020@126.com
http://blog.csdn.net/leixiaohua1020
尊重雷神,原版還是雷神的,我只是來學習的。