需求
打开检测摄像头工具,包括分辨率和帧率。
Demo
体验下载地址
CSDN:https://download.csdn.net/download/qq21497936/12815691
QQ群:1047134658(点击“文件”搜索“ffmpegCameraTool”,群内与博文同步更新)
涉及其他技术
QCameraInfo打开摄像头偶尔拿不到摄像头;
QCamera动态切换分辨率会导致崩溃;
QCamera处理高分辨率存在卡顿问题;
OpenCV无法拿取摄像头;
OpenCV设置高分辨率存在帧率跟不上,卡顿问题;
OpenCV保存高分辨率视频需要修改源码,否则限制mat上限大小为0xFFFF;
OpenCV保存高分辨率修改源码后存储视频会导致通道混乱,需要手动矫正颜色通道。
相关博客
《项目实战:Qt+Ffmpeg+OpenCV相机程序(打开摄像头、支持多种摄像头、分辨率调整、翻转、旋转、亮度调整、拍照、录像、回放图片、回放录像)》
《项目实战:Qt+ffmpeg摄像头检测工具》
《项目实战:使用OpenCV库操作摄像头拍照、调节参数和视频录制》
《项目实战:使用OpenCV库的视频播放器(支持播放器操作,如暂停、恢复、停止、时间、进度条拽托等)》
《OpenCV开发专栏》
《OpenCV开发笔记(四):OpenCV图片和视频数据的读取与存储》
《OpenCV开发笔记(五):OpenCV读取与操作摄像头》
《FFmpeg开发笔记(一):ffmpeg介绍、windows开发环境搭建(mingw和msvc)》
v1.0.0功能
- 程序启动打开计算机默认第一个摄像头,最高分辨率最高帧率打开;
- 支持动态切换分辨率和帧率;
- 支持原图显示,等比例显示;
- 多个设备终端测试可用;
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/108416332
核心代码
FfmpegCameraManager.h
#ifndef FFMPEGCAMERAMANAGER_H #define FFMPEGCAMERAMANAGER_H /************************************************************\ * 控件名称: FfmpegCameraManager, ffmpeg管理类(用于摄像头操作) * 控件描述: * 1.打开摄像头 * 2.支持动态切换分辨率 * 作者:红模仿 联系方式:QQ21497936 * 博客地址:https://blog.csdn.net/qq21497936 * 日期 版本 描述 * 2018年09年14日 v1.0.0 ffmpeg模块封装空类 * 2020年09年05日 v1.1.0 ffmpeg打开摄像头,支持的动态分辨率切换 \************************************************************/ #include <QObject> #include <QString> #include <QDebug> #include <QTimer> #include <QThread> #include <QImage> #include <QProcess> #include <QMessageBox> extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "libavdevice/avdevice.h" #include "libavformat/version.h" #include "libavutil/time.h" #include "libavutil/mathematics.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "libswresample/swresample.h" #include "errno.h" #include "error.h" } #define LOG qDebug()<<__FILE__<<__LINE__ class FfmpegCameraManager : public QObject { Q_OBJECT public: public: explicit FfmpegCameraManager(QObject *parent = nullptr); signals: void signal_captureOneFrame(QImage image); public: static QString getAvcodecConfiguration(); public: bool init(); bool openUsbCamera(); QString getUsbCameraName(); QList<QString> getUsbCameraInfo(); public slots: void slot_start(); void slot_stop(); void slot_setSizeFps(int index); protected slots: void slot_captureOneFrame(); signals: public slots: private: static bool _init; AVFormatContext *_pAVFormatContext; // 全局上下文 AVInputFormat *_pAVInputFormat; AVDictionary* _pAVDictionary; // 打开编码器的配置 AVCodecContext *_pAVCodecContextForAudio; // 音频解码器上下文 AVCodecContext *_pAVCodecContextForVideo; // 视频解码器上下文(不带音频) AVCodec * _pAVCodecForAudio; // 音频解码器 AVCodec * _pAVCodecForVideo; // 视频解码器(不带音频) int _streamIndexForAudio; // 音频流序号 int _streamIndexForVideo; // 视频流序号 SwrContext *_pSwrContextForAudio; // 音频转换上下文 bool _running; bool _first; bool _opened; uint8_t *_pOutBuffer; AVFrame * _pFrame; AVFrame * _pFrameRGB; AVPacket *_pAVPacket; SwsContext *_pSwsContext; int _videoIndex; QString _cameraDescription; QList<QSize> _listSize; QList<int> _listFps; QList<QString> _listSizeFpsInfo; int _currentSuzeFpsIndex; }; #endif // FfmpegCameraManager_H
FfmpegCameraManager.cpp
... void FfmpegCameraManager::slot_captureOneFrame() { if(_first) { // 读取一个媒体文件的数据包以获取流信息 if(avformat_find_stream_info(_pAVFormatContext, NULL) < 0) { LOG << "Couldn't find stream information"; }else{ LOG << "Success find stream information"; } // 循环查找数据包包含的流信息,直到找到视频类型的流 // 便将其记录下来 保存到videoStream变量中 _videoIndex = -1; for(int index = 0; index < _pAVFormatContext->nb_streams; index++) { if(_pAVFormatContext->streams[index]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { _videoIndex = index; break; } } if(_videoIndex == -1) { LOG << "Couldn't find a video stream"; }else{ LOG << "Success find a video stream"; } _pAVCodecContextForVideo = _pAVFormatContext->streams[_videoIndex]->codec; _pAVCodecForVideo = avcodec_find_decoder(_pAVCodecContextForVideo->codec_id); //软编码 // _pAVCodecForVideo = avcodec_find_encoder(AV_CODEC_ID_H264); //硬编码 // _pAVCodecForVideo = avcodec_find_encoder_by_name("nvenc_h264"); if(_pAVCodecForVideo == NULL) { qDebug() << ("Codec not found.\n"); }else{ qDebug() << "Codec found Successfuly!\n"; } if(avcodec_open2(_pAVCodecContextForVideo, _pAVCodecForVideo, NULL) < 0)//打开解码器 { LOG << "Failed to open codec"; }else{ LOG << "Success open codec"; } //分配一个AVFrame并将其字段设置为默认值 if(_pFrame == 0) { _pFrame = av_frame_alloc(); } if(_pFrameRGB == 0) { _pFrameRGB = av_frame_alloc(); } //分配和返回一个SwsContext你需要它来执行使用swsscale()的缩放/转换操作 _pSwsContext = sws_getContext(_pAVCodecContextForVideo->width, _pAVCodecContextForVideo->height, _pAVCodecContextForVideo->pix_fmt, _pAVCodecContextForVideo->width, _pAVCodecContextForVideo->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL); int numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, _pAVCodecContextForVideo->width, _pAVCodecContextForVideo->height); LOG << "numBytes:" << numBytes; _pOutBuffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t)); avpicture_fill((AVPicture *)_pFrameRGB, _pOutBuffer, AV_PIX_FMT_RGB32, _pAVCodecContextForVideo->width, _pAVCodecContextForVideo->height);//根据指定的图像参数和提供的图像数据缓冲区设置图像域 int ySize = _pAVCodecContextForVideo->width * _pAVCodecContextForVideo->height; LOG; //分配一个packet if(_pAVPacket == 0) { LOG; _pAVPacket = (AVPacket *)malloc(sizeof(AVPacket)); //分配packet的数据 av_new_packet(_pAVPacket, ySize); }else{ LOG; av_free_packet(_pAVPacket); av_new_packet(_pAVPacket, ySize); LOG; } _first = false; } // 解码压缩 if(av_read_frame(_pAVFormatContext, _pAVPacket) < 0) { LOG << "解码失败"; return; } if(_pAVPacket->stream_index == _videoIndex) { int gotPicture; // 解码一帧视频数据 int ret = avcodec_decode_video2(_pAVCodecContextForVideo, _pFrame, &gotPicture, _pAVPacket); if(ret < 0) { LOG << "decode error"; } if(gotPicture) { // 缩放图像切片,并将得到的缩放切片放在pFrameRGB->data图像中 sws_scale(_pSwsContext, (uint8_t const * const *)_pFrame->data, _pFrame->linesize, 0, _pAVCodecContextForVideo->height, _pFrameRGB->data, _pFrameRGB->linesize); QImage tmpImg((uchar *)_pOutBuffer, _pAVCodecContextForVideo->width, _pAVCodecContextForVideo->height, QImage::Format_RGB32); QImage image = tmpImg.copy(); LOG << "get a pciture"; emit signal_captureOneFrame(image); QTimer::singleShot(10, this, SLOT(slot_captureOneFrame())); } } } ...