Improve GetAudioConfiguration Use "DeviceInfo" to discover "builtin" audio outputs, "org.rdk.Bluetooth" to discover "wireless". Change-Id: I4c6d9db8b8e70c35eaabfa7e4999b33d3aea0194
diff --git a/src/third_party/starboard/rdk/shared/BUILD.gn b/src/third_party/starboard/rdk/shared/BUILD.gn index d27e704..5b65b86 100644 --- a/src/third_party/starboard/rdk/shared/BUILD.gn +++ b/src/third_party/starboard/rdk/shared/BUILD.gn
@@ -262,7 +262,6 @@ "//starboard/shared/starboard/log_mutex.cc", "//starboard/shared/starboard/log_raw_format.cc", "//starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc", - "//starboard/shared/starboard/media/media_get_audio_configuration.cc", "//starboard/shared/starboard/media/media_get_audio_output_count_single_audio_output.cc", "//starboard/shared/starboard/memory.cc", "//starboard/shared/starboard/queue_application.cc", @@ -331,6 +330,7 @@ "main_rdk.cc", "media/gst_media_utils.cc", "media/media_get_audio_buffer_budget.cc", + "media/media_get_audio_configuration.cc", "media/media_get_buffer_alignment.cc", "media/media_get_buffer_allocation_unit.cc", "media/media_get_buffer_garbage_collection_duration_threshold.cc",
diff --git a/src/third_party/starboard/rdk/shared/media/media_get_audio_configuration.cc b/src/third_party/starboard/rdk/shared/media/media_get_audio_configuration.cc new file mode 100644 index 0000000..fa47943 --- /dev/null +++ b/src/third_party/starboard/rdk/shared/media/media_get_audio_configuration.cc
@@ -0,0 +1,40 @@ +// +// Copyright 2020 Comcast Cable Communications Management, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright 2023 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard/media.h" + +#include "third_party/starboard/rdk/shared/rdkservices.h" + +bool SbMediaGetAudioConfiguration( + int output_index, + SbMediaAudioConfiguration* out_configuration) { + return third_party::starboard::rdk::shared::DeviceInfo::GetAudioConfiguration(output_index, out_configuration); +}
diff --git a/src/third_party/starboard/rdk/shared/player/player_get_audio_configuration.cc b/src/third_party/starboard/rdk/shared/player/player_get_audio_configuration.cc index bcc416b..5366f77 100644 --- a/src/third_party/starboard/rdk/shared/player/player_get_audio_configuration.cc +++ b/src/third_party/starboard/rdk/shared/player/player_get_audio_configuration.cc
@@ -40,10 +40,7 @@ SbPlayer player, int index, SbMediaAudioConfiguration* out_audio_configuration) { - if (index == 0) { - return SbMediaGetAudioConfiguration(index, out_audio_configuration); - } - return false; + return SbMediaGetAudioConfiguration(index, out_audio_configuration); } #endif // SB_API_VERSION >= 15
diff --git a/src/third_party/starboard/rdk/shared/rdkservices.cc b/src/third_party/starboard/rdk/shared/rdkservices.cc index 6d776cf..a18a9dd 100644 --- a/src/third_party/starboard/rdk/shared/rdkservices.cc +++ b/src/third_party/starboard/rdk/shared/rdkservices.cc
@@ -30,17 +30,21 @@ #include <interfaces/json/JsonData_HDRProperties.h> #include <interfaces/json/JsonData_PlayerProperties.h> #include <interfaces/json/JsonData_DeviceIdentification.h> +#include <interfaces/json/JsonData_DeviceInfo.h> #ifdef HAS_SECURITY_AGENT #include <securityagent/securityagent.h> #endif #include "starboard/atomic.h" +#include "starboard/audio_sink.h" #include "starboard/event.h" +#include "starboard/media.h" #include "starboard/once.h" #include "starboard/common/atomic.h" #include "starboard/common/condition_variable.h" #include "starboard/common/mutex.h" +#include "starboard/common/media.h" #include "starboard/accessibility.h" #include "starboard/common/file.h" #include "starboard/shared/starboard/media/mime_supportability_cache.h" @@ -68,6 +72,9 @@ const char kTTSCallsign[] = "org.rdk.TextToSpeech.1"; const char kAuthServiceCallsign[] = "org.rdk.AuthService.1"; +const char kDeviceInfoCallsign[] = "DeviceInfo.1"; +const char kBluetoothCallsign[] = "org.rdk.Bluetooth.1"; + const char kAuthServiceExperienceFile[] = "/opt/www/authService/experience.dat"; const uint32_t kPriviligedRequestErrorCode = -32604U; @@ -1139,6 +1146,213 @@ SB_ONCE_INITIALIZE_FUNCTION(NetworkInfoImpl, GetNetworkInfo); +struct DeviceInfoImpl { + + bool GetAudioConfiguration(int index, SbMediaAudioConfiguration* out_audio_configuration); + + void Teardown() { + if (did_subscribe_.load()) { + bluetooth_.Unsubscribe(kDefaultTimeoutMs, "onStatusChanged"); + did_subscribe_.store(false); + } + device_info_.Teardown(); + bluetooth_.Teardown(); + needs_refresh_.store(true); + } + +private: + struct DeviceDetailsData : public Core::JSON::Container { + DeviceDetailsData() + : Core::JSON::Container() { + Init(); + } + DeviceDetailsData(const DeviceDetailsData& other) + : Core::JSON::Container() + , Name(other.Name) + , Devicetype(other.Devicetype) { + Init(); + } + DeviceDetailsData& operator=(const DeviceDetailsData& rhs) { + Name = rhs.Name; + Devicetype = rhs.Devicetype; + return *this; + } + Core::JSON::String Name; + Core::JSON::String Devicetype; + private: + void Init() { + Add(_T("name"), &Name); + Add(_T("deviceType"), &Devicetype); + } + }; + struct ConnectedDevicesData : public Core::JSON::Container { + ConnectedDevicesData() + : Core::JSON::Container() { + Add(_T("connectedDevices"), &Connecteddevices); + } + ConnectedDevicesData(const ConnectedDevicesData&) = delete; + ConnectedDevicesData& operator=(const ConnectedDevicesData&) = delete; + Core::JSON::ArrayType<DeviceDetailsData> Connecteddevices; + }; + + void OnBluetoothStatusChanged(const Core::JSON::String&) { ForceNeedsRefresh(); } + void Refresh(); + void ForceNeedsRefresh() { needs_refresh_.store(true); } + void InitAudioConfigurationForAudioPort(const std::string& port_name, SbMediaAudioConfiguration* out); + + ServiceLink device_info_ { kDeviceInfoCallsign }; + ServiceLink bluetooth_ { kBluetoothCallsign }; + + ::starboard::atomic_bool did_subscribe_ { false }; + ::starboard::atomic_bool needs_refresh_ { true }; + ::starboard::Mutex mutex_; + + std::vector<SbMediaAudioConfiguration> audio_configurations_; + + static constexpr SbMediaAudioConnector kAudioConnectorUnknown = static_cast<SbMediaAudioConnector>(0); +}; + +SB_ONCE_INITIALIZE_FUNCTION(DeviceInfoImpl, GetDeviceInfo); + +void DeviceInfoImpl::InitAudioConfigurationForAudioPort(const std::string& port_name, SbMediaAudioConfiguration* audio_configuration) +{ + if (!audio_configuration) + return; + + const auto& connectorType = [](const std::string& name) { + if (strncasecmp(name.c_str(), "hdmi", 4) == 0) + return kSbMediaAudioConnectorHdmi; + else if (strncasecmp(name.c_str(), "spdif", 5) == 0) + return kSbMediaAudioConnectorSpdif; + else if (strncasecmp(name.c_str(), "bluetooth", 9) == 0) + return kSbMediaAudioConnectorBluetooth; +#if SB_API_VERSION >= 15 + else if (strncasecmp(name.c_str(), "speaker", 7) == 0) + return kSbMediaAudioConnectorBuiltIn; +#endif + return kAudioConnectorUnknown; + }; + + memset(audio_configuration, 0, sizeof(SbMediaAudioConfiguration)); + + audio_configuration->connector = connectorType(port_name); + audio_configuration->coding_type = kSbMediaAudioCodingTypePcm; + audio_configuration->number_of_channels = SbAudioSinkGetMaxChannels(); + return; +} + +void DeviceInfoImpl::Refresh() { + if ( !needs_refresh_.load() || !needs_refresh_.exchange( false ) ) + return; + + std::vector<SbMediaAudioConfiguration> audio_configs; + + uint32_t rc; + bool needs_refresh = false; + 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); + if (Core::ERROR_NONE != rc && Core::ERROR_DUPLICATE_KEY != rc) { + SB_LOG(ERROR) << "Failed to subscribe to '" << kBluetoothCallsign + << ".onStatusChanged' event, rc=" << rc + << " ( " << Core::ErrorToString(rc) << " )"; + } + } + + using namespace WPEFramework::JsonData::DeviceInfo; + SupportedaudioportsData audio_ports; + rc = device_info_.Get(timeout.value(), "supportedaudioports", audio_ports); + if (Core::ERROR_NONE != rc) { + SB_LOG(ERROR) << "'" << kDeviceInfoCallsign << ".supportedaudioports' failed, rc = " << rc + << " ( " << Core::ErrorToString(rc) << " )"; + needs_refresh |= (Core::ERROR_ASYNC_FAILED == rc || Core::ERROR_TIMEDOUT == rc); + } else if (audio_ports.SupportedAudioPorts.Length() == 0) { + SB_LOG(INFO) << "No supported audio ports."; + } else { + auto index(audio_ports.SupportedAudioPorts.Elements()); + while (index.Next()) { + const auto& port_name = index.Current().Value(); + SB_LOG(INFO) << "Supported audio port name: " << port_name; + SbMediaAudioConfiguration configuration; + InitAudioConfigurationForAudioPort(port_name, &configuration); + if (configuration.connector != kAudioConnectorUnknown) + audio_configs.push_back(std::move(configuration)); + } + } + + ConnectedDevicesData connected_devices; + rc = bluetooth_.Get(timeout.value(), "getConnectedDevices", connected_devices); + if (Core::ERROR_NONE != rc) { + SB_LOG(ERROR) << "'" << kBluetoothCallsign << ".getConnectedDevices' failed, rc = " << rc + << " ( " << Core::ErrorToString(rc) << " )"; + needs_refresh |= (Core::ERROR_ASYNC_FAILED == rc || Core::ERROR_TIMEDOUT == rc); + } else if (connected_devices.Connecteddevices.Length() == 0) { + SB_LOG(INFO) << "No bluetooth connected devices."; + } 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())) { + SbMediaAudioConfiguration configuration; + InitAudioConfigurationForAudioPort("bluetooth", &configuration); + audio_configs.push_back(std::move(configuration)); + break; + } + } + } + + if (audio_configs.empty()) { + SbMediaAudioConfiguration configuration; + InitAudioConfigurationForAudioPort("", &configuration); + audio_configs.push_back(std::move(configuration)); + } + + if (needs_refresh) { + SbEventSchedule([](void* data) { + GetDeviceInfo()->ForceNeedsRefresh(); + }, nullptr, kSbTimeSecond); + } + + SB_LOG(INFO) << "Updated audio configuration:"; + for (const auto& config : audio_configs) { + SB_LOG(INFO) << " connector: " << (uint32_t) config.connector << " (" << ::starboard::GetMediaAudioConnectorName(config.connector) << ")"; + } + + ::starboard::ScopedLock lock(mutex_); + std::swap(audio_configurations_, audio_configs); +} + +bool DeviceInfoImpl::GetAudioConfiguration(int output_index, SbMediaAudioConfiguration* out_configuration) { + SB_DCHECK(output_index >= 0); + SB_DCHECK(out_configuration); + + if (!out_configuration || output_index < 0) + return false; + + Refresh(); + + ::starboard::ScopedLock lock(mutex_); + if (output_index < audio_configurations_.size()) { + *out_configuration = audio_configurations_[output_index]; + return true; + } + else if (output_index == 0) { + InitAudioConfigurationForAudioPort("", out_configuration); + return true; + } + + return false; +} + } // namespace ResolutionInfo DisplayInfo::GetResolution() { @@ -1265,10 +1479,15 @@ return GetAuthService()->GetExperience(out); } +bool DeviceInfo::GetAudioConfiguration(int index, SbMediaAudioConfiguration* out_audio_configuration) { + return GetDeviceInfo()->GetAudioConfiguration(index, out_audio_configuration); +} + void TeardownJSONRPCLink() { GetDisplayInfo()->Teardown(); GetTextToSpeech()->Teardown(); GetNetworkInfo()->Teardown(); + GetDeviceInfo()->Teardown(); } } // namespace shared
diff --git a/src/third_party/starboard/rdk/shared/rdkservices.h b/src/third_party/starboard/rdk/shared/rdkservices.h index d810bd0..43721a0 100644 --- a/src/third_party/starboard/rdk/shared/rdkservices.h +++ b/src/third_party/starboard/rdk/shared/rdkservices.h
@@ -25,6 +25,7 @@ struct SbAccessibilityCaptionSettings; struct SbAccessibilityDisplaySettings; +struct SbMediaAudioConfiguration; namespace third_party { namespace starboard { @@ -111,6 +112,10 @@ static bool GetExperience(std::string &out); }; +class DeviceInfo { +public: + static bool GetAudioConfiguration(int index, SbMediaAudioConfiguration* out_audio_configuration); +}; void TeardownJSONRPCLink();