• ffmpeg -sources分析


    ffmpeg -sources分析
    ----------------------------------------
    author:hjjdebug
    date:  2022年 08月 03日 星期三 16:42:27 CST
    ----------------------------------------
    先看一下它的输出:
    $ ffmpeg -sources

    Device name is not provided.
    You can pass devicename[,opt1=val1[,opt2=val2...]] as an argument.

    Auto-detected sources for alsa:
    * default [Playback/recording through the PulseAudio sound server]
      surround21 [2.1 Surround output to Front and Subwoofer speakers]
      surround40 [4.0 Surround output to Front and Rear speakers]
      surround41 [4.1 Surround output to Front, Rear and Subwoofer speakers]
      surround50 [5.0 Surround output to Front, Center and Rear speakers]
      surround51 [5.1 Surround output to Front, Center, Rear and Subwoofer speakers]
      surround71 [7.1 Surround output to Front, Center, Side, Rear and Woofer speakers]
      null [Discard all samples (playback) or generate zero samples (capture)]
      samplerate [Rate Converter Plugin Using Samplerate Library]
      speexrate [Rate Converter Plugin Using Speex Resampler]
      jack [JACK Audio Connection Kit]
      oss [Open Sound System]
      pulse [PulseAudio Sound Server]
      upmix [Plugin for channel upmix (4,6,8)]
      vdownmix [Plugin for channel downmix (stereo) with a simple spacialization]
      sysdefault:CARD=PCH [Default Audio Device]
      front:CARD=PCH,DEV=0 [Front speakers]
      dmix:CARD=PCH,DEV=0 [Direct sample mixing device]
      dmix:CARD=PCH,DEV=2 [Direct sample mixing device]
      dsnoop:CARD=PCH,DEV=0 [Direct sample snooping device]
      dsnoop:CARD=PCH,DEV=2 [Direct sample snooping device]
      hw:CARD=PCH,DEV=0 [Direct hardware device without any conversions]
      hw:CARD=PCH,DEV=2 [Direct hardware device without any conversions]
      plughw:CARD=PCH,DEV=0 [Hardware device with all software conversions]
      plughw:CARD=PCH,DEV=2 [Hardware device with all software conversions]
      usbstream:CARD=PCH [USB Stream Output]
      usbstream:CARD=NVidia [USB Stream Output]
    Auto-detected sources for oss:
    Cannot list sources. Not implemented.
    Auto-detected sources for sndio:
    Cannot list sources. Not implemented.
    Auto-detected sources for fbdev:
    Could not open framebuffer device '/dev/fb0': Permission denied
    Auto-detected sources for lavfi:
    Cannot list sources. Not implemented.
    Auto-detected sources for video4linux2,v4l2:
    Auto-detected sources for x11grab:
    Cannot list sources. Not implemented.

    下面分析代码:
    ffmpeg 分析选项之后,调用了show_sources() 函数,关键代码如下:
        fmt = 0;
        do {
            fmt = av_input_audio_device_next(fmt);
            if (fmt) {
                print_device_sources(fmt, opts);
            }
        } while (fmt);
        do {
            fmt = av_input_video_device_next(fmt);
            if (fmt) {
                print_device_sources(fmt, opts);
            }
        } while (fmt);
    可见是2个循环.
    先看看如何得到fmt. ,由上一个format 可得到下一个format.

        int i = 0;
        while (prev && (fmt = indev_list[i])) {
            i++;
            if (prev == fmt)  // fmt 与前一个相等了就停下来。
                break;
        }
        do {
            fmt = indev_list[i++];
            if (!fmt)
                break;
            category = pc->category;
        } while (category != c1 && category != c2);  //找到下一个符合要求的,c1,c2是条件
        return (AVInputFormat *)fmt;

    原来核心是有一个indev_list[]数组, 查找之为:

    static const AVInputFormat * const indev_list[] = {
        &ff_alsa_demuxer,
        &ff_fbdev_demuxer,
        &ff_lavfi_demuxer,
        &ff_oss_demuxer,
        &ff_sndio_demuxer,
        &ff_v4l2_demuxer,
        &ff_xcbgrab_demuxer,
        NULL };

    这样我们就知道了,就这么多AVInputFormat. 这是收获1

    下面看看每一种AVInputFormat 中具体的项又是怎么得到的?
    这要看下面print函数, opts为NULL,fmt 是上面获取的
                print_device_sources(fmt, opts);
    fmt 的结构比较复杂,相当于一个类。

      type = struct AVInputFormat {
          const char *name;
          const char *long_name;
          int flags;
          const char *extensions;
          const struct AVCodecTag * const *codec_tag;
          const AVClass *priv_class;
          const char *mime_type;
          struct AVInputFormat *next;
          int raw_codec_id;
          int priv_data_size;
          int (*read_probe)(const AVProbeData *);
          int (*read_header)(struct AVFormatContext *);
          int (*read_packet)(struct AVFormatContext *, AVPacket *);
          int (*read_close)(struct AVFormatContext *);
          int (*read_seek)(struct AVFormatContext *, int, int64_t, int);
          int64_t (*read_timestamp)(struct AVFormatContext *, int, int64_t *, int64_t);
          int (*read_play)(struct AVFormatContext *);
          int (*read_pause)(struct AVFormatContext *);
          int (*read_seek2)(struct AVFormatContext *, int, int64_t, int64_t, int64_t, int);
          int (*get_device_list)(struct AVFormatContext *, struct AVDeviceInfoList *);
          int (*create_device_capabilities)(struct AVFormatContext *, struct AVDeviceCapabilitiesQuery *);
          int (*free_device_capabilities)(struct AVFormatContext *, struct AVDeviceCapabilitiesQuery *);
      }
      好在别人都写好了,就是上面7种,(当然跟编译选项有关,还可以添加例如decklink等设备)

        打印分两部分,一步是获取设备,第二步是打印。
        获取设备是关键,信息存入device_list
        if ((ret = avdevice_list_input_sources(fmt, NULL, opts, &device_list)) < 0) {
            printf("Cannot list sources.\n");
            goto fail;
        }
        打印很简单,调用printf打就是了。
        for (i = 0; i < device_list->nb_devices; i++) {
            printf("%s %s [%s]\n", device_list->default_device == i ? "*" : " ",
                   device_list->devices[i]->device_name, device_list->devices[i]->device_description);
        }

        device_list 需要讲一下,因为它储存了很多信息。数据组织是收获2
        AVDeviceInfoList *device_list ;
        typedef struct AVDeviceInfoList {
            AVDeviceInfo **devices;              /**< list of autodetected devices */
            int nb_devices;                      /**< number of autodetected devices */
            int default_device;                  /**< index of default device or -1 if no default */
        } AVDeviceInfoList;

        2层指针是什么?2层指针是指向表格的指针,这个表格是一个指针表格(一层指针),2层指针解引用是指针。
        同样也可以理解3层指针是一个指向2层指针构成的表的指针。解引用就是一个2层指针。当然这里没有使用,类推一下,后面它确实用过3层指针。
        因为是一个AVDeviceInfo 列表,所以有一个nb_devices

        AVDeviceInfo 是如下定义的,name,description
        typedef struct AVDeviceInfo {
            char *device_name;                   /**< device name, format depends on device */
            char *device_description;            /**< human friendly name */
        } AVDeviceInfo;

        下面看看如何获取设备列表:
        以 ff_alsa_demuxer,为利,抛开它的一些过程,直接来到
        return ff_alsa_get_device_list(device_list, SND_PCM_STREAM_CAPTURE);    //获取CAPTURE 的列表
        该函数把获取到的device 组织到device_list 中,主要流程如下:

        if (snd_device_name_hint(-1, "pcm", &hints) < 0)      //拿到所有卡信息的hint
            return AVERROR_EXTERNAL;
        n = hints;
        while (*n && !ret) {
            name = snd_device_name_get_hint(*n, "NAME");
            descr = snd_device_name_get_hint(*n, "DESC");
            io = snd_device_name_get_hint(*n, "IOID");
            if (!io || !strcmp(io, filter)) {
                new_device = av_mallocz(sizeof(AVDeviceInfo));        // 分配内存
                new_device->device_name = av_strdup(name);
                new_device->device_description = av_strdup(descr);
                if ((ret = av_dynarray_add_nofree(&device_list->devices,     //加入数组
                      &device_list->nb_devices, new_device)) < 0) {
                    goto fail;
                }
            }
            free(io);
            free(name);
            free(descr);
            n++;
        }

    int snd_device_name_hint(int card, const char *iface, void ***hints);
    card==-1, 获取所有卡的hint, hints 是3层指针。

    char *snd_device_name_get_hint(const void *hint, const char *id);
    根据hint和id, 拿到对应的名称。
    这两个函数就不是ffmpeg 函数,而是驱动提供的函数了. 最终的数据来自驱动,这是收获3
    这样把数据流程就分析完了。 结束。

  • 相关阅读:
    PHP Cookie
    ERR_CONNECTION_REFUSED等非标准的HTTP错误状态码原因分析和解决办法
    abp vnext 通过Claim扩展用户表字段
    ORA-28001:the password has expired,Linux上修改Oracle密码
    windows系统c语言编译器安装
    CITE2022丨中科创达发布一站式交钥匙边缘计算解决方案 解锁边缘应用落地新方式
    腾讯C++二面,全程2小时追问基础!
    装饰器模式详解和实现(设计模式 二)
    麒麟软件操作系统下载
    怎么保护苹果手机移动应用程序ipa中文件安全?
  • 原文地址:https://blog.csdn.net/hejinjing_tom_com/article/details/126143702