[FFmpeg-devel] [PATCH v16 01/16] global: Prepare AVFrame for subtitle handling

Soft Works softworkz at hotmail.com
Sat Nov 27 04:57:29 EET 2021



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces at ffmpeg.org> On Behalf Of Andreas
> Rheinhardt
> Sent: Friday, November 26, 2021 11:35 AM
> To: ffmpeg-devel at ffmpeg.org
> Subject: Re: [FFmpeg-devel] [PATCH v16 01/16] global: Prepare AVFrame for
> subtitle handling

Hi Andreas,

thanks for the detailed review. There was hardly anything to object or disagree.
Everything I don't respond to is implemented as suggested.

> As has already been said by others, this should be split: One patch for
> the actual new libavutil additions, one patch for the lavc decoding API,
> one patch for the encoding API, one patch for every user that is made to
> use the new APIs.

By keeping the AVSubtitle definition in its original location, it was 
possible to split this as suggested on IRC.
Thanks.

> This is not how you deprecate something: You have to add a @deprecated
> doxygen comment as well as the attribute_deprecated to make some noise.

I didn’t want the noise before things get serious ;-)

> 
> Actually, you should deprecate the whole AVSubtitle API.

Done. Do we deprecate structs as well?

Also, there's a function avcodec_get_subtitle_rect_class().

I have no idea whether it ever had any practical use. A similar function
above (avcodec_get_frame_class) is deprecated.


> > +/**
> > + * Return subtitle format from a codec descriptor
> > + *
> > + * @param codec_descriptor codec descriptor
> > + * @return                 the subtitle type (e.g. bitmap, text)
> > + */
> > +enum AVSubtitleType av_get_subtitle_format_from_codecdesc(const
> AVCodecDescriptor *codec_descriptor);
> 
> Do we need this function? It seems too trivial and as Anton has pointed
> out is flawed. And anyway, this would belong into codec_desc.h.

I had replied to Anton why it's not flawed. Moved it to codec_desc and
named it as Anton had suggested.


> >  int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const
> AVPacket *avpkt)
> >  {
> >      AVCodecInternal *avci = avctx->internal;
> > @@ -590,6 +618,9 @@ int attribute_align_arg
> avcodec_send_packet(AVCodecContext *avctx, const AVPacke
> >      if (avpkt && !avpkt->size && avpkt->data)
> >          return AVERROR(EINVAL);
> >
> > +    if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
> > +        return decode_subtitle_shim(avctx, avci->buffer_frame, avpkt);
> 
> 1. This is missing a check whether buffer_frame is empty or not; in the
> latter case, you need to return AVERROR(EAGAIN); instead
> decode_subtitle_shim() destroys what is already there.
> (Said check is currently offloaded to the BSF API in the audio/video
> codepath. This is actually a violation of the BSF API.)

Added that check.

> 2. Flushing is not really implemented well:
> a) It is legal to call avcodec_send_packet() with avpkt == NULL to
> indicate EOF. If I am not mistaken, decode_subtitle2_priv() will just
> crash in this case.

Like all subtitle decoders would when supplying a NULL packet.
What should I return when a null packet is supplied?
EAGAIN or EOF?

> b) avcodec_receive_frame() only returns what is in buffer_frame; it
> never calls decode_subtitle2_priv() or the underlying decode function at
> all. This basically presumes that a subtitle decoder can only return one
> subtitle after flushing. I don't know whether this is true for our
> decoders with the delay cap.

The only one I could see with that flag is cc_dec, which is a special case
anyway as it doesn't work on regular streams.
(even this one would crash when providing a NULL packet)

> c) avcodec_receive_frame() seems to never return AVERROR_EOF after
> flushing, but always AVERROR(EAGAIN) (if the delay cap is set, the first
> call to avcodec_receive_frame() after flushing can also result in a
> frame being returned). Yet this makes no sense and is IMO an API
> violation on lavc's part.
> (While we have subtitle decoders with the delay cap, I don't know
> whether this is actually tested by fate and whether all code actually
> flushes subtitle decoders at all.)

