Interrupt playback when new bluetooth audio output device is connected Change-Id: I2bf08484e7912cb1a1a968b24394db2534544750 Signed-off-by: Eugene Mutavchi <Ievgen_Mutavchi@comcast.com>
diff --git a/src/third_party/starboard/rdk/shared/player/player_internal.cc b/src/third_party/starboard/rdk/shared/player/player_internal.cc index 5fdc3fb..0236174 100644 --- a/src/third_party/starboard/rdk/shared/player/player_internal.cc +++ b/src/third_party/starboard/rdk/shared/player/player_internal.cc
@@ -1113,6 +1113,7 @@ GstElement* GetPipeline() const { return pipeline_; } bool IsValid() const { return SbThreadIsValid(playback_thread_); } bool HasMaxVideoCaps() const { return !max_video_capabilities_.empty(); } + void AudioConfigurationChanged(); private: enum class State { @@ -1400,6 +1401,13 @@ #endif return true; } + + void AudioConfigurationChanged() { + ::starboard::ScopedLock lock(mutex_); + for(const auto& p: players_) { + p->AudioConfigurationChanged(); + } + } }; SB_ONCE_INITIALIZE_FUNCTION(PlayerRegistry, GetPlayerRegistry); @@ -3069,6 +3077,15 @@ } } +void PlayerImpl::AudioConfigurationChanged() { + GST_WARNING_OBJECT(pipeline_, "Emitting an error on audio configuration change."); + DispatchOnWorkerThread( + new PlayerErrorTask( + player_error_func_, player_, context_, + kSbPlayerErrorCapabilityChanged, "Audio device capability changed")); + +} + } // namespace void ForceStop() { @@ -3076,6 +3093,11 @@ GetPlayerRegistry()->ForceStop(); } +void AudioConfigurationChanged() { + using third_party::starboard::rdk::shared::player::GetPlayerRegistry; + GetPlayerRegistry()->AudioConfigurationChanged(); +} + } // namespace player } // namespace shared } // namespace rdk
diff --git a/src/third_party/starboard/rdk/shared/player/player_internal.h b/src/third_party/starboard/rdk/shared/player/player_internal.h index 2116198..845031f 100644 --- a/src/third_party/starboard/rdk/shared/player/player_internal.h +++ b/src/third_party/starboard/rdk/shared/player/player_internal.h
@@ -45,6 +45,9 @@ virtual void SetBounds(int zindex, int x, int y, int w, int h) = 0; }; +void ForceStop(); +void AudioConfigurationChanged(); + } // namespace player } // namespace shared } // namespace rdk @@ -75,6 +78,7 @@ using third_party::starboard::rdk::shared::player::Player; return Player::MaxNumberOfSamplesPerWrite(); } + std::unique_ptr<third_party::starboard::rdk::shared::player::Player> player_; };
diff --git a/src/third_party/starboard/rdk/shared/rdkservices.cc b/src/third_party/starboard/rdk/shared/rdkservices.cc index a18a9dd..a7061ae 100644 --- a/src/third_party/starboard/rdk/shared/rdkservices.cc +++ b/src/third_party/starboard/rdk/shared/rdkservices.cc
@@ -52,6 +52,7 @@ #include "third_party/starboard/rdk/shared/accessibility_data.h" #include "third_party/starboard/rdk/shared/log_override.h" #include "third_party/starboard/rdk/shared/application_rdk.h" +#include "third_party/starboard/rdk/shared/player/player_internal.h" MODULE_NAME_DECLARATION(BUILD_REFERENCE); @@ -1185,6 +1186,7 @@ Add(_T("deviceType"), &Devicetype); } }; + struct ConnectedDevicesData : public Core::JSON::Container { ConnectedDevicesData() : Core::JSON::Container() { @@ -1195,11 +1197,52 @@ Core::JSON::ArrayType<DeviceDetailsData> Connecteddevices; }; - void OnBluetoothStatusChanged(const Core::JSON::String&) { ForceNeedsRefresh(); } + struct StatusChangedData : public Core::JSON::Container { + StatusChangedData() + : Core::JSON::Container() { + Init(); + } + StatusChangedData(const StatusChangedData& other) + : Core::JSON::Container() + , Name(other.Name) + , Newstatus(other.Newstatus) + , Devicetype(other.Devicetype) + , Connected(other.Connected) { + Init(); + } + StatusChangedData& operator=(const StatusChangedData& rhs) { + Name = rhs.Name; + Devicetype = rhs.Devicetype; + Newstatus = rhs.Newstatus; + Connected = rhs.Connected; + return *this; + } + Core::JSON::String Name; + Core::JSON::String Newstatus; + Core::JSON::String Devicetype; + Core::JSON::Boolean Connected; + private: + void Init() { + Add(_T("name"), &Name); + Add(_T("newStatus"), &Newstatus); + Add(_T("deviceType"), &Devicetype); + Add(_T("connected"), &Connected); + } + }; + + void OnBluetoothStatusChanged(const StatusChangedData&); void Refresh(); void ForceNeedsRefresh() { needs_refresh_.store(true); } void InitAudioConfigurationForAudioPort(const std::string& port_name, SbMediaAudioConfiguration* out); + static bool IsAudioOutputDeviceType(const std::string& type) { + return (strcasestr(type.c_str(), "audio") || + strcasestr(type.c_str(), "headset") || + strcasestr(type.c_str(), "headphones") || + strcasestr(type.c_str(), "loudspeaker") || + strcasestr(type.c_str(), "handsfree")); + }; + ServiceLink device_info_ { kDeviceInfoCallsign }; ServiceLink bluetooth_ { kBluetoothCallsign }; @@ -1241,6 +1284,39 @@ return; } +void DeviceInfoImpl::OnBluetoothStatusChanged(const StatusChangedData& data) { + const char kConnectionChange[] = "CONNECTION_CHANGE"; + + SB_LOG(INFO) << "Bluetooth status changed, new status: " << data.Newstatus.Value(); + + if (data.Newstatus.Value().compare(0, sizeof(kConnectionChange), kConnectionChange) != 0) + return; + + if (!IsAudioOutputDeviceType(data.Devicetype.Value())) + return; + + SB_LOG(INFO) << "Audio device change." + << " name: '" << data.Name.Value() << "'," + << " type: '" << data.Devicetype.Value() << "'," + << " connected: " << (data.Connected.Value() ? "yes" : "no"); + + const auto& hasBluetooth = [&]() -> bool { + ::starboard::ScopedLock lock(mutex_); + return std::find_if(audio_configurations_.begin(), audio_configurations_.end(), [](const SbMediaAudioConfiguration& cfg) { + return cfg.connector == kSbMediaAudioConnectorBluetooth; + }) != audio_configurations_.end(); + }; + + ForceNeedsRefresh(); + + // Interrupt player only if new wireless device got connected + if (data.Connected.Value() && !hasBluetooth()) { + SbEventSchedule([](void*) { + player::AudioConfigurationChanged(); + }, nullptr, 0); + } +} + void DeviceInfoImpl::Refresh() { if ( !needs_refresh_.load() || !needs_refresh_.exchange( false ) ) return; @@ -1252,7 +1328,7 @@ VariableTimeout timeout { kDefaultTimeoutMs, 1000 }; if (did_subscribe_.load() == false && did_subscribe_.exchange(true) == false) { - rc = bluetooth_.Subscribe<Core::JSON::String>(timeout.value(), "onStatusChanged", &DeviceInfoImpl::OnBluetoothStatusChanged, this); + rc = bluetooth_.Subscribe<StatusChangedData>(timeout.value(), "onStatusChanged", &DeviceInfoImpl::OnBluetoothStatusChanged, this); if (Core::ERROR_NONE != rc && Core::ERROR_DUPLICATE_KEY != rc) { SB_LOG(ERROR) << "Failed to subscribe to '" << kBluetoothCallsign << ".onStatusChanged' event, rc=" << rc @@ -1292,16 +1368,9 @@ } else { auto index(connected_devices.Connecteddevices.Elements()); while (index.Next()) { - const auto& isAudioOutputDeviceType = [](const std::string& type) -> bool { - return (strcasestr(type.c_str(), "audio") || - strcasestr(type.c_str(), "headset") || - strcasestr(type.c_str(), "headphones") || - strcasestr(type.c_str(), "loudspeaker") || - strcasestr(type.c_str(), "handsfree")); - }; const auto& device_details = index.Current(); SB_LOG(INFO) << "Bluetooth device name: " << device_details.Name.Value() << ", type: " << device_details.Devicetype.Value(); - if (isAudioOutputDeviceType(device_details.Devicetype.Value())) { + if (IsAudioOutputDeviceType(device_details.Devicetype.Value())) { SbMediaAudioConfiguration configuration; InitAudioConfigurationForAudioPort("bluetooth", &configuration); audio_configs.push_back(std::move(configuration));