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目录。

ffmpeg录制mp4,ffmpeg,qt,音视频

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

ffmpeg录制mp4,ffmpeg,qt,音视频

三、实现代码

1、main.cpp

#include "screenRecord.h"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    screenRecord w;
    w.show();
    return a.exec();
}

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_OBJECT

public:
    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

ffmpeg录制mp4,ffmpeg,qt,音视频

 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;
    //ffmpeg
    AVFormatContext* 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_date_time.toString("/yyyyMMdd_hhmmss");
	QString SaveFile = m_outFileName + current_date + ".mp4";
	if (OpenOutPut(SaveFile.toStdString().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我并不熟悉,只求能正常跑通使用就行了,花了好多时间踩坑,在此记录一下。

ffmpeg录制mp4,ffmpeg,qt,音视频文章来源地址https://www.toymoban.com/news/detail-555904.html

到了这里,关于Qt+FFmpeg简单实现录屏并保存为MP4视频的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FFmpeg将编码后数据保存成mp4

          以下测试代码实现的功能是:持续从内存块中获取原始数据,然后依次进行解码、编码、最后保存成mp4视频文件。       可保存成单个视频文件,也可指定每个视频文件的总帧数,保存多个视频文件。       为了便于查看和修改,这里将可独立的程序段存放在单个函

    2024年02月13日
    浏览(43)
  • Qt/C++音视频开发69-保存监控pcm音频数据到mp4文件/监控录像/录像存储和回放/264/265/aac/pcm等

    用ffmpeg做音视频保存到mp4文件,都会遇到一个问题,尤其是在视频监控行业,就是监控摄像头设置的音频是PCM/G711A/G711U,解码后对应的格式是pcm_s16be/pcm_alaw/pcm_mulaw,将这个原始的音频流保存到mp4文件是会报错的,在调用avformat_write_header写文件头的时候提示(-22) Invalid argument,

    2024年04月11日
    浏览(58)
  • Node.js脚本项目合集(一):Node.js+FFmpeg实现批量从B站导出离线缓存视频到mp4格式,mp4转mp3,实现听歌自由

    最近被一首JISOO的FLOWER歌洗脑,但碍于版权原因,只能在B站上看mv视频,盯着尬舞听歌着实有些尴尬,突发奇想,如果能将视频中的音频和视频分开不就能只听音乐,不用看尴尬的舞蹈吗?刚好手机上有不少B站本地的学习视频想导入到电脑上看,可是B站下载的格式.m4s文件,

    2024年02月07日
    浏览(75)
  • ffmpeg操作MP4视频封面

    提取视频封面 视频流中提取帧图 3.重新设置视频封面 更多参考: https://blog.csdn.net/m0_37624402/article/details/125123818

    2024年02月04日
    浏览(44)
  • 20231005使用ffmpeg旋转MP4视频

    20231005使用ffmpeg旋转MP4视频 2023/10/5 12:21 百度搜搜:ffmpeg 旋转90度 https://zhuanlan.zhihu.com/p/637790915 【FFmpeg实战】FFMPEG常用命令行 https://blog.csdn.net/weixin_37515325/article/details/127817057 FFMPEG常用命令行 5.视频旋转 顺时针旋转90度:ffmpeg -i test.mp4 -vf \\\"transpose=1\\\" out.mp4//顺时针旋转90° 逆时针

    2024年02月07日
    浏览(50)
  • 使用ffmpeg将多个TS视频拼接成mp4视频

    点击下面网址下载对应版本安装 https://ffmpeg.org/download.html   下载好之后添加环境变量 添加成功之后在cmd窗口输入ffmpeg,显示如下结果则为成功  合并单个文件或者少量文件时,通过以下命令合并 多个ts视频可以编辑一个txt文档,file.txt 注意:这里必须是单引号,双引号会报错 进入

    2024年02月11日
    浏览(84)
  • 【音视频 ffmpeg 学习】 RTMP推流 mp4文件

    1.RTMP(实时消息传输协议)是Adobe 公司开发的一个基于TCP的应用层协议。 2.RTMP协议中基本的数据单元称为消息(Message)。 3.当RTMP协议在互联网中传输数据的时候,消息会被拆分成更小的单元,称为消息块(Chunk)。 (1). linux 环境准备 安装nginx 和 rtmp模块 下载nginx安装包 下载

    2024年02月03日
    浏览(67)
  • 【音视频开发】FFmpeg转换与封装 I - MP4格式

    1 FFmpeg转换与封装 1.1 MP4格式转换 1.1.1 MP4格式标准         FFmpeg支持的媒体封装格式具有多样性与全面性,与此, 我们还可以使用FFmpeg来对媒体格式进行转换与封装 。 在互联网常见的格式中,跨平台最好的应该是 MP4 文件,因为 MP4 文件既可以在PC 平台的Flashplayer中播放,

    2024年02月08日
    浏览(84)
  • 用ffmpeg解析mp4文件得到时长、比特率、音视频信息

    以下是使用C++语言调用FFmpeg获取视频流和音频流信息的示例代码: 上述代码通过 AVFormatContext 结构体和FFmpeg库函数 avformat_open_input 、 avformat_find_stream_info 等,获取MP4文件的视频流和音频流信息,并将结果存储到 MediaInfo 类中。在实际应用中,可以将上述代码封装成一个函数,

    2024年02月12日
    浏览(67)
  • FFMPEG mp4封装实现

    FFMPEG mp4录像 author:lyn date:2022.09.28 version: ffmpeg4.1.3 1.mp4数据结构 2.ffmpeg mp4封装实现 3.mp4函数调用关系 4.参考资料 1.mp4数据结构 1.1mp4简介 MP4或称MPEG-4第14部分(英语:MPEG-4 Part 14)是一种标准的数字多媒体容器格式。MPEG-4第14部分的扩展名为 .mp4 ,以存储数字音频及数字视频

    2023年04月08日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包