项目实战:Qt+ffmpeg摄像头检测工具
若该文为原创文章,未经允许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/108416332
红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…(点击传送门)
需求
打开检测摄像头工具,包括分辨率和帧率。
Demo


体验下载地址
CSDN:https://download.csdn.net/download/qq21497936/12815691
QQ群:1047134658(点击“文件”搜索“ffmpegCameraTool”,群内与博文同步更新)
涉及其他技术
QCameraInfo打开摄像头偶尔拿不到摄像头;
QCamera动态切换分辨率会导致崩溃;
QCamera处理高分辨率存在卡顿问题;
OpenCV无法拿取摄像头;
OpenCV设置高分辨率存在帧率跟不上,卡顿问题;
OpenCV保存高分辨率视频需要修改源码,否则限制mat上限大小为0xFFFF;
OpenCV保存高分辨率修改源码后存储视频会导致通道混乱,需要手动矫正颜色通道。
v1.0.0功能
- 程序启动打开计算机默认第一个摄像头,最高分辨率最高帧率打开;
- 支持动态切换分辨率和帧率;
- 支持原图显示,等比例显示;
- 多个设备终端测试可用;
核心代码
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()));
}
}
}
...
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/108416332
项目实战:Qt+ffmpeg摄像头检测工具的更多相关文章
- TF项目实战(SSD目标检测)-VOC2007
TF项目实战(SSD目标检测)-VOC2007 训练好的模型和代码会公布在网上: 步骤: 1.代码地址:https://github.com/balancap/SSD-Tensorflow 2.解压s ...
- AJ学IOS 之微博项目实战(12)发送微博自定义工具条代理实现点击事件
AJ分享,必须精品 一:效果 二:封装好的工具条 NYComposeToolbar.h 带代理方法 #import <UIKit/UIKit.h> typedef enum { NYCom ...
- 项目实战:Qt+Ffmpeg+OpenCV相机程序(打开摄像头、支持多种摄像头、分辨率调整、翻转、旋转、亮度调整、拍照、录像、回放图片、回放录像)
若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...
- 【大话QT之十六】使用ctkPluginFramework插件系统构建项目实战
"使用ctkPluginFramework插件系统构建项目实战",这篇文章是写博客以来最纠结的一篇文章. 倒不是由于技术都多么困难,而是想去描写叙述一个项目架构採用ctkPlugi ...
- 项目实战:Qt手机模拟器拉伸旋转框架
若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...
- QT5 QSS QML界面美化视频课程系列 QT原理 项目实战 C++1X STL
QT5 QSS QML界面美化视频课程系列 QT原理 项目实战 C++1X STL 课程1 C语言程序设计高级实用速成课程 基础+进阶+自学 课程2 C语言程序设计Windows GDI图形绘 ...
- 项目规范性检测工具Lint
项目规范性检测工具lint.bat 一.Lint基本概念介绍 Android Lint是SDK Tools 16 (ADT 16)之后才引入的工具,通过它对Android工程源代码进行扫描和检查,可发 ...
- 项目实战10.1—企业级自动化运维工具应用实战-ansible
实战环境: 公司计划在年底做一次大型市场促销活动,全面冲刺下交易额,为明年的上市做准备.公司要求各业务组对年底大促做准备,运维部要求所有业务容量进行三倍的扩容,并搭建出多套环境可以共开发和测试人员做测 ...
- Visual Studio 2015开发Qt项目实战经验分享(附项目示例源码)
Visual Studio 2015开发Qt项目实战经验分享(附项目示例源码) 转 https://blog.csdn.net/lhl1124281072/article/details/800 ...
- 机器学习_线性回归和逻辑回归_案例实战:Python实现逻辑回归与梯度下降策略_项目实战:使用逻辑回归判断信用卡欺诈检测
线性回归: 注:为偏置项,这一项的x的值假设为[1,1,1,1,1....] 注:为使似然函数越大,则需要最小二乘法函数越小越好 线性回归中为什么选用平方和作为误差函数?假设模型结果与测量值 误差满足 ...
随机推荐
- [转帖]【测试】 FIO:ceph/磁盘IO测试工具 fio(iodepth深度)
目录 随看随用 NAS文件系统测试 块系统测试 FIO用法 FIO介绍 FIO 工具常用参数: FIO结果说明 I/O 的重放('录'下实际工况的IO,用fio'重放') fio工作参数可以写入配置文 ...
- ipset的学习与使用
ipset的学习与使用 场景说明 虽然可以通过: firewall-cmd --zone=trusted --add-source=$1 --permanent && firewall ...
- 取消ts校验的注释
常用的有以下注释 单行忽略 // @ts-ignore 忽略全文:如果你使用这样,需要放在ts的最顶部哈. // @ts-nocheck 如下 <script lang="ts&quo ...
- 【遇到一个神奇的问题】暂未想到原因,http.Post 传入 nil参数正确,但是传输值为 nil 的 *bytes.BytesReader 就 `invalid memory address or nil pointer dereference`
出错的代码如下: func getEab(ctx context.Context, credentialsJSON string, old *externalAccountKeyResp) (*ext ...
- 【K哥爬虫普法】房产数据刑吗?爬虫多年没踩过缝纫机,劝你找找自己原因!
我国目前并未出台专门针对网络爬虫技术的法律规范,但在司法实践中,相关判决已屡见不鲜,K哥特设了"K哥爬虫普法"专栏,本栏目通过对真实案例的分析,旨在提高广大爬虫工程师的法律意识,知 ...
- Fabric网络升级(总)
原文地址在这里. 在fabric网络中,升级nodes和通道至最新版本需要四步: 备份账本和MSPs. 以滚动的方式将orderer升级到最新版. 以滚动的方式将peers升级到最新版. 将order ...
- vim 从嫌弃到依赖(13)——motion 进阶
在最开始的时候我们介绍了一些vim中的motion 包括如何在字符间.单词间.行间以及多行间移动.·但是motion中的内容可远不止我们介绍的这些,平时用到的也远不止之间介绍的那些. 之所以没有一次介 ...
- SQL 训练题目
title: SQL 训练题目 date: 2023-7-15 01:45:50 tags: - SQL 训练 一. 查询 "01" 课程比 "02" 课程成绩 ...
- 虚拟桌宠模拟器:VPet-Simulator,一个开源的桌宠软件, 可以内置到任何WPF应用程序
虚拟桌宠模拟器:VPet-Simulator,一个开源的桌宠软件, 可以内置到任何WPF应用程序 虚拟桌宠模拟器 一个开源的桌宠软件, 可以内置到任何WPF应用程序 获取虚拟桌宠模拟器 OnSteam ...
- Nginx相关快速入门,负载均衡等
快速入门Nginx[正向反向代理,负载均衡的概念,学会Nginx的安装和常用命令,并在实际中去应用Nginx] - 知乎 1.背景介绍:当用户使用较少时,低并发的情况下,使用内部toma ...