栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

音视频开发系列(11):基于qt音频推流的代码封装

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

音视频开发系列(11):基于qt音频推流的代码封装

本次分享的内容是对上次分享的音频的代码进行封装,将其封装成两个类,本次的封装主要是在上次分享的视频推流的封装上进行的,所以与视频推六中的封装类一致,即(XMediaEncode:音视频编解码类,XRtmp:网络推流类。)

XMediaEncode类的音频部分主要包括以下几个函数:

	//音频重采样上下文初始化
	virtual bool InitResample() = 0;

	virtual AVFrame* Resample(char *data) = 0;
    
    //音频编码器初始化
	virtual bool InitAudioCodec() = 0;

    //音频编码 返回值无需调用者清理
	virtual AVPacket *EncodeAudio(AVFrame *frame) = 0;

由于视频和音频的推流部分相似,所以只是对其中的代码逻辑部分进行改动,接下来是源码分享,由于与上次视频封装的内容相似,代码中也有具体的解释,所以就不进行过多的说明了

XRtmp.h

#pragma once
class AVCodecContext;
class AVPacket;
class XRtmp
{
public:
	static XRtmp *Get(unsigned char index = 0);
	//初始化封装器上下文
	virtual bool Init(const char *url) = 0;

	//添加视频或者音频流
	virtual bool AddStream(const AVCodecContext *c) = 0;
	
	//打开rtmp网络IO,发送封装头
	virtual bool SendHead() = 0;

	//rtmp 帧推流
	virtual bool SendFrame(AVPacket *pack) = 0;
	~XRtmp();
protected:
	XRtmp();
};

XRtmp.cpp

#include "XRtmp.h"
#include 
#include 
using namespace std;
extern "C"
{
#include 
}
#pragma comment(lib,"avformat.lib")
class CXRtmp :public XRtmp
{
public:

	void Close()
	{
		if (ic)
		{
			avformat_close_input(&ic);
			vs = NULL;
		}
		vc = NULL;
		url = "";
	}
	bool Init(const char *url)
	{
		//a.创建输出封装器上下文
		int ret = avformat_alloc_output_context2(&ic, 0, "flv", url);
		this->url = url;
		if (ret != 0)
		{
			char buf[1024] = { 0 };
			av_strerror(ret, buf, sizeof(buf) - 1);
			cout << buf << endl;
			return false;
		}
		return true;
	}
	bool AddStream(const AVCodecContext *c)
	{
		if (!c)return false;
		AVStream *st = avformat_new_stream(ic, NULL);
		if (!st)
		{
			cout << ("avformat_new_stream failed!") << endl;
			return false;
		}
		st->codecpar->codec_tag = 0;
		//从编码器复制参数
		avcodec_parameters_from_context(st->codecpar, c);
		av_dump_format(ic, 0, url.c_str(), 1);
		if (c->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			vc = c;
			vs = st;
		}
		else if (c->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			ac = c;
			as = st;
		}
		return true;
	}
	bool SendHead()
	{
		int ret = avio_open(&ic->pb, url.c_str(), AVIO_FLAG_WRITE);
		if (ret != 0)
		{
			char buf[1024] = { 0 };
			av_strerror(ret, buf, sizeof(buf) - 1);
			cout << buf << endl;
			return false;
		}
		//写入封装头
		ret = avformat_write_header(ic, NULL);
		if (ret != 0)
		{
			char buf[1024] = { 0 };
			av_strerror(ret, buf, sizeof(buf) - 1);
			cout << buf << endl;
			return false;
		}
		return true;
	}

	bool SendFrame(AVPacket *pack)
	{
		if (!pack || pack->size <= 0 || !pack->data)return false;
		//判断是音频还是视频
		AVRational stime;
		AVRational dtime;
		if (vs && vc && pack->stream_index == vs->index)
		{
			stime = vc->time_base;
			dtime = vs->time_base;

		}
		else if (as && ac &&pack->stream_index == as->index)
		{
			stime = ac->time_base;
			dtime = as->time_base;
		}
		else
		{
			return false;
		}
		//推流
		pack->pts = av_rescale_q(pack->pts, stime, dtime);
		pack->dts = av_rescale_q(pack->dts, stime, dtime);
		pack->duration = av_rescale_q(pack->duration, stime, dtime);
		int ret = av_interleaved_write_frame(ic, pack);
		if (ret == 0)
		{
			cout << "#" << flush;
		}
		return true;
	}
private:
	AVFormatContext *ic = NULL;  	//rtmp flv 封装器
	string url = "";
	//视频编码器流
	const AVCodecContext *vc = NULL;
	//视频流
	AVStream *vs = NULL;
	//音频编码器流
	const AVCodecContext *ac = NULL;
	//音频流
	AVStream *as = NULL;
};

XRtmp * XRtmp::Get(unsigned char index)
{
	static CXRtmp cxr[255];
	static bool isFirst = true;
	if (isFirst)
	{
		//注册所有的封装器
		av_register_all();
		//注册所有的网络协议
		avformat_network_init();
		isFirst = false;
	}
	return &cxr[index];
}
XRtmp::XRtmp()
{
}


XRtmp::~XRtmp()
{
}

XMediaEncode.h

#pragma once
struct  AVFrame;
struct  AVPacket;
class AVCodecContext;
enum XSampleFMT
{
	X_S16 = 1,
	X_FLTP = 8
};

///音视频编码接口类
class XMediaEncode
{
public:

	///输入参数
	int inWidth = 1280;
	int inHeight = 720;
	int inPixSize = 3;
	int channels = 2;
	int sampleRate = 44100;
	XSampleFMT inSampleFmt = X_S16;
	///输出参数
	int outWidth = 1280;
	int outHeight = 720;
	int bitrate = 4000000; 
	int fps = 25;
	int nbSample = 1024;
	XSampleFMT outSampleFmt = X_FLTP;

	//工厂的生产方法
	static XMediaEncode *Get(unsigned char index = 0);
	//初始化像素格式转换的上下文
	virtual	bool InitScale() = 0;

	//音频重采样上下文初始化
	virtual bool InitResample() = 0;


	virtual AVFrame* Resample(char *data) = 0;

	virtual AVFrame* RGBToYUV(char *rgb) = 0;

	//视频编码器初始化
	virtual bool InitVideoCodec() = 0;

	//音频编码器初始化
	virtual bool InitAudioCodec() = 0;

	//视频编码 返回值无需调用者清理
	virtual AVPacket *EncodeVideo(AVFrame *frame) = 0;
	
	//音频编码 返回值无需调用者清理
	virtual AVPacket *EncodeAudio(AVFrame *frame) = 0;

	virtual ~XMediaEncode();
	AVCodecContext *vc = 0; 		//编码器上下文
	AVCodecContext *ac = 0;         //音频编码器上下文
protected:
	XMediaEncode();
};

XMediaEncode.cpp

#include "XMediaEncode.h"
#include 
extern "C"
{
#include 
#include 
#include 
#include "libswresample/swresample.h"
}
#pragma comment(lib,"swscale.lib")
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"swresample.lib")
using namespace std;

#if defined WIN32 || defined _WIN32
#include 
#endif
//获取CPU数量
static int XGetCpuNum()
{
#if defined WIN32 || defined _WIN32
	SYSTEM_INFO sysinfo;
	GetSystemInfo(&sysinfo);

	return (int)sysinfo.dwNumberOfProcessors;
#elif defined __linux__
	return (int)sysconf(_SC_NPROCESSORS_ONLN);
#elif defined __APPLE__
	int numCPU = 0;
	int mib[4];
	size_t len = sizeof(numCPU);

	// set the mib for hw.ncpu
	mib[0] = CTL_HW;
	mib[1] = HW_AVAILCPU;  // alternatively, try HW_NCPU;

						   // get the number of CPUs from the system
	sysctl(mib, 2, &numCPU, &len, NULL, 0);

	if (numCPU < 1)
	{
		mib[1] = HW_NCPU;
		sysctl(mib, 2, &numCPU, &len, NULL, 0);

		if (numCPU < 1)
			numCPU = 1;
	}
	return (int)numCPU;
#else
	return 1;
#endif
}

