FFmpeg的視頻解碼詳解

FFmpeg的視頻編解碼流程:

第一步:組冊(cè)組件

av_register_all()

例如:編碼器、解碼器等等…

第二步:打開封裝格式->打開文件

例如:.mp4、.mov、.wmv文件等等...

avformat_open_input();

第三步:查找視頻流

如果是視頻解碼,那么查找視頻流,如果是音頻解碼,那么就查找音頻流

avformat_find_stream_info();

第四步:查找視頻解碼器

1、查找視頻流索引位置

2、根據(jù)視頻流索引,獲取解碼器上下文

3、根據(jù)解碼器上下文,獲得解碼器ID,然后查找解碼器

第五步:打開解碼器

avcodec_open2();

第六步:讀取視頻壓縮數(shù)據(jù)->循環(huán)讀取

沒讀取一幀數(shù)據(jù),立馬解碼一幀數(shù)據(jù)

第七步:視頻解碼->播放視頻->得到視頻像素?cái)?shù)據(jù)

第八步:關(guān)閉解碼器->解碼完成

上代碼:

//第一步:組冊(cè)組件

? ? av_register_all();

? ? //第二步:打開封裝格式->打開文件

? ? //參數(shù)一:封裝格式上下文

? ? //作用:保存整個(gè)視頻信息(解碼器、編碼器等等...)

? ? //信息:碼率、幀率等...

? ? AVFormatContext* avformat_context = avformat_alloc_context();

? ? //參數(shù)二:視頻路徑

? ? const char *url = [jinFilePath UTF8String]

? ? //參數(shù)三:指定輸入的格式

? ? //參數(shù)四:設(shè)置默認(rèn)參數(shù)

? ? int avformat_open_input_result = avformat_open_input(&avformat_context, url,NULL, NULL);

? ? if (avformat_open_input_result !=0){

? ? ? ? NSLog("打開文件失敗");

? ? ? ? //不同的平臺(tái)替換不同平臺(tái)log日志

? ? ? ? return;

? ? }

? ? //第三步:查找視頻流->拿到視頻信息

? ? //參數(shù)一:封裝格式上下文

? ? //參數(shù)二:指定默認(rèn)配置

? ? int avformat_find_stream_info_result = avformat_find_stream_info(avformat_context,NULL);

? ? if (avformat_find_stream_info_result <0){

? ? ? ? NSLog("?查找失敗");

? ? ? ? return;

? ? }

? ? //第四步:查找視頻解碼器

? ? //1、查找視頻流索引位置

? ? int av_stream_index = -1;

? ? for (int i =0; i < avformat_context->nb_streams; ++i) {

? ? ? ? //判斷流類型:視頻流、音頻流、字母流等等...

? ? ? ? if (avformat_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){

? ? ? ? ? ? av_stream_index = i;

? ? ? ? ? ? break;

? ? ? ? }

? ? }

? ? //2、根據(jù)視頻流索引,獲取解碼器上下文

? ? AVCodecContext *avcodec_context = avformat_context->streams[av_stream_index]->codec;

? ? //3、根據(jù)解碼器上下文,獲得解碼器ID,然后查找解碼器

? ? AVCodec *avcodec = avcodec_find_decoder(avcodec_context->codec_id);

? ? //第五步:打開解碼器

? ? int avcodec_open2_result = avcodec_open2(avcodec_context, avcodec,NULL);

? ? if (avcodec_open2_result !=0){

? ? ? ? NSLog("打開解碼器失敗");

? ? ? ? return;

? ? }

? ? //第六步:讀取視頻壓縮數(shù)據(jù)->循環(huán)讀取

? ? //1、分析av_read_frame參數(shù)

? ? //參數(shù)一:封裝格式上下文

? ? //參數(shù)二:一幀壓縮數(shù)據(jù) = 一張圖片

? ? //av_read_frame()

? ? //結(jié)構(gòu)體大小計(jì)算:字節(jié)對(duì)齊原則

? ? AVPacket* packet = (AVPacket*)av_malloc(sizeof(AVPacket));

