Qt6 屏幕录相(包括音频视频)

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include 
#include 

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void timerEvent(QTimerEvent *e) override;
public slots:
    void Record();
signals:
    
private:
    Ui::Widget *ui;
    bool isRecord = false;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include 
#include 
#include 
#include 
#include "XScreenRecord.h"
using namespace std;

#define RECORDQSS                                          "\
QPushButton:!hover{                                        \
background-image: url(:/Resources/record_normal.png);      \
}                                                          \
QPushButton:hover{                                         \
    background-image: url(:/Resources/record_hot.png);     \
}                                                          \
QPushButton:press{                                         \
    background-image: url(:/Resources/record_pressed.png); \
}                                                          \
QPushButton{                                               \
    background-color: rgba(255, 255, 255, 0);              \
    background-image: url(:/Resources/record_normal.png);  \
}                                                          \
"

static QElapsedTimer timer;
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->timelabel->setText("000:00");
    //隐藏标题
    setWindowFlags(Qt::FramelessWindowHint);
    setAttribute(Qt::WA_TranslucentBackground);
    startTimer(100);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::Record()
{
    isRecord = !isRecord;
    std::cout<<"00000000000000000000000000000"<<std::endl;
    std::cout<<"Record = "<<isRecord<<std::endl;
    if(isRecord)
    {
        timer.restart();
        ui->recordButton->setStyleSheet("background-image: url(:/Resources/stop.png);background-color: rgba(255, 255, 255, 0)");
        QDateTime t = QDateTime::currentDateTime();
        QString filename = t.toString("yyyyMMdd_hhmmss");
        filename = "xscreen_"+ filename;
        filename += ".mp4";
       // filename = ui->urlEdit->text()+"\\" + filename;
        XScreenRecord::Get()->outWidth = ui->widthEdit->text().toInt();
        XScreenRecord::Get()->outHeight = ui->heighEdit->text().toInt();
        XScreenRecord::Get()->fps = ui->fpsEdit->text().toInt();
        if(!XScreenRecord::Get()->Start(filename.toLocal8Bit()))
        {
            ui->recordButton->setStyleSheet(RECORDQSS);
            isRecord = false;
            return;
        }
    }
    else
    {
        ui->recordButton->setStyleSheet(RECORDQSS);
        XScreenRecord::Get()->Stop();
    }
}

void Widget::timerEvent(QTimerEvent *e)
{
   // qDebug()<<"timerEvent";
    if(isRecord)
    {
        int es = timer.elapsed() / 1000;
        char buf[1024] = {0};
        sprintf(buf,"%03d:%02d",es/60,es%60);
        ui->timelabel->setText(buf);
    }
}

XAudioThread.h

#pragma once
#include 
#include 
#include 
#include 
#include 

class XAudioThread : public QThread
{
	Q_OBJECT
public:
	int sampleRate = 44100;
	int channels = 2;
	int sampleByte = 2;
	int nbSample = 1024;

	int cacheSize = 10;
	char *GetPCM();
	virtual ~XAudioThread();
	//out

	
	void Start();	
	void Stop();
	void run();

	static XAudioThread *Get()
	{
		static XAudioThread ct;
		return &ct;
	}
protected:
	bool isExit = false;
	std::list<char *> pcms;
	QMutex mutex;
	QAudioDevice audioInput;
	QAudioSource *audio = NULL;
	QIODevice *io =  NULL;
	QAudioFormat format;
	XAudioThread();
};

XAudioThread.cpp

#include "XAudioThread.h"
#include 
#include 
#include 

using namespace std;

void XAudioThread::Start()
{
	std::cout<<"XAudioThread::Start"<<std::endl;
	//Stop();
	mutex.lock();
	std::cout<<"set isExit = false"<<std::endl;
	isExit = false;

	QAudioFormat format;
	format.setSampleRate(sampleRate);
	format.setChannelCount(channels);
	format.setSampleFormat(QAudioFormat::Int16);
	
    audioInput = QMediaDevices::defaultAudioInput();
    if (!audioInput.isFormatSupported(format))
    {
        qWarning() << "Default format not supported, trying to use the nearest.";
    }
    audio = new QAudioSource(format);
    //开始录制音频
    io = audio->start();
	mutex.unlock();
	start();
}

void XAudioThread::Stop()
{
	std::cout<<"XAudioThread::Stop"<<std::endl;
	isExit = true;	
	mutex.lock();	
	std::cout<<"set isExit = true"<<std::endl;
	while(!pcms.empty())
	{
		delete pcms.front();
		pcms.pop_front();
	}
	if(audio)
	{
		io->close();
		audio->stop();
		delete audio;
		audio = NULL;
		io = NULL;
	}
	mutex.unlock();
	std::cout<<"isExit = "<<isExit<<std::endl;
	wait();
}


