音视频通话 2.0
Android
动态与公告
更新日志(V4.6)
更新日志(V5)
活动与公告
【活动】音视频通话内容安全检测限时补贴
【活动】赠送100万分钟音视频通话时长
【邀测】音视频通话2.0V5.3邀请公测
新手接入指南
产品简介
产品介绍
功能特性
产品优势
应用场景
基本概念
使用限制
性能指标
产品计费
按量计费
资源包
体验 Demo
下载 SDK 和示例代码
升级指南
快速开始
快速跑通 Sample Code
接入流程
创建应用
开通服务
集成 SDK
实现音视频通话
Token 鉴权
高级 Token 鉴权
基础功能
设置音频属性
设置视频属性
设置视频旋转方向
设置通话音量
屏幕共享
音频共享
监测发言者音量
通话前网络质量探测
通话中质量监测
进阶功能
音频管理
客户端音频录制
原始音频数据
美声变声与混响
耳返
自定义音频采集与渲染
音效与伴音
设置音频订阅优先级
音频裸流传输
媒体补充增强信息SEI
视频管理
视频截图
水印
云信美颜
相芯美颜
自定义视频采集
虚拟背景
视频图像畸变矫正
视频裸流传输
多房间管理
设备管理
视频设备管理
音频设备管理
媒体流管理
跨房间媒体流转发
媒体流加密
视频流回退
云端录制
使用云代理
本地服务端录制
AI 融合功能
AI 超分
AI 降噪
场景实践
1 对 1 娱乐社交
语聊房
PK连麦
在线教育
互联网问诊
最佳实践
音视频参数配置推荐
房间连接状态管理
实现音视频安全检测
轻松构建本土Clubhouse
API 参考
Android API 参考
服务端 API
错误码(V5)
错误码(V4.x)
控制台指南
常见问题处理
FAQ
错题集
获取音频 Dump 文件
音频常见问题排查
视频常见问题排查
服务协议

原始音频数据

更新时间: 2023/05/17 10:31:58

NERTC SDK 的音频模块会严格控制声音设备的采集和播放逻辑,同时支持对采集到的音视频原始数据进行自定义的前处理和后处理,获取想要的播放效果。适用于非标设备接入、自定义音频效果、语音处理、语音识别等场景。

  • 前处理:在音频数据发送到编码器前获取原始的音频数据进行修改,主要针对本地麦克风采集到的音频数据或自定义外部音频流。
  • 后处理:即在音频数据发送给解码器后获取原始的音频数据进行修改,主要针对接收到的远端用户音频数据。

NERTC SDK 通过提供 NERtcAudioFrameObserver 类,实现采集、修改原始音频数据功能。

前提条件

在使用原始数据处理功能前,请确保您已在项目中实现基本的实时音视频功能。

注意事项

技术原理

rawdata.png

实现方法

API 调用时序

以实现修改采集音频的音频数据为例,API 调用时序如下图所示。

sequenceDiagram
    participant 应用层
    participant NERtcSDK
    应用层->>NERtcSDK: setRecordingAudioFrameParameters
    应用层->>NERtcSDK: setAudioFrameObserver
    NERtcSDK-->>应用层: onRecordFrame
    Note over 应用层, NERtcSDK: 自行处理音频数据
    应用层->>NERtcSDK: onRecordFrame

配置步骤

  1. 设置回调的音频采样率。

  2. 调用 setAudioFrameObserver 方法注册语音观测器,并在该方法中实现一个 NERtcEngineAudioFrameObserver 类。

  3. SDK 返回回调。

  4. 用户拿到音频数据后,需要根据场景自行进行处理。

  5. 完成音频数据处理后,您可以直接进行自播放,或根据场景需求再通过 onRecordFrameonPlaybackFrame 回调发送给 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 接收远端播放的音频数据帧回调
此文档是否对你有帮助?
有帮助
我要吐槽
  • 前提条件
  • 注意事项
  • 技术原理
  • 实现方法
  • API 调用时序
  • 配置步骤
  • 示例项目源码
  • 示例代码
  • API 参考