音視頻開發(fā)之旅(61)- 調(diào)試分析FFmpeg (解碼部分的)常用結(jié)構(gòu)體

上一篇我們分析了解封裝部分的常用結(jié)構(gòu)體,這篇我們來(lái)學(xué)習(xí)分析解碼部分的常用結(jié)構(gòu)體。

目錄

  1. 斷點(diǎn)分析ffplay解碼流程及關(guān)鍵結(jié)構(gòu)體
  2. (解碼部分)常用結(jié)構(gòu)體以及之間的關(guān)系分析
  3. 資料
  4. 收獲

一、斷點(diǎn)分析ffplay解碼流程及關(guān)鍵結(jié)構(gòu)體

還是從read_thread進(jìn)行分析

        stream_component_open(is, st_index[AVMEDIA_TYPE_AUDIO]);
        ret = stream_component_open(is, st_index[AVMEDIA_TYPE_VIDEO]);

stream_component_open 打開指定的流

static int stream_component_open(VideoState *is, int stream_index)

AVFormatContext *ic = is->ic;
AVCodecContext *avctx;
const AVCodec *codec;

其中的關(guān)鍵函數(shù)如下 
avctx = avcodec_alloc_context3(NULL);
int avcodec_parameters_to_context(AVCodecContext *codec,
                                  const AVCodecParameters *par)
codec = avcodec_find_decoder(avctx->codec_id);
codec = avcodec_find_decoder_by_name(forced_codec_name);
//打開codec
avcodec_open2(avctx, codec, &opts)

//音頻解碼
decoder_init(&is->auddec, avctx, &is->audioq, is->continue_read_thread)
decoder_start(&is->auddec, audio_thread, "audio_decoder", is)

//視頻解碼
decoder_init(&is->viddec, avctx, &is->videoq, is->continue_read_thread)
decoder_start(&is->viddec, video_thread, "video_decoder", is)

avcodec_free_context(&avctx);

涉及到結(jié)構(gòu)體:AVFormatContext、AVCodecContext、AVCodec

avcodec_parameters_to_context

int avcodec_parameters_to_context(AVCodecContext *codec,
                                  const AVCodecParameters *par)

涉及到結(jié)構(gòu)體:AVCodecContext、AVCodecParameters

avcodec_find_decoder通過(guò)codecid查找解碼器

const AVCodec *avcodec_find_decoder(enum AVCodecID id)

涉及到結(jié)構(gòu)體:AVCodec

avcodec_open2

int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)

涉及到結(jié)構(gòu)體:AVCodecContext、AVCodec

解碼線程

static int audio_thread(void *arg)
static int video_thread(void *arg)

解碼線程中以AVFrame作為解壓縮后的結(jié)構(gòu)體

涉及到結(jié)構(gòu)體:AVFrame

然后分別開啟音頻和視頻的解碼線程開始解碼。我們可以看到涉及的主要結(jié)構(gòu)體有AVCodecContext 、AVCodecParameters 、AVCodec 、AVFrame

作為解碼的輸入AVPacket(存儲(chǔ)壓縮編碼數(shù)據(jù)相關(guān)信息的結(jié)構(gòu)體)以及AVStream(每個(gè)AVStream存儲(chǔ)一個(gè)視頻/音頻流的相關(guān)數(shù)據(jù);是解封裝器分離出來(lái)的流對(duì)象)我們上一篇已經(jīng)介紹過(guò),下面我們來(lái)主要分析解碼相關(guān)的幾個(gè)結(jié)構(gòu)體AVCodecContext 、AVCodec 、AVFrame

二、(解碼部分)常用結(jié)構(gòu)體以及之間的關(guān)系分析

2.1 常用結(jié)構(gòu)體以及之間的關(guān)系(再引用一次,雷神總結(jié)梳理的太好了)

FFMPEG中結(jié)構(gòu)體很多。最關(guān)鍵的結(jié)構(gòu)體可以分成以下幾類:

a)        解協(xié)議(http,rtsp,rtmp,mms)

AVIOContext,URLProtocol,URLContext主要存儲(chǔ)視音頻使用的協(xié)議的類型以及狀態(tài)。URLProtocol存儲(chǔ)輸入視音頻使用的封裝格式。每種協(xié)議都對(duì)應(yīng)一個(gè)URLProtocol結(jié)構(gòu)。(注意:FFMPEG中文件也被當(dāng)做一種協(xié)議“file”)

b)        解封裝(flv,avi,rmvb,mp4)

AVFormatContext主要存儲(chǔ)視音頻封裝格式中包含的信息;AVInputFormat存儲(chǔ)輸入視音頻使用的封裝格式。每種視音頻封裝格式都對(duì)應(yīng)一個(gè)AVInputFormat 結(jié)構(gòu)。

c)        解碼(h264,mpeg2,aac,mp3)