char *XAudioThread::GetPCM()
{
	mutex.lock();
	if(pcms.empty())
	{
		mutex.unlock();
		return NULL;
	}	
	char *re = pcms.front();
	pcms.pop_front();
	mutex.unlock();
	cout<<"A"<<flush;
	return re;
}


void XAudioThread::run()
{
//	std::cout<<"---------------------------run------------"<
	int size = nbSample*channels*sampleByte;
	
//	std::cout<<"XAudioThread::run()"<
	while(!isExit)
	{

		//std::cout<<"-----------------while(!isExit)----------run------------"<
		mutex.lock();
		if(pcms.size() > cacheSize)
		{
		//	std::cout<<"--------------------pcms.size() > cacheSize-------run------------"<
			mutex.unlock();
			msleep(5);
			continue;
		}
		//char data[size];
		char *data = new char[size];
		int readedSize = 0;
		//1.qt音频开始录制
	   	//音频重采样
		while(readedSize < size && !isExit)
		{
	    	if(!io || isExit)
			{
				mutex.unlock();
				cout<<"quit run"<<endl;
				return;
			}
	        qint64 rev_len = io->bytesAvailable();
		
		   // std::cout<<"rev_len = "<
	        if (rev_len < 1024)
	        {
	        	msleep(1);
	        	continue;
	        }
	        int read_len = 1024;
	    	//最后一帧
	    	if(size - readedSize < 1024)
	    	{
	    		read_len = size - readedSize;
	    	}
	    	//std::cout<<"size - readedSize = "<
	        qint64 readedLen = io->read(data + readedSize, read_len);
	      //  std::cout<<"read_len "<+= readedLen;
	    }
//	    std::cout<<"push_back------------------------"<
		pcms.push_back(data);
		mutex.unlock();
	}
	cout<<"quit run"<<endl;
	
}

XAudioThread::XAudioThread()
{

}

XAudioThread::~XAudioThread()
{

}

XCaptureThread.h

#pragma once

#include 
#include 


class XCaptureThread : protected QThread
{
	Q_OBJECT
public:
	//out

	int width = 1280;
	int height = 720;
	
	//in
	int fps = 10;
	int cacheSize = 3;
	
	char *GetRGB();
	void Start();	
	void Stop();
	void run();

	static XCaptureThread *Get()
	{
		static XCaptureThread ct;
		return &ct;
	}
	virtual ~XCaptureThread();
private:
	bool isExit = false;
	std::list<char *> rgbs;
	QMutex mutex;
	XCaptureThread();
};

XCaptureThread.cpp

#include "XCaptureThread.h"
#include 
#include 
#include 
#include 
#include 

using namespace std;
//截取全屏
void CaptureScreen(char *data)
{
	QRect rect =  QGuiApplication::primaryScreen()->geometry();
	int width = rect.width();
	int height = rect.height();
	 //获取QScreen
    static QScreen *scr = nullptr;
    if(!scr)
    {
        scr = QGuiApplication::primaryScreen();
        // if (const QWindow *window = windowHandle())
        //     scr = window->screen();
        if (!scr)
        return;
    }
    //截取全屏
//    QPixmap pix = scr->grapWindow(windowHandle()); //HWND
    QPixmap pix = scr->grabWindow(0);
    int size = width * height * 4;
    memcpy(data,pix.toImage().bits(),size);
    std::cout<<"pix.width() = "<<pix.width()<<" pix.height ="<<pix.height();
}



XCaptureThread::XCaptureThread()
{
	QRect rect =  QGuiApplication::primaryScreen()->geometry();
	width = rect.width();
	height = rect.height();
	std::cout<<"width = "<<width<<"height = "<<height<<std::endl;
}

XCaptureThread::~XCaptureThread()
{
	
}

void XCaptureThread::Start()
{
	std::cout<<"XCaptureThread::Start"<<std::endl;
	//Stop();
	mutex.lock();
	std::cout<<"set isExit = false"<<std::endl;
	isExit = false;
	mutex.unlock();
	start();
}

void XCaptureThread::Stop()
{
	std::cout<<"XCaptureThread::Stop"<<std::endl;
	mutex.lock();
	std::cout<<"set isExit = true"<<std::endl;
	isExit = true;
	while(!rgbs.empty())
	{
		delete rgbs.front();
		rgbs.pop_front();
	}
	mutex.unlock();
	std::cout<<"isExit = "<<isExit<<std::endl;
	wait();
}

