赞
踩
用ffmpeg采集本地摄像头,如果不指定格式的话,默认小分辨率比如640x480使用rawvideo格式,大分辨率比如1280x720使用mjpeg格式,当然前提是这个摄像头设备要支持这些格式。目前市面上有一些厂家做的本地设备支持264格式,这个压缩率极高,由于采集到的就是264格式的裸流,所以不用编码就可以正常保存到文件以及推流,这就非常方便了,不像之前的摄像头都是mjpeg为主,都需要用SwsContext进行转换。这样就白白牺牲了CPU,而且分辨率越大,这个转换就越是占CPU,如果源头就是264的流则根本不需要转换。
公众号:Qt实战,各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发。
公众号:Qt入门和进阶,专门介绍Qt/C++相关知识点学习,帮助Qt开发者更好的深入学习Qt。多位Qt元婴期大神,一步步带你从入门到进阶,走上财务自由之路。
void FFmpegHelper::setVideoCodecName(AVFormatContext *formatCtx, const QString &videoCodecName) { if (videoCodecName == "mjpeg") { formatCtx->video_codec_id = AV_CODEC_ID_MJPEG; } else if (videoCodecName == "h264") { formatCtx->video_codec_id = AV_CODEC_ID_H264; } else { formatCtx->video_codec_id = AV_CODEC_ID_RAWVIDEO; } } bool FFmpegThread::initInput() { //本地摄像头/桌面录屏/linux系统可以打开cheese程序查看本地摄像头(如果是在虚拟机中需要设置usb选项3.1) if (mediaType == MediaType_Device) { ifmt = av_find_input_format(mediaUrl.startsWith("audio=") ? Device_Audio : Device_Video); } else if (mediaType == MediaType_Screen) { ifmt = av_find_input_format(Device_Screen); } //实例化格式处理上下文 formatCtx = avformat_alloc_context(); //设置超时回调(有些不存在的地址或者网络不好的情况下要卡很久) formatCtx->interrupt_callback.callback = FFmpegThreadHelper::openAndReadCallBack; formatCtx->interrupt_callback.opaque = this; //可以强制指定本地摄像头采集解码为mjpeg或者h264/找遍了网络原来是这样设置才起作用 if (mediaType == MediaType_Device) { FFmpegHelper::setVideoCodecName(formatCtx, videoCodecName); } //取出最终的播放地址 QString url = VideoHelper::getPlayUrl(VideoCore_FFmpeg, mediaType, mediaUrl); //桌面采集指定窗口标题需要转换才能支持中文/ffmpeg5开始才修复了这个问题 QByteArray urlData = VideoHelper::getUrlData(mediaType, url, FFMPEG_VERSION_MAJOR < 5); //打开输入(通过标志位控制回调那边做超时判断) tryOpen = true; int result = avformat_open_input(&formatCtx, urlData.constData(), ifmt, &options); tryOpen = false; if (result < 0) { debug(result, "打开地址", ""); emit receivePlayError(VideoError_Open); return false; } //根据自己项目需要开启下面部分代码加快视频流打开速度 //开启后由于值太小可能会出现部分视频流获取不到分辨率 if (decodeType == DecodeType_Fastest && mediaType == MediaType_Rtsp) { FFmpegHelper::initRtspFast(formatCtx); } //获取流信息 result = avformat_find_stream_info(formatCtx, NULL); if (result < 0) { debug(result, "找流失败", ""); return false; } //封装格式 formatName = formatCtx->iformat->name; //校验硬解码 FFmpegThreadHelper::checkHardware(formatName, hardware); //获取文件时长(这里获取到的是秒) double length = (double)formatCtx->duration / AV_TIME_BASE; //如果是本地文件而且没有时长则用最原始方法读取时长 //有部分设备导出的视频文件读取出来时长不对也可以用此方法读取 if (mediaType == MediaType_FileLocal && duration <= 0) { if (this->property("getDurationByFrame").toBool()) { length = FFmpegUtil::getDuration(mediaUrl); } } duration = length * 1000; this->checkMediaType(); //发送文件时长信号 if (getIsFile()) { emit receiveDuration(duration > 0 ? duration : 0); } //获取音视频轨道信息(一般有一个音频或者一个视频/ts节目文件可能有多个) FFmpegHelper::getTracks(formatCtx, audioTracks, videoTracks); emit receiveTrack(audioTracks, videoTracks); QString msg = QString("格式: %1 时长: %2 秒 加速: %3").arg(formatName).arg(duration / 1000).arg(hardware); debug(0, "媒体信息", msg); return true; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。