51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

本地摄像头推流,支持分辨率/帧率设置,支持指定采集格式比如mjpeg/h264,实时性极高。

一、前言

本地摄像头推流和本地桌面推流类似,无非就是采集的设备源头换成了本地摄像头设备而不是桌面,其他代码完全一样。采集本地摄像头实时视频要注意的是如果设置分辨率和帧率,一定要是设备本身就支持的,如果不支持那就歇菜,比如设备本身最大分辨率到1280x720,你主动设置要采集1080x720分辨率的图像,那是采集不到的,会失败的,如果默认不设置,一般会采用640x480分辨率以及25帧率去采集。采集本地摄像头设备命令行是 ffmpeg -f dshow -i video="USB Video Device":audio="麦克风 (USB Audio Device)" -vcodec libx264 -preset:v ultrafast -tune:v zerolatency -f rtsp rtsp://192.168.0.110:6907/stream ,可以指定带上麦克风,这样音视频都有。

采集大致步骤:

  • 查找格式 av_find_input_format,参数 dshow/v4l2/avfoundation

  • 打开桌面 avformat_open_input,参数 video=USB Video Device

  • 查找视频流 av_find_best_stream

  • 查找解码器 avcodec_find_decoder

  • 打开解码器 avcodec_open2

  • 循环读取 av_read_frame

  • 解码视频 avcodec_send_packet/avcodec_receive_frame

  • 关闭释放 avcodec_free_context/avformat_close_input

推流大致步骤:

  • 创建输出 avformat_alloc_output_context2

  • 创建视频流 avformat_new_stream

  • 打开输出 avio_open,参数填推流完整地址

  • 写入开始符 avformat_write_header

  • 写入帧数据 av_interleaved_write_frame

  • 关闭释放 avio_close/avformat_free_context

二、效果图


在这里插入图片描述


三、体验地址

  1. 国内站点:https://gitee.com/feiyangqingyun

  2. 国际站点:https://github.com/feiyangqingyun

  3. 个人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652

  4. 体验地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取码:01jf 文件名:bin_video_push。

  5. 联系方式:微信 feiyangqingyun

  6. 官方店:https://shop114595942.taobao.com/

四、相关代码

 
   
 void FFmpegThread::initInputFormat()
{
    //本地摄像头/桌面录屏
    if (videoType == VideoType_Camera) {
#if defined(Q_OS_WIN)
        //ifmt = av_find_input_format("vfwcap");
        ifmt = av_find_input_format("dshow");
#elif defined(Q_OS_LINUX)
        //可以打开cheese程序查看本地摄像头(如果是在虚拟机中需要设置usb选项3.1)
        //ifmt = av_find_input_format("v4l2");
        ifmt = av_find_input_format("video4linux2");
#elif defined(Q_OS_MAC)
        ifmt = av_find_input_format("avfoundation");
#endif
    } else if (videoType == VideoType_Desktop) {
#if defined(Q_OS_WIN)
        ifmt = av_find_input_format("gdigrab");
#elif defined(Q_OS_LINUX)
        ifmt = av_find_input_format("x11grab");
#elif defined(Q_OS_MAC)
        ifmt = av_find_input_format("avfoundation");
#endif
    }
}

