[- 貳 FFmpeg4.2.1 -] 解碼decode-提取視頻數據h264和YUV

FFmpeg4.2.1 系列

都0202年了,本文基於FFmpeg4.2.1,將使用最新版的api。讓av_register_all()見鬼去吧!
FFmpeg的文章絕大多數都是3.X的,不少方法都過期了。對於喜新厭舊的潔癖君而言,滿屏飄黃的警告、運行一下全是過期的警告是多麼糟心。本文根據源碼中的exsample進行改編,刪繁就簡,對於判空,校驗返回值,打印錯誤什麼的,本身在使用時注意一下,自行處理。java


1. 講個小故事

爲了讓你明白這篇文章是在幹嗎,講個小故事先:ios

捷特有兩個護體神獸:白皇和黑皇    
白皇善鳴,聲震天地。身長千尺,振翅遮天蔽日。  
黑皇善視,目之所見錄於腦中,萬世不滅。身高萬丈,舉手可握雲擎天

像這種巨無霸級別的神獸是沒法隨身攜帶的。男主不被打到鎖血,是不會召喚出來的。
那如何凸顯主角的特別:動漫裏的橋段是萌化成兩個黑白服飾的漂亮妹子守護男主。
但兩我的在面前晃晃悠悠也很差帶回家,將兩者封印在一個掛墜中,使用時進行召喚。

打BOSS怎麼打的: 
捷特握着掛墜-->召喚黑白萌皇-->萌皇巨大化 --> 打Boss
複製代碼

上面的故事包含音視頻的數據概念:編程

很是大的原始數據: 音頻pcm --> 巨獸白皇
編碼後較小數據: 音頻aac --> 人型萌白皇

很是大的原始數據: 視頻YUV --> 巨獸黑皇
編碼後較小數據: 視頻h264 --> 人型萌黑皇

 mp四、ts、avi等封裝體格式: aac + h264 ---->封印掛墜
 
 播放器怎麼播放的: 
拆封封裝格式-->召喚尋找編碼流-->解碼流 --> 渲染呈現
複製代碼

這篇的目標是將掛墜(sh.ts)中的黑萌娘(sh.h264)召喚出來,而且轉換成神獸黑皇(sh_768x432.yuv)api

爲何說YUV是巨獸,看下面的數據就知道了。視頻3分30秒,YUV數據飆到2.48G。也許你會以爲爲何會有YUV這樣逆天的存在,其實渲染層須要YUV,給它壓縮後的數據人家不認識。播放時會先進行解碼, 你能在手機裏存幾百部片,都要感謝壓縮格式。bash


2. 最精簡代碼

這裏介紹主要的流程,這大白話的註釋你要再看不懂我也沒辦法了。ide

#include <iostream>

extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
};
using namespace std;

