实现一个录屏+录音的功能且需要快速开发,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_2012版本的动态库可以复制下面的链接进行下载:

链接:https://pan.baidu.com/s/1UNJ--Ief7QN-NkbOo5E3Qw 
提取码:qtsc 
--来自百度网盘超级会员V2的分享

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

  1. android ffmpeg编译动态库,最简单的android studio调用ffmpeg动态库

    准备工作: 1.编译好的ffmpeg动态库.so文件,以及ffmpeg的头文件: 2.android studio开发环境,包括NDK等可用环境: 步骤一: 新建一个android studio工程, ...

  2. Unity录屏的坑(FFmpeg)

    一.FFmpeg 安装与Setup Screen Capturer Recorder v0.12.10安装 下载地址:链接:https://pan.baidu.com/s/1cIKyKMa6nhymV ...

  3. 解决首次安装Captura免费录屏软件时出现FFMPEG was not found on your system的问题

    免费录屏软件:Captura, 免费的国外开源软件,无录制时长限制. 链接:https://pan.baidu.com/s/11_99TObpCcxzSGJHA1H5ug 提取码:14d9 复制这段内 ...

  4. 免费录屏软件 Captura 及 FFmpeg 安装配置教程

    mathewsachin.github.io/Captura/ 下载Portable版本,找到下载的文件,解压到想要放置的位置. 双击软件开始运行,可能会出现问题,报出错误,这是因为默认热键与系统热键 ...

  5. ffmpeg 录屏推流_使用FFMpeg命令行录屏推rtmp流

    最近在做局域网内屏幕分享方面的东西,要把录制一台设备的屏幕然后实时推送给内网的一个或多个用户. 做了很多实验,效果还没有达到要求,这里分享一下推rtmp流的实验. 实验使用到的各种工具:FFmpeg. ...

  6. Qt 调用CUDA静态库和动态库生成与配置

    前言 通过将CUDA相关计算操作放在库中,方便在项目中调用,省去了每次编译cu文件的麻烦,也便于集成到其他平台上. 关于部署CUDA加速的程序时,往往对CUDA加速的程序编译为动态链接库或者静态链接库 ...

  7. 基于ffmpeg的音视频转码、压制、录屏、裁切、合并、提取

    ffmpeg转码.压制.录屏.裁切.合并.提取 1.ffmpeg介绍 2.转换格式 3.音频转码 4.视频转码 5.码率控制模式 6.合并.提取音视频 7.截取.连接音视频 8.截图.水印.动图 9. ...

  8. 免费开源录屏软件Captura设置中文界面及录像时出现FFmpeg was not found on yoursystem 解决方法

    大家好,最近想制作一些课件,需要使用录屏软件,于是接触到了Captura这个免费的录屏软件. 由于接触到的Captura版本是9.0.0Beta3和9.0.0Beta4两个版本,在此介绍一下如何设置中 ...

  9. JavaCV音视频开发宝典:JavaCV混合屏幕录屏和系统声音录制mp4视频文件(windows桌面屏幕和系统声音混合录制)

    <JavaCV音视频开发宝典>专栏目录导航 <JavaCV音视频开发宝典>专栏介绍和目录 前言 之前已经写过dshow方式采集摄像头画面.麦克风.系统声音和桌面屏幕画面. 之前 ...

最新文章

  1. 使用vscode连接服务器写代码指南
  2. LinkedList 实现原理及源码解析(jdk8 底层⽤的是链表)
  3. 利用Relations实现多DataTable的聚合
  4. server sql 无法从long转为int_MySQL中,21个写SQL的好习惯(修正版)
  5. php携程 线程,携程api开发解决方法
  6. Android:Android SDK的下载与安装
  7. 扒一扒9.3阅兵直播如何采用虚拟现实技术
  8. jvm中的native、方法区
  9. matlab 2ask原理,基于Matlab对2ASK调制解调及其仿真设计
  10. 小程序随笔(关于香港手机号无法接收短信)
  11. vlan间路由详细实验步骤
  12. Ethernet/IP以太网接M12 X-Coded 协议:port1(Ethernet连接)
  13. openGL之glsl入门3--正弦函数叠加为方波
  14. 为泄愤程序员攻击北京摇号网站昨受审 曾为市高考状元
  15. 五、03【Java IO模型】之字节流
  16. 【每日微信新闻早报简报】10月11日 星期五
  17. 人工智能既预测世界杯冠军,还帮你“讨债”?
  18. c语言驱动显卡,佳能 Tesla C2050 驱动程序下载-更新佳能软件(显卡)
  19. Java Language——IO 机制
  20. 电源热敏电阻计算选择

热门文章

  1. 怎样在平时中培育小孩人格
  2. 软件工程(速成)——第一章 软件与软件工程
  3. python scrapy框架 抓取的图片路径打不开图片_Python使用Scrapy爬虫框架全站爬取图片并保存本地的实现代码...
  4. 无法打开Win10计算机管理,win10系统我的电脑管理打不开怎么办
  5. 苹果发布iOS9.1 Beta 3:新增壁纸和表情
  6. 秘籍:MSN好友IP地址随便查
  7. 水晶报表设置纸张大小
  8. 忘了neo4j密码怎么办
  9. 放大器的传递函数_运放全差分放大器实现单端与平衡的相互转换
  10. 任职母校!C9,迎来新副校长!