//内存需要用户释放
char *XCaptureThread::GetRGB()
{
	mutex.lock();
	if(rgbs.empty())
	{
		mutex.unlock();
		return NULL;
	}	
	char *re = rgbs.front();
	rgbs.pop_front();
	mutex.unlock();
	cout<<"V"<<flush;
	return re;
}

void XCaptureThread::run()
{
	QElapsedTimer  t;
	while(!isExit)
	{
		t.restart();
		mutex.lock();
		int s = 1000 / fps;
		if(rgbs.size() < cacheSize)
		{
			char *data = new char[width*height*4];
			CaptureScreen(data);
			rgbs.push_back(data);
		}
		mutex.unlock();
		s = s - t.restart();
		if(s <= 0 || s > 10000)
		{
			s = 10;
		}
		//cout<
		msleep(s);		
	}
}

XScreenRecord.h

#pragma once
#include 
#include 

class XScreenRecord : protected QThread
{
	Q_OBJECT
public:
	//in
	int fps = 10;
	int outWidth = 1280;
	int outHeight = 720;
	
	bool Start(const char *filename);
	void Stop();
	void run();
	static XScreenRecord *Get()
	{
		static XScreenRecord ct;
		return &ct;
	}
	virtual ~XScreenRecord();
protected:
	bool isExit = false;
	QMutex mutex;
	XScreenRecord();
};
``
XScreenRecord.cpp`
````cpp
#include "XScreenRecord.h"
#include "XCaptureThread.h"
#include "XAudioThread.h"
#include "XVideoWriter.h"
#include 
using namespace std;

bool XScreenRecord::Start(const char *filename)
{
	if(!filename) return false;
	//Stop();
	mutex.lock();
	isExit = false;
	//初始化屏幕录制
	XCaptureThread::Get()->fps = fps;
	XCaptureThread::Get()->Start();

	//初始化音频的录制
	XAudioThread::Get()->Start();

	//初始化编码器
	XVideoWriter::Get()->inWidth = XCaptureThread::Get()->width;
	XVideoWriter::Get()->inHeight = XCaptureThread::Get()->height;
	XVideoWriter::Get()->outWidth = XCaptureThread::Get()->width;;
	XVideoWriter::Get()->outHeight = XCaptureThread::Get()->height;
	std::cout<<"outWidth = "<<XCaptureThread::Get()->width<<" outHeight = "<<XCaptureThread::Get()->height<<std::endl;
	XVideoWriter::Get()->outFPS = fps;
	XVideoWriter::Get()->Init(filename);
	XVideoWriter::Get()->AddVideoStream();
	XVideoWriter::Get()->AddAudioStream();
	if(!XVideoWriter::Get()->WriteHead())
	{
		mutex.unlock();
		Stop();
		return false;
	}
	std::cout<<"=--------------------------------"<<std::endl;
	mutex.unlock();
	start();
	return true;
}

void XScreenRecord::Stop()
{
	std::cout<<"XScreenRecord::Stop"<<std::endl;

	mutex.lock();
	isExit = true;
	mutex.unlock();

	mutex.lock();

	XVideoWriter::Get()->WriteEnd();
	std::cout<<"=========================="<<endl;
	XVideoWriter::Get()->Close();
	std::cout<<"=========================="<<endl;
	XAudioThread::Get()->Stop();
	std::cout<<"=========================="<<endl;
	XCaptureThread::Get()->Stop();
	std::cout<<"=========================="<<endl;

	mutex.unlock();
	wait();
}

void XScreenRecord::run()
{
	mutex.lock();
	while(!isExit)
	{
		//写入视频
		char *rgb = XCaptureThread::Get()->GetRGB();
		if(rgb)
		{
			AVPacket *p = XVideoWriter::Get()->EncodeVideo((unsigned char*)rgb);
			delete rgb;
			XVideoWriter::Get()->WriteFrame(p);
			cout<<"@";			
		}

		//写入音频
		char *pcm = XAudioThread::Get()->GetPCM();
		if(pcm)
		{
			AVPacket *p = XVideoWriter::Get()->EncodeAudio((unsigned char*)pcm);
			delete pcm;
			XVideoWriter::Get()->WriteFrame(p);
			cout<<"#";			
		}

		msleep(10);
		mutex.unlock();
	}
}

XScreenRecord::XScreenRecord()
{

}

XScreenRecord::~XScreenRecord()
{

}

XVideoWriter.h

#pragma once
#include 

class AVPacket;
enum XSAMPLEFMT
{
	X_S16 = 1,
	X_FLATP = 8
};

enum pixelFormat
{
    PIX_FMT_ARGB = 23,      ///< packed ARGB 8:8:8:8, 32bpp, ARGBARGB...
    PIX_FMT_RGBA = 26,      ///< packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
    PIX_FMT_ABGR = 27,      ///< packed ABGR 8:8:8:8, 32bpp, ABGRABGR...
    PIX_FMT_BGRA = 28,      ///< packed BGRA 8:8:8:8, 32bpp, BGRABGRA...
};

class XVideoWriter
{
public:
	//视频输入参数
	int inWidth = 848;
	int inHeight = 480;
	int inPixFmt = PIX_FMT_BGRA;//28;//30 //AV_PIX_FMT_BGRA

	//音频的输入参数
	int inSampleRate = 44100;
	int inChannels = 2;
	XSAMPLEFMT inSampleFmt = X_S16;

	//视频输出参数
	int vBitrate = 4000000;
	int outWidth = 848;
	int outHeight = 480;
	int outFPS = 25;

	//音频输出参数
	int aBitrate = 64000;
	int outChannels = 2;
	int outSampleRate = 44100;
	XSAMPLEFMT outSampleFmt = X_FLATP;

	int nb_sample = 1024; //输入输出的每帧数据每通道样本数量

	virtual bool Init(const char* file) = 0;
	virtual void Close() = 0;
	virtual bool AddVideoStream() = 0;
	virtual bool AddAudioStream() = 0;
	//把转yuv和编码放在一起做
	virtual AVPacket *EncodeVideo(const unsigned char *rgb) = 0;
	virtual AVPacket *EncodeAudio(const unsigned char *pcm) = 0;
	virtual bool WriteHead() = 0;
	//尾部文件没有写会视频不能拖动,时长不对

	//会释放pkt的空间
	virtual bool WriteFrame(AVPacket *pkt) = 0;

	virtual bool WriteEnd() = 0;

	virtual bool IsVideoBefore() = 0;

	static XVideoWriter *Get(unsigned short index = 0);
	~XVideoWriter();
	std::string filename;
protected:
	XVideoWriter();	
};

XVideoWriter.cpp

#include "XVideoWriter.h"

extern "C"
{
	#include 
	#include 
	#include 
	#include 
}

#include 
using namespace std;

class CXVideoWriter : public XVideoWriter
{
public:
	AVFormatContext *ic = NULL; //封装mp4输出上下文
	AVCodecContext *vc = NULL; //视频编码器上下文
	AVCodecContext *ac = NULL; //音频编码器山下文
	AVStream *vs =  NULL; //视频流
	AVStream *as = NULL; //音频流
	SwsContext *vsc = NULL; //像素转换的上下文
	SwrContext *asc = NULL; //音频重采样上下文

	AVFrame *yuv = NULL; //输出yuv
	AVFrame *pcm = NULL; //重采样后输出的pcm

	int vpts = 0; //视频的pts;
	int apts = 0; //音频的pts;

	void Close()
	{
		if(ic) avformat_close_input(&ic);
		
		if(vc) 
		{
			avcodec_close(vc);
			avcodec_free_context(&vc);
		}
		if(ac)
		{
			avcodec_close(ac);
			avcodec_free_context(&ac);
		}
		if(vsc)
		{
			sws_freeContext(vsc);
			vsc = NULL;
		}

		if(yuv){ av_frame_free(&yuv);}
		if(pcm){ av_frame_free(&pcm);}

		if(asc)
		{
			swr_free(&asc);
		}

	}
	bool Init(const char* file)
	{
		Close();
		//封装文件的上限文
		avformat_alloc_output_context2(&ic,NULL,NULL,file);
		if(!ic)
		{
			cerr<<"avformat_alloc_output_context2 failed!"<<endl;
			return false;
		}
		filename = file;
		return true;
 	}
	bool AddVideoStream()
	{

		if(!ic) return false;
		//1.视频编码器创建
		AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
		if(!codec)
		{
			cerr<<"avcodec_find_encoder AV_CODEC_ID_H264 failed!"<<endl;
			return false;
		}
		vc = avcodec_alloc_context3(codec);
		if(!vc)
		{
			cerr<<"avcodec_alloc_context3 failed!"<<endl;
			return false;
		}
		//比特率,压缩后每秒大小
		vc->bit_rate = vBitrate;
		vc->width = outWidth;
		vc->height = outHeight;

		//时间基数
		vc->time_base = {1,outFPS};
		vc->framerate = {outFPS,1};
		
		//画面组大小,多少帧一个关键帧
		vc->gop_size = 50;
		//两个非b帧之间的b帧数量
		vc->max_b_frames = 0;
		//像素格式
		vc->pix_fmt = AV_PIX_FMT_YUV420P;
		//编码id
		vc->codec_id = AV_CODEC_ID_H264;
		//选项的设置
		av_opt_set(vc->priv_data,"preset","superfast",0);
		//统一的封装头
		vc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
		//打开编码器
		int ret = avcodec_open2(vc,codec,NULL);
		if(ret != 0)
		{
			cerr<<"avcodec_open2 failed!"<<endl;
			return false;
		}
		cout<<"avcodec_open2 success!"<<endl;

		//添加视频流到上下文
		vs = avformat_new_stream(ic,NULL);
		//不包含编码信息
		vs->codecpar->codec_tag = 0;
		//把编码器信息拷贝到封装流里面
		avcodec_parameters_from_context(vs->codecpar,vc);
		av_dump_format(ic,0,filename.c_str(),1);


		//像素(尺寸)转换的上下文 rgb to yuv
		vsc = sws_getCachedContext(vsc,
			inWidth,inHeight,(AVPixelFormat)inPixFmt,//输入参数
			outWidth,outHeight,AV_PIX_FMT_YUV420P,
			SWS_BICUBIC,
			NULL,NULL,NULL
			);
		if(!vsc)
		{
			cerr<<"sws_getCachedContext failed!"<<endl;
			return false;
		}

		if(!yuv)
		{
			//对对象成员初始化
			yuv = av_frame_alloc();
			yuv->format = AV_PIX_FMT_YUV420P;
			yuv->width = outWidth;
			yuv->height = outHeight;
			//需要指定pts 每一帧加1
			yuv->pts = 0;

			//创建yuv 空间
			int ret = av_frame_get_buffer(yuv,32);
			if(ret != 0)
			{
				cerr<<"sws_getCachedContext failed!"<<endl;
				return false;
			}
		}

		return true;		
	}
	AVPacket *EncodeVideo(const unsigned char *rgb)
	{
		if(!ic || !vsc || !yuv) return NULL;
		AVPacket *p = NULL;
		uint8_t *inData[AV_NUM_DATA_POINTERS] = { 0 };
		inData[0] = (uint8_t *)rgb;

		int insize[AV_NUM_DATA_POINTERS] = {0};
		insize[0] = inWidth * 4;

		//rgb to yuv
		int h = sws_scale(vsc, inData,insize,0,inHeight,
			yuv->data,yuv->linesize);
		if(h<0)
			return NULL;
		//cout<
		yuv->pts = vpts;
		vpts++;
		//encode
		int ret = avcodec_send_frame(vc,yuv);
		if(ret != 0)
		{
			return NULL;
		}
		p = av_packet_alloc();
		ret = avcodec_receive_packet(vc,p);
		if(ret != 0 || p->size <= 0)
		{
			av_packet_free(&p);
			return NULL;
		}
		av_packet_rescale_ts(p,vc->time_base,vs->time_base);	
		p->stream_index = vs->index;
		return p;
	}

	bool WriteHead()
	{
		if(!ic) return false;		
		//打开io
		int ret = avio_open(&ic->pb,filename.c_str(),AVIO_FLAG_WRITE);
		if(ret != 0)
		{
			cerr<<"avio_open failed!"<<endl;
			return false;
		}
		cout<<"write "<<filename<<" head success"<<endl;

		//写入封装头
		ret = avformat_write_header(ic,NULL);
		if(ret != 0)
		{
			cerr<<"avformat_write_header failed!"<<endl;
			return false;
		}
		cout<<"write "<<filename<<" head success"<<endl;

		return true;
	}

	bool WriteFrame(AVPacket *pkt)
	{
		if(!ic || !pkt || pkt->size <= 0) return false;
		//av_write_frame
		if(av_interleaved_write_frame(ic,pkt) != 0) return false;
		return true;
	}

	virtual bool WriteEnd()
	{
		if(!ic || !ic->pb) return false;
		//写入尾部信息和索引
		if(av_write_trailer(ic) != 0)
		{
			cerr<<"av_write_trailer failed!"<<endl;
			return false;
		}

		//关闭输入io
		if(avio_closep(&ic->pb) != 0)
		{
			cerr<<"avio_close failed!"<<endl;
			return false;
		}
		cout<<"WriteEnd success!"<<endl;
		return true;
	}

	bool AddAudioStream()
	{
		if (!ic)return false;
		//1.找到音频编码
		AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
		if(!codec)
		{
			cerr<<"avcodec_find_encoder AV_CODEC_ID_AAC failed!"<<endl;
			return false;
		}
		//2.创建并打开音频编码器
		ac = avcodec_alloc_context3(codec);
		if(!ac)
		{
			cerr<<"avcodec_alloc_context3 failed!"<<endl;
			return false;
		}

		ac->bit_rate = aBitrate;
		ac->sample_rate = outSampleRate;
		ac->sample_fmt = (AVSampleFormat)outSampleFmt;
		ac->channels = outChannels;
		ac->channel_layout = av_get_default_channel_layout(outChannels);
		ac->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
		int ret = avcodec_open2(ac,codec,NULL);
		if(ret != 0)
		{
			avcodec_free_context(&ac);
			cerr<<"avcodec_open2 failed!"<<endl;
			return false;
		}
		cout<<"avcodec_open2 AV_CODEC_ID_AAC success"<<endl;
		//新曾音频流
		as = avformat_new_stream(ic,NULL);
		if(!as)
		{
			cerr<<"avformat_new_stream failed!"<<endl;
			return false;
		}
		//参数设置
		as->codecpar->codec_tag = 0;
		avcodec_parameters_from_context(as->codecpar,ac);
		av_dump_format(ic,0,filename.c_str(),1);

		//音频重采样上下文
		asc = swr_alloc_set_opts(asc,
			ac->channel_layout,ac->sample_fmt,ac->sample_rate,//输入格式
			av_get_default_channel_layout(inChannels),(AVSampleFormat)inSampleFmt,inSampleRate,
			0,0);
		ret = swr_init(asc);
		if(ret != 0)
		{
			cerr<<"swr_init failed!"<<endl;
			return false;
		}
		//音频重采样后输出AVFrame
		if(!pcm)
		{
			pcm = av_frame_alloc();
			pcm->format = ac->sample_fmt;
			pcm->channels = ac->channels;
			pcm->channel_layout = ac->channel_layout;
			pcm->nb_samples = nb_sample;//一帧音频的采样数量
			ret = av_frame_get_buffer(pcm,0);
			if(ret < 0)
			{
				cerr<<"av_frame_get_buffer failed "<<endl;
				return false;
			}
			cout<<"audio av_frame_get_buffer success"<<endl;
		}
		return true;
	}
	AVPacket* EncodeAudio(const unsigned char *d)
	{
		if(!ic || !asc || !pcm) return NULL;
		const uint8_t *data[AV_NUM_DATA_POINTERS] = {0};
		data[0] = (uint8_t *)d;
		int len = swr_convert(asc,pcm->data,pcm->nb_samples,
			data,pcm->nb_samples);
		cout<<len<<"*";

		//2音频编码
		int ret = avcodec_send_frame(ac,pcm);
		if(ret != 0)
		{
			return NULL;
		}
		//需要对pkt进行释放
		AVPacket *pkt = av_packet_alloc();
		av_init_packet(pkt);
		ret = avcodec_receive_packet(ac,pkt);
		if(ret != 0)
		{
			av_packet_free(&pkt);
			return NULL;
		}
		cout<<pkt->size<<"|";
		pkt->stream_index = as->index;
		pkt->pts = apts;
		pkt->dts = pkt->pts;
		apts += av_rescale_q(pcm->nb_samples,{1,ac->sample_rate},ac->time_base);
		return pkt;
	}

	bool IsVideoBefore()
	{	
		if(!ic || !as || !vs) 
		{
			std::cout<<"!ic || !as || !vs = false"<<std::endl;
			return false;
		}

		int ret = av_compare_ts(vpts,vc->time_base,apts,ac->time_base);
		if(ret <= 0)
		{
			std::cout<<"IsVideoBefore = true"<<std::endl;	
			return true;
		}
		std::cout<<"IsVideoBefore = false"<<std::endl;
		return false;
	}
};

XVideoWriter* XVideoWriter::Get(unsigned short index)
{
	static bool isfirst = true;
	if(isfirst)
	{
		av_register_all();
		avcodec_register_all();
		isfirst = false;
	}

	static CXVideoWriter wrs[65535];
	return &wrs[index]; 
}

XVideoWriter::XVideoWriter()
{

}

XVideoWriter::~XVideoWriter()
{

}

bool XVideoWriter::AddVideoStream()
{
	return true;
}

main.cpp

#include "widget.h"
#include "XCaptureThread.h"
#include "XAudioThread.h"
#include "XScreenRecord.h"
#include 
#include 
#include 
using namespace std;
int main(int argc, char *argv[])
{

    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

resource.qrc

<RCC>
  <qresource>
    <file>Resources/stop.png</file>
    <file>Resources/stop.png</file>
    <file>Resources/record_pressed.png</file>
    <file>Resources/record_normal.png</file>
    <file>Resources/record_hot.png</file>
    <file>Resources/mini.png</file>
    <file>Resources/logo.png</file>
    <file>Resources/logo.ico</file>
    <file>Resources/close.png</file>
    <file>Resources/record_pressed.png</file>
    <file>Resources/record_hot.png</file>
    <file>Resources/record_normal.png</file>
    <file>Resources/logo.ico</file>
    <file>Resources/mini.png</file>
    <file>Resources/close.png</file>
    <file>Resources/logo.png</file>
  </qresource>
</RCC>

widget.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Widget</class>
 <widget class="QWidget" name="Widget">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>381</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Widget</string>
  </property>
  <property name="windowIcon">
   <iconset resource="resource.qrc">
    <normaloff>:/Resources/logo.ico</normaloff>:/Resources/logo.ico</iconset>
  </property>
  <property name="styleSheet">
   <string notr="true">#back{
	background-color: rgb(46, 52, 54);
	border-radius:15px;
}

#all{
	background-color: rgb(35, 35, 35);
}

#title{
	color: rgb(78, 154, 6);
	font: 700 21pt &quot;aakar&quot;;
}


#timelabel{
	color: rgb(78, 154, 6);
	font: 700 25pt &quot;aakar&quot;;
}
</string>
  </property>
  <widget class="QWidget" name="back" native="true">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>1000</height>
    </rect>
   </property>
   <widget class="QPushButton" name="closeButton">
    <property name="geometry">
     <rect>
      <x>735</x>
      <y>20</y>
      <width>41</width>
      <height>21</height>
     </rect>
    </property>
    <property name="styleSheet">
     <string notr="true">background-image: url(:/Resources/close.png);
background-color: rgba(255, 255, 255,0);</string>
    </property>
    <property name="text">
     <string/>
    </property>
    <property name="flat">
     <bool>true</bool>
    </property>
   </widget>
   <widget class="QPushButton" name="minButton">
    <property name="geometry">
     <rect>
      <x>684</x>
      <y>19</y>
      <width>41</width>
      <height>21</height>
     </rect>
    </property>
    <property name="styleSheet">
     <string notr="true">background-image: url(:/Resources/mini.png);
background-color: rgba(255, 255, 255,0);</string>
    </property>
    <property name="text">
     <string/>
    </property>
    <property name="flat">
     <bool>true</bool>
    </property>
   </widget>
   <widget class="QWidget" name="all" native="true">
    <property name="geometry">
     <rect>
      <x>0</x>
      <y>57</y>
      <width>801</width>
      <height>321</height>
     </rect>
    </property>
    <widget class="QPushButton" name="recordButton">
     <property name="geometry">
      <rect>
       <x>580</x>
       <y>60</y>
       <width>101</width>
       <height>101</height>
      </rect>
     </property>
     <property name="styleSheet">
      <string notr="true">QPushButton:!hover
{
background-image: url(:/Resources/record_normal.png);
}

QPushButton:hover
{
	background-image: url(:/Resources/record_hot.png);
}

QPushButton:press
{
	background-image: url(:/Resources/record_pressed.png);
}

QPushButton{
	background-color: rgba(255, 255, 255, 0);
}

</string>
     </property>
     <property name="text">
      <string/>
     </property>
     <property name="flat">
      <bool>true</bool>
     </property>
    </widget>
    <widget class="QLineEdit" name="urlEdit">
     <property name="geometry">
      <rect>
       <x>80</x>
       <y>80</y>
       <width>461</width>
       <height>61</height>
      </rect>
     </property>
     <property name="text">
      <string/>
     </property>
    </widget>
    <widget class="QLineEdit" name="widthEdit">
     <property name="geometry">
      <rect>
       <x>100</x>
       <y>200</y>
       <width>101</width>
       <height>61</height>
      </rect>
     </property>
     <property name="text">
      <string>1280</string>
     </property>
    </widget>
    <widget class="QLineEdit" name="heighEdit">
     <property name="geometry">
      <rect>
       <x>240</x>
       <y>200</y>
       <width>101</width>
       <height>61</height>
      </rect>
     </property>
     <property name="text">
      <string>720</string>
     </property>
    </widget>
    <widget class="QLabel" name="timelabel">
     <property name="geometry">
      <rect>
       <x>400</x>
       <y>160</y>
       <width>141</width>
       <height>61</height>
      </rect>
     </property>
     <property name="text">
      <string>000:00</string>
     </property>
    </widget>
    <widget class="QLabel" name="infolabel">
     <property name="geometry">
      <rect>
       <x>380</x>
       <y>240</y>
       <width>141</width>
       <height>61</height>
      </rect>
     </property>
     <property name="styleSheet">
      <string notr="true">color: rgb(188, 70, 8);
font: 15pt &quot;Ubuntu&quot;;</string>
     </property>
     <property name="text">
      <string>虾球</string>
     </property>
    </widget>
    <widget class="QLineEdit" name="fpsEdit">
     <property name="geometry">
      <rect>
       <x>620</x>
       <y>200</y>
       <width>101</width>
       <height>61</height>
      </rect>
     </property>
     <property name="text">
      <string>10</string>
     </property>
    </widget>
    <widget class="QLabel" name="label">
     <property name="geometry">
      <rect>
       <x>520</x>
       <y>220</y>
       <width>61</width>
       <height>40</height>
      </rect>
     </property>
     <property name="styleSheet">
      <string notr="true">color: rgb(164, 0, 0);
font: 20pt &quot;Ubuntu&quot;;</string>
     </property>
     <property name="text">
      <string>FPS</string>
     </property>
    </widget>
   </widget>
   <widget class="QLabel" name="title">
    <property name="geometry">
     <rect>
      <x>80</x>
      <y>0</y>
      <width>321</width>
      <height>61</height>
     </rect>
    </property>
    <property name="text">
     <string>Screen 屏幕录像机</string>
    </property>
   </widget>
   <widget class="QLabel" name="logo">
    <property name="geometry">
     <rect>
      <x>20</x>
      <y>13</y>
      <width>40</width>
      <height>40</height>
     </rect>
    </property>
    <property name="styleSheet">
     <string notr="true">border-image: url(:/Resources/logo.png);</string>
    </property>
    <property name="text">
     <string/>
    </property>
   </widget>
  </widget>
 </widget>
 <resources>
  <include location="resource.qrc"/>
 </resources>
 <connections>
  <connection>
   <sender>closeButton</sender>
   <signal>clicked()</signal>
   <receiver>Widget</receiver>
   <slot>close()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>749</x>
     <y>34</y>
    </hint>
    <hint type="destinationlabel">
     <x>714</x>
     <y>380</y>
    </hint>
   </hints>
  </connection>
  <connection>
   <sender>minButton</sender>
   <signal>clicked()</signal>
   <receiver>Widget</receiver>
   <slot>showMinimized()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>703</x>
     <y>37</y>
    </hint>
    <hint type="destinationlabel">
     <x>710</x>
     <y>380</y>
    </hint>
   </hints>
  </connection>
  <connection>
   <sender>recordButton</sender>
   <signal>clicked()</signal>
   <receiver>Widget</receiver>
   <slot>Record()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>627</x>
     <y>160</y>
    </hint>
    <hint type="destinationlabel">
     <x>626</x>
     <y>383</y>
    </hint>
   </hints>
  </connection>
 </connections>
 <slots>
  <slot>Record()</slot>
 </slots>
</ui>

CMakeLists.txt

cmake_minimum_required(VERSION 3.1)
project(opencv_example_project)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)


find_package(OpenCV REQUIRED)

message(STATUS "OpenCV library status:")
message(STATUS "    config: ${OpenCV_DIR}")
message(STATUS "    version: ${OpenCV_VERSION}")
message(STATUS "    libraries: ${OpenCV_LIBS}")
message(STATUS "    include path: ${OpenCV_INCLUDE_DIRS}")

find_library(AVCODEC_LIBRARY avcodec)
find_library(AVFORMAT_LIBRARY avformat)
find_library(AVUTIL_LIBRARY avutil)
find_library(AVDEVICE_LIBRARY avdevice)

find_package(Qt6 COMPONENTS Core)
find_package(Qt6 COMPONENTS Gui)
find_package(Qt6 COMPONENTS Multimedia)
find_package(Qt6 COMPONENTS Widgets)


add_executable(XScreen main.cpp  resource.qrc  XAudioThread.cpp    XScreenRecord.cpp
widget.cpp  XCaptureThread.cpp  XVideoWriter.cpp)

target_link_libraries(XScreen PRIVATE 
    ${OpenCV_LIBS} 
    Qt::Core
    Qt::Gui
    Qt::Multimedia
    Qt::Widgets
    pthread 
    swresample 
    m 
    swscale 
    avformat    
    avcodec 
    avutil 
    avfilter 
    avdevice 
    postproc 
    z 
    lzma  
    rt)

你可能感兴趣的:(Qt,学习,Qt,ffmpeg,学习,音视频,opencv,计算机视觉)