雷神FFMpeg原始碼學習筆記
阿新 • • 發佈:2019-01-04
雷神FFMpeg原始碼學習筆記
文章目錄
讀取編碼並依據編碼初始化內容結構
- 在開始編解碼視訊的時候首先第一步需要註冊一個編解碼器 :av_register_all();
avformat_open_input
來開啟這個檔案並給AVformartcontext
賦值 ,在其中會去查詢當前快取檔案的格式avformat_open_input
來開啟這個檔案並給AVformartcontext
avformat_find_stream_info
使用該方法給每個視訊/音訊流的AVStream
結構體進行賦值並得到,這個引數在裡面實現了一定的解碼過程- 在
AVstream
有值了以後我們需要拿到當前當前的avcodecContext
和其對應的AVcodeC
(使用avcodec_find_decoder
) avcodec_open2
,初始化一個視音訊編解碼器的AVCodecContext
,,呼叫AVCodeC
的初始化到具體的解碼器AVCodeC init()
所以是在avcodec_open2
在開始真正的初始化avcodecContext
- 在得到了初始化的
AVcodecContext
之後我們就可以開始為解碼之後的AVframe
分配空間(使用(unsigned char *)av_mallocz(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecContex->width, pCodecContex->height, 1)))
av_image_fill_arrays()
:為AVframe
的畫素點分配空間
sws_getContext
:使用源影象的高和寬得到目標影象的高和寬,flag 為設定影象拉伸的演算法
每一幀的視訊解碼處理
av_read_frame
avcodec_decode_video2
使用該函式來解碼得到的AVpacket
,輸出一個下一幀的AVframe
函式- 使用
sws_scale
來對下一幀的AVframe
進行拉伸變化 ,輸出想要得到的AVframe
- 釋放上述的
AVformartcontext
,AVstream
,avcodecContext.
下面是程式碼的具體處理操作:
1.首先需要註冊所有的編解碼器
//1.註冊所有的編解碼器等等
av_register_all();
//在需要網路的情況下初始化
avformat_network_init();
//2.初始化AVformartcontext,AVformartcontext 是
pContext=avformat_alloc_context();
//3.得到視訊流的URL 地址
//4.嘗試開啟檔案流,在AVformartcontext 中查詢當前的AVInputContext的格式
if (avformat_open_input(&pContext, input_str_full, NULL, NULL)!=0) {
NSLog(@"開啟檔案流失敗");
return;
}
avformat_open_input:內部實現就是去開啟當前檔案並將其賦值給AVformartcontext ,
然後查詢到當前AVformartcontext的AVinputcontext的格式,如:flv 等
//5.在AVStream 解碼一段視音流資訊 ,
if (avformat_find_stream_info(pContext, NULL)<0){
NSLog(@"解碼得到AVstream流資訊失敗");
return;
}
avformat_find_stream_info:該函式主要是給每個視訊/音訊流的AVStream 結構體進行賦值
其實在他內部本身實現瞭解碼的整個流程:查詢解碼器->開啟解碼器->讀取完整的一幀壓縮編碼資料->解碼壓縮編碼資料得到資訊
//6.拿到當前avformatcontext ->AVstream 的AVCodecContext 視訊流
int videoIndex=-1;
for (int i=0; i<pContext->nb_streams; i++) {
if (pContext->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
videoIndex=i;
}
}
if (videoIndex==-1) {
NSLog(@"當前的資料是錯誤的");return;
}
//拿到視訊流的上下文
AVCodecContext *pCodecContex=pContext->streams[videoIndex]->codec;
//7.通過avcodec_find——decoder 去找尋當前id 下的AVcodeC 解碼器
AVCodec *pCodec=avcodec_find_decoder(pCodecContex->codec_id);
if (pCodec==NULL) {
NSLog(@"找不到AVCodeC");
return;
}
//8.初始化一個視音訊編解碼器的AVCodecContext,,呼叫AVCodeC的初始化到具體的解碼器 AVCodeC init() 所以是在
avcodec_open2在開始真正的初始化avcodecContext
if (avcodec_open2(pCodecContex, pCodec, NULL)!=0 ) {
NSLog(@"開啟codeC錯誤");
return;
}
avcodec_open2:引數為初始化一個codecContext 目標解碼器 AVdictionary
//建立一個AVframe 就是用來預測下一幀的視訊幀,使用avcodec_decode_video2 預測下一幀
AVFrame *pFrame;
pFrame=av_frame_alloc();
AVFrame *pFrameYUV;
pFrameYUV=av_frame_alloc();
uint8_t *outBuffer;
//9 pFrameYUV分配空間,該函式並沒有為AVFrame的畫素資料分配空間,需要使用av_image_fill_arrays分配
outBuffer=(unsigned char *)av_mallocz(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecContex->width,
pCodecContex->height, 1));
//給目標的AVframe 來分配空間
av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, outBuffer, AV_PIX_FMT_YUV420P, pCodecContex->width,
pCodecContex->height, 1);
//10.給AVFrame 分配完了地址空間並給 畫素點新增進去之後 需要是用libswscale 包&來進行影象的格式轉換和影象的拉伸
AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket));
struct SwsContext *img_conver_ctx;
/**
* Allocate and return an SwsContext. You need it to perform
* scaling/conversion operations using sws_scale().
*
* @param srcW the width of the source image
* @param srcH the height of the source image
* @param srcFormat the source image format
* @param dstW the width of the destination image
* @param dstH the height of the destination image
* @param dstFormat the destination image format
* @param flags specify which algorithm and options to use for rescaling
* @return a pointer to an allocated context, or NULL in case of error
* @note this function is to be removed after a saner alternative is
* written
*/
img_conver_ctx= sws_getContext(pCodecContex->width, pCodecContex->height, pCodecContex->pix_fmt, pCodecContex->width, pCodecContex->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
sws_getContext:從sws_getContext()的定義中可以看出,它首先呼叫了一個函式sws_alloc_context()用於給SwsContext分配記憶體。然後將傳入的源影象,目標影象的寬高,畫素格式,以及標誌位分別賦值給該SwsContext相應的欄位。最後呼叫一個函式sws_init_context()完成初始化工作
int frame_cnt;
//用來計算處理事件用了多久的時間
clock_t time_start ,timeFinsh;
time_start=clock();
int y_size;
int got_picture, ret;
FILE *fp_yuv;
fp_yuv=fopen(output_str_full, "wb+");
if (fp_yuv==NULL) {
NSLog(@"不能開啟該地址下的檔案");
return;
}
//11.解碼之前都要先用 av_read_frame()獲得一幀視訊的壓縮資料,或者是若干幀的音訊資料,
while (av_read_frame(pContext, packet)>=0) {
if (packet->stream_index==videoIndex) {
//avcodec_decode_video2()的作用是解碼一幀視訊資料。輸入一個壓縮編碼的結構體AVPacket,輸出一個解碼後的結構體AVFrame
ret=avcodec_decode_video2(pCodecContex, pFrame, &got_picture, packet);
if (ret<0) {
NSLog(@"解碼下一幀失敗");
return;
}
if (got_picture) {
//對影象進行拉伸等處理,這裡是不進行拉伸變換
sws_scale(img_conver_ctx, (const uint8_t* const* )pFrame->data, pFrame->linesize, 0, pCodecContex->height, pFrameYUV->data, pFrameYUV->linesize);
y_size=pCodecContex->width*pCodecContex->height;
fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv);
fwrite(pFrameYUV->data[1], 1, y_size/4, fp_yuv);
fwrite(pFrameYUV->data[2], 1, y_size/4, fp_yuv);
char picture_type_str[10]={0};
switch (pFrame->pict_type) {
case AV_PICTURE_TYPE_I:sprintf(picture_type_str,"I");break;
case AV_PICTURE_TYPE_P:sprintf(picture_type_str,"P");break;
case AV_PICTURE_TYPE_B:sprintf(picture_type_str,"B");break;
default:sprintf(picture_type_str,"Other");break;
}
NSLog(@"frame index:%.5d type:%s",frame_cnt,picture_type_str);
frame_cnt++;
}
}
av_free_packet(packet);
}
while (1) {
ret=avcodec_decode_video2(pCodecContex, pFrame, &got_picture, packet);
if (ret<0) {
return;
}
if (!got_picture) {
break;
}
sws_scale(img_conver_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecContex->height, pFrameYUV->data, pFrameYUV->linesize);
int y_size=pCodecContex->width*pCodecContex->height;
fwrite(pFrameYUV->data[0],1,y_size,fp_yuv); //Y
fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv); //U
fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv); //V
//Output info
char pictype_str[10]={0};
switch(pFrame->pict_type){
case AV_PICTURE_TYPE_I:sprintf(pictype_str,"I");break;
case AV_PICTURE_TYPE_P:sprintf(pictype_str,"P");break;
case AV_PICTURE_TYPE_B:sprintf(pictype_str,"B");break;
default:sprintf(pictype_str,"Other");break;
}
printf("Frame Index: %5d. Type:%s\n",frame_cnt,pictype_str);
frame_cnt++;
}
timeFinsh=clock();
double time_duration=timeFinsh-time_start;
NSLog(@"這個過程持續的時間是%f",time_duration);
sws_freeContext(img_conver_ctx);
fclose(fp_yuv);
av_frame_free(&pFrameYUV);
av_frame_free(&pFrame);
avcodec_close(pCodecContex);
avformat_close_input(&pContext);