Qt+FFmpeg录屏录音

https://blog.csdn.net/ET_Endeavoring/article/details/88264693

Qt+FFmpeg录屏录音

录屏功能支持:开始,暂停,结束。
使用Qt+C++封装FFmpeg API,没有使用废弃的FFmpeg API。
主线程:Qt GUI线程,以后可接入录屏UI。
MuxThreadProc:复用线程,启动音视频采集线程。打开输入/输出流,然后从fifoBuffer读取帧,编码生成各种格式视频。
ScreenRecordThreadProc:视频采集线程,从输入流采集帧,缩放后写入fifoBuffer。
SoundRecordThreadProc:音频采集线程,从输入流采集样本,重采样后写入fifoBuffer。
ScreenRecordImpl.h
 

#pragma once
#include <Windows.h>
#include <atomic>
#include <QObject>
#include <QString>
#include <QMutex>
#include <condition_variable>
 
#ifdef    __cplusplus
extern "C"
{
#endif
struct AVFormatContext;
struct AVCodecContext;
struct AVCodec;
struct AVFifoBuffer;
struct AVAudioFifo;
struct AVFrame;
struct SwsContext;
struct SwrContext;
#ifdef __cplusplus
};
#endif
 
class ScreenRecordImpl : public QObject
{
    Q_OBJECT
private:
    enum RecordState {
        NotStarted,
        Started,
        Paused,
        Stopped,
        Unknown,
    };
public:
    ScreenRecordImpl(QObject * parent = Q_NULLPTR);
    void Init(const QVariantMap& map);
 
    private slots :
    void Start();
    void Pause();
    void Stop();
 
private:
    //从fifobuf读取音视频帧,写入输出流,复用,生成文件
    void MuxThreadProc();
    //从视频输入流读取帧,写入fifobuf
    void ScreenRecordThreadProc();
    //从音频输入流读取帧,写入fifobuf
    void SoundRecordThreadProc();
    int OpenVideo();
    int OpenAudio();
    int OpenOutput();
    QString GetSpeakerDeviceName();
    //获取麦克风设备名称
    QString GetMicrophoneDeviceName();
    AVFrame* AllocAudioFrame(AVCodecContext* c, int nbSamples);
    void InitVideoBuffer();
    void InitAudioBuffer();
    void FlushVideoDecoder();
    void FlushAudioDecoder();
    //void FlushVideoEncoder();
    //void FlushAudioEncoder();
    void FlushEncoders();
    void Release();
 
private:
    QString                m_filePath;
    int                    m_width;
    int                    m_height;
    int                    m_fps;
    int                    m_audioBitrate;
 
    int m_vIndex;        //输入视频流索引
    int m_aIndex;        //输入音频流索引
    int m_vOutIndex;    //输出视频流索引
    int m_aOutIndex;    //输出音频流索引
    AVFormatContext        *m_vFmtCtx;
    AVFormatContext        *m_aFmtCtx;
    AVFormatContext        *m_oFmtCtx;
    AVCodecContext        *m_vDecodeCtx;
    AVCodecContext        *m_aDecodeCtx;
    AVCodecContext        *m_vEncodeCtx;
    AVCodecContext        *m_aEncodeCtx;
    SwsContext            *m_swsCtx;
    SwrContext            *m_swrCtx;
    AVFifoBuffer        *m_vFifoBuf;
    AVAudioFifo            *m_aFifoBuf;
 
    AVFrame                *m_vOutFrame;
    uint8_t                *m_vOutFrameBuf;
    int                    m_vOutFrameSize;
 
    int                    m_nbSamples;
    RecordState            m_state;
    std::condition_variable m_cvNotPause;    //当点击暂停的时候,两个采集线程挂起
    std::mutex                m_mtxPause;
    std::condition_variable m_cvVBufNotFull;
    std::condition_variable m_cvVBufNotEmpty;
    std::mutex                m_mtxVBuf;
    std::condition_variable m_cvABufNotFull;
    std::condition_variable m_cvABufNotEmpty;
    std::mutex                m_mtxABuf;
    int64_t                    m_vCurPts;
    int64_t                    m_aCurPts;
};
 

ScreenRecordImpl.cpp

#ifdef    __cplusplus
extern "C"
{
#endif
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/imgutils.h"
#include "libswresample/swresample.h"
#include <libavutil\avassert.h>
#ifdef __cplusplus
};
#endif
 
#include "ScreenRecordImpl.h"
#include <QDebug>
#include <QAudioDeviceInfo>
#include <thread>
#include <fstream>
 
#include <dshow.h>
 
using namespace std;
 
int g_vCollectFrameCnt = 0;    //视频采集帧数
int g_vEncodeFrameCnt = 0;    //视频编码帧数
int g_aCollectFrameCnt = 0;    //音频采集帧数
int g_aEncodeFrameCnt = 0;    //音频编码帧数
 
ScreenRecordImpl::ScreenRecordImpl(QObject * parent) :
    QObject(parent)
    , m_fps(30)
    , m_vIndex(-1), m_aIndex(-1)
    , m_vFmtCtx(nullptr), m_aFmtCtx(nullptr), m_oFmtCtx(nullptr)
    , m_vDecodeCtx(nullptr), m_aDecodeCtx(nullptr)
    , m_vEncodeCtx(nullptr), m_aEncodeCtx(nullptr)
    , m_vFifoBuf(nullptr), m_aFifoBuf(nullptr)
    , m_swsCtx(nullptr)
    , m_swrCtx(nullptr)
    , m_state(RecordState::NotStarted)
    , m_vCurPts(0), m_aCurPts(0)
{
}
 
void ScreenRecordImpl::Init(const QVariantMap& map)
{
    m_filePath = map["filePath"].toString();
    m_width = map["width"].toInt();
    m_height = map["height"].toInt();
    m_fps = map["fps"].toInt();
    m_audioBitrate = map["audioBitrate"].toInt();
}
 
void ScreenRecordImpl::Start()
{
    if (m_state == RecordState::NotStarted)
    {
        qDebug() << "start record";
        m_state = RecordState::Started;
        std::thread muxThread(&ScreenRecordImpl::MuxThreadProc, this);
        muxThread.detach();
    }
    else if (m_state == RecordState::Paused)
    {
        qDebug() << "continue record";
        m_state = RecordState::Started;
        m_cvNotPause.notify_one();
    }
}
 
void ScreenRecordImpl::Pause()
{
    qDebug() << "pause record";
    m_state = RecordState::Paused;
}
 
