[FFmpeg-devel] [PATCH 1/3] lavc: introduce a new decoding/encoding API with decoupled input/output

Michael Niedermayer michael at niedermayer.cc
Wed Apr 20 16:54:30 CEST 2016


On Tue, Apr 19, 2016 at 11:49:11AM +0200, wm4 wrote:
> Until now, the decoding API was restricted to outputting 0 or 1 frames
> per input packet. It also enforces a somewhat rigid dataflow in general.
> 
> This new API seeks to relax these restrictions by decoupling input and
> output. Instead of doing a single call on each decode step, which may
> consume the packet and may produce output, the new API requires the user
> to send input first, and then ask for output.
> 
> For now, there are no codecs supporting this API. The API can work with
> codecs using the old API, and most code added here is to make them
> interoperate. The reverse is not possible, although for audio it might.
> 
> From Libav commit 05f66706d182eb0c36af54d72614bf4c33e957a9.
> 
> Signed-off-by: Anton Khirnov <anton at khirnov.net>
> ---
> This commit was skipped when merging from Libav.
> ---
>  doc/APIchanges         |   5 +
>  libavcodec/avcodec.h   | 241 ++++++++++++++++++++++++++++++++++++++++-
>  libavcodec/internal.h  |  13 +++
>  libavcodec/utils.c     | 286 ++++++++++++++++++++++++++++++++++++++++++++++++-
>  libavcodec/version.h   |   2 +-
>  libavformat/avformat.h |   4 +-
>  6 files changed, 543 insertions(+), 8 deletions(-)
> 
> diff --git a/doc/APIchanges b/doc/APIchanges
> index 8a14e77..ef69e98 100644
> --- a/doc/APIchanges
> +++ b/doc/APIchanges
> @@ -15,6 +15,11 @@ libavutil:     2015-08-28
>  
>  API changes, most recent first:
>  
> +2016-xx-xx - xxxxxxx - lavc 57.36.0 - avcodec.h
> +  Add a new audio/video encoding and decoding API with decoupled input
> +  and output -- avcodec_send_packet(), avcodec_receive_frame(),
> +  avcodec_send_frame() and avcodec_receive_packet().
> +
>  2016-xx-xx - xxxxxxx - lavc 57.15.0 - avcodec.h
>    Add a new bitstream filtering API working with AVPackets.
>    Deprecate the old bistream filtering API.
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index 9a8a0f0..61de80e 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -73,6 +73,95 @@
>   */
>  
>  /**
> + * @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 decoder 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.
> + *
> + * 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. In
> + * general, no codec will permit unlimited buffering of input or output.
> + *
> + * This API replaces the following legacy functions:
> + * - avcodec_decode_video2() and avcodec_decode_audio4():
> + *   Use avcodec_send_packet() to feed input to the decoder, then use
> + *   avcodec_receive_frame() to receive decoded frames after each packet.
> + *   Unlike with the old video decoding API, multiple frames might result from
> + *   a packet. For audio, splitting the input packet into frames by partially
> + *   decoding packets becomes transparent to the API user. You never need to
> + *   feed an AVPacket to the API twice.
> + *   Additionally, sending a flush/draining packet is required only once.
> + * - avcodec_encode_video2()/avcodec_encode_audio2():
> + *   Use avcodec_send_frame() to feed input to the encoder, then use
> + *   avcodec_receive_packet() to receive encoded packets.
> + *   Providing user-allocated buffers for avcodec_receive_packet() is not
> + *   possible.
> + * - The new API does not handle subtitles yet.
> + *
> + * Mixing new and old function calls on the same AVCodecContext is not allowed,

> + * and will result in arbitrary behavior.
                        ^^^^^^^^^^
probably "undefined" is the better word but its fine as is too


[...]
> @@ -3522,6 +3613,21 @@ typedef struct AVCodec {
>      int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, AVPacket *avpkt);
>      int (*close)(AVCodecContext *);
>      /**
> +     * Decode/encode API with decoupled packet/frame dataflow. The API is the
> +     * same as the avcodec_ prefixed APIs (avcodec_send_frame() etc.), except
> +     * that:
> +     * - never called if the codec is closed or the wrong type,
> +     * - AVPacket parameter change side data is applied right before calling
> +     *   AVCodec->send_packet,
> +     * - if AV_CODEC_CAP_DELAY is not set, drain packets or frames are never sent,
> +     * - only one drain packet is ever passed down (until the next flush()),
> +     * - a drain AVPacket is always NULL (no need to check for avpkt->size).
> +     */
> +    int (*send_frame)(AVCodecContext *avctx, const AVFrame *frame);
> +    int (*send_packet)(AVCodecContext *avctx, const AVPacket *avpkt);
> +    int (*receive_frame)(AVCodecContext *avctx, AVFrame *frame);
> +    int (*receive_packet)(AVCodecContext *avctx, AVPacket *avpkt);
> +    /**
>       * Flush buffers.
>       * Will be called when seeking
>       */

This breaks ABI
the functions must be added after caps_internal or a major bump is
needed
caps_internal is accessed in libavformat/utils.c it seems


[...]
> @@ -2655,6 +2692,243 @@ void avsubtitle_free(AVSubtitle *sub)
>      memset(sub, 0, sizeof(AVSubtitle));
>  }
>  
> +static int do_decode(AVCodecContext *avctx, AVPacket *pkt)
> +{
> +    int got_frame;
> +    int ret;
> +
> +    av_assert0(!avctx->internal->buffer_frame->buf[0]);
> +
> +    if (!pkt)
> +        pkt = avctx->internal->buffer_pkt;
> +
> +    // This is the lesser evil. The field is for compatibility with legacy users
> +    // of the legacy API, and users using the new API should not be forced to
> +    // even know about this field.
> +    avctx->refcounted_frames = 1;
> +

> +    // Some codecs (at least wma lossless) will crash when feeding drain packets
> +    // after EOF was signaled.
> +    if (avctx->internal->draining_done)
> +        return AVERROR_EOF;

maybe decoders ca be fixed to avoid this, can you add a TODO note or
something in the code maybe something like
// TODO: check if decoders can be changed to avoid this check and the draining_done field

that is unless this is needed for something else

rest should be ok

thx

[...]
-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

While the State exists there can be no freedom; when there is freedom there
will be no State. -- Vladimir Lenin
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: Digital signature
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20160420/d912c497/attachment.sig>


More information about the ffmpeg-devel mailing list