4

I'm looking at re-muxing some containers holding audio and video such that I extract the best, first audio stream, and store it in a new container where e.g. only the audio stream is present.

The output context for FFmpeg is created like so:

AVFormatContext* output_context = NULL;
avformat_alloc_output_context2( &output_context, NULL, "mp4", NULL );

I have a shortlist of acceptable outputs, e.g. MP4, M4A, etc … essentially those that are readable by Apple's Audio File Services:

kAudioFileAIFFType              = 'AIFF',
kAudioFileAIFCType              = 'AIFC',
kAudioFileWAVEType              = 'WAVE',
kAudioFileSoundDesigner2Type    = 'Sd2f',
kAudioFileNextType              = 'NeXT',
kAudioFileMP3Type               = 'MPG3',   // mpeg layer 3
kAudioFileMP2Type               = 'MPG2',   // mpeg layer 2
kAudioFileMP1Type               = 'MPG1',   // mpeg layer 1
kAudioFileAC3Type               = 'ac-3',
kAudioFileAAC_ADTSType          = 'adts',
kAudioFileMPEG4Type             = 'mp4f',
kAudioFileM4AType               = 'm4af',
kAudioFileM4BType               = 'm4bf',
kAudioFileCAFType               = 'caff',
kAudioFile3GPType               = '3gpp',
kAudioFile3GP2Type              = '3gp2',
kAudioFileAMRType               = 'amrf'

My question is this : is there an easy API in FFmpeg that can be leveraged to choose a compatible output container given the codec the audio stream is in?

Dan
  • 1,258
  • 1
  • 10
  • 22
  • 1
    The following resource seems to indicate that certain audio codecs are embeddable in MP4 containers, but https://en.wikipedia.org/wiki/Comparison_of_video_container_formats I have had issues with e.g. MP3 within MP4, and having Apple's APIs be happy to decode them. – Dan Nov 14 '17 at 02:42

3 Answers3

5

There's a dynamic approach to that problem. This enumerates the codecs for each container, but you also get the inverse:

// enumerate all codecs and put into list
std::vector<AVCodec*> encoderList;
AVCodec * codec = nullptr;
while (codec = av_codec_next(codec))
{
    // try to get an encoder from the system
    auto encoder = avcodec_find_encoder(codec->id);
    if (encoder)
    {
        encoderList.push_back(encoder);
    }
}
// enumerate all containers
AVOutputFormat * outputFormat = nullptr;
while (outputFormat = av_oformat_next(outputFormat))
{
    for (auto codec : encoderList)
    {
        // only add the codec if it can be used with this container
        if (avformat_query_codec(outputFormat, codec->id, FF_COMPLIANCE_STRICT) == 1)
        {
            // add codec for container
        }
    }
}

If you just want specific containers or codecs you can use a whitelist with their name or id fields and use that when enumerating.

Bim
  • 1,008
  • 1
  • 10
  • 29
  • I tried this, and it seems to give back sensible results, however for certain formats `avformat_query_codec(outputFormat, codec->id, FF_COMPLIANCE_STRICT)` returns -1 for all codecs, hence no supported codecs for those formats. Notably the `mp3` format has zero supported codecs. I was expecting that one of AVCodecs `libmp3lame` or `mp3` would be supported. On the command line I can do `ffmpeg -i input -c:a libmp3lame -f mp3 out.mp3` without any problem. – Mikael Finstad Dec 12 '20 at 22:50
1

For each individual muxer, there is usually a codec tag writing function.That function will check against a list in another source file or work through a switch statement in the same. There is no central roster or container-matching utility function. Your best bet is to identify the codec id in libavcodec/allcodecs.c and then grep in libavformat/ for that ID, particularly within files suffixed with enc e.g. matroskaenc.c.

Gyan
  • 85,394
  • 9
  • 169
  • 201
  • Interesting … for example, I was able to find in `isom.c` a list of audio codecs that FFmpeg seems to associate with MP4, and other similar lists. Looks like I'll have to roll some matching code on my own, or fallback strategies when combinations fail. – Dan Nov 14 '17 at 21:08
  • There's a better way. See my answer. – Bim Feb 14 '19 at 13:10
  • This works partially - for example isom.c gives all the .mov codecs. Using `AV_CODEC_ID_PRORES` and using grep only shows matroska and mxf – moi Sep 12 '21 at 11:46
  • The tag list has been shifted to isom_tags.c which contains ProRes. – Gyan Sep 12 '21 at 13:09
0

the ffmpeg API has changed since, enumerating all containers and codecs can be done now like

void enumerate() {
    // enumerate all codecs and put into list
    std::vector<const AVCodec*> encoderList;

    void* codecState = nullptr; //start with null
    const AVCodec* codec = av_codec_iterate(&codecState);
    while (codec) {
        encoderList.push_back(codec);
        codec = av_codec_iterate(&codecState);
    }

    // enumerate all containers available for muxing
    void* muxerState = nullptr;
    const AVOutputFormat* ofmt = av_muxer_iterate(&muxerState);
    while (ofmt) {
        std::set<std::string> codecNames;
        for (auto codec : encoderList) {
            // check if codec can be used in this container
            if (avformat_query_codec(ofmt, codec->id, FF_COMPLIANCE_NORMAL) == 1) {
                // check for audio codecs here
                if (avcodec_get_type(codec->id) == AVMEDIA_TYPE_AUDIO) {
                    codecNames.insert(avcodec_get_name(codec->id));
                }
            }
        }
        // print unique format and codec names
        for (auto& codecName : codecNames) {
            std::printf("format '%s' codec '%s'\n", ofmt->name, codecName.c_str());
        }
        // get next format
        ofmt = av_muxer_iterate(&muxerState);
    }
}
ray_ray_ray
  • 316
  • 1
  • 14