void ScreenRecordImpl::Stop()
{
    qDebug() << "stop record";
    RecordState state = m_state;
    m_state = RecordState::Stopped;
    if (state == RecordState::Paused)
        m_cvNotPause.notify_one();
}
 
int ScreenRecordImpl::OpenVideo()
{
    int ret = -1;
    AVInputFormat *ifmt = av_find_input_format("gdigrab");
    AVDictionary *options = nullptr;
    AVCodec *decoder = nullptr;
    av_dict_set(&options, "framerate", QString::number(m_fps).toStdString().c_str(), NULL);
 
    if (avformat_open_input(&m_vFmtCtx, "desktop", ifmt, &options) != 0)
    {
        qDebug() << "Cant not open video input stream";
        return -1;
    }
    if (avformat_find_stream_info(m_vFmtCtx, nullptr) < 0)
    {
        printf("Couldn't find stream information.(无法获取视频流信息)\n");
        return -1;
    }
    for (int i = 0; i < m_vFmtCtx->nb_streams; ++i)
    {
        AVStream *stream = m_vFmtCtx->streams[i];
        if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            decoder = avcodec_find_decoder(stream->codecpar->codec_id);
            if (decoder == nullptr)
            {
                printf("Codec not found.(没有找到解码器)\n");
                return -1;
            }
            //从视频流中拷贝参数到codecCtx
            m_vDecodeCtx = avcodec_alloc_context3(decoder);
            if ((ret = avcodec_parameters_to_context(m_vDecodeCtx, stream->codecpar)) < 0)
            {
                qDebug() << "Video avcodec_parameters_to_context failed,error code: " << ret;
                return -1;
            }
            m_vIndex = i;
            break;
        }
    }
    if (avcodec_open2(m_vDecodeCtx, decoder, nullptr) < 0)
    {
        printf("Could not open codec.(无法打开解码器)\n");
        return -1;
    }
 
    m_swsCtx = sws_getContext(m_vDecodeCtx->width, m_vDecodeCtx->height, m_vDecodeCtx->pix_fmt,
        m_width, m_height, AV_PIX_FMT_YUV420P, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
    return 0;
}
 
static char *dup_wchar_to_utf8(wchar_t *w)
{
    char *s = NULL;
    int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0);
    s = (char *)av_malloc(l);
    if (s)
        WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, 0, 0);
    return s;
}
 
static int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt)
{
    const enum AVSampleFormat *p = codec->sample_fmts;
 
    while (*p != AV_SAMPLE_FMT_NONE) {
        if (*p == sample_fmt)
            return 1;
        p++;
    }
    return 0;
}
 
int ScreenRecordImpl::OpenAudio()
{
    int ret = -1;
    AVCodec *decoder = nullptr;
    qDebug() << GetMicrophoneDeviceName();
 
    AVInputFormat *ifmt = av_find_input_format("dshow");
    QString audioDeviceName = "audio=" + GetMicrophoneDeviceName();
 
    if (avformat_open_input(&m_aFmtCtx, audioDeviceName.toStdString().c_str(), ifmt, nullptr) < 0)
    {
        qDebug() << "Can not open audio input stream";
        return -1;
    }
    if (avformat_find_stream_info(m_aFmtCtx, nullptr) < 0)
        return -1;
 
    for (int i = 0; i < m_aFmtCtx->nb_streams; ++i)
    {
        AVStream * stream = m_aFmtCtx->streams[i];
        if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            decoder = avcodec_find_decoder(stream->codecpar->codec_id);
            if (decoder == nullptr)
            {
                printf("Codec not found.(没有找到解码器)\n");
                return -1;
            }
            //从视频流中拷贝参数到codecCtx
            m_aDecodeCtx = avcodec_alloc_context3(decoder);
            if ((ret = avcodec_parameters_to_context(m_aDecodeCtx, stream->codecpar)) < 0)
            {
                qDebug() << "Audio avcodec_parameters_to_context failed,error code: " << ret;
                return -1;
            }
            m_aIndex = i;
            break;
        }
    }
    if (0 > avcodec_open2(m_aDecodeCtx, decoder, NULL))
    {
        printf("can not find or open audio decoder!\n");
        return -1;
    }
    return 0;
}
 
