本次分享的内容是对上次分享的音频的代码进行封装,将其封装成两个类,本次的封装主要是在上次分享的视频推流的封装上进行的,所以与视频推六中的封装类一致,即(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" #includeextern "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(); }