class CXMediaEncode :public XMediaEncode
{
public:
	void Close()
	{
		if (vsc)
		{
			sws_freeContext(vsc);
			vsc = NULL;
		}
		if (yuv)
		{
			av_frame_free(&yuv);
		}
		if (asc)
		{
			swr_free(&asc);
		}
		if (vc)
		{
			avcodec_free_context(&vc);
		}
				if (vc)
		{
			avcodec_free_context(&vc);
		}
		if (pcm)
		{
			av_frame_free(&pcm);
		}
		vpts = 0;
		apts = 0;
		av_packet_unref(&vpack);
		av_packet_unref(&apack);
	}
	bool InitVideoCodec()
	{
				///4.初始化编码上下文
		//a 找到编码器
		AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
		if (!codec)
		{
			cout << "Can't find h264 encoder!";
			return false;
		}
		//b 创建编码器上下文
		vc = avcodec_alloc_context3(codec);
		if (!vc)
		{
			cout<<"avcodec_alloc_context3 failed!"<flags |= AV_CODEC_FLAG_GLOBAL_HEADER; //全局参数

		cout << "cpunum=" << XGetCpuNum() << endl;
		vc->thread_count = XGetCpuNum();

		vc->codec_id = codec->id;
		vc->bit_rate = 50 * 1024 * 8; //压缩后每秒视频的bit位大小  200kB
		vc->width = outWidth;
		vc->height = outHeight;
		vc->time_base = { 1,fps };
		vc->framerate = { fps,1 };

		//画面组的大小,多少帧一个关键帧
		vc->gop_size = 50;
		//设置不需要b帧
		vc->max_b_frames = 0;
		//设置像素格式
		vc->pix_fmt = AV_PIX_FMT_YUV420P;
		//d 打开编码器上下文
		int ret = avcodec_open2(vc, 0, 0);
		if (ret != 0)
		{
			char buf[1024] = { 0 };
			av_strerror(ret, buf, sizeof(buf) - 1);
			cout << buf << endl;
			return false;
		}
		cout << "avcodec_open2 success!" << endl;
		return true;
	}


	bool InitAudioCodec()
	{
		///4 初始化音频编码器
		if (!CreateCodec(AV_CODEC_ID_AAC))
		{
			return false;
		}

		//音频的参数
		ac->bit_rate = 40000;
		ac->sample_rate = sampleRate;
		ac->sample_fmt = AV_SAMPLE_FMT_FLTP;
		ac->channels = channels;
		ac->channel_layout = av_get_default_channel_layout(channels);
		//打开编码器
		return OpenCodec(&ac);
	}
	AVPacket *EncodeVideo(AVFrame *frame)
	{
		av_packet_unref(&vpack);
		frame->pts = vpts;
		vpts++;
		int ret = avcodec_send_frame(vc, frame);
		if (ret != 0)
		{
			return NULL;
		}

		ret = avcodec_receive_packet(vc, &vpack);
		if (ret != 0 || vpack.size < 0)
		{
			return NULL;
		}
		return &vpack;
	}

	AVPacket *EncodeAudio(AVFrame *frame)
	{
		pcm->pts = apts;
		apts += av_rescale_q(pcm->nb_samples, { 1,sampleRate }, ac->time_base);

		int ret = avcodec_send_frame(ac, pcm);
		if (ret != 0)return NULL;

		av_packet_unref(&apack);
		ret = avcodec_receive_packet(ac, &apack);
		//cout << "avcodec_receive_packet   " << ret << endl;
		if (ret != 0)return NULL;
		return &apack;
	}
	bool InitScale()
	{
		///2.初始化格式转换上下文
		vsc = sws_getCachedContext(vsc,
			//源宽、高、像素格式
			inWidth, inHeight, AV_PIX_FMT_BGR24,
			//目标宽、高、像素格式
			outWidth, outHeight, AV_PIX_FMT_YUV420P,
			SWS_BICUBIC,  //尺寸变化使用的算法
			0, 0, 0
			);

		if (!vsc)
		{
			cout << "sws_getCachedContext failed";
			return false;
		}

		///3.输出的数据结构
		yuv = av_frame_alloc();
		yuv->format = AV_PIX_FMT_YUV420P;
		yuv->width = inWidth;
		yuv->height = inHeight;
		yuv->pts = 0;
		//分配yuv空间
		int ret = av_frame_get_buffer(yuv, 32);
		if (ret != 0)
		{
			char buf[1024] = { 0 };
			av_strerror(ret, buf, sizeof(buf) - 1);
			throw exception(buf);
		}
		return true;
	}

	bool InitResample()
	{
		asc = NULL;
		asc = swr_alloc_set_opts(asc,
			av_get_default_channel_layout(channels), (AVSampleFormat)outSampleFmt, sampleRate,               //输出格式
			av_get_default_channel_layout(channels), (AVSampleFormat)inSampleFmt, sampleRate,//输入格式	
			0, 0);
		if (!asc)
		{
			cout << "swr_alloc_set_opts failed!" << endl;
			return false;
		}
		int ret = swr_init(asc);
		if (ret != 0)
		{
			char err[1024] = { 0 };
			av_strerror(ret, err, sizeof(err) - 1);
			cout << err << endl;
			return false;
		}
		cout << "音频重采样 上下文初始化成功" << endl;
		
		///3 音频重采样输出空间分配
		pcm = av_frame_alloc();
		pcm->format = outSampleFmt;
		pcm->channels = channels;
		pcm->channel_layout = av_get_default_channel_layout(channels);
		pcm->nb_samples = nbSample;//一帧音频一通道的采样数量
		ret = av_frame_get_buffer(pcm, 0); //给pcm分配存储空间
		if (ret != 0)
		{
			char err[1024] = { 0 };
			av_strerror(ret, err, sizeof(err) - 1);
			cout << err << endl;
			return false;
		}
		return true;
	}
	AVFrame *RGBToYUV(char *rgb)
	{
		///rgb to yuv
		//3.初始化输入的数据结构
		uint8_t *indata[AV_NUM_DATA_POINTERS] = { 0 };
		//indata[0] bgrbgrbgr
		//plane  indata[0] bbbbb indata[1]ggggg indata[2] rrrrr
		indata[0] = (uint8_t*)rgb;
		int insize[AV_NUM_DATA_POINTERS] = { 0 };
		//一行(宽)数据的字节数
		insize[0] = inWidth*inPixSize;
		int h = sws_scale(vsc, indata, insize, 0, inHeight,//源数据
			yuv->data, yuv->linesize);
		if (h <= 0)
		{
			return NULL;
		}

		return yuv;
	}
	AVFrame* Resample(char *data)
	{
		const uint8_t *indata[AV_NUM_DATA_POINTERS] = { 0 };
		indata[0] = (uint8_t *)data;
		//已经读取一帧源数据
		//重采样数据
		int len = swr_convert(asc, pcm->data, pcm->nb_samples,//输出参数,输出存储地址,样本数
			indata, pcm->nb_samples
			);
		if (len <= 0)
		{
			return NULL;
		}
		return pcm;
	}
private:
	bool OpenCodec(AVCodecContext **c)
	{
		int ret = avcodec_open2(*c, 0, 0);
		if (ret != 0)
		{
			char err[1024] = { 0 };
			av_strerror(ret, err, sizeof(err) - 1);
			cout << err << endl;
			avcodec_free_context(c);
			return false;
		}
		cout << "avcodec_open2 success!" << endl;
		return true;
	}
	bool CreateCodec(AVCodecID cid)
	{
		///4 初始化编码器
		AVCodec *codec = avcodec_find_encoder(cid);
		if (!codec)
		{
			cout << "avcodec_find_encoder failed!" << endl;
			return false;
		}
		//音频编码器上下文
		ac = avcodec_alloc_context3(codec);
		if (!ac)
		{
			cout << "avcodec_alloc_context3 failed!" << endl;
			getchar();
			return false;
		}
		cout << "avcodec_alloc_context3 success!" << endl;

		ac->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
		ac->thread_count = XGetCpuNum();
		return true;
	}
	SwsContext *vsc = NULL;         //像素格式转换上下文
	AVFrame *yuv = NULL;            //输出的yuv
	SwrContext *asc = NULL;         //音频重采样上下文
	AVPacket vpack = {0};           //视频帧
	AVPacket apack = { 0 };			//音频帧
	AVFrame *pcm = NULL;            //重采样输出的pcm
	int vpts = 0;
	int apts = 0;
}; 

XMediaEncode *XMediaEncode::Get(unsigned char index)
{
	static bool isFirst = true;
	if (isFirst)
	{
		//注册所有的编解码器
		avcodec_register_all();
		isFirst = false;
	}
	static CXMediaEncode cxm[255];
	return &cxm[index];
}
XMediaEncode::XMediaEncode()
{
}


XMediaEncode::~XMediaEncode()
{
}

main.cpp

#include 
#include 
#include 
#include 
#include "XMediaEncode.h"
#include "XRtmp.h"
using namespace std;
int main(int argc, char *argv[])
{
	QCoreApplication a(argc, argv);

	char *outUrl = "rtmp://192.168.198.128/live";
	int sampleRate = 44100;
	int channels = 2;
	int sampleByte = 2;
	///1 qt音频开始录制 
	QAudioFormat fmt;
	int ret;
	//采样频率
	fmt.setSampleRate(sampleRate);
	//通道数量
	fmt.setChannelCount(channels);
	//样本大小
	fmt.setSampleSize(sampleByte *8);
	//格式
	fmt.setCodec("audio/pcm");
	//字节序
	fmt.setByteOrder(QAudioFormat::LittleEndian);
	fmt.setSampleType(QAudioFormat::UnSignedInt);
	QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
	if (!info.isFormatSupported(fmt))
	{
		cout << "Audio format not support!" << endl;
		fmt = info.nearestFormat(fmt);
	}
	cout << "Audio format success" << endl;

	QAudioInput *input = new QAudioInput(fmt);
	//开始录制音频
	QIODevice *io= input->start();

	XMediaEncode *xe = XMediaEncode::Get();
	xe->channels = channels;
	xe->nbSample = 1024;
	xe->sampleRate = sampleRate;
	xe->inSampleFmt = XSampleFMT::X_S16;
	xe->outSampleFmt = XSampleFMT::X_FLTP;
	if (!xe->InitResample())
	{
		getchar();
		return -1;
	}


	///4 初始化音频编码器
	if (!xe->InitAudioCodec())
	{
		getchar();
		return -1;
	}

	///5 封装器和音频流配置
	//a.创建输出封装器上下文

	XRtmp *xr = XRtmp::Get(0);
	if (!xr->Init(outUrl))
	{
		getchar();
		return -1;
	}

	//b.添加音频流
	if (!xr->AddStream(xe->ac))
	{
		getchar();
		return -1;
	}
	//从编码器复制参数

	///6 打开rtmp的网络输出io
	//写入封装头

	if (!xr->SendHead())
	{
		getchar();
		return -1;
	}
	//一次读取一帧音频的字节数
	int readSize = xe->nbSample*channels*sampleByte;
	char *buf = new char[readSize];
	for (;;)
	{
		//一次读取一帧音频
		if (input->bytesReady() < readSize)
		{
			QThread::msleep(1);
			continue;
		}
		int size = 0;
		while (size != readSize)
		{
			int len = io->read(buf + size, readSize - size);
			if (len < 0)break;
			size += len;
		}
		if (size != readSize)continue;

		//已经读取一帧源数据
		//重采样数据
		AVFrame *pcm = xe->Resample(buf);
		//pts运算
		//nb_sample/sample_rate =一帧音频的秒数 
		//time_base pts=sec * timebase.den 
		AVPacket *pkt=xe->EncodeAudio(pcm);

		if (!pkt)continue;
		//cout << pkt->size << " " << flush;
		推流
		xr->SendFrame(pkt);
		if (ret == 0)
		{
			cout << "#" << flush;
		}
	}
	delete buf;
	getchar();
	return a.exec();
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/994284.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号