int ScreenRecordImpl::OpenOutput()
{
    int ret = -1;
    AVStream *vStream = nullptr, *aStream = nullptr;
    const char *outFileName = "test.mp4";
    ret = avformat_alloc_output_context2(&m_oFmtCtx, nullptr, nullptr, outFileName);
    if (ret < 0)
    {
        qDebug() << "avformat_alloc_output_context2 failed";
        return -1;
    }
 
    if (m_vFmtCtx->streams[m_vIndex]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
    {
        vStream = avformat_new_stream(m_oFmtCtx, nullptr);
        if (!vStream)
        {
            printf("can not new stream for output!\n");
            return -1;
        }
        //AVFormatContext第一个创建的流索引是0,第二个创建的流索引是1
        m_vOutIndex = vStream->index;
        vStream->time_base = AVRational{ 1, m_fps };
 
        m_vEncodeCtx = avcodec_alloc_context3(NULL);
        if (nullptr == m_vEncodeCtx)
        {
            qDebug() << "avcodec_alloc_context3 failed";
            return -1;
        }
        m_vEncodeCtx->width = m_width;
        m_vEncodeCtx->height = m_height;
        m_vEncodeCtx->codec_type = AVMEDIA_TYPE_VIDEO;
        m_vEncodeCtx->time_base.num = 1;
        m_vEncodeCtx->time_base.den = m_fps;
        m_vEncodeCtx->pix_fmt = AV_PIX_FMT_YUV420P;
        m_vEncodeCtx->codec_id = AV_CODEC_ID_H264;
        m_vEncodeCtx->bit_rate = 800 * 1000;
        m_vEncodeCtx->rc_max_rate = 800 * 1000;
        m_vEncodeCtx->rc_buffer_size = 500 * 1000;
        //设置图像组层的大小, gop_size越大,文件越小 
        m_vEncodeCtx->gop_size = 30;
        m_vEncodeCtx->max_b_frames = 3;
         //设置h264中相关的参数,不设置avcodec_open2会失败
        m_vEncodeCtx->qmin = 10;    //2
        m_vEncodeCtx->qmax = 31;    //31
        m_vEncodeCtx->max_qdiff = 4;
        m_vEncodeCtx->me_range = 16;    //0    
        m_vEncodeCtx->max_qdiff = 4;    //3    
        m_vEncodeCtx->qcompress = 0.6;    //0.5
 
        //查找视频编码器
        AVCodec *encoder;
        encoder = avcodec_find_encoder(m_vEncodeCtx->codec_id);
        if (!encoder)
        {
            qDebug() << "Can not find the encoder, id: " << m_vEncodeCtx->codec_id;
            return -1;
        }
        m_vEncodeCtx->codec_tag = 0;
        //正确设置sps/pps
        m_vEncodeCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
        //打开视频编码器
        ret = avcodec_open2(m_vEncodeCtx, encoder, nullptr);
        if (ret < 0)
        {
            qDebug() << "Can not open encoder id: " << encoder->id << "error code: " << ret;
            return -1;
        }
        //将codecCtx中的参数传给输出流
        ret = avcodec_parameters_from_context(vStream->codecpar, m_vEncodeCtx);
        if (ret < 0)
        {
            qDebug() << "Output avcodec_parameters_from_context,error code:" << ret;
            return -1;
        }
    }
    if (m_aFmtCtx->streams[m_aIndex]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
    {
        aStream = avformat_new_stream(m_oFmtCtx, NULL);
        if (!aStream)
        {
            printf("can not new audio stream for output!\n");
            return -1;
        }
        m_aOutIndex = aStream->index;
 
        AVCodec *encoder = avcodec_find_encoder(m_oFmtCtx->oformat->audio_codec);
        if (!encoder)
        {
            qDebug() << "Can not find audio encoder, id: " << m_oFmtCtx->oformat->audio_codec;
            return -1;
        }
        m_aEncodeCtx = avcodec_alloc_context3(encoder);
        if (nullptr == m_vEncodeCtx)
        {
            qDebug() << "audio avcodec_alloc_context3 failed";
            return -1;
        }
        m_aEncodeCtx->sample_fmt = encoder->sample_fmts ? encoder->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
        m_aEncodeCtx->bit_rate = m_audioBitrate;
        m_aEncodeCtx->sample_rate = 44100;
        if (encoder->supported_samplerates) 
        {
            m_aEncodeCtx->sample_rate = encoder->supported_samplerates[0];
            for (int i = 0; encoder->supported_samplerates[i]; ++i)
            {
                if (encoder->supported_samplerates[i] == 44100)
                    m_aEncodeCtx->sample_rate = 44100;
            }
        }
        m_aEncodeCtx->channels = av_get_channel_layout_nb_channels(m_aEncodeCtx->channel_layout);
        m_aEncodeCtx->channel_layout = AV_CH_LAYOUT_STEREO;
        if (encoder->channel_layouts) 
        {
            m_aEncodeCtx->channel_layout = encoder->channel_layouts[0];
            for (int i = 0; encoder->channel_layouts[i]; ++i) 
            {
                if (encoder->channel_layouts[i] == AV_CH_LAYOUT_STEREO)
                    m_aEncodeCtx->channel_layout = AV_CH_LAYOUT_STEREO;
            }
        }
        m_aEncodeCtx->channels = av_get_channel_layout_nb_channels(m_aEncodeCtx->channel_layout);
        aStream->time_base = AVRational{ 1, m_aEncodeCtx->sample_rate };
 
        m_aEncodeCtx->codec_tag = 0;
        m_aEncodeCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
 
        if (!check_sample_fmt(encoder, m_aEncodeCtx->sample_fmt)) 
        {
            qDebug() << "Encoder does not support sample format " << av_get_sample_fmt_name(m_aEncodeCtx->sample_fmt);
            return -1;
        }
 
        //打开音频编码器,打开后frame_size被设置
        ret = avcodec_open2(m_aEncodeCtx, encoder, 0);
        if (ret < 0)
        {
            qDebug() << "Can not open the audio encoder, id: " << encoder->id << "error code: " << ret;
            return -1;
        }
        //将codecCtx中的参数传给音频输出流
        ret = avcodec_parameters_from_context(aStream->codecpar, m_aEncodeCtx);
        if (ret < 0)
        {
            qDebug() << "Output audio avcodec_parameters_from_context,error code:" << ret;
            return -1;
        }
 
        m_swrCtx = swr_alloc();
        if (!m_swrCtx)
        {
            qDebug() << "swr_alloc failed";
            return -1;
        }
        av_opt_set_int(m_swrCtx, "in_channel_count", m_aDecodeCtx->channels, 0);    //2
        av_opt_set_int(m_swrCtx, "in_sample_rate", m_aDecodeCtx->sample_rate, 0);    //44100
        av_opt_set_sample_fmt(m_swrCtx, "in_sample_fmt", m_aDecodeCtx->sample_fmt, 0);    //AV_SAMPLE_FMT_S16
        av_opt_set_int(m_swrCtx, "out_channel_count", m_aEncodeCtx->channels, 0);    //2
        av_opt_set_int(m_swrCtx, "out_sample_rate", m_aEncodeCtx->sample_rate, 0);    //44100
        av_opt_set_sample_fmt(m_swrCtx, "out_sample_fmt", m_aEncodeCtx->sample_fmt, 0);    //AV_SAMPLE_FMT_FLTP
 
        if ((ret = swr_init(m_swrCtx)) < 0) 
        {
            qDebug() << "swr_init failed";
            return -1;
        }
    }
 
    //打开输出文件
    if (!(m_oFmtCtx->oformat->flags & AVFMT_NOFILE))
    {
        if (avio_open(&m_oFmtCtx->pb, outFileName, AVIO_FLAG_WRITE) < 0)
        {
            printf("can not open output file handle!\n");
            return -1;
        }
    }
    //写文件头
    if (avformat_write_header(m_oFmtCtx, nullptr) < 0)
    {
        printf("can not write the header of the output file!\n");
        return -1;
    }
    return 0;
}
 
QString ScreenRecordImpl::GetSpeakerDeviceName()
{
    char sName[256] = { 0 };
    QString speaker = "";
    bool bRet = false;
    ::CoInitialize(NULL);
 
    ICreateDevEnum* pCreateDevEnum;//enumrate all speaker devices
    HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum,
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_ICreateDevEnum,
        (void**)&pCreateDevEnum);
 
    IEnumMoniker* pEm;
    hr = pCreateDevEnum->CreateClassEnumerator(CLSID_AudioRendererCategory, &pEm, 0);
    if (hr != NOERROR)
    {
        ::CoUninitialize();
        return "";
    }
 
    pEm->Reset();
    ULONG cFetched;
    IMoniker *pM;
    while (hr = pEm->Next(1, &pM, &cFetched), hr == S_OK)
    {
 
        IPropertyBag* pBag = NULL;
        hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pBag);
        if (SUCCEEDED(hr))
        {
            VARIANT var;
            var.vt = VT_BSTR;
            hr = pBag->Read(L"FriendlyName", &var, NULL);//还有其他属性,像描述信息等等
            if (hr == NOERROR)
            {
                //获取设备名称
                WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sName, 256, "", NULL);
                speaker = QString::fromLocal8Bit(sName);
                SysFreeString(var.bstrVal);
            }
            pBag->Release();
        }
        pM->Release();
        bRet = true;
    }
    pCreateDevEnum = NULL;
    pEm = NULL;
    ::CoUninitialize();
    return speaker;
}
 
