Qt C++实现录屏录音功能的示例详解 - 网站

Qt C++实现录屏录音功能的示例详解

分类:C语言 · 发布时间:2023-04-15 00:31 · 阅读:9862

实现一个录屏+录音的功能且需要快速开发,Qt无疑是一个非常好的选择。他有丰富的类库和接口可以很好的满足开发需求。本文就来和大家聊聊具体的实现方法吧

录屏部分

录屏的主要思路为抓取屏幕截图,然后将其合成视频。抓取屏幕若使用qt自带的抓屏会出现抓不到鼠标的问题,所以应重写抓屏:

static QPixmap grabWindow(HWND winId, int x, int y, int w, int h) { RECT r; GetClientRect(winId, &r); if (w <0) w = r.right - r.left; if (h <0) h = r.bottom - r.top; HDC display_dc = GetDC(winId); HDC bitmap_dc = CreateCompatibleDC(display_dc); HBITMAP bitmap = CreateCompatibleBitmap(display_dc, w, h); HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap); BitBlt(bitmap_dc, 0, 0, w, h, display_dc, x, y, SRCCOPY | CAPTUREBLT); CURSORINFO ci; ci.cbSize = sizeof(CURSORINFO); GetCursorInfo(&ci); if ((ci.ptScreenPos.x > x) && (ci.ptScreenPos.y > y) && (ci.ptScreenPos.x <(x + w)) && (ci.ptScreenPos.y <(y + h))) DrawIcon(bitmap_dc, ci.ptScreenPos.x - x, ci.ptScreenPos.y - y, ci.hCursor); // clean up all but bitmap ReleaseDC(winId, display_dc); SelectObject(bitmap_dc, null_bitmap); DeleteDC(bitmap_dc); QPixmap pixmap = QtWin::fromHBITMAP(bitmap); DeleteObject(bitmap); return pixmap; }

这样抓取的图片会包括鼠标。

但是,如果直接while循环进行抓屏的话,一秒顶多抓10帧。所以应该启动一个计时器,按照想要的帧率进行抓屏。可惜,Qt的计时器会有各种各样的限制,所以我自己实现了计时器进行处理:

#pragma once #include  #include  #include  #include  #include  #include  #include  using namespace std; class STTimer { public: ~STTimer(void); template STTimer(F func):m_func(func){}; void Start(unsigned int secd,bool isBimmediately_run = false); void Stop(); void SetExit(bool b_exit); private: // 私有数据部分 std::atomic_bool m_bexit; std::atomic_bool m_bimmediately_run; // 是否立即执行 unsigned int m_imsec;    // 间隔时间 std::function m_func;    // 执行函数 std::thread m_thread; std::mutex m_mutex; std::condition_variable m_cond; void Run(); };
#include "STTimer.h" #include "ScreenController.h" #include  STTimer::~STTimer(void) { } void STTimer::Start(unsigned int sec, bool bim_run) { m_bexit.store(false); m_imsec = sec; m_bimmediately_run.store(bim_run); m_thread = std::thread(std::bind(&STTimer::Run,this)); } void STTimer::Stop() { m_bexit.store(true); m_cond.notify_all(); // 唤醒线程 if (m_thread.joinable()) { m_thread.join(); } } void STTimer::SetExit(bool b_exit) { m_bexit.store(b_exit); } void STTimer::Run() { if(m_bimmediately_run.load()) { if(m_func) { m_func(); } } while(!m_bexit.load()) { qDebug()<<"runmning"; std::unique_lock locker(m_mutex); m_cond.wait_for(locker,std::chrono::milliseconds(m_imsec),[this](){return m_bexit.load(); }); if(m_func) { m_func(); } } if(m_bexit.load()) { return; } }

这样,就可以多线程进行抓屏了,合成视频我使用的是avilib,理论上它可以同时合成音频,但合成后除了potplayer都无法解码,所以仅用它做合成视频。

void ScreenController::getOneFrame() { int ids = curController->getId(); controlIds(false, ids); std::thread t1(startThread,ids); t1.detach(); } void ScreenController::startThread(int ids) { QPixmap mp = grabWindow((HWND)QApplication::desktop()->winId(), curController->curRect.x(), curController->curRect.y(), curController->curRect.width(), curController->curRect.height()); QByteArray ba; QBuffer bf(&ba); mp.save(&bf, "jpg", 100); char* framBf = ba.data(); int byteLen = ba.length(); qDebug()<m_smutex2); AVI_write_frame(curController->avfd, framBf, byteLen, 1); lockeer.unlock(); controlIds(true, ids); }

在停止录屏时,需要判断抓屏线程是否结束,很多人会想到线程池,其实不必那么复杂,只需为每个线程绑定一个独立的id,然后操作含有这个id的列表即可。

void ScreenController::controlIds(bool isDelete, int index) { QMutexLocker locker(&curController->m_smutex); if (isDelete) { int ind = curController->ids.indexOf(index); curController->ids.removeAt(ind); } else { curController->ids.push_back(index); } } 

录音部分

录音部分其实非常简单,仅需使用qt的模板即可实现:

QAudioDeviceInfo info = QAudioDeviceInfo::availableDevices(QAudio::AudioInput).at(macIndex); recorder = new QAudioRecorder(this); QAudioEncoderSettings settings = recorder->audioSettings(); settings.setCodec("audio/PCM");   // 这些是QAudioRecorder是设置,见名思意 settings.setBitRate(96000); //settings.setSampleRate(44100); settings.setChannelCount(2); settings.setQuality(QMultimedia::EncodingQuality::HighQuality); settings.setEncodingMode(QMultimedia::ConstantQualityEncoding); recorder->setAudioSettings(settings); recorder->setAudioInput(info.deviceName()); recorder->setOutputLocation(QUrl::fromLocalFile(fileName)); recorder->setContainerFormat("audio/wav"); recorder->record();

