上次分享了如何对图像进行进行处理以达到图像磨皮的效果,由于后面要把这个音视频项目做成一个带有界面的项目,所以需要对其中例如编码器初始化这些操作封装成一个控制类,这样的话界面只需要调用该类即可,就不需要再写重复的代码。
这个项目主要的核心类如下图所示。
这次主要要封装一个控制类,该类主要包括以下方法和属性:
#pragma once
#include <QThread>
#include <string>
#include "XDataThread.h"
class XController:public XDataThread
{
public:
std::string outUrl;
int camIndex = -1;
std::string inUrl = "";
std::string error = "";
static XController *Get()
{
static XController xc;
return &xc;
}
//设定美颜参数
virtual bool Set(std::string key,double value);
virtual bool Start();
virtual void Stop();
void run();
virtual ~XController();
protected:
int vindex = 0;//视频流索引
int aindex = 0;//音频流索引
XController();
};
该类为一个线程类,主要在用户调用该类进行Start后就会开启一个线程,然后在线程里面就会开启音视频录制类线程并进行读取录制好的数据,这样在主函数中只需要调用Start和Stop来开启和暂停线程即可,接下来是源码的分享。该类也是一个线程类,继承于之前编写过的XDataThread类
XController.cpp
#include "XController.h"
#include <iostream>
#include "XVideoCapture.h"
#include "XAudioRecord.h"
#include "XMediaEncode.h"
#include "XRtmp.h"
using namespace std;
void XController::run()
{
long long beginTime = GetCurTime();
while (!isExit)
{
//一次读取一帧音频
XData ad = XAudioRecord::Get()->Pop();
XData vd = XVideoCapture::Get()->Pop();
if (ad.size <= 0 && vd.size <= 0)
{
msleep(1);
continue;
}
if (ad.size > 0)
{
ad.pts = ad.pts - beginTime;
//已经读取一帧源数据
//重采样数据
XData pcm = XMediaEncode::Get()->Resample(ad);
ad.Drop();
XData pkt = XMediaEncode::Get()->EncodeAudio(pcm);
if (pkt.size>0)
{
//推流
if (XRtmp::Get()->SendFrame(pkt, aindex))
{
cout << "#" << flush;
}
}
}
if (vd.size > 0)
{
vd.pts = vd.pts - beginTime;
XData yuv = XMediaEncode::Get()->RGBToYUV(vd);
vd.Drop();
XData pkt = XMediaEncode::Get()->EncodeVideo(yuv);
if (pkt.size>0)
{
//推流
if (XRtmp::Get()->SendFrame(pkt, vindex))
{
cout << "@" << flush;
}
}
}
}
}
bool XController::Set(std::string key, double value)
{
XFilter::Get()->Set(key, value);
return true;
}
bool XController::Start()
{
///1.设置磨皮过滤器
XVideoCapture::Get()->AddFilter(XFilter::Get());
cout << "1设置磨皮过滤器成功" << endl;
///2.打开相机
if (camIndex >= 0)
{
if (!XVideoCapture::Get()->Init(camIndex))
{
error = "打开相机失败";
cout << error << endl;
return false;
}
}else if(inUrl.empty())
{
if (!XVideoCapture::Get()->Init(inUrl.c_str()))
{
error = "打开";
error += inUrl;
error += "相机失败";
cout << error << endl;
return false;
}
}
else
{
error = "请输入相机参数";
cout << "请输入相机参数" << endl;
return false;
}
cout << "2相机打开成功" << endl;
///3.qt音频开始录制
if (!XAudioRecord::Get()->Init())
{
error = "录音设备打开失败";
cout << error << endl;
return false;
}
cout << "3录音设备打开成功" << endl;
///11.启动音视频录制线程
XAudioRecord::Get()->Start();
XVideoCapture::Get()->Start();
///音视频编码类
///4.初始化格式转换上下文
///初始化输出的数据结构
XMediaEncode::Get()->inWidth=XVideoCapture::Get()->width;
XMediaEncode::Get()->inHeight = XVideoCapture::Get()->height;
XMediaEncode::Get()->outWidth = XVideoCapture::Get()->width;
XMediaEncode::Get()->outHeight = XVideoCapture::Get()->height;
if (!XMediaEncode::Get()->InitScale())
{
error = "视频像素格式转换打开失败";
cout << error << endl;
return false;
}
cout << "4视频像素格式转换打开成功" << endl;
///5.音频重采样 上下文初始化
XMediaEncode::Get()->channels = XAudioRecord::Get()->channels;
XMediaEncode::Get()->nbSample = XAudioRecord::Get()->nbSample;
XMediaEncode::Get()->sampleRate = XAudioRecord::Get()->sampleRate;
if (!XMediaEncode::Get()->InitResample())
{
error = "音频重采样上下文初始化失败";
cout << error << endl;
return false;
}
cout << "5音频重采样上下文初始化成功" << endl;
///6.初始化音频编码器
if (!XMediaEncode::Get()->InitAudioCodec())
{
error = "初始化音频编码器失败";
cout << error << endl;
return false;
}
cout << "6初始化音频编码器成功" << endl;
///7.初始化视频编码器
if (!XMediaEncode::Get()->InitVideoCodec())
{
error = "初始化视频编码器失败";
cout << error << endl;
return false;
}
cout << "7初始化视频编码器成功" << endl;
///8.创建输出封装器上下文
if (!XRtmp::Get()->Init(outUrl.c_str()))
{
error = "创建输出封装器上下文失败";
cout << error << endl;
return false;
}
cout << "8创建输出封装器上下文成功" << endl;
///9.添加音视频流
vindex = XRtmp::Get()->AddStream(XMediaEncode::Get()->vc);
aindex = XRtmp::Get()->AddStream(XMediaEncode::Get()->ac);
if (vindex < 0 || aindex < 0)
{
error = "添加音视频流失败";
cout << error << endl;
return false;
}
cout << "9添加音视频流成功" << endl;
///10.写入封装头
if (!XRtmp::Get()->SendHead())
{
error = "发送封装头失败";
cout << error << endl;
return false;
}
cout << "10发送封装头成功" << endl;
XAudioRecord::Get()->Clear();
XVideoCapture::Get()->Clear();
XDataThread::Start();
return true;
}
void XController::Stop()
{
XDataThread::Stop();
XAudioRecord::Get()->Stop();
XVideoCapture::Get()->Stop();
XMediaEncode::Get()->Close();
XRtmp::Get()->Close();
camIndex = -1;
inUrl = "";
return;
}
XController::XController()
{
}
XController::~XController()
{
}
main.cpp
主函数主要用于测试停止后是否存在问题。
#include <QtCore/QCoreApplication>
#include <QThread>
#include <iostream>
#include "XMediaEncode.h"
#include "XRtmp.h"
#include "XAudioRecord.h"
#include "XVideoCapture.h"
#include "XFilter.h"
#include "XController.h"
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
const char *outUrl = "rtmp://192.168.198.128/live";
XController::Get()->camIndex = 0;
XController::Get()->outUrl = outUrl;
XController::Get()->Start();
QThread::msleep(10000);
XController::Get()->Stop();
XController::Get()->camIndex = 0;
XController::Get()->outUrl = outUrl;
XController::Get()->Start();
XController::Get()->wait();
return a.exec();
}