QString ScreenRecordImpl::GetMicrophoneDeviceName()
{
    char sName[256] = { 0 };
    QString capture = "";
    bool bRet = false;
    ::CoInitialize(NULL);
 
    ICreateDevEnum* pCreateDevEnum;//enumrate all audio capture devices
    HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum,
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_ICreateDevEnum,
        (void**)&pCreateDevEnum);
 
    IEnumMoniker* pEm;
    hr = pCreateDevEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory, &pEm, 0);
    if (hr != NOERROR)
    {
        ::CoUninitialize();
        return "";
    }
 
    pEm->Reset();
    ULONG cFetched;
    IMoniker *pM;
    while (hr = pEm->Next(1, &pM, &cFetched), hr == S_OK)
    {
 
        IPropertyBag* pBag = NULL;
        hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pBag);
        if (SUCCEEDED(hr))
        {
            VARIANT var;
            var.vt = VT_BSTR;
            hr = pBag->Read(L"FriendlyName", &var, NULL);//还有其他属性,像描述信息等等
            if (hr == NOERROR)
            {
                //获取设备名称
                WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sName, 256, "", NULL);
                capture = QString::fromLocal8Bit(sName);
                SysFreeString(var.bstrVal);
            }
            pBag->Release();
        }
        pM->Release();
        bRet = true;
    }
    pCreateDevEnum = NULL;
    pEm = NULL;
    ::CoUninitialize();
    return capture;
}
 
AVFrame* ScreenRecordImpl::AllocAudioFrame(AVCodecContext* c, int nbSamples)
{
    AVFrame *frame = av_frame_alloc();
    int ret;
 
    frame->format = c->sample_fmt;
    frame->channel_layout = c->channel_layout ? c->channel_layout: AV_CH_LAYOUT_STEREO;
    frame->sample_rate = c->sample_rate;
    frame->nb_samples = nbSamples;
 
    if (nbSamples)
    {
        ret = av_frame_get_buffer(frame, 0);
        if (ret < 0) 
        {
            qDebug() << "av_frame_get_buffer failed";
            return nullptr;
        }
    }
    return frame;
}
 
void ScreenRecordImpl::InitVideoBuffer()
{
    m_vOutFrameSize = av_image_get_buffer_size(m_vEncodeCtx->pix_fmt, m_width, m_height, 1);
    m_vOutFrameBuf = (uint8_t *)av_malloc(m_vOutFrameSize);
    m_vOutFrame = av_frame_alloc();
    //先让AVFrame指针指向buf,后面再写入数据到buf
    av_image_fill_arrays(m_vOutFrame->data, m_vOutFrame->linesize, m_vOutFrameBuf, m_vEncodeCtx->pix_fmt, m_width, m_height, 1);
    //申请30帧缓存
    if (!(m_vFifoBuf = av_fifo_alloc_array(30, m_vOutFrameSize)))
    {
        qDebug() << "av_fifo_alloc_array failed";
        return;
    }
}
 
void ScreenRecordImpl::InitAudioBuffer()
{
    m_nbSamples = m_aEncodeCtx->frame_size;
    if (!m_nbSamples)
    {
        qDebug() << "m_nbSamples==0";
        m_nbSamples = 1024;
    }
    m_aFifoBuf = av_audio_fifo_alloc(m_aEncodeCtx->sample_fmt, m_aEncodeCtx->channels, 30 * m_nbSamples);
    if (!m_aFifoBuf)
    {
        qDebug() << "av_audio_fifo_alloc failed";
        return;
    }
}
 
void ScreenRecordImpl::FlushVideoDecoder()
{
    int ret = -1;
    int y_size = m_width * m_height;
    AVFrame    *oldFrame = av_frame_alloc();
    AVFrame *newFrame = av_frame_alloc();
 
    ret = avcodec_send_packet(m_vDecodeCtx, nullptr);
    if (ret != 0)
    {
        qDebug() << "flush video avcodec_send_packet failed, ret: " << ret;
        return;
    }
    while (ret >= 0)
    {
        ret = avcodec_receive_frame(m_vDecodeCtx, oldFrame);
        if (ret < 0)
        {
            if (ret == AVERROR(EAGAIN))
            {
                qDebug() << "flush EAGAIN avcodec_receive_frame";
                ret = 1;
                continue;
            }
            else if (ret == AVERROR_EOF)
            {
                qDebug() << "flush video decoder finished";
                break;
            }
            qDebug() << "flush video avcodec_receive_frame error, ret: " << ret;
            return;
        }
        ++g_vCollectFrameCnt;
        sws_scale(m_swsCtx, (const uint8_t* const*)oldFrame->data, oldFrame->linesize, 0,
            m_vEncodeCtx->height, newFrame->data, newFrame->linesize);
 
        {
            unique_lock<mutex> lk(m_mtxVBuf);
            m_cvVBufNotFull.wait(lk, [this] { return av_fifo_space(m_vFifoBuf) >= m_vOutFrameSize; });
        }
        av_fifo_generic_write(m_vFifoBuf, newFrame->data[0], y_size, NULL);
        av_fifo_generic_write(m_vFifoBuf, newFrame->data[1], y_size / 4, NULL);
        av_fifo_generic_write(m_vFifoBuf, newFrame->data[2], y_size / 4, NULL);
        m_cvVBufNotEmpty.notify_one();
    }
    qDebug() << "video collect frame count: " << g_vCollectFrameCnt;
}
 
