整体框架:
屏幕录制、声音录制、音视频合成分别在不同的子线程中运行,由主程序控制录制的开始和结束。控制流程如下图所示:
主线程:点击开始按钮à打开音频设备、视频设备、输出文件、启动子线程à发送开始录制信号;
子线程:收到开始信号à开始录制;
主线程:点击结束按钮à发送结束信号;
子线程:接收到结束信号à发送结束信号;
主线程:已经接收到了所有子线程发来的结束信号à处理结束事项并清理资源。
主要代码:
主线程:screenrecord.h
#ifndef SCREENRECORD_H #define SCREENRECORD_H #include#include #include #include #include #include #include #include #include #include #include #include #include #include #include "myglobals.h" #include "muxerprocess.h" #include "screencaptureprocess.h" #include "audiocaptureprocess.h" class ScreenRecord : public QWidget { Q_OBJECT public: explicit ScreenRecord(QWidget *parent = nullptr); ~ScreenRecord(); public: QMutex mutex; bool screenCapturing; bool audioCapturing; bool muxing; QThread muxerThread; MuxerProcess *muxerProcess; QThread screenCaptureThread; ScreenCaptureProcess *screenCaptureProcess; QThread audioCaptureThread; AudioCaptureProcess *audioCaptureProcess; QPushButton *startButton; int outputWidth; int outputHeight; QString outputDirectory; QString outputFilename; QString outputFileType; //视频输出路径 QLabel *outputDirLabel; QLineEdit *outputDirEdit; QPushButton *outputDirButton; public slots: void replyButtonPressed(); void replyScreenCapturingFinished(); void replyAudioCapturingFinished(); void replyMuxingFinished(); signals: void startRecord(); //开始录屏信号 void stopRecord(); //结束录屏信号 public: int openScreenCaptureDevice(); int openAudioCaptureDevice(); int openOutput(); QString getSpeakerDeviceName(); void releaseGlobals(); void stopChildThreads(); void startChildThreads(); protected: void paintEvent(QPaintEvent *event); }; #endif // SCREENRECORD_H
主线程:screenrecord.cpp
#include "screencaptureprocess.h"
ScreenCaptureProcess::ScreenCaptureProcess(QObject *parent)
: QObject{parent}
{
capturing = false;
videoFrame = nullptr;
videoYUVFrame = nullptr;
videoYUVBuffer = nullptr;
}
void ScreenCaptureProcess::start()
{
qDebug()<<"录屏子线程开始";
capturing = true;
captureScreen();
}
void ScreenCaptureProcess::stop()
{
qDebug()<<"ScreenCapture收到停止信号";
capturing = false;
}
void ScreenCaptureProcess::release()
{
if (videoFrame != nullptr)
av_frame_free(&videoFrame);
if (videoYUVFrame != nullptr)
av_frame_free(&videoYUVFrame);
if (videoYUVBuffer != nullptr)
av_freep(&videoYUVBuffer);
}
void ScreenCaptureProcess::captureScreen()
{
int ret = 0;
int number = 0;
int _lineSize = 0;
if (videoFrame == nullptr)
videoFrame = av_frame_alloc();
if (videoYUVFrame == nullptr)
videoYUVFrame = av_frame_alloc();
if (videoYUVBuffer == nullptr)
videoYUVBuffer = (uint8_t *)av_malloc(videoFrameSize);
//帧尺寸输出直接使用输入的尺寸
av_image_fill_arrays(videoYUVFrame->data,videoYUVFrame->linesize,videoYUVBuffer,AV_PIX_FMT_YUV420P, sourceWidth, sourceHeight,1);
_lineSize = sourceWidth * sourceHeight;
AVPacket packet,outPacket;
av_init_packet(&packet);
av_init_packet(&outPacket);
while (1){
if (!capturing){
if (recording)
emit(capturingfinished());
break;
}
av_packet_unref(&packet);
av_packet_unref(&outPacket);
if (av_read_frame(videoSourceFormatContext,&packet) < 0)
continue;
ret = avcodec_send_packet(videoSourceCodecContext,&packet);
if (ret >= 0){
ret = avcodec_receive_frame(videoSourceCodecContext,videoFrame);
if (ret == EAGAIN)
continue;
else if (ret == AVERROR_EOF)
break;
else if (ret < 0)
qDebug()<<"Error during decoding";
}
sws_scale(videoSwsContext, (const uint8_t* const*)videoFrame->data, videoFrame->linesize, 0, sourceHeight,videoYUVFrame->data,videoYUVFrame->linesize);
mutex.lock();
if (av_fifo_space(videoFifoBuffer) > videoFrameSize){
av_fifo_generic_write(videoFifoBuffer, videoYUVFrame->data[0], _lineSize, NULL);
av_fifo_generic_write(videoFifoBuffer, videoYUVFrame->data[1], _lineSize / 4, NULL);
av_fifo_generic_write(videoFifoBuffer, videoYUVFrame->data[2], _lineSize / 4, NULL);
}
mutex.unlock();
number ++;
QCoreApplication::processEvents();
}
}
声音录制函数:void captureAudio();
void AudioCaptureProcess::captureAudio()
{
int ret = -1;
//编码器的frame size
int _sampleNumber = audioEncodeFrameSize;
if (_sampleNumber == 0)
_sampleNumber = 1024;
int dstNbSamples, maxDstNbSamples;
maxDstNbSamples = dstNbSamples = av_rescale_rnd(_sampleNumber,
audioEncodeCodecContext->sample_rate,
audioSourceCodecContext->sample_rate,
AV_ROUND_UP);
AVPacket packet;
av_init_packet(&packet);
AVFrame *_rawFrame = av_frame_alloc();
AVFrame *_newFrame = AllocAudioFrame(audioEncodeCodecContext, _sampleNumber);
while (1){
if (av_read_frame(audioSourceFormatContext,&packet) < 0){
av_packet_unref(&packet);
qDebug()<<"不能读取音频信号:av_read_frame";
continue;
}
if (packet.stream_index != 0){
av_packet_unref(&packet);
qDebug()<<"读取的音频信号不是正确的:av_read_frame";
continue;
}
ret = avcodec_send_packet(audioSourceCodecContext,&packet);
if (ret != 0){
av_packet_unref(&packet);
continue;
}
ret = avcodec_receive_frame(audioSourceCodecContext,_rawFrame);
if (ret != 0){
av_packet_unref(&packet);
continue;
}
dstNbSamples = av_rescale_rnd(swr_get_delay(audioSwrContext, audioSourceCodecContext->sample_rate) + _rawFrame->nb_samples,
audioEncodeCodecContext->sample_rate, audioSourceCodecContext->sample_rate, AV_ROUND_UP);
if (dstNbSamples > maxDstNbSamples)
{
av_freep(&_newFrame->data[0]);
//nb_samples*nb_channels*Bytes_sample_fmt
ret = av_samples_alloc(_newFrame->data, _newFrame->linesize, audioEncodeCodecContext->channels,
dstNbSamples, audioEncodeCodecContext->sample_fmt, 1);
if (ret < 0){
qDebug() << "av_samples_alloc failed";
return;
}
maxDstNbSamples = dstNbSamples;
audioEncodeCodecContext->frame_size = dstNbSamples;
audioEncodeFrameSize = _newFrame->nb_samples;
}
//为音频分配内存(音频还未转换)
if (audioFifoBuffer == nullptr)
audioFifoBuffer = av_audio_fifo_alloc(audioEncodeCodecContext->sample_fmt,
audioEncodeCodecContext->channels,
30 * _sampleNumber);
_newFrame->nb_samples = swr_convert(audioSwrContext, _newFrame->data, dstNbSamples,
(const uint8_t **)_rawFrame->data,
_rawFrame->nb_samples);
if (_newFrame->nb_samples < 0){
qDebug() << "audio swr_convert failed";
return;
}
int _space = av_audio_fifo_space(audioFifoBuffer);
if (_space >= _newFrame->nb_samples){
ret = av_audio_fifo_write(audioFifoBuffer, (void **)_newFrame->data, _newFrame->nb_samples);
if (ret < _newFrame->nb_samples){
qDebug() << "av_audio_fifo_write error!";
return;
}
}
av_packet_unref(&packet);
QCoreApplication::processEvents();
}
av_frame_free(&_rawFrame);
av_frame_free(&_newFrame);
}
屏幕抓取函数:void captureScreen();
void captureScreen()
{
int ret = 0;
int number = 0;
int _lineSize = 0;
if (videoFrame == nullptr)
videoFrame = av_frame_alloc();
if (videoYUVFrame == nullptr)
videoYUVFrame = av_frame_alloc();
if (videoYUVBuffer == nullptr)
videoYUVBuffer = (uint8_t *)av_malloc(videoFrameSize);
//帧尺寸输出直接使用输入的尺寸
av_image_fill_arrays(videoYUVFrame->data,videoYUVFrame->linesize,videoYUVBuffer,AV_PIX_FMT_YUV420P, sourceWidth, sourceHeight,1);
_lineSize = sourceWidth * sourceHeight;
AVPacket packet,outPacket;
av_init_packet(&packet);
av_init_packet(&outPacket);
while (1){
av_packet_unref(&packet);
av_packet_unref(&outPacket);
if (av_read_frame(videoSourceFormatContext,&packet) < 0)
continue;
ret = avcodec_send_packet(videoSourceCodecContext,&packet);
if (ret >= 0){
ret = avcodec_receive_frame(videoSourceCodecContext,videoFrame);
if (ret == EAGAIN)
continue;
else if (ret == AVERROR_EOF)
break;
else if (ret < 0)
qDebug()<<"Error during decoding";
}
sws_scale(videoSwsContext, (const uint8_t* const*)videoFrame->data, videoFrame->linesize, 0, sourceHeight,videoYUVFrame->data,videoYUVFrame->linesize);
if (av_fifo_space(videoFifoBuffer) > videoFrameSize){
av_fifo_generic_write(videoFifoBuffer, videoYUVFrame->data[0], _lineSize, NULL);
av_fifo_generic_write(videoFifoBuffer, videoYUVFrame->data[1], _lineSize / 4, NULL);
av_fifo_generic_write(videoFifoBuffer, videoYUVFrame->data[2], _lineSize / 4, NULL);
}
number ++;
}
}
音视频合并函数:void mux();
void MuxerProcess::mux()
{
int _cts = -1;
AVFrame *_yuvFrame = av_frame_alloc();
uint8_t *_yuvBuffer = (uint8_t *)av_malloc(videoFrameSize);
av_image_fill_arrays(_yuvFrame->data, _yuvFrame->linesize, _yuvBuffer, AV_PIX_FMT_YUV420P, sourceWidth, sourceHeight, 1);
AVPacket packet;
av_init_packet(&packet);
while(1){
if (audioFifoBuffer == nullptr || videoFifoBuffer == nullptr){
qDebug()<<"音频或者视频缓存还没分配:"time_base,
audioCurrentPTS,outputFormatContext->streams[audioOutputStreamIndex]->time_base);
if (_cts <= 0){
if (av_fifo_size(videoFifoBuffer) >= videoFrameSize){
mutex.lock();
//从缓存中读出数据
av_fifo_generic_read(videoFifoBuffer, _yuvBuffer, videoFrameSize, NULL);
mutex.unlock();
//计算时间
packet.pts = videoFrameIndex;
packet.dts = videoFrameIndex;
av_packet_rescale_ts(&packet, videoSourceCodecContext->time_base, outputFormatContext->streams[0]->time_base);
//设置frame参数
_yuvFrame->pts = packet.pts;
_yuvFrame->pkt_dts = packet.pts;
_yuvFrame->width = sourceWidth;
_yuvFrame->height = sourceHeight;
_yuvFrame->format = AV_PIX_FMT_YUV420P;
videoCurrentPTS = packet.pts;
//释放packet
av_packet_unref(&packet);
avcodec_send_frame(videoEncodeCodecContext,_yuvFrame);
avcodec_receive_packet(videoEncodeCodecContext, &packet);
av_interleaved_write_frame(outputFormatContext, &packet);
avio_flush(outputFormatContext->pb);
videoFrameIndex ++;
}
}
else{
if (av_audio_fifo_size(audioFifoBuffer) >= audioEncodeFrameSize ){
AVFrame *frame_mic = av_frame_alloc();
frame_mic->nb_samples = audioEncodeFrameSize;
frame_mic->channel_layout = audioEncodeCodecContext->channel_layout;
frame_mic->format = audioEncodeCodecContext->sample_fmt;
frame_mic->sample_rate = audioEncodeCodecContext->sample_rate;
frame_mic->pts = audioFrameIndex * audioEncodeFrameSize;
audioFrameIndex ++;
int ret = av_frame_get_buffer(frame_mic, 0);
if (ret < 0){
qDebug()<<"av_frame_get_buffer错误!"<data,audioEncodeFrameSize);
mutex.unlock();
if (ret < 0){
qDebug()<<"不能从音频流读取数据:"<
完整代码下载:
QT+FFMPEG屏幕录制及声音抓取完整代码(包含音视频同步)-C++文档类资源-CSDN文库https://download.csdn.net/download/hulinhulin/85421269



