项目实战: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、单片机、软硬结合等等)持续更新中…(点击传送门)

Qt开发专栏:项目实战(点击传送门)

Qt开发专栏:三方库开发技术


需求

  打开检测摄像头工具,包括分辨率和帧率。


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()));
        }
    }
}
...

原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/108416332

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页