获取原始音频数据

更新时间:
复制为 MD 格式

本文将介绍如何获取采集的原始音频数据,对这些数据进行处理。

示例代码

Android端获取原始音频数据Android/ARTCExample/AdvancedUsage/src/main/java/com/aliyun/artc/api/advancedusage/ProcessAudioRawData/ProcessAudioRawDataActivity.java

iOS端获取原始音频数据iOS/ARTCExample/AdvancedUsage/ProcessAudioRawData/ProcessAudioRawDataVC.swift

Harmony端获取原始音频数据Harmony/ARTCExample/entry/src/main/ets/pages/advancedusage/ProcessAudioRawDataPage.ets

前提条件

在设置视频配置之前,请确保达成以下条件:

功能实现

image

1.初始化 RTC 引擎

获取音频原始数据前,需要创建和初始化一个音视频引擎类。

2.实现音频数据回调方法

为了获取音频数据,需要实现相关回调方法,回调方法如下:

回调方法

相关含义

注意事项

onCapturedAudioFrame

本地采集的音频数据回调。

无。

onProcessCapturedAudioFrame

经过3A环节后的音频数据回调。

无。

onPublishAudioFrame

推流的音频数据回调。

只能设置为只读模式。

onPlaybackAudioFrame

本地播放的音频数据回调(可能包含多个远端用户混音)。

无。

onRemoteUserAudioFrame

指定远端用户的音频数据回调。

不能设置采样率和声道数,会跟随远端用户的设置,但是可以设置为读写模式。

Android

需要集成并实现AliRtcAudioFrameObserver类的相关方法,具体实现哪些回调请根据您的业务场景来决定。

private final AliRtcEngine.AliRtcAudioFrameObserver rtcAudioFrameObserver = new AliRtcEngine.AliRtcAudioFrameObserver() {
    @Override
    public boolean onCapturedAudioFrame(AliRtcEngine.AliRtcAudioFrame frame) {
        // 本地采集音频数据回调,根据业务场景进行处理
        Log.i(TAG, "onCaptureAudioFrame");
        return true;
    }

    @Override
    public boolean onProcessCapturedAudioFrame(AliRtcEngine.AliRtcAudioFrame frame) {
        // 3A后数据回调,根据业务场景进行处理
        Log.i(TAG, "onProcessCaptureAudioFrame");
        return true;
    }

    @Override
    public boolean onPublishAudioFrame(AliRtcEngine.AliRtcAudioFrame frame) {
        // 推流音频数据回调,根据业务场景进行处理
        Log.i(TAG, "onPublishAudioFrame");
        return true;
    }

    @Override
    public boolean onPlaybackAudioFrame(AliRtcEngine.AliRtcAudioFrame frame) {
        // 播放数据(混音后),根据业务场景进行处理
        Log.i(TAG, "onPlaybackAudioFrame");
        return true;
    }

    @Override
    @Deprecated
    public boolean onMixedAllAudioFrame(AliRtcEngine.AliRtcAudioFrame frame) {
        // 该方法已弃用
        return true;
    }

    @Override
    public boolean onRemoteUserAudioFrame(String uid, AliRtcEngine.AliRtcAudioFrame frame) {
        // 远端用户拉流音频数据,根据业务场景进行处理
        Log.i(TAG, "onRemoteUserAudioFrame");
        return true;
    }
};

iOS

为了获取原始音频数据,需要实现AliRtcAudioFrameDelegate中的回调方法,具体实现哪些回调请根据您的业务场景来决定。

// MARK: 音频数据回调
extension ProcessAudioRawDataMainVC: AliRtcAudioFrameDelegate {
    func onCapturedAudioFrame(_ frame: AliRtcAudioFrame) -> Bool {
        let message = "onCaptureVideoSample: numofSamples: \(frame.numOfSamples), numofChannels: \(frame.numOfChannels), SampleRate: \(frame.samplesPerSec)"
        message.printLog()
        updateInfoLabel(captureInfoLabel, text: message)
        return true
    }
    
    func onProcessCapturedAudioFrame(_ frame: AliRtcAudioFrame) -> Bool {
        let message = "onProcessCapturedAudioFrame: numofSamples: \(frame.numOfSamples), numofChannels: \(frame.numOfChannels), SampleRate: \(frame.samplesPerSec)"
        message.printLog()
        updateInfoLabel(processInfoLabel, text: message)
        return true
    }
    
    func onPublishAudioFrame(_ frame: AliRtcAudioFrame) -> Bool {
        let message = "onPublishAudioFrame: numofSamples: \(frame.numOfSamples), numofChannels: \(frame.numOfChannels), SampleRate: \(frame.samplesPerSec)"
        message.printLog()
        updateInfoLabel(publishInfoLabel, text: message)
        return true
    }
    
    func onPlaybackAudioFrame(_ frame: AliRtcAudioFrame) -> Bool {
        let message = "onPlaybackAudioFrame: numofSamples: \(frame.numOfSamples), numofChannels: \(frame.numOfChannels), SampleRate: \(frame.samplesPerSec)"
        message.printLog()
        updateInfoLabel(prePlaybackInfoLabel, text: message)
        return true
    }
    
    func onRemoteUserAudioFrame(_ uid: String?, frame: AliRtcAudioFrame) -> Bool {
        let message = "onRemoteUserAudioFrame: uid: \(uid ?? "invalid"), numofSamples: \(frame.numOfSamples), numofChannels: \(frame.numOfChannels), SampleRate: \(frame.samplesPerSec)"
        message.printLog()
        updateInfoLabel(remoteUserInfoLabel, text: message)
        return true
    }
}

Harmony

private createAudioFrameObserver(): void {
  const rtcAudioFrameObserver :AliRtcEngineAudioFrameListener = {} as AliRtcEngineAudioFrameListener
    rtcAudioFrameObserver.onCapturedAudioFrame ((frame: AliRtcAudioFrame) => {
  // 本地采集音频数据回调
  console.info('onCaptureAudioFrame');
  return true;
});

rtcAudioFrameObserver.onProcessCapturedAudioFrame((frame: AliRtcAudioFrame) => {
  // 3A处理后音频数据回调
  console.info('onProcessCaptureAudioFrame');
  return true;
});

rtcAudioFrameObserver.onPublishAudioFrame((frame: AliRtcAudioFrame) => {
  // 推流音频数据回调
  console.info('onPublishAudioFrame');
  return true;
});

rtcAudioFrameObserver.onPlaybackAudioFrame((frame: AliRtcAudioFrame) => {
  // 播放数据(混音后)回调
  console.info('onPlaybackAudioFrame');
  return true;
});

rtcAudioFrameObserver.onRemoteUserAudioFrame((uid: string, frame: AliRtcAudioFrame) => {
  // 远端用户音频数据回调
  console.info(`onRemoteUserAudioFrame, uid: ${uid}`);
  return true;
})

// 注册音频帧观察者
if (this.rtcEngine) {
  this.rtcEngine.registerAudioFrameObserver(rtcAudioFrameObserver);
}
}

Mac

需要集成并实现AliRtcAudioFrameDelegate类的相关方法,具体实现哪些回调请根据您的业务场景来决定。

- (BOOL)onCapturedAudioFrame:(AliRtcAudioFrame* _Nonnull)frame {
    if (_isSaveLocalAudio) {
        [self saveAudioWithUid:selfUid audioSample:frame audioSource:AliRtcAudioSourceCaptured];
    }
    if (_isModifyLocalAudio) {
        [self modifyAudio:(AliRtcAudioFrame *)frame];
    }
    return true;
}

- (BOOL)onProcessCapturedAudioFrame:(AliRtcAudioFrame* _Nonnull)frame {
    if (_isSaveLocalAudio) {
        [self saveAudioWithUid:selfUid audioSample:frame audioSource:AliRtcAudioSourceProcessCaptured];
    }
    if (_isModifyLocalAudio) {
        [self modifyAudio:(AliRtcAudioFrame *)frame];
    }
    return true;
}

- (BOOL)onPublishAudioFrame:(AliRtcAudioFrame* _Nonnull)frame {
    if (_isSaveLocalAudio) {
        [self saveAudioWithUid:selfUid audioSample:frame audioSource:AliRtcAudioSourcePub];
    }
    if (_isModifyLocalAudio) {
        [self modifyAudio:(AliRtcAudioFrame *)frame];
    }
    return true;
}

- (BOOL)onPlaybackAudioFrame:(AliRtcAudioFrame* _Nonnull)frame {
    if (_isSaveLocalAudio) {
        [self saveAudioWithUid:selfUid audioSample:frame audioSource:AliRtcAudioSourcePlayback];
    }
    if (_isModifyLocalAudio) {
        [self modifyAudio:(AliRtcAudioFrame *)frame];
    }
    return true;
}


- (BOOL)onRemoteUserAudioFrame:(NSString *_Nullable)uid frame:(AliRtcAudioFrame* _Nonnull)frame {
    BOOL canSave = NO;
    for (NSString *itemString in self.saveAudioUidArray) {
        if ([itemString isEqualToString:uid]) {
            canSave = YES;
            break;
        }
    }
    if (canSave) {
        [self saveAudioWithUid:uid audioSample:frame audioSource:(AliRtcAudioSource)AliRtcAudioSourceRemoteUser];
    }
    if (_isModifyLocalAudio) {
        [self modifyAudio:(AliRtcAudioFrame *)frame];
    }

    return true;
}

