Skip to content
This repository was archived by the owner on Nov 6, 2025. It is now read-only.

Commit 79c60ca

Browse files
committed
Allow to call deliverRecordedData: on multiple threads
It is required to keep [RTCAudioDeviceModule deliverRecordedData:] calling once an audio stream added. In such a case, you may invoke the method with dummy samples on the background thread before real samples are ready, and then invoke the method with real samples in threads handling audio. This change covers the scenario.
1 parent 6a662ee commit 79c60ca

File tree

3 files changed

+16
-17
lines changed

3 files changed

+16
-17
lines changed

README.pixiv.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,16 @@ is [`CMSampleBuffer`](https://developer.apple.com/documentation/coremedia/cmsamp
5454
Provide [`CMSampleBuffer`](https://developer.apple.com/documentation/coremedia/cmsamplebuffer-u71)
5555
acquired with [`[RPBroadcastSampleHandler processSampleBuffer: with:]`](https://developer.apple.com/documentation/replaykit/rpbroadcastsamplehandler/2123045-processsamplebuffer).
5656

57+
You must keep calling this method once you added an audio stream. However,
58+
broadcast extension does not provide audio buffer with
59+
`RPSampleBufferType.audioMic` while the microphone is disabled. Therefore,
60+
you have to provide dummy samples before the microphone is enabled if you
61+
are broadcasting the audio recorded with the microphone.
62+
63+
Note that `NSTimer` and `DispatchSourceTimer` cannot be used to implement
64+
such a mechanism, at least on iOS 12.2. `DispatchQueue.asyncAfter` is
65+
a nice alternative.
66+
5767
The audio unit must be uninitialized and disabled to use this method.
5868

5969
### `RTCAudioSession.useManualAudio`

sdk/objc/native/src/audio/audio_device_ios.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,8 @@ class AudioDeviceIOS : public AudioDeviceGeneric,
208208
// created on.
209209
rtc::ThreadChecker thread_checker_;
210210

211-
// Native I/O audio thread checker.
212-
rtc::ThreadChecker io_thread_checker_;
211+
// Native audio I/O critical section.
212+
rtc::CriticalSection io_lock_;
213213

214214
// Thread that this object is created on.
215215
rtc::Thread* thread_;
@@ -282,7 +282,7 @@ class AudioDeviceIOS : public AudioDeviceGeneric,
282282

283283
// Counts number of detected audio glitches on the playout side.
284284
int64_t num_detected_playout_glitches_ RTC_GUARDED_BY(thread_checker_);
285-
int64_t last_playout_time_ RTC_GUARDED_BY(io_thread_checker_);
285+
int64_t last_playout_time_ RTC_GUARDED_BY(io_lock_);
286286

287287
// Counts number of playout callbacks per call.
288288
// The value isupdated on the native I/O thread and later read on the

sdk/objc/native/src/audio/audio_device_ios.mm

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ static void LogDeviceInfo() {
116116
num_playout_callbacks_(0),
117117
last_output_volume_change_time_(0) {
118118
LOGI() << "ctor" << ios::GetCurrentThreadDescription();
119-
io_thread_checker_.Detach();
120119
thread_checker_.Detach();
121120
thread_ = rtc::Thread::Current();
122121

@@ -139,7 +138,6 @@ static void LogDeviceInfo() {
139138

140139
AudioDeviceGeneric::InitStatus AudioDeviceIOS::Init() {
141140
LOGI() << "Init";
142-
io_thread_checker_.Detach();
143141
thread_checker_.Detach();
144142

145143
RTC_DCHECK_RUN_ON(&thread_checker_);
@@ -359,7 +357,7 @@ static void LogDeviceInfo() {
359357
}
360358

361359
void AudioDeviceIOS::OnDeliverRecordedExternalData(CMSampleBufferRef sample_buffer) {
362-
RTC_DCHECK_RUN_ON(&io_thread_checker_);
360+
rtc::CritScope scoped_lock(&io_lock_);
363361

364362
if (audio_unit_ && audio_unit_->GetState() != VoiceProcessingAudioUnit::kUninitialized) {
365363
RTCLogError(@"External recorded data was provided while audio unit is enabled.");
@@ -421,7 +419,7 @@ static void LogDeviceInfo() {
421419
UInt32 bus_number,
422420
UInt32 num_frames,
423421
AudioBufferList* /* io_data */) {
424-
RTC_DCHECK_RUN_ON(&io_thread_checker_);
422+
rtc::CritScope scoped_lock(&io_lock_);
425423
OSStatus result = noErr;
426424
// Simply return if recording is not enabled.
427425
if (!rtc::AtomicOps::AcquireLoad(&recording_)) return result;
@@ -469,7 +467,7 @@ static void LogDeviceInfo() {
469467
UInt32 bus_number,
470468
UInt32 num_frames,
471469
AudioBufferList* io_data) {
472-
RTC_DCHECK_RUN_ON(&io_thread_checker_);
470+
rtc::CritScope scoped_lock(&io_lock_);
473471
// Verify 16-bit, noninterleaved mono PCM signal format.
474472
RTC_DCHECK_EQ(1, io_data->mNumberBuffers);
475473
AudioBuffer* audio_buffer = &io_data->mBuffers[0];
@@ -979,10 +977,6 @@ static void LogDeviceInfo() {
979977
// Close and delete the voice-processing I/O unit.
980978
audio_unit_.reset();
981979

982-
// Detach thread checker for the AURemoteIO::IOThread to ensure that the
983-
// next session uses a fresh thread id.
984-
io_thread_checker_.Detach();
985-
986980
// Remove audio session notification observers.
987981
RTCAudioSession* session = [RTCAudioSession sharedInstance];
988982
[session removeDelegate:audio_session_observer_];
@@ -994,11 +988,6 @@ static void LogDeviceInfo() {
994988

995989
void AudioDeviceIOS::PrepareForNewStart() {
996990
LOGI() << "PrepareForNewStart";
997-
// The audio unit has been stopped and preparations are needed for an upcoming
998-
// restart. It will result in audio callbacks from a new native I/O thread
999-
// which means that we must detach thread checkers here to be prepared for an
1000-
// upcoming new audio stream.
1001-
io_thread_checker_.Detach();
1002991
}
1003992

1004993
bool AudioDeviceIOS::IsInterrupted() {

0 commit comments

Comments
 (0)