//void ScreenRecordImpl::FlushVideoEncoder()
//{
//    int ret = -1;
//    AVPacket pkt = { 0 };
//    av_init_packet(&pkt);
//    ret = avcodec_send_frame(m_vEncodeCtx, nullptr);
//    qDebug() << "avcodec_send_frame ret:" << ret;
//    while (ret >= 0)
//    {
//        ret = avcodec_receive_packet(m_vEncodeCtx, &pkt);
//        if (ret < 0)
//        {
//            av_packet_unref(&pkt);
//            if (ret == AVERROR(EAGAIN))
//            {
//                qDebug() << "flush EAGAIN avcodec_receive_packet";
//                ret = 1;
//                continue;
//            }
//            else if (ret == AVERROR_EOF)
//            {
//                qDebug() << "flush video encoder finished";
//                break;
//            }
//            qDebug() << "flush video avcodec_receive_packet failed, ret: " << ret;
//            return;
//        }
//        pkt.stream_index = m_vOutIndex;
//        av_packet_rescale_ts(&pkt, m_vEncodeCtx->time_base, m_oFmtCtx->streams[m_vOutIndex]->time_base);
//
//        ret = av_interleaved_write_frame(m_oFmtCtx, &pkt);
//        if (ret == 0)
//            qDebug() << "flush Write video packet id: " << ++g_vEncodeFrameCnt;
//        else
//            qDebug() << "video av_interleaved_write_frame failed, ret:" << ret;
//        av_free_packet(&pkt);
//    }
//}
 
void ScreenRecordImpl::FlushAudioDecoder()
{
    int ret = -1;
    AVPacket pkt = { 0 };
    av_init_packet(&pkt);
    int dstNbSamples, maxDstNbSamples;
    AVFrame *rawFrame = av_frame_alloc();
    AVFrame *newFrame = AllocAudioFrame(m_aEncodeCtx, m_nbSamples);
    maxDstNbSamples = dstNbSamples = av_rescale_rnd(m_nbSamples,
        m_aEncodeCtx->sample_rate, m_aDecodeCtx->sample_rate, AV_ROUND_UP);
 
    ret = avcodec_send_packet(m_aDecodeCtx, nullptr);
    if (ret != 0)
    {
        qDebug() << "flush audio avcodec_send_packet  failed, ret: " << ret;
        return;
    }
    while (ret >= 0)
    {
        ret = avcodec_receive_frame(m_aDecodeCtx, rawFrame);
        if (ret < 0)
        {
            if (ret == AVERROR(EAGAIN))
            {
                qDebug() << "flush audio EAGAIN avcodec_receive_frame";
                ret = 1;
                continue;
            }
            else if (ret == AVERROR_EOF)
            {
                qDebug() << "flush audio decoder finished";
                break;
            }
            qDebug() << "flush audio avcodec_receive_frame error, ret: " << ret;
            return;
        }
        ++g_aCollectFrameCnt;
 
        dstNbSamples = av_rescale_rnd(swr_get_delay(m_swrCtx, m_aDecodeCtx->sample_rate) + rawFrame->nb_samples,
            m_aEncodeCtx->sample_rate, m_aDecodeCtx->sample_rate, AV_ROUND_UP);
        if (dstNbSamples > maxDstNbSamples)
        {
            qDebug() << "flush audio newFrame realloc";
            av_freep(&newFrame->data[0]);
            ret = av_samples_alloc(newFrame->data, newFrame->linesize, m_aEncodeCtx->channels,
                dstNbSamples, m_aEncodeCtx->sample_fmt, 1);
            if (ret < 0)
            {
                qDebug() << "flush av_samples_alloc failed";
                return;
            }
            maxDstNbSamples = dstNbSamples;
            m_aEncodeCtx->frame_size = dstNbSamples;
            m_nbSamples = newFrame->nb_samples;
        }
        newFrame->nb_samples = swr_convert(m_swrCtx, newFrame->data, dstNbSamples,
            (const uint8_t **)rawFrame->data, rawFrame->nb_samples);
        if (newFrame->nb_samples < 0)
        {
            qDebug() << "flush swr_convert failed";
            return;
        }
 
        {
            unique_lock<mutex> lk(m_mtxABuf);
            m_cvABufNotFull.wait(lk, [newFrame, this] { return av_audio_fifo_space(m_aFifoBuf) >= newFrame->nb_samples; });
        }
        if (av_audio_fifo_write(m_aFifoBuf, (void **)newFrame->data, newFrame->nb_samples) < newFrame->nb_samples)
        {
            qDebug() << "av_audio_fifo_write";
            return;
        }
        m_cvABufNotEmpty.notify_one();
    }
    qDebug() << "audio collect frame count: " << g_aCollectFrameCnt;
}
 
//void ScreenRecordImpl::FlushAudioEncoder()
//{
//}
 
