项目实战: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保存高分辨率修改源码后存储视频会导致通道混乱,需要手动矫正颜色通道。

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

#include

#include

#include

#include

#include

#include

#include

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 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 _listSize;

    QList _listFps;

    QList _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摄像头检测工具)