ffmpeg3版本的解碼接口做了不少調(diào)整,之前的視頻解碼接口avcodec_decode_video2和avcodec_decode_audio4音頻解碼被設(shè)置為deprecated,對這兩個接口做了合并,使用統(tǒng)一的接口。并且將音視頻解碼步驟分為了兩步,第一步avcodec_send_packet,第二步avcodec_receive_frame,通過接口名字我們就可以知道第一步是發(fā)送編碼數(shù)據(jù)包,第二步是接收解碼后數(shù)據(jù)。新版本是否只是做了接口的變化,還有有哪些我們需要注意的事項,我們來分析一下。
成都創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價比寧德網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式寧德網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋寧德地區(qū)。費用合理售后完善,十載實體公司更值得信賴。
首先我們先看一下這兩個接口。
接口源碼
/**
* Supply raw packet data as input to a decoder.
*
* Internally, this call will copy relevant AVCodecContext fields, which can
* influence decoding per-packet, and apply them when the packet is actually
* decoded. (For example AVCodecContext.skip_frame, which might direct the
* decoder to drop the frame contained by the packet sent with this function.)
*
* @warning The input buffer, avpkt->data must be AV_INPUT_BUFFER_PADDING_SIZE
* larger than the actual read bytes because some optimized bitstream
* readers read 32 or 64 bits at once and could read over the end.
*
* @warning Do not mix this API with the legacy API (like avcodec_decode_video2())
* on the same AVCodecContext. It will return unexpected results now
* or in future libavcodec versions.
*
* @note The AVCodecContext MUST have been opened with @ref avcodec_open2()
* before packets may be fed to the decoder.
*
* @param avctx codec context
* @param[in] avpkt The input AVPacket. Usually, this will be a single video
* frame, or several complete audio frames.
* Ownership of the packet remains with the caller, and the
* decoder will not write to the packet. The decoder may create
* a reference to the packet data (or copy it if the packet is
* not reference-counted).
* Unlike with older APIs, the packet is always fully consumed,
* and if it contains multiple frames (e.g. some audio codecs),
* will require you to call avcodec_receive_frame() multiple
* times afterwards before you can send a new packet.
* It can be NULL (or an AVPacket with data set to NULL and
* size set to 0); in this case, it is considered a flush
* packet, which signals the end of the stream. Sending the
* first flush packet will return success. Subsequent ones are
* unnecessary and will return AVERROR_EOF. If the decoder
* still has frames buffered, it will return them after sending
* a flush packet.
*
* @return 0 on success, otherwise negative error code:
* AVERROR(EAGAIN): input is not accepted right now - the packet must be
* resent after trying to read output
* AVERROR_EOF: the decoder has been flushed, and no new packets can
* be sent to it (also returned if more than 1 flush
* packet is sent)
* AVERROR(EINVAL): codec not opened, it is an encoder, or requires flush
* AVERROR(ENOMEM): failed to add packet to internal queue, or similar
* other errors: legitimate decoding errors
*/
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
參數(shù)分析
AVCodecContext *avctx:第一個參數(shù)與舊的接口一致,是視頻解碼的上下文,包含×××。
const AVPacket *avpkt: 編碼的音視頻幀數(shù)據(jù)
為什么要傳遞空的avpkt
這里有一個說明是可以傳遞NULL,什么情況下需要傳遞NULL,你平時看一些視頻播放器,播放經(jīng)常會少最后幾幀,很多情況就是因為沒有處理好緩沖幀的問題,ffmpeg內(nèi)部會緩沖幾幀,要想取出來就需要傳遞空的AVPacket進去。
接口源碼
/**
* Return decoded output data from a decoder.
*
* @param avctx codec context
* @param frame This will be set to a reference-counted video or audio
* frame (depending on the decoder type) allocated by the
* decoder. Note that the function will always call
* av_frame_unref(frame) before doing anything else.
*
* @return
* 0: success, a frame was returned
* AVERROR(EAGAIN): output is not available right now - user must try
* to send new input
* AVERROR_EOF: the decoder has been fully flushed, and there will be
* no more output frames
* AVERROR(EINVAL): codec not opened, or it is an encoder
* other negative values: legitimate decoding errors
*/
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
參數(shù)分析
AVCodecContext *avctx:第一個參數(shù)視頻解碼的上下文,與上面接口一致。
AVFrame *frame:解碼后的視頻幀數(shù)據(jù)。
空間申請和釋放問題
解碼后圖像空間由函數(shù)內(nèi)部申請,你所做的只需要分配 AVFrame 對象空間,如果你每次調(diào)用avcodec_receive_frame傳遞同一個對象,接口內(nèi)部會判斷空間是否已經(jīng)分配,如果沒有分配會在函數(shù)內(nèi)部分配。
avcodec_send_packet和avcodec_receive_frame調(diào)用關(guān)系并不一定是一對一的,比如一些音頻數(shù)據(jù)一個AVPacket中包含了1秒鐘的音頻,調(diào)用一次avcodec_send_packet之后,可能需要調(diào)用25次 avcodec_receive_frame才能獲取全部的解碼音頻數(shù)據(jù),所以要做如下處理:
int re = avcodec_send_packet(codec, pkt);
if (re != 0)
{
return;
}
while( avcodec_receive_frame(codec, frame) == 0)
{
//讀取到一幀音頻或者視頻
//處理解碼后音視頻 frame
}
更多的資料也可以關(guān)注我51CTO上的視頻課程
夏老師的課堂 http://edu.51cto.com/lecturer/12016059.html
手把手教您開發(fā)視頻播放器
http://edu.51cto.com/course/course_id-8059.html