bool FFmpegThread::initInput()
{
    //实例化格式处理上下文
    formatCtx = avformat_alloc_context();
    //设置超时回调(有些不存在的地址或者网络不好的情况下要卡很久)
    formatCtx->interrupt_callback.callback = FFmpegHelper::avinterruptCallBackFun;
    formatCtx->interrupt_callback.opaque = this;

    //打开输入(通过标志位控制回调那边做超时判断)
    //其他地方调用 formatCtx->url formatCtx->filename 可以拿到设置的地址(两个变量值一样)
    tryOpen = true;
    QByteArray urlData = VideoHelper::getRightUrl(videoType, videoUrl).toUtf8();
    int result = avformat_open_input(&formatCtx, urlData.data(), ifmt, &options);
    tryOpen = false;
    if (result < 0) {
        debug("打开出错", "错误: " + FFmpegHelper::getError(result));
        return false;
    }

    //根据自己项目需要开启下面部分代码加快视频流打开速度
    //开启后由于值太小可能会出现部分视频流获取不到分辨率
    if (decodeType == DecodeType_Fastest && videoType == VideoType_Rtsp) {
        //接口内部读取的最大数据量(从源文件中读取的最大字节数)
        //默认值5000000导致这里卡很久最耗时(可以调小来加快打开速度)
        formatCtx->probesize = 50000;
        //从文件中读取的最大时长(单位为 AV_TIME_BASE units)
        formatCtx->max_analyze_duration = 5 * AV_TIME_BASE;
        //内部读取的数据包不放入缓冲区
        //formatCtx->flags |= AVFMT_FLAG_NOBUFFER;
        //设置解码错误验证过滤花屏
        //formatCtx->error_recognition |= AV_EF_EXPLODE;
    }

    //获取流信息
    result = avformat_find_stream_info(formatCtx, NULL);
    if (result < 0) {
        debug("找流失败", "错误: " + FFmpegHelper::getError(result));
        return false;
    }

    //解码格式
    formatName = formatCtx->iformat->name;
    //某些格式比如视频流不做音视频同步(响应速度快)
    if (formatName == "rtsp" || videoUrl.endsWith(".sdp")) {
        useSync = false;
    }

    //设置了最快速度则不启用音视频同步
    if (decodeType == DecodeType_Fastest) {
        useSync = false;
    }

    //有些格式不支持硬解码
    if (formatName.contains("rm") || formatName.contains("avi") || formatName.contains("webm")) {
        hardware = "none";
    }

    //本地摄像头设备解码出来的直接就是yuv显示不需要硬解码
    if (videoType == VideoType_Camera || videoType == VideoType_Desktop) {
        useSync = false;
        hardware = "none";
    }

    //过低版本不支持硬解码
#if (FFMPEG_VERSION_MAJOR < 3)
    hardware = "none";
#endif

    //获取文件时长(这里获取到的是秒)
    double length = (double)formatCtx->duration / AV_TIME_BASE;
    duration = length * 1000;
    this->checkVideoType();

    //有时候网络地址也可能是纯音频
    if (videoType == VideoType_FileHttp) {
        onlyAudio = VideoHelper::getOnlyAudio(videoUrl, formatName);
    }

    if (getIsFile()) {
        //文件必须要音视频同步
        useSync = true;
        //发送文件时长信号
        emit receiveDuration(duration > 0 ? duration : 0);
    }

    QString msg = QString("格式: %1 时长: %2 秒 加速: %3").arg(formatName).arg(duration / 1000).arg(hardware);
    debug("媒体信息", msg);
    return true;
}

五、功能特点

5.1 文件推流

  1. 指定网卡和监听端口,接收网络请求推送音视频等各种文件。

  2. 实时统计显示每个文件对应的访问数量、总访问数量、不同IP地址访问数量。

  3. 可指定多种模式,0-直接播放、1-下载播放。

  4. 实时打印显示各种收发请求和应答数据。

  5. 每个文件对应MD5加密的唯一标识符,用于请求地址后缀区分访问哪个文件。

  6. 支持各种浏览器(谷歌chromium/微软edge/火狐firefox等)、各种播放器(vlc/mpv/ffplay/potplayer/mpchc等)打开请求。

  7. 播放过程中可以任意切换播放进度,支持倍速播放。

  8. 需要推流的文件名称历史记录自动存储和打开加载应用。

  9. 切换文件获取访问地址,自动拷贝地址到剪切板方便直接粘贴测试使用。

  10. 极低CPU占用,128路1080P同时推流不到1%CPU占用,异步发送数据机制。

  11. 纯QTcpSocket通信,不依赖流媒体服务程序,核心源码不到500行,注释详细,功能完整。

  12. 支持Qt4/Qt5/Qt6任意版本,支持任意系统(windows/linux/macos/android/嵌入式linux等)。

5.2 网络推流

  1. 支持各种本地视频文件和网络视频文件。

  2. 支持各种网络视频流,网络摄像头,协议包括rtsp、rtmp、http。

  3. 支持将本地摄像头设备推流,可指定分辨率和帧率等。

  4. 支持将本地桌面推流,可指定屏幕区域和帧率等。

  5. 自动启动流媒体服务程序,默认mediamtx(原rtsp-simple-server),可选用srs、EasyDarwin、LiveQing、ZLMediaKit等。

  6. 可实时切换预览视频文件。

  7. 推流的清晰度和质量可调。

  8. 可动态添加文件、目录、地址。

  9. 视频文件自动循环推流,如果视频源是视频流,在掉线后会自动重连。

  10. 网络视频流自动重连,重连成功自动继续推流。

  11. 网络视频流实时性极高,延迟极低,延迟时间大概在100ms左右。

  12. 推流后除了用rtmp地址访问以外,还支持直接hls/webrtc访问,可以直接浏览器打开看实时画面。

  13. 支持Qt4/Qt5/Qt6任意版本,支持任意系统(windows/linux/macos/android/嵌入式linux等)。

赞(8)
未经允许不得转载:工具盒子 » 本地摄像头推流,支持分辨率/帧率设置,支持指定采集格式比如mjpeg/h264,实时性极高。