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

目录
  • 录屏部分
  • 录音部分
  • 合成部分
  • 转成动态库
  • 完整项目

录屏部分

录屏的主要思路为抓取屏幕截图,然后将其合成视频。抓取屏幕若使用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 <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/1eBtLfE21rZ545T7rrmmXpg

提取码:qg1h

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

(0)

相关推荐

  • java实现录音播放功能

    本文实例为大家分享了java实现录音播放的具体代码,供大家参考,具体内容如下 需求: 1.实现可以从麦克风进行录音 2.可以停止录音 3.实现播放录音内容 4.并将所录的mp3文件全部存到F:/语音文件夹,语音的文件名以当前时间命名(java中是换算成秒),其中文件夹程序自己创建,不用担心出错 程序如下: import java.awt.*;   import javax.swing.*; import java.awt.event.*; import java.io.*; import jav

  • QT+OpenCV实现录屏功能

    本文使用QT+opencv来实现对指定窗体画面录制,并保存为avi文件. (1)获取窗体界面 QScreen类有一个grabWindow函数,可以用来获取窗体的画面,这个函数使用很简单,就是传入窗体句柄和要截取的坐标.但是这个函数有一个缺陷,它是通过截取桌面画面的方式,而不是通过 窗体获取界面,所以当你的窗体被其他窗体遮挡时,就无法截取完整的窗体界面,如果你是要录制整个桌面画面,那用这个函数就可以了,下面的方法调用GDI函数来实现,即使窗体被遮挡时仍然能够获取到完整界面,但是窗体最小化时也一样无

  • 利用Python3编写一个电脑录屏神器

    目录 1.引言 2.代码实战 2.1 编写思路 2.2 代码示例 3.总结 1.引言 女神:鱼哥,忙吗? 小鱼:嗯嗯, 忙, 哦不 , 不忙不忙. 女神:鱼哥,那能不能帮我个忙? 小鱼:这没问题啊这. 女神:你是认真说的吗? 小鱼:认真的啊,这还能不认真吗. 女神:说好了,不准反悔. 小鱼:我鱼愿意为你,牺牲睡觉的时间,做事情. 女神:鱼哥,你还帅哦. 小鱼:不要羡慕鱼哥, 鱼哥上学从来没在一班呆过. 女神:嗯嗯~ ~ 太哇塞了. 小鱼:说吧, 你有啥事需要我,该不会是…? 女神:想什么呢? 我

  • python实现录音小程序

    本文为大家分享了python实现录音小程序的具体代码,供大家参考,具体内容如下 学习目标:掌握python的pyaudio扩展包和Wave模块录制语音的方法 Wav音频:声道数,采样频率,量化位数 python Wav包是自带的,pyaudio需要下载 pip3 install pyaudio python读Wav文件: fp=wave.open('','rb') nf=fp.getnframes()#获取文件的采样点数量 print('sampwidth:',fp.getsampwidth()

  • 基于Python实现录音功能的示例代码

    目录 安装 查找可用的麦克风 录制音频 将音频保存到文件 今天我们来介绍一个好玩且实用的东西,我们使用python来实现一个录音的功能,废话不多说,让我们直接开始. 安装 使用 PIP 安装 PvRecorder: pip3 install pvrecorder 查找可用的麦克风 一台计算机可以有多个麦克风, 例如,笔记本电脑有一个内置麦克风,可能还连接了一个耳机, 第一步是找到我们要录音的麦克风. from pvrecorder import PvRecorder for index, dev

  • python可视化大屏库big_screen示例详解

    目录 big_screen 特点 安装环境 输入数据 本地运行 在线部署 对于从事数据领域的小伙伴来说,当需要阐述自己观点.展示项目成果时,我们需要在最短时间内让别人知道你的想法.我相信单调乏味的语言很难让别人快速理解.最直接有效的方式就是将数据如上图所示这样,进行可视化展现. 具体如下: big_screen 特点 便利性工具, 结构简单, 你只需传数据就可以实现数据大屏展示. 安装环境 pip install -i https://pypi.tuna.tsinghua.edu.cn/simp

  • echart实现大屏动效示例详解

    目录 1.通过dataZoom实现柱状图动态前移效果 2.叠加流光效果 3.柱状图光亮轮播 1.通过dataZoom实现柱状图动态前移效果 最近做大屏相关需求,产品说需要炫酷一点的效果,于是做了一些echart相关的动效 设置dataZoom当前缩放值,加定时器,实现轮播效果. 示例: option = { color: ['#1E90FF', '#FFA500'], tooltip: { trigger: 'axis', axisPointer: {} }, grid: { left: 20,

  • Qt利用QJson实现解析数组的示例详解

    目录 前言 第一步:进行数据转换 第二步:将字符串转成QJsonDocument格式 第三步:解析json数据 前言 现在有这样一个json结构,需要使用QJson来解析,结构如下: "code": "0001", "descrip": "文本描述1详细描述", "id": "1", "title": "文本1标题", "type&quo

  • JS实现一个微信录音功能过程示例详解

    目录 功能原型图 拆解需求 评估时间 代码实现 功能原型图 其实就是微信发送语音的功能.没有转文字的功能. 拆解需求 根据原型图可以很容易的得出我们需要做的内容包括下面三个部分: 接入微信的语音SDK 调用微信SDK的API逻辑 界面和交互的实现 其中第一点和第二点属于业务逻辑部分,第三点属于交互逻辑部分.对于业务逻辑和交互逻辑的关系在我的另外一篇文章描述过,我在vue中是这样拆分组件的 从原型图可以分析出如下的流程图: 评估时间 第三事情是评估时间.在接到这个需求的时候,我们需要假设我们在此之

  • QT+ffmpeg实现视频解析的示例详解

    目录 一.创建QT项目 二.引入ffmpeg 1.复制头文件和lib 2.复制bin文件 3.简单测试 三.视频解析 1.创建线程 2.创建自定义绘制控件 3.使用自定义控件 4.开启线程,进行视频解析 一.创建QT项目 首先安装了最新的Community版本,Creator是8.0.1版本了. 然后进行项目的创建. 得到的项目没有pro文件,而是CMakeLists.txt. 二.引入ffmpeg 从下面下载的ffmpeg-5.0.1-full_build-shared.7z. https:/

  • C++ Qt利用GPU加速计算的示例详解

    在 C++ 和 Qt 中,可以通过以下方式利用 GPU 进行加速计算: 使用 GPU 编程框架:可以使用类似 CUDA.OpenCL.DirectCompute 等 GPU 编程框架,这些框架提供了对 GPU 的访问和操作,可以使用 GPU 进行并行计算,从而加速计算速度. 使用图形 API:在 Qt 中,可以使用 QOpenGLFunctions 等 API 访问 GPU,这些 API 可以用于执行图形渲染.图像处理等任务,利用 GPU 进行计算. 使用高性能计算库:在 C++ 中,有一些高性

  • Python深度学习实战PyQt5布局管理项目示例详解

    目录 1. 从绝对定位到布局管理 1.1 什么是布局管理 1.2 Qt 中的布局管理方法 2. 水平布局(Horizontal Layout) 3. 垂直布局(Vertical Layout) 4. 栅格布局(Grid Layout) 5. 表格布局(Form Layout) 6. 嵌套布局 7. 容器布局 布局管理就是管理图形窗口中各个部件的位置和排列.图形窗口中的大量部件也需要通过布局管理,对部件进行整理分组.排列定位,才能使界面整齐有序.美观大方. 1. 从绝对定位到布局管理 1.1 什么

  • Python制作可视化报表的示例详解

    大家好,我是小F- 在数据展示中使用图表来分享自己的见解,是个非常常见的方法. 这也是Tableau.Power BI这类商业智能仪表盘持续流行的原因之一,这些工具为数据提供了精美的图形解释. 当然了,这些工具也有着不少缺点,比如不够灵活,无法让你自己创建设计. 当你对图表展示要求定制化时,编程也许就比较适合你,比如Echarts.D3.js. 今天小F给大家介绍一个用Python制作可视化报表的案例,主要是使用到Dash+Tailwindcss. 可视化报表效果如下,水果销售情况一览~ Das

  • Vue 服务端渲染SSR示例详解

    目录 手写Vue服务端渲染 一.开始vue-ssr之旅 二.采用模板渲染 三.ssr目录创建 四.通过webpack实现编译vue项目 app.js client-entry.js server-entry.js 五.配置客户端打包和服务端打包 六.配置运行脚本 七.服务端配置 七.通过json配置createBundleRenderer方法 八.集成VueRouter 配置入口文件 配置组件信息 防止刷新页面不存在 保证异步路由加载完成 十.集成vuex配置 在后端更新vuex 在浏览器运行时

  • React Native 中实现确认码组件示例详解

    目录 正文 实现原理 开源方案 正文 确认码控件也是一个较为常见的组件了,乍一看,貌似较难实现,但实则主要是障眼法. 实现原理 上图 CodeInput 组件的 UI 结构如下: <View style={[styles.container]}> <TextInput autoFocus={true} /> <View style={[styles.cover, StyleSheet.absoluteFillObject]} pointerEvents="none&

随机推荐