合成部分

合成我使用的是ffmpeg进行视频合成。仅需传入参数即可。

void CaptureController::MakeVideo() { if(curController->isMakingVideo) { return; } qDebug()<<"making video"; curController->isMakingVideo = true; QString program = QCoreApplication::applicationDirPath(); program += "/ffmpeg.exe"; qDebug()<<"program"; qDebug() << program; QProcess process; QStringList arguments; arguments << "-i" << curController->voicefileName << "-i" << curController->screenfileName << "-s" << QString::number(curController->screenRect.width()) + "x" + QString::number(curController->screenRect.height()) <<"-b:v" << "40000k" << curController->finalfileName;//传递到exe的参数 qDebug() << arguments; process.start(program, arguments); process.waitForFinished(); QFile f1(curController->voicefileName); QFile f2(curController->screenfileName); f1.remove(); f2.remove(); curController->isMakingVideo = false; }

转成动态库

有时我们很想将这个功能提供给其他人使用,但是其他人未必使用qt,甚至未必使用C++,那么就需要将其封装成动态库。但是,qt的消息机制是十分独立的,在没有QApplication::exec()的时候,或者说没有发起qt独立的消息循环机制的时候,他的信号槽机制将不会起作用。比如这个录音模块,在直接提供给他人使用的时候将不会录制到任何声音。所以需要对录音部分进行封装。

class MCCtClass:public QThread{ public: MCCtClass(); void startTestingMac(int index); int getCurrentVoice(); void startCapVoice(int index); void stopThread(); void setFileName(QString name); protected: virtual void run(); private: volatile bool isStop; int macIndex; int currentRun; QEventLoop *lp; MacController *ct; QString fileName; };
MCCtClass::MCCtClass() { currentRun = -1; ct = nullptr; } void MCCtClass::startCapVoice(int index) { currentRun = 1; macIndex = index; this->start(); } void MCCtClass::startTestingMac(int index) { currentRun =2; macIndex = index; this->start(); } void MCCtClass::setFileName(QString name) { fileName = name; } void MCCtClass::run() { ct = new MacController(); if(currentRun == 1) { ct->SetFileName(fileName); ct->StartRecordingVoice(macIndex); lp = new QEventLoop(); lp->exec(); } else if(currentRun == 2) { qDebug()<<"run2"; ct->StartTestingMac(macIndex); lp = new QEventLoop(); lp->exec(); } } int MCCtClass::getCurrentVoice() { if(ct == nullptr) { return 0; } return ct->getTestVolume(); } void MCCtClass::stopThread() { lp->exit(); lp->deleteLater(); if(currentRun == 1) { ct->StopRecordingVoice(); } else if(currentRun == 2) { ct->StopTestingMac(); } ct->deleteLater(); ct = nullptr; }

使用qthread派生出一个独立的麦克风操作类,在run函数中启动一个独立的消息循环,这样麦克风的录制功能就可以进行了。

关于动态库的封装,用到了传说中的QMFCAPP,这个大家百度一下随便下载一个就可以,但这样封装还有问题,因为别人未必有qt环境,所以我对它进行了二次封装。相信各位也有其他的解决办法。

完整项目

我做的demo提供了额外的麦克风检测和屏幕检测功能,同时也提供了视频合成进度检查的接口,喜欢的朋友可以下载进行参考。

完整的项目和msvc_2012版本的动态库可以复制下面的链接进行下载:

链接:https://pan.baidu.com/s/1eBtLfE21rZ545T7rrmmXpg

提取码:qg1h

到此这篇关于Qt C++实现录屏录音功能的示例详解的文章就介绍到这了,更多相关Qt C++录屏录音内容请搜索0133技术站以前的文章或继续浏览下面的相关文章希望大家以后多多支持0133技术站!

标签:
Qt C++ 录屏 录音

相关文章

C/C++预处理浅析使用形式

预处理是指在进行编译的词法扫描和语法分析之前所作的工作。预处理指令指示在程序正式编译前就由编译器进行的操作,可放在程序中任何位置。处理完毕自动进入对源程序的编译。C/C++中的预处理主要包含三种:文件包含、宏定义、条件编译

C++中的字符串编码处理方法

这篇文章主要介绍了C++中的字符串编码处理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

C++中的强制类型转换操作详解

C++中提供了四种强制类型转换技术:static_cast、dynamic_cast、reinterpret_cast和const_cast。这些技术能够在需要时将一种类型转换为另一种类型,但需要注意它们的适用条件和安全性。程序员需要根据具体情况选择合适的强制类型转换方式,以确保程序的正确性和可靠性

QT+OpenGL实现简单图形的绘制

这篇文章主要为大家详细介绍了如何利用QT和OpenGL实现简单图形的绘制,文中的示例代码讲解详细,具有一定的借鉴价值,需要的可以参考一下

C语言之结构体定义 typedef struct 用法详解和用法小结

这篇文章主要介绍了C语言的结构体定义typedef struct用法详解和用法小结,typedef是类型定义,typedef struct 是为了使用这个结构体方便,感兴趣的同学可以参考阅读

返回分类 返回首页