void ScreenRecordImpl::FlushEncoders()
{
    int ret = -1;
    bool vBeginFlush = false;
    bool aBeginFlush = false;
 
    m_vCurPts = m_aCurPts = 0;
 
    int nFlush = 2;
 
    while (1)
    {
        AVPacket pkt = { 0 };
        av_init_packet(&pkt);
        if (av_compare_ts(m_vCurPts, m_oFmtCtx->streams[m_vOutIndex]->time_base,
            m_aCurPts, m_oFmtCtx->streams[m_aOutIndex]->time_base) <= 0)
        {
            if (!vBeginFlush)
            {
                vBeginFlush = true;
                ret = avcodec_send_frame(m_vEncodeCtx, nullptr);
                if (ret != 0)
                {
                    qDebug() << "flush video avcodec_send_frame failed, ret: " << ret;
                    return;
                }
            }
            ret = avcodec_receive_packet(m_vEncodeCtx, &pkt);
            if (ret < 0)
            {
                av_packet_unref(&pkt);
                if (ret == AVERROR(EAGAIN))
                {
                    qDebug() << "flush video EAGAIN avcodec_receive_packet";
                    ret = 1;
                    continue;
                }
                else if (ret == AVERROR_EOF)
                {
                    qDebug() << "flush video encoder finished";
                    //break;
                    if (!(--nFlush))
                        break;
                    m_vCurPts = INT_MAX;
                    continue;
                }
                qDebug() << "flush video avcodec_receive_packet failed, ret: " << ret;
                return;
            }
            pkt.stream_index = m_vOutIndex;
            //将pts从编码层的timebase转成复用层的timebase
            av_packet_rescale_ts(&pkt, m_vEncodeCtx->time_base, m_oFmtCtx->streams[m_vOutIndex]->time_base);
            m_vCurPts = pkt.pts;
            qDebug() << "m_vCurPts: " << m_vCurPts;
 
            ret = av_interleaved_write_frame(m_oFmtCtx, &pkt);
            if (ret == 0)
                qDebug() << "flush Write video packet id: " << ++g_vEncodeFrameCnt;
            else
                qDebug() << "flush video av_interleaved_write_frame failed, ret:" << ret;
            av_free_packet(&pkt);
        }
        else
        {
            if (!aBeginFlush)
            {
                aBeginFlush = true;
                ret = avcodec_send_frame(m_aEncodeCtx, nullptr);
                if (ret != 0)
                {
                    qDebug() << "flush audio avcodec_send_frame failed, ret: " << ret;
                    return;
                }
            }
            ret = avcodec_receive_packet(m_aEncodeCtx, &pkt);
            if (ret < 0)
            {
                av_packet_unref(&pkt);
                if (ret == AVERROR(EAGAIN))
                {
                    qDebug() << "flush EAGAIN avcodec_receive_packet";
                    ret = 1;
                    continue;
                }
                else if (ret == AVERROR_EOF)
                {
                    qDebug() << "flush audio encoder finished";
                    /*break;*/
                    if (!(--nFlush))
                        break;
                    m_aCurPts = INT_MAX;
                    continue;
                }
                qDebug() << "flush audio avcodec_receive_packet failed, ret: " << ret;
                return;
            }
            pkt.stream_index = m_aOutIndex;
            //将pts从编码层的timebase转成复用层的timebase
            av_packet_rescale_ts(&pkt, m_aEncodeCtx->time_base, m_oFmtCtx->streams[m_aOutIndex]->time_base);
            m_aCurPts = pkt.pts;
            qDebug() << "m_aCurPts: " << m_aCurPts;
            ret = av_interleaved_write_frame(m_oFmtCtx, &pkt);
            if (ret == 0)
                qDebug() << "flush write audio packet id: " << ++g_aEncodeFrameCnt;
            else
                qDebug() << "flush audio av_interleaved_write_frame failed, ret: " << ret;
            av_free_packet(&pkt);
        }
    }
}
 
void ScreenRecordImpl::Release()
{
    if (m_vOutFrame)
    {
        av_frame_free(&m_vOutFrame);
        m_vOutFrame = nullptr;
    }
    if (m_vOutFrameBuf)
    {
        av_free(m_vOutFrameBuf);
        m_vOutFrameBuf = nullptr;
    }
    if (m_oFmtCtx)
    {
        avio_close(m_oFmtCtx->pb);
        avformat_free_context(m_oFmtCtx);
        m_oFmtCtx = nullptr;
    }
    if (m_vDecodeCtx)
    {
        avcodec_free_context(&m_vDecodeCtx);
        m_vDecodeCtx = nullptr;
    }
    if (m_aDecodeCtx)
    {
        avcodec_free_context(&m_aDecodeCtx);
        m_aDecodeCtx = nullptr;
    }
    if (m_vEncodeCtx)
    {
        avcodec_free_context(&m_vEncodeCtx);
        m_vEncodeCtx = nullptr;
    }
    if (m_aEncodeCtx)
    {
        avcodec_free_context(&m_aEncodeCtx);
        m_aEncodeCtx = nullptr;
    }
    if (m_vFifoBuf)
    {
        av_fifo_freep(&m_vFifoBuf);
        m_vFifoBuf = nullptr;
    }
    if (m_aFifoBuf)
    {
        av_audio_fifo_free(m_aFifoBuf);
        m_aFifoBuf = nullptr;
    }
    if (m_vFmtCtx)
    {
        avformat_close_input(&m_vFmtCtx);
        m_vFmtCtx = nullptr;
    }
    if (m_aFmtCtx)
    {
        avformat_close_input(&m_aFmtCtx);
        m_aFmtCtx = nullptr;
    }
}
 
