原始音频数据

更新时间: 2024/03/15 17:20:41

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

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

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

注意事项

前提条件

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

技术原理

rawdata.png

实现方法

API 调用时序

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

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

配置步骤

  1. 加入房间前基于 INERtcAudioFrameObserver 接口类实现一个 NERtcAudioFrameObserver 类,并调用 setAudioFrameObserver 方法注册语音观测器。

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

  3. SDK 返回回调。

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

  5. 完成音频数据处理后,您可以直接进行自播放,或根据场景需求再通过 onAudioFrameDidRecordonAudioFrameWillPlayback 回调发送给 SDK。

示例代码

你可以参考下面的示例代码片段,在项目中实现音频原始数据功能:

class AudioFrameObserver : public INERtcAudioFrameObserver
{
public:
    void onAudioFrameDidRecord(NERtcAudioFrame *frame) {
        FILE *fd = getFileDescr("Record", frame->format);
        
        if (NULL != fd) {
            fwrite(frame->data, frame->format.bytes_per_sample * frame->format.channels,
                   frame->format.samples_per_channel, fd);
        }
    }

    void onAudioFrameWillPlayback(NERtcAudioFrame *frame) {
        FILE *fd = getFileDescr("Playback", frame->format);

        if (NULL != fd) {
            fwrite(frame->data, frame->format.bytes_per_sample * frame->format.channels,
                   frame->format.samples_per_channel, fd);
        }
    }

    void onMixedAudioFrame(nertc::NERtcAudioFrame *frame) {
        FILE *fd = getFileDescr("Mixed", frame->format);

        if (NULL != fd) {
            fwrite(frame->data, frame->format.bytes_per_sample * frame->format.channels,
                   frame->format.samples_per_channel, fd);
        }
    }

    void onPlaybackAudioFrameBeforeMixing(nertc::uid_t uid, nertc::NERtcAudioFrame *frame) {
        FILE *fd = getFileDescr(std::to_string(uid), frame->format);

        if (NULL != fd) {
            fwrite(frame->data, frame->format.bytes_per_sample * frame->format.channels,
                   frame->format.samples_per_channel, fd);
        }
    }

    AudioFrameObserver(const std::string &record_file_dir) : m_record_file_dir(record_file_dir) {}

    virtual ~AudioFrameObserver()
    {
        for (auto &fd : m_fds)
        {
            fclose(fd.second.fd);
            fd.second.fd = NULL;
        }
        m_fds.clear();
    }

private:
    struct file_descr {
        FILE *fd;
        nertc::NERtcAudioFormat fmt;
    };

    FILE *getFileDescr(const std::string &id, const nertc::NERtcAudioFormat &fmt) {
        if (m_fds.end() == m_fds.find(id)) {
            m_fds[id] = file_descr();
            m_fds[id].fd = NULL;
        } else if ((m_fds[id].fmt.sample_rate != fmt.sample_rate) ||
                   (m_fds[id].fmt.channels != fmt.channels) ||
                   (m_fds[id].fmt.type != fmt.type)) {
            if (NULL != m_fds[id].fd) {
                fclose(m_fds[id].fd);
                m_fds[id].fd = NULL;
            }
        }

        if (NULL == m_fds[id].fd) {
            time_t now = time(0);
            tm *ltm = localtime(&now);
            std::ostringstream oss;
            oss << m_record_file_dir << "/audio-frame-" << id << "-" << fmt.sample_rate << "-ch" << fmt.channels;
            oss << "-" << ltm->tm_year + 1900;
            oss << std::setfill('0') << std::setw(2) << ltm->tm_mon + 1;
            oss << std::setfill('0') << std::setw(2) << ltm->tm_mday;
            oss << std::setfill('0') << std::setw(2) << ltm->tm_hour;
            oss << std::setfill('0') << std::setw(2) << ltm->tm_min;
            oss << std::setfill('0') << std::setw(2) << ltm->tm_sec;
            oss << ".pcm";

            m_fds[id].fd = fopen(oss.str().c_str(), "wb");
            m_fds[id].fmt = fmt;
        }

        return m_fds[id].fd;
    }

    std::map<std::string /*id*/, file_descr> m_fds;
    std::string m_record_file_dir;
};

AudioFrameObserver *audioFrameObserver = new AudioFrameObserver("/dir/for/recor_file");
rtc_engine_->setAudioFrameObserver(audioFrameObserver);

API 参考

方法 功能描述
setRecordingAudioFrameParameters 设置回调的采集音频采样率
setPlaybackAudioFrameParameters 设置回调的播放音频采样率
setMixedAudioFrameParameters 设置回调的混音音频采样率
setAudioFrameObserver 注册语音观测器
onAudioFrameDidRecord 接收本端输入的采集音频数据回调
onAudioFrameWillPlayback 接收本端输入的播放音频数据播放回调
onMixedAudioFrame 接收采集与播放音频混合数据帧回调
onPlaybackAudioFrameBeforeMixing 接收远端播放的音频数据帧回调
此文档是否对你有帮助?
有帮助
去反馈
  • 注意事项
  • 前提条件
  • 技术原理
  • 实现方法
  • API 调用时序
  • 配置步骤
  • 示例代码
  • API 参考