1. 程式人生 > >多媒體開發(18):FFmpeg的常見結構體

多媒體開發(18):FFmpeg的常見結構體

除了之前講的avpacket跟avframe,FFmpeg還有其它一些結構經常在流程中出現。FFmpeg還有哪些常見的結構呢?先來看一下這個截圖:

這張圖中的主角,是AVFormatContext。AVFormatContext是FFmpeg的基本結構之一,對應於封裝格式(或容器格式)。

圍繞FFmpeg的“格式場景”,本文介紹FFmpeg常見的資料結構。按照上圖,小程依次介紹圖中的幾個結構體。

(一)AVCodec

AVCodec是FFmpeg設計上的一個結構體,用來儲存編解碼器的資訊,也就是說,AVCodec是編碼器或解碼器。

還是以除錯的辦法,具體看一下AVCodec變數中的內容吧。

(1)演示程式碼

演示程式碼的目錄結構是這樣的:

其中的FFmpeg靜態庫是事先編譯好的(這裡是macos版本,因為我現在用的是mac電腦),編譯的辦法可以參考之前的文章。

moments.mp4 是試用的視訊檔案(mp4封裝格式)。

makefile是編譯指令碼,用來編譯演示程式碼,當然也可以直接用gcc來編譯。

show_avcodec.c就是演示程式碼了,內容如下:

#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"

void show_avcodec(const char* filepath) {
    av_register_all();
    av_log_set_level(AV_LOG_DEBUG);
    AVFormatContext* formatContext = avformat_alloc_context();
    int status = 0;
    int success = 0;
    int videostreamidx = -1;
    AVCodecContext* codecContext = NULL;
    status = avformat_open_input(&formatContext, filepath, NULL, NULL);
    if (status == 0) {
        status = avformat_find_stream_info(formatContext, NULL);
        if (status >= 0) {
            for (int i = 0; i < formatContext->nb_streams; i ++) {
                if (formatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
                    videostreamidx = i;
                    break;
                }
            }
            if (videostreamidx > -1) {
                codecContext = formatContext->streams[videostreamidx]->codec;
                AVCodec* codec = avcodec_find_decoder(codecContext->codec_id);
                if (codec) {
                    status = avcodec_open2(codecContext, codec, NULL);
                    if (status == 0) {
                        success = 1;
                    }
                }
            }
        }
        else {
            av_log(NULL, AV_LOG_DEBUG, "avformat_find_stream_info error\n");
        }
        avformat_close_input(&formatContext);
    }
    avformat_free_context(formatContext);
}

int main(int argc, char *argv[])
{
    show_avcodec("moments.mp4");
    return 0;
}

你已經看到一個常規的FFmpeg使用套路:open_input得到avformatcontext,然後find_stream_info得到avstream,再從avstream中找到avcodeccontext,然後找到avcodec,最後open開avcodec。

(2)編譯與除錯

makefile的內容:

exe=showavcodec
srcs=show_avcodec.c 
$(exe):$(srcs)
    gcc -o $(exe) $(srcs) -Iffmpeg/include/ -Lffmpeg -lffmpeg -liconv -lz -g
clean:
    rm -f $(exe) *.o

直接執行make來編譯,編譯後會生成符號表檔案即showavcodec.dSYM。

這裡只是簡單看一下AVCodec的內容,用gdb來除錯即可:

gdb showavcodec
b 25
r

在斷點的地方,看一下AVCodec變數中的值:

(3)AVCodec結構內容

AVCodec是編解碼器的結構體,在libavcodec/avcodec.h中定義。

在這個示例中,AVCodec是一個解碼器。

AVCodec結構中的一些變數,從它的名字或者FFmpeg詳細的註釋中,可以知道是什麼含義。

比如name是編解碼的名稱,而long_name就是長的名稱,等等。

在設計上,AVCodec是編解碼器的抽象,所以,編解碼器是有相應的具體實現的。

事實上,每一個編解碼器都有具體的實現。

