【QT中实现摄像头播放、以及视频录制】

1、效果图

在这里插入图片描述

2、camerathread.h

#ifndef CAMERATHREAD_H
#define CAMERATHREAD_H

#include <QObject>
#include <QThread>
#include <QDebug>
#include <QImage>
#include <unistd.h>
#include <iostream>
#include <QDateTime>
#include <opencv2/opencv.hpp>
using namespace  std;
using namespace  cv;
class CameraThread :public QThread
{
    Q_OBJECT
public:
    static CameraThread *camerathread;
    static CameraThread *getInstance();
    void run();
    bool getIsRun() const;
    void setIsRun(bool value);
    int getFrame_width() const;
    int getFrame_height() const;
    void detecCarDaw(Mat &frame,CascadeClassifier &cascade,double scale);
    bool getIsStop() const;
    void setIsStop(bool value);
    bool getIsRecord() const;
    void setIsRecord(bool value);
    VideoWriter getWriter() const;
    bool getIsPersistent() const;
    void setIsPersistent(bool value);
    bool getIsRun_s() const;
    void setIsRun_s(bool value);
signals:
    void sendQImage(Mat frame);
private:
    CameraThread();
    CascadeClassifier cascade;//级联分类器的过滤器
    VideoCapture cap;
    Mat frame;
    VideoWriter writer;//OpenCV视频录制类
    bool isRun;//控制线程是否运行
    int frame_width;
    int frame_height;
    int recordNum; //录制帧率,设定为300帧
    bool isRun_s; //控制线程是否运行
    bool isStop; //控制线程结束
    bool isRecord; //控制线程是否开始录制
    bool isPersistent;
};

#endif // CAMERATHREAD_H

3、camerathread.cpp

#include "camerathread.h"

CameraThread * CameraThread::camerathread =nullptr;
CameraThread * CameraThread::getInstance()
{
    if(CameraThread::camerathread ==nullptr)
    {
        CameraThread::camerathread =new CameraThread ();
    }
    return CameraThread::camerathread;
}
CameraThread::CameraThread()
{

    this->isRun =true;
    cap.open(0);
    // 获取摄像头的宽度和高度
    this->frame_width = static_cast<int>(cap.get(CAP_PROP_FRAME_WIDTH));
    this->frame_height = static_cast<int>(cap.get(CAP_PROP_FRAME_HEIGHT));

    // 定义视频编码格式、帧率和画面尺寸
     int fourcc = VideoWriter::fourcc('X', 'V', 'I', 'D');
     Size frameSize(frame_width, frame_height);
     this->isRun_s=false;
     this->isStop=false;
     this->isRecord=false;
     this->isPersistent=false;
     this->recordNum=0;

}


bool CameraThread::getIsStop() const
{
    return isStop;
}

void CameraThread::setIsStop(bool value)
{
    isStop = value;
}



bool CameraThread::getIsRecord() const
{
    return isRecord;
}

void CameraThread::setIsRecord(bool value)
{
    isRecord = value;
    if(this->isRecord == false && writer.isOpened())
    {
        qDebug()<<"手动关闭"<<endl;
        writer.release();
        this->recordNum =0;
    }
}

VideoWriter CameraThread::getWriter() const
{
    return writer;
}

bool CameraThread::getIsPersistent() const
{
    return isPersistent;
}

void CameraThread::setIsPersistent(bool value)
{
    isPersistent = value;
}

bool CameraThread::getIsRun_s() const
{
    return isRun_s;
}

