From 70845b5be374c12744551cead22f6798a753b25b Mon Sep 17 00:00:00 2001 From: Xiaoming Shi Date: Fri, 27 Dec 2024 11:14:34 -0800 Subject: [PATCH] [android] Refactor MinRequiredFramesTester b/327287075 --- .../audio_sink_min_required_frames_tester.cc | 130 ++++++++++++--- .../audio_sink_min_required_frames_tester.h | 49 +++--- .../shared/audio_track_audio_sink_type.cc | 153 ++++-------------- .../shared/audio_track_audio_sink_type.h | 15 -- 4 files changed, 170 insertions(+), 177 deletions(-) diff --git a/starboard/android/shared/audio_sink_min_required_frames_tester.cc b/starboard/android/shared/audio_sink_min_required_frames_tester.cc index 36031f6148ab..a769e5137b1d 100644 --- a/starboard/android/shared/audio_sink_min_required_frames_tester.cc +++ b/starboard/android/shared/audio_sink_min_required_frames_tester.cc @@ -18,6 +18,7 @@ #include #include "starboard/android/shared/audio_track_audio_sink_type.h" +#include "starboard/android/shared/media_capabilities_cache.h" #include "starboard/shared/pthread/thread_create_priority.h" namespace starboard { @@ -25,7 +26,10 @@ namespace android { namespace shared { namespace { + const int kCheckpointFramesInterval = 1024; +const int kSampleFrequency22050 = 22050; +const int kSampleFrequency48000 = 48000; // Helper function to compute the size of the two valid starboard audio sample // types. @@ -39,30 +43,83 @@ size_t GetSampleSize(SbMediaAudioSampleType sample_type) { SB_NOTREACHED(); return 0u; } + +bool HasRemoteAudioOutput() { + // SbPlayerBridge::GetAudioConfigurations() reads up to 32 configurations. The + // limit here is to avoid infinite loop and also match + // SbPlayerBridge::GetAudioConfigurations(). + const int kMaxAudioConfigurations = 32; + SbMediaAudioConfiguration configuration; + int index = 0; + while (index < kMaxAudioConfigurations && + MediaCapabilitiesCache::GetInstance()->GetAudioConfiguration( + index, &configuration)) { + switch (configuration.connector) { + case kSbMediaAudioConnectorUnknown: + case kSbMediaAudioConnectorAnalog: + case kSbMediaAudioConnectorBuiltIn: + case kSbMediaAudioConnectorHdmi: + case kSbMediaAudioConnectorSpdif: + case kSbMediaAudioConnectorUsb: + break; + case kSbMediaAudioConnectorBluetooth: + case kSbMediaAudioConnectorRemoteWired: + case kSbMediaAudioConnectorRemoteWireless: + case kSbMediaAudioConnectorRemoteOther: + return true; + } + index++; + } + return false; +} + } // namespace -MinRequiredFramesTester::MinRequiredFramesTester(int max_required_frames, - int required_frames_increment, - int min_stable_played_frames) - : max_required_frames_(max_required_frames), - required_frames_increment_(required_frames_increment), - min_stable_played_frames_(min_stable_played_frames), - condition_variable_(mutex_), - destroying_(false) {} +MinRequiredFramesTester::MinRequiredFramesTester() { + Start(); +} MinRequiredFramesTester::~MinRequiredFramesTester() { SB_DCHECK(thread_checker_.CalledOnValidThread()); + destroying_.store(true); if (tester_thread_ != 0) { { - ScopedLock scoped_lock(mutex_); + ScopedLock scoped_lock(condition_variable_mutex_); condition_variable_.Signal(); } - pthread_join(tester_thread_, NULL); + pthread_join(tester_thread_, nullptr); tester_thread_ = 0; } } +int MinRequiredFramesTester::GetMinBufferSizeInFrames( + int channels, + SbMediaAudioSampleType sample_type, + int sampling_frequency_hz) { + bool has_remote_audio_output = HasRemoteAudioOutput(); + ScopedLock lock(min_required_frames_map_mutex_); + if (has_remote_audio_output == has_remote_audio_output_) { + // There's no audio output type change, we can use the numbers we got from + // the tests at app launch. + if (sampling_frequency_hz <= kSampleFrequency22050) { + if (min_required_frames_map_.find(kSampleFrequency22050) != + min_required_frames_map_.end()) { + return min_required_frames_map_[kSampleFrequency22050]; + } + } else if (sampling_frequency_hz <= kSampleFrequency48000) { + if (min_required_frames_map_.find(kSampleFrequency48000) != + min_required_frames_map_.end()) { + return min_required_frames_map_[kSampleFrequency48000]; + } + } + } + // We cannot find a matched result from our tests, or the audio output type + // has changed. We use the default max required frames to avoid underruns. + return has_remote_audio_output ? kMaxRequiredFramesRemote + : kMaxRequiredFramesLocal; +} + void MinRequiredFramesTester::AddTest( int number_of_channels, SbMediaAudioSampleType sample_type, @@ -82,6 +139,33 @@ void MinRequiredFramesTester::Start() { // MinRequiredFramesTester only supports to start once. SB_DCHECK(tester_thread_ == 0); + auto onMinRequiredFramesForWebAudioReceived = + [&](int number_of_channels, SbMediaAudioSampleType sample_type, + int sample_rate, int min_required_frames) { + bool has_remote_audio_output = HasRemoteAudioOutput(); + SB_LOG(INFO) << "Received min required frames " << min_required_frames + << " for " << number_of_channels << " channels, " + << sample_rate << "hz, with " + << (has_remote_audio_output ? "remote" : "local") + << " audio output device."; + ScopedLock lock(min_required_frames_map_mutex_); + has_remote_audio_output_ = has_remote_audio_output; + min_required_frames_map_[sample_rate] = + std::min(min_required_frames, has_remote_audio_output_ + ? kMaxRequiredFramesRemote + : kMaxRequiredFramesLocal); + }; + + SbMediaAudioSampleType sample_type = kSbMediaAudioSampleTypeFloat32; + if (!SbAudioSinkIsAudioSampleTypeSupported(sample_type)) { + sample_type = kSbMediaAudioSampleTypeInt16Deprecated; + SB_DCHECK(SbAudioSinkIsAudioSampleTypeSupported(sample_type)); + } + AddTest(2, sample_type, kSampleFrequency48000, + onMinRequiredFramesForWebAudioReceived, 8 * 1024); + AddTest(2, sample_type, kSampleFrequency22050, + onMinRequiredFramesForWebAudioReceived, 4 * 1024); + pthread_create(&tester_thread_, nullptr, &MinRequiredFramesTester::TesterThreadEntryPoint, this); SB_DCHECK(tester_thread_ != 0); @@ -97,7 +181,7 @@ void* MinRequiredFramesTester::TesterThreadEntryPoint(void* context) { MinRequiredFramesTester* tester = static_cast(context); tester->TesterThreadFunc(); - return NULL; + return nullptr; } void MinRequiredFramesTester::TesterThreadFunc() { @@ -108,7 +192,7 @@ void MinRequiredFramesTester::TesterThreadFunc() { if (destroying_.load()) { break; } - std::vector silence_buffer(max_required_frames_ * + std::vector silence_buffer(kMaxRequiredFrames * task.number_of_channels * GetSampleSize(task.sample_type), 0); @@ -123,15 +207,15 @@ void MinRequiredFramesTester::TesterThreadFunc() { last_total_consumed_frames_ = 0; audio_sink_ = new AudioTrackAudioSink( - NULL, task.number_of_channels, task.sample_rate, task.sample_type, - frame_buffers, max_required_frames_, + nullptr, task.number_of_channels, task.sample_rate, task.sample_type, + frame_buffers, kMaxRequiredFrames, min_required_frames_ * task.number_of_channels * GetSampleSize(task.sample_type), &MinRequiredFramesTester::UpdateSourceStatusFunc, &MinRequiredFramesTester::ConsumeFramesFunc, &MinRequiredFramesTester::ErrorFunc, 0, -1, false, this); { - ScopedLock scoped_lock(mutex_); + ScopedLock scoped_lock(condition_variable_mutex_); wait_timeout = !condition_variable_.WaitTimed(5'000'000); } @@ -149,14 +233,14 @@ void MinRequiredFramesTester::TesterThreadFunc() { if (wait_timeout) { SB_LOG(ERROR) << "Audio sink min required frames tester timeout."; // Overwrite |min_required_frames_| if failed to get a stable result. - min_required_frames_ = max_required_frames_; + min_required_frames_ = kMaxRequiredFrames; } if (has_error_) { SB_LOG(ERROR) << "There's an error while running the test. Fallback to " "max required frames " - << max_required_frames_ << "."; - min_required_frames_ = max_required_frames_; + << kMaxRequiredFrames << "."; + min_required_frames_ = kMaxRequiredFrames; } if (start_threshold > min_required_frames_) { @@ -242,8 +326,8 @@ void MinRequiredFramesTester::ConsumeFrames(int frames_consumed) { // we need to write more buffers into audio sink. int underrun_count = audio_sink_->GetUnderrunCount(); if (underrun_count > last_underrun_count_) { - min_required_frames_ += required_frames_increment_; - if (min_required_frames_ >= max_required_frames_) { + min_required_frames_ += kRequiredFramesIncrement_; + if (min_required_frames_ >= kMaxRequiredFrames) { SB_LOG(WARNING) << "Min required frames reached maximum."; } else { last_underrun_count_ = -1; @@ -252,13 +336,13 @@ void MinRequiredFramesTester::ConsumeFrames(int frames_consumed) { } } - if (min_required_frames_ >= max_required_frames_ || - total_consumed_frames_ - min_stable_played_frames_ >= + if (min_required_frames_ >= kMaxRequiredFrames || + total_consumed_frames_ - kMinStablePlayedFrames_ >= last_total_consumed_frames_) { // |min_required_frames_| reached maximum, or playback is stable and // doesn't have underruns. Stop the test. last_total_consumed_frames_ = INT_MAX; - ScopedLock scoped_lock(mutex_); + ScopedLock scoped_lock(condition_variable_mutex_); condition_variable_.Signal(); } } diff --git a/starboard/android/shared/audio_sink_min_required_frames_tester.h b/starboard/android/shared/audio_sink_min_required_frames_tester.h index 662b0aba1c18..cf3cb7ddfce8 100644 --- a/starboard/android/shared/audio_sink_min_required_frames_tester.h +++ b/starboard/android/shared/audio_sink_min_required_frames_tester.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -43,18 +44,16 @@ class MinRequiredFramesTester { int min_required_frames)> OnMinRequiredFramesReceivedCallback; - MinRequiredFramesTester(int max_required_frames, - int required_frames_increment, - int min_stable_played_frames); - ~MinRequiredFramesTester(); + MinRequiredFramesTester(); - void AddTest(int number_of_channels, - SbMediaAudioSampleType sample_type, - int sample_rate, - const OnMinRequiredFramesReceivedCallback& received_cb, - int default_required_frames); + MinRequiredFramesTester(const MinRequiredFramesTester&) = delete; + MinRequiredFramesTester& operator=(const MinRequiredFramesTester&) = delete; - void Start(); + ~MinRequiredFramesTester(); + + int GetMinBufferSizeInFrames(int channels, + SbMediaAudioSampleType sample_type, + int sampling_frequency_hz); private: struct TestTask { @@ -76,6 +75,14 @@ class MinRequiredFramesTester { const int default_required_frames; }; + void AddTest(int number_of_channels, + SbMediaAudioSampleType sample_type, + int sample_rate, + const OnMinRequiredFramesReceivedCallback& received_cb, + int default_required_frames); + + void Start(); + static void* TesterThreadEntryPoint(void* context); void TesterThreadFunc(); @@ -96,12 +103,12 @@ class MinRequiredFramesTester { bool* is_eos_reached); void ConsumeFrames(int frames_consumed); - MinRequiredFramesTester(const MinRequiredFramesTester&) = delete; - MinRequiredFramesTester& operator=(const MinRequiredFramesTester&) = delete; + static constexpr int kMaxRequiredFramesLocal = 16 * 1024; + static constexpr int kMaxRequiredFramesRemote = 32 * 1024; - const int max_required_frames_; - const int required_frames_increment_; - const int min_stable_played_frames_; + static constexpr int kMaxRequiredFrames = kMaxRequiredFramesRemote; + static constexpr int kRequiredFramesIncrement_ = 4 * 1024; + static constexpr int kMinStablePlayedFrames_ = 12 * 1024; ::starboard::shared::starboard::ThreadChecker thread_checker_; @@ -115,10 +122,16 @@ class MinRequiredFramesTester { int last_underrun_count_; int last_total_consumed_frames_; - Mutex mutex_; - ConditionVariable condition_variable_; + Mutex condition_variable_mutex_; // Only used by `condition_variable_` below + ConditionVariable condition_variable_{condition_variable_mutex_}; + pthread_t tester_thread_ = 0; - std::atomic_bool destroying_; + std::atomic_bool destroying_ = false; + + Mutex min_required_frames_map_mutex_; + // The minimum frames required to avoid underruns of different frequencies. + std::map min_required_frames_map_; + bool has_remote_audio_output_ = false; }; } // namespace shared diff --git a/starboard/android/shared/audio_track_audio_sink_type.cc b/starboard/android/shared/audio_track_audio_sink_type.cc index 4af3a1751f18..f07a366bfa15 100644 --- a/starboard/android/shared/audio_track_audio_sink_type.cc +++ b/starboard/android/shared/audio_track_audio_sink_type.cc @@ -19,7 +19,7 @@ #include #include -#include "starboard/android/shared/media_capabilities_cache.h" +#include "starboard/android/shared/audio_sink_min_required_frames_tester.h" #include "starboard/common/string.h" #include "starboard/common/time.h" #include "starboard/shared/pthread/thread_create_priority.h" @@ -27,9 +27,13 @@ #include "starboard/shared/starboard/player/filter/common.h" namespace { -starboard::android::shared::AudioTrackAudioSinkType* - audio_track_audio_sink_type_; -} + +::starboard::android::shared::MinRequiredFramesTester* + s_min_required_frames_tester; +::starboard::android::shared::AudioTrackAudioSinkType* + s_audio_track_audio_sink_type; + +} // namespace namespace starboard { namespace android { @@ -54,15 +58,6 @@ const int64_t kMaxDurationPerRequestInTunnelMode = 16'000; // 16ms const size_t kSilenceFramesPerAppend = 1024; -const int kMaxRequiredFramesLocal = 16 * 1024; -const int kMaxRequiredFramesRemote = 32 * 1024; -const int kMaxRequiredFrames = kMaxRequiredFramesRemote; -const int kRequiredFramesIncrement = 4 * 1024; -const int kMinStablePlayedFrames = 12 * 1024; - -const int kSampleFrequency22050 = 22050; -const int kSampleFrequency48000 = 48000; - void* IncrementPointerByBytes(void* pointer, size_t offset) { return static_cast(pointer) + offset; } @@ -73,35 +68,6 @@ int GetMaxFramesPerRequestForTunnelMode(int sampling_frequency_hz) { return (max_frames + 15) / 16 * 16; // align to 16 } -bool HasRemoteAudioOutput() { - // SbPlayerBridge::GetAudioConfigurations() reads up to 32 configurations. The - // limit here is to avoid infinite loop and also match - // SbPlayerBridge::GetAudioConfigurations(). - const int kMaxAudioConfigurations = 32; - SbMediaAudioConfiguration configuration; - int index = 0; - while (index < kMaxAudioConfigurations && - MediaCapabilitiesCache::GetInstance()->GetAudioConfiguration( - index, &configuration)) { - switch (configuration.connector) { - case kSbMediaAudioConnectorUnknown: - case kSbMediaAudioConnectorAnalog: - case kSbMediaAudioConnectorBuiltIn: - case kSbMediaAudioConnectorHdmi: - case kSbMediaAudioConnectorSpdif: - case kSbMediaAudioConnectorUsb: - break; - case kSbMediaAudioConnectorBluetooth: - case kSbMediaAudioConnectorRemoteWired: - case kSbMediaAudioConnectorRemoteWireless: - case kSbMediaAudioConnectorRemoteOther: - return true; - } - index++; - } - return false; -} - } // namespace AudioTrackAudioSink::AudioTrackAudioSink( @@ -168,7 +134,7 @@ AudioTrackAudioSink::~AudioTrackAudioSink() { quit_ = true; if (audio_out_thread_ != 0) { - pthread_join(audio_out_thread_, NULL); + pthread_join(audio_out_thread_, nullptr); } } @@ -192,7 +158,7 @@ void* AudioTrackAudioSink::ThreadEntryPoint(void* context) { AudioTrackAudioSink* sink = reinterpret_cast(context); sink->AudioThreadFunc(); - return NULL; + return nullptr; } // TODO: Break down the function into manageable pieces. @@ -430,19 +396,15 @@ int AudioTrackAudioSinkType::GetMinBufferSizeInFrames( int channels, SbMediaAudioSampleType sample_type, int sampling_frequency_hz) { - SB_DCHECK(audio_track_audio_sink_type_); + SB_DCHECK(s_audio_track_audio_sink_type); - return std::max( - AudioTrackBridge::GetMinBufferSizeInFrames(sample_type, channels, - sampling_frequency_hz), - audio_track_audio_sink_type_->GetMinBufferSizeInFramesInternal( - channels, sample_type, sampling_frequency_hz)); + return std::max(AudioTrackBridge::GetMinBufferSizeInFrames( + sample_type, channels, sampling_frequency_hz), + s_min_required_frames_tester->GetMinBufferSizeInFrames( + channels, sample_type, sampling_frequency_hz)); } -AudioTrackAudioSinkType::AudioTrackAudioSinkType() - : min_required_frames_tester_(kMaxRequiredFrames, - kRequiredFramesIncrement, - kMinStablePlayedFrames) {} +AudioTrackAudioSinkType::AudioTrackAudioSinkType() = default; SbAudioSink AudioTrackAudioSinkType::Create( int channels, @@ -498,83 +460,32 @@ SbAudioSink AudioTrackAudioSinkType::Create( return audio_sink; } -void AudioTrackAudioSinkType::TestMinRequiredFrames() { - auto onMinRequiredFramesForWebAudioReceived = - [&](int number_of_channels, SbMediaAudioSampleType sample_type, - int sample_rate, int min_required_frames) { - bool has_remote_audio_output = HasRemoteAudioOutput(); - SB_LOG(INFO) << "Received min required frames " << min_required_frames - << " for " << number_of_channels << " channels, " - << sample_rate << "hz, with " - << (has_remote_audio_output ? "remote" : "local") - << " audio output device."; - ScopedLock lock(min_required_frames_map_mutex_); - has_remote_audio_output_ = has_remote_audio_output; - min_required_frames_map_[sample_rate] = - std::min(min_required_frames, has_remote_audio_output_ - ? kMaxRequiredFramesRemote - : kMaxRequiredFramesLocal); - }; - - SbMediaAudioSampleType sample_type = kSbMediaAudioSampleTypeFloat32; - if (!SbAudioSinkIsAudioSampleTypeSupported(sample_type)) { - sample_type = kSbMediaAudioSampleTypeInt16Deprecated; - SB_DCHECK(SbAudioSinkIsAudioSampleTypeSupported(sample_type)); - } - min_required_frames_tester_.AddTest(2, sample_type, kSampleFrequency48000, - onMinRequiredFramesForWebAudioReceived, - 8 * 1024); - min_required_frames_tester_.AddTest(2, sample_type, kSampleFrequency22050, - onMinRequiredFramesForWebAudioReceived, - 4 * 1024); - min_required_frames_tester_.Start(); -} - -int AudioTrackAudioSinkType::GetMinBufferSizeInFramesInternal( - int channels, - SbMediaAudioSampleType sample_type, - int sampling_frequency_hz) { - bool has_remote_audio_output = HasRemoteAudioOutput(); - ScopedLock lock(min_required_frames_map_mutex_); - if (has_remote_audio_output == has_remote_audio_output_) { - // There's no audio output type change, we can use the numbers we got from - // the tests at app launch. - if (sampling_frequency_hz <= kSampleFrequency22050) { - if (min_required_frames_map_.find(kSampleFrequency22050) != - min_required_frames_map_.end()) { - return min_required_frames_map_[kSampleFrequency22050]; - } - } else if (sampling_frequency_hz <= kSampleFrequency48000) { - if (min_required_frames_map_.find(kSampleFrequency48000) != - min_required_frames_map_.end()) { - return min_required_frames_map_[kSampleFrequency48000]; - } - } - } - // We cannot find a matched result from our tests, or the audio output type - // has changed. We use the default max required frames to avoid underruns. - return has_remote_audio_output ? kMaxRequiredFramesRemote - : kMaxRequiredFramesLocal; -} - } // namespace shared } // namespace android } // namespace starboard // static void SbAudioSinkPrivate::PlatformInitialize() { - SB_DCHECK(!audio_track_audio_sink_type_); - audio_track_audio_sink_type_ = + SB_DCHECK(!s_min_required_frames_tester); + SB_DCHECK(!s_audio_track_audio_sink_type); + + s_min_required_frames_tester = + new starboard::android::shared::MinRequiredFramesTester; + + s_audio_track_audio_sink_type = new starboard::android::shared::AudioTrackAudioSinkType; - SetPrimaryType(audio_track_audio_sink_type_); + SetPrimaryType(s_audio_track_audio_sink_type); EnableFallbackToStub(); - audio_track_audio_sink_type_->TestMinRequiredFrames(); } // static void SbAudioSinkPrivate::PlatformTearDown() { - SB_DCHECK(audio_track_audio_sink_type_ == GetPrimaryType()); - SetPrimaryType(NULL); - delete audio_track_audio_sink_type_; - audio_track_audio_sink_type_ = NULL; + SB_DCHECK(s_audio_track_audio_sink_type == GetPrimaryType()); + + SetPrimaryType(nullptr); + delete s_audio_track_audio_sink_type; + s_audio_track_audio_sink_type = nullptr; + + delete s_min_required_frames_tester; + s_min_required_frames_tester = nullptr; } diff --git a/starboard/android/shared/audio_track_audio_sink_type.h b/starboard/android/shared/audio_track_audio_sink_type.h index 9d59c8049e92..1a26f8f307d9 100644 --- a/starboard/android/shared/audio_track_audio_sink_type.h +++ b/starboard/android/shared/audio_track_audio_sink_type.h @@ -19,11 +19,9 @@ #include #include -#include #include #include -#include "starboard/android/shared/audio_sink_min_required_frames_tester.h" #include "starboard/android/shared/audio_track_bridge.h" #include "starboard/android/shared/jni_env_ext.h" #include "starboard/android/shared/jni_utils.h" @@ -85,19 +83,6 @@ class AudioTrackAudioSinkType : public SbAudioSinkPrivate::Type { } delete audio_sink; } - - void TestMinRequiredFrames(); - - private: - int GetMinBufferSizeInFramesInternal(int channels, - SbMediaAudioSampleType sample_type, - int sampling_frequency_hz); - - Mutex min_required_frames_map_mutex_; - // The minimum frames required to avoid underruns of different frequencies. - std::map min_required_frames_map_; - MinRequiredFramesTester min_required_frames_tester_; - bool has_remote_audio_output_ = false; }; class AudioTrackAudioSink : public SbAudioSinkPrivate {