对于编解码 API 有不懂的地方,官方文档是最值得阅读的。
此文对 ffmpeg4.4 版本编解码 API 的官方说明进行了翻译,如下文:
avcodec_send_packet()/avcodec_receive_frame() 为解码 API,
avcodec_send_frame()/avcodec_receive_packet() 为编码 API,
他们对输入输出进行了解耦。
音频、视频的编解码 API 非常相似,他们的工作原理如下:
流程
-
像往常一样建立并打开 AVCodecContext;
-
发送有效的输入:
- 对于解码,调用 avcodec_send_packet() 给解码器喂压缩数据:AVPacket
- 对于编码,调用 avcodec_send_frame() 给编码器发送未压缩的音频或视频帧:AVFrame
在这两种情况下,建议将 AVPackets 和 AVFrames 使用引用计数,不然 libavcodec 可能得拷贝输入数据(libavformat 总是返回引用计数的 AVPackets,而 av_frame_get_buffer 分配引用计数的 AVFrames)。
-
循环接收输出。定期调用对应的 avcodec_receive_*() 函数并处理他们的输出:
- 对于解码,调用 avcodec_receive_frame() 。如果成功,会返回解压缩的视频或音频数据 AVFrame
- 对于编码,调用 avcodec_receive_packet() 。如果成功,会返回压缩的数据包 AVPacket
重复调用上述函数,直到它返回 AVERROR(EAGAIN) 或错误。AVERROR(EAGAIN) 返回值表示需要新的输入数据才能返回新的输出。在这种情况下,继续发送输入数据。对于每个输入 frame/packet,解码器通常会返回 1 个输出 frame/packet,但也可以是 0 或大于 1 个。
在编码或解码开始时,编解码器可能会接收多个输入 frames/packets 而不返回一个 frame,直到其内部缓冲区被填满。 如果您按照上述步骤进行操作,这种情况已被良好处理。
从理论上讲,发送输入可能会导致 EAGAIN – 只有在没有收到任何输出时才会发生这种情况。 您可以使用它来构建除上述建议之外的解码或编码的循环。 例如,可以试着在每次循环中发送新的输入,并在返回 EAGAIN 时接收输出。
流结束的情况
文件流结束后需要“刷新”(耗尽)编解码器,因为编解码器可能会在内部缓冲多个帧或数据包以提高性能,或出于必要的原因(考虑到 B 帧的情况)。
处理如下:
- 发送 “NULL” 数据包到 avcodec_send_packet()(解码)或 avcodec_send_frame()(编码)函数,而不是输入有效的数据。 这将进入 draining 模式。
- 循环调用 avcodec_receive_frame()(解码)或 avcodec_receive_packet()(编码)函数直到返回 AVERROR_EOF。函数将不会返回 AVERROR(EAGAIN),除非你忘记进入 draining 模式。
- 在解码重新恢复之前,必须使用 avcodec_flush_buffers() 重置编解码器。
其它
强烈建议使用上述 API。但是也可以脱离这个固定模式去调用函数。例如,您可以重复调用 avcodec_send_packet() 而无需调用 avcodec_receive_frame()。在这种情况下,avcodec_send_packet() 将成功,直到编解码器的内部缓冲区被填满(通常在初始化输入后,每个输出帧的大小为 1),然后返回 AVERROR(EAGAIN) 拒绝输入。一旦开始被拒绝输入,你别无选择,只能至少去读取一些输出。
并非所有编解码器都会遵循严格且可预测的数据流;唯一的保证是,一端的 send/receive 调用返回 AVERROR(EAGAIN) 意味着另一端的 receive/send 调用将成功,或者至少不会因 AVERROR(EAGAIN) 而失败。通常,没有编解码器会允许输入/输出的无限制缓冲。
至此。
原文如下:
* @ingroup libavc
* @defgroup lavc_encdec send/receive encoding and decoding API overview
* @{
*
* The avcodec_send_packet()/avcodec_receive_frame()/avcodec_send_frame()/
* avcodec_receive_packet() functions provide an encode/decode API, which
* decouples input and output.
*
* The API is very similar for encoding/decoding and audio/video, and works as
* follows:
* - Set up and open the AVCodecContext as usual.
* - Send valid input:
* - For decoding, call avcodec_send_packet() to give the decoder raw
* compressed data in an AVPacket.
* - For encoding, call avcodec_send_frame() to give the encoder an AVFrame
* containing uncompressed audio or video.
*
* In both cases, it is recommended that AVPackets and AVFrames are
* refcounted, or libavcodec might have to copy the input data. (libavformat
* always returns refcounted AVPackets, and av_frame_get_buffer() allocates
* refcounted AVFrames.)
* - Receive output in a loop. Periodically call one of the avcodec_receive_*()
* functions and process their output:
* - For decoding, call avcodec_receive_frame(). On success, it will return
* an AVFrame containing uncompressed audio or video data.
* - For encoding, call avcodec_receive_packet(). On success, it will return
* an AVPacket with a compressed frame.
*
* Repeat this call until it returns AVERROR(EAGAIN) or an error. The
* AVERROR(EAGAIN) return value means that new input data is required to
* return new output. In this case, continue with sending input. For each
* input frame/packet, the codec will typically return 1 output frame/packet,
* but it can also be 0 or more than 1.
*
* At the beginning of decoding or encoding, the codec might accept multiple
* input frames/packets without returning a frame, until its internal buffers
* are filled. This situation is handled transparently if you follow the steps
* outlined above.
*
* In theory, sending input can result in EAGAIN - this should happen only if
* not all output was received. You can use this to structure alternative decode
* or encode loops other than the one suggested above. For example, you could
* try sending new input on each iteration, and try to receive output if that
* returns EAGAIN.
*
* End of stream situations. These require "flushing" (aka draining) the codec,
* as the codec might buffer multiple frames or packets internally for
* performance or out of necessity (consider B-frames).
* This is handled as follows:
* - Instead of valid input, send NULL to the avcodec_send_packet() (decoding)
* or avcodec_send_frame() (encoding) functions. This will enter draining
* mode.
* - Call avcodec_receive_frame() (decoding) or avcodec_receive_packet()
* (encoding) in a loop until AVERROR_EOF is returned. The functions will
* not return AVERROR(EAGAIN), unless you forgot to enter draining mode.
* - Before decoding can be resumed again, the codec has to be reset with
* avcodec_flush_buffers().
*
* Using the API as outlined above is highly recommended. But it is also
* possible to call functions outside of this rigid schema. For example, you can
* call avcodec_send_packet() repeatedly without calling
* avcodec_receive_frame(). In this case, avcodec_send_packet() will succeed
* until the codec's internal buffer has been filled up (which is typically of
* size 1 per output frame, after initial input), and then reject input with
* AVERROR(EAGAIN). Once it starts rejecting input, you have no choice but to
* read at least some output.
*
* Not all codecs will follow a rigid and predictable dataflow; the only
* guarantee is that an AVERROR(EAGAIN) return value on a send/receive call on
* one end implies that a receive/send call on the other end will succeed, or
* at least will not fail with AVERROR(EAGAIN). In general, no codec will
* permit unlimited buffering of input or output.