void CameraThread::setIsRun_s(bool value)
{
    isRun_s = value;
}
void CameraThread::detecCarDaw(Mat &frame, CascadeClassifier &cascade, double scale)
{
    //灰度处理
    Mat gray;
    cvtColor(frame,gray,CV_BGR2GRAY);
    //将灰度图缩小一半
    //cvRound()用于四舍五入       CV_8UC1:单通道
    Mat smalling(cvRound(frame.rows/scale),cvRound(frame.cols/scale),CV_8UC1);
    //resize()改变大小      INTER_LINEAR 等比例缩小
    resize(gray,smalling,smalling.size(),0,0,INTER_LINEAR);

    //直方图均衡化:利用直方图函数将图像黑白分明  (结果跟二值化类似)
    equalizeHist(smalling,smalling);
    //进行模型检测
    vector<Rect>cars;
    cascade.detectMultiScale(smalling,cars,1.1,2,0|CV_HAAR_SCALE_IMAGE,Size(30,30));

    //绘制边框
    vector<Rect>::const_iterator iter;//系统默认迭代器
    for(iter =cars.begin();iter!=cars.end();iter++)
    {
        rectangle(
                  frame,//原图
                  cvPoint(cvRound(iter->x*scale),cvRound(iter->y*scale)),//左上角坐标
                  cvPoint(cvRound((iter->x+iter->width)*scale),cvRound((iter->y+iter->height)*scale)),//右下角坐标
                  Scalar(0,255,0),//绿色
                  2,//像素大小
                  8//亮度
                  );
    }
}


int CameraThread::getFrame_height() const
{
    return frame_height;
}

int CameraThread::getFrame_width() const
{
    return frame_width;
}

bool CameraThread::getIsRun() const
{
    return isRun;
}

void CameraThread::setIsRun(bool value)
{
    isRun = value;
}
void CameraThread::run()
{
    cascade.load("D:/OpenCV/cars.xml");//识别级联分类器  车辆
    while(this->isRun ==true)
    {
        if(cap.read(frame))
        {
            cvtColor(frame,frame,CV_BGR2RGB);
            detecCarDaw(frame,cascade,2);
            emit sendQImage(frame);

            if(this->isStop ==false)//控制线程结束
            {
                if(this->isRun_s == true)//控制线程是否运行
                {
                    if(cap.read(frame))
                    {
                        if(this->isRecord==true)
                        {
                            if(this->recordNum ==0)
                            {
                                QDateTime current_date_time =QDateTime::currentDateTime();
                                QString current_date =current_date_time.toString("yyyy-MM-dd-hh-mm-ss");
                                //QString filename ="../data/"+current_date+".avi";
                                QString filename ="D:/Qtsoft/videoDemo/data/"+current_date+".avi";
                                qDebug()<<"filename="<<filename;
                                writer.open(filename.toStdString().c_str(),CV_FOURCC('M','J','P','G'),30.0,Size(frame.cols,frame.rows),true);
                            }
                            if(!writer.isOpened())
                            {
                                qDebug()<<"录制路径失败!!!"<<endl;
                            }
                            else
                            {
                                if(this->recordNum<300)
                                {
                                        //当前帧不等于总帧数,录制300帧还没有结束
                                        //qDebug()<<"录制中..."<<endl;
                                        writer<<frame;
                                        this->recordNum++;
                                }
                                else
                                {
                                    qDebug()<<"已经到300帧结束录制";
                                    writer.release();
                                    this->recordNum =0;
                                    if(this->isPersistent==true)
                                    {
                                         this->isRecord =true;
                                    }else if(this->isPersistent ==false)
                                    {
                                        this->isRecord =false;
                                    }

                                }
                            }
                        }
                        cvtColor(frame,frame,CV_BGR2RGB);
                    }
                    msleep(10);
                }
            }
        }
    }

}

4、mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "camerathread.h"
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void paintEvent(QPaintEvent * Eevent);
private:
    Ui::MainWindow *ui;
    CameraThread *ct;
//    VideoThread *vt;
    QImage image;
    Mat frame;
private slots:
    void isChecked(Mat frame);
    void on_pushButton_clicked();
    void on_checkBox_clicked(bool checked);
};

#endif // MAINWINDOW_H

5、mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->ct =CameraThread::getInstance();
    connect(this->ct,SIGNAL(sendQImage(Mat)),this,SLOT(isChecked(Mat)),Qt::BlockingQueuedConnection);
    this->ct->start();
//    this->vt =new VideoThread(0);
//    this->vt->start();
    waitKey(40);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::paintEvent(QPaintEvent *Eevent)
{
    ui->label->setPixmap(QPixmap::fromImage(this->image));
    QImage q_image = QImage(frame.data,frame.cols,frame.rows,QImage::Format_RGB888);
    ui->label->setPixmap(QPixmap::fromImage(q_image));
    ui->label->setScaledContents(true);
}

