我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

录屏部分

录屏的主要思路为抓取屏幕截图,然后将其合成视频。抓取屏幕若使用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, class="lazy" data-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++录屏录音内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

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

android MediaRecorder实现录屏时带录音功能

下面说说android的事把 最近是不是也会遇到需求中需要用到录屏录音的功能,最近也是遇到的 现在整理完记录一下 首先呢,录音录屏需要权限 先贴一个动态权限类public class TalAllow {/*** RECORD_AUDIO
2022-06-07

Qt C++如何实现录屏录音功能

这篇文章主要介绍了Qt C++如何实现录屏录音功能的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Qt C++如何实现录屏录音功能文章都会有所收获,下面我们一起来看看吧。录屏部分录屏的主要思路为抓取屏幕截图,然后
2023-07-05

Python实现录屏功能的示例代码

这篇文章主要为大家详细介绍了如何利用Python实现录屏功能,文中的示例代码讲解详细,对我们掌握Python开发有一定的帮助,需要的可以参考一下
2023-03-24

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

这篇文章主要介绍了JS实现一个微信录音功能过程示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-02-21

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

今天我们来介绍一个好玩且实用的东西,我们使用python来实现一个录音的功能。文中的示例代码简洁易懂,感兴趣的小伙伴快跟随小编一起学习一下吧
2023-02-07

实例详解uniapp如何实现电话录音功能(附代码)

本篇文章给大家带来了关于uniapp的相关知识,其中主要介绍了怎么用uniapp实现拨打电话并且还能同步录音的功能,感兴趣的朋友一起来看一下吧,希望对大家有帮助。
2023-05-14

Android App调用MediaRecorder实现录音功能的实例

MediaRecorder Android的MediaRecorder包含了Audio和video的记录功能,在Android的界面上,Music和Video两个应用程序都是调用MediaRecorder实现的。 MediaRecorder
2022-06-06

Android实现登录功能demo示例

本文实例讲述了Android实现登录功能的方法。分享给大家供大家参考,具体如下: 安卓,在小编实习之前的那段岁月里面,小编都没有玩儿过,如果说玩儿过,那就是安卓手机了,咳咳,敲登录的时候有种特别久违的熟悉,这种熟悉的感觉就和当时敲机房收费系
2022-06-06

vue实现录音功能js-audio-recorder带波浪图效果的示例

这篇文章主要介绍了vue实现录音功能js-audio-recorder带波浪图效果,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2022-11-13

Android录音功能的实现以及踩坑实战记录

目录前言前提 :代码实现流程 :踩坑1、Android Q:2、RuntimeException:setAudioSource failed3、MediaRecorder: stop failed总结前言 最近接到个需求,不使用第三方SDK
2022-06-07

Android 录音与播放功能的简单实例

Android 录音与播放功能的简单实例最近在研究Android中一些常用的功能,像地图、拍照、录音和播放的实现等等,还有一些侧滑、动画等是如何实现的。今天就把录音和播放的实现分享一下,录音和播放比较简单,利用android内部的类即可实
2023-05-31

详解uniapp实现打电话录音功能(附核心代码)

本篇文章给大家带来了关于uniapp的相关知识,其中主要跟大家介绍uniapp怎么实现打电话录音功能,感兴趣的朋友下面一起来看一下吧,希望对大家有帮助。
2023-05-14

Spring Boot实现登录验证码功能的案例详解

验证码的作用可以有效防止其他人对某一个特定的注册用户用特定的程序暴力破解方式进行不断的登录尝试,接下来通过本文给大家介绍Spring Boot实现登录验证码功能,需要的朋友可以参考下
2022-11-13

Vue3+axios+Mock.js实现登录功能的示例代码

本文主要介绍了Vue3+axios+Mock.js实现登录功能的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-05-19

web实现页面录屏的示例分析

小编给大家分享一下web实现页面录屏的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!录屏重现错误场景如果你的应用有接入到web apm系统中,那么你可能就
2023-06-09

android 通过MediaRecorder实现简单的录音示例

整理文档,搜刮出一个android 通过MediaRecorder实现简单的录音示例,稍微整理精简一下做下分享。MainActivitypackage com.centaur.collectvoice; import android.med
2022-06-06

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录