? ? //3.2 解碼一幀視頻壓縮數(shù)據(jù)->進(jìn)行解碼(作用:用于解碼操作)

? ? //開辟一塊內(nèi)存空間

? ? AVFrame* avframe_in = av_frame_alloc();

? ? int decode_result =0;

? ? //4、注意:在這里我們不能夠保證解碼出來的一幀視頻像素?cái)?shù)據(jù)格式是yuv格式

? ? //參數(shù)一:源文件->原始視頻像素?cái)?shù)據(jù)格式寬

? ? //參數(shù)二:源文件->原始視頻像素?cái)?shù)據(jù)格式高

? ? //參數(shù)三:源文件->原始視頻像素?cái)?shù)據(jù)格式類型

? ? //參數(shù)四:目標(biāo)文件->目標(biāo)視頻像素?cái)?shù)據(jù)格式寬

? ? //參數(shù)五:目標(biāo)文件->目標(biāo)視頻像素?cái)?shù)據(jù)格式高

? ? //參數(shù)六:目標(biāo)文件->目標(biāo)視頻像素?cái)?shù)據(jù)格式類型

? ? SwsContext *swscontext = sws_getContext(avcodec_context->width,

?? ? ? ? ? ? ? ? ? avcodec_context->height,

?? ? ? ? ? ? ? ? ? avcodec_context->pix_fmt,

?? ? ? ? ? ? ? ? ? avcodec_context->width,

?? ? ? ? ? ? ? ? ? avcodec_context->height,

?? ? ? ? ? ? ? ? ? AV_PIX_FMT_YUV420P,

?? ? ? ? ? ? ? ? ? SWS_BICUBIC,

?? ? ? ? ? ? ? ? ? NULL,

?? ? ? ? ? ? ? ? ? NULL,

?? ? ? ? ? ? ? ? ? NULL);

? ? //創(chuàng)建一個(gè)yuv420視頻像素?cái)?shù)據(jù)格式緩沖區(qū)(一幀數(shù)據(jù))

? ? AVFrame* avframe_yuv420p = av_frame_alloc();

? ? //給緩沖區(qū)設(shè)置類型->yuv420類型

? ? //得到Y(jié)UV420P緩沖區(qū)大小

? ? //參數(shù)一:視頻像素?cái)?shù)據(jù)格式類型->YUV420P格式

? ? //參數(shù)二:一幀視頻像素?cái)?shù)據(jù)寬 = 視頻寬

? ? //參數(shù)三:一幀視頻像素?cái)?shù)據(jù)高 = 視頻高

? ? //參數(shù)四:字節(jié)對(duì)齊方式->默認(rèn)是1

? ? int buffer_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P,

?? ? ? ? ? ? ? ? ? ? ? ? ? ? avcodec_context->width,

?? ? ? ? ? ? ? ? ? ? ? ? ? ? avcodec_context->height,

?? ? ? ? ? ? ? ? ? ? ? ? ? ? 1);

? ? //開辟一塊內(nèi)存空間

? ? uint8_t *out_buffer = (uint8_t *)av_malloc(buffer_size);

? ? //向avframe_yuv420p->填充數(shù)據(jù)

? ? //參數(shù)一:目標(biāo)->填充數(shù)據(jù)(avframe_yuv420p)

? ? //參數(shù)二:目標(biāo)->每一行大小

? ? //參數(shù)三:原始數(shù)據(jù)

? ? //參數(shù)四:目標(biāo)->格式類型

? ? //參數(shù)五:寬

? ? //參數(shù)六:高

? ? //參數(shù)七:字節(jié)對(duì)齊方式

? ? av_image_fill_arrays(avframe_yuv420p->data,

?? ? ? ? ? ? ? ? ? ? ? ? avframe_yuv420p->linesize,

?? ? ? ? ? ? ? ? ? ? ? ? out_buffer,

?? ? ? ? ? ? ? ? ? ? ? ? AV_PIX_FMT_YUV420P,

?? ? ? ? ? ? ? ? ? ? ? ? avcodec_context->width,

?? ? ? ? ? ? ? ? ? ? ? ? avcodec_context->height,

?? ? ? ? ? ? ? ? ? ? ? ? 1);

