Skip to content

Instantly share code, notes, and snippets.

@shalyf
Last active August 21, 2023 09:23
Show Gist options
  • Save shalyf/19350c20557c11bd9004dc5e92cb1615 to your computer and use it in GitHub Desktop.
Save shalyf/19350c20557c11bd9004dc5e92cb1615 to your computer and use it in GitHub Desktop.
WebRTC iOS源码修改 - 让SDK支持静音麦克风(用户可以在语音通话过程中静音自己的麦克风)

在WebRTC官方SDK中,如果为RTCPeerConnection添加了AudioTrack,WebRTC就会尝试去初始化音频的输入输出。 Audio通道建立成功之后WebRTC会自动完成声音的采集传输播放。 RTCAudioSession提供了一个useManualAudio属性,将它设置为true,那么音频的输入输出开关将由isAudioEnabled属性控制。 但是,isAudioEnabled只能同时控制音频的输入输出,无法分开控制。 我们的产品现在需要在静音麦克风的功能,也就是保持输出打开,但是关闭输入。
目前官方没有提供API,底层相关代码还没有实现

// sdk/objc/native/src/audio/audio_device_ios.mm
int32_t AudioDeviceIOS::SetMicrophoneMute(bool enable) {
  RTC_NOTREACHED() << "Not implemented";
  return -1;
}

分析源码,可以在VoiceProcessingAudioUnit中找到Audio Unit的使用。 OnDeliverRecordedData回调函数拿到音频数据后通过VoiceProcessingAudioUnitObserver通知给AudioDeviceIOS

// sdk/objc/native/src/audio/voice_processing_audio_unit.mm
OSStatus VoiceProcessingAudioUnit::OnDeliverRecordedData(
    void* in_ref_con,
    AudioUnitRenderActionFlags* flags,
    const AudioTimeStamp* time_stamp,
    UInt32 bus_number,
    UInt32 num_frames,
    AudioBufferList* io_data) {
  VoiceProcessingAudioUnit* audio_unit =
      static_cast<VoiceProcessingAudioUnit*>(in_ref_con);
  return audio_unit->NotifyDeliverRecordedData(flags, time_stamp, bus_number,
                                               num_frames, io_data);
}
// sdk/objc/native/src/audio/audio_device_ios.mm
OSStatus AudioDeviceIOS::OnDeliverRecordedData(AudioUnitRenderActionFlags* flags,
                                               const AudioTimeStamp* time_stamp,
                                               UInt32 bus_number,
                                               UInt32 num_frames,
                                               AudioBufferList* /* io_data */) {
  // 在处理音频之前,通过一个参数来控制麦克风是否静音,如果静音则直接退出函数,这样就不会有声音被传输
  if (is_microphone_mute_) return noErr;
                                               
  // 处理音频数据,这里WebRTC没有使用传输的`io_data`,而是重新去`render`了一次数据,详情可以看源码
  
  fine_audio_buffer_->DeliverRecordedData(record_audio_buffer_, kFixedRecordDelayEstimate);
  return noErr;
}

其实要静音麦克风也很简单,只需要忽略掉输入数据,或者将输入全部替换成0即可,这边采用了忽略数据的方式。 上面代码新增了一个is_microphone_mute_变量,这个变量将会像之前的isAudioEnabled属性一样,通过RTCAudioSession对外提供接口。 下面就是将is_microphone_mute_变量暴露给外部,我们只要模仿isAudioEnabled就可以轻松实现目的。

RTCAudioSession中实现isMicrophoneMute属性

// sdk/objc/components/audio/RTCAudioSession.mm
- (void)setIsMicrophoneMute:(BOOL)isMicrophoneMute {
  @synchronized(self) {
    if (_isMicrophoneMute == isMicrophoneMute) {
      return;
    }
    _isMicrophoneMute = isMicrophoneMute;
  }
  [self notifyDidChangeMicrophoneMute];
}

- (BOOL)isMicrophoneMute {
  @synchronized(self) {
    return _isMicrophoneMute;
  }
}

- (void)notifyDidChangeMicrophoneMute {
  for (auto delegate : self.delegates) {
    SEL sel = @selector(audioSession:didChangeMicrophoneMute:);
    if ([delegate respondsToSelector:sel]) {
      [delegate audioSession:self didChangeMicrophoneMute:self.isMicrophoneMute];
    }
  }
}

setIsMicrophoneMute将通过RTCNativeAudioSessionDelegateAdapter把消息传递给AudioDeviceIOS

// sdk/objc/components/audio/RTCNativeAudioSessionDelegateAdapter.mm
- (void)audioSession:(RTCAudioSession *)session 
    didChangeMicrophoneMute:(BOOL)isMicrophoneMute {
  _observer->OnMicrophoneMuteChange(isMicrophoneMute);
}

AudioDeviceIOS中实现具体逻辑,AudioDeviceIOS::OnMicrophoneMuteChange将消息发送给线程来处理

// sdk/objc/native/src/audio/audio_device_ios.mm
void AudioDeviceIOS::OnMicrophoneMuteChange(bool is_microphone_mute) {
  RTC_DCHECK(thread_);
  thread_->Post(RTC_FROM_HERE,
                this,
                kMessageTypeMicrophoneMuteChange,
                new rtc::TypedMessageData<bool>(is_microphone_mute));
}

void AudioDeviceIOS::OnMessage(rtc::Message* msg) {
  switch (msg->message_id) {
    // ...
    case kMessageTypeMicrophoneMuteChange: {
      rtc::TypedMessageData<bool>* data = static_cast<rtc::TypedMessageData<bool>*>(msg->pdata);
      HandleMicrophoneMuteChange(data->data());
      delete data;
      break;
    }
  }
}

void AudioDeviceIOS::HandleMicrophoneMuteChange(bool is_microphone_mute) {
  RTC_DCHECK_RUN_ON(&thread_checker_);
  RTCLog(@"Handling MicrophoneMute change to %d", is_microphone_mute);
  is_microphone_mute_ = is_microphone_mute;
}

至此,麦克风的静音就完成了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment