2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > Qt C++ 录屏录音功能实现(avilib+ffmpeg)以及动态库生成

Qt C++ 录屏录音功能实现(avilib+ffmpeg)以及动态库生成

时间:2020-08-12 21:32:23

相关推荐

Qt C++ 录屏录音功能实现(avilib+ffmpeg)以及动态库生成

实现一个录屏+录音的功能且需要快速开发,Qt无疑是一个非常好的选择。他有丰富的类库和接口可以很好的满足开发需求。

完整demo代码在文章最下方的百度网盘链接中,有需要的各位可以随意下载。

录屏部分

录屏的主要思路为抓取屏幕截图,然后将其合成视频。抓取屏幕若使用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 bitmapReleaseDC(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 <iostream>#include <string>#include <thread>#include <chrono>#include <atomic>#include <memory>#include <condition_variable>using namespace std;class STTimer{public:~STTimer(void);template<class F>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<void()> m_func;// 执行函数std::thread m_thread;std::mutex m_mutex;std::condition_variable m_cond;void Run();};

#include "STTimer.h"#include "ScreenController.h"#include <QDebug>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<std::mutex> 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()<<byteLen;QMutexLocker lockeer(&curController->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_版本的动态库可以复制下面的链接进行下载:

链接:/s/1UNJ--Ief7QN-NkbOo5E3Qw

提取码:qtsc

--来自百度网盘超级会员V2的分享

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。