? ? int y_size, u_size, v_size;

? ? //5.2 將yuv420p數(shù)據(jù)寫入.yuv文件中

? ? //打開寫入文件

? ? const char *outfile = [joutFilePath UTF8String];

? ? FILE* file_yuv420p = fopen(outfile,"wb+");

? ? if (file_yuv420p ==NULL){

? ? ? ?NSLog("輸出文件打開失敗");

? ? ? ? return;

? ? }

? ? int current_index =0;

? ? while (av_read_frame(avformat_context, packet) >=0){

? ? ? ? //>=:讀取到了

? ? ? ? //<0:讀取錯(cuò)誤或者讀取完畢

? ? ? ? //2、是否是我們的視頻流

? ? ? ? if (packet->stream_index == av_stream_index){

? ? ? ? ? ? //第七步:解碼

? ? ? ? ? ? //學(xué)習(xí)一下C基礎(chǔ),結(jié)構(gòu)體

? ? ? ? ? ? //3、解碼一幀壓縮數(shù)據(jù)->得到視頻像素?cái)?shù)據(jù)->yuv格式

? ? ? ? ? ? //采用新的API

? ? ? ? ? ? //3.1 發(fā)送一幀視頻壓縮數(shù)據(jù)

? ? ? ? ? ? avcodec_send_packet(avcodec_context, packet);

? ? ? ? ? ? //3.2 解碼一幀視頻壓縮數(shù)據(jù)->進(jìn)行解碼(作用:用于解碼操作)

? ? ? ? ? ? decode_result = avcodec_receive_frame(avcodec_context, avframe_in);

? ? ? ? ? ? if (decode_result ==0){

? ? ? ? ? ? ? ? //解碼成功

? ? ? ? ? ? ? ? //4、注意:在這里我們不能夠保證解碼出來的一幀視頻像素?cái)?shù)據(jù)格式是yuv格式

? ? ? ? ? ? ? ? //視頻像素?cái)?shù)據(jù)格式很多種類型: yuv420P、yuv422p、yuv444p等等...

? ? ? ? ? ? ? ? //保證:我的解碼后的視頻像素?cái)?shù)據(jù)格式統(tǒng)一為yuv420P->通用的格式

? ? ? ? ? ? ? ? //進(jìn)行類型轉(zhuǎn)換: 將解碼出來的視頻像素點(diǎn)數(shù)據(jù)格式->統(tǒng)一轉(zhuǎn)類型為yuv420P

? ? ? ? ? ? ? ? //sws_scale作用:進(jìn)行類型轉(zhuǎn)換的

? ? ? ? ? ? ? ? //參數(shù)一:視頻像素?cái)?shù)據(jù)格式上下文

? ? ? ? ? ? ? ? //參數(shù)二:原來的視頻像素?cái)?shù)據(jù)格式->輸入數(shù)據(jù)

? ? ? ? ? ? ? ? //參數(shù)三:原來的視頻像素?cái)?shù)據(jù)格式->輸入畫面每一行大小

? ? ? ? ? ? ? ? //參數(shù)四:原來的視頻像素?cái)?shù)據(jù)格式->輸入畫面每一行開始位置(填寫:0->表示從原點(diǎn)開始讀取)

? ? ? ? ? ? ? ? //參數(shù)五:原來的視頻像素?cái)?shù)據(jù)格式->輸入數(shù)據(jù)行數(shù)

? ? ? ? ? ? ? ? //參數(shù)六:轉(zhuǎn)換類型后視頻像素?cái)?shù)據(jù)格式->輸出數(shù)據(jù)

? ? ? ? ? ? ? ? //參數(shù)七:轉(zhuǎn)換類型后視頻像素?cái)?shù)據(jù)格式->輸出畫面每一行大小

? ? ? ? ? ? ? ? sws_scale(swscontext,

? ? ? ? ? ? ? ? ? ? ? ? ? (const uint8_t *const *)avframe_in->data,

? ? ? ? ? ? ? ? ? ? ? ? ? avframe_in->linesize,

? ? ? ? ? ? ? ? ? ? ? ? ? 0,

? ? ? ? ? ? ? ? ? ? ? ? ? avcodec_context->height,

? ? ? ? ? ? ? ? ? ? ? ? ? avframe_yuv420p->data,

? ? ? ? ? ? ? ? ? ? ? ? ? avframe_yuv420p->linesize);

? ? ? ? ? ? ? ? //方式一:直接顯示視頻上面去

? ? ? ? ? ? ? ? //方式二:寫入yuv文件格式

? ? ? ? ? ? ? ? //5、將yuv420p數(shù)據(jù)寫入.yuv文件中

? ? ? ? ? ? ? ? //5.1 計(jì)算YUV大小

? ? ? ? ? ? ? ? //分析一下原理?

? ? ? ? ? ? ? ? //Y表示:亮度

? ? ? ? ? ? ? ? //UV表示:色度

? ? ? ? ? ? ? ? //有規(guī)律

? ? ? ? ? ? ? ? //YUV420P格式規(guī)范一:Y結(jié)構(gòu)表示一個(gè)像素(一個(gè)像素對(duì)應(yīng)一個(gè)Y)

? ? ? ? ? ? ? ? //YUV420P格式規(guī)范二:4個(gè)像素點(diǎn)對(duì)應(yīng)一個(gè)(U和V: 4Y = U = V)

? ? ? ? ? ? ? ? y_size = avcodec_context->width * avcodec_context->height;

? ? ? ? ? ? ? ? u_size = y_size /4;

? ? ? ? ? ? ? ? v_size = y_size /4;

? ? ? ? ? ? ? ? //5.2 寫入.yuv文件

? ? ? ? ? ? ? ? //首先->Y數(shù)據(jù)

? ? ? ? ? ? ? ? fwrite(avframe_yuv420p->data[0], 1, y_size, file_yuv420p);

? ? ? ? ? ? ? ? //其次->U數(shù)據(jù)

? ? ? ? ? ? ? ? fwrite(avframe_yuv420p->data[1], 1, u_size, file_yuv420p);

? ? ? ? ? ? ? ? //再其次->V數(shù)據(jù)

? ? ? ? ? ? ? ? fwrite(avframe_yuv420p->data[2], 1, v_size, file_yuv420p);

? ? ? ? ? ? ? ? current_index++;

? ? ? ? ? ? ? ? NSLog("當(dāng)前解碼第%d幀", current_index);

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? //第八步:釋放內(nèi)存資源,關(guān)閉解碼器

? ? av_packet_free(&packet);

? ? fclose(file_yuv420p);

? ? av_frame_free(&avframe_in);

? ? av_frame_free(&avframe_yuv420p);

? ? free(out_buffer);

? ? avcodec_close(avcodec_context);

? ? avformat_free_context(avformat_context);

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 教程一:視頻截圖(Tutorial 01: Making Screencaps) 首先我們需要了解視頻文件的一些基...
    90后的思維閱讀 4,988評(píng)論 0 3
  • ##初始化 ``` av_register_all 編解碼初始化函數(shù),此函數(shù)無參數(shù)返回 avcodec_regi...
    嗨皮波士代閱讀 1,887評(píng)論 0 1
  • 通過上一篇文章iOS平臺(tái)FFmpeg開發(fā)(一)初識(shí)FFmpeg的學(xué)習(xí),我相信你已經(jīng)了解了視音頻的基礎(chǔ)知識(shí),并且把F...
    ZephyrCo閱讀 4,374評(píng)論 6 22
  • “小姐,小姐,你醒醒……”汝琪喚道。 暈乎乎的觀寒語困難的睜開眼睛。 “媽的,逼鬧什么?。俊?“小姐,你終于醒了,...
    千陌笙言閱讀 238評(píng)論 0 0
  • 一日正為件事發(fā)愁,朋友對(duì)我說,你的這些煩惱,全都因?yàn)槟闾阋?。我想了想,深以為然?如果別人認(rèn)為你很廉價(jià),那么他不...
    韋一的雜貨鋪閱讀 213評(píng)論 0 0

友情鏈接更多精彩內(nèi)容