Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "api/media_stream_interface.h"
#include "rtc_base/ref_counted_object.h"
#include "rtc_base/synchronization/mutex.h"

#include "media/SyncClock.h"

Expand Down Expand Up @@ -50,9 +51,13 @@ namespace jni
void SetAudioCaptureDelay(int64_t delay_us);

private:
// Guards sinks_ and audio_capture_delay_us_ against concurrent
// AddSink()/RemoveSink() (called by WebRTC's own signaling/worker
// thread as tracks attach/detach) racing with PushAudioData() and
// SetAudioCaptureDelay() (called by the application's own threads).
webrtc::Mutex mutex_;
std::vector<webrtc::AudioTrackSinkInterface*> sinks_;
std::shared_ptr<SyncClock> clock_;
//webrtc::CriticalSection crit_;
std::atomic<int64_t> total_samples_captured_;
int64_t audio_capture_delay_us_;
};
Expand Down
8 changes: 5 additions & 3 deletions webrtc-jni/src/main/cpp/src/media/audio/CustomAudioSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ namespace jni

void CustomAudioSource::AddSink(webrtc::AudioTrackSinkInterface * sink)
{
//webrtc::CritScope lock(&crit_);
webrtc::MutexLock lock(&mutex_);

sinks_.push_back(sink);
}

void CustomAudioSource::RemoveSink(webrtc::AudioTrackSinkInterface * sink)
{
//webrtc::CritScope lock(&crit_);
webrtc::MutexLock lock(&mutex_);

sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
}
Expand All @@ -50,7 +50,7 @@ namespace jni
int sample_rate, size_t number_of_channels,
size_t number_of_frames)
{
//webrtc::CritScope lock(&crit_);
webrtc::MutexLock lock(&mutex_);

// Calculate absolute capture time
int64_t timestamp_us = clock_->GetTimestampUs();
Expand All @@ -77,6 +77,8 @@ namespace jni

void CustomAudioSource::SetAudioCaptureDelay(int64_t delay_us)
{
webrtc::MutexLock lock(&mutex_);

audio_capture_delay_us_ = delay_us;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,37 @@ void pushAudioData() {
testAudioFormat(16, 48000, 2, 480);
}

@Test
void concurrentAddRemoveSinkDoesNotCrash() throws InterruptedException {
// Regression test: AddSink()/RemoveSink() (called here from this thread,
// mirroring the internal thread WebRTC uses as tracks attach/detach) used
// to race unsynchronized with PushAudioData() (the application's capture
// thread) over the native sinks_ vector.
AudioTrack audioTrack = factory.createAudioTrack("audioTrack", customAudioSource);
AudioTrackSink sink = (data, bitsPerSample, sampleRate, channels, frames) -> { };

byte[] audioData = new byte[480 * 2 * 2]; // 10ms of 48kHz stereo 16-bit audio

AtomicBoolean running = new AtomicBoolean(true);

Thread pushThread = new Thread(() -> {
while (running.get()) {
customAudioSource.pushAudio(audioData, 16, 48000, 2, 480);
}
});
pushThread.start();

for (int i = 0; i < 5000; i++) {
audioTrack.addSink(sink);
audioTrack.removeSink(sink);
}

running.set(false);
pushThread.join(5000);

audioTrack.dispose();
}

@Test
void pushAudioWithDifferentFormats() {
testAudioFormat(8, 8000, 1, 80); // 8-bit, 8kHz, mono, 10ms
Expand Down
Loading