一、前言
本地摄像头推流和本地桌面推流类似,无非就是采集的设备源头换成了本地摄像头设备而不是桌面,其他代码完全一样。采集本地摄像头实时视频要注意的是如果设置分辨率和帧率,一定要是设备本身就支持的,如果不支持那就歇菜,比如设备本身最大分辨率到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
二、效果图
在这里插入图片描述
三、体验地址
-
国内站点:https://gitee.com/feiyangqingyun
-
国际站点:https://github.com/feiyangqingyun
-
个人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
-
体验地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取码:01jf 文件名:bin_video_push。
-
联系方式:微信 feiyangqingyun
-
官方店: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 文件推流
-
指定网卡和监听端口,接收网络请求推送音视频等各种文件。
-
实时统计显示每个文件对应的访问数量、总访问数量、不同IP地址访问数量。
-
可指定多种模式,0-直接播放、1-下载播放。
-
实时打印显示各种收发请求和应答数据。
-
每个文件对应MD5加密的唯一标识符,用于请求地址后缀区分访问哪个文件。
-
支持各种浏览器(谷歌chromium/微软edge/火狐firefox等)、各种播放器(vlc/mpv/ffplay/potplayer/mpchc等)打开请求。
-
播放过程中可以任意切换播放进度,支持倍速播放。
-
需要推流的文件名称历史记录自动存储和打开加载应用。
-
切换文件获取访问地址,自动拷贝地址到剪切板方便直接粘贴测试使用。
-
极低CPU占用,128路1080P同时推流不到1%CPU占用,异步发送数据机制。
-
纯QTcpSocket通信,不依赖流媒体服务程序,核心源码不到500行,注释详细,功能完整。
-
支持Qt4/Qt5/Qt6任意版本,支持任意系统(windows/linux/macos/android/嵌入式linux等)。
5.2 网络推流
-
支持各种本地视频文件和网络视频文件。
-
支持各种网络视频流,网络摄像头,协议包括rtsp、rtmp、http。
-
支持将本地摄像头设备推流,可指定分辨率和帧率等。
-
支持将本地桌面推流,可指定屏幕区域和帧率等。
-
自动启动流媒体服务程序,默认mediamtx(原rtsp-simple-server),可选用srs、EasyDarwin、LiveQing、ZLMediaKit等。
-
可实时切换预览视频文件。
-
推流的清晰度和质量可调。
-
可动态添加文件、目录、地址。
-
视频文件自动循环推流,如果视频源是视频流,在掉线后会自动重连。
-
网络视频流自动重连,重连成功自动继续推流。
-
网络视频流实时性极高,延迟极低,延迟时间大概在100ms左右。
-
推流后除了用rtmp地址访问以外,还支持直接hls/webrtc访问,可以直接浏览器打开看实时画面。
-
支持Qt4/Qt5/Qt6任意版本,支持任意系统(windows/linux/macos/android/嵌入式linux等)。