system_media_controls_win.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/system_media_controls/win/system_media_controls_win.h"
  5. #include <systemmediatransportcontrolsinterop.h>
  6. #include <windows.media.control.h>
  7. #include <wrl/client.h>
  8. #include <wrl/event.h>
  9. #include "base/strings/string_piece.h"
  10. #include "base/strings/utf_string_conversions.h"
  11. #include "base/win/core_winrt_util.h"
  12. #include "base/win/scoped_hstring.h"
  13. #include "components/system_media_controls/system_media_controls_observer.h"
  14. #include "ui/gfx/codec/png_codec.h"
  15. #include "ui/gfx/win/singleton_hwnd.h"
  16. namespace system_media_controls {
  17. // static
  18. std::unique_ptr<SystemMediaControls> SystemMediaControls::Create(
  19. const std::string& product_name) {
  20. auto service = std::make_unique<internal::SystemMediaControlsWin>();
  21. if (service->Initialize())
  22. return std::move(service);
  23. return nullptr;
  24. }
  25. namespace internal {
  26. using ABI::Windows::Media::ISystemMediaTransportControls;
  27. using ABI::Windows::Media::ISystemMediaTransportControlsButtonPressedEventArgs;
  28. using ABI::Windows::Media::SystemMediaTransportControls;
  29. using ABI::Windows::Media::SystemMediaTransportControlsButton;
  30. using ABI::Windows::Media::SystemMediaTransportControlsButtonPressedEventArgs;
  31. using ABI::Windows::Storage::Streams::IDataWriter;
  32. using ABI::Windows::Storage::Streams::IDataWriterFactory;
  33. using ABI::Windows::Storage::Streams::IOutputStream;
  34. using ABI::Windows::Storage::Streams::IRandomAccessStream;
  35. using ABI::Windows::Storage::Streams::IRandomAccessStreamReference;
  36. using ABI::Windows::Storage::Streams::IRandomAccessStreamReferenceStatics;
  37. // static
  38. SystemMediaControlsWin* SystemMediaControlsWin::GetInstance() {
  39. return instance_;
  40. }
  41. // static
  42. SystemMediaControlsWin* SystemMediaControlsWin::instance_ = nullptr;
  43. SystemMediaControlsWin::SystemMediaControlsWin() {
  44. DCHECK(!instance_);
  45. instance_ = this;
  46. }
  47. SystemMediaControlsWin::~SystemMediaControlsWin() {
  48. if (has_valid_registration_token_) {
  49. DCHECK(system_media_controls_);
  50. system_media_controls_->remove_ButtonPressed(registration_token_);
  51. ClearMetadata();
  52. }
  53. DCHECK_EQ(instance_, this);
  54. instance_ = nullptr;
  55. }
  56. bool SystemMediaControlsWin::Initialize() {
  57. if (attempted_to_initialize_)
  58. return initialized_;
  59. attempted_to_initialize_ = true;
  60. if (!base::win::ResolveCoreWinRTDelayload() ||
  61. !base::win::ScopedHString::ResolveCoreWinRTStringDelayload()) {
  62. return false;
  63. }
  64. Microsoft::WRL::ComPtr<ISystemMediaTransportControlsInterop> interop;
  65. HRESULT hr = base::win::GetActivationFactory<
  66. ISystemMediaTransportControlsInterop,
  67. RuntimeClass_Windows_Media_SystemMediaTransportControls>(&interop);
  68. if (FAILED(hr))
  69. return false;
  70. hr = interop->GetForWindow(gfx::SingletonHwnd::GetInstance()->hwnd(),
  71. IID_PPV_ARGS(&system_media_controls_));
  72. if (FAILED(hr))
  73. return false;
  74. auto handler =
  75. Microsoft::WRL::Callback<ABI::Windows::Foundation::ITypedEventHandler<
  76. SystemMediaTransportControls*,
  77. SystemMediaTransportControlsButtonPressedEventArgs*>>(
  78. &SystemMediaControlsWin::ButtonPressed);
  79. hr = system_media_controls_->add_ButtonPressed(handler.Get(),
  80. &registration_token_);
  81. if (FAILED(hr))
  82. return false;
  83. has_valid_registration_token_ = true;
  84. hr = system_media_controls_->put_IsEnabled(true);
  85. if (FAILED(hr))
  86. return false;
  87. hr = system_media_controls_->get_DisplayUpdater(&display_updater_);
  88. if (FAILED(hr))
  89. return false;
  90. // The current MediaSession API implementation matches the SMTC music type
  91. // most closely, since MediaSession has the artist property which the SMTC
  92. // only presents to music playback types.
  93. hr = display_updater_->put_Type(
  94. ABI::Windows::Media::MediaPlaybackType::MediaPlaybackType_Music);
  95. if (FAILED(hr))
  96. return false;
  97. hr = display_updater_->get_MusicProperties(&display_properties_);
  98. if (FAILED(hr))
  99. return false;
  100. initialized_ = true;
  101. return true;
  102. }
  103. void SystemMediaControlsWin::AddObserver(
  104. SystemMediaControlsObserver* observer) {
  105. observers_.AddObserver(observer);
  106. if (initialized_)
  107. observer->OnServiceReady();
  108. }
  109. void SystemMediaControlsWin::RemoveObserver(
  110. SystemMediaControlsObserver* observer) {
  111. observers_.RemoveObserver(observer);
  112. }
  113. void SystemMediaControlsWin::SetEnabled(bool enabled) {
  114. DCHECK(initialized_);
  115. HRESULT hr = system_media_controls_->put_IsEnabled(enabled);
  116. DCHECK(SUCCEEDED(hr));
  117. }
  118. void SystemMediaControlsWin::SetIsNextEnabled(bool value) {
  119. DCHECK(initialized_);
  120. HRESULT hr = system_media_controls_->put_IsNextEnabled(value);
  121. DCHECK(SUCCEEDED(hr));
  122. }
  123. void SystemMediaControlsWin::SetIsPreviousEnabled(bool value) {
  124. DCHECK(initialized_);
  125. HRESULT hr = system_media_controls_->put_IsPreviousEnabled(value);
  126. DCHECK(SUCCEEDED(hr));
  127. }
  128. void SystemMediaControlsWin::SetIsPlayPauseEnabled(bool value) {
  129. DCHECK(initialized_);
  130. HRESULT hr = system_media_controls_->put_IsPlayEnabled(value);
  131. DCHECK(SUCCEEDED(hr));
  132. hr = system_media_controls_->put_IsPauseEnabled(value);
  133. DCHECK(SUCCEEDED(hr));
  134. }
  135. void SystemMediaControlsWin::SetIsStopEnabled(bool value) {
  136. DCHECK(initialized_);
  137. HRESULT hr = system_media_controls_->put_IsStopEnabled(value);
  138. DCHECK(SUCCEEDED(hr));
  139. }
  140. void SystemMediaControlsWin::SetPlaybackStatus(PlaybackStatus status) {
  141. DCHECK(initialized_);
  142. HRESULT hr =
  143. system_media_controls_->put_PlaybackStatus(GetSmtcPlaybackStatus(status));
  144. DCHECK(SUCCEEDED(hr));
  145. }
  146. void SystemMediaControlsWin::SetTitle(const std::u16string& title) {
  147. DCHECK(initialized_);
  148. DCHECK(display_properties_);
  149. base::win::ScopedHString h_title =
  150. base::win::ScopedHString::Create(base::UTF16ToWide(title));
  151. HRESULT hr = display_properties_->put_Title(h_title.get());
  152. DCHECK(SUCCEEDED(hr));
  153. }
  154. void SystemMediaControlsWin::SetArtist(const std::u16string& artist) {
  155. DCHECK(initialized_);
  156. DCHECK(display_properties_);
  157. base::win::ScopedHString h_artist =
  158. base::win::ScopedHString::Create(base::UTF16ToWide(artist));
  159. HRESULT hr = display_properties_->put_Artist(h_artist.get());
  160. DCHECK(SUCCEEDED(hr));
  161. }
  162. void SystemMediaControlsWin::SetThumbnail(const SkBitmap& bitmap) {
  163. DCHECK(initialized_);
  164. DCHECK(display_updater_);
  165. // Use |icon_data_writer_| to write the bitmap data into |icon_stream_| so we
  166. // can populate |icon_stream_reference_| and then give it to the SMTC. All of
  167. // these are member variables to avoid a race condition between them being
  168. // destructed and the async operation completing.
  169. base::win::ScopedHString id = base::win::ScopedHString::Create(
  170. RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream);
  171. HRESULT hr = base::win::RoActivateInstance(id.get(), &icon_stream_);
  172. DCHECK(SUCCEEDED(hr));
  173. Microsoft::WRL::ComPtr<IDataWriterFactory> data_writer_factory;
  174. hr = base::win::GetActivationFactory<
  175. IDataWriterFactory, RuntimeClass_Windows_Storage_Streams_DataWriter>(
  176. &data_writer_factory);
  177. DCHECK(SUCCEEDED(hr));
  178. Microsoft::WRL::ComPtr<IOutputStream> output_stream;
  179. hr = icon_stream_.As(&output_stream);
  180. DCHECK(SUCCEEDED(hr));
  181. hr = data_writer_factory->CreateDataWriter(output_stream.Get(),
  182. &icon_data_writer_);
  183. DCHECK(SUCCEEDED(hr));
  184. std::vector<unsigned char> icon_png;
  185. gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &icon_png);
  186. hr = icon_data_writer_->WriteBytes(icon_png.size(), (BYTE*)icon_png.data());
  187. DCHECK(SUCCEEDED(hr));
  188. // Store the written bytes in the stream, an async operation.
  189. Microsoft::WRL::ComPtr<
  190. ABI::Windows::Foundation::IAsyncOperation<unsigned int>>
  191. store_async_operation;
  192. hr = icon_data_writer_->StoreAsync(&store_async_operation);
  193. DCHECK(SUCCEEDED(hr));
  194. // Make a callback that gives the icon to the SMTC once the bits make it into
  195. // |icon_stream_|
  196. auto store_async_callback = Microsoft::WRL::Callback<
  197. ABI::Windows::Foundation::IAsyncOperationCompletedHandler<unsigned int>>(
  198. [this](ABI::Windows::Foundation::IAsyncOperation<unsigned int>* async_op,
  199. ABI::Windows::Foundation::AsyncStatus status) mutable {
  200. // Check the async operation completed successfully.
  201. ABI::Windows::Foundation::IAsyncInfo* async_info;
  202. HRESULT hr = async_op->QueryInterface(
  203. IID_IAsyncInfo, reinterpret_cast<void**>(&async_info));
  204. DCHECK(SUCCEEDED(hr));
  205. async_info->get_ErrorCode(&hr);
  206. if (SUCCEEDED(hr) &&
  207. status == ABI::Windows::Foundation::AsyncStatus::Completed) {
  208. Microsoft::WRL::ComPtr<IRandomAccessStreamReferenceStatics>
  209. reference_statics;
  210. HRESULT result = base::win::GetActivationFactory<
  211. IRandomAccessStreamReferenceStatics,
  212. RuntimeClass_Windows_Storage_Streams_RandomAccessStreamReference>(
  213. &reference_statics);
  214. DCHECK(SUCCEEDED(result));
  215. result = reference_statics->CreateFromStream(icon_stream_.Get(),
  216. &icon_stream_reference_);
  217. DCHECK(SUCCEEDED(result));
  218. result =
  219. display_updater_->put_Thumbnail(icon_stream_reference_.Get());
  220. DCHECK(SUCCEEDED(result));
  221. result = display_updater_->Update();
  222. DCHECK(SUCCEEDED(result));
  223. }
  224. return hr;
  225. });
  226. hr = store_async_operation->put_Completed(store_async_callback.Get());
  227. DCHECK(SUCCEEDED(hr));
  228. }
  229. void SystemMediaControlsWin::ClearThumbnail() {
  230. DCHECK(initialized_);
  231. DCHECK(display_updater_);
  232. HRESULT hr = display_updater_->put_Thumbnail(nullptr);
  233. DCHECK(SUCCEEDED(hr));
  234. hr = display_updater_->Update();
  235. DCHECK(SUCCEEDED(hr));
  236. }
  237. void SystemMediaControlsWin::ClearMetadata() {
  238. DCHECK(initialized_);
  239. DCHECK(display_updater_);
  240. HRESULT hr = display_updater_->ClearAll();
  241. DCHECK(SUCCEEDED(hr));
  242. // To prevent disabled controls and the executable name from showing up in the
  243. // SMTC, we need to tell them that we are disabled.
  244. hr = system_media_controls_->put_IsEnabled(false);
  245. DCHECK(SUCCEEDED(hr));
  246. }
  247. void SystemMediaControlsWin::UpdateDisplay() {
  248. DCHECK(initialized_);
  249. DCHECK(system_media_controls_);
  250. DCHECK(display_updater_);
  251. HRESULT hr = system_media_controls_->put_IsEnabled(true);
  252. DCHECK(SUCCEEDED(hr));
  253. // |ClearAll()| unsets the type, if we don't set it again then the artist
  254. // won't be displayed.
  255. hr = display_updater_->put_Type(
  256. ABI::Windows::Media::MediaPlaybackType::MediaPlaybackType_Music);
  257. DCHECK(SUCCEEDED(hr));
  258. hr = display_updater_->Update();
  259. DCHECK(SUCCEEDED(hr));
  260. }
  261. void SystemMediaControlsWin::OnPlay() {
  262. for (SystemMediaControlsObserver& obs : observers_)
  263. obs.OnPlay();
  264. }
  265. void SystemMediaControlsWin::OnPause() {
  266. for (SystemMediaControlsObserver& obs : observers_)
  267. obs.OnPause();
  268. }
  269. void SystemMediaControlsWin::OnNext() {
  270. for (SystemMediaControlsObserver& obs : observers_)
  271. obs.OnNext();
  272. }
  273. void SystemMediaControlsWin::OnPrevious() {
  274. for (SystemMediaControlsObserver& obs : observers_)
  275. obs.OnPrevious();
  276. }
  277. void SystemMediaControlsWin::OnStop() {
  278. for (SystemMediaControlsObserver& obs : observers_)
  279. obs.OnStop();
  280. }
  281. ABI::Windows::Media::MediaPlaybackStatus
  282. SystemMediaControlsWin::GetSmtcPlaybackStatus(PlaybackStatus status) {
  283. switch (status) {
  284. case PlaybackStatus::kPlaying:
  285. return ABI::Windows::Media::MediaPlaybackStatus::
  286. MediaPlaybackStatus_Playing;
  287. case PlaybackStatus::kPaused:
  288. return ABI::Windows::Media::MediaPlaybackStatus::
  289. MediaPlaybackStatus_Paused;
  290. case PlaybackStatus::kStopped:
  291. return ABI::Windows::Media::MediaPlaybackStatus::
  292. MediaPlaybackStatus_Stopped;
  293. }
  294. NOTREACHED();
  295. return ABI::Windows::Media::MediaPlaybackStatus::MediaPlaybackStatus_Stopped;
  296. }
  297. // static
  298. HRESULT SystemMediaControlsWin::ButtonPressed(
  299. ISystemMediaTransportControls* sender,
  300. ISystemMediaTransportControlsButtonPressedEventArgs* args) {
  301. SystemMediaTransportControlsButton button;
  302. HRESULT hr = args->get_Button(&button);
  303. if (FAILED(hr))
  304. return hr;
  305. SystemMediaControlsWin* impl = GetInstance();
  306. switch (button) {
  307. case SystemMediaTransportControlsButton::
  308. SystemMediaTransportControlsButton_Play:
  309. impl->OnPlay();
  310. break;
  311. case SystemMediaTransportControlsButton::
  312. SystemMediaTransportControlsButton_Pause:
  313. impl->OnPause();
  314. break;
  315. case SystemMediaTransportControlsButton::
  316. SystemMediaTransportControlsButton_Next:
  317. impl->OnNext();
  318. break;
  319. case SystemMediaTransportControlsButton::
  320. SystemMediaTransportControlsButton_Previous:
  321. impl->OnPrevious();
  322. break;
  323. case SystemMediaTransportControlsButton::
  324. SystemMediaTransportControlsButton_Stop:
  325. impl->OnStop();
  326. break;
  327. case SystemMediaTransportControlsButton::
  328. SystemMediaTransportControlsButton_Record:
  329. case SystemMediaTransportControlsButton::
  330. SystemMediaTransportControlsButton_FastForward:
  331. case SystemMediaTransportControlsButton::
  332. SystemMediaTransportControlsButton_Rewind:
  333. case SystemMediaTransportControlsButton::
  334. SystemMediaTransportControlsButton_ChannelUp:
  335. case SystemMediaTransportControlsButton::
  336. SystemMediaTransportControlsButton_ChannelDown:
  337. break;
  338. }
  339. return S_OK;
  340. }
  341. } // namespace internal
  342. } // namespace system_media_controls