比如h264的解碼器(libavcodec/h264.c):

比如mp3lame的編碼器(libavcodec/libmp3lame.c)

FFmpeg使用這些具體的編解碼器的實現,以完成編解碼等功能。

(二)AVCodecContext

AVCodecContext可以簡單理解為AVCodec的使用場景,而實際上AVCodecContext包括的內容,除了關聯AVCodec,還有其它資訊。

跟除錯AVCodec變數一樣,直接使用上面的演示程式碼就可以除錯AVCodecContext,部分程式碼如下 :

if (videostreamidx > -1) {
    codecContext = formatContext->streams[videostreamidx]->codec;
    AVCodec* codec = avcodec_find_decoder(codecContext->codec_id);
    if (codec) {
        status = avcodec_open2(codecContext, codec, NULL);
        if (status == 0) {
            success = 1;
        }
    }
}

同樣用gdb來除錯就可以了,在拿到codecContext後下斷點,可以看到AVCodecContext的部分內容如下:

其中有一些變數應該引起注意,比如:

width/height  視訊的寬與高
codec_id 編解碼器的id,根據它可以找到對應的編解碼器
extradata 對於h264編碼的視訊,儲存了pps與sps的引數資訊
profile 視訊編碼複雜等級
sample_rate 音訊的取樣率
channels 音訊的聲道數
sample_fmt 音訊的取樣格式

跟AVCodec一樣,AVCodecContext結構體在libavcodec/avcodec.h中定義。

(三)AVStream

上面介紹了AVCodec、AVCodecContext,現在介紹AVStream。

這三者的大概關係是這樣的:

AVStream對應音訊流、視訊流、字幕等媒體流。FFmepg以流的概念來封裝不同的媒體。

除錯AVStream的示例程式碼與編譯,可以檢視上面AVCodec除錯的介紹。大概如下:

下斷點,可以看到AVStream中的內容,比如:

AVStream中的一些變數:

index,流的索引
codec,流對應的avcodeccontext
time_base,時間基準(比例)
duration,流的時長
metadata,流的元資訊
nb_frames,流中幀的數量

AVStream結構,在libavformat/avformat.h中定義。

(四)AVFormatContext

AVFormatContext是主角,表示為格式的場景,對應於封裝格式(或容器格式)。

同樣,使用之前的示例程式碼,在avformat_open_input函式後下斷點:

可以檢視avformatcontext結構中的變數值:

AVFormatContext中的metadata記錄了多媒體檔案的一些資訊(比如作者、專輯之類),可以這樣取得裡面的資訊:

if (formatCtx->metadata) {
    AVDictionaryEntry *item = NULL;
    while((item = av_dict_get(formatCtx->metadata, "", item, AV_DICT_IGNORE_SUFFIX))){
        printf("key:%s value:%s \n", item->key, item->value);
    }

    // 或者這樣:
    AVDictionaryEntry *tag = NULL;
    tag = av_dict_get(formatCtx->metadata, "artist", NULL, 0);
    if (tag) {
        std::string artist = (char*)tag->value;
    }
}

AVFormatContext的一些變數說明:

iformat/oformat,輸入/輸出格式,在解複用(解封裝)或複用(封裝)時使用。
pb,輸入或輸出場景,提供資料操作介面(比如讀寫、seek等)。
nb_streams,流的個數(以流的方式來複用)。
streams,流的陣列。
filename,檔名。
start_time,流的起始時間,以AV_TIME_BASE為單位(除以AV_TIME_BASE轉為秒)。
duration,流的時長,以AV_TIME_BASE為單位。
bit_rate,位元率。
probesize,在檢測容器格式時,最大的探測大小,在avformat_open_input之前設定(或不設定使用預設值)。
max_analyze_duration,最大的分析資料的時長,在檢測編碼格式時使用,在avformat_find_stream_info前設定(或不設定),越大越耗時。
metadata,元資訊。