int main() {
    const char *rec_path = "/Volumes/coder/Projects/Media/TolyFF/res/sh.ts";
    AVFormatContext *fmt_ctx;//格式化上下文--召喚場地
    fmt_ctx = avformat_alloc_context();// 準備召喚場地

    avformat_open_input(&fmt_ctx, rec_path, nullptr, nullptr);//打開封印

    avformat_find_stream_info(fmt_ctx, nullptr);//找到召喚物

    int v_idx = av_find_best_stream(//尋找視頻流索引 -- 萌黑
            fmt_ctx, AVMEDIA_TYPE_VIDEO,-1, -1, nullptr, 0);

    AVCodecParameters *c_par;//聲明-召喚器參數
    AVCodecContext *cc_ctx;//聲明-召喚器環境
    const AVCodec *codec;//聲明-召喚器

    c_par = fmt_ctx->streams[v_idx]->codecpar;//實例化-召喚器參數
    codec = avcodec_find_decoder(c_par->codec_id);//實例化-召喚器

    //用參數c_par實例化編解碼器上下文,,並打開編解碼器
    cc_ctx = avcodec_alloc_context3(codec);//實例化-召喚器環境
    avcodec_parameters_to_context(cc_ctx, c_par);//召喚器環境參數填充
    avcodec_open2(cc_ctx, codec, nullptr);//打開召喚器

    AVPacket *pkt; //聲明 萌黑-數據包
    AVFrame *frame;//聲明 黑皇-幀
    
    pkt = av_packet_alloc();//準備 黑皇-幀
    frame = av_frame_alloc();//準備 萌黑-數據包

    FILE *dst_f=fopen("/Volumes/coder/Projects/Media/TolyFF/res/sh.h264","wb+");//萌黑實體sh.h264
    FILE *dst_f_yuv=fopen("/Volumes/coder/Projects/Media/TolyFF/res/sh_768x432.yuv","wb+");//黑皇實體sh_768x432.yuv

    while (av_read_frame(fmt_ctx, pkt) >= 0) {//持續讀幀
        if (pkt->stream_index == v_idx) {
            avcodec_send_packet(cc_ctx, pkt);//發送 萌黑-數據包
            fwrite(pkt->data,1,pkt->size,dst_f);// 萌黑-數據包拼合集結 
            
            vcodec_receive_frame(cc_ctx, frame);//接收解碼 -- 巨大化
            fwrite(frame->data[0],1,cc_ctx->width*cc_ctx->height,dst_f_yuv);// 黑皇Y-數據包拼合集結
            fwrite(frame->data[1],1,cc_ctx->width/2*cc_ctx->height/2,dst_f_yuv);// 黑皇U-數據包拼合集結
            fwrite(frame->data[2],1,cc_ctx->width/2*cc_ctx->height/2,dst_f_yuv);// 黑皇V-數據包拼合集結
        }
    }
    fclose(dst_f);// 萌黑OK , 拔掉鏈接頭
    fclose(dst_f_yuv);// 萌黑OK , 拔掉鏈接頭
    avcodec_free_context(&cc_ctx);//關閉環境
    av_frame_free(&frame);//釋放本體
    av_packet_free(&pkt);//釋放載體
}
複製代碼

運行後會生成h264和YUV兩個文件,經過ffplay能夠播放
二者分別是純視頻的壓縮流和原始流,因此播放起來是沒有聲音的。
你也許會問爲嘛要解碼出這兩個流,有一種神技叫作融合,有一種操做叫作變換。少年呦,你對於力量一無所知。post

ffplay sh.h264
ffplay -video_size 768x432 sh_768x432.yuv
複製代碼

2.過期的幾個方法與替換說明

下圖是新舊的示意圖:編碼

註冊方法,去了吧spa

attribute_deprecated
void av_register_all(void);
複製代碼

AVStream中的codec方法能夠獲取AVCodecContext,已過期。
取而代之的是codecpar的AVCodecParameters,在根據參數去建立上下文。如上3d

cc_ctx = fmt_ctx->streams[v_idx]->codec;

    /**
     * @deprecated use the codecpar struct instead
     */
    attribute_deprecated
    AVCodecContext *codec;
複製代碼

avcodec_decode_video2 方法被拆成了兩個:avcodec_send_packet()avcodec_receive_frame()分別用於包和幀的處理。透露一下packet用來召喚萌娘,frame用來激活巨獸。

* @deprecated Use avcodec_send_packet() and avcodec_receive_frame().
 */
attribute_deprecated
int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
                         int *got_picture_ptr,
                         const AVPacket *avpkt);
複製代碼

3.幾個主要的結構體

AVFormatContext:
封裝格式上下文,包含一些介紹信息,及最重要的碼流stream,這就是數據的源泉。AVInputFormat記錄了封裝格式的信息。


AVCodecParameters:
編解碼器的參數,從AVStream的codecpar獲取,取代codec屬性。能夠獲取流的參數,如視頻流寬、高、編解碼器類型、編解碼器id等。


AVCodecContext:
編解碼器的上下文,可經過avcodec_parameters_to_context使用AVCodecParameters進行參數填充。也是記錄着視頻流的信息,不一樣的是他包含了編解碼器對象codec。


AVCodec:
編解碼器的上下文,至關於大古的神光棒,能讓大古變成光的東西。


AVPacket: 編碼後的碼流,對應現視頻,data字段也就是以及壓縮後的h264數據。初次以外還有其餘信息:


AVFrame:
解碼後的原始流,YUV份量合併可造成巨大的YUV神獸。


@張風捷特烈 2019.11.26 未允禁轉
個人公衆號:編程之王
~ END ~

相關文章
相關標籤/搜索