1.libavformat
AVFormatContext可以進(jìn)行格式的封裝與解封裝淑趾,它的數(shù)據(jù)部分由底層提供待笑,底層使用了AVIOContext丽柿,這個(gè)AVIOContext實(shí)際上就是為普通的I/O增加了一層Buffer緩沖區(qū)飞袋,再往 底層就是URLContext恰聘,也就是到達(dá)了協(xié)議層句各。如圖:
相關(guān)函數(shù)及結(jié)構(gòu)體:
av_register_all()
編譯FFmpeg的時(shí)候,做了一個(gè)configure的配置,configure的配置會(huì)生成兩個(gè)文件:config.mk與config.h晴叨。 config.mk實(shí)際上就是makefile文件需要包含進(jìn)去的子模塊凿宾,會(huì)作用在編譯階段,幫助開發(fā)者編譯出正確的庫;而config.h是作用在運(yùn)行階段兼蕊, 這一階段將確定需要注冊(cè)哪些容器以及編解碼格式到FFmpeg框架中初厚。 所以該函數(shù)的內(nèi)部實(shí)現(xiàn)會(huì)先調(diào)用avcodec_register_all來注冊(cè)所有config.h 里面開放的編解碼器,然后會(huì)注冊(cè)所有的Muxer和Demuxer(也就是封 裝格式)孙技,最后注冊(cè)所有的Protocol(即協(xié)議層的東西)产禾。
avformat_open_input()
avformat_open_input會(huì)根據(jù)所提供的文件路徑判斷文件的格式,其實(shí)就是通過這一步來決定使用的到底是哪一個(gè)Demuxer牵啦。
avformat_find_stream_info()
該方法的作用就是把所有Stream的MetaData信息填充好亚情。方法內(nèi)部會(huì)先查找對(duì)應(yīng)的解碼器,然后打開對(duì)應(yīng)的解碼器哈雏,緊接著會(huì)利用Demuxer中的 read_packet函數(shù)讀取一段數(shù)據(jù)進(jìn)行解碼楞件,當(dāng)然解碼的數(shù)據(jù)越多,分析出的流信息就會(huì)越準(zhǔn)確僧著,如果是本地資源履因,那么很快就可以得到非常準(zhǔn)確的信息了,但是對(duì)于網(wǎng)絡(luò)資源來說盹愚,則會(huì)比較慢,因此該函數(shù)有幾個(gè)參數(shù)可以控制讀取數(shù)據(jù)的長度站故,一個(gè)是probe size皆怕,一個(gè)是max_analyze_duration,還有一個(gè)是fps_probe_size西篓,這三個(gè)參數(shù)共同控制解碼數(shù)據(jù)的長度愈腾,當(dāng)然,如果配置這幾個(gè)參數(shù)的值越小岂津,那么這個(gè)函數(shù)執(zhí)行的時(shí)間就會(huì)越快虱黄,但是會(huì)導(dǎo)致AVStream結(jié)構(gòu)體里面一些信息(視頻的寬、高吮成、fps橱乱、編碼類型等)不準(zhǔn)確辜梳。
av_find_best_stream()
獲取各種流的索引。
av_read_frame()
使用該方法讀取出來的數(shù)據(jù)是AVPacket泳叠,在FFmpeg的早期版本中 開放給開發(fā)者的函數(shù)其實(shí)就是av_read_packet作瞄,但是需要開發(fā)者自己來處理AVPacket中的數(shù)據(jù)不能被解碼器完全處理完的情況,即需要把未處理完的壓縮數(shù)據(jù)緩存起來的問題危纫。所以到了新版本的FFmpeg中宗挥,其提供了該函數(shù),用于處理此狀況种蝶。該函數(shù)的實(shí)現(xiàn)首先會(huì)委托到Demuxer的 read_packet方法中去契耿,當(dāng)然read_packet通過解復(fù)用層和協(xié)議層的處理之后,會(huì)將數(shù)據(jù)返回到這里螃征,在該函數(shù)中進(jìn)行數(shù)據(jù)緩沖處理宵喂。為了保證每次讀取完整的一幀,讀取到的數(shù)據(jù)長度可能不一樣会傲。
2.libavcodec
對(duì)于開發(fā)者來說锅棕,這一層我們能接觸到的最頂層的結(jié)構(gòu)體就是 AVCodecContext,該結(jié)構(gòu)體包含的就是與實(shí)際的編解碼有關(guān)的部分淌山。首先裸燎,AVCodecContext是包含在一個(gè)AVStream里面的,即描述了這路流的編碼格式是什么泼疑,其中存放了具體的編碼格式信息德绿,根據(jù)Codec的信息可以打開編碼器或者解碼器,然后利用該編碼器或者解碼器進(jìn)行 AVPacket與AVFrame之間的轉(zhuǎn)換(實(shí)際上就是解碼或者編碼的過程)退渗, 這是FFmpeg中最重要的一部分移稳。結(jié)構(gòu)如圖:
相關(guān)函數(shù)及結(jié)構(gòu)體:
avcodec_find_decoder()
根據(jù)上下文找到解碼器。
avcodec_open2()
打開解碼器会油。
av_frame_alloc()
AVFrame對(duì)象必須調(diào)用av_frame_alloc()在堆上分配个粱。
avcodec_decode_audio4()
參數(shù)1:avctx編解碼器上下文
參數(shù)2:frame用于存儲(chǔ)解碼音頻樣本的AVFrame
參數(shù)3:got_frame_ptr如果沒有幀可以解碼則為零,否則為非零
參數(shù)4:avpkt包含輸入緩沖區(qū)的輸入AVPacket
參數(shù)5:如果在解碼期間發(fā)生錯(cuò)誤翻翩,則返回否定錯(cuò)誤代碼都许,否則返回從輸入AVPacket消耗的字節(jié)數(shù)。
avcodec_send_packet():發(fā)送解碼未處理數(shù)據(jù)
avcodec_receive_frame():接收解碼后的數(shù)據(jù)
avcodec_send_frame:發(fā)送編碼未處理數(shù)據(jù)
avcodec_receive_packet():接收編碼后的數(shù)據(jù)
一個(gè)packet會(huì)被解碼出一個(gè)frame嫂冻,不過也存在一個(gè)packet被解碼出多個(gè)frame或者多個(gè)packet才能解碼出一個(gè)frame的情況胶征,甚至也有些解碼器在輸入以及輸出端上可能會(huì)有延遲。了解更多看這里
返回狀態(tài):
send 0 :send_packet返回值為0桨仿,正常狀態(tài)睛低,意味著輸入的packet被解碼器正常接收。
send EAGAIN :send_packet返回值為EAGAIN,輸入的packet未被接收钱雷,需要輸出一個(gè)或多個(gè)的frame后才能重新輸入當(dāng)前packet骂铁。
send EOF :send_packet返回值為EOF,當(dāng)send_packet輸入為NULL時(shí)才會(huì)觸發(fā)該狀態(tài)急波,用于通知解碼器輸入packet已結(jié)束从铲。
send EINVAL :沒有打開×××,或者這是一個(gè)編碼器澄暮,或者要求刷新
receive 0 :receive_frame返回值為0名段,正常狀態(tài),意味著已經(jīng)輸出一幀泣懊。
receive EAGAIN:receive_frame返回值為EAGAIN伸辟,未能輸出frame,需要輸入更多的packet才能輸出當(dāng)前frame馍刮。
receive EOF :receive_frame返回值為EOF信夫,當(dāng)處于send EOF狀態(tài)后,調(diào)用一次或者多次receive_frame后就能得到該狀態(tài)卡啰,表示所有的幀已經(jīng)被輸出静稻。
簡略流程:av_read_frame讀取一幀數(shù)據(jù),avcodec_send_packet發(fā)送數(shù)據(jù)匈辱,解碼后avcodec_receive_frame接收一個(gè)完整幀振湾,但因?yàn)橐恍┨厥庠騛vcodec_send_packet發(fā)送的不是一個(gè)完成幀,此時(shí)avcodec_receive_frame會(huì)返回
3.libavutil
av_image_get_buffer_size()
給緩沖區(qū)設(shè)置類型類型亡脸,得到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,
videoCodecCtx->width,
videoCodecCtx->height,
1);
av_image_fill_arrays()
緩存區(qū)填充數(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,
videoCodecCtx->width,
videoCodecCtx->height,
1);
4.libswresample
音頻重采樣浅碾,就是改變音頻的采樣率大州、sample format、聲道數(shù)等參數(shù)垂谢,使之按照我們期望的參數(shù)輸出厦画。
SwrContext
音頻格式轉(zhuǎn)換上下文。
swr_alloc()
初始化上下文結(jié)構(gòu)體埂陆。
swr_alloc_set_opts()
設(shè)置重采樣參數(shù)苛白。
swr_init()
在swr_alloc_set_opts之后調(diào)用,將采樣參數(shù)設(shè)置到上下文焚虱。
參數(shù)1:重采樣上下文
參數(shù)2:輸出的layout, 如:5.1聲道…
參數(shù)3:輸出的樣本格式。Float, S16, S24
參數(shù)4:輸出的樣本率懂版【樵裕可以不變。
參數(shù)5:輸入的layout。
參數(shù)6:輸入的樣本格式民鼓。
參數(shù)7:輸入的樣本率薇芝。
參數(shù)8,參數(shù)9丰嘉,日志夯到,不用管,可直接傳0 */
av_samples_get_buffer_size()
使用av_sample_get_buffer_size來計(jì)算音頻占用的字節(jié)數(shù)饮亏。
參數(shù)1:linesize calculated linesize, may be NULL
參數(shù)2:nb_channels 聲道
參數(shù)3:nb_samples 單個(gè)通道中的樣本數(shù)
參數(shù)4:sample_fmt 采樣格式
參數(shù)5:align 對(duì)齊緩沖區(qū)大小對(duì)齊(0 =默認(rèn)耍贾,1 =無對(duì)齊)
swr_convert()
轉(zhuǎn)碼。使用avcodec_decode_audio4函數(shù)解碼音頻得到的數(shù)據(jù)類型為float 4bit路幸,而播放器播放的格式一般為S16(signed 16bit),這就需要對(duì)解碼得到的數(shù)據(jù)進(jìn)行轉(zhuǎn)換荐开。
參數(shù)1:音頻重采樣的上下文
參數(shù)2:輸出的指針。傳遞的輸出的數(shù)組
參數(shù)3:輸出的樣本數(shù)量简肴,不是字節(jié)數(shù)晃听。單通道的樣本數(shù)量。
參數(shù)4:輸入的數(shù)組砰识,AVFrame解碼出來的DATA
參數(shù)5:輸入的單通道的樣本數(shù)量能扒。
5.libswscale
SwsContext
視頻格式轉(zhuǎn)換上下文,編解碼中可能需要對(duì)視頻格式重新設(shè)置辫狼,就需要用到SwsContext初斑。
sws_getContext()
設(shè)置視頻轉(zhuǎn)換上下文。
參數(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ù)格式類型
sws_getContext(videoCodecCtx->width,
videoCodecCtx->height,
videoCodecCtx->pix_fmt,
videoCodecCtx->width,
videoCodecCtx->height,
AV_PIX_FMT_YUV420P,
SWS_FAST_BILINEAR,
NULL,
NULL,
NULL);
sws_scale()
視頻參數(shù)轉(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);