void ScreenRecordImpl::MuxThreadProc()
{
    int ret = -1;
    bool done = false;
    int vFrameIndex = 0, aFrameIndex = 0;
 
    av_register_all();
    avdevice_register_all();
    avcodec_register_all();
 
    if (OpenVideo() < 0)
        return;
    if (OpenAudio() < 0)
        return;
    if (OpenOutput() < 0)
        return;
 
    InitVideoBuffer();
    InitAudioBuffer();
 
    //启动音视频数据采集线程
    std::thread screenRecord(&ScreenRecordImpl::ScreenRecordThreadProc, this);
    std::thread soundRecord(&ScreenRecordImpl::SoundRecordThreadProc, this);
    screenRecord.detach();
    soundRecord.detach();
 
    while (1)
    {
        if (m_state == RecordState::Stopped && !done)
            done = true;
        if (done)
        {
            unique_lock<mutex> vBufLock(m_mtxVBuf, std::defer_lock);
            unique_lock<mutex> aBufLock(m_mtxABuf, std::defer_lock);
            std::lock(vBufLock, aBufLock);
            if (av_fifo_size(m_vFifoBuf) < m_vOutFrameSize &&
                av_audio_fifo_size(m_aFifoBuf) < m_nbSamples)
            {
                qDebug() << "both video and audio fifo buf are empty, break";
                break;
            }
        }
        if (av_compare_ts(m_vCurPts, m_oFmtCtx->streams[m_vOutIndex]->time_base,
            m_aCurPts, m_oFmtCtx->streams[m_aOutIndex]->time_base) <= 0)
    /*    if (av_compare_ts(vCurPts, m_vEncodeCtx->time_base,
            aCurPts, m_aEncodeCtx->time_base) <= 0)*/
        {
            if (done)
            {
                lock_guard<mutex> lk(m_mtxVBuf);
                if (av_fifo_size(m_vFifoBuf) < m_vOutFrameSize)
                {
                    qDebug() << "video wirte done";
                    //break;
                    //m_vCurPts = 0x7ffffffffffffffe;    //int64_t最大有符号整数
                    m_vCurPts = INT_MAX;
                    continue;
                }
            }
            else 
            {
                unique_lock<mutex> lk(m_mtxVBuf);
                m_cvVBufNotEmpty.wait(lk, [this] { return av_fifo_size(m_vFifoBuf) >= m_vOutFrameSize; });
            }
            av_fifo_generic_read(m_vFifoBuf, m_vOutFrameBuf, m_vOutFrameSize, NULL);
            m_cvVBufNotFull.notify_one();
 
            //设置视频帧参数
            //m_vOutFrame->pts = vFrameIndex * ((m_oFmtCtx->streams[m_vOutIndex]->time_base.den / m_oFmtCtx->streams[m_vOutIndex]->time_base.num) / m_fps);
            m_vOutFrame->pts = vFrameIndex++;
            m_vOutFrame->format = m_vEncodeCtx->pix_fmt;
            m_vOutFrame->width = m_vEncodeCtx->width;
            m_vOutFrame->height = m_vEncodeCtx->height;
 
            AVPacket pkt = { 0 };
            av_init_packet(&pkt);
            ret = avcodec_send_frame(m_vEncodeCtx, m_vOutFrame);
            if (ret != 0)
            {
                qDebug() << "video avcodec_send_frame failed, ret: " << ret;
                av_packet_unref(&pkt);
                continue;
            }
            ret = avcodec_receive_packet(m_vEncodeCtx, &pkt);
            if (ret != 0)
            {
                qDebug() << "video avcodec_receive_packet failed, ret: " << ret;
                av_packet_unref(&pkt);
                continue;
            }
            pkt.stream_index = m_vOutIndex;
            //将pts从编码层的timebase转成复用层的timebase
            av_packet_rescale_ts(&pkt, m_vEncodeCtx->time_base, m_oFmtCtx->streams[m_vOutIndex]->time_base);
 
            m_vCurPts = pkt.pts;
            qDebug() << "m_vCurPts: " << m_vCurPts;
 
            ret = av_interleaved_write_frame(m_oFmtCtx, &pkt);
            if (ret == 0)
                qDebug() << "Write video packet id: " << ++g_vEncodeFrameCnt;
            else
                qDebug() << "video av_interleaved_write_frame failed, ret:" << ret;
            av_free_packet(&pkt);
        }
        else
        {
            if (done)
            {
                lock_guard<mutex> lk(m_mtxABuf);
                if (av_audio_fifo_size(m_aFifoBuf) < m_nbSamples)
                {
                    qDebug() << "audio write done";
                    //m_aCurPts = 0x7fffffffffffffff;
                    m_aCurPts = INT_MAX;
                    continue;
                }
            }
            else
            {
                unique_lock<mutex> lk(m_mtxABuf);
                m_cvABufNotEmpty.wait(lk, [this] { return av_audio_fifo_size(m_aFifoBuf) >= m_nbSamples; });
            }
 
            int ret = -1;
            AVFrame *aFrame = av_frame_alloc();
            aFrame->nb_samples = m_nbSamples;
            aFrame->channel_layout = m_aEncodeCtx->channel_layout;
            aFrame->format = m_aEncodeCtx->sample_fmt;
            aFrame->sample_rate = m_aEncodeCtx->sample_rate;
            aFrame->pts = m_nbSamples * aFrameIndex++;
            //分配data buf
            ret = av_frame_get_buffer(aFrame, 0);
            av_audio_fifo_read(m_aFifoBuf, (void **)aFrame->data, m_nbSamples);
            m_cvABufNotFull.notify_one();
 
            AVPacket pkt = { 0 };
            av_init_packet(&pkt);
            ret = avcodec_send_frame(m_aEncodeCtx, aFrame);
            if (ret != 0)
            {
                qDebug() << "audio avcodec_send_frame failed, ret: " << ret;
                av_frame_free(&aFrame);
                av_packet_unref(&pkt);
                continue;
            }
            ret = avcodec_receive_packet(m_aEncodeCtx, &pkt);
            if (ret != 0)
            {
                qDebug() << "audio avcodec_receive_packet failed, ret: " << ret;
                av_frame_free(&aFrame);
                av_packet_unref(&pkt);
                continue;
            }
            pkt.stream_index = m_aOutIndex;
 
            av_packet_rescale_ts(&pkt, m_aEncodeCtx->time_base, m_oFmtCtx->streams[m_aOutIndex]->time_base);
 
            m_aCurPts = pkt.pts;
            qDebug() << "aCurPts: " << m_aCurPts;
 
            ret = av_interleaved_write_frame(m_oFmtCtx, &pkt);
            if (ret == 0)
                qDebug() << "Write audio packet id: " << ++g_aEncodeFrameCnt;
            else
                qDebug() << "audio av_interleaved_write_frame failed, ret: " << ret;
 
            av_frame_free(&aFrame);
            av_free_packet(&pkt);
        }
    }
    FlushEncoders();
    av_write_trailer(m_oFmtCtx);
    Release();
    qDebug() << "parent thread exit";
}
 
void ScreenRecordImpl::ScreenRecordThreadProc()
{
    int ret = -1;
    AVPacket pkt = { 0 };
    av_init_packet(&pkt);
    int y_size = m_width * m_height;
    AVFrame    *oldFrame = av_frame_alloc();
    AVFrame *newFrame = av_frame_alloc();
 
    int newFrameBufSize = av_image_get_buffer_size(m_vEncodeCtx->pix_fmt, m_width, m_height, 1);
    uint8_t *newFrameBuf = (uint8_t*)av_malloc(newFrameBufSize);
    av_image_fill_arrays(newFrame->data, newFrame->linesize, newFrameBuf,
        m_vEncodeCtx->pix_fmt, m_width, m_height, 1);
 
    while (m_state != RecordState::Stopped)
    {
        if (m_state == RecordState::Paused)
        {
            unique_lock<mutex> lk(m_mtxPause);
            m_cvNotPause.wait(lk, [this] { return m_state != RecordState::Paused; });
        }
        if (av_read_frame(m_vFmtCtx, &pkt) < 0)
        {
            qDebug() << "video av_read_frame < 0";
            continue;
        }
        if (pkt.stream_index != m_vIndex)
        {
            qDebug() << "not a video packet from video input";
            av_packet_unref(&pkt);
        }
        ret = avcodec_send_packet(m_vDecodeCtx, &pkt);
        if (ret != 0)
        {
            qDebug() << "video avcodec_send_packet failed, ret:" << ret;
            av_packet_unref(&pkt);
            continue;
        }
        ret = avcodec_receive_frame(m_vDecodeCtx, oldFrame);
        if (ret != 0)
        {
            qDebug() << "video avcodec_receive_frame failed, ret:" << ret;
            av_packet_unref(&pkt);
            continue;
        }
        ++g_vCollectFrameCnt;
        sws_scale(m_swsCtx, (const uint8_t* const*)oldFrame->data, oldFrame->linesize, 0,
            m_vEncodeCtx->height, newFrame->data, newFrame->linesize);
 
        {
            unique_lock<mutex> lk(m_mtxVBuf);
            m_cvVBufNotFull.wait(lk, [this] { return av_fifo_space(m_vFifoBuf) >= m_vOutFrameSize; });
        }
        av_fifo_generic_write(m_vFifoBuf, newFrame->data[0], y_size, NULL);
        av_fifo_generic_write(m_vFifoBuf, newFrame->data[1], y_size / 4, NULL);
        av_fifo_generic_write(m_vFifoBuf, newFrame->data[2], y_size / 4, NULL);
        m_cvVBufNotEmpty.notify_one();
 
        av_packet_unref(&pkt);
    }
    FlushVideoDecoder();
 
    av_free(newFrameBuf);
    av_frame_free(&oldFrame);
    av_frame_free(&newFrame);
    qDebug() << "screen record thread exit";
}
 