Windows

需要集成并实现IAudioFrameObserver类的相关方法,具体实现哪些回调请根据您的业务场景来决定。

bool CTutorialDlg::OnPublishAudioFrame(AliEngineAudioRawData audioRawData)
{
	if (NULL == audioRawData.dataPtr) return false;

	// save audio
	if (cshell_dlg.mbSavePcmAudio) {
		tag_AudioDataSample *p = new tag_AudioDataSample(AliEngineAudioSourcePub, &audioRawData);
		m_MutexSaveaudio.Lock();
		m_listSavePubAudio.push_back(p);
		m_MutexSaveaudio.Unlock();
	}

	// modify audio
	if (mbModifyAudio) {
		modifyDataAudioFrame(audioRawData);
	}
	return true;
}

bool CTutorialDlg::OnCapturedAudioFrame(AliEngineAudioRawData audioRawData) {
	if (audioRawData.dataPtr == NULL) return false;

	// save audio
	if (cshell_dlg.mbSavePcmAudio) {
		tag_AudioDataSample *p = new tag_AudioDataSample(AliEngineAudioSourceCaptured, &audioRawData);
		m_MutexSaveaudio.Lock();
		m_listSaveRawAudio.push_back(p);
		m_MutexSaveaudio.Unlock();
	}
	// modify audio
	if (mbModifyAudio) {
		modifyDataAudioFrame(audioRawData);
	}
 	return true;
}

bool CTutorialDlg::OnProcessCapturedAudioFrame(AliEngineAudioRawData audioRawData) {
	if (NULL == audioRawData.dataPtr) return false;

	// save audio
	if (cshell_dlg.mbSavePcmAudio) {
		tag_AudioDataSample *p = new tag_AudioDataSample(AliEngineAudioSourceProcessCaptured, &audioRawData);
		m_MutexSaveaudio.Lock();
		m_listSaveProcessedRawAudio.push_back(p);
		m_MutexSaveaudio.Unlock();
	}
	// modify audio
	if (mbModifyAudio) {
		modifyDataAudioFrame(audioRawData);
	}
	return true;
}

bool CTutorialDlg::OnPlaybackAudioFrame(AliEngineAudioRawData audioRawData) {
	if (NULL == audioRawData.dataPtr) return false;

	// save audio
	if (cshell_dlg.mbSavePcmAudio) {
		tag_AudioDataSample *p = new tag_AudioDataSample(AliEngineAudioSourcePlayback, &audioRawData);
		m_MutexSaveaudio.Lock();
		m_listSaveSubAudio.push_back(p);
		m_MutexSaveaudio.Unlock();
	}

	// modify audio
	if (mbModifyAudio) {
		modifyDataAudioFrame(audioRawData);
	}
	return true;
}

bool CTutorialDlg::OnRemoteUserAudioFrame(const char *uid, AliEngineAudioRawData audioRawData) {
	PartCfgDlg *partView = mRemotePartDlgs[uid];
	if (NULL == audioRawData.dataPtr) return false;

	// save audio
	if (nullptr != partView  && partView->mbSaveAudioData) {
		tag_AudioDataSample *p = new tag_AudioDataSample(AliEngineAudioSourceRemoteUser, &audioRawData);
		m_MutexSaveUserAudio.Lock();
		m_multimapSaveUserAudio.insert(std::pair<std::string, tag_AudioDataSample*>(uid, p));
		m_MutexSaveUserAudio.Unlock();
	}

	// modify audio
	if (mbModifyAudio) {
		modifyDataAudioFrame(audioRawData);
	}

	return true;
}

3.开启获取原始音频数据功能

  1. 调用registerAudioFrameObserver接口注册音频数据监听器。

  2. 调用enableAudioFrameObserver接口开启指定Audio Source回调,并配置期望的数据格式。如果需要开启多个回调,请多次调用。

    • enable:true表示开启,false表示关闭。

    • audioSource:回调数据类型,与上面的回调一一对应。

    • config:回调数据类型设置,如果为null,默认为48000+单声道+ReadOnly模式。

    说明
    • enableAudioFrameObserver的调用与音频数据回调一一对应,可以根据业务场景多次调用。

    • enableAudioFrameObserver在加入频道前后均可调用。

  3. 在对应的回调接口中接收音频数据,并根据业务场景对音频数据进行处理。

Android

// 注册音频帧原始数据回调监听器
mAliRtcEngine.registerAudioFrameObserver(rtcAudioFrameObserver);

