前段時間因為項目需要,嘗試通過FFMPEG將映像幀進行格式轉換,評估測試了一下,記錄一下FFMPEG的一些基本資料資料 概念:
來自百科:
FFmpeg是一套可以用來記錄、轉換數字音頻、視頻,並能將其轉化為流的開源電腦程式。它包括了領先的音/視頻編碼庫libavcodec等。
libavformat:用於各種音視頻封裝格式的產生和解析,包括擷取解碼所需資訊以產生解碼上下文結構
和讀取音視訊框架等功能;
libavcodec:用於各種類型聲音/映像編解碼;
libavutil:包含一些公用的工具函數;
libswscale:用於視頻情境比例縮放、色彩映射轉換;
libpostproc:用於後期效果處理;
ffmpeg:該項目提供的一個工具,可用于格式轉換、解碼或電視卡即時編碼等;
ffsever:一個 HTTP 多媒體即時廣播串流伺服器;
ffplay:是一個簡單的播放器,使用ffmpeg 庫解析和解碼,通過SDL顯示;
通俗理解為一個 音視頻流處理庫集合,跟之前接觸過的opencv有相同之處,都涉及不深,個人感覺感覺側重點不同,ffmpeg側重音視頻來源資料流的格式編解碼,opencv側重的是對已存在視圖的演算法處理操作
ffmpeg 官網下載:https://ffmpeg.org/ 編譯:
ubuntu 32bit 下編譯,目標平台android,需要配置NDK ,下載ffmpeg-3.1.4的根目錄下建立一個編譯指令碼build_android.sh,內容如下:
#!/bin/bashNDK=/home/xxx/tool/android-ndk-r10SYSROOT=$NDK/platforms/android-19/arch-arm/TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86function build_one{./configure \ --prefix=$PREFIX \ --enable-shared \ --disable-static \ --disable-doc \ --disable-ffmpeg \ --disable-ffplay \ --disable-ffprobe \ --disable-ffserver \ --enable-gpl \ --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \ --target-os=linux \ --arch=arm \ --enable-cross-compile \ --sysroot=$SYSROOT \ --extra-cflags="-Os -fpic $ADDI_CFLAGS" \ --extra-ldflags="$ADDI_LDFLAGS" \ $ADDITIONAL_CONFIGURE_FLAG#make cleanmakemake install}CPU=armPREFIX=$(pwd)/android/$CPU ADDI_CFLAGS="-marm"build_one
關鍵是NDK的路徑,根據實際情況配置,native-api 的版本以及交叉編譯工具鏈的選擇,需注意 32-64 x86主機區別,目標平台為arm
按上面編譯之後對應ffmpeg-3.1.4\android\arm 目錄下產生 標頭檔以及 lib庫,直接拿到android上使用 YUV格式概念:
網上的總結:
YUV。分為三個分量,“Y”表示明亮度(Luminance或Luma)。也就是灰階值;而“U”和“V” 表示的則是色度(Chrominance或Chroma),
作用是描寫敘述影像色彩及飽和度,用於指定像素的顏色。
與我們熟知的RGB類似。YUV也是一種顏色編碼方法,主要用於電視系統以及類比視頻領域,它將亮度資訊(Y)與色多媒體訊息息(UV)分離,
沒有UV資訊一樣能夠顯示完整的映像,僅僅只是是黑白的,這種設計非常好地攻克了彩色電視機與黑白電視的相容問題。
而且,YUV不像RGB那樣要求三個獨立的視頻訊號同一時候傳輸,所以用YUV方式傳送佔用極少的頻寬。
通過YUV與RGB的轉換公式提取出每一個像素點的RGB值,然後顯示出來。
YUV 4:4:4採樣,每個Y相應一組UV分量。
YUV 4:2:2採樣。每兩個Y共用一組UV分量。
YUV 4:2:0採樣,每四個Y共用一組UV分量。
參考網上的部落格 http://blog.csdn.net/beyond_cn/article/details/12998247 YUV轉RGB:
通過ffmpeg將一幀YUV格式的映像轉換成成RGB格式並儲存,需要用到 libavutil 以及 libswscale
網上摘的轉換實現:
#include <stdio.h>#include <utils/Log.h>#define LOG_TAG "ffmpeg"extern "C"{#include "libswscale/swscale.h"#include "libavutil/opt.h"#include "libavutil/imgutils.h"};#include <android/log.h>#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , "ffmpeg", __VA_ARGS__)int main(int argc, char* argv[]){ //Parameters FILE *src_file =fopen("1088p_yuv420p.yuv", "rb"); const int src_w=1920,src_h=1088; AVPixelFormat src_pixfmt=AV_PIX_FMT_YUV420P; int src_bpp=av_get_bits_per_pixel(av_pix_fmt_desc_get(src_pixfmt)); FILE *dst_file = fopen("480p_rgb24.rgb", "wb"); const int dst_w=800,dst_h=480; AVPixelFormat dst_pixfmt=AV_PIX_FMT_RGB565LE/*AV_PIX_FMT_RGB24*/; int dst_bpp=av_get_bits_per_pixel(av_pix_fmt_desc_get(dst_pixfmt)); //Structures uint8_t *src_data[4]; int src_linesize[4]; uint8_t *dst_data[4]; int dst_linesize[4]; int rescale_method=SWS_BILINEAR/*SWS_POINT*//*SWS_BICUBIC*/; struct SwsContext *img_convert_ctx; uint8_t *temp_buffer=(uint8_t *)malloc(src_w*src_h*src_bpp/8); int frame_idx=0; int ret=0; ret= av_image_alloc(src_data, src_linesize,src_w, src_h, src_pixfmt, 1); if (ret< 0) { printf( "Could not allocate source image\n"); return -1; } ret = av_image_alloc(dst_data, dst_linesize,dst_w, dst_h, dst_pixfmt, 1); if (ret< 0) { printf( "Could not allocate destination image\n"); return -1; } //----------------------------- //Init Method 1// img_convert_ctx =sws_alloc_context();// //Show AVOption// av_opt_show2(img_convert_ctx,stdout,AV_OPT_FLAG_VIDEO_PARAM,NULL);// //Set Value// av_opt_set_int(img_convert_ctx,"sws_flags",SWS_BICUBIC|SWS_PRINT_INFO,NULL);// av_opt_set_int(img_convert_ctx,"srcw",src_w,NULL);// av_opt_set_int(img_convert_ctx,"srch",src_h,NULL);// av_opt_set_int(img_convert_ctx,"src_format",src_pixfmt,NULL);// //'0' for MPEG (Y:0-235);'1' for JPEG (Y:0-255)// av_opt_set_int(img_convert_ctx,"src_range",1,NULL);// av_opt_set_int(img_convert_ctx,"dstw",dst_w,NULL);// av_opt_set_int(img_convert_ctx,"dsth",dst_h,NULL);// av_opt_set_int(img_convert_ctx,"dst_format",dst_pixfmt,NULL);// av_opt_set_int(img_convert_ctx,"dst_range",1,NULL);// sws_init_context(img_convert_ctx,NULL,NULL); //Init Method 2 img_convert_ctx = sws_getContext(src_w, src_h,src_pixfmt, dst_w, dst_h, dst_pixfmt, rescale_method, NULL, NULL, NULL); //----------------------------- /* //Colorspace ret=sws_setColorspaceDetails(img_convert_ctx,sws_getCoefficients(SWS_CS_ITU601),0, sws_getCoefficients(SWS_CS_ITU709),0, 0, 1 << 16, 1 << 16); if (ret==-1) { printf( "Colorspace not support.\n"); return -1; } */ while(1) { if (fread(temp_buffer, 1, src_w*src_h*src_bpp/8, src_file) != src_w*src_h*src_bpp/8){ break; } switch(src_pixfmt){ case AV_PIX_FMT_GRAY8:{ memcpy(src_data[0],temp_buffer,src_w*src_h); break; } case AV_PIX_FMT_YUV420P:{ memcpy(src_data[0],temp_buffer,src_w*src_h); //Y memcpy(src_data[1],temp_buffer+src_w*src_h,src_w*src_h/4); //U memcpy(src_data[2],temp_buffer+src_w*src_h*5/4,src_w*src_h/4); //V break; } case AV_PIX_FMT_YUV422P:{ memcpy(src_data[0],temp_buffer,src_w*src_h); //Y memcpy(src_data[1],temp_buffer+src_w*src_h,src_w*src_h/2); //U memcpy(src_data[2],temp_buffer+src_w*src_h*3/2,src_w*src_h/2); //V break; } case AV_PIX_FMT_YUV444P:{ memcpy(src_data[0],temp_buffer,src_w*src_h); //Y memcpy(src_data[1],temp_buffer+src_w*src_h,src_w*src_h); //U memcpy(src_data[2],temp_buffer+src_w*src_h*2,src_w*src_h); //V break; } case AV_PIX_FMT_YUYV422:{ memcpy(src_data[0],temp_buffer,src_w*src_h*2); //Packed break; } case AV_PIX_FMT_RGB24:{ memcpy(src_data[0],temp_buffer,src_w*src_h*3); //Packed break; } default:{ printf("Not Support Input Pixel Format.\n"); break; } } ALOGE("-Finish process frame %5d\n",frame_idx); sws_scale(img_convert_ctx, src_data, src_linesize, 0, src_h, dst_data, dst_linesize); ALOGE("*Finish process frame %5d\n",frame_idx); printf("test-Finish process frame %5d\n",frame_idx); frame_idx++; switch(dst_pixfmt){ case AV_PIX_FMT_GRAY8:{ fwrite(dst_data[0],1,dst_w*dst_h,dst_file); break; } case AV_PIX_FMT_YUV420P:{ fwrite(dst_data[0],1,dst_w*dst_h,dst_file); //Y fwrite(dst_data[1],1,dst_w*dst_h/4,dst_file); //U fwrite(dst_data[2],1,dst_w*dst_h/4,dst_file); //V break; } case AV_PIX_FMT_YUV422P:{ fwrite(dst_data[0],1,dst_w*dst_h,dst_file); //Y fwrite(dst_data[1],1,dst_w*dst_h/2,dst_file); //U fwrite(dst_data[2],1,dst_w*dst_h/2,dst_file); //V break; } case AV_PIX_FMT_YUV444P:{ fwrite(dst_data[0],1,dst_w*dst_h,dst_file); //Y fwrite(dst_data[1],1,dst_w*dst_h,dst_file); //U fwrite(dst_data[2],1,dst_w*dst_h,dst_file); //V break; } case AV_PIX_FMT_YUYV422:{ fwrite(dst_data[0],1,dst_w*dst_h*2,dst_file); //Packed break; } case AV_PIX_FMT_RGB24:{ fwrite(dst_data[0],1,dst_w*dst_h*3,dst_file); //Packed break; } case AV_PIX_FMT_RGB565LE:{ fwrite(dst_data[0],1,dst_w*dst_h*2,dst_file); //Packed break; } default:{ printf("Not Support Output Pixel Format.\n"); break; } } } sws_freeContext(img_convert_ctx); free(temp_buffer); fclose(dst_file); av_freep(&src_data[0]); av_freep(&dst_data[0]); printf("ffmpeg_scale complete \n"); return 0;}
代碼比較簡單明了,需要注意的是 img_convert_ctx Init Method 方法有兩種,
調用swscale中的介面 sws_scale(…)實現縮放,第一種init預設縮放演算法為 SWS_BICUBIC ,第二種可以自己設定指定rescale_method
支援格式標頭檔:ffmpeg-3.1.4\libavutil\pixfmt.h
scale演算法種類: ffmpeg-3.1.4\libswscale\swscale.h
至於演算法的選擇可參考 http://www.cnblogs.com/acloud/archive/2011/10/29/sws_scale.html
有具體的測試資料