void MainWindow::isChecked(Mat frame)
{
    this->image =QImage(frame.data,frame.cols,frame.rows,QImage::Format_RGB888);
    this->image = this->image.scaled(ui->label->width(),ui->label->height());//以UI中的界面大小进行等比例缩放
    this->frame = frame.clone();
    this->update();
}

void MainWindow::on_pushButton_clicked()
{

    if(this->ct->getWriter().isOpened())
    {
               qDebug()<<"已经有录制项目:请先结束录制,再操作";
               return;
     }


    this->ct->setIsRun_s(true);
    this->ct->setIsRecord(true);

}

void MainWindow::on_checkBox_clicked(bool checked)
{
    if(checked==true)
    {
         //qDebug()<<"true";
        this->ct->setIsRecord(true);
        this->ct->setIsPersistent(true);
    }else if(checked==false)
    {
         //qDebug()<<"false";
         this->ct->setIsPersistent(false);
    }
}

6、main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();


    return a.exec();
}

要使用Qt和FFmpeg实现网络摄像头视频录制,您需要做以下几个步骤: 1. 安装FFmpeg库。您可以从官方网站下载预编译的二进制文件,或者自己编译源代码。 2. 在Qt项目导入FFmpeg库。您可以使用Qt的插件系统或手动链接库文件。 3. 创建一个Qt界面,用于显示摄像头捕捉到的视频流。 4. 使用FFmpeg库编写代码,将摄像头捕捉到的视频流转换为所需的格式并保存到本地文件。 以下是一个简单的示例代码,演示如何使用Qt和FFmpeg实现网络摄像头视频录制: ```cpp #include <QApplication> #include <QMainWindow> #include <QVBoxLayout> #include <QLabel> #include <QTimer> #include <QDebug> #include <QDateTime> #include <QThread> extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" } #define WIDTH 640 #define HEIGHT 480 #define FPS 25 class CameraWidget : public QWidget { public: CameraWidget(QWidget *parent = nullptr) : QWidget(parent) { label_ = new QLabel(this); QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(label_); setLayout(layout); } virtual ~CameraWidget() {} void start() { timer_ = new QTimer(this); connect(timer_, SIGNAL(timeout()), this, SLOT(update())); timer_->start(1000/FPS); } void stop() { timer_->stop(); delete timer_; timer_ = nullptr; } protected: void update() { // Capture camera frame QImage image(WIDTH, HEIGHT, QImage::Format_RGB888); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { QRgb color = qRgb(qrand() % 256, qrand() % 256, qrand() % 256); image.setPixel(x, y, color); } } // Display camera frame label_->setPixmap(QPixmap::fromImage(image)); // Save camera frame to file if (recording_) { if (!formatContext_) { initFormatContext(); } if (formatContext_ && videoStream_) { AVPacket packet; av_init_packet(&packet); packet.data = nullptr; packet.size = 0; if (av_new_packet(&packet, WIDTH * HEIGHT * 3) < 0) { qWarning() << "Failed to allocate packet"; } SwsContext *swsContext = sws_getContext(WIDTH, HEIGHT, AV_PIX_FMT_RGB24, WIDTH, HEIGHT, videoStream_->codecpar->format, SWS_BILINEAR, nullptr, nullptr, nullptr); if (swsContext) { uint8_t *srcData[4] = {(uint8_t *)image.bits(), nullptr, nullptr, nullptr}; int srcLinesize[4] = {image.bytesPerLine(), 0, 0, 0}; uint8_t *dstData[4] = {nullptr, nullptr, nullptr, nullptr}; int dstLinesize[4] = {0, 0, 0, 0}; av_image_alloc(dstData, dstLinesize, videoStream_->codecpar->width, videoStream_->codecpar->height, videoStream_->codecpar->format, 1); if (dstData[0] && av_image_fill_arrays(videoFrame_->data, videoFrame_->linesize, dstData[0], videoStream_->codecpar->format, videoStream_->codecpar->width, videoStream_->codecpar->height, 1) >= 0) { sws_scale(swsContext, srcData, srcLinesize, 0, HEIGHT, videoFrame_->data, videoFrame_->linesize); videoFrame_->pts = pts_++; if (avcodec_send_frame(videoCodecContext_, videoFrame_) == 0) { while (avcodec_receive_packet(videoCodecContext_, &packet) == 0) { packet.stream_index = videoStream_->index; packet.pts = av_rescale_q(packet.pts, videoCodecContext_->time_base, videoStream_->time_base); packet.dts = av_rescale_q(packet.dts, videoCodecContext_->time_base, videoStream_->time_base); packet.duration = av_rescale_q(packet.duration, videoCodecContext_->time_base, videoStream_->time_base); packet.pos = -1; if (av_interleaved_write_frame(formatContext_, &packet) < 0) { qWarning() << "Failed to write packet"; } av_packet_unref(&packet); } } } av_freep(&dstData[0]); sws_freeContext(swsContext); } av_packet_unref(&packet); } } } void initFormatContext() { QString filename = QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss"); filename = QDir::tempPath() + "/" + filename + ".avi"; if (avformat_alloc_output_context2(&formatContext_, nullptr, "avi", filename.toUtf8().constData()) < 0) { qWarning() << "Failed to allocate output format context"; return; } if (avio_open(&formatContext_->pb, filename.toUtf8().constData(), AVIO_FLAG_WRITE) < 0) { qWarning() << "Failed to open output file"; avformat_free_context(formatContext_); formatContext_ = nullptr; return; } videoStream_ = avformat_new_stream(formatContext_, nullptr); if (!videoStream_) { qWarning() << "Failed to create video stream"; avformat_free_context(formatContext_); formatContext_ = nullptr; return; } videoCodecContext_ = avcodec_alloc_context3(nullptr); if (!videoCodecContext_) { qWarning() << "Failed to allocate codec context"; avformat_free_context(formatContext_); formatContext_ = nullptr; return; } videoCodecContext_->codec_id = AV_CODEC_ID_H264; videoCodecContext_->codec_type = AVMEDIA_TYPE_VIDEO; videoCodecContext_->width = WIDTH; videoCodecContext_->height = HEIGHT; videoCodecContext_->pix_fmt = AV_PIX_FMT_YUV420P; videoCodecContext_->time_base = av_make_q(1, FPS); videoStream_->codecpar->codec_id = AV_CODEC_ID_H264; videoStream_->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; videoStream_->codecpar->width = WIDTH; videoStream_->codecpar->height = HEIGHT; videoStream_->codecpar->format = videoCodecContext_->pix_fmt; videoStream_->time_base = av_make_q(1, FPS); if (avcodec_open2(videoCodecContext_, avcodec_find_encoder(videoCodecContext_->codec_id), nullptr) < 0) { qWarning() << "Failed to open codec"; avformat_free_context(formatContext_); formatContext_ = nullptr; return; } if (avformat_write_header(formatContext_, nullptr) < 0) { qWarning() << "Failed to write header"; avformat_free_context(formatContext_); formatContext_ = nullptr; return; } videoFrame_ = av_frame_alloc(); if (!videoFrame_) { qWarning() << "Failed to allocate frame"; avformat_free_context(formatContext_); formatContext_ = nullptr; return; } videoFrame_->format = videoCodecContext_->pix_fmt; videoFrame_->width = WIDTH; videoFrame_->height = HEIGHT; if (av_frame_get_buffer(videoFrame_, 32) < 0) { qWarning() << "Failed to allocate frame buffer"; avformat_free_context(formatContext_); formatContext_ = nullptr; return; } pts_ = 0; } private: QLabel *label_; QTimer *timer_ = nullptr; bool recording_ = false; AVFormatContext *formatContext_ = nullptr; AVStream *videoStream_ = nullptr; AVCodecContext *videoCodecContext_ = nullptr; AVFrame *videoFrame_ = nullptr; int64_t pts_ = 0; }; int main(int argc, char *argv[]) { QApplication a(argc, argv); QMainWindow mainWindow; CameraWidget cameraWidget(&mainWindow); mainWindow.setCentralWidget(&cameraWidget); cameraWidget.start(); QThread::sleep(10); cameraWidget.stop(); return a.exec(); } ``` 该示例代码使用QtQTimer类定期捕获摄像头视频流,并将其显示在界面上。如果设置recording_为true,则使用FFmpeg库将当前帧转换为指定格式并写入文件。请注意,此示例代码仅是演示如何使用Qt和FFmpeg实现网络摄像头视频录制的示例,您需要根据您的实际需求进行修改和调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值