Search code examples
c++ffmpeg

How do we get the channel layout syntax from new ffmpeg AVCodecParameters?


In ffmpeg 6.0 I want to get the channel layout syntax name like: FFmpeg Channel Layout Syntax

Then I could use some filter like aformat :

aformat=sample_fmts=u8|s16:channel_layouts=stereo

I did use AVCodecParameters get the ch_layout, but I don't know how to change it to channel layout syntax, like: stereo, or 5.1.

Is there any function we could get it? thanks.


Solution

  • We may use av_channel_layout_describe for getting string describing the channel layout.

    int av_channel_layout_describe(const AVChannelLayout *channel_layout, char *buf, size_t buf_size):

    Get a human-readable string describing the channel layout properties.


    For getting the description, we have to find the channel layout (AVChannelLayout structure) of the audio stream first.

    • Open an input file and find stream information:

       avformat_open_input(&avFmtCtx, input_file, nullptr, nullptr)
       avformat_find_stream_info(avFmtCtx, nullptr)
      
    • Find the index of the relevant audio stream (first audio stream for example), and get codec parameters:

       int streamIndex = -1;
       for (int si = 0; si < (int)avFmtCtx->nb_streams; si++)
       {
           if (avFmtCtx->streams[si]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
           {
               avCodecParams = avFmtCtx->streams[si]->codecpar;
               streamIndex = si;
               break;
           }
       }
      
    • Get channel layout description:

       char chLayoutDescription[128];
       int sts = av_channel_layout_describe(&avCodecParams->ch_layout, chLayoutDescription, sizeof(chLayoutDescription));
      

    Code sample:

    extern "C"
    {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavutil/avutil.h>
    }
    
    #include <stdio.h>
    
    //Create "stereo.mp4" for testing:
    //ffmpeg -y -f lavfi -i testsrc=size=192x108:rate=1 -f lavfi -i sine=frequency=100 -f lavfi -i sine=frequency=1000 -map_channel 1.0.0 -map_channel 2.0.0 -acodec aac -ar 44100 -ac 2 -t 10 stereo.mp4
    
    //Create "six_channels.mp4" for testing:
    //ffmpeg -y -f lavfi -i testsrc=size=192x108:rate=1 -f lavfi -i sine=frequency=100 -f lavfi -i sine=frequency=500 -f lavfi -i sine=frequency=600 -f lavfi -i sine=frequency=700 -f lavfi -i sine=frequency=800 -f lavfi -i sine=frequency=900 -map_channel 1.0.0 -map_channel 2.0.0 -map_channel 3.0.0 -map_channel 4.0.0 -map_channel 5.0.0 -map_channel 6.0.0 -acodec aac -ar 44100 -ac 6 -t 10 six_channels.mp4
    
    
    int main()
    {
        const char *input_file = "stereo.mp4";              //Output: ch_layout_description = stereo
        //const char *input_file = "six_channels.mp4";      //Output: ch_layout_description = 5.1
        
        AVFormatContext* avFmtCtx = avformat_alloc_context();
        AVStream* avFirstAudioStream = nullptr;    
        AVCodecParameters* avCodecParams = nullptr;
    
        //https://stackoverflow.com/q/76083629/4926757
        if (avformat_open_input(&avFmtCtx, input_file, nullptr, nullptr) != 0)
        {
            fprintf(stderr, "Couldn't open file with avformat_open_input\n");
            return 1;
        }
    
        if (avformat_find_stream_info(avFmtCtx, nullptr) < 0) 
        {
            fprintf(stderr, "Couldn't get stream info with avformat_find_stream_info\n");
            return 1;
        }
    
        //Find the index of the first audio stream, and get codec parameters
        int streamIndex = -1;
        for (int si = 0; si < (int)avFmtCtx->nb_streams; si++)
        {
            if (avFmtCtx->streams[si]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
            {
                avCodecParams = avFmtCtx->streams[si]->codecpar;
                streamIndex = si;
                break;
            }
        }
    
        //Get channel layout description:
        ////////////////////////////////////////////////////////////////////////////
        char chLayoutDescription[128];
        int sts = av_channel_layout_describe(&avCodecParams->ch_layout, chLayoutDescription, sizeof(chLayoutDescription));
    
        if (sts < 0)
        {
            fprintf(stderr, "Error: av_channel_layout_describe returned status %d\n", sts); //Error...
            return 1;
        }
    
        printf("ch_layout_description = %s\n", chLayoutDescription);  //Print channel layout description
        ////////////////////////////////////////////////////////////////////////////
    
        avformat_free_context(avFmtCtx);
        return 0;
    }
    

    Update:

    Reading the channel layout of dshow microphone device:

    When reading the channel layout of the microphone, the channel layout description is 2 channels and not stereo.

    Looking at avCodecParams->ch_layout of "stereo.mp4" file, we may see that:

    • order = AV_CHANNEL_ORDER_NATIVE
    • nb_channels = 2

    Looking at avCodecParams->ch_layout of the microphone (using the debugger), we may see that:

    • order = AV_CHANNEL_ORDER_UNSPEC
    • nb_channels = 2

    We may conclude that the channels order of the microphone is unspecified.
    When the channels order is unspecified, FFmpeg reports 2 channels instead of stereo.

    Looking at my microphone, it looks like the two microphones holds are one very close, and located one above the other (while stereo layout supposed to be left and right with some distance).
    I don't know if there are microphone models that reports stereo layout, but 2 channels layout seems correct for my microphone.


    Getting the default channel layout for the given number of channels:

    We may use av_channel_layout_default for getting the default channel layout for the given number of channels (i.e getting stereo for 2 channels).

    Code sample for getting the default channel layout:

    char defaultChLayoutDescription[128];
    AVChannelLayout default_ch_layout;
    
    av_channel_layout_default(&default_ch_layout, avCodecParams->ch_layout.nb_channels);
    
    sts = av_channel_layout_describe(&default_ch_layout, defaultChLayoutDescription, sizeof(defaultChLayoutDescription));
    

    Code sample for getting the channel layout of a microphone, and also getting the default layout for 2 channels:

    extern "C"
    {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavdevice/avdevice.h>
    #include <libavutil/avutil.h>
    }
    
    #include <stdio.h>
    
    //Create "stereo.mp4" for testing:
    //ffmpeg -y -f lavfi -i testsrc=size=192x108:rate=1 -f lavfi -i sine=frequency=100 -f lavfi -i sine=frequency=1000 -map_channel 1.0.0 -map_channel 2.0.0 -acodec aac -ar 44100 -ac 2 -t 10 stereo.mp4
    
    //Create "six_channels.mp4" for testing:
    //ffmpeg -y -f lavfi -i testsrc=size=192x108:rate=1 -f lavfi -i sine=frequency=100 -f lavfi -i sine=frequency=500 -f lavfi -i sine=frequency=600 -f lavfi -i sine=frequency=700 -f lavfi -i sine=frequency=800 -f lavfi -i sine=frequency=900 -map_channel 1.0.0 -map_channel 2.0.0 -map_channel 3.0.0 -map_channel 4.0.0 -map_channel 5.0.0 -map_channel 6.0.0 -acodec aac -ar 44100 -ac 6 -t 10 six_channels.mp4
    
    //Listing dshow devices:
    //ffmpeg -list_devices true -f dshow -i dummy
    
    int main()
    {
        //const char *input_file = "stereo.mp4";              //Output: ch_layout_description = stereo
        //const char *input_file = "six_channels.mp4";      //Output: ch_layout_description = 5.1
    
    
        //Capture dshow microphone
        ////////////////////////////////////////////////////////////////////////////
        avdevice_register_all();
        const char *input_file = "audio=""@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\\wave_{C7D33711-2FD7-4800-8D99-72ED74527121}""";  //Using Alternative name for my audio device.
    
        const AVInputFormat* ifmt = av_find_input_format("dshow");
        ////////////////////////////////////////////////////////////////////////////
        
        AVFormatContext* avFmtCtx = avformat_alloc_context();
        AVStream* avFirstAudioStream = nullptr;    
        AVCodecParameters* avCodecParams = nullptr;
    
        //https://stackoverflow.com/q/76083629/4926757
        if (avformat_open_input(&avFmtCtx, input_file, ifmt, nullptr) != 0)
        {
            fprintf(stderr, "Couldn't open file with avformat_open_input\n");
            return 1;
        }
    
        if (avformat_find_stream_info(avFmtCtx, nullptr) < 0) 
        {
            fprintf(stderr, "Couldn't get stream info with avformat_find_stream_info\n");
            return 1;
        }
    
        //Find the index of the first audio stream, and get codec parameters
        int streamIndex = -1;
        for (int si = 0; si < (int)avFmtCtx->nb_streams; si++)
        {
            if (avFmtCtx->streams[si]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
            {
                avCodecParams = avFmtCtx->streams[si]->codecpar;
                streamIndex = si;
                break;
            }
        }
    
        
        //Get channel layout description:
        ////////////////////////////////////////////////////////////////////////////
        char chLayoutDescription[128];
        int sts = av_channel_layout_describe(&avCodecParams->ch_layout, chLayoutDescription, sizeof(chLayoutDescription));
    
        if (sts < 0)
        {
            fprintf(stderr, "Error: av_channel_layout_describe returned status %d\n", sts); //Error...
            return 1;
        }
        ////////////////////////////////////////////////////////////////////////////
    
        //Get the default channel layout for the given number of channels:
        ////////////////////////////////////////////////////////////////////////////
        char defaultChLayoutDescription[128];
        AVChannelLayout default_ch_layout;
    
        av_channel_layout_default(&default_ch_layout, avCodecParams->ch_layout.nb_channels);
    
        sts = av_channel_layout_describe(&default_ch_layout, defaultChLayoutDescription, sizeof(defaultChLayoutDescription));
    
        if (sts < 0)
        {
            fprintf(stderr, "Error: av_channel_layout_describe returned status %d\n", sts); //Error...
            return 1;
        }    
        ////////////////////////////////////////////////////////////////////////////
    
        avformat_close_input(&avFmtCtx);
        avformat_free_context(avFmtCtx);
    
        fprintf(stderr, "\nchLayoutDescription = %s\n", chLayoutDescription);  //Print channel layout description //2 channels
    
        fprintf(stderr, "\ndefaultChLayoutDescription = %s\n", defaultChLayoutDescription);  //Print channel layout description //stereo
    
        return 0;
    }
    

    Output:

    chLayoutDescription = 2 channels

    defaultChLayoutDescription = stereo


    Note:
    The result is the same for FFmpeg 6.0 and FFmpeg 5.1.2 (while FFmpeg 4.4 excludes the function av_channel_layout_describe).