IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 播放器实战18 Xaudio打开音频 -> 正文阅读

[C++知识库]播放器实战18 Xaudio打开音频

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++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-02-14 20:56:33  更:2022-02-14 20:57:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:51:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码