1.xaudio.h
#pragma once
class xaudioplay
{
public:
static xaudioplay* get();
xaudioplay();
virtual ~xaudioplay();
virtual bool open()=0;
virtual bool close() = 0;
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
{
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 = 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声道就可以正常播放(暂时不知道为什么)
|