I don't think so.


> > diff --git a/libavfilter/vf_subtitles.c b/libavfilter/vf_subtitles.c
> > index 377160c72b..6b54cf669b 100644
> > --- a/libavfilter/vf_subtitles.c
> > +++ b/libavfilter/vf_subtitles.c
> > @@ -35,14 +35,12 @@
> >  # include "libavformat/avformat.h"
> >  #endif
> >  #include "libavutil/avstring.h"
> > -#include "libavutil/imgutils.h"
> >  #include "libavutil/opt.h"
> >  #include "libavutil/parseutils.h"
> >  #include "drawutils.h"
> >  #include "avfilter.h"
> >  #include "internal.h"
> >  #include "formats.h"
> > -#include "video.h"
> >
> >  typedef struct AssContext {
> >      const AVClass *class;
> > @@ -292,6 +290,29 @@ static int attachment_is_font(AVStream * st)
> >      return 0;
> >  }
> >
> > +static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame,
> AVPacket *pkt)
> > +{
> > +    int ret;
> > +
> > +    *got_frame = 0;
> 
> You don't really need this: You could just return 0 if no frame was
> returned, 1 if a frame was returned and < 0 on (actual, not EAGAIN) errors.
> 
> > +
> > +    if (pkt) {
> > +        ret = avcodec_send_packet(avctx, pkt);
> > +        // In particular, we don't expect AVERROR(EAGAIN), because we read
> all
> > +        // decoded frames with avcodec_receive_frame() until done.
> 
> Where do you do this? For this one would need to call
> avcodec_receive_frame() in a loop until it returns EAGAIN, but you don't
> do this.

This decode() function is copied from ffmpeg.c


> > +static int get_subtitle_buffer(AVFrame *frame)
> > +{
> > +    // Buffers in AVFrame->buf[] are not used in case of subtitle frames.
> > +    // To accomodate with existing code, checking ->buf[0] to determine
> > +    // whether a frame is ref-counted or has data, we're adding a 1-byte
> > +    // buffer here, which marks the subtitle frame to contain data.
> 
> This is a terrible hack that might be necessary for now. But I don't see
> anything

I'm not sure about the second half sentence, but what I can say is that I 
had taken the time to try and replace the checks for ->buf[0] to a frame
field, but it turned out that this change would be so big that it cannot
be part of the subtitle patchset.
(also talked about this with Hendrik a while ago)



> > +    for (i = 0; i < frame->num_subtitle_areas; i++) {
> > +        area = frame->subtitle_areas[i];
> > +        if (area) {
> 
> Who sets an incorrect num_subtitle_areas?

Maybe code that hasn't gotten reviewed by you ;-)

> > +    /**
> > +     * Display start time, relative to packet pts, in ms.
> > +     */
> > +    uint32_t subtitle_start_time;
> > +
> > +    /**
> > +     * Display end time, relative to packet pts, in ms.
> > +     */
> > +    uint32_t subtitle_end_time;
> 
> Hardcoding a millisecond timestamp precision into the API doesn't seem
> good. As does using 32bit.

Our internal text subtitle format is defined to be ASS and those
variables are reflecting just that.


> > +/**
> > + * Copies subtitle data from AVFrame to AVSubtitle.
> > + *
> > + * @deprecated This is a compatibility method for interoperability with
> > + * the legacy subtitle API.
> > + */
> > +void av_frame_get_subtitle(AVSubtitle *sub, AVFrame *frame);
> 
> 1. Missing const.
> 2. Wrong return type.
> 3. I don't see a need for this compatibility at all. As explained below,
> the only user that needs something like this is libavcodec and then it
> should live in libavcodec.
> The last two points apply to both av_frame_get_subtitle and
> av_frame_put_subtitle. Notice that the only use of any of these
> functions outside of libavcodec is remove later again (patch #8 adds an
> av_frame_get_subtitle() in ffmpeg.c, #16 removes it again) which shows
> that such a compatibility stuff is not needed outside of libavcodec.

Moved to avcodec as ff_ functions.


> > +    uint32_t pal[256]; ///< RGBA palette for the bitmap.
> > +
> > +    char *text;        ///< 0-terminated plain UTF-8 text
> > +    char *ass;         ///< 0-terminated ASS/SSA compatible event line.
> > +
> > +} AVSubtitleArea;
> 
> I presume that sizeof(AVSubtitleArea) is not supposed to be part of the
> public API/ABI (after all, you used an array of pointers to
> AVSubtitleArea in AVFrame). This needs to be documented.

Makes sense, but I'm not sure how and where?



> > +/**
> > + * Return a subtitle format corresponding to name, or AV_SUBTITLE_FMT_NONE
> > + * on error.
> > + *
> > + * @param name Subtitle format name.
> > + */
> > +enum AVSubtitleType av_get_subtitle_fmt(const char *name);
> 
> Do we need this? Given that there are many possible ways to name the
> subtitles, it doesn't seem useful to me. The only way to be sure to use
> the "authoritative" name for a subtitle format is by
> av_get_subtitle_fmt_name(). But then one doesn't need
> av_get_subtitle_fmt() at all.

I've tried to implement everything analog to the functionality we have
for audio and video. This corresponds to av_get_pix_fmt().


> > + */
> > +int av_subtitle_area2area(AVSubtitleArea *dst, const AVSubtitleArea *src,
> int copy_data);
> 
> This documentation does not mention that dst should be "blank". But this
> actually poses a problem: How does the user know that an AVSubtitleArea
> is blank given that ? For AVFrames the answer is this: An AVFrame is
> blank if it comes directly from av_frame_alloc() or av_frame_unref().
> But there is no equivalent for these functions here.
> Furthermore, is this API supposed to be a standalone API or is
> AVSubtitleArea something that is always supposed to be part of an
> AVFrame? The usage in this patchset suggests the latter:
> av_subtitle_area2area() is only used in frame.c.

AVSubtitleArea is always supposed to be part of an AVFrame.
I had thought the copy function might be useful outside, but I didn't 
need it in any of the filter.
Made it private in AVFrame now.


> > +/**
> > + * Copy data from @ref AVSubtitleArea to @ref AVSubtitleRect.
> > + *
> > + * @param dst        The target rect (@ref AVSubtitleRect).
> > + * @param src        The source area (@ref AVSubtitleArea).
> > + *
> > + * @return 0 on success.
> > + *
> > + * @deprecated This is a compatibility method for interoperability with
> > + * the legacy subtitle API.
> > + */
> > +int av_subtitle_area2rect(AVSubtitleRect *dst, const AVSubtitleArea *src);
> > +
> > +/**
> > + * Copy data from @ref AVSubtitleRect to @ref AVSubtitleArea.
> > + *
> > + * @param dst        The source area (@ref AVSubtitleArea).
> > + * @param src        The target rect (@ref AVSubtitleRect).
> > + *
> > + * @return 0 on success.
> > + *
> > + * @deprecated This is a compatibility method for interoperability with
> > + * the legacy subtitle API.
> > + */
> > +int av_subtitle_rect2area(AVSubtitleArea *dst, const AVSubtitleRect *src);
> 
> Do we need compatibility with the legacy API at all? I only see two
> usecases for this:
> 
> 1. One has a huge project using AVSubtitles that uses AVSubtitle so
> pervasively that one can't transition to the new API in one step. But I
> doubt that that's a thing.
> 2. One is forced to provide compatibility with the legacy API while also
> providing an API based upon the new API. This is of course the situation
> with libavcodec, which for this reason needs such compatibility
> functions (in libavcodec!). But I don't see a second user with the same
> needs, so I don't see why these functions (which would actually need to
> be declared as deprecated from the very beginning if public) should be
> public.

Made those private in avcodec.

Thanks again,
softworkz


More information about the ffmpeg-devel mailing list