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[]数组, 这样我们就知道了,就这么多AVInputFormat. 这是收获1

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 中具体的项又是怎么得到的?
这要看下面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
这样把数据流程就分析完了。 结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值