• Qt音视频开发37-USB摄像头解码ffmpeg方案


    一、前言

    用ffmpeg来处理USB摄像头,是前段时间研究视频监控ffmpeg内核的时候搞定的,既然ffmpeg这么牛逼的库可以解析各种音视频,我想处理个本地USB摄像头应该也不是什么难事,果真搜索也是一大堆,当然主要也是因为有个项目的应用需要用到ffmpeg来处理本地USB摄像头,需要拿到每张图片做智能分析,用Qt自带的camera类不大好处理,刚好将ffmpeg的处理流程都搞清楚了,索性直接用ffmpeg来直接处理好了,用上这么强大的解码库,理论上支持各种USB摄像头。本地USB摄像机不需要硬解码,视频流编码类型为 AV_CODEC_ID_RAWVIDEO 像素格式为 AV_PIX_FMT_YUYV422 不经过解码操作直接就可显示。

    ffmpeg方案处理流程:

    1. 引入avdevice.h头文件,调用avdevice_register_all();注册本地设备处理。
    2. 调用av_dict_set设置分辨率(video_size)、帧率(framerate)等参数。
    3. 调用av_find_input_format设置输入格式。
    4. 调用avformat_open_input打开文件。
    5. 调用av_find_best_stream找到视频流地址。
    6. 调用avcodec_find_decoder设置视频解码器。
    7. 调用av_read_frame循环解码读取帧数据。
    8. 调用avcodec_send_packet avcodec_receive_frame解码数据。
    9. 处理完以后调用av_frame_free avcodec_close等释放资源。

    二、功能特点

    1. 同时支持windows、linux、嵌入式linux上的USB摄像头实时采集。
    2. 支持多路USB摄像头多线程实时采集。
    3. 在嵌入式linux设备上,自动查找USB设备文件并加载。
    4. 可手动设置设备文件名称,手动设置后按照手动设置的设备文件加载。
    5. 在嵌入式linux设备上支持人脸识别接口,实时绘制人脸框。
    6. 具有打开、暂停、继续、关闭、截图等常规功能。
    7. 可设置两路OSD标签,分别设置文本、颜色、字号、位置等。
    8. 可作为视频监控系统使用。

    三、效果图

    四、相关站点

    1. 国内站点:https://gitee.com/feiyangqingyun/QWidgetDemo
    2. 国际站点:https://github.com/feiyangqingyun/QWidgetDemo
    3. 个人主页:https://blog.csdn.net/feiyangqingyun
    4. 知乎主页:https://www.zhihu.com/people/feiyangqingyun/
    5. 体验地址:https://blog.csdn.net/feiyangqingyun/article/details/97565652

    五、核心代码

    void FFmpegThread::initOption()
    {
        //在打开码流前指定各种参数比如:探测时间/超时时间/最大延时等
        //设置缓存大小,1080p可将值调大
        av_dict_set(&options, "buffer_size", "8192000", 0);
        //以tcp方式打开,如果以udp方式打开将tcp替换为udp
        av_dict_set(&options, "rtsp_transport", transport.toUtf8().constData(), 0);
        //设置超时断开连接时间,单位微秒,3000000表示3秒
        av_dict_set(&options, "stimeout", "3000000", 0);
        //设置最大时延,单位微秒,1000000表示1秒
        av_dict_set(&options, "max_delay", "1000000", 0);
        //自动开启线程数
        av_dict_set(&options, "threads", "auto", 0);
    
        //单独对USB摄像机设置参数
        if (isUsbCamera) {
            //设置输入格式
            //av_dict_set(&options, "input_format", "mjpeg", 0);
            //设置分辨率
            QString size = QString("%1x%2").arg(videoWidth).arg(videoHeight);
            av_dict_set(&options, "video_size", size.toUtf8().constData(), 0);
            //设置帧率
            av_dict_set(&options, "framerate", "25", 0);
        }
    
        //本地USB摄像机不需要硬解码,强制改成回调运行和无硬解码
        //视频流编码类型为 AV_CODEC_ID_RAWVIDEO 像素格式为 AV_PIX_FMT_YUYV422 不经过解码操作直接就可显示
        if (isUsbCamera) {
            callback = true;
            hardware = "none";
        }
    
        //没有启用opengl则强制改为回调
    #ifndef opengl
        callback = true;
    #endif
    
        //rtmp视频流强制改成存储成h264裸流,目前存储成mp4还有问题
        if (url.startsWith("rtmp", Qt::CaseInsensitive)) {
            saveMp4 = false;
        }
    }
    
    bool FFmpegThread::initInput()
    {
        //实例化格式处理上下文
        formatCtx = avformat_alloc_context();
        //设置超时回调,有些不存在的地址或者网络不好的情况下要卡很久
        formatCtx->interrupt_callback.callback = AVInterruptCallBackFun;
        formatCtx->interrupt_callback.opaque = this;
    
        //必须要有tryOpen标志位来控制超时回调,由他来控制是否继续阻塞等待打开
        tryOpen = false;
    
        //先判断是否是本地设备(video=设备名字符串),打开的方式不一样
        QByteArray urlData = url.toUtf8();
        AVInputFormat *ifmt = NULL;
        if (isUsbCamera) {
    #if defined(Q_OS_WIN)
            ifmt = av_find_input_format("dshow");
    #elif defined(Q_OS_LINUX)
            //ifmt = av_find_input_format("v4l2");
            ifmt = av_find_input_format("video4linux2");
    #elif defined(Q_OS_MAC)
            ifmt = av_find_input_format("avfoundation");
    #endif
        }
    
        int result = avformat_open_input(&formatCtx, urlData.data(), ifmt, &options);
        tryOpen = true;
        if (result < 0) {
            qDebug() << TIMEMS << "open input error" << url;
            return false;
        }
    
        //释放设置参数
        if (options != NULL) {
            av_dict_free(&options);
        }
    
        //获取流信息
        result = avformat_find_stream_info(formatCtx, NULL);
        if (result < 0) {
            qDebug() << TIMEMS << "find stream info error";
            return false;
        }
    
        return true;
    }
    
  • 相关阅读:
    RESTful API 设计指南【转】
    一款 .NET 下的轻量级 REST 和 HTTP API 客户端
    Windows 服务开发框架介绍
    实例演示 kino.razor (前端 Javascript 模板工具,Razor 风格)的使用
    深入研究 蒋金楠(Artech)老师的 MiniMvc(迷你 MVC),看看 MVC 内部到底是如何运行的
    扩展 IEnumerable<T>,让它根据另一个集合的顺序来排列
    谷歌正式发布Google APIs Client Library for .NET
    身为码农,为12306说两句公道话
    12306外包给阿里巴巴/IBM到底是否可行?
    自定义一个叫 ReadOnlyXmlMembershipProvider 的 MembershipProvider,用 XML 作为用户储藏室
  • 原文地址:https://www.cnblogs.com/feiyangqingyun/p/13848254.html
Copyright © 2020-2023  润新知