播放有声素材
包含 Movie 功能的 PAG 企业版(包名带有 movie 后缀)集成了音频读取与播放的能力,用于解码播放带音频的 PAG 素材和占位图一键替换进来的视频;使用方可以直接使用 PAGView 呈现声音,或者读取 PCM 音频数据自行处理。
包含音频数据的 PAG 素材
当 PAGFile 的 audioBytes 不为空时,代表这个 pag 文件包含了音频数据。
在 PAG 中,音频数据由 AAC 编码,MPEG-4 封装。使用 PAG 的业务自己播放音频会比较繁琐。
因此 PAG 企业版加入音频解码与播放的能力,主要是支持音频播放的 PAGView,与方便获取 PCM 数据的PAGAudioReader。
UI 场景使用
在 UI 场景中,企业版的 PAGView 已经集成了音频播放能力,把编辑好的 PAGComposition 设置给 PAGView 就可播放音频。
PAGComposition 在设置后仍然可以编辑,比如添加或移除 PAGMovie,此时 PAGAudioReader 和 PAGView 会自动播放新的数据
PAGView 的详细文档参见:Android | iOS。
自行处理 PCM 数据
在一些其他场景,例如后编辑,或者其他需要自行获取 PCM 数据处理的场景,我们提供 PAGAudioReader 接口用于使用方直接读取 PAGComposition 实例的音频帧数据。
注意 PAGAudioReader 输出的 SampleData 格式是 PCMSigned16。
使用流程如下
1、构建 PAGAudioReader 实例
代码示例
android:
// 获取指定音频参数的 reader 实例
int sampleRate = 48000;
int sampleCount = 1024;
int channels = 2;
PAGAudioReader reader = PAGAudioReader.Make(sampleRate, sampleCount, channels);
iOS:
// 获取指定音频参数的 reader 实例
NSInteger sampleRate = 48000;
NSInteger sampleCount = 1024;
NSInteger channels = 2;
CGFloat volume = 1.0f; // 0 ~ 1.0f
PAGAudioReader *reader = [PAGAudioReader MakeWithSampleRate:sampleRate sampleCount:sampleCount channels:channels volume:1.0f];
2、设置 PAGComposition 实例
代码示例
android:
PAGAudioReader reader = PAGAudioReader.Make(sampleRate, sampleCount, channels);
PAGComposition composition = PAGFile.Load(getAssets(), selectedPAGFileName);
reader.setComposition(composition);
iOS:
PAGComposition *composition = [PAGFile Load:path];
[reader setComposition:composition];
3、seek(可选)
android:
// seek 到第5秒的位置
long positionUs = 5_000_000;
reader.seek(positionUs);
iOS:
NSInteger positionUs = 5*1000*1000;
[reader seek:positionUs];
4、读取音频
通过 PAGAudioReader 的 readNextSample 接口可以读取一帧音频,同时移动到下一帧的位置;
读取一个 PAGComposition 实例中完整的音频数据,只需要循环调用 readNextSample,直到所有音频数据都读取完毕。
因为 composition 允许实时修改,当 composition 没有音频数据时,audioReader 仍然会返回音频帧,此时音频中 data 数据均为0。
代码示例:
android:
if (reader.isEmpty()) {
// 如果 reader.isEmpty()返回 true,说明 composition 没有音频数据
return;
}
// 循环读取音频数据,直到读取完毕
PAGAudioSample audioFrame;
while ((audioFrame = reader.readNextSample()) != null
&& audioFrame.timestamp + audioFrame.duration < composition.duration()) {
audioFrame = reader.readNextSample();
playAudio(audioFrame);
}
iOS:
if ([reader isEmpty]) {
// 如果[reader isEmpty]返回 true,说明 composition 没有音频数据
return;
}
PAGAudioSample* audioFrame = nil;
while ((audioFrame = [reader readNextSample]) != null
&& audioFrame.timestamp + audioFrame.duration < composition.duration) {
audioFrame = reader.readNextSample();
dealWithAudio(audioFrame);
}