⑴ iOS Audio Queue播放PCM音频流
PCM音频流播放主要步骤如下:
播放PCM音频流前,我们首先需要确定播放的PCM音频的格式信息,iOS中,有一个结构体专用于描述音频格式信息,AudioStreamBasicDescription结构体,组成如下:
我预先录制好一个PCM文件,根据 https://www.jianshu.com/p/15d95e593451 录制的音频文件。
根据我们的PCM文件,我们设置参数如下:
格式确定后我们创建Audio Queue对象,使用如下方法:
参数解释:
音频格式填入上一步创建的音频格式结构体即可,回调函数指针填入后面创建的函数名即可,自定义回调函数数据填入自己需要的对象即可,回调函数的RunLoop和RunLoopMode无特殊要求填写NULL即使用默认值,保留保留标志位写0。创建AudioQueueRef对象成功则返回0,失败则返回其他错误值。
创建音频缓冲区函数如下:
参数解释:
缓冲区创建数量越少则延迟越小,同时同步要求更高。这里就创建三个即可,可根据自己需求进行调整。
函数的第一个参数为我们创建的AudioQueueRef对象,第二个参数填NULL则代表即可开始,我们填写NULL即可。函数返回0则代表成功。
我们先从文件中依次循环读取二进制数据进行填充,填充函数如下:
参数解释:
inAQ参数填我们刚才创建的AudioQueueRef对象,inNumPacketDescs填0,inPacketDescs填NULL即可。inBuffer则为我们刚才创建的缓冲区对象,对象为结构体,属性如下:
我们为第二个和第三个属性赋值即可,根据从文件读取到的实际值填入即可,不可超过缓冲对象的最大缓存空间。
回调函数类型如下:
第一个参数为自定义的对象,第二个为我们创建的AudioQueueRef对象,第三个为已经处理完毕的AudioQueueBufferRef缓冲区对象。
我们把函数名称填入第二步的创建函数中的第二个参数即可。
当AudioQueueBufferRef播放完一个缓冲区对象的音频数据后就会调用回调函数,在回调函数返回已经播放了的缓冲区对象,我们的回调函数里面处理空的AudioQueueBufferRef对象,为空AudioQueueBufferRef对象重新填充新的数据,并重新调用AudioQueueEnqueueBuffer()函数填充到播放队列中去。循环调用回调函数,我们依次从文件读取数据循环填充,即实现播放功能。
当我们需要停止播放时,调用停止的方法即可,方法也非常简单,如下:
第一个参数为需要停止的AudioQueueRef对象,第二个参数表示是否需要立即停止,我们传入YES即可。当传入NO时,该函数会立即返回,但并不立即停止,而是处理完以填充在队里里面的缓存区对象后停止,停止为异步操作;传入YES时则会立即停止,并等待AudioQueue停止后返回,停止为同步操作。
如果我们有需要也可以在停止后立即重置播放队列,函数如下:
参数为我们需要重置播放的队列对象,该方法会重置AudioQueueRef对象,并清空正在播放的缓冲区对象,我们又可以重新开始填充播放新的音频数据。
最后欢迎大家留言交流,同时附上Demo和文档地址。
Demo地址: https://github.com/XMSECODE/ESAudioQueueDemo
AudioQueue框架的更详细的使用及文档可以查阅苹果官方文档: https://developer.apple.com/documentation/audiotoolbox/audio_queue_services
⑵ Android音视频【十二】使用OpenSLES和AudioTrack进行播放PCM
本节我们学习下如何播放pcm数据,在Android中有两种方法:一种是使用java层的 AudioTrack 方法,一种是使用底层的 OpenSLES 直接在 jni 层调用系统的 OpenSLES的c方法 实现。
两种使用场景不一样:
AudioTrack 一般用于 比如本地播放一个pcm文件/流,又或者播放解码后的音频的pcm流,API较简单。
OpenSLES 一般用于一些播放器中开发中,比如音频/视频播放器,声音/音频的播放采用的OpenSLES,一是播放器一般是c/c++实现,便于直接在c层调用OpenSLES的API,二也是如果用AudioTrack进行播放,务必会带来java和jni层的反射调用的开销,API较复杂。
可以根据业务自行决定来进行选择。
AudioTrack的方式使用较简单,直接在java层。
指定采样率,采样位数,声道数进行创建。
其中44100是采样率, AudioFormat.CHANNEL_OUT_STEREO 为双声道,还有 CHANNEL_OUT_MONO 单声道。 AudioFormat.ENCODING_PCM_16BIT 为采样位数16位,还有 ENCODING_PCM_8BIT 8位。 minBufferSize 是播放器缓冲的大小,也是根据采样率和采样位数,声道数 进行获取,只有满足最小的buffer才去操作底层进程播放。
最后一个参数mode。可以指定的值有 AudioTrack.MODE_STREAM 和 AudioTrack.MODE_STATIC 。
MODE_STREAM 适用于大多数的场景,比如动态的处理audio buffer,或者播放很长的音频文件,它是将audio buffers从java层传递到native层。音频播放时音频数据从Java流式传输到native层的创建模式。
MODE_STATIC 适用场景,比如播放很短的音频,它是一次性将全部的音频资源从java传递到native层。音频数据在音频开始播放前仅从Java传输到native层的创建模式。
是的,就这么一个方法。注意此方法是同步方法,是个耗时方法,一般是开启一个线程循环调用 write 方法进行写入。
注意在调用 write 方法前需要调用 audioTrack.play() 方法开始播放。
因为是pcm裸数据,无法像mediaplayer一样提供了API。所以需要自己处理下。可以利用 getPlaybackHeadPosition 方法。
getPlaybackHeadPosition() 的意思是返回以帧为单位表示的播放头位置
getPlaybackRate() 的意思是返回以Hz为单位返回当前播放采样率。
所以当前播放时间可以通过如下方式获取
OpenSLES:(Open Sound Library for Embedded Systems).
OpenSLES是跨平台是针对嵌入式系统精心优化的硬件音频加速API。使用OpenSLES进行音频播放的好处是可以不依赖第三方。比如一些音频或者视频播放器中都是用OpenSLES进行播放解码后的pcm的,这样免去了和java层的交互。
在Android中使用OpenSLES首先需要把Android 系统提供的so链接到外面自己的so。在CMakeLists.txt脚本中添加链接库OpenSLES。库的名字可以在 类似如下目录中
需要去掉lib
然后导入头文件即可使用了OpenSLES提供的底层方法了。
创建&使用的步骤大致分为:
一个 SLObjectItf 里面可能包含了多个Interface,获取Interface通过 GetInterface 方法,而 GetInterface 方法的地2个参数 SLInterfaceID 参数来指定到的需要获取Object里面的那个Interface。比如通过指定 SL_IID_ENGINE 的类型来获取 SLEngineItf 。我们可以通过 SLEngineItf 去创建各种Object,例如播放器、录音器、混音器的Object,然后在用这些Object去获取各种Interface去实现各种功能。
如上所说,SLEngineItf可以创建混音器的Object。
在创建播放器前需要创建音频的配置信息(比如采样率,声道数,每个采样的位数等)
开始播放后会不断的回调这个 pcmBufferCallBack 函数将音频数据压入队列
(*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, this);
如果想要暂停播放参数直接设置为SL_PLAYSTATE_PAUSED,若暂停后继续播放设置参数为SL_PLAYSTATE_PLAYING即可。若想要停止播放参数设置为SL_PLAYSTATE_STOPPED即可。
首先获取播放器的用于控制音量的接口SLVolumeItf pcmVolumePlay
然后动态设置
首先也是获取播放器的用于控制音量的接口SLMuteSoloItf pcmMutePlay
然后动态设置
看起来控制还是蛮简单的哈。先熟悉这么多,OpenSLES还是蛮强大的。
https://github.com/ta893115871/PCMPlay
⑶ google为什么要开源webrtc
google开源webrtc的理由如下:【点击免费试用,0成本启动】
WebRTC(Web Real-Time Communication)项目的最终目的主要是让Web开发者能够基于浏览器(ChromeFireFox...)轻易快捷开发出丰富的实时多媒体应用,而无需下载安装任何插件,Web开发者也无需关注多媒体的数字信号处理过程,只需编写简单的Javascript程序即可实现,W3C等组织正在制定Javascript 标准API,目前是WebRTC 1.0版本,Draft状态;另外WebRTC还希望能够建立一个多互联网浏览器间健壮的实时通信的平台,形成开发者与浏览器厂商良好的生态环境。同时,Google也希望和致力于让WebRTC的技术成为HTML5标准之一,可见Google布局之深远。
想要了解更多关于webrtc的相关信息,推荐咨询ZEGO即构科技。公司自成立伊始,就专注自研音视频引擎,在音频前处理、网络自适应和跨平台兼容性等方面,达到国际一流水平,同时充分利用基础云服务商的能力,构建了MSDN海量有序自学习数据网络,服务覆盖全球,涵盖上百个音视频互动业务场景,单日时长突破30亿分钟。
⑷ PCM 音频数据使用和处理
什么是PCM
PCM: PCM(Pulse Code Molation----脉码调制录音)。所谓PCM录音就是将声音等模拟信号变成符号化的脉冲列,再予以记录。PCM信号是由[1]
PCM 采样、位深与码率
PCM 数据的一些简单处理
1:计算分贝 音频数据与大小
2: 采样转换
3:位深转换
⑸ WebRTC 的音频网络对抗概述
WebRTC 音频数据处理中,期望可以实现音频数据处理及传输,延时低,互动性好,声音平稳无抖动,码率低消耗带宽少等。在数据传输上,WebRTC 采用基于 UDP 的 RTP/RTCP 协议,RTP/RTCP 本身不提供数据的可靠传输及质量保障。公共互联网这种分组交换网络,天然具有数据包传输的丢失、重复、乱序及延时等问题。WebRTC 音频数据处理的这些目标很难同时实现,WebRTC 的音频网络对抗实现中针对不同情况对这些目标进行平衡。
这里更仔细地看一下 WebRTC 音频数据处理管线,并特别关注与音频网络对抗相关的逻辑。
前面在 WebRTC 的音频数据编码及发送控制管线 一文中分析了 WebRTC 的音频数据编码及发送控制相关逻辑,这里再来看一下 WebRTC 的音频数据接收及解码播放过程。
WebRTC 的音频数据接收处理的概念抽象层面的完整流程大体如下:
对于 WebRTC 的音频数据接收处理过程, webrtc::AudioDeviceMole 负责把声音 PCM 数据通过系统接口送进设备播放出来。 webrtc::AudioDeviceMole 内部一般会起专门的播放线程,由播放线程驱动整个解码播放过程。 webrtc::AudioTransport 作为一个适配和胶水模块,它把音频数据播放和 webrtc::AudioProcessing 的音频数据处理及混音等结合起来,它通过 webrtc::AudioMixer 同步获取并混音各个远端音频流,这些混音之后的音频数据除了返回给 webrtc::AudioDeviceMole 用于播放外,还会被送进 webrtc::AudioProcessing ,以作为回声消除的参考信号。 webrtc::AudioMixer::Source / webrtc::AudioReceiveStream 为播放过程提供解码之后的数据。RTCP 反馈在 webrtc::AudioMixer::Source / webrtc::AudioReceiveStream 中会通过 webrtc::Transport 发送出去。 webrtc::Transport 也是一个适配和胶水模块,它通过 cricket::MediaChannel::NetworkInterface 实际将数据包发送网络。 cricket::MediaChannel 从网络中接收音频数据包并送进 webrtc::AudioMixer::Source / webrtc::AudioReceiveStream 。
如果将音频数据接收处理流水线上的适配和胶水模块省掉,音频数据接收处理流水线将可简化为类似下面这样:
webrtc::AudioMixer::Source / webrtc::AudioReceiveStream 是整个过程的中心,其实现位于 webrtc/audio/audio_receive_stream.h / webrtc/audio/audio_receive_stream.cc ,相关的类层次结构如下图:
在 RTC 中,为了实现交互和低延迟,音频数据接收处理不能只做包的重排序和解码,它还要充分考虑网络对抗,如 PLC 及发送 RTCP 反馈等,这也是一个相当复杂的过程。WebRTC 的设计大量采用了控制流与数据流分离的思想,这在 webrtc::AudioReceiveStream 的设计与实现中也有体现。分析 webrtc::AudioReceiveStream 的设计与实现时,也可以从配置及控制,和数据流两个角度来看。
可以对 webrtc::AudioReceiveStream 执行的配置和控制主要有如下这些:
对于数据流,一是从网络中接收到的数据包被送进 webrtc::AudioReceiveStream ;二是播放时, webrtc::AudioDeviceMole 从 webrtc::AudioReceiveStream 获得解码后的数据,并送进播放设备播放出来;三是 webrtc::AudioReceiveStream 发送 RTCP 反馈包给发送端以协助实现拥塞控制,对编码发送过程产生影响。
webrtc::AudioReceiveStream 的实现中,最主要的数据处理流程 —— 音频数据接收、解码及播放过程,及相关模块如下图:
这个图中的箭头表示数据流动的方向,数据在各个模块中处理的先后顺序为自左向右。图中下方红色的框中是与网络对抗密切相关的逻辑。
webrtc::AudioReceiveStream 的实现的数据处理流程中,输入数据为音频网络数据包和对端发来的 RTCP 包,来自于 cricket::MediaChannel ,输出数据为解码后的 PCM 数据,被送给 webrtc::AudioTransport ,以及构造的 RTCP 反馈包,如 TransportCC、RTCP NACK 包,被送给 webrtc::Transport 发出去。
webrtc::AudioReceiveStream 的实现内部,音频网络数据包最终被送进 NetEQ 的缓冲区 webrtc::PacketBuffer 里,播放时 NetEQ 做解码、PLC 等,解码后的数据提供给 webrtc::AudioDeviceMole 。
这里先来看一下, webrtc::AudioReceiveStream 实现的这个数据处理流水线的搭建过程。
webrtc::AudioReceiveStream 实现的数据处理管线是分步骤搭建完成的。我们围绕上面的 webrtc::AudioReceiveStream 数据处理流程图 来看这个过程。
在 webrtc::AudioReceiveStream 对象创建,也就是 webrtc::voe::(anonymous namespace)::ChannelReceive 对象创建时,会创建一些关键对象,并建立部分对象之间的联系,这个调用过程如下:
webrtc::AudioReceiveStream 通过 webrtc::Call 创建,传入 webrtc::AudioReceiveStream::Config,其中包含与 NACK、jitter buffer 最大大小、payload type 与 codec 的映射相关,及 webrtc::Transport 等各种配置。
webrtc::voe::(anonymous namespace)::ChannelReceive 对象的构造函数如下:
webrtc::voe::(anonymous namespace)::ChannelReceive 对象的构造函数的执行过程如下:
图中标为绿色的模块为这个阶段已经接入 webrtc::voe::(anonymous namespace)::ChannelReceive 的模块,标为黄色的则为那些还没有接进来的模块;实线箭头表示这个阶段已经建立的连接,虚线箭头则表示还没有建立的连接。
在 ChannelReceive 的 () 函数中, webrtc::PacketRouter 被接进来:
这个操作也发生在 webrtc::AudioReceiveStream 对象创建期间。 ChannelReceive 的 () 函数的实现如下:
这里 webrtc::PacketRouter 和 webrtc::MoleRtpRtcpImpl2 被连接起来,前面图中标号为 5 的这条连接也建立起来了。NetEQ 在需要音频解码器时创建音频解码器,这个过程这里不再赘述。
这样 webrtc::AudioReceiveStream 内部的数据处理管线的状态变为如下图所示:
webrtc::AudioReceiveStream 的生命周期函数 Start() 被调用时, webrtc::AudioReceiveStream 被加进 webrtc::AudioMixer :
这样 webrtc::AudioReceiveStream 的数据处理管线就此搭建完成。整个音频数据处理管线的状态变为如下图所示:
WebRTC 音频数据接收处理的实现中,保存从网络上接收的音频数据包的缓冲区为 NetEQ 的 webrtc::PacketBuffer ,收到音频数据包并保存进 NetEQ 的 webrtc::PacketBuffer 的过程如下面这样:
播放时, webrtc::AudioDeviceMole 最终会向 NetEQ 请求 PCM 数据,此时 NetEQ 会从 webrtc::PacketBuffer 中取出数据包并解码。网络中传输的音频数据包中包含的音频采样点和 webrtc::AudioDeviceMole 每次请求的音频采样点不一定是完全相同的,比如采样率为 48kHz 的音频, webrtc::AudioDeviceMole 每次请求 10ms 的数据,也就是 480 个采样点,而 OPUS 音频编解码器每个编码帧中包含 20ms 的数据,也就是 960 个采样点,这样 NetEQ 返回 webrtc::AudioDeviceMole 每次请求的采样点之后,可能会有解码音频数据的剩余,这需要一个专门的 PCM 数据缓冲区。这个数据缓冲区为 NetEQ 的 webrtc::SyncBuffer 。
webrtc::AudioDeviceMole 请求播放数据的大体过程如下面这样:
更加仔细地审视 WebRTC 的音频数据处理、编码和发送过程,更完整地将网络对抗考虑进来, WebRTC 的音频数据处理、编码和发送过程,及相关模块如下图:
在 WebRTC 的音频数据处理、编码和发送过程中,编码器对于网络对抗起着巨大的作用。WebRTC 通过一个名为 audio network adapter (ANA) 的模块,根据网络状况,对编码过程进行调节。
pacing 模块平滑地将媒体数据发送到网络,拥塞控制 congestion control 模块通过影响 pacing 模块来影响媒体数据发送的过程,以达到控制拥塞的目的。
由 WebRTC 的音频采集、处理、编码和发送过程,及音频的接收、解码、处理及播放过程,可以粗略梳理出 WebRTC 的音频网络对抗的复杂机制:
没看到 WebRTC 有音频带外 FEC 机制的实现。
参考文章
干货|一文读懂腾讯会议在复杂网络下如何保证高清音频
Done.
⑹ 全民k歌pcm文件怎么播放
全民k歌pcm文件播放方法:
1、把app的视频文件下载下来,在手机上找到那个app的文件夹,把下载的pcm格式导出到电脑上。
2、在电脑上下一个pcm格式批量提取器,就可以实现把视频下载到电脑上用常用的视频格式给打开了。
⑺ iOS语音对讲(三)FFmpeg实时解码AAC并播放PCM
具体过程如下:
初始化解码器
解码AAC,block中返回解码后的PCM
释放解码器
播放PCM使用 AudioQueue ,具体流程:
通过上图可以得知,Audio Queue的播放流程即是一个 生产者与消费者 的模式:
创建多个Buffer容器,依次填充(生产)Buffer后插入队列中,开始播放(消费),然后通过回调将消费过的Buffer reuse,循环整个过程。
创建Buffer和Queue,设置参数并开始执行队列
填充Buffer
在回调中将容器状态设置为空,用于循环复用
⑻ 如何取出webrtc里面pcm数据输出
取出webrtc里面pcm数据输出推荐选择ZEGO即构科技,ZEGO即构科技是一家全球云通讯服务商,提供优质的服务,为客户解决所需解决的问题。【点击免费试用,0成本启动】
webrtc的语音QoS机制几乎可以说是行业的标杆,其实现的方式主要融入了三种技术,包括丢包重传(NACK),前向纠错(FEC)以及原GIPS公司的网络均衡器(NetEqualizer,简称NetEQ)。前两种都是在牺牲一定成本的前提下,尽最大努力降低丢包率,而NetEQ是在前两者的基础上降低延迟、弥补丢包。
想要了解更多关于webrtc的相关信息,推荐咨询ZEGO即构科技。ZEGO即构科技自主研发的高音质语音视频引擎,能够提供实时清晰的多人语音视频通话。支持多路视频画面,保障每一路语音视频都清晰流畅提供端到端的SDK、分布式转码、接入鉴权云服务接入、摆脱运维、轻松支撑海量用户运营。
⑼ tts如何拿pcm数据
1. 开始TTS合成.
2. 合成出来第一段PCM数据后调用tts_media_play函数,开始播放,设置callback函数Pcm_play_callback。(在开始播放后,tts_media_play只应被调用一次,后面
3. TTS继续合成数据,合成完后就放合适大小(
⑽ 播放PCM文件
如果你的mac的参数样式和我的是一样的话,那么执行上面的命令,应该就能正常播放了之前录制的pcm,如果不是的话,需要你对照自己的mac参数来进行相关处理
在之前执行ffmpeg的时候,已经安装了SDL,安装目录位于/usr/local/Cellar/sdl2
安装ffmpeg
如果没有安装这个目录,可以执行brew install sdl2
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void showVersion() {
SDL_version version;
SDL_VERSION(&version);
qDebug() << version.major << version.minor << version.patch;
}
void MainWindow::on_playButton_clicked()
{
if (_playThread) { // 停止播放
_playThread->requestInterruption();
_playThread = nullptr;
ui->playButton->setText("开始播放");
} else { // 开始播放
_playThread = new PlayThread(this);
_playThread->start();
// 监听线程的结束
connect(_playThread, &PlayThread::finished,
this {
_playThread = nullptr;
ui->playButton->setText("开始播放");
});
ui->playButton->setText("停止播放");
}
}
···
然后Run程序,就可以发现成功播放了pcm文件