1717#ifndef NATIVE_WITH_STATE_INTERNAL_MEDIA_API_AUDIO_DEVICE_MODULE_H_
1818#define NATIVE_WITH_STATE_INTERNAL_MEDIA_API_AUDIO_DEVICE_MODULE_H_
1919
20+ #include < stdbool.h>
21+
2022#include < cstdint>
21- #include < memory >
23+ #include < utility >
2224
23- #include " absl/base/thread_annotations.h"
24- #include " absl/synchronization/mutex.h"
2525#include " webrtc/api/audio/audio_device.h"
2626#include " webrtc/api/audio/audio_device_defines.h"
27+ #include " webrtc/api/scoped_refptr.h"
28+ #include " webrtc/api/task_queue/pending_task_safety_flag.h"
29+ #include " webrtc/api/units/time_delta.h"
2730#include " webrtc/modules/audio_device/include/audio_device_default.h"
2831#include " webrtc/rtc_base/thread.h"
2932
3033namespace meet {
3134
35+ // Audio is sampled at 48000Hz.
36+ constexpr int kAudioSampleRatePerMillisecond = 48 ;
37+ // Produce mono audio (i.e. 1 channel).
38+ constexpr int kNumberOfAudioChannels = 1 ;
39+ constexpr int kBytesPerSample = sizeof (int16_t );
40+
3241// Very simple implementation of an AudioDeviceModule.
3342//
3443// WebRTC has platform dependent implementations. However they are not fully
@@ -49,26 +58,80 @@ class MediaApiAudioDeviceModule
4958 : public webrtc::webrtc_impl::AudioDeviceModuleDefault<
5059 webrtc::AudioDeviceModule> {
5160 public:
52- MediaApiAudioDeviceModule () = default ;
61+ // Default constructor for production use.
62+ //
63+ // In production, audio should be sampled at 48000 Hz every 10ms.
64+ explicit MediaApiAudioDeviceModule (rtc::Thread& worker_thread)
65+ : MediaApiAudioDeviceModule(worker_thread,
66+ webrtc::TimeDelta::Millis (10 )) {}
5367
54- ~MediaApiAudioDeviceModule () override { StopPlayout (); };
68+ // Constructor for testing with configurable sampling interval; the default
69+ // sampling interval of 10ms is too small to write non-flaky tests with.
70+ MediaApiAudioDeviceModule (rtc::Thread& worker_thread,
71+ webrtc::TimeDelta sampling_interval)
72+ : worker_thread_(worker_thread),
73+ sampling_interval_(std::move(sampling_interval)) {
74+ safety_flag_ = webrtc::PendingTaskSafetyFlag::CreateAttachedToTaskQueue (
75+ /* alive=*/ true , &worker_thread_);
76+ };
5577
56- int32_t InitPlayout () override ;
57- int32_t StopPlayout () override final ;
5878 int32_t RegisterAudioCallback (webrtc::AudioTransport* callback) override ;
59- bool Playing () const override { return task_thread_ != nullptr ; };
79+ int32_t StartPlayout () override ;
80+ int32_t StopPlayout () override ;
81+ int32_t Terminate () override ;
82+ bool Playing () const override ;
6083
6184 private:
6285 // Periodically calls the registered audio callback, registered by WebRTC
6386 // internals, to provide audio data. It is to be invoked every 10ms with a
6487 // sampling rate of 48000 Hz. If this is not done, no audio will be provided
65- // to the registered audio sinks with the RTPReceiver of the RTPTransceiver
88+ // to the audio sinks registered with the RTPReceiver of the RTPTransceiver
6689 // that remote audio is being received on.
6790 void ProcessPlayData ();
6891
69- mutable absl::Mutex mutex_;
70- webrtc::AudioTransport* audio_callback_ ABSL_GUARDED_BY (mutex_) = nullptr;
71- std::unique_ptr<rtc::Thread> task_thread_;
92+ // Note that this MUST be the same worker thread used when creating the peer
93+ // connection.
94+ //
95+ // Not only does this remove the need for synchronization in this class (as
96+ // all methods are called on the worker thread by WebRTC), it also prevents
97+ // a deadlock when closing the peer connection:
98+ //
99+ // When audio data is passed to `ConferenceAudioTrack::OnData()`, it is
100+ // called on whatever thread `audio_callback_` is called on. When attempting
101+ // to read the audio csrcs and ssrcs from
102+ // `RtpReceiverInterface::GetSources()`, a blocking call will be made to the
103+ // worker thread (via the rtp receiver proxy layer) if the current thread is
104+ // NOT the worker thread.
105+ //
106+ // `ConferenceAudioTrack::OnData()` is called while holding a mutex in
107+ // WebRTC's `AudioMixerImpl::Mix()` method (also running on whatever thread
108+ // `audio_callback_` is called on).
109+ //
110+ // At the same time, when closing the peer connection,
111+ // `AudioMixerImpl::RemoveSource()` is called on the worker thread and
112+ // attempts to acquire the mutex held by `AudioMixerImpl::Mix()`, blocking
113+ // the worker thread.
114+ //
115+ // Therefore, it is possible for the worker thread to be blocked while
116+ // waiting for the `AudioMixerImpl` mutex, while
117+ // `ConferenceAudioTrack::OnData()` is blocked waiting for the worker thread
118+ // to read the audio csrcs and ssrcs.
119+ //
120+ // By ensuring that this class is always called on the worker thread, this
121+ // deadlock is avoided, as:
122+ // 1. The worker thread is a task queue, and task queue operatons are
123+ // executed sequentially.
124+ // 2. `ConferenceAudioTrack::OnData()` is called on the worker thread and
125+ // therefore does not need to switch to the worker thread to read the
126+ // audio csrcs and ssrcs.
127+ rtc::Thread& worker_thread_;
128+ // Used to ensure that tasks are not posted after `Terminate()` is called,
129+ // since this class does not own the worker thread.
130+ rtc::scoped_refptr<webrtc::PendingTaskSafetyFlag> safety_flag_;
131+ webrtc::TimeDelta sampling_interval_;
132+
133+ webrtc::AudioTransport* audio_callback_ = nullptr ;
134+ bool is_playing_ = false ;
72135};
73136
74137} // namespace meet
0 commit comments