// Copyright 2023 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CHROME_BROWSER_MEDIA_PREFS_CAPTURE_DEVICE_RANKING_H_ #define CHROME_BROWSER_MEDIA_PREFS_CAPTURE_DEVICE_RANKING_H_ #include "chrome/browser/media/prefs/pref_names.h" #include "chrome/common/pref_names.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" #include "media/audio/audio_device_description.h" #include "media/capture/video/video_capture_device_info.h" #include "third_party/blink/public/common/mediastream/media_devices.h" #include "third_party/blink/public/common/mediastream/media_stream_request.h" #include <string> namespace media_prefs::internal { // Returns a stable device id given `device_info`. This isn't guaranteed // to be unique, but it is stable across reboots and device plug/unplug. std::string DeviceInfoToStableId( const media::VideoCaptureDeviceInfo& device_info); std::string DeviceInfoToStableId( const media::AudioDeviceDescription& device_info); std::string DeviceInfoToStableId(const blink::WebMediaDeviceInfo& device_info); std::string DeviceInfoToStableId(const blink::MediaStreamDevice& device_info); // Returns a unique id given `device_info`. This is unique for the current // session, but isn't guaranteed to be stable through reboots or device // plug/unplug. std::string DeviceInfoToUniqueId( const media::VideoCaptureDeviceInfo& device_info); std::string DeviceInfoToUniqueId( const media::AudioDeviceDescription& device_info); std::string DeviceInfoToUniqueId(const blink::WebMediaDeviceInfo& device_info); std::string DeviceInfoToUniqueId(const blink::MediaStreamDevice& device_info); } // namespace media_prefs::internal namespace { DeviceInfoToStableId; DeviceInfoToUniqueId; // Returns the rank index for the passed `info`. If `info` doesn't have a // rank then return size of `id_to_rank` because that will rank lower than // any of the ranked devices. template <typename T> size_t GetRank(const T& info, const base::flat_map<std::string, size_t>& id_to_rank) { … } // Map device ids to a numeric ranking. Smaller values are more preferred. template <typename Iterator> base::flat_map<std::string, size_t> GetIdToRankMap(Iterator begin, Iterator end) { … } template <typename T> const base::Value::List& GetAndMaybeMigratePref(PrefService& prefs, const std::string& pref_name, std::vector<T>& device_infos); // Reorders the passed `device_infos`, so that ranked devices are ordered at the // beginning of the list. Devices don't have a ranking will retain their // ordering, but will be shifted below ranked devices. // // This must be run on the UI thread as it reads from the PrefService. template <typename T> void PreferenceRankDeviceInfos(PrefService& prefs, const std::string& pref_name, std::vector<T>& device_infos) { … } // Updates device ranking given a preferred device and the list of competing // devices. // // The algorithm is as follows: // 1. Identify the best ranked device among the current devices // a. If a device isn't found in the ranking list, then it is appended // 2. If the best ranked device is the preferred device, then exit // 3. Else, move the preferred device to be just before the best ranked device // // The goal of the algorithm is to ensure that given the same set of devices we // would get the same device ranking. // // Example case: // Update 1 - // The user selects the USB mic as preferred, and the other current // devices are at the end of the list in the order enumerated by the OS. // current ranking: [] // preferred_device_iter: 'USB Camera Mic', // current_device_infos: 'bluetooth mic', 'builtin mic', 'USB Camera Mic' // // updated ranking: 'USB Camera Mic', 'bluetooth mic', 'builtin mic' // // Update 2 - // The user disconnects the USB camera because they left their desk // and they select the 'builtin mic' as preferred. // current ranking: 'USB Camera Mic', 'builtin mic', 'bluetooth mic' // preferred_device_iter: 'builtin mic' // current_deivce_infos: 'bluetooth mic', 'builtin mic' // // updated ranking: 'USB Camera Mic', 'builtin mic', 'bluetooth mic' // // Note that the second update leaves the 'USB Camera Mic' as preferred over the // other two mics because it wasn't present to be considered. // // This must be run on the UI thread as it read/writes to the PrefService. template <typename T> void UpdateDevicePreferenceRanking( PrefService& prefs, const std::string& pref_name, const typename std::vector<T>::const_iterator preferred_device_iter, const std::vector<T>& current_device_infos) { … } // Get the value of the ranking pref. // // If the ranking pref is unset, it will be initialized from the default device // pref. If the default device pref is unset or the device isn't in // `device_infos`, then the ranking pref will remain uninitialized. // `pref_name` is required to be in {kAudioInputUserPreferenceRanking, // kVideoInputUserPreferenceRanking}. // // Example scenarios: // For all scenarios the user has set dev2 as the default and updated the // browser. // // Scenario A: // 1) User connects [dev1, dev2] // 2) User visits a website that calls enumerateDevices or getUserMedia // 3) Device ranking pref is updated to [dev2, dev1] // 4) All subsequent usages refer to the device ranking pref and ignore legacy // default pref // // Scenario B: // 1) User connects [dev1] // 2) User visits a website that calls enumerateDevices or getUserMedia // 3) Device ranking pref remains uninitialized because the default device // isn't present // 4) User connects [dev1, dev2] // 5) User visits a website that calls enumerateDevices or getUserMedia // 6) Device ranking pref is updated to [dev2, dev1] // 7) All subsequent usages refer to the device ranking pref and // ignore legacy default pref. // // Scenario C: // 1) User connects [dev1] // 2) User visits a website that calls enumerateDevices or getUserMedia // 3) Device ranking pref remains uninitialized because the default device // isn't present // 4) User expresses preference for dev1 in permission bubble or // Chrome settings // 5) Device ranking pref is updated to [dev1] // 6) All subsequent usages refer to the device ranking pref and ignore legacy // default pref. // // TODO(crbug.com/311205211): Remove this special initialization logic once the // default device pref is removed. template <typename T> const base::Value::List& GetAndMaybeMigratePref(PrefService& prefs, const std::string& pref_name, std::vector<T>& device_infos) { … } } // namespace namespace media_prefs { void RegisterUserPrefs(PrefRegistrySimple* registry); template <typename T> void PreferenceRankAudioDeviceInfos(PrefService& prefs, std::vector<T>& device_infos) { … } template <typename T> void PreferenceRankVideoDeviceInfos(PrefService& prefs, std::vector<T>& device_infos) { … } template <typename T> void UpdateAudioDevicePreferenceRanking( PrefService& prefs, const typename std::vector<T>::const_iterator preferred_device_iter, const std::vector<T>& current_device_infos) { … } template <typename T> void UpdateVideoDevicePreferenceRanking( PrefService& prefs, const typename std::vector<T>::const_iterator preferred_device_iter, const std::vector<T>& current_device_infos) { … } } // namespace media_prefs #endif // CHROME_BROWSER_MEDIA_PREFS_CAPTURE_DEVICE_RANKING_H_