每個(gè)AVStream存儲(chǔ)一個(gè)視頻/音頻流的相關(guān)數(shù)據(jù);每個(gè)AVStream對(duì)應(yīng)一個(gè)AVCodecContext,存儲(chǔ)該視頻/音頻流使用解碼方式的相關(guān)數(shù)據(jù);每個(gè)AVCodecContext中對(duì)應(yīng)一個(gè)AVCodec,包含該視頻/音頻對(duì)應(yīng)的解碼器。每種解碼器都對(duì)應(yīng)一個(gè)AVCodec結(jié)構(gòu)。

d) 存數(shù)據(jù)

視頻的話,每個(gè)結(jié)構(gòu)一般是存一幀;音頻可能有好幾幀

解碼前數(shù)據(jù):AVPacket

解碼后數(shù)據(jù):AVFrame


引用自: https://blog.csdn.net/leixiaohua1020/article/details/11693997

他們之間的關(guān)系如下:


FFMPEG中最關(guān)鍵的結(jié)構(gòu)體之間的關(guān)系

2.2 AVCodecContext
AVCodecContext是一個(gè)描述解碼器上下文的數(shù)據(jù)結(jié)構(gòu),包含了很多編碼器需要的參數(shù)信息。
該結(jié)構(gòu)體定義位于libavcodec/AVcodec.h中,主要變量如下:

enum AVMediaType codec_type; // 該枚舉定義在libavutil/Avutil.h中,編碼的類型,音頻、視頻、字母等 
const struct AVCodec  *codec;//采用的解碼器AVCodec,下面單獨(dú)分析
enum AVCodecID     codec_id; // 該枚舉定義在libavcodec/Codec_id.h中,定了一了所有的編解碼器id 
void *priv_data;
struct AVCodecInternal *internal;//內(nèi)部使用的上下文環(huán)境
void *opaque;
int64_t bit_rate;//平均碼率
uint8_t *extradata; int extradata_size:針對(duì)特定編碼器包含的附加信息(例如對(duì)于H.264解碼器來(lái)說(shuō),存儲(chǔ)SPS,PPS等)

AVRational time_base;//時(shí)間基,根據(jù)該參數(shù),可以把PTS轉(zhuǎn)化為實(shí)際的時(shí)間(單位為秒s)
int width, height;//僅視頻類型用
int gop_size;//關(guān)鍵幀間隔
int max_b_frames;//最大b幀數(shù)量
int has_b_frames;//是否有b幀,關(guān)系到視頻的壓縮比率,一般b幀越多壓縮比越大
int slice_count;//片的總和,關(guān)于slice相關(guān),可以[音視頻開發(fā)之旅(56) -H264/AVC基本結(jié)構(gòu)](https://mp.weixin.qq.com/s?__biz=MzU5NjkxMjE5Mg==&mid=2247484355&idx=1&sn=538378561c16b640a4ea42bc1f354044&chksm=fe5a32ecc92dbbfa1d6a2e83f22aece727badb99966b6e621322ed8bf6b0cd8f0b2d1c262013&token=778944351&lang=zh_CN#rd)
AVRational sample_aspect_ratio;//采樣率


  /* audio only */
int sample_rate; ///< samples per second 音頻采樣率
int channels;    ///< number of audio channels 通道數(shù)
enum AVSampleFormat sample_fmt;  ///< sample format 采樣格式,定義在libavutil/Samplefmt.h中

enum AVColorSpace colorspace;//顏色空間,定義在libavutil/Pixfmt.h 
AVRational framerate;//幀率
enum AVPixelFormat sw_pix_fmt;//像素格式,如yuv420pdeng ,定義在libavutil/Pixfmt.h 如果設(shè)置不對(duì)導(dǎo)致解碼器無(wú)法正常解碼會(huì)出現(xiàn)花屏的情況。

這個(gè)結(jié)構(gòu)體涉及的變量很多,并且很多是和編碼相關(guān),解碼用的的其實(shí)并不算太多。

2.3 AVCodec
AVCodec是存儲(chǔ)編碼器信息的結(jié)構(gòu)體。
該結(jié)構(gòu)體定義位于libavcodec/Codec.h中,主要變量如下:

const char *name;//codec的名稱
enum AVMediaType type;//codec的類型,AVCodecContext中已經(jīng)介紹
enum AVCodecID id;//codec的id,AVCodecContext中已經(jīng)介紹
int capabilities;//編解碼的能力,見 AV_CODEC_CAP_
const AVRational *supported_framerates;//支持的幀率
const enum AVPixelFormat *pix_fmts;//支持的像素格式
const int *supported_samplerates; //支持的音頻采樣率
const enum AVSampleFormat *sample_fmts;//支持的采樣格式

下面是一些函數(shù)指針
int (*init)(struct AVCodecContext *);//初始化
int (*encode2)(struct AVCodecContext *avctx, struct AVPacket *avpkt,const struct AVFrame *frame, int *got_packet_ptr);//編碼
int (*decode)(struct AVCodecContext *avctx, void *outdata,
                  int *got_frame_ptr, struct AVPacket *avpkt);
