原始音频数据

更新时间: 2024/08/05 15:02:55

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

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

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

注意事项

前提条件

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

技术原理

rawdata.png

实现方法

API 调用时序

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

uml diagram

配置步骤

  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 参考