Qt+FFmpeg简单实现录屏并保存为MP4视频

阅读: 评论:0

Qt+FFmpeg简单实现录屏并保存为MP4视频

Qt+FFmpeg简单实现录屏并保存为MP4视频

一、前言

        最近需要实现一个录屏功能,网上查了好多资料,最可靠的方案当然还是用FFmpeg实现,但是也踩了很多坑,包括FFmpeg版本问题,vs2019里相关编译问题,FFmpeg也不太熟悉,很多代码不太容易看懂,想要按自己熟悉的方式实现录屏功能,花了一番功夫。

        如果你进来了,可以不用走了,应该能帮到你。

二、环境

        VS2019 + Qt5 + FFmpeg4.2.2

        FFmpeg的版本比较重要,不同的版本很多函数没法通用。

0、查看FFmpeg版本:

extern "C"
{
#include "libavutil/version.h"
}const char* versionInfo = av_version_info();
qDebug() << "FFmpeg Version: " << versionInfo;

1、vs2019配置FFmpeg

        右键点击项目——>属性——>VC++目录——>添加包含目录和库目录为FFmpeg的include和lib目录。

         右键点击项目——>属性——>链接器——>输入——>添加附加依赖项,avcodec.lib,avformat.lib,avutil.lib,avdevice.lib,avfilter.lib,postproc.lib,swresample.lib,swscale.lib

三、实现代码

1、main.cpp

#include "screenRecord.h"
#include <QtWidgets/QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);screenRecord w;w.show();();
}

2、screenRecord.h

#pragma once
#pragma execution_character_set("utf-8")#include <QtWidgets/QMainWindow>
#include "ui_screenRecord.h"
#include "ffmpegVideoThread.h"
#include <QDateTime>
#include <QTimer>
#include <QFileDialog>
#include <QMessageBox>class screenRecord : public QMainWindow
{Q_OBJECTpublic:screenRecord(QWidget *parent = nullptr);~screenRecord();private slots:void on_pushButton_startRecord_clicked();void on_pushButton_savePath_clicked();void slotShowTime();private:Ui::screenRecordClass ui;ffmpegVideoThread* m_ffmpegVideoThread;bool hasScreenRecord = false;QTimer* m_timer;int _h, _m, _s;
};

3、screenRecord.cpp.

#include "screenRecord.h"screenRecord::screenRecord(QWidget *parent): QMainWindow(parent)
{ui.setupUi(this);this->setWindowTitle("录屏");this->setWindowFlags(Qt::WindowCloseButtonHint);ui.pushButton_startRecord->setEnabled(false);m_timer = new QTimer();connect(m_timer, &QTimer::timeout, this, &screenRecord::slotShowTime);_h = 0;_m = 0;_s = 0;
}screenRecord::~screenRecord()
{}void screenRecord::on_pushButton_startRecord_clicked()
{if (ui.pushButton_startRecord->text() == "开始录屏") {ui.pushButton_startRecord->setText("停止录屏");ui.pushButton_savePath->setEnabled(false);//开始录屏m_ffmpegVideoThread = new ffmpegVideoThread();connect(m_ffmpegVideoThread, &ffmpegVideoThread::sigRecordError, this, [=](QString errorStr) {QMessageBox::warning(this, "错误", errorStr, "确定");m_timer->stop();});m_ffmpegVideoThread->start();m_ffmpegVideoThread->setOutMpeFileName(ui.lineEdit->text().trimmed().toStdString().c_str());hasScreenRecord = true;_h = 0;_m = 0;_s = 0;if (m_timer->isActive())m_timer->stop();m_timer->start(1000);}else {ui.pushButton_savePath->setEnabled(true);ui.pushButton_startRecord->setEnabled(true);ui.pushButton_startRecord->setText("开始录屏");//停止录屏m_ffmpegVideoThread->setStoped(true);m_ffmpegVideoThread->quitThread();m_ffmpegVideoThread->quit();m_ffmpegVideoThread->wait();m_ffmpegVideoThread->deleteLater();m_ffmpegVideoThread = NULL;hasScreenRecord = false;m_timer->stop();}
}void screenRecord::on_pushButton_savePath_clicked()
{QString dirpath = QFileDialog::getExistingDirectory(this, "选择目录", "./", QFileDialog::ShowDirsOnly);ui.lineEdit->setText(dirpath);ui.pushButton_startRecord->setEnabled(true);
}void screenRecord::slotShowTime()
{_s++;if (_s >= 60) {_m++;_s = 0;}if (_m >= 60) {_h++;_m = 0;}QString showTime = QString("%1:%2:%3").arg(_h).arg(_m).arg(_s);ui.label_recordTime->setText(showTime);
}

4、screenRecord.ui

 5、ffmpegVideoThread.h

#pragma once
#pragma execution_character_set("utf-8")
#include <QThread>
#include <QImage>
#include <QDebug>
#include <QDateTime>#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <conio.h>
#include <SDKDDKVer.h>extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/version.h"
};class ffmpegVideoThread : public QThread
{Q_OBJECT
public:ffmpegVideoThread(QObject* parent = Q_NULLPTR);~ffmpegVideoThread();void setStoped(bool);void quitThread();void setOutMpeFileName(QString outFileName);signals:void sigRecordError(QString);protected:virtual void run();private:bool stopped = false;bool quited = false;QString m_outFileName;//ffmpegAVFormatContext* pFormatCtx_Video = NULL,* pFormatCtx_Out = NULL;AVCodecContext* pCodecCtx_Video;AVCodec* pCodec_Video;AVFifoBuffer* fifo_video = NULL;int VideoIndex;CRITICAL_SECTION VideoSection;SwsContext* img_convert_ctx;int frame_size = 0;uint8_t* picture_buf = NULL, * frame_buf = NULL;bool bCap = true;static DWORD WINAPI ScreenCapThreadProc(LPVOID lpParam);int OpenVideoCapture();int OpenOutPut(const char* outFileName);
};