AVFormatContext結構,在libavformat/avformat.h中定義。

(五)AVIOContext

AVIOContext是輸入輸出資訊的結構體,它在FFmpeg結構體系中的位置是這樣的:

可以看到,AVIOContext是AVFormatContext的一個成員,叫作pb。

pb是提供資料的變數,既用於讀(解碼)也用於寫(編碼)。

(1)解碼時

在解碼時,pb提供解碼的原始資料,一般在呼叫avio_alloc_context建立aviocontext時,指定read與seek函式(自定義的實現,提供讀資料、跳轉位置的功能),然後把建立的aviocontext(即pb)直接設定給AVFormatContext。比如這樣:

pb = avio_alloc_context(readBuf, readBufLen, 0, this, myReadFunc, NULL, mySeekFunc);
mFormatCtx->pb = pb;

或者,在解碼時,這樣使用aviocontext:

mIOContext.read_packet = myReadFunc;
mIOContext.seek = mySeekFunc;
const int MAX_PRO_SIZE = 32*1024;
unsigned char* probuf = (unsigned char*)av_malloc(MAX_PRO_SIZE); 
mIOContext.buffer = probuf;
mIOContext.buf_ptr = probuf;
mIOContext.buffer_size = MAX_PRO_SIZE;
mIOContext.buf_end = probuf + MAX_PRO_SIZE;
mIOContext.max_packet_size = MAX_PRO_SIZE;
formatContext->pb = &mIOContext;

其中,函式myReadFunc與mySeekFunc,按照結構體AVIOContext中的格式說明(參照標頭檔案說明)來定義即可。

解碼時,pb的設定,要在avformat_open_input呼叫前完成。

(2)編碼時

在編碼寫檔案時,pb提供寫到檔案的資料(編碼後的資料,對應AVPacket),比如可以直接用avio_open2來開啟pb:

if (!(mFormatContext->flags & AVFMT_NOFILE)) {
    err = avio_open2(&mFormatContext->pb, url, AVIO_FLAG_WRITE, &mFormatContext->interrupt_callback, NULL);
    // ...
}

寫檔案時,可以用av_write_frame來寫入一個packet,也可以用avio_write往pb中寫入資料。

編碼寫檔案時,pb的設定,要在avformat_write_header呼叫前完成。

(3)AVIOContext的變數

這裡只列表一部分:

buffer,AVIOContext快取資料的buffer,起始地址。
buffer_size,buffer的大小。
buf_ptr,操作buffer的當前位置。
buf_end,資料的結束位置,有可能未到buffer的未端。
opaque,指向URLContext,提供讀、寫、seek等介面,可以讓它為空,從而使用自定義的介面。
read_packet/write_packet/seek,讀寫與seek的介面,可以在avio_alloc_context時指定,從而自定義。

AVIOContext結構體在libavformat/avio.h中定義。

至此,FFmpeg常見的幾個結構體就介紹完畢了。

總結一下,本文介紹了FFmpeg的常見結構體,包括AVFormatContext、AVIOContext、AVStream、AVCodecContext、AVCodec等,並且以除錯的方式查看了結構體的一些變數值。


相關推薦

多媒體開發18FFmpeg常見結構

除了之前講的avpacket跟avframe,FFmpeg還有其它一些結構經常在流程中出現。FFmpeg還有哪些常見的結構呢?先來看一下這個截圖: 這張圖中的主角,是AVFormatContext。AVFormatContext是FFmpeg的基本結構之一,對應於封裝格式(或容器格式)。 圍繞FFmpeg

多媒體開發8調試FFmpeg

run 包括 啟用 return tar.bz2 %d 參考 efi turn 編譯FFmpeg得到二進制文件,之後就是對二進制庫的調用,這時FFmpeg就像一個黑盒子。作為程序員,難道不想研究一下FFmpeg的具體實現?比如是怎麽拿到歌曲信息的、怎麽解碼的、怎麽推流的,等

多媒體開發15H264的常見概念

