Search code examples
c++cc++-cli

How do i get a list of all codecs available in ffmpeg?


I have this code example I'm using in C:

//#define  UINT64_C  (uint64_t);

#pragma comment(lib, "Gdi32.lib")
#pragma comment(lib, "User32.lib")
#pragma comment(lib, "gdiplus.lib")

#include <windows.h>
#include <gdiplus.h>
#include <GdiPlusEnums.h>

using namespace Gdiplus;


extern "C" {

#include  "libavcodec\avcodec.h"
#include "libavutil\mathematics.h"
//#include "libavcodec\avcodec.h"

WCHAR *fname;
AVCodec *codec;
AVCodecContext *c= NULL;
int i, ret, x, y, got_output;
int total_frame_counter;
FILE *f;
AVFrame *frame;
AVPacket pkt;
int codec_id;
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
int errn;

void Encoder_init()
{
   avcodec_register_all();
    /* find the mpeg1 video encoder */

   codec_id = CODEC_ID_MPEG1VIDEO;

    codec = avcodec_find_encoder(CODEC_ID_MPEG1VIDEO);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }
    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }
    /* put sample parameters */
    c->bit_rate = 400000;
    /* resolution must be a multiple of two */
    c->width = 352;
    c->height = 288;
    /* frames per second */
    
   //c->time_base= (AVRational){1,25};
   c->time_base.num=1;c->time_base.den=25;
   
    c->gop_size = 10; /* emit one intra frame every ten frames */
    c->max_b_frames=1;
   c->pix_fmt = AV_PIX_FMT_YUV420P;
   /*
    if(codec_id == AV_CODEC_ID_H264)
        av_opt_set(c->priv_data, "preset", "slow", 0);
      */
}

const char *Encoder_GetCodecName( int id )
{
   
   return avcodec_get_name( (AVCodecID)id );
}

Then I have a header file in c++:

const char *Encoder_GetCodecName( int id );

Then I have another header file of the C++ where I'm using the GetCodecName() to get the List:

List<String^> ^GetCodecs()
   {
      List<String^> ^l = gcnew List<String^>;

      String ^s;

      for (int i=0;i<3333;i++)
      {
      s = gcnew String(Encoder_GetCodecName( i ));
      l->Add(s);
      }

      return l;
   }

But I did now i<3333 so maybe one the of indexes are empty since there are less than 3333 codecs?

So how can I get/calculate how many codecs there are in the ffmpeg library so I will do something like for example:

i < codecs.Length

and not use 3333?


Solution

  • AVCodecID is an enum. It has a finite number of values that it defines. However, a lot of those values are not sequential, there are several gaps in between values, and also there are values that go well into 6 digit numbers (the highest being 0x21000 ie 135168). There is no mechanism in libav's API to find out the highest available AVCodecID value, so if you want to stick with an ID loop then you need to increase your loop counter quite a bit. Also, avcodec_get_name() returns "unknown_codec" for unknown IDs, so you need to filter those out before adding them to your list. Or you can incorporate a switch statement into your loop with hard-coded case statements for known ID values.

    A better approach is not to loop through the IDs at all, but to instead loop through the registered codecs themselves. Call av_codec_next() to get a pointer to the first registered AVCodec struct. AVCodec has name and long_name fields. Then call av_codec_next() again to get the next AVCodec, and so on, until it returns a NULL pointer. The documentation states:

    AVCodec* av_codec_next ( const AVCodec *  c )

    If c is NULL, returns the first registered codec, if c is non-NULL, returns the next registered codec after c, or NULL if c is the last one.

    By accessing the AVCodec structs directly, your loop will run much faster and more accurately, and it will also allow you to distinguish between encoders and decoders, which may share common names.

    Try something like this:

    __declspec(thread) AVCodec* current_codec = NULL;
        
    const char* Encoder_GetNextCodecName()
    {
        current_codec = av_codec_next(current_codec);
        while (current_codec != NULL)
        {
            /* this is optional...
            if (!av_codec_is_encoder(current_codec))
            {
                current_codec = av_codec_next(current_codec);
                continue;
            }
            */
            return current_codec->name;
        }
        return "";
    }
        
    const char* Encoder_GetFirstCodecName()
    {
        current_codec = NULL;
        return Encoder_GetNextCodecName();
    }
    
    List<String^> ^GetCodecs()
    {
        List<String^> ^l = gcnew List<String^>;
        
        String ^s = gcnew String(Encoder_GetFirstCodecName());
        while (!String.IsNullOrEmpty(s))
        {
            l->Add(s);
            s = gcnew String(Encoder_GetNextCodecName());
        }
        
        return l;
    }
    

    UPDATE: as av_codec_next() was deprecated several years ago, you can use av_codec_iterate() instead, eg:

    __declspec(thread) void* iterate_data = NULL;
        
    const char* Encoder_GetNextCodecName()
    {
        AVCodec* current_codec = av_codec_iterate(&iterate_data);
        while (current_codec != NULL)
        {
            /* this is optional...
            if (!av_codec_is_encoder(current_codec))
            {
                current_codec = av_codec_iterate(&iterate_data);
                continue;
            }
            */
            return current_codec->name;
        }
        return "";
    }
    
    const char* Encoder_GetFirstCodecName()
    {
        iterate_data = NULL;
        return Encoder_GetNextCodecName();
    }