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 "aakar";
}
#timelabel{
color: rgb(78, 154, 6);
font: 700 25pt "aakar";
}
</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 "Ubuntu";</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 "Ubuntu";</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)