FFMPEG编解码

解码

ffmpeg3版本的解码接口做了调整,之前的视频解码接口avcodec_decode_video2和音频解码接口avcodec_decode_audio4被设置为deprecated,对这两个接口做了合并,使用同一的接口.并且将音视频解码步骤分成了两步,第一步avcodec_send_packet,第二步avcodec_receive_frame,

旧版本avcodec_decode_video2

旧版本avcodec_decode_audio4

1
2
3
4
5
6
7
8
9
10
11
12
int got_picture;
ret = avcodec_decode_audio4(enc, pcm,&got_picture, pkt);
if ( ret < 0 ) {
char buf[1024] = {0};
av_strerror(err, buf, sizeof(buf));
printf(buf);
printf("avcodec_decode_audio4 failed:%d" ,got_picture);
av_packet_unref(pkt);
av_frame_free(&pcm);
if(ic) avformat_close_input(&ic);
return -1;
}

将AVPacket的pkt解码成AVFrame的pcm

新版本avcodec_send_packet

接口源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* 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);
参数分析
  • AVCodecContext *avctx:第一个参数与旧的接口一致,是视频解码的上下文,包含解码器。
  • const AVPacket *avpkt: 编码的音视频帧数据

为什么要传递空的avpkt 这里有一个说明是可以传递NULL,什么情况下需要传递NULL,你平时看一些视频播放器,播放经常会少最后几帧,很多情况就是因为没有处理好缓冲帧的问题,ffmpeg内部会缓冲几帧,要想取出来就需要传递空的AVPacket进去。

新版本avcodec_receive_frame

接口源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 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);
参数分析
  • AVCodecContext *avctx:第一个参数视频解码的上下文,与上面接口一致。
  • AVFrame *frame:解码后的视频帧数据。
空间申请和释放问题

解码后图像空间由函数内部申请,你所做的只需要分配 AVFrame 对象空间,如果你每次调用avcodec_receive_frame传递同一个对象,接口内部会判断空间是否已经分配,如果没有分配会在函数内部分配。 avcodec_send_packet和avcodec_receive_frame调用关系并不一定是一对一的,比如一些音频数据一个AVPacket中包含了1秒钟的音频,调用一次avcodec_send_packet之后,可能需要调用25次 avcodec_receive_frame才能获取全部的解码音频数据,所以要做如下处理:

1
2
3
4
5
6
7
8
9
10
int re = avcodec_send_packet(codec, pkt);
if (re != 0)
{
return;
}
while( avcodec_receive_frame(codec, frame) == 0)
{
//读取到一帧音频或者视频
//处理解码后音视频 frame
}

参考 send/receive encoding and decoding API overview

坚持原创技术分享,您的支持将鼓励我继续创作!