int (*close)(struct AVCodecContext *);//解碼
int (*receive_packet)(struct AVCodecContext *avctx, struct AVPacket *avpkt);//接收packet數(shù)據(jù)
int (*receive_frame)(struct AVCodecContext *avctx, struct AVFrame *frame);//接收f(shuō)rame數(shù)據(jù)
void (*flush)(struct AVCodecContext *);//刷新緩沖區(qū)

2.4 AVFrame
AVFrame一般用于存儲(chǔ)原始數(shù)據(jù)(即非壓縮數(shù)據(jù),例如對(duì)于視頻而言YUV、RGB,對(duì)于音頻而言 PCM),此外還包含了一些相關(guān)的信息。比如說(shuō),解碼的時(shí)候存儲(chǔ)了宏塊類型表,QP表,運(yùn)動(dòng)矢量表等數(shù)據(jù)。編碼的時(shí)候也存儲(chǔ)了相關(guān)的數(shù)據(jù)。
該結(jié)構(gòu)體定義位于libavutil/Frame.h中,主要變量如下:

#define AV_NUM_DATA_POINTERS 8
uint8_t *data[AV_NUM_DATA_POINTERS];//對(duì)于planar格式的數(shù)據(jù)(例如YUV420P),則會(huì)分開成data[0],data[1],data[2]...(YUV420P中data[0]存Y,data[1]存U,data[2]存V)
int width, height;//視頻寬高
int nb_samples;//每個(gè)信道音頻采樣點(diǎn)的個(gè)數(shù)
int format;//幀的像素格式
int key_frame;//1 -> keyframe, 0-> not
enum AVPictureType pict_type;//定義在libavutil/AVutil.h中,該幀的類型,I、P、B等
AVRational sample_aspect_ratio;//寬高比(16:9,4:3...)FFMPEG中用AVRational表達(dá)分?jǐn)?shù):
int64_t pts;//顯示時(shí)間戳
int64_t pkt_dts;//從packet復(fù)制的PTS
int quality;
void *opaque;
int coded_picture_number;//編碼幀序號(hào)
int display_picture_number;//顯示幀序號(hào)
int8_t *qscale_table;//QP表 QP表指向一塊內(nèi)存,里面存儲(chǔ)的是每個(gè)宏塊的QP值。宏塊的標(biāo)號(hào)是從左往右,一行一行的來(lái)的。每個(gè)宏塊對(duì)應(yīng)1個(gè)QP。
uint8_t *mbskip_table;//跳過(guò)宏塊表
int16_t (*motion_val[2])[2];//運(yùn)動(dòng)矢量表
int8_t *ref_index[2];//運(yùn)動(dòng)估計(jì)參考幀列表
int interlaced_frame;//交錯(cuò)幀,表示圖像內(nèi)容是交錯(cuò)的,即是否是隔行掃描
int sample_rate;//音頻采樣率
uint8_t motion_subsample_log2;//一個(gè)宏塊中的運(yùn)動(dòng)矢量采樣個(gè)數(shù),取log2的.1個(gè)運(yùn)動(dòng)矢量所能代表的畫面大?。ㄓ脤捇蛘吒弑硎?,單位是像素),注意,這里取了log2。

三、資料

  1. 《Android音視頻開發(fā)》-第八章
  2. FFMPEG中最關(guān)鍵的結(jié)構(gòu)體之間的關(guān)系
  3. FFMPEG結(jié)構(gòu)體分析:AVCodecContext
  4. FFMPEG結(jié)構(gòu)體分析:AVCodec
  5. FFMPEG結(jié)構(gòu)體分析:AVFrame
  6. FFMPEG 實(shí)現(xiàn) YUV,RGB各種圖像原始數(shù)據(jù)之間的轉(zhuǎn)換(swscale)

四、收獲

通過(guò)本篇的學(xué)習(xí)實(shí)踐,收獲如下:

  1. 斷點(diǎn)分析解碼的流程加深理解
  2. 重溫ffmpeg重要結(jié)構(gòu)體之間的關(guān)系
  3. 了解了解碼相關(guān)的結(jié)構(gòu)體 AVCodecContext、AVCodec、AVFrame. 其中設(shè)置到很多編碼協(xié)議相關(guān)知識(shí),需要系統(tǒng)性的在進(jìn)行學(xué)習(xí)(X265)

感謝你的閱讀
下一篇我們學(xué)習(xí)知識(shí)fflay關(guān)鍵結(jié)構(gòu)體的最后一篇,解協(xié)議相關(guān)結(jié)構(gòu)體,歡迎關(guān)注公眾號(hào)“音視頻開發(fā)之旅”,一起學(xué)習(xí)成長(zhǎng)。
歡迎交流

最后編輯于
?著作權(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)容

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