6、ffmpegVideoThread.cpp部分代码

ffmpegVideoThread.cpp部分代码暂不完全开放,有需要私聊我。

void ffmpegVideoThread::run()
{av_register_all();avdevice_register_all();if (OpenVideoCapture() < 0){emit sigRecordError("视频捕获打开错误!");return;}if (m_outFileName.isEmpty())m_outFileName = "./";QDateTime current_date_time = QDateTime::currentDateTime();QString current_date = current_String("/yyyyMMdd_hhmmss");QString SaveFile = m_outFileName + current_date + ".mp4";if (StdString().c_str()) < 0){emit sigRecordError("不能打开输出文件句柄!n输出文件:" + SaveFile);return;}InitializeCriticalSection(&VideoSection);AVFrame* picture = av_frame_alloc();int size = avpicture_get_size(pFormatCtx_Out->streams[VideoIndex]->codec->pix_fmt,pFormatCtx_Out->streams[VideoIndex]->codec->width, pFormatCtx_Out->streams[VideoIndex]->codec->height);picture_buf = new uint8_t[size];avpicture_fill((AVPicture*)picture, picture_buf,pFormatCtx_Out->streams[VideoIndex]->codec->pix_fmt,pFormatCtx_Out->streams[VideoIndex]->codec->width,pFormatCtx_Out->streams[VideoIndex]->codec->height);//开始捕获屏幕线程CreateThread(NULL, 0, ScreenCapThreadProc, this, 0, NULL);int64_t cur_pts_v = 0, cur_pts_a = 0;int VideoFrameIndex = 0, AudioFrameIndex = 0;while (!quited){while (!stopped) {//从fifo读取数据if (av_fifo_size(fifo_video) < frame_size && !bCap){cur_pts_v = 0x7fffffffffffffff;}if (av_fifo_size(fifo_video) >= size){EnterCriticalSection(&VideoSection);av_fifo_generic_read(fifo_video, picture_buf, size, NULL);LeaveCriticalSection(&VideoSection);avpicture_fill((AVPicture*)picture, picture_buf,pFormatCtx_Out->streams[VideoIndex]->codec->pix_fmt,pFormatCtx_Out->streams[VideoIndex]->codec->width,pFormatCtx_Out->streams[VideoIndex]->codec->height);picture->pts = VideoFrameIndex * ((pFormatCtx_Video->streams[0]->time_base.den / pFormatCtx_Video->streams[0]->time_base.num) / 15);int got_picture = 0;AVPacket pkt;av_init_packet(&pkt);pkt.data = NULL;pkt.size = 0;int ret = avcodec_encode_video2(pFormatCtx_Out->streams[VideoIndex]->codec, &pkt, picture, &got_picture);if (ret < 0){//编码错误,不理会此帧continue;}if (got_picture == 1){pkt.stream_index = VideoIndex;pkt.pts = av_rescale_q_rnd(pkt.pts, pFormatCtx_Video->streams[0]->time_base,pFormatCtx_Out->streams[VideoIndex]->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));pkt.dts = av_rescale_q_rnd(pkt.dts, pFormatCtx_Video->streams[0]->time_base,pFormatCtx_Out->streams[VideoIndex]->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));pkt.duration = ((pFormatCtx_Out->streams[0]->time_base.den / pFormatCtx_Out->streams[0]->time_base.num) / 15);cur_pts_v = pkt.pts;ret = av_interleaved_write_frame(pFormatCtx_Out, &pkt);av_free_packet(&pkt);}VideoFrameIndex++;}			}bCap = false;Sleep(2000);//等待采集线程关闭}delete[] picture_buf;av_fifo_free(fifo_video);av_write_trailer(pFormatCtx_Out);avio_close(pFormatCtx_Out->pb);avformat_free_context(pFormatCtx_Out);if (pFormatCtx_Video != NULL){avformat_close_input(&pFormatCtx_Video);pFormatCtx_Video = NULL;}
}

四、运行效果

ffmpeg实现录屏功能

五、最后

      FFmpeg4.2.2编译好了的,直接使用。FFmpeg4.2.2已编译好的

      FFmpeg我并不熟悉,只求能正常跑通使用就行了,花了好多时间踩坑,在此记录一下。

本文发布于:2024-01-29 01:40:47,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170646365211798.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:保存为   简单   视频   Qt   FFmpeg
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23