1.xaudio.h
#pragma once
class xaudioplay
{
public:
static xaudioplay* get();
xaudioplay();
//一定得是虚析构函数,delete时才可以通过父类指针完成继承类的析构
virtual ~xaudioplay();
virtual bool open()=0;
//第二次打开如果是其他媒体文件的话下面这些参数就要变,因此需要有close
virtual bool close() = 0;
//将参数传递与ffmpeg隔离,故通过成员传递,QT的音频格式对象需要这些参数
//channels和采样率通过demux传递,采样大小固定
int samplerate = 0;
int samplesize = 16;
int channels = 0;
//音频
virtual bool write(const unsigned char* data, int datasize) = 0;
virtual int Getfree() = 0;
};
1.纯虚函数(open(),close())
在基类中仅仅给出声明,不对虚函数实现定义,而是在派生类中实现。这个虚函数称为纯虚函数。普通函数如果仅仅给出它的声明而没有实现它的函数体,这是编译不过的。纯虚函数没有函数体。
纯虚函数需要在声明之后加个=0;
class <基类名>
{
virtual <类型><函数名>(<参数表>)=0; …
};
2.抽象类 (xaudioplay)
含有纯虚函数的类被称为抽象类。抽象类只能作为派生类的基类,不能定义对象,但可以定义指针。在派生类实现该纯虚函数后,定义抽象类对象的指针,并指向或引用子类对象。
1)在定义纯虚函数时,不能定义虚函数的实现部分;
2)在没有重新定义这种纯虚函数之前,是不能调用这种函数的。
抽象类的唯一用途是为派生类提供基类,纯虚函数的作用是作为派生类中的成员函数的基础,并实现动态多态性。继承于抽象类的派生类如果不能实现基类中所有的纯虚函数,那么这个派生类也就成了抽象类。因为它继承了基类的抽象函数,只要含有纯虚函数的类就是抽象类。纯虚函数已经在抽象类中定义了这个方法的声明,其它类中只能按照这个接口去实现。
3.该类(xaudioplay)做为工厂来创建对象(在头文件中定义),对象具体(xaudio)创建过程在其继承类中(在cpp中定义)
可能会几种播放器:基于QT,基于DIRECXT,根据不同的需要然后返回不同的继承类对象
4.C++提供static这个关键词对静态成员进行声明,静态成员函数和类的实例化无关,对于同一类来说,静态成员函数是共享的。而普通成员函数需要实例化才能调用,对于每一个实例来说,普通成员函数是自己独有的
:static xaudioplay* get();
在使用使通过类名来调用,而不是通过实例化后的类名来调用:
xaudioplay::get()->samplerate
2.xaudio.cpp
class audioplay :public xaudioplay
{
//还有个好处是可以将细节隐藏起来
//与qt相关的成员放在audioplay中
public:
QAudioOutput* output;
QIODevice* io;
//因为打开与关闭可能不在一个线程,所以要加锁
std::mutex mux;
virtual bool open()
{
close();
QAudioFormat fmt;
fmt.setSampleRate(samplerate);
fmt.setSampleSize(samplesize);//采样位数
fmt.setChannelCount(channels);
fmt.setCodec("audio/pcm");
fmt.setByteOrder(QAudioFormat::LittleEndian);
fmt.setSampleType(QAudioFormat::UnSignedInt);
mux.lock();
output = new QAudioOutput(fmt);
io = output->start();//开始播放
mux.unlock();
if (io)
return true;
return false;
}
virtual bool close()
{
mux.lock();
if (io)
{
io->close();
//io空间是由output产生的,所以不需要delete,当deleteoutput时会自动删除这块空间
//“由谁产生就谁释放”
io = 0;
}
if (output)
{
output->stop();
delete output;
output = 0;
}
mux.unlock();
return true;
}
virtual bool write(const unsigned char* data, int datasize)
{
if (!data || datasize <= 0)return false;
mux.lock();
if (!output||!io)
{
mux.unlock();
return 0;
}
int size=io->write((char*)data, datasize);
if (size != datasize)
{
mux.unlock();
return false;
}
mux.unlock();
return true;
}
virtual int Getfree()
{
mux.lock();
if (!output)
{
mux.unlock();
return 0;
}
int free = output->bytesFree();
mux.unlock();
return free;
}
};
xaudioplay* xaudioplay::get()
{
static audioplay play;
return &play;
}
xaudioplay::xaudioplay()
{
}
xaudioplay::~xaudioplay()
{
}
本例中父类中只有get()没有定义为虚函数,get函数做为父类与子类的桥梁,工厂中的传送带
通过父类xxaudioplay的get()方法定义一个其子类,将其设为static,调用期间该不会销毁,保证多次调用都是同一个对象,用法:xaudioplay::get()->open()
3.main
在线程类中修改
class testtread :public QThread
{
public:
void init()
{
const char* path = "E:\\ffmpeg\\test222.mp4";
cout << "demux.Open = " << demux.Open(path) << endl;
cout << "vdecode.open()" << vdecode.open(demux.CopyVPara()) << endl;
cout << "adecode.open()" << adecode.open(demux.CopyAPara()) << endl;
cout << "reasmple.open()" << resample.Open(demux.CopyAPara()) << endl;
xaudioplay::get()->channels = demux.channels;
xaudioplay::get()->samplerate = demux.sampletrate;
cout << "xaudioplay::get()->open()" << xaudioplay::get()->open()<<endl;
}
unsigned char *pcm = new unsigned char[1024 * 1024*1024];
void run()
{
for (;;)
{
AVPacket* pkt = demux.readfz();
if (demux.isvideo(pkt) == true)
{
vdecode.send(pkt);
AVFrame* frame = vdecode.receive();
video->Repaint(frame);
}
else
{
adecode.send(pkt);
AVFrame* frame = adecode.receive();
int len = resample.Resample(frame, pcm);
cout<<"resample大小:"<<len<<endl;
while (len > 0)
{
if (xaudioplay::get()->Getfree() >= len)
{
xaudioplay::get()->write(pcm, len);
break;
}
msleep(5);
}
}
if (!pkt)break;
}
}
xdemux demux;
xvideowidget* video;
xresample resample;
protected:
xdecode vdecode;
xdecode adecode;
};
resample.open()的参数是解码获得的,整个媒体文件只有一份,
resample.resample()的参数是AVFrame,是在循环里依次对解码得到的frame进行处理,每从解码队列里获得一个音频packet,就对这个packet进行重采样,并获得重采样后每个音频通道每次采样的比特数,当用QAudioOutput类的方法成员Getfree()判断空余空间中比特数,当前者小于后者时便将pcm(重采样后的结果放在pcm中)进行write并播放
6声道媒体文件播放全是杂音,使用ffmpeg转成2声道就可以正常播放(暂时不知道为什么)
本文介绍了如何使用C++的抽象类和纯虚函数实现音频播放器的工厂模式,展示了xaudioplay基类与派生类audioplay的交互,以及如何根据不同需求动态创建不同类型的播放器实例。
2944

被折叠的 条评论
为什么被折叠?



