FFMPEG解碼流程:
1. 注冊所有容器格式和CODEC: av_register_all() 2. 打開文件: av_open_input_file() 3. 從文件中提取流信息: av_find_stream_info() 4. 窮舉所有的流,,查找其中種類為CODEC_TYPE_VIDEO 5. 查找對應(yīng)的解碼器: avcodec_find_decoder() 6. 打開編解碼器: avcodec_open() 7. 為解碼幀分配內(nèi)存: avcodec_alloc_frame() 8. 不停地從碼流中提取出幀數(shù)據(jù): av_read_frame() 9. 判斷幀的類型,對于視頻幀調(diào)用: avcodec_decode_video() 10. 解碼完后,,釋放解碼器: avcodec_close() 11. 關(guān)閉輸入文件: avformat_close_input_file() 主要數(shù)據(jù)結(jié)構(gòu): 基本概念: 編解碼器,、數(shù)據(jù)幀,、媒體流和容器是數(shù)字媒體處理系統(tǒng)的四個基本概念。 首先需要統(tǒng)一術(shù)語: 容器/文件(Conainer/File):即特定格式的多媒體文件,。 媒體流(Stream):指時間軸上的一段連續(xù)數(shù)據(jù),,如一段聲音數(shù)據(jù),一段視頻數(shù)據(jù)或一段字幕數(shù)據(jù),,可以是壓縮的,,也可以是非壓縮的,壓縮的數(shù)據(jù)需要關(guān)聯(lián)特定的編解碼器,。 數(shù)據(jù)幀/數(shù)據(jù)包(Frame/Packet):通常,,一個媒體流由大量的數(shù)據(jù)幀組成,對于壓縮數(shù)據(jù),,幀對應(yīng)著編解碼器的最小處理單元,。通常,分屬于不同媒體流的數(shù)據(jù)幀交錯復(fù)用于容器之中,,參見交錯,。 編解碼器:編解碼器以幀為單位實現(xiàn)壓縮數(shù)據(jù)和原始數(shù)據(jù)之間的相互轉(zhuǎn)換。 在FFMPEG中,,使用AVFormatContext、AVStream,、AVCodecContext,、AVCodec及AVPacket等結(jié)構(gòu)來抽象這些基本要素,它們的關(guān)系如上圖所示: AVCodecContext: 這是一個描述編解碼器上下文的數(shù)據(jù)結(jié)構(gòu),,包含了眾多編解碼器需要的參數(shù)信息,,如下列出了部分比較重要的域: typedef struct AVCodecContext { / ** *一些編解碼器需要/可以像使用extradata Huffman表。 * MJPEG:Huffman表 * RV10其他標(biāo)志 * MPEG4:全球頭(也可以是在比特流或這里) *分配的內(nèi)存應(yīng)該是FF_INPUT_BUFFER_PADDING_SIZE字節(jié)較大 *,,比extradata_size避免比特流器,,如果它與讀prolems。 * extradata按字節(jié)的內(nèi)容必須不依賴于架構(gòu)或CPU的字節(jié)順序,。 * - 編碼:設(shè)置/分配/釋放由libavcodec的,。 * - 解碼:由用戶設(shè)置/分配/釋放。 * / uint8_t *extradata; int extradata_size; / ** *這是時間的基本單位,,在條件(以秒為單位) *幀時間戳派代表出席了會議,。對于固定fps的內(nèi)容, *基應(yīng)該1/framerate和時間戳的增量應(yīng)該 *相同的1,。 * - 編碼:必須由用戶設(shè)置,。 * - 解碼:libavcodec的設(shè)置。 * / AVRational time_base; /*視頻* / / ** *圖片寬度/高度,。 * - 編碼:必須由用戶設(shè)置,。 * - 解碼:libavcodec的設(shè)置,。 *請注意:兼容性,它是可能的,,而不是設(shè)置此 * coded_width/高解碼之前,。 * / int width, height; ...... / *僅音頻* / int sample_rate; ///< 每秒采樣 int channels; ///< 音頻通道數(shù) / ** *音頻采樣格式 * - 編碼:由用戶設(shè)置。 * - 解碼:libavcodec的設(shè)置,。 * / enum SampleFormat sample_fmt; ///< 樣本格式 / *下面的數(shù)據(jù)不應(yīng)該被初始化,。* / / ** *每包樣品,初始化時調(diào)用“init”,。 * / int frame_size; int frame_number; ///<音頻或視頻幀數(shù)量 char codec_name[32]; enum AVMediaType codec_type; /* 看到AVMEDIA_TYPE_xxx */ enum CodecID codec_id; /* see CODEC_ID_xxx */ / ** *的fourcc(LSB在前,,所以“的ABCD” - >(“D”<< 24)(“C”<< 16)(“B”<< 8)+“A”)。 *這是用來解決一些編碼錯誤,。 *分路器應(yīng)設(shè)置什么是編解碼器用于識別領(lǐng)域中,。 *如果有分路器等多個領(lǐng)域,在一個容器,,然后選擇一個 *最大化使用的編解碼器有關(guān)的信息,。 *如果在容器中的編解碼器標(biāo)記字段然后32位大分路器應(yīng)該 *重新映射到一個表或其他結(jié)構(gòu)的32位編號。也可選擇新 * extra_codec_tag+大小可以添加,,但必須證明這是一個明顯的優(yōu)勢 *第一,。 * - 編碼:由用戶設(shè)置,如果沒有則默認(rèn)基礎(chǔ)上codec_id將使用,。 * - 解碼:由用戶設(shè)置,,將被轉(zhuǎn)換成在初始化libavcodec的大寫。 * / unsigned int codec_tag; ...... / ** *在解碼器的幀重排序緩沖區(qū)的大小,。 *對于MPEG-2,,這是IPB1或0低延時IP。 * - 編碼:libavcodec的設(shè)置,。 * - 解碼:libavcodec的設(shè)置,。 * / int has_b_frames; / ** *每包的字節(jié)數(shù),如果常量和已知或0 *用于一些WAV的音頻編解碼器,。 * / int block_align; / ** *從分路器位每個樣品/像素(huffyuv需要),。 * - 編碼:libavcodec的設(shè)置。 * - 解碼:由用戶設(shè)置,。 * / int bits_per_coded_sample; ..... } AVCodecContext; 如果是單純使用libavcodec,,這部分信息需要調(diào)用者進(jìn)行初始化;如果是使用整個FFMPEG庫,,這部分信息在調(diào)用avformat_open_input和avformat_find_stream_info的過程中根據(jù)文件的頭信息及媒體流內(nèi)的頭部信息完成初始化,。其中幾個主要域的釋義如下: extradata/extradata_size:這個buffer中存放了解碼器可能會用到的額外信息,在av_read_frame中填充,。一般來說,,首先,,某種具體格式的demuxer在讀取格式頭信息的時候會填充extradata,其次,,如果demuxer沒有做這個事情,,比如可能在頭部壓根兒就沒有相關(guān)的編解碼信息,則相應(yīng)的parser會繼續(xù)從已經(jīng)解復(fù)用出來的媒體流中繼續(xù)尋找,。在沒有找到任何額外信息的情況下,,這個buffer指針為空。 time_base: width/height:視頻的寬和高,。 sample_rate/channels:音頻的采樣率和信道數(shù)目,。 sample_fmt: 音頻的原始采樣格式。 codec_name/codec_type/codec_id/codec_tag:編解碼器的信息,。 AVStrea 該結(jié)構(gòu)體描述一個媒體流,,定義如下: typedef struct AVStream { int index; /** <在AVFormatContext流的索引* / int id; /**< 特定格式的流ID */ AVCodecContext *codec; /**< codec context */ / ** *流的實時幀率基地。 *這是所有時間戳可以最低幀率 *準(zhǔn)確代表(它是所有的最小公倍數(shù) *流的幀率),。請注意,,這個值只是一個猜測! *例如,,如果時間基數(shù)為1/90000和所有幀 *約3600或1800計時器刻度,,,然后r_frame_rate將是50/1,。 * / AVRational r_frame_rate; / ** *這是時間的基本單位,,在條件(以秒為單位) *幀時間戳派代表出席了會議。對于固定fps的內(nèi)容,, *時基應(yīng)該是1/framerate的時間戳的增量應(yīng)為1。 * / AVRational time_base; ...... / ** *解碼流量的第一幀,,在流量時-base分,。 *如果你是絕對100%的把握,設(shè)定值 *它真的是第一幀點,。 *這可能是未定義(AV_NOPTS_VALUE)的,。 *@注意的業(yè)余頭不弱者受制與正確的START_TIME的業(yè)余 *分路器必須不設(shè)定此。 * / int64_t start_time; / ** *解碼:時間流流時基,。 *如果源文件中沒有指定的時間,,但不指定 *比特率,這個值將被從碼率和文件大小的估計,。 * / int64_t duration; #if LIBAVFORMAT_VERSION_INT < (53<<16) char language[4]; /** ISO 639-2/B 3-letter language code (empty string if undefined) */ #endif /* av_read_frame()支持* / enum AVStreamParseType need_parsing; struct AVCodecParserContext *parser; ..... /*函數(shù)av_seek_frame()支持* / AVIndexEntry *index_entries; / **<僅用于如果格式不notsupport尋求本身,。* / int nb_index_entries; unsigned int index_entries_allocated_size; int64_t nb_frames; ///< 在此流的幀,如果已知或0 ...... //*平均幀率 AVRational avg_frame_rate; ...... } AVStream; 主要域的釋義如下,,其中大部分域的值可以由avformat_open_input根據(jù)文件頭的信息確定,,缺少的信息需要通過調(diào)用avformat_find_stream_info讀幀及軟解碼進(jìn)一步獲?。?br> index/id:index對應(yīng)流的索引,這個數(shù)字是自動生成的,,根據(jù)index可以從AVFormatContext::streams表中索引到該流,;而id則是流的標(biāo)識,依賴于具體的容器格式,。比如對于MPEG TS格式,,id就是pid。 time_base:流的時間基準(zhǔn),,是一個實數(shù),,該流中媒體數(shù)據(jù)的pts和dts都將以這個時間基準(zhǔn)為粒度。通常,,使用av_rescale/av_rescale_q可以實現(xiàn)不同時間基準(zhǔn)的轉(zhuǎn)換,。 start_time:流的起始時間,以流的時間基準(zhǔn)為單位,,通常是該流中第一個幀的pts,。 duration:流的總時間,以流的時間基準(zhǔn)為單位,。 need_parsing:對該流parsing過程的控制域,。 nb_frames:流內(nèi)的幀數(shù)目。 r_frame_rate/framerate/avg_frame_rate:幀率相關(guān),。 codec:指向該流對應(yīng)的AVCodecContext結(jié)構(gòu),,調(diào)用avformat_open_input時生成。 parser:指向該流對應(yīng)的AVCodecParserContext結(jié)構(gòu),,調(diào)用avformat_find_stream_info時生成,。。 AVFormatContext 這個結(jié)構(gòu)體描述了一個媒體文件或媒體流的構(gòu)成和基本信息,,定義如下: typedef struct AVFormatContext { const AVClass *av_class; /**<由avformat_alloc_context設(shè)置的,。* / / *只能是iFormat的,或在同一時間oformat,,不是兩個,。* / struct AVInputFormat *iformat; struct AVOutputFormat *oformat; void *priv_data; ByteIOContext *pb; unsigned int nb_streams; AVStream *streams[MAX_STREAMS]; char filename[1024]; / **<輸入或輸出的文件名*/ / *流信息* / int64_t timestamp; #if LIBAVFORMAT_VERSION_INT < (53<<16) char title[512]; char author[512]; char copyright[512]; char comment[512]; char album[512]; int year; /**< ID3 year, 0 if none */ int track; /**< track number, 0 if none */ char genre[32]; /**< ID3 genre */ #endif int ctx_flags; /** <格式特定的標(biāo)志,看到AVFMTCTX_xx* / /*分處理的私人數(shù)據(jù)(不直接修改),。* / / **此緩沖區(qū)只需要當(dāng)數(shù)據(jù)包已經(jīng)被緩沖,,但 不解碼,例如,,在MPEG編解碼器的參數(shù) 流,。 * / struct AVPacketList *packet_buffer; / **解碼元件的第一幀的位置,在 AV_TIME_BASE分?jǐn)?shù)秒,。從來沒有設(shè)置這個值直接: 推導(dǎo)的AVStream值,。 * / int64_t start_time; / **解碼流的時間,,在AV_TIME_BASE分?jǐn)?shù) 秒。只設(shè)置這個值,,如果你知道沒有個人流 工期,,也不要設(shè)置任何他們。這是從推導(dǎo) AVStream值如果沒有設(shè)置,。 int64_t duration; / **解碼:總的文件大小,,如果未知0* / int64_t file_size; / **解碼:在比特/秒的總流率,如果不 可用,。從來沒有直接設(shè)置它如果得到file_size和 時間是已知的如FFmpeg的自動計算,。 * / int bit_rate; /* av_read_frame()支持* / AVStream *cur_st; #if LIBAVFORMAT_VERSION_INT < (53<<16) const uint8_t *cur_ptr_deprecated; int cur_len_deprecated; AVPacket cur_pkt_deprecated; #endif /* av_seek_frame() 支持 */ int64_t data_offset; /** 第一包抵消 */ int index_built; int mux_rate; unsigned int packet_size; int preload; int max_delay; #define AVFMT_NOOUTPUTLOOP -1 #define AVFMT_INFINITEOUTPUTLOOP 0 /** 次循環(huán)輸出的格式支持它的數(shù)量 */ int loop_output; int flags; #define AVFMT_FLAG_GENPTS 0x0001 ///< 生成失蹤分,即使它需要解析未來框架,。 #define AVFMT_FLAG_IGNIDX 0x0002 ///< 忽略指數(shù),。 #define AVFMT_FLAG_NONBLOCK 0x0004 ///<從輸入中讀取數(shù)據(jù)包時,不要阻止,。 #define AVFMT_FLAG_IGNDTS 0x0008 ///< 忽略幀的DTS包含DTS與PTS #define AVFMT_FLAG_NOFILLIN 0x0010 ///< 不要從任何其他值推斷值,,只是返回存儲在容器中 #define AVFMT_FLAG_NOPARSE 0x0020 ///< 不要使用AVParsers,你還必須設(shè)置為FILLIN幀代碼的工作,,沒有解析AVFMT_FLAG_NOFILLIN - >無幀,。也在尋求框架不能工作,如果找到幀邊界的解析已被禁用 #define AVFMT_FLAG_RTP_HINT 0x0040 ///< 暗示到輸出文件添加的RTP int loop_input; /**解碼:對探測數(shù)據(jù)的大小;編碼:未使用,。* / unsigned int probesize; / ** 在此期間,,輸入*最大時間(在AV_TIME_BASE單位)應(yīng) *進(jìn)行分析在avformat_find_stream_info()。 * / int max_analyze_duration; const uint8_t *key; int keylen; unsigned int nb_programs; AVProgram **programs; / ** *強迫影片codec_id,。 * Demuxing:由用戶設(shè)置,。 * / enum CodecID video_codec_id; / ** *強迫音頻codec_id。 * Demuxing:由用戶設(shè)置,。 * / enum CodecID audio_codec_id; / ** *強制的:字幕codec_id,。 * Demuxing:由用戶設(shè)置。 * / enum CodecID subtitle_codec_id; / ** *以字節(jié)為單位的最高限額為每個數(shù)據(jù)流的索引使用的內(nèi)存,。 *如果該指數(shù)超過此大小,,條目將被丟棄 *需要保持一個較小的規(guī)模,。這可能會導(dǎo)致較慢或更少 *準(zhǔn)確的尋求(分路器),。 *分路器內(nèi)存中的一個完整的指數(shù)是強制性的將忽略 *此。 *混流:未使用 * demuxing:由用戶設(shè)置* / unsigned int max_index_size; / ** *以字節(jié)為單位的最高限額使用幀緩沖內(nèi)存 *從實時捕獲設(shè)備獲得,。* / unsigned int max_picture_buffer; unsigned int nb_chapters; AVChapter **chapters; / ** *標(biāo)志啟用調(diào)試,。* / int debug; #define FF_FDEBUG_TS 0x0001 / ** *原始數(shù)據(jù)包從分路器之前,解析和解碼,。 *此緩沖區(qū)用于緩沖數(shù)據(jù)包,,直到編解碼器可以 *確定,,因為不知道不能做解析 *編解碼器。* / struct AVPacketList *raw_packet_buffer; struct AVPacketList *raw_packet_buffer_end; struct AVPacketList *packet_buffer_end; AVMetadata *metadata; / ** *剩余的大小可為raw_packet_buffer,,以字節(jié)為單位,。 *不屬于公共API* / #define RAW_PACKET_BUFFER_SIZE 2500000 int raw_packet_buffer_remaining_size; / ** *在現(xiàn)實世界中的時間流的開始時間,以微秒 *自Unix紀(jì)元(1970年1月1日起00:00),。也就是說,,pts= 0 在這個現(xiàn)實世界的時間*流被抓獲。 * - 編碼:由用戶設(shè)置,。 * - 解碼:未使用,。 * / int64_t start_time_realtime; } AVFormatContext; 這是FFMpeg中最為基本的一個結(jié)構(gòu),是其他所有結(jié)構(gòu)的根,,是一個多媒體文件或流的根本抽象,。其中: nb_streams和streams所表示的AVStream結(jié)構(gòu)指針數(shù)組包含了所有內(nèi)嵌媒體流的描述; iformat和oformat指向?qū)?yīng)的demuxer和muxer指針,; pb則指向一個控制底層數(shù)據(jù)讀寫的ByteIOContext結(jié)構(gòu),。 start_time和duration是從streams數(shù)組的各個AVStream中推斷出的多媒體文件的起始時間和長度,以微妙為單位,。 通常,,這個結(jié)構(gòu)由avformat_open_input在內(nèi)部創(chuàng)建并以缺省值初始化部分成員。但是,,如果調(diào)用者希望自己創(chuàng)建該結(jié)構(gòu),,則需要顯式為該結(jié)構(gòu)的一些成員置缺省值——如果沒有缺省值的話,會導(dǎo)致之后的動作產(chǎn)生異常,。以下成員需要被關(guān)注: probesize mux_rate packet_size flags max_analyze_duration key max_index_size max_picture_buffer max_delay AVPacket AVPacket定義在avcodec.h中,,如下: typedef struct AVPacket { / ** AVStream->基time_base單位介紹時間戳的時間 *解壓縮包將被提交給用戶。 *可AV_NOPTS_VALUE如果沒有存儲在文件中,。 *分必須大于或等于DTS作為演示不能發(fā)生之前 *減壓,,除非要查看十六進(jìn)制轉(zhuǎn)儲。有些格式濫用 * DTS和PTS/ CTS的條款意味著不同的東西,。如時間戳 *必須轉(zhuǎn)換為真正的PTS / DTS之前,,他們在AVPacket存儲。 * / int64_t pts; / ** AVStream->基time_base單位時間的減壓時間戳記; *包解壓,。 *可AV_NOPTS_VALUE如果沒有存儲在文件中,。 * / int64_t dts; uint8_t *data; int size; int stream_index; int flags; / ** *這個包的時間AVStream->基time_base單位,如果未知,。 *等于next_pts - 在呈現(xiàn)順序this_pts,。* / int duration; void (*destruct)(struct AVPacket *); void *priv; int64_t pos; ///< 如果未知字節(jié)的位置,在流,-1 / ** * AVStream->基time_base單位的時差,,這點 *包從解碼器輸出的已融合在哪個點 *獨立的前一幀的情況下,。也就是說, *框架幾乎是一致的,,沒有問題,,如果解碼開始從 *第一幀或從這個關(guān)鍵幀。 * AV_NOPTS_VALUE如果不明,。 *此字段是不是當(dāng)前數(shù)據(jù)包的顯示時間,。 * *這一領(lǐng)域的目的是允許在流,沒有尋求 *在傳統(tǒng)意義上的關(guān)鍵幀,。它所對應(yīng)的 *恢復(fù)點SEI的H.264和match_time_delta在螺母,。這也是 *必不可少的一些類型的字幕流,以確保所有 *后尋求正確顯示字幕,。* / int64_t convergence_duration; } AVPacket; FFMPEG使用AVPacket來暫存解復(fù)用之后,、解碼之前的媒體數(shù)據(jù)(一個音/視頻幀、一個字幕包等)及附加信息(解碼時間戳,、顯示時間戳,、時長等)。其中: dts表示解碼時間戳,,pts表示顯示時間戳,,它們的單位是所屬媒體流的時間基準(zhǔn)。 stream_index給出所屬媒體流的索引,; data為數(shù)據(jù)緩沖區(qū)指針,,size為長度; duration為數(shù)據(jù)的時長,,也是以所屬媒體流的時間基準(zhǔn)為單位,; pos表示該數(shù)據(jù)在媒體流中的字節(jié)偏移量; destruct為用于釋放數(shù)據(jù)緩沖區(qū)的函數(shù)指針,; flags為標(biāo)志域,,其中,最低為置1表示該數(shù)據(jù)是一個關(guān)鍵幀,。 AVPacket結(jié)構(gòu)本身只是個容器,,它使用data成員引用實際的數(shù)據(jù)緩沖區(qū)。這個緩沖區(qū)通常是由av_new_packet創(chuàng)建的,,但也可能由FFMPEG的API創(chuàng)建(如av_read_frame),。當(dāng)某個AVPacket結(jié)構(gòu)的數(shù)據(jù)緩沖區(qū)不再被使用時,要需要通過調(diào)用av_free_packet釋放,。av_free_packet調(diào)用的是結(jié)構(gòu)體本身的destruct函數(shù),,它的值有兩種情況:1)av_destruct_packet_nofree或0;2)av_destruct_packet,,其中,,情況1)僅僅是將data和size的值清0而已,情況2)才會真正地釋放緩沖區(qū),。 FFMPEG內(nèi)部使用AVPacket結(jié)構(gòu)建立緩沖區(qū)裝載數(shù)據(jù),,同時提供destruct函數(shù),如果FFMPEG打算自己維護緩沖區(qū),,則將destruct設(shè)為av_destruct_packet_nofree,,用戶調(diào)用av_free_packet清理緩沖區(qū)時并不能夠?qū)⑵溽尫牛蝗绻鸉FMPEG打算將該緩沖區(qū)徹底交給調(diào)用者,,則將destruct設(shè)為av_destruct_packet,,表示它能夠被釋放。安全起見,,如果用戶希望自由地使用一個FFMPEG內(nèi)部創(chuàng)建的AVPacket結(jié)構(gòu),,最好調(diào)用av_dup_packet進(jìn)行緩沖區(qū)的克隆,將其轉(zhuǎn)化為緩沖區(qū)能夠被釋放的AVPacket,,以免對緩沖區(qū)的不當(dāng)占用造成異常錯誤,。av_dup_packet會為destruct指針為av_destruct_packet_nofree的AVPacket新建一個緩沖區(qū),然后將原緩沖區(qū)的數(shù)據(jù)拷貝至新緩沖區(qū),,置data的值為新緩沖區(qū)的地址,,同時設(shè)destruct指針為av_destruct_packet。 時間信息 時間信息用于實現(xiàn)多媒體同步,。 同步的目的在于展示多媒體信息時,,能夠保持媒體對象之間固有的時間關(guān)系。同步有兩類,,一類是流內(nèi)同步,,其主要任務(wù)是保證單個媒體流內(nèi)的時間關(guān)系,以滿足感知要求,,如按照規(guī)定的幀率播放一段視頻,;另一類是流間同步,主要任務(wù)是保證不同媒體流之間的時間關(guān)系,,如音頻和視頻之間的關(guān)系(lipsync),。 對于固定速率的媒體,如固定幀率的視頻或固定比特率的音頻,,可以將時間信息(幀率或比特率)置于文件首部(header),,如AVI的hdrl List、MP4的moov box,,還有一種相對復(fù)雜的方案是將時間信息嵌入媒體流的內(nèi)部,,如MPEG TS和Real video,這種方案可以處理變速率的媒體,亦可有效避免同步過程中的時間漂移,。 FFMPEG會為每一個數(shù)據(jù)包打上時間標(biāo)簽,,以更有效地支持上層應(yīng)用的同步機制。時間標(biāo)簽有兩種,,一種是DTS,,稱為解碼時間標(biāo)簽,另一種是PTS,,稱為顯示時間標(biāo)簽,。對于聲音來說 ,這兩個時間標(biāo)簽是相同的,,但對于某些視頻編碼格式,,由于采用了雙向預(yù)測技術(shù),會造成DTS和PTS的不一致,。 無雙向預(yù)測幀的情況: 圖像類型: I P P P P P P ... I P P DTS: 0 1 2 3 4 5 6... 100 101 102 PTS: 0 1 2 3 4 5 6... 100 101 102 有雙向預(yù)測幀的情況: 圖像類型: I P B B P B B ... I P B DTS: 0 1 2 3 4 5 6 ... 100 101 102 PTS: 0 3 1 2 6 4 5 ... 100 104 102 對于存在雙向預(yù)測幀的情況,,通常要求解碼器對圖像重排序,以保證輸出的圖像順序為顯示順序: 解碼器輸入:I P B B P B B (DTS) 0 1 2 3 4 5 6 (PTS) 0 3 1 2 6 4 5 解碼器輸出:X I B B P B B P (PTS) X 0 1 2 3 4 5 6 時間信息的獲?。?br> 通過調(diào)用avformat_find_stream_info,,多媒體應(yīng)用可以從AVFormatContext對象中拿到媒體文件的時間信息:主要是總時間長度和開始時間,此外還有與時間信息相關(guān)的比特率和文件大小,。其中時間信息的單位是AV_TIME_BASE:微秒,。 typedef struct AVFormatContext { / **解碼元件的第一幀的位置,在 AV_TIME_BASE分?jǐn)?shù)秒,。從來沒有設(shè)置這個值直接: 推導(dǎo)的AVStream值,。 * / int64_t start_time; / **解碼流的時間,在AV_TIME_BASE分?jǐn)?shù)秒,。只設(shè)置這個值,,如果你知道沒有個人流工期,也不要設(shè)置任何他們,。這是從推導(dǎo)AVStream值如果沒有設(shè)置,。 * / int64_t duration; / **解碼:總的文件大小,如果未知=0* / int64_t file_size; / **解碼:在比特/秒的總流率,,如果不可用,。從來沒有直接設(shè)置它如果得到file_size和時間是已知的如FFmpeg的自動計算。 * / int bit_rate; ..... } AVFormatContext; 以上4個成員變量都是只讀的,,基于FFMpeg的中間件需要將其封裝到某個接口中,,如: LONG GetDuratioin(IntfX*); LONG GetStartTime(IntfX*); LONG GetFileSize(IntfX*); LONG GetBitRate(IntfX*); APIs avformat_open_input: int avformat_open_input(AVFormatContext **ic_ptr, const char *filename, AVInputFormat *fmt, AVDictionary **options); avformat_open_input完成兩個任務(wù): 打開一個文件或URL,基于字節(jié)流的底層輸入模塊得到初始化,。 解析多媒體文件或多媒體流的頭信息,,創(chuàng)建AVFormatContext結(jié)構(gòu)并填充其中的關(guān)鍵字段,,依次為各個原始流建立AVStream結(jié)構(gòu)。 一個多媒體文件或多媒體流與其包含的原始流的關(guān)系如下: 多媒體文件/多媒體流 (movie.mkv) 原始流 1 (h.264 video) 原始流 2 (aac audio for Chinese) 原始流 3 (aac audio for english) 原始流 4 (Chinese Subtitle) 原始流 5 (English Subtitle) ... 關(guān)于輸入?yún)?shù): ic_ptr,,這是一個指向指針的指針,,用于返回avformat_open_input內(nèi)部構(gòu)造的一個AVFormatContext結(jié)構(gòu)體。 filename,,指定文件名。 fmt,,用于顯式指定輸入文件的格式,,如果設(shè)為空則自動判斷其輸入格式。 options 這個函數(shù)通過解析多媒體文件或流的頭信息及其他輔助數(shù)據(jù),,能夠獲取足夠多的關(guān)于文件,、流和編解碼器的信息,但由于任何一種多媒體格式提供的信息都是有限的,,而且不同的多媒體內(nèi)容制作軟件對頭信息的設(shè)置不盡相同,,此外這些軟件在產(chǎn)生多媒體內(nèi)容時難免會引入一些錯誤,因此這個函數(shù)并不保證能夠獲取所有需要的信息,,在這種情況下,,則需要考慮另一個函數(shù): avformat_find_stream_info: int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options); 這個函數(shù)主要用于獲取必要的編解碼器參數(shù),設(shè)置到ic→streams[i]→codec中,。 首先必須得到各媒體流對應(yīng)編解碼器的類型和id,,這是兩個定義在avutils.h和avcodec.h中的枚舉: enum AVMediaType { AVMEDIA_TYPE_UNKNOWN = -1, AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_DATA, AVMEDIA_TYPE_SUBTITLE, AVMEDIA_TYPE_ATTACHMENT, AVMEDIA_TYPE_NB }; enum CodecID { CODEC_ID_NONE, /* video codecs */ CODEC_ID_MPEG1VIDEO, CODEC_ID_MPEG2VIDEO, ///< preferred ID for MPEG-1/2 video decoding CODEC_ID_MPEG2VIDEO_XVMC, CODEC_ID_H261, CODEC_ID_H263, ... }; 通常,如果某種媒體格式具備完備而正確的頭信息,,調(diào)用avformat_open_input即可以得到這兩個參數(shù),,但若是因某種原因avformat_open_input無法獲取它們,這一任務(wù)將由avformat_find_stream_info完成,。 其次還要獲取各媒體流對應(yīng)編解碼器的時間基準(zhǔn),。 此外,對于音頻編解碼器,,還需要得到: 采樣率,, 聲道數(shù), 位寬,, 幀長度(對于某些編解碼器是必要的),, 對于視頻編解碼器,則是: 圖像大小,, 色彩空間及格式,, av_read_frame int av_read_frame(AVFormatContext *s, AVPacket *pkt); 這個函數(shù)用于從多媒體文件或多媒體流中讀取媒體數(shù)據(jù),獲取的數(shù)據(jù)由AVPacket結(jié)構(gòu)pkt來存放,。對于音頻數(shù)據(jù),,如果是固定比特率,,則pkt中裝載著一個或多個音頻幀;如果是可變比特率,,則pkt中裝載有一個音頻幀,。對于視頻數(shù)據(jù),pkt中裝載有一個視頻幀,。需要注意的是:再次調(diào)用本函數(shù)之前,,必須使用av_free_packet釋放pkt所占用的資源。 通過pkt→stream_index可以查到獲取的媒體數(shù)據(jù)的類型,,從而將數(shù)據(jù)送交相應(yīng)的解碼器進(jìn)行后續(xù)處理,。 av_seek_frame int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags); 這個函數(shù)通過改變媒體文件的讀寫指針來實現(xiàn)對媒體文件的隨機訪問,支持以下三種方式: 基于時間的隨機訪問:具體而言就是將媒體文件讀寫指針定位到某個給定的時間點上,,則之后調(diào)用av_read_frame時能夠讀到時間標(biāo)簽等于給定時間點的媒體數(shù)據(jù),,通常用于實現(xiàn)媒體播放器的快進(jìn)、快退等功能,。 基于文件偏移的隨機訪問:相當(dāng)于普通文件的seek函數(shù),,timestamp也成為文件的偏移量。 基于幀號的隨機訪問:timestamp為要訪問的媒體數(shù)據(jù)的幀號,。 關(guān)于參數(shù): s:是個AVFormatContext指針,,就是avformat_open_input返回的那個結(jié)構(gòu)。 stream_index:指定媒體流,,如果是基于時間的隨機訪問,,則第三個參數(shù)timestamp將以此媒體流的時間基準(zhǔn)為單位;如果設(shè)為負(fù)數(shù),,則相當(dāng)于不指定具體的媒體流,,F(xiàn)FMPEG會按照特定的算法尋找缺省的媒體流,此時,,timestamp的單位為AV_TIME_BASE(微秒),。 timestamp:時間標(biāo)簽,單位取決于其他參數(shù),。 flags:定位方式,,AVSEEK_FLAG_BYTE表示基于字節(jié)偏移,AVSEEK_FLAG_FRAME表示基于幀號,,其它表示基于時間,。 av_close_input_file: void av_close_input_file(AVFormatContext *s); 關(guān)閉一個媒體文件:釋放資源,關(guān)閉物理IO,。 avcodec_find_decoder: AVCodec *avcodec_find_decoder(enum CodecID id); AVCodec *avcodec_find_decoder_by_name(const char *name); 根據(jù)給定的codec id或解碼器名稱從系統(tǒng)中搜尋并返回一個AVCodec結(jié)構(gòu)的指針,。 avcodec_open: int avcodec_open(AVCodecContext *avctx, AVCodec *codec); 此函數(shù)根據(jù)輸入的AVCodec指針具體化AVCodecContext結(jié)構(gòu)。在調(diào)用該函數(shù)之前,,需要首先調(diào)用avcodec_alloc_context分配一個AVCodecContext結(jié)構(gòu),,或調(diào)用avformat_open_input獲取媒體文件中對應(yīng)媒體流的AVCodecContext結(jié)構(gòu),;此外還需要通過avcodec_find_decoder獲取AVCodec結(jié)構(gòu)。 這一函數(shù)還將初始化對應(yīng)的解碼器,。 avcodec_decode_video2 int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture, int *got_picture_ptr, AVPacket *avpkt); 解碼一個視頻幀,。got_picture_ptr指示是否有解碼數(shù)據(jù)輸出。 輸入數(shù)據(jù)在AVPacket結(jié)構(gòu)中,,輸出數(shù)據(jù)在AVFrame結(jié)構(gòu)中,。AVFrame是定義在avcodec.h中的一個數(shù)據(jù)結(jié)構(gòu): typedef struct AVFrame { FF_COMMON_FRAME } AVFrame; FF_COMMON_FRAME定義了諸多數(shù)據(jù)域,大部分由FFMpeg內(nèi)部使用,,對于用戶來說,,比較重要的主要包括: #define FF_COMMON_FRAME \ ...... uint8_t *data[4];\ int linesize[4];\ int key_frame;\ int pict_type;\ int64_t pts;\ int reference;\ ...... FFMpeg內(nèi)部以planar的方式存儲原始圖像數(shù)據(jù),即將圖像像素分為多個平面(R/G/B或Y/U/V),,data數(shù)組內(nèi)的指針分別指向四個像素平面的起始位置,,linesize數(shù)組則存放各個存貯各個平面的緩沖區(qū)的行寬: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++data[0]->#################################++++++++++++ ++++++++++++###########picture data##########++++++++++++ ........................ ++++++++++++#################################++++++++++++ |<-------------------line_size[0]---------------------->| 此外,,key_frame標(biāo)識該圖像是否是關(guān)鍵幀,;pict_type表示該圖像的編碼類型:I(1)/P(2)/B(3)……;pts是以time_base為單位的時間標(biāo)簽,,對于部分解碼器如H.261,、H.263和MPEG4,可以從頭信息中獲??;reference表示該圖像是否被用作參考。 avcodec_decode_audio4 int avcodec_decode_audio4(AVCodecContext *avctx, AVFrame *frame, int *got_frame_ptr, AVPacket *avpkt); 解碼一個音頻幀,。輸入數(shù)據(jù)在AVPacket結(jié)構(gòu)中,,輸出數(shù)據(jù)在frame中,got_frame_ptr表示是否有數(shù)據(jù)輸出,。 avcodec_close int avcodec_close(AVCodecContext *avctx); 關(guān)閉解碼器,,釋放avcodec_open中分配的資源。 |
|