SoFunction
Updated on 2025-04-06

Detailed explanation of Qt C++'s example of realizing screen recording and recording function

Screen recording part

The main idea of ​​recording screenshots is to capture screenshots and then synthesize them into videos. If you use the screen capture that comes with qt, the mouse will not be caught, so you should rewritten the screen capture:

static QPixmap grabWindow(HWND winId, int x, int y, int w, int h)
{
 
    RECT r;
    GetClientRect(winId, &r);
 
    if (w < 0) w =  - ;
    if (h < 0) h =  - ;
 
    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;
     = sizeof(CURSORINFO);
    GetCursorInfo(&ci);
    if (( > x) && ( > y) && ( < (x + w)) && ( < (y + h)))
        DrawIcon(bitmap_dc,  - x,  - y, );
 
    // 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;
 
}

The captured image will include the mouse.

However, if you directly capture the screen while loop, you can capture at most 10 frames in a second. So a timer should be started and the screen is captured at the desired frame rate. Unfortunately, Qt's timer has various limitations, so I implemented the timer myself to handle it:

#pragma once
 
#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;thread&gt;
#include &lt;chrono&gt;
#include &lt;atomic&gt;
#include &lt;memory&gt;
#include &lt;condition_variable&gt;
 
using namespace std;
 
class STTimer
{
public:
    ~STTimer(void);
    template&lt;class F&gt;
    STTimer(F func):m_func(func){};
    void Start(unsigned int secd,bool isBimmediately_run = false);
    void Stop();
    void SetExit(bool b_exit);
private: // Private data part    std::atomic_bool m_bexit;
    std::atomic_bool m_bimmediately_run; // Whether to execute immediately    unsigned int m_imsec;    // Interval time    std::function&lt;void()&gt; m_func;    // Execute function    std::thread m_thread;
    std::mutex m_mutex;
    std::condition_variable m_cond;
 
    void Run();
 
 
};
#include ""
 
#include ""
#include &lt;QDebug&gt;
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(&amp;STTimer::Run,this));
}
void STTimer::Stop()
{
    m_bexit.store(true);
    m_cond.notify_all(); // Wake up the thread    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()&lt;&lt;"runmning";
        std::unique_lock&lt;std::mutex&gt; 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;
    }
    
}

In this way, you can capture screens through multiple threads. I use avilib for synthesizing videos. Theoretically, it can synthesize audio at the same time, but after synthesis, no one can decode except potplayer, so I only use it for synthesizing videos.

void ScreenController::getOneFrame()
{
    int ids = curController->getId();
    controlIds(false, ids);
    std::thread t1(startThread,ids);    
    ();
}
void ScreenController::startThread(int ids)
{
    QPixmap mp = grabWindow((HWND)QApplication::desktop()->winId(), curController->(), curController->(), curController->(), curController->());
    QByteArray ba;
    QBuffer bf(&ba);
    (&bf, "jpg", 100);
    char* framBf = ();
    int byteLen = ();
    qDebug()<<byteLen;
    QMutexLocker lockeer(&curController->m_smutex2);
    AVI_write_frame(curController->avfd, framBf, byteLen, 1);
    ();
    controlIds(true, ids);
}

When stopping screen recording, you need to determine whether the screen capture thread is over. Many people will think of thread pools. In fact, it doesn’t have to be that complicated. Just bind an independent id to each thread and then operate a list containing this id.

void ScreenController::controlIds(bool isDelete, int index)
{
    QMutexLocker locker(&curController->m_smutex);
    if (isDelete)
    {
        int ind = curController->(index);
        curController->(ind);
    }
    else
    {
        curController->ids.push_back(index);
    }
}

Recording part

The recording part is actually very simple, you can only use the qt template:

QAudioDeviceInfo info = QAudioDeviceInfo::availableDevices(QAudio::AudioInput).at(macIndex);
    recorder = new QAudioRecorder(this);
    QAudioEncoderSettings settings = recorder-&gt;audioSettings();
 
    ("audio/PCM");   // These are QAudioRecorders. They are set, see the meaning of    (96000);
    //(44100);
    (2);
    (QMultimedia::EncodingQuality::HighQuality);
    (QMultimedia::ConstantQualityEncoding);
    recorder-&gt;setAudioSettings(settings);
    recorder-&gt;setAudioInput(());
    recorder-&gt;setOutputLocation(QUrl::fromLocalFile(fileName));
    recorder-&gt;setContainerFormat("audio/wav");
    recorder-&gt;record();

Synthetic part

Synthesis I use ffmpeg for video synthesis. Just pass in parameters.

void CaptureController::MakeVideo()
{
    if(curController-&gt;isMakingVideo)
    {
        return;
    }
    qDebug()&lt;&lt;"making video";
    curController-&gt;isMakingVideo = true;
    QString program = QCoreApplication::applicationDirPath();
    program += "/";
    qDebug()&lt;&lt;"program";
    qDebug() &lt;&lt; program;
    QProcess process;
    QStringList arguments;
 
    arguments &lt;&lt; "-i" &lt;&lt; curController-&gt;voicefileName 
        &lt;&lt; "-i" &lt;&lt; curController-&gt;screenfileName 
        &lt;&lt; "-s" &lt;&lt; QString::number(curController-&gt;()) + "x" + QString::number(curController-&gt;())
        &lt;&lt;"-b:v" &lt;&lt; "40000k"
        &lt;&lt; curController-&gt;finalfileName;//Passed to exe parameters 
    qDebug() &lt;&lt; arguments;
    (program, arguments);
 
    ();
    QFile f1(curController-&gt;voicefileName);
    QFile f2(curController-&gt;screenfileName);
    ();
    ();
    curController-&gt;isMakingVideo = false;
}

Convert to dynamic library

Sometimes we want to provide this function to others, but others may not necessarily use qt, or even C++, so we need to encapsulate it into a dynamic library. However, the message mechanism of qt is very independent. Without QApplication::exec(), or when qt independent message loop mechanism is not initiated, its signal slot mechanism will not work. For example, this recording module will not record any sound when it is directly provided to others for use. Therefore, the recording part needs to be encapsulated.

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

Use qthread to derive an independent microphone operation class, and start an independent message loop in the run function, so that the microphone recording function can be performed.

Regarding the encapsulation of dynamic libraries, the legendary QMFCAPP is used. You can download any of this on Baidu, but there are still problems with encapsulation because others may not have a QT environment, so I encapsulated it twice. I believe you have other solutions.

Complete project

The demo I made provides additional microphone detection and screen detection functions, and also provides an interface for video synthesis progress checking. Friends who like it can download it for reference.

The complete project and the dynamic library of msvc_2012 version can be downloaded by copying the following link:

Link:/s/1eBtLfE21rZ545T7rrmmXpg

Extraction code: qg1h

This is the end of this article about the detailed explanation of Qt C++’s example implementation of screen recording and recording functions. For more related Qt C++ screen recording and recording content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!