Why does ffmpeg 4. X no longer need to call AV_ register_ Where's all

Posted by timecatcher on Sun, 05 Dec 2021 01:20:09 +0100

The old version of ffmpeg program, at the beginning of the program, is usually AV_ register_ After all. 4. X, this function is obsolete and does not need to be called. Let's take ffmpeg4.4 as an example. First, let's take a look at the change description of the official version features (doc\APIchanges):

2018-02-06 - 0694d87024 - lavf 58.9.100 - avformat.h
  Deprecate use of av_register_input_format(), av_register_output_format(),
  av_register_all(), av_iformat_next(), av_oformat_next().
  Add av_demuxer_iterate(), and av_muxer_iterate().

You can see av_register_all has been marked obsolete

So how does the new version of ffmpeg save calling this function

Start with AV_ register_ This function is to register various muxers, demuxers, encoders, decoders, etc., and string various categories into a linked list. For the analysis of this function, please refer to Dr. Lei's chapter: ffmpeg source code simple analysis: av_register_all() .

Let's take multiplexers as an example. In the new version, all multiplexers are organized into a global static array. It is located in libavformat\format.c, as follows:

libavformat\format.c:

 static const AVOutputFormat * const muxer_list[] = {
    &ff_a64_muxer,
    &ff_ac3_muxer,
    &ff_adts_muxer,
    &ff_adx_muxer,
    &ff_aiff_muxer,
    &ff_alp_muxer,
    &ff_amr_muxer,
    ......,
    }

Some students will find that their source code does not have libavformat\muxer_list.c file. This is because this file is generated according to the actual configuration when the configure command is executed. As follows, this file will be automatically generated after the configure command is executed

configure:

# generate the lists of enabled components
print_enabled_components(){
    file=$1
    struct_name=$2
    name=$3
    shift 3
    echo "static const $struct_name * const $name[] = {" > $TMPH
    for c in $*; do
        if enabled $c; then
            case $name in
                filter_list)
                    eval c=\$full_filter_name_${c%_filter}
                ;;
                indev_list)
                    c=${c%_indev}_demuxer
                ;;
                outdev_list)
                    c=${c%_outdev}_muxer
                ;;
            esac
            printf "    &ff_%s,\n" $c >> $TMPH
        fi
    done
    if [ "$name" = "filter_list" ]; then
        for c in asrc_abuffer vsrc_buffer asink_abuffer vsink_buffer; do
            printf "    &ff_%s,\n" $c >> $TMPH
        done
    fi
    echo "    NULL };" >> $TMPH
    cp_if_changed $TMPH $file
}
......
print_enabled_components libavformat/muxer_list.c AVOutputFormat muxer_list $MUXER_LIST
......

In order to be compatible with the old code, the new version of AV_ register_ The all function remains

libavformat\allformats.c:

void av_register_all(void)
{
    ff_thread_once(&av_format_next_init, av_format_init_next);
}

static void av_format_init_next(void)
{
......

    for (int i = 0; (out = (AVOutputFormat*)muxer_list[i]); i++) {
        if (prevout)
            prevout->next = out;
        prevout = out;
    }

......
}

As you can see, call av_register_all can connect all multiplexers into a linked list through the next pointer. Is the chain connected with next still useful? Is the next pointer still functional? We don't call av_register_all, which logic will string all multiplexers into a linked list?

To solve these mysteries, we should start with the traversal of the multiplexer. The traversal of the multiplexer in the old version of ffmpeg is through AV_ oformat_ The function of the new version of ffmpeg is also abandoned. AV is used to traverse the multiplexer_ muxer_ iterate.

libavformat\allformats.c:

const AVOutputFormat *av_muxer_iterate(void **opaque)
{
    static const uintptr_t size = sizeof(muxer_list)/sizeof(muxer_list[0]) - 1;
    uintptr_t i = (uintptr_t)*opaque;
    const AVOutputFormat *f = NULL;

    if (i < size) {
        f = muxer_list[i];
    } else if (outdev_list) {
        f = outdev_list[i - size];
    }

    if (f)
        *opaque = (void*)(i + 1);
    return f;
}

As you can see, av_muxer_iterate traversal does not depend on the next pointer. It is done through the array subscript. We can traverse all multiplexers through the following code

#include <stdio.h>
#include "libavformat/avformat.h"

int main()
{
  void *ofmt_opaque = NULL;
  const AVOutputFormat *ofmt = NULL;
  while ((ofmt = av_muxer_iterate(&ofmt_opaque)))
  {
    printf("short name:%-30s  long name:%-50s next point:0x%llx \n",ofmt->name,ofmt->long_name,ofmt->next);
  }
}

The output is as follows:

short name:a64                             long name:a64 - video for Commodore 64                       next point:0x0
short name:ac3                             long name:raw AC-3                                           next point:0x0
short name:adts                            long name:ADTS AAC (Advanced Audio Coding)                   next point:0x0
short name:adx                             long name:CRI ADX                                            next point:0x0
short name:aiff                            long name:Audio IFF                                          next point:0x0
short name:alp                             long name:LEGO Racers ALP                                    next point:0x0
short name:amr                             long name:3GPP AMR                                           next point:0x0
short name:amv                             long name:AMV                                                next point:0x0

......

I specially printed the value of the next pointer to show that the traversal does not actually depend on next. Next is a null pointer

There is another question, the old version of the traversal function AV_ oformat_ Can next still be used? The answer is yes. And there is no need to call AV_ register_ It can be used in the case of all

libavformat\allformats.c

AVOutputFormat *av_oformat_next(const AVOutputFormat *f)
{
    ff_thread_once(&av_format_next_init, av_format_init_next);

    if (f)
......
        return f->next;
......        
    else {
        void *opaque = NULL;
        return (AVOutputFormat *)av_muxer_iterate(&opaque);
    }
}

You should note that AV_ oformat_ The first line of code of the next function, the first line of code and AV_ register_ The logic of all is the same. That is, if the old traversal method is used, all multiplexers will be concatenated into a linked list through the next pointer again