renderer_controller.cc 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. // Copyright 2016 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 "media/remoting/renderer_controller.h"
  5. #include "base/bind.h"
  6. #include "base/logging.h"
  7. #include "base/time/default_tick_clock.h"
  8. #include "base/time/tick_clock.h"
  9. #include "base/time/time.h"
  10. #include "build/build_config.h"
  11. #include "media/remoting/metrics.h"
  12. #if BUILDFLAG(IS_ANDROID)
  13. #include "media/base/android/media_codec_util.h"
  14. #endif
  15. namespace media {
  16. namespace remoting {
  17. using mojom::RemotingSinkAudioCapability;
  18. using mojom::RemotingSinkFeature;
  19. using mojom::RemotingSinkVideoCapability;
  20. namespace {
  21. // The duration to delay the start of media remoting to ensure all preconditions
  22. // are held stable before switching to media remoting.
  23. constexpr base::TimeDelta kDelayedStart = base::Seconds(5);
  24. constexpr int kPixelsPerSec4k = 3840 * 2160 * 30; // 4k 30fps.
  25. constexpr int kPixelsPerSec2k = 1920 * 1080 * 30; // 1080p 30fps.
  26. // The minimum media element duration that is allowed for media remoting.
  27. // Frequent switching into and out of media remoting for short-duration media
  28. // can feel "janky" to the user.
  29. constexpr double kMinRemotingMediaDurationInSec = 60;
  30. StopTrigger GetStopTrigger(mojom::RemotingStopReason reason) {
  31. switch (reason) {
  32. case mojom::RemotingStopReason::ROUTE_TERMINATED:
  33. return ROUTE_TERMINATED;
  34. case mojom::RemotingStopReason::SOURCE_GONE:
  35. return MEDIA_ELEMENT_DESTROYED;
  36. case mojom::RemotingStopReason::MESSAGE_SEND_FAILED:
  37. return MESSAGE_SEND_FAILED;
  38. case mojom::RemotingStopReason::DATA_SEND_FAILED:
  39. return DATA_SEND_FAILED;
  40. case mojom::RemotingStopReason::UNEXPECTED_FAILURE:
  41. return UNEXPECTED_FAILURE;
  42. case mojom::RemotingStopReason::SERVICE_GONE:
  43. return SERVICE_GONE;
  44. case mojom::RemotingStopReason::USER_DISABLED:
  45. return USER_DISABLED;
  46. case mojom::RemotingStopReason::LOCAL_PLAYBACK:
  47. // This RemotingStopReason indicates the RendererController initiated the
  48. // session shutdown in the immediate past, and the trigger for that should
  49. // have already been recorded in the metrics. Here, this is just duplicate
  50. // feedback from the sink for that same event. Return UNKNOWN_STOP_TRIGGER
  51. // because this reason can not be a stop trigger and it would be a logic
  52. // flaw for this value to be recorded in the metrics.
  53. return UNKNOWN_STOP_TRIGGER;
  54. }
  55. return UNKNOWN_STOP_TRIGGER; // To suppress compiler warning on Windows.
  56. }
  57. MediaObserverClient::ReasonToSwitchToLocal GetSwitchReason(
  58. StopTrigger stop_trigger) {
  59. switch (stop_trigger) {
  60. case FRAME_DROP_RATE_HIGH:
  61. case PACING_TOO_SLOWLY:
  62. return MediaObserverClient::ReasonToSwitchToLocal::POOR_PLAYBACK_QUALITY;
  63. case EXITED_FULLSCREEN:
  64. case BECAME_AUXILIARY_CONTENT:
  65. case DISABLED_BY_PAGE:
  66. case USER_DISABLED:
  67. case UNKNOWN_STOP_TRIGGER:
  68. return MediaObserverClient::ReasonToSwitchToLocal::NORMAL;
  69. case UNSUPPORTED_AUDIO_CODEC:
  70. case UNSUPPORTED_VIDEO_CODEC:
  71. case UNSUPPORTED_AUDIO_AND_VIDEO_CODECS:
  72. case DECRYPTION_ERROR:
  73. case RECEIVER_INITIALIZE_FAILED:
  74. case RECEIVER_PIPELINE_ERROR:
  75. case PEERS_OUT_OF_SYNC:
  76. case RPC_INVALID:
  77. case DATA_PIPE_CREATE_ERROR:
  78. case MOJO_DISCONNECTED:
  79. case DATA_PIPE_WRITE_ERROR:
  80. case MESSAGE_SEND_FAILED:
  81. case DATA_SEND_FAILED:
  82. case UNEXPECTED_FAILURE:
  83. return MediaObserverClient::ReasonToSwitchToLocal::PIPELINE_ERROR;
  84. case ROUTE_TERMINATED:
  85. case MEDIA_ELEMENT_DESTROYED:
  86. case START_RACE:
  87. case SERVICE_GONE:
  88. return MediaObserverClient::ReasonToSwitchToLocal::ROUTE_TERMINATED;
  89. }
  90. // To suppress compiler warning on Windows.
  91. return MediaObserverClient::ReasonToSwitchToLocal::ROUTE_TERMINATED;
  92. }
  93. } // namespace
  94. RendererController::RendererController(
  95. mojo::PendingReceiver<mojom::RemotingSource> source_receiver,
  96. mojo::PendingRemote<mojom::Remoter> remoter)
  97. #if BUILDFLAG(ENABLE_MEDIA_REMOTING_RPC)
  98. : rpc_messenger_([this](std::vector<uint8_t> message) {
  99. SendMessageToSink(std::move(message));
  100. }),
  101. #else
  102. :
  103. #endif
  104. receiver_(this, std::move(source_receiver)),
  105. remoter_(std::move(remoter)),
  106. clock_(base::DefaultTickClock::GetInstance()) {
  107. DCHECK(remoter_);
  108. }
  109. RendererController::~RendererController() {
  110. DCHECK(thread_checker_.CalledOnValidThread());
  111. SetClient(nullptr);
  112. }
  113. void RendererController::OnSinkAvailable(
  114. mojom::RemotingSinkMetadataPtr metadata) {
  115. DCHECK(thread_checker_.CalledOnValidThread());
  116. sink_metadata_ = *metadata;
  117. if (!SinkSupportsRemoting()) {
  118. OnSinkGone();
  119. return;
  120. }
  121. UpdateAndMaybeSwitch(SINK_AVAILABLE, UNKNOWN_STOP_TRIGGER);
  122. }
  123. void RendererController::OnSinkGone() {
  124. DCHECK(thread_checker_.CalledOnValidThread());
  125. // Prevent the clients to start any future remoting sessions. Won't affect the
  126. // behavior of the currently-running session (if any).
  127. sink_metadata_ = mojom::RemotingSinkMetadata();
  128. }
  129. void RendererController::OnStarted() {
  130. DCHECK(thread_checker_.CalledOnValidThread());
  131. VLOG(1) << "Remoting started successively.";
  132. if (remote_rendering_started_ && client_) {
  133. metrics_recorder_.DidStartSession();
  134. client_->SwitchToRemoteRenderer(sink_metadata_.friendly_name);
  135. }
  136. }
  137. void RendererController::OnStartFailed(mojom::RemotingStartFailReason reason) {
  138. DCHECK(thread_checker_.CalledOnValidThread());
  139. VLOG(1) << "Failed to start remoting:" << reason;
  140. if (remote_rendering_started_) {
  141. metrics_recorder_.WillStopSession(START_RACE);
  142. metrics_recorder_.StartSessionFailed(reason);
  143. remote_rendering_started_ = false;
  144. }
  145. }
  146. void RendererController::OnStopped(mojom::RemotingStopReason reason) {
  147. DCHECK(thread_checker_.CalledOnValidThread());
  148. VLOG(1) << "Remoting stopped: " << reason;
  149. OnSinkGone();
  150. UpdateAndMaybeSwitch(UNKNOWN_START_TRIGGER, GetStopTrigger(reason));
  151. }
  152. void RendererController::OnMessageFromSink(
  153. const std::vector<uint8_t>& message) {
  154. DCHECK(thread_checker_.CalledOnValidThread());
  155. #if BUILDFLAG(ENABLE_MEDIA_REMOTING_RPC)
  156. std::unique_ptr<openscreen::cast::RpcMessage> rpc(
  157. new openscreen::cast::RpcMessage());
  158. if (!rpc->ParseFromArray(message.data(), message.size())) {
  159. VLOG(1) << "corrupted Rpc message";
  160. OnSinkGone();
  161. UpdateAndMaybeSwitch(UNKNOWN_START_TRIGGER, RPC_INVALID);
  162. return;
  163. }
  164. rpc_messenger_.ProcessMessageFromRemote(std::move(rpc));
  165. #endif
  166. }
  167. void RendererController::OnBecameDominantVisibleContent(bool is_dominant) {
  168. DCHECK(thread_checker_.CalledOnValidThread());
  169. if (is_dominant_content_ == is_dominant)
  170. return;
  171. is_dominant_content_ = is_dominant;
  172. // Reset the errors when the media element stops being the dominant visible
  173. // content in the tab.
  174. if (!is_dominant_content_)
  175. encountered_renderer_fatal_error_ = false;
  176. UpdateAndMaybeSwitch(BECAME_DOMINANT_CONTENT, BECAME_AUXILIARY_CONTENT);
  177. }
  178. void RendererController::OnRemotePlaybackDisabled(bool disabled) {
  179. DCHECK(thread_checker_.CalledOnValidThread());
  180. is_remote_playback_disabled_ = disabled;
  181. metrics_recorder_.OnRemotePlaybackDisabled(disabled);
  182. UpdateAndMaybeSwitch(ENABLED_BY_PAGE, DISABLED_BY_PAGE);
  183. }
  184. #if BUILDFLAG(ENABLE_MEDIA_REMOTING_RPC)
  185. openscreen::WeakPtr<openscreen::cast::RpcMessenger>
  186. RendererController::GetRpcMessenger() {
  187. DCHECK(thread_checker_.CalledOnValidThread());
  188. return rpc_messenger_.GetWeakPtr();
  189. }
  190. #endif
  191. void RendererController::StartDataPipe(uint32_t data_pipe_capacity,
  192. bool audio,
  193. bool video,
  194. DataPipeStartCallback done_callback) {
  195. DCHECK(thread_checker_.CalledOnValidThread());
  196. DCHECK(!done_callback.is_null());
  197. bool ok = audio || video;
  198. mojo::ScopedDataPipeProducerHandle audio_producer_handle;
  199. mojo::ScopedDataPipeConsumerHandle audio_consumer_handle;
  200. if (ok && audio) {
  201. ok &= mojo::CreateDataPipe(data_pipe_capacity, audio_producer_handle,
  202. audio_consumer_handle) == MOJO_RESULT_OK;
  203. }
  204. mojo::ScopedDataPipeProducerHandle video_producer_handle;
  205. mojo::ScopedDataPipeConsumerHandle video_consumer_handle;
  206. if (ok && video) {
  207. ok &= mojo::CreateDataPipe(data_pipe_capacity, video_producer_handle,
  208. video_consumer_handle) == MOJO_RESULT_OK;
  209. }
  210. if (!ok) {
  211. LOG(ERROR) << "No audio nor video to establish data pipe";
  212. std::move(done_callback)
  213. .Run(mojo::NullRemote(), mojo::NullRemote(),
  214. mojo::ScopedDataPipeProducerHandle(),
  215. mojo::ScopedDataPipeProducerHandle());
  216. return;
  217. }
  218. mojo::PendingRemote<mojom::RemotingDataStreamSender> audio_stream_sender;
  219. mojo::PendingRemote<mojom::RemotingDataStreamSender> video_stream_sender;
  220. remoter_->StartDataStreams(
  221. std::move(audio_consumer_handle), std::move(video_consumer_handle),
  222. audio ? audio_stream_sender.InitWithNewPipeAndPassReceiver()
  223. : mojo::NullReceiver(),
  224. video ? video_stream_sender.InitWithNewPipeAndPassReceiver()
  225. : mojo::NullReceiver());
  226. std::move(done_callback)
  227. .Run(std::move(audio_stream_sender), std::move(video_stream_sender),
  228. std::move(audio_producer_handle), std::move(video_producer_handle));
  229. }
  230. void RendererController::OnMetadataChanged(const PipelineMetadata& metadata) {
  231. DCHECK(thread_checker_.CalledOnValidThread());
  232. const bool was_audio_codec_supported = has_audio() && IsAudioCodecSupported();
  233. const bool was_video_codec_supported = has_video() && IsVideoCodecSupported();
  234. pipeline_metadata_ = metadata;
  235. const bool is_audio_codec_supported = has_audio() && IsAudioCodecSupported();
  236. const bool is_video_codec_supported = has_video() && IsVideoCodecSupported();
  237. metrics_recorder_.OnPipelineMetadataChanged(metadata);
  238. StartTrigger start_trigger = UNKNOWN_START_TRIGGER;
  239. if (!was_audio_codec_supported && is_audio_codec_supported)
  240. start_trigger = SUPPORTED_AUDIO_CODEC;
  241. if (!was_video_codec_supported && is_video_codec_supported) {
  242. start_trigger = start_trigger == SUPPORTED_AUDIO_CODEC
  243. ? SUPPORTED_AUDIO_AND_VIDEO_CODECS
  244. : SUPPORTED_VIDEO_CODEC;
  245. }
  246. StopTrigger stop_trigger = UNKNOWN_STOP_TRIGGER;
  247. if (was_audio_codec_supported && !is_audio_codec_supported)
  248. stop_trigger = UNSUPPORTED_AUDIO_CODEC;
  249. if (was_video_codec_supported && !is_video_codec_supported) {
  250. stop_trigger = stop_trigger == UNSUPPORTED_AUDIO_CODEC
  251. ? UNSUPPORTED_AUDIO_AND_VIDEO_CODECS
  252. : UNSUPPORTED_VIDEO_CODEC;
  253. }
  254. UpdateRemotePlaybackAvailabilityMonitoringState();
  255. UpdateAndMaybeSwitch(start_trigger, stop_trigger);
  256. }
  257. void RendererController::OnDataSourceInitialized(
  258. const GURL& url_after_redirects) {
  259. DCHECK(thread_checker_.CalledOnValidThread());
  260. if (url_after_redirects == url_after_redirects_)
  261. return;
  262. // TODO(avayvod): Does WMPI update MediaObserver when metadata becomes
  263. // invalid or should we reset it here?
  264. url_after_redirects_ = url_after_redirects;
  265. UpdateRemotePlaybackAvailabilityMonitoringState();
  266. }
  267. void RendererController::OnHlsManifestDetected() {
  268. #if BUILDFLAG(IS_ANDROID)
  269. is_hls_ = true;
  270. UpdateRemotePlaybackAvailabilityMonitoringState();
  271. #else
  272. NOTREACHED();
  273. #endif
  274. }
  275. void RendererController::UpdateRemotePlaybackAvailabilityMonitoringState() {
  276. // Currently RemotePlayback-initated media remoting only supports URL flinging
  277. // thus the source is supported when the URL is either http or https, video and
  278. // audio codecs are supported by the remote playback device; HLS is playable by
  279. // Chrome on Android (which is not detected by the pipeline metadata atm).
  280. #if BUILDFLAG(IS_ANDROID)
  281. const bool is_media_supported = is_hls_ || IsRemotePlaybackSupported();
  282. #else
  283. const bool is_media_supported = IsAudioOrVideoSupported();
  284. #endif
  285. // TODO(avayvod): add a check for CORS.
  286. bool is_source_supported = url_after_redirects_.has_scheme() &&
  287. (url_after_redirects_.SchemeIs("http") ||
  288. url_after_redirects_.SchemeIs("https")) &&
  289. is_media_supported;
  290. if (client_)
  291. client_->UpdateRemotePlaybackCompatibility(is_source_supported);
  292. }
  293. bool RendererController::IsVideoCodecSupported() const {
  294. DCHECK(thread_checker_.CalledOnValidThread());
  295. return GetVideoCompatibility() == RemotingCompatibility::kCompatible;
  296. }
  297. bool RendererController::IsAudioCodecSupported() const {
  298. DCHECK(thread_checker_.CalledOnValidThread());
  299. return GetAudioCompatibility() == RemotingCompatibility::kCompatible;
  300. }
  301. void RendererController::OnPlaying() {
  302. DCHECK(thread_checker_.CalledOnValidThread());
  303. is_paused_ = false;
  304. UpdateAndMaybeSwitch(PLAY_COMMAND, UNKNOWN_STOP_TRIGGER);
  305. }
  306. void RendererController::OnPaused() {
  307. DCHECK(thread_checker_.CalledOnValidThread());
  308. is_paused_ = true;
  309. // Cancel the start if in the middle of delayed start.
  310. CancelDelayedStart();
  311. }
  312. RemotingCompatibility RendererController::GetVideoCompatibility() const {
  313. DCHECK(thread_checker_.CalledOnValidThread());
  314. DCHECK(has_video());
  315. // Media Remoting doesn't support encrypted media.
  316. if (pipeline_metadata_.video_decoder_config.is_encrypted())
  317. return RemotingCompatibility::kEncryptedVideo;
  318. bool compatible = false;
  319. switch (pipeline_metadata_.video_decoder_config.codec()) {
  320. case VideoCodec::kH264:
  321. compatible = HasVideoCapability(RemotingSinkVideoCapability::CODEC_H264);
  322. break;
  323. case VideoCodec::kVP8:
  324. compatible = HasVideoCapability(RemotingSinkVideoCapability::CODEC_VP8);
  325. break;
  326. case VideoCodec::kVP9:
  327. compatible = HasVideoCapability(RemotingSinkVideoCapability::CODEC_VP9);
  328. break;
  329. case VideoCodec::kHEVC:
  330. compatible = HasVideoCapability(RemotingSinkVideoCapability::CODEC_HEVC);
  331. break;
  332. default:
  333. VLOG(2) << "Remoting does not support video codec: "
  334. << pipeline_metadata_.video_decoder_config.codec();
  335. }
  336. return compatible ? RemotingCompatibility::kCompatible
  337. : RemotingCompatibility::kIncompatibleVideoCodec;
  338. }
  339. RemotingCompatibility RendererController::GetAudioCompatibility() const {
  340. DCHECK(thread_checker_.CalledOnValidThread());
  341. DCHECK(has_audio());
  342. // Media Remoting doesn't support encrypted media.
  343. if (pipeline_metadata_.audio_decoder_config.is_encrypted())
  344. return RemotingCompatibility::kEncryptedAudio;
  345. bool compatible = false;
  346. switch (pipeline_metadata_.audio_decoder_config.codec()) {
  347. case AudioCodec::kAAC:
  348. compatible = HasAudioCapability(RemotingSinkAudioCapability::CODEC_AAC);
  349. break;
  350. case AudioCodec::kOpus:
  351. compatible = HasAudioCapability(RemotingSinkAudioCapability::CODEC_OPUS);
  352. break;
  353. case AudioCodec::kMP3:
  354. case AudioCodec::kPCM:
  355. case AudioCodec::kVorbis:
  356. case AudioCodec::kFLAC:
  357. case AudioCodec::kAMR_NB:
  358. case AudioCodec::kAMR_WB:
  359. case AudioCodec::kPCM_MULAW:
  360. case AudioCodec::kGSM_MS:
  361. case AudioCodec::kPCM_S16BE:
  362. case AudioCodec::kPCM_S24BE:
  363. case AudioCodec::kEAC3:
  364. case AudioCodec::kPCM_ALAW:
  365. case AudioCodec::kALAC:
  366. case AudioCodec::kAC3:
  367. case AudioCodec::kDTS:
  368. case AudioCodec::kDTSXP2:
  369. compatible =
  370. HasAudioCapability(RemotingSinkAudioCapability::CODEC_BASELINE_SET);
  371. break;
  372. default:
  373. VLOG(2) << "Remoting does not support audio codec: "
  374. << pipeline_metadata_.audio_decoder_config.codec();
  375. }
  376. return compatible ? RemotingCompatibility::kCompatible
  377. : RemotingCompatibility::kIncompatibleAudioCodec;
  378. }
  379. RemotingCompatibility RendererController::GetCompatibility() const {
  380. DCHECK(thread_checker_.CalledOnValidThread());
  381. DCHECK(client_);
  382. if (is_remote_playback_disabled_)
  383. return RemotingCompatibility::kDisabledByPage;
  384. if (!has_video() && !has_audio())
  385. return RemotingCompatibility::kNoAudioNorVideo;
  386. if (has_video()) {
  387. RemotingCompatibility compatibility = GetVideoCompatibility();
  388. if (compatibility != RemotingCompatibility::kCompatible)
  389. return compatibility;
  390. }
  391. if (has_audio()) {
  392. RemotingCompatibility compatibility = GetAudioCompatibility();
  393. if (compatibility != RemotingCompatibility::kCompatible)
  394. return compatibility;
  395. }
  396. if (client_->Duration() <= kMinRemotingMediaDurationInSec)
  397. return RemotingCompatibility::kDurationBelowThreshold;
  398. return RemotingCompatibility::kCompatible;
  399. }
  400. bool RendererController::IsAudioOrVideoSupported() const {
  401. return ((has_audio() || has_video()) &&
  402. (!has_video() || IsVideoCodecSupported()) &&
  403. (!has_audio() || IsAudioCodecSupported()));
  404. }
  405. void RendererController::UpdateAndMaybeSwitch(StartTrigger start_trigger,
  406. StopTrigger stop_trigger) {
  407. DCHECK(thread_checker_.CalledOnValidThread());
  408. // Being the dominant visible content is the signal that starts remote
  409. // rendering.
  410. // Also, only switch to remoting when media is playing. Since the renderer is
  411. // created when video starts loading, the receiver would display a black
  412. // screen if switching to remoting while paused. Thus, the user experience is
  413. // improved by not starting remoting until playback resumes.
  414. bool should_be_remoting = client_ && !encountered_renderer_fatal_error_ &&
  415. is_dominant_content_ && !is_paused_ &&
  416. SinkSupportsRemoting();
  417. if (should_be_remoting) {
  418. const RemotingCompatibility compatibility = GetCompatibility();
  419. metrics_recorder_.RecordCompatibility(compatibility);
  420. should_be_remoting = compatibility == RemotingCompatibility::kCompatible;
  421. }
  422. if ((remote_rendering_started_ ||
  423. delayed_start_stability_timer_.IsRunning()) == should_be_remoting) {
  424. return;
  425. }
  426. if (should_be_remoting) {
  427. WaitForStabilityBeforeStart(start_trigger);
  428. } else if (delayed_start_stability_timer_.IsRunning()) {
  429. DCHECK(!remote_rendering_started_);
  430. CancelDelayedStart();
  431. } else {
  432. remote_rendering_started_ = false;
  433. DCHECK_NE(UNKNOWN_STOP_TRIGGER, stop_trigger);
  434. metrics_recorder_.WillStopSession(stop_trigger);
  435. if (client_)
  436. client_->SwitchToLocalRenderer(GetSwitchReason(stop_trigger));
  437. VLOG(2) << "Request to stop remoting: stop_trigger=" << stop_trigger;
  438. remoter_->Stop(mojom::RemotingStopReason::LOCAL_PLAYBACK);
  439. }
  440. }
  441. void RendererController::WaitForStabilityBeforeStart(
  442. StartTrigger start_trigger) {
  443. DCHECK(!delayed_start_stability_timer_.IsRunning());
  444. DCHECK(!remote_rendering_started_);
  445. DCHECK(client_);
  446. delayed_start_stability_timer_.Start(
  447. FROM_HERE, kDelayedStart,
  448. base::BindOnce(&RendererController::OnDelayedStartTimerFired,
  449. base::Unretained(this), start_trigger,
  450. client_->DecodedFrameCount(), clock_->NowTicks()));
  451. }
  452. void RendererController::CancelDelayedStart() {
  453. delayed_start_stability_timer_.Stop();
  454. }
  455. void RendererController::OnDelayedStartTimerFired(
  456. StartTrigger start_trigger,
  457. unsigned decoded_frame_count_before_delay,
  458. base::TimeTicks delayed_start_time) {
  459. DCHECK(is_dominant_content_);
  460. DCHECK(!remote_rendering_started_);
  461. DCHECK(client_); // This task is canceled otherwise.
  462. base::TimeDelta elapsed = clock_->NowTicks() - delayed_start_time;
  463. DCHECK(!elapsed.is_zero());
  464. if (has_video()) {
  465. const double frame_rate =
  466. (client_->DecodedFrameCount() - decoded_frame_count_before_delay) /
  467. elapsed.InSecondsF();
  468. const double pixels_per_second =
  469. frame_rate * pipeline_metadata_.natural_size.GetArea();
  470. const bool supported = RecordPixelRateSupport(pixels_per_second);
  471. if (!supported) {
  472. permanently_disable_remoting_ = true;
  473. return;
  474. }
  475. }
  476. remote_rendering_started_ = true;
  477. DCHECK_NE(UNKNOWN_START_TRIGGER, start_trigger);
  478. metrics_recorder_.WillStartSession(start_trigger);
  479. // |MediaObserverClient::SwitchToRemoteRenderer()| will be called after
  480. // remoting is started successfully.
  481. remoter_->Start();
  482. }
  483. bool RendererController::RecordPixelRateSupport(double pixels_per_second) {
  484. if (pixels_per_second <= kPixelsPerSec2k) {
  485. metrics_recorder_.RecordVideoPixelRateSupport(
  486. PixelRateSupport::k2kSupported);
  487. return true;
  488. }
  489. if (pixels_per_second <= kPixelsPerSec4k) {
  490. if (HasVideoCapability(mojom::RemotingSinkVideoCapability::SUPPORT_4K)) {
  491. metrics_recorder_.RecordVideoPixelRateSupport(
  492. PixelRateSupport::k4kSupported);
  493. return true;
  494. } else {
  495. metrics_recorder_.RecordVideoPixelRateSupport(
  496. PixelRateSupport::k4kNotSupported);
  497. return false;
  498. }
  499. }
  500. metrics_recorder_.RecordVideoPixelRateSupport(
  501. PixelRateSupport::kOver4kNotSupported);
  502. return false;
  503. }
  504. void RendererController::OnRendererFatalError(StopTrigger stop_trigger) {
  505. DCHECK(thread_checker_.CalledOnValidThread());
  506. // Do not act on errors caused by things like Mojo pipes being closed during
  507. // shutdown.
  508. if (!remote_rendering_started_)
  509. return;
  510. encountered_renderer_fatal_error_ = true;
  511. UpdateAndMaybeSwitch(UNKNOWN_START_TRIGGER, stop_trigger);
  512. }
  513. void RendererController::SetClient(MediaObserverClient* client) {
  514. DCHECK(thread_checker_.CalledOnValidThread());
  515. client_ = client;
  516. if (!client_) {
  517. CancelDelayedStart();
  518. if (remote_rendering_started_) {
  519. metrics_recorder_.WillStopSession(MEDIA_ELEMENT_DESTROYED);
  520. remoter_->Stop(mojom::RemotingStopReason::UNEXPECTED_FAILURE);
  521. remote_rendering_started_ = false;
  522. }
  523. return;
  524. }
  525. }
  526. bool RendererController::HasVideoCapability(
  527. mojom::RemotingSinkVideoCapability capability) const {
  528. return std::find(std::begin(sink_metadata_.video_capabilities),
  529. std::end(sink_metadata_.video_capabilities),
  530. capability) != std::end(sink_metadata_.video_capabilities);
  531. }
  532. bool RendererController::HasAudioCapability(
  533. mojom::RemotingSinkAudioCapability capability) const {
  534. return std::find(std::begin(sink_metadata_.audio_capabilities),
  535. std::end(sink_metadata_.audio_capabilities),
  536. capability) != std::end(sink_metadata_.audio_capabilities);
  537. }
  538. bool RendererController::HasFeatureCapability(
  539. RemotingSinkFeature capability) const {
  540. return std::find(std::begin(sink_metadata_.features),
  541. std::end(sink_metadata_.features),
  542. capability) != std::end(sink_metadata_.features);
  543. }
  544. bool RendererController::SinkSupportsRemoting() const {
  545. return HasFeatureCapability(RemotingSinkFeature::RENDERING);
  546. }
  547. void RendererController::SendMessageToSink(std::vector<uint8_t> message) {
  548. DCHECK(thread_checker_.CalledOnValidThread());
  549. remoter_->SendMessageToSink(message);
  550. }
  551. #if BUILDFLAG(IS_ANDROID)
  552. bool RendererController::IsAudioRemotePlaybackSupported() const {
  553. DCHECK(thread_checker_.CalledOnValidThread());
  554. DCHECK(has_audio());
  555. if (pipeline_metadata_.audio_decoder_config.is_encrypted())
  556. return false;
  557. switch (pipeline_metadata_.audio_decoder_config.codec()) {
  558. case AudioCodec::kAAC:
  559. case AudioCodec::kOpus:
  560. case AudioCodec::kMP3:
  561. case AudioCodec::kPCM:
  562. case AudioCodec::kVorbis:
  563. case AudioCodec::kFLAC:
  564. case AudioCodec::kAMR_NB:
  565. case AudioCodec::kAMR_WB:
  566. case AudioCodec::kPCM_MULAW:
  567. case AudioCodec::kGSM_MS:
  568. case AudioCodec::kPCM_S16BE:
  569. case AudioCodec::kPCM_S24BE:
  570. case AudioCodec::kEAC3:
  571. case AudioCodec::kPCM_ALAW:
  572. case AudioCodec::kALAC:
  573. case AudioCodec::kAC3:
  574. case AudioCodec::kDTS:
  575. case AudioCodec::kDTSXP2:
  576. return true;
  577. default:
  578. return false;
  579. }
  580. }
  581. bool RendererController::IsVideoRemotePlaybackSupported() const {
  582. DCHECK(thread_checker_.CalledOnValidThread());
  583. DCHECK(has_video());
  584. if (pipeline_metadata_.video_decoder_config.is_encrypted())
  585. return false;
  586. switch (pipeline_metadata_.video_decoder_config.codec()) {
  587. case VideoCodec::kH264:
  588. case VideoCodec::kVP8:
  589. case VideoCodec::kVP9:
  590. case VideoCodec::kHEVC:
  591. return true;
  592. default:
  593. return false;
  594. }
  595. }
  596. bool RendererController::IsRemotePlaybackSupported() const {
  597. return ((has_audio() || has_video()) &&
  598. (!has_video() || IsVideoRemotePlaybackSupported()) &&
  599. (!has_audio() || IsAudioRemotePlaybackSupported()));
  600. }
  601. #endif // BUILDFLAG(IS_ANDROID)
  602. } // namespace remoting
  603. } // namespace media