欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Qt C++實(shí)現(xiàn)錄屏錄音功能的示例詳解

 更新時(shí)間:2023年03月08日 10:44:19   作者:TGTSTTG  
實(shí)現(xiàn)一個(gè)錄屏+錄音的功能且需要快速開發(fā),Qt無疑是一個(gè)非常好的選擇。他有豐富的類庫和接口可以很好的滿足開發(fā)需求。本文就來和大家聊聊具體的實(shí)現(xiàn)方法吧

錄屏部分

錄屏的主要思路為抓取屏幕截圖,然后將其合成視頻。抓取屏幕若使用qt自帶的抓屏?xí)霈F(xiàn)抓不到鼠標(biāo)的問題,所以應(yīng)重寫抓屏:

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;
 
}

這樣抓取的圖片會(huì)包括鼠標(biāo)。

但是,如果直接while循環(huán)進(jìn)行抓屏的話,一秒頂多抓10幀。所以應(yīng)該啟動(dòng)一個(gè)計(jì)時(shí)器,按照想要的幀率進(jìn)行抓屏??上В琎t的計(jì)時(shí)器會(huì)有各種各樣的限制,所以我自己實(shí)現(xiàn)了計(jì)時(shí)器進(jìn)行處理:

#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: // 私有數(shù)據(jù)部分
    std::atomic_bool m_bexit;
    std::atomic_bool m_bimmediately_run; // 是否立即執(zhí)行
    unsigned int m_imsec;    // 間隔時(shí)間
    std::function<void()> m_func;    // 執(zhí)行函數(shù)
    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;
    }
    
}

這樣,就可以多線程進(jìn)行抓屏了,合成視頻我使用的是avilib,理論上它可以同時(shí)合成音頻,但合成后除了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);
}

在停止錄屏?xí)r,需要判斷抓屏線程是否結(jié)束,很多人會(huì)想到線程池,其實(shí)不必那么復(fù)雜,只需為每個(gè)線程綁定一個(gè)獨(dú)立的id,然后操作含有這個(gè)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);
    }
}

錄音部分

錄音部分其實(shí)非常簡(jiǎn)單,僅需使用qt的模板即可實(shí)現(xiàn):

QAudioDeviceInfo info = QAudioDeviceInfo::availableDevices(QAudio::AudioInput).at(macIndex);
    recorder = new QAudioRecorder(this);
    QAudioEncoderSettings settings = recorder->audioSettings();
 
    settings.setCodec("audio/PCM");   // 這些是QAudioRecorder是設(shè)置,見名思意
    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進(jìn)行視頻合成。僅需傳入?yún)?shù)即可。

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的參數(shù)
 
    qDebug() << arguments;
    process.start(program, arguments);
 
    process.waitForFinished();
    QFile f1(curController->voicefileName);
    QFile f2(curController->screenfileName);
    f1.remove();
    f2.remove();
    curController->isMakingVideo = false;
}

轉(zhuǎn)成動(dòng)態(tài)庫

有時(shí)我們很想將這個(gè)功能提供給其他人使用,但是其他人未必使用qt,甚至未必使用C++,那么就需要將其封裝成動(dòng)態(tài)庫。但是,qt的消息機(jī)制是十分獨(dú)立的,在沒有QApplication::exec()的時(shí)候,或者說沒有發(fā)起qt獨(dú)立的消息循環(huán)機(jī)制的時(shí)候,他的信號(hào)槽機(jī)制將不會(huì)起作用。比如這個(gè)錄音模塊,在直接提供給他人使用的時(shí)候?qū)⒉粫?huì)錄制到任何聲音。所以需要對(duì)錄音部分進(jìn)行封裝。

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派生出一個(gè)獨(dú)立的麥克風(fēng)操作類,在run函數(shù)中啟動(dòng)一個(gè)獨(dú)立的消息循環(huán),這樣麥克風(fēng)的錄制功能就可以進(jìn)行了。

關(guān)于動(dòng)態(tài)庫的封裝,用到了傳說中的QMFCAPP,這個(gè)大家百度一下隨便下載一個(gè)就可以,但這樣封裝還有問題,因?yàn)閯e人未必有qt環(huán)境,所以我對(duì)它進(jìn)行了二次封裝。相信各位也有其他的解決辦法。

