利用ffmpeg打开windows系统下面的摄像头源代码分析

首先在主函数里面调用两个注册函数:

avcodec_register_all();
avdevice_register_all();
通过函数名称就很容易看出来这两个函数的实现了,前者主要注册所支持的编码器,而后者主要注册所支持的输入和输出设备.这两个函数的主要通过宏实现:

#define REGISTER_INDEV(X, x)                                            \
    {                                                                   \
        extern AVInputFormat ff_##x##_demuxer;                          \
        if (CONFIG_##X##_INDEV)                                         \
            av_register_input_format(&ff_##x##_demuxer);                \
}

上面通过调用av_register_input_format函数将一系列预先定义好的结构体AVInputFormat当中的成员变量AVInputFormat* next构成一个链表,而这个链表当中的的首部和尾部有一个预先定义的全局变量保存.下面看看支持windows所支持的AVInputFormat结构体的预定义:

static const AVClass vfw_class = {
    .class_name = "VFW indev",
    .item_name  = av_default_item_name,
    .option     = options,
    .version    = LIBAVUTIL_VERSION_INT,
};
AVInputFormat ff_vfwcap_demuxer = {
    .name           = "vfwcap",
    .long_name      = NULL_IF_CONFIG_SMALL("VfW video capture"),
    .priv_data_size = sizeof(struct vfw_ctx),
    .read_header    = vfw_read_header,
    .read_packet    = vfw_read_packet,
    .read_close     = vfw_read_close,
    .flags          = AVFMT_NOFILE,
    .priv_class     = &vfw_class,
};

在上面的函数初始化之后,通过调用av_find_input_format("vfwcap")函数就可以实现找到我们所需要的AVInputFormat结构体指针,而这正好是av_find_input_format函数的返回值,而传入的参数刚好是等于之前预定义结构体当中的name成员变量,所以这个函数的实现大致就是通过字符串和之前定义的AVInputFormat结构体当中的name字符串进行比对比对找到一个系统支持的AVInputFormat指针。

int avformat_open_input(AVFormatContext **ps, const char *filename,AVInputFormat *fmt, AVDictionary **options)
找到相应的设备AVInputFormat之后,我们需要找到相应的输入文件。由于在操作系统的概念当中设备也是文件,所以对摄像头的操作和对视频文件的操作是一样的。上面这个函数就是用于找到相应的输入文件的,所以这里视频文件盒摄像头文件时一视同仁的,只不过摄像头文件有预先定义好的AVInputFormat,而打开视频文件的时候需要传入的fmt参数为空,以便于avformat_open_input函数生成所需要的AVInputFormat。而*ps推荐的值也为NULL,并且在函数当中调用avformat_alloc_context函数来分配AVFormatContext内存。整个函数主要是预读一些数据用于判别文件格式,所以接下来是一个循环,根据ps传回来的指针里面的文件格式数组来判断整个文件的格式。

接下来的操作就是对刚刚打开的文件进行读取操作:

int av_read_frame(AVFormatContext *s, AVPacket *pkt)
这个函数实现将整个文件中的数据一次读取一帧到内存当中。当然也可以根据之前分析得到的解码器标号来打开新的解码器。寻找解码器的函数如下,这个函数的实现主要是根据id,扫描整个由avcodec_register_all函数生成的解码器链表,如果没找到则返回NULL。avcodec_find_decoder和avcodec_open2的关系实际上和前面的av_find_input_format和avformat_open_input的关系很相似。这就是设计模式的魅力所在。
AVCodec *avcodec_find_decoder(enum AVCodecID id)
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
接下来进入真正的数据解码过程

avcodec_alloc_frame();
int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,int *got_picture_ptr,const AVPacket *avpkt)
首先分配一个AVFrame,然后将所有的操作都交给函数avcodec_decode_video2。函数的参数avctx是是上面生成的解码器,而avpkt则是之前通过av_read_frame读出来的数据,然后返回的数据将会被放入到picture当中。至于got_picture_ptr则是用于保存读取的结果,当got_picture_ptr等于0的时候,表明解码失败。

上面的整个流程就是讲数据读取到内存当中,然后将这一块内存转化为视频所支持的格式。这些格式还不一定是我们所需要的,因为视频所支持的格式并不一定和保存下来的或者可以直观展示的格式一致,所以还需要经过一道转换的过程。在转换之前,系统首先会根据转换目标格式分配足够的内存,然后创建一个转换的上下文。

SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat,
                           int dstW, int dstH, enum AVPixelFormat dstFormat,
                           int flags, SwsFilter *srcFilter,
                           SwsFilter *dstFilter, const double *param)
这个函数主要是为了得到一个源目标格式到目的目标格式转换的上下文,所以最主要的参数有源和目标的长度和宽度以及格式,还有一个就是目标的生成方式,也就是flags。其他的都可以设置为NULL。生成上诉SwsContext上下文之后,将上诉返回的SwsContext指针做为参数调用sws_scale函数。sws_scale函数额完整定义如下:

int sws_scale(struct SwsContext *c,
              const uint8_t * const srcSlice[],
              const int srcStride[], int srcSliceY,
              int srcSliceH, uint8_t *const dst[],
              const int dstStride[])

除了第一个参数SwsContext*指针之外,其他的对于源和目标都是一样的,也很容易理解,就是整个数据的数据所在,以及整个数据该如何分割。

相关示例可以去如下网址下载:http://download.csdn.net/detail/dayenglish/7364741

你可能感兴趣的:(windows,ffmpeg,摄像头)