void ScreenRecordImpl::SoundRecordThreadProc()
{
    int ret = -1;
    AVPacket pkt = { 0 };
    av_init_packet(&pkt);
    int nbSamples = m_nbSamples;
    int dstNbSamples, maxDstNbSamples;
    AVFrame *rawFrame = av_frame_alloc();
    AVFrame *newFrame = AllocAudioFrame(m_aEncodeCtx, nbSamples);
 
    maxDstNbSamples = dstNbSamples = av_rescale_rnd(nbSamples, 
        m_aEncodeCtx->sample_rate, m_aDecodeCtx->sample_rate, AV_ROUND_UP);
 
    while (m_state != RecordState::Stopped)
    {
        if (m_state == RecordState::Paused)
        {
            unique_lock<mutex> lk(m_mtxPause);
            m_cvNotPause.wait(lk, [this] { return m_state != RecordState::Paused; });
        }
        if (av_read_frame(m_aFmtCtx, &pkt) < 0)
        {
            qDebug() << "audio av_read_frame < 0";
            continue;
        }
        if (pkt.stream_index != m_aIndex)
        {
            qDebug() << "not a audio packet";
            av_packet_unref(&pkt);
            continue;
        }
        ret = avcodec_send_packet(m_aDecodeCtx, &pkt);
        if (ret != 0)
        {
            qDebug() << "audio avcodec_send_packet failed, ret: " << ret;
            av_packet_unref(&pkt);
            continue;
        }
        ret = avcodec_receive_frame(m_aDecodeCtx, rawFrame);
        if (ret != 0)
        {
            qDebug() << "audio avcodec_receive_frame failed, ret: " << ret;
            av_packet_unref(&pkt);
            continue;
        }
        ++g_aCollectFrameCnt;
 
        dstNbSamples = av_rescale_rnd(swr_get_delay(m_swrCtx, m_aDecodeCtx->sample_rate) + rawFrame->nb_samples,
            m_aEncodeCtx->sample_rate, m_aDecodeCtx->sample_rate, AV_ROUND_UP);
        if (dstNbSamples > maxDstNbSamples) 
        {
            qDebug() << "audio newFrame realloc";
            av_freep(&newFrame->data[0]);
            //nb_samples*nb_channels*Bytes_sample_fmt
            ret = av_samples_alloc(newFrame->data, newFrame->linesize, m_aEncodeCtx->channels,
                dstNbSamples, m_aEncodeCtx->sample_fmt, 1);
            if (ret < 0)
            {
                qDebug() << "av_samples_alloc failed";
                return;
            }
 
            maxDstNbSamples = dstNbSamples;
            m_aEncodeCtx->frame_size = dstNbSamples;
            m_nbSamples = newFrame->nb_samples;    //1024
            /*
             * m_nbSamples = dstNbSamples;        //22050
             * 如果改为m_nbSamples = dstNbSamples;则av_audio_fifo_write会异常,不明白为什么?
             * 我觉得应该改为22050,不然编码线程一次编码的帧sample太少了,
             * 但是用1024生成的音频好像没问题?
             * 音频是否应该根据采集的nb_samples而重新分配fifo?
            */
        }
 
        newFrame->nb_samples = swr_convert(m_swrCtx, newFrame->data, dstNbSamples,
            (const uint8_t **)rawFrame->data, rawFrame->nb_samples);
        if (newFrame->nb_samples < 0)
        {
            qDebug() << "swr_convert error";
            return;
        }
        {
            unique_lock<mutex> lk(m_mtxABuf);
            m_cvABufNotFull.wait(lk, [newFrame, this] { return av_audio_fifo_space(m_aFifoBuf) >= newFrame->nb_samples; });
        }
        if (av_audio_fifo_write(m_aFifoBuf, (void **)newFrame->data, newFrame->nb_samples) < newFrame->nb_samples)
        {
            qDebug() << "av_audio_fifo_write";
            return;
        }
        m_cvABufNotEmpty.notify_one();
    }
    FlushAudioDecoder();
    av_frame_free(&rawFrame);
    av_frame_free(&newFrame);
    qDebug() << "sound record thread exit";
}
 

ScreenRecordTest.h

#pragma once
#include <QObject>
#include <QVariant>
 
class ScreenRecord : public QObject
{
    Q_OBJECT
public:
    ScreenRecord(QObject *parent = Q_NULLPTR);
 
private:
    QVariantMap m_args;
};
 

ScreenRecordTest.cpp

#include "ScreenRecordTest.h"
#include "ScreenRecordImpl.h"
#include <QTimer>
 
ScreenRecord::ScreenRecord(QObject *parent) :
    QObject(parent)
{
    ScreenRecordImpl *sr = new ScreenRecordImpl(this);
    QVariantMap args;
    args["filePath"] = "test.mp4";
    //args["width"] = 1920;
    //args["height"] = 1080;
    args["width"] = 1440;
    args["height"] = 900;
    args["fps"] = 30;
    args["audioBitrate"] = 128000;
 
    sr->Init(args);
 
    QTimer::singleShot(1000, sr, SLOT(Start()));
    //QTimer::singleShot(5000, sr, SLOT(Pause()));
    QTimer::singleShot(11000, sr, SLOT(Stop()));
}
 

main.cpp

#include <QApplication>
#include "ScreenRecordImpl.h"
#include "ScreenRecordTest.h"
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
 
    ScreenRecord sr;
 
    return a.exec();
}
 

 

猜你喜欢

转载自blog.csdn.net/m0_37346206/article/details/89318420
今日推荐