// 启用对应回调,根据业务场景选择性调用
mAliRtcEngine.enableAudioFrameObserver(isChecked, AliRtcEngine.AliRtcAudioSource.AliRtcAudioSourceCaptured, config);
mAliRtcEngine.enableAudioFrameObserver(isChecked, AliRtcEngine.AliRtcAudioSource.AliRtcAudioSourceProcessCaptured, config);
mAliRtcEngine.enableAudioFrameObserver(isChecked, AliRtcEngine.AliRtcAudioSource.AliRtcAudioSourcePub, config);
mAliRtcEngine.enableAudioFrameObserver(isChecked, AliRtcEngine.AliRtcAudioSource.AliRtcAudioSourcePlayback, config);
mAliRtcEngine.enableAudioFrameObserver(isChecked, AliRtcEngine.AliRtcAudioSource.AliRtcAudioSourceRemoteUser, config);

iOS

// 注册音频数据回调
self.rtcEngine?.registerAudioFrameObserver(self)

// 启用对应回调,根据业务场景选择性调用,在此根据配置调用
for (source, config) in configs {
    if config.enable, let observerConfig = config.observerConfig {
        // 启用音频数据监听器
        self.rtcEngine?.enableAudioFrameObserver(config.enable, audioSource: source, config: observerConfig)
        isEnableAudioRawFrame = true
    }
}

Harmony

// 注册音频数据回调
this.rtcEngine.registerAudioFrameObserver(rtcAudioFrameObserver);

// 启用对应回调,根据业务场景选择性调用,在此根据配置调用
const config: AliRtcAudioFrameObserverConfig = new AliRtcAudioFrameObserverConfig();
config.sampleRate = AliRtcAudioSampleRate.AliRtcAudioSampleRate_48000;
config.channels = AliRtcAudioNumChannel.AliRtcMonoAudio;

this.rtcEngine.enableAudioFrameObserver(isChecked, AliRtcAudioSource.AliRtcAudioSourceCaptured, config);

Mac

[_engine registerAudioFrameObserver:self];

AliRtcAudioFrameObserverConfig* config = [[AliRtcAudioFrameObserverConfig alloc] init];

config.sampleRate = AliRtcAudioSampleRate_48000;
config.channels = audioNumChannel;
config.mode = AliRtcAudioFrameObserverOperationModeReadWrite;

[_engine enableAudioFrameObserver:TRUE audioSource:AliRtcAudioSourcePub config:config];
[_engine enableAudioFrameObserver:TRUE audioSource:AliRtcAudioSourcePlayback config:config];
[_engine enableAudioFrameObserver:TRUE audioSource:AliRtcAudioSourceCaptured config:config];
[_engine enableAudioFrameObserver:TRUE audioSource:AliRtcAudioSourceProcessCaptured config:config];
[_engine enableAudioFrameObserver:TRUE audioSource:AliRtcAudioSourceRemoteUser];

Windows

// 注册音频帧原始数据回调监听器
mAliRtcEngine->RegisterAudioFrameObserver(mAudioFrameObserver);

AliEngineAudioFrameObserverConfig config;

config.sampleRate = AliEngineAudioSampleRate_48000;
config.channels   = AliEngineStereoAudio;

// 启用对应回调,根据业务场景选择性调用
mAliRtcEngine->EnableAudioFrameObserver(true, AliEngineAudioSourceCaptured, config);
mAliRtcEngine->EnableAudioFrameObserver(true, AliEngineAudioSourceProcessCaptured, config);
mAliRtcEngine->EnableAudioFrameObserver(true, AliEngineAudioSourcePub, config);
mAliRtcEngine->EnableAudioFrameObserver(true, AliEngineAudioSourcePlayback, config);
mAliRtcEngine->EnableAudioFrameObserver(true, AliEngineAudioSourceRemoteUser, config);

4.停止音频数据回调

如果希望停止音频数据回调,可以调用registerAudioFrameObserver(null)取消音频帧注册监听器,或者通过enableAudioFrameObserver传入false来关闭对应Audio Source的数据回调。

Android

// 停止指定Audio Source的音频数据回调
mAliRtcEngine.enableAudioFrameObserver(false, AliRtcEngine.AliRtcAudioSource.AliRtcAudioSourceCaptured, config);

// 取消注册
mAliRtcEngine.registerAudioFrameObserver(null);

iOS

// 停止指定Audio Source的音频数据回调
self.rtcEngine?.enableAudioFrameObserver(false, audioSource: source, config: observerConfig)

// 取消注册
self.rtcEngine?.registerAudioFrameObserver(nil)

Harmony

// 停止指定Audio Source的音频数据回调
this.rtcEngine.enableAudioFrameObserver(false, AliRtcAudioSource.AliRtcAudioSourceCaptured, config);

// 取消注册
self.rtcEngine?.registerAudioFrameObserver(nil)

注意事项

必须调用enableAudioFrameObserver接口开启对应Audio Source的音频数据回调,否则不会触发对应回调。