原始音频数据
更新时间: 2024/09/18 16:26:13
NERTC SDK 的音频模块会严格控制声音设备的采集和播放逻辑,同时支持对采集到的音视频原始数据进行自定义的前处理和后处理,获取想要的播放效果。适用于非标设备接入、自定义音频效果、语音处理、语音识别等场景。
- 前处理:在音频数据发送到编码器前获取原始的音频数据进行修改,主要针对本地麦克风采集到的音频数据或自定义外部音频流。
- 后处理:即在音频数据发送给解码器后获取原始的音频数据进行修改,主要针对接收到的远端用户音频数据。
NERTC SDK 通过提供 NERtcAudioFrameObserver
类,实现采集、修改原始音频数据功能。
前提条件
在使用原始数据处理功能前,请确保您已在项目中实现基本的实时音视频功能。
注意事项
- 采集回调
onRecordFrame
、播放回调onPlaybackFrame
中的原始音频数据可进行处理,例如美声变声。 - 混音回调
onMixedAudioFrame
和某一用户的播放回调onPlaybackAudioFrameBeforeMixingWithUserID
中的原始音频数据不能进行处理。
技术原理
实现方法
API 调用时序
以实现修改采集音频的音频数据为例,API 调用时序如下图所示。
sequenceDiagram
participant 应用层
participant NERtcSDK
应用层->>NERtcSDK: setRecordingAudioFrameParameters
应用层->>NERtcSDK: setAudioFrameObserver
NERtcSDK-->>应用层: onRecordFrame
Note over 应用层, NERtcSDK: 自行处理音频数据
应用层->>NERtcSDK: onRecordFrame
配置步骤
-
设置回调的音频采样率。
- 调用
setRecordingAudioFrameParameters
方法修改回调的采集音频采样率,并将回调的音频数据设置为只读模式或读写模式。 - 调用
setPlaybackAudioFrameParameters
方法修改回调的播放音频采样率,并将回调的音频数据设置为只读模式或读写模式。 - 调用
setMixedAudioFrameParameters
方法,设置onMixedAudioFrame
回调中的混音音频采样率。
- 调用
-
调用
setAudioFrameObserver
方法注册语音观测器,并在该方法中实现一个NERtcEngineAudioFrameObserver
类。 -
SDK 返回回调。
- SDK 收到输入的采集数据和播放的音频数据时,返回
onRecordFrame
和onPlaybackFrame
回调。 - SDK 收到音频采集与播放混合后数据帧时,返回
onMixedAudioFrame
回调;SDK 收到某一远端用户播放的音频数据时,返回onPlaybackAudioFrameBeforeMixingWithUserID
回调。
- SDK 收到输入的采集数据和播放的音频数据时,返回
-
用户拿到音频数据后,需要根据场景自行进行处理。
-
完成音频数据处理后,您可以直接进行自播放,或根据场景需求再通过
onRecordFrame
和onPlaybackFrame
回调发送给 SDK。
示例项目源码
网易云信提供原始音频数据的示例项目源码 RawAudioCallback ,您可以参考该源码实现采集和修改原始音频数据。
示例代码
///设置音频回调参数
NERtcAudioFrameRequestFormat formatMix = new NERtcAudioFrameRequestFormat();
formatMix.setChannels(channel);
formatMix.setSampleRate(sampleRate);
formatMix.setOpMode(readOnly.isChecked() ? NERtcAudioFrameOpMode.kNERtcAudioFrameOpModeReadOnly : NERtcAudioFrameOpMode.kNERtcAudioFrameOpModeReadWrite);
Log.i(TAG, "AudioCallback ,channel: "+formatMix.getChannels()+ " Mixed Sample:" + formatMix.getSampleRate() + " ReadWrite:" + formatMix.getOpMode());
NERtcEx.getInstance().setMixedAudioFrameParameters(formatMix);
NERtcEx.getInstance().setPlaybackAudioFrameParameters(formatMix);
NERtcEx.getInstance().setRecordingAudioFrameParameters(formatMix);
NERtcEx.getInstance().setAudioFrameObserver(observer);
///音频数据回调处理
observer = new NERtcAudioFrameObserver() {
@Override
public void onRecordFrame(NERtcAudioFrame audioFrame) {
try {
if(!isAudioCallbackDump){
return;
}
if (pcmCallbackRecordDump == null) {
pcmCallbackRecordDump = createPCMDump("Record_" +audioFrame.getFormat().getChannels()
+"_"+ audioFrame.getFormat().getSampleRate());
if(pcmCallbackMixDump == null) {
Log.e(TAG, "create dump file failed!");
return;
}
}
byte[] remaining = new byte[audioFrame.getData().remaining()];
audioFrame.getData().get(remaining);
pcmCallbackRecordDump.write(remaining);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onPlaybackFrame(NERtcAudioFrame audioFrame) {
if(!isAudioCallbackDump){
return;
}
try {
if (pcmCallbackPlaybackDump == null) {
pcmCallbackPlaybackDump = createPCMDump("PlayBack_" +audioFrame.getFormat().getChannels()
+"_"+ audioFrame.getFormat().getSampleRate());
if(pcmCallbackMixDump == null) {
Log.e(TAG, "create dump file failed!");
return;
}
}
byte[] remaining = new byte[audioFrame.getData().remaining()];
audioFrame.getData().get(remaining);
pcmCallbackPlaybackDump.write(remaining);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onPlaybackAudioFrameBeforeMixingWithUserID(long userID, NERtcAudioFrame audioFrame) {
if(!isAudioCallbackDump){
return;
}
try {
if(mRemoteUserMap.get(userID) != null){
if(mRemoteUserMap.get(userID).audioPCMDump == null){
mRemoteUserMap.get(userID).audioPCMDump = createPCMDump("PlayBackUid_"+ userID +"_"+ audioFrame.getFormat().getChannels()
+"_"+ audioFrame.getFormat().getSampleRate());
if(mRemoteUserMap.get(userID).audioPCMDump == null){
Log.e(TAG, "create dump file failed!");
return;
}
}
byte[] remaining = new byte[audioFrame.getData().remaining()];
audioFrame.getData().get(remaining);
mRemoteUserMap.get(userID).audioPCMDump.write(remaining);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onMixedAudioFrame(NERtcAudioFrame audioFrame) {
if(!isAudioCallbackDump){
return;
}
try {
if (pcmCallbackMixDump == null) {
pcmCallbackMixDump = createPCMDump("Mix_" +audioFrame.getFormat().getChannels()
+"_"+ audioFrame.getFormat().getSampleRate());
if(pcmCallbackMixDump == null) {
Log.e(TAG, "create dump file failed!");
return;
}
}
byte[] remaining = new byte[audioFrame.getData().remaining()];
audioFrame.getData().get(remaining);
pcmCallbackMixDump.write(remaining);
} catch (Exception e) {
e.printStackTrace();
}
}
};
API 参考
方法 | 功能描述 |
---|---|
setRecordingAudioFrameParameters |
设置回调的采集音频采样率 |
setPlaybackAudioFrameParameters |
设置回调的播放音频采样率 |
setMixedAudioFrameParameters |
设置回调的混音音频采样率 |
setAudioFrameObserver |
注册语音观测器 |
onRecordFrame |
接收本端输入的采集音频数据回调 |
onPlaybackFrame |
接收本端输入的播放音频数据播放回调 |
onMixedAudioFrame |
接收采集与播放音频混合数据帧回调 |
onPlaybackAudioFrameBeforeMixingWithUserID |
接收远端播放的音频数据帧回调 |
此文档是否对你有帮助?