H264,是你常見的技術術語了吧。 那h264是什麼東西呢? H.264是視訊編碼標準,又是標準,得標準得天下啊。 在術語的拼寫上,小程以能理解為準。 本文介紹H264的常見概念。 預警,本文相對枯燥,你可隨時放棄閱讀。 (1)H264從哪裡來? 之前介紹媒體格式的概念時,有提到過國際標準化組織(ISO)

多媒體開發7編譯Android與iOS平臺的FFmpeg

編譯FFmpeg,一個古老的話題,但我還是介紹一遍,就當記錄。之前介紹怎麼給視訊新增水印時,就已經提到FFmpeg的編譯,並且在編譯時指定了濾鏡的功能。 但是,在手機盛行的時代,你可能更需要的是能在iOS或Android平臺上執行的FFmpeg,而對於命令列的ffmpeg,你可以在個人電腦上面使用(因為它簡

多媒體開發8除錯FFmpeg

編譯FFmpeg得到二進位制檔案,之後就是對二進位制庫的呼叫,這時FFmpeg就像一個黑盒子。作為程式設計師,難道不想研究一下FFmpeg的具體實現?比如是怎麼拿到歌曲資訊的、怎麼解碼的、怎麼推流的,等等。 看原始碼是理解程式碼實現的一個辦法,而單步除錯能從另一個維度去幫到你。**本文介紹如何單步除錯FFm

多媒體開發3直播

特點 nss ams 測試的 方式 input cat nginx 成功 之前介紹了如何錄制音視頻,以及相關的多媒體的概念。對於已經錄制的多媒體進行“就地”播放(參考前文),就是回放,除了“回放”這個流程,還有一個流程也會經常遇到,那就是“直播”。 本文介紹直播的實現。 “

多媒體開發6濾鏡實現各種圖片效果 | Video-Filters | 變色

命令行 let img 很多 保持 yuv 黑白 多媒體 ati 之前講過使用FFmpeg的drawtext濾鏡(把圖片或文字加到視頻上),而實際上,FFmpeg的濾鏡很強大,遠不止加字幕或加圖片的功能。濾鏡是很有趣的,可以把圖片變模糊、變色、縮放旋轉,等等。 本文介紹FF

多媒體開發9聲音采集的概念 | 振幅 | 頻率 | 共振 | 電平化

坐標 波形 上下 樣本 形狀 多少 為什麽不使用 dsd 運動 之前介紹通過ffmpeg程序來錄制聲音或圖像,這個辦法是一個操作的過程,很少涉及到概念上的東西。 而本文,要介紹的是聲音采集的一些流程與概念。 聲音的采集流程與概念,是枯燥的,你如果不想了解的話,到這裏就可以退

多媒體開發10從視訊中提取圖片

小白:提取視訊中的圖片嗎?那很簡單,播放視訊再截圖就行啦。 播放視訊再截圖的做法,當然也可以。但是,手動地截圖會太累而且無法保證準確度,特別是需要反覆提取圖片時,或者需要提取“105秒那一瞬間的美女圖片”時,或者我需要每秒出一張圖片時,那有別的辦法嗎? 本文介紹,如何使用FFmpeg實現從視訊中提取圖片的

多媒體開發11Android平臺上裁剪m4a

Android手機上設定鈴聲的操作比較靈活,你聽到一首喜歡的歌曲,馬上就可以對這首歌曲進行裁剪,裁剪到片段後,再通過系統的介面設定為鈴聲(電話鈴聲、鬧鐘鈴聲等)。前提是,播放這首歌的APP,需要提供裁剪歌曲的功能。 那麼,怎麼樣實現擷取音訊檔案的功能呢? 基於之前的介紹,你可能很自然就想到使用FFmpeg命令

多媒體開發12解碼aac到wav檔案

簡單來說,aac是一種音訊編碼格式,需要解碼後才能用於音訊輸出。aac編碼格式,已經是一種很常見的音訊編碼格式,以至於很多系統都支援aac的編解碼,比如iOS上的AudioConverterRef介面、Android上的MediaCodec介面等。 但是,不要以為用了系統的介面就是用了硬體解碼,因為,這個系統

多媒體開發14媒體格式的概念

之前講了一些音視訊的錄製操作,還有聲音採集的概念。採集只是多媒體操作流程中的一個環節,更多的環節可以看看這個圖: 聲音或視訊採集後,就是編碼、寫檔案或推流。不管是編碼還是寫“檔案”,你都能找到相應的程式(比如FFmpeg)來完成,一般加上自己的業務程式碼就能實現自己的功能需求。那就沒有東西好說的了? 沒東

多媒體開發16幀率與位元速率的概念

為什麼說音視訊開發入門較難,因為涉及到很多概念,之前還專門講“媒體格式”、“h264概念”的東西。現在又來,“幀率”跟“位元速率”,這也是兩個常見的概念。你應該經常聽到“重新整理的幀率是多少”或“位元速率比較高所以網速要比較快”的表達吧。 本文介紹音視訊的幀率與位元速率的概念。 (1)幀率 幀率,表示的是頻率

多媒體開發2錄製視訊

上一節介紹了用ffplay來播放檔案(或url),這裡有一個概念,如果是播放已經存在的檔案,那叫“回放”,也就是Playback(從流媒體的角度也叫點播),如果播放的是正在錄製的資料(邊錄邊播),那叫直播。 不管是回放還是直播,都需要有媒體資料,那這個媒體資料是怎麼來的呢?從已有的檔案編輯而來是一個辦法,但

多媒體開發6用濾鏡實現各種圖片效果

之前講過使用FFmpeg的drawtext濾鏡(把圖片或文字加到視訊上),而實際上,FFmpeg的濾鏡很強大,遠不止加字幕或加圖片的功能。濾鏡很有趣,可以把圖片變模糊、變色、縮放旋轉,等等。 **本文介紹FFmpeg濾鏡的使用。目的是讓你感受一下FFmepg的濾鏡效果,這樣在實際需要某種效果時,可以考慮使用

多媒體開發9我是聲音

之前介紹通過ffmpeg程式來錄製聲音或影象,這個辦法是一個操作的過程,很少涉及到概念上的東西。而**本文,要介紹的是聲音採集的一些流程與概念。** 聲音的採集流程與概念,是枯燥的,但是,我也會盡量說一些有趣的現象來緩解這種枯燥。 聽得到的,或聽不到的聲音,抽象來說,都是模擬訊號,也可以形象一點,叫能量波

多媒體開發10提取圖片以及點陣圖儲存

> 小白:提取視訊中的圖片嗎?那很簡單,播放視訊再截圖就行啦。 播放視訊再截圖的做法,當然可以。但是,手動截圖會太累而且無法保證準確度,特別是需要反覆提取圖片時,或者需要提取“105秒那一瞬間的美女圖片”時,或者我需要每秒出一張圖片時,那有別的辦法嗎? **本文介紹,如何使用FFmpeg實現從視訊中

小白學開發iOSOC_ 經常使用結構2015-08-14

轉換成 tracking 小白 epo idt rgb 表示範圍 dsm tor // // main.m // 經常使用結構體 // // Created by admin on 15/8/13. // Copyright (c) 2015年 admin.

即時通訊音視訊開發常見的實時語音通訊編碼標準

前言 即時通訊應用中的實時音視訊技術,幾乎是IM開發中的最後一道高牆。原因在於:實時音視訊技術 = 音視訊處理技術 + 網路傳輸技術 的橫向技術應用集合體,而公共網際網路不是為了實時通訊設計的。 系列文章 《即時通訊音視訊開發(六):如何開始音訊編解碼技術的學習》 《即時通

Android多媒體開發2————使用Android NKD編譯原版FFmpeg

/********************************************************************************************  * author:[email protected]大鐘