完整項(xiàng)目

我做的demo提供了額外的麥克風(fēng)檢測(cè)和屏幕檢測(cè)功能,同時(shí)也提供了視頻合成進(jìn)度檢查的接口,喜歡的朋友可以下載進(jìn)行參考。

完整的項(xiàng)目和msvc_2012版本的動(dòng)態(tài)庫可以復(fù)制下面的鏈接進(jìn)行下載:

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

提取碼:qg1h

到此這篇關(guān)于Qt C++實(shí)現(xiàn)錄屏錄音功能的示例詳解的文章就介紹到這了,更多相關(guān)Qt C++錄屏錄音內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語言 scanf的工作原理詳解

    C語言 scanf的工作原理詳解

    這篇文章主要為大家介紹了C語言 scanf的工作原理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • C++淺析數(shù)據(jù)在內(nèi)存中如何存儲(chǔ)

    C++淺析數(shù)據(jù)在內(nèi)存中如何存儲(chǔ)

    使用編程語言進(jìn)行編程時(shí),需要用到各種變量來存儲(chǔ)各種信息。變量保留的是它所存儲(chǔ)的值的內(nèi)存位置。這意味著,當(dāng)您創(chuàng)建一個(gè)變量時(shí),就會(huì)在內(nèi)存中保留一些空間。您可能需要存儲(chǔ)各種數(shù)據(jù)類型的信息,操作系統(tǒng)會(huì)根據(jù)變量的數(shù)據(jù)類型,來分配內(nèi)存和決定在保留內(nèi)存中存儲(chǔ)什么
    2022-08-08
  • C++實(shí)現(xiàn)LeetCode(208.實(shí)現(xiàn)字典樹(前綴樹))

    C++實(shí)現(xiàn)LeetCode(208.實(shí)現(xiàn)字典樹(前綴樹))

    這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(208.實(shí)現(xiàn)字典樹(前綴樹)),本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • C語言中l(wèi)seek()函數(shù)和fseek()函數(shù)的使用詳解

    C語言中l(wèi)seek()函數(shù)和fseek()函數(shù)的使用詳解

    這篇文章主要介紹了C語言中l(wèi)seek()函數(shù)和fseek()函數(shù)的使用詳解,是C語言入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-08-08
  • c++如何分割字符串示例代碼

    c++如何分割字符串示例代碼

    因?yàn)閏++字符串沒有split函數(shù),所以字符串分割單詞的時(shí)候必須自己手寫,也相當(dāng)于自己實(shí)現(xiàn)一個(gè)split函數(shù)吧!下面跟小編一起來看看如何實(shí)現(xiàn)這個(gè)功能。
    2016-08-08
  • C語言中如何利用循環(huán)嵌套輸出一個(gè)菱形

    C語言中如何利用循環(huán)嵌套輸出一個(gè)菱形

    這篇文章主要介紹了C語言中如何利用循環(huán)嵌套輸出一個(gè)菱形問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • QT+OpenCV實(shí)現(xiàn)錄屏功能

    QT+OpenCV實(shí)現(xiàn)錄屏功能

    這篇文章主要為大家詳細(xì)介紹了QT+OpenCV實(shí)現(xiàn)錄屏功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • C++中關(guān)鍵字Struct和Class的區(qū)別

    C++中關(guān)鍵字Struct和Class的區(qū)別

    這篇文章主要介紹了C++中關(guān)鍵字Struct和Class的區(qū)別,本文用大量實(shí)例講解了Struct和Class的區(qū)別,需要的朋友可以參考下
    2014-09-09
  • C語言職工信息管理系統(tǒng)源碼

    C語言職工信息管理系統(tǒng)源碼

    這篇文章主要為大家詳細(xì)介紹了C語言職工信息管理系統(tǒng)源碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • C++ std::unique_lock 用法實(shí)例詳解

    C++ std::unique_lock 用法實(shí)例詳解

    std::unique_lock 是 C++11 提供的一個(gè)用于管理互斥鎖的類,它提供了更靈活的鎖管理功能,適用于各種多線程場(chǎng)景,這篇文章給大家介紹了C++ std::unique_lock 用法,感興趣的朋友跟隨小編一起看看吧
    2023-09-09

最新評(píng)論