blob: 128ba690a58111611a78605dba6dae59d3ed0887 [file] [log] [blame]
[email protected]85a37afd2013-05-30 22:51:151// Copyright 2013 The Chromium Authors. All rights reserved.
[email protected]891acc92009-04-27 19:56:412// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
[email protected]ec9212f2008-12-18 21:40:364
acolwell9e0840d2014-09-06 19:01:325#include "media/blink/webmediaplayer_impl.h"
[email protected]8931c41a2009-07-07 17:31:496
[email protected]c2a45e242013-02-15 20:24:587#include <algorithm>
mateuszs3371ab02015-04-24 13:20:238#include <cmath>
[email protected]47b06ceb2010-08-04 22:41:119#include <limits>
Gyuyoung Kim62a5de42018-01-10 09:48:4210#include <memory>
guidouc7babef2015-10-22 00:42:3511#include <string>
dcheng652f5ff2015-12-27 08:54:0012#include <utility>
[email protected]47b06ceb2010-08-04 22:41:1113
[email protected]08273c7b2011-09-17 00:33:5114#include "base/bind.h"
sandersd1c0bba02016-03-04 23:14:0815#include "base/bind_helpers.h"
[email protected]2041cf342010-02-19 03:15:5916#include "base/callback.h"
[email protected]7502ef12014-01-25 01:19:2717#include "base/callback_helpers.h"
sandersd1e49fb62015-12-12 01:18:0618#include "base/command_line.h"
[email protected]ca04095f2014-02-07 10:23:5619#include "base/debug/alias.h"
[email protected]926f8fd2013-04-12 20:27:5320#include "base/debug/crash_logging.h"
fdoraydb3ef7d2016-06-09 15:42:3821#include "base/location.h"
Noah Rose Ledesma3cf74762020-08-27 18:36:0722#include "base/memory/weak_ptr.h"
Dale Curtis3899090ea2018-01-12 00:10:3523#include "base/metrics/histogram_functions.h"
asvitkine30330812016-08-30 04:01:0824#include "base/metrics/histogram_macros.h"
acolwellb4034942014-08-28 15:42:4325#include "base/single_thread_task_runner.h"
servolka233f832016-06-10 20:39:0426#include "base/strings/string_number_conversions.h"
Gabriel Charette44db1422018-08-06 11:19:3327#include "base/task/post_task.h"
Gabriel Charetted5c656c2020-02-26 16:35:2228#include "base/task/thread_pool.h"
wdzierzanowskifd4cd91c52015-12-02 23:50:2029#include "base/task_runner_util.h"
gabba14a442016-05-11 20:10:2030#include "base/threading/thread_task_runner_handle.h"
Eugene Zemtsov77d56262020-03-06 05:52:0331#include "base/trace_event/memory_dump_manager.h"
ssid9525f4672015-01-28 12:13:1532#include "base/trace_event/trace_event.h"
sandersd1e49fb62015-12-12 01:18:0633#include "build/build_config.h"
[email protected]21c3f7502013-03-23 03:29:5134#include "cc/layers/video_layer.h"
[email protected]e4fc09e2012-04-06 03:17:4435#include "media/audio/null_audio_sink.h"
[email protected]2eccbed2014-01-10 05:15:5336#include "media/base/bind_to_current_loop.h"
xhwang0ad11e512014-11-25 23:43:0937#include "media/base/cdm_context.h"
John Rummelldb5a7ef2018-05-16 00:28:0138#include "media/base/encryption_scheme.h"
[email protected]32da1002010-03-03 21:57:3539#include "media/base/limits.h"
zqzhang5d8eab72016-08-26 20:34:3040#include "media/base/media_content_type.h"
[email protected]090f7312011-08-05 23:26:4041#include "media/base/media_log.h"
sandersd1e49fb62015-12-12 01:18:0642#include "media/base/media_switches.h"
tguilbert25a4d112016-10-13 21:56:5143#include "media/base/media_url_demuxer.h"
Eugene Zemtsov77d56262020-03-06 05:52:0344#include "media/base/memory_dump_provider_proxy.h"
[email protected]8a561062013-11-22 01:19:3145#include "media/base/text_renderer.h"
watk9f9dfdc92015-09-04 21:33:2946#include "media/base/timestamp_constants.h"
[email protected]e81283bb2010-08-31 18:01:2147#include "media/base/video_frame.h"
acolwell9e0840d2014-09-06 19:01:3248#include "media/blink/texttrack_impl.h"
John Delaneyb933391602018-10-17 21:50:4749#include "media/blink/url_index.h"
Chris Cunninghamd9df58e2017-08-29 00:04:2350#include "media/blink/video_decode_stats_reporter.h"
dalecurtis04bdb582016-08-17 22:15:2351#include "media/blink/watch_time_reporter.h"
xhwang97de4202014-11-25 08:44:0152#include "media/blink/webcontentdecryptionmodule_impl.h"
acolwell9e0840d2014-09-06 19:01:3253#include "media/blink/webinbandtexttrack_impl.h"
acolwell9e0840d2014-09-06 19:01:3254#include "media/blink/webmediasource_impl.h"
[email protected]efe7cd22012-09-12 23:55:0155#include "media/filters/chunk_demuxer.h"
[email protected]ddbc6ff2013-04-19 15:28:3356#include "media/filters/ffmpeg_demuxer.h"
Dale Curtis4841c712018-12-13 18:14:0557#include "media/filters/memory_data_source.h"
[email protected]96665a82020-01-23 00:35:3758#include "media/learning/mojo/public/cpp/mojo_learning_task_controller.h"
Scott Violeta35f9a42018-03-22 22:00:4459#include "media/media_buildflags.h"
Chih-Hsuan Kuo8f7209652020-06-10 16:40:1860#include "media/remoting/remoting_constants.h"
Gyuyoung Kima040bc52019-10-30 01:14:3561#include "mojo/public/cpp/bindings/pending_remote.h"
Dale Curtis4841c712018-12-13 18:14:0562#include "net/base/data_url.h"
Noah Rose Ledesma3cf74762020-08-27 18:36:0763#include "third_party/blink/public/platform/media/webmediaplayer_delegate.h"
Blink Reformata30d4232018-04-07 15:31:0664#include "third_party/blink/public/platform/web_encrypted_media_types.h"
[email protected]0526d5692020-02-28 00:24:3165#include "third_party/blink/public/platform/web_fullscreen_video_status.h"
Blink Reformata30d4232018-04-07 15:31:0666#include "third_party/blink/public/platform/web_media_player_client.h"
67#include "third_party/blink/public/platform/web_media_player_encrypted_media_client.h"
68#include "third_party/blink/public/platform/web_media_player_source.h"
69#include "third_party/blink/public/platform/web_media_source.h"
70#include "third_party/blink/public/platform/web_rect.h"
71#include "third_party/blink/public/platform/web_runtime_features.h"
72#include "third_party/blink/public/platform/web_security_origin.h"
73#include "third_party/blink/public/platform/web_size.h"
74#include "third_party/blink/public/platform/web_string.h"
75#include "third_party/blink/public/platform/web_surface_layer_bridge.h"
Ted Meyer1c37a8512020-08-06 15:16:3276#include "third_party/blink/public/platform/web_texttrack_metadata.h"
Blink Reformata30d4232018-04-07 15:31:0677#include "third_party/blink/public/platform/web_url.h"
Antonio Gomes3a858b52019-05-31 02:47:5278#include "third_party/blink/public/platform/webaudiosourceprovider_impl.h"
Gyuyoung Kimf99f5f32019-09-26 03:45:1579#include "third_party/blink/public/strings/grit/blink_strings.h"
Antonio Gomesf01cfbd2019-07-12 08:53:1180#include "third_party/blink/public/web/modules/media/webmediaplayer_util.h"
Blink Reformata30d4232018-04-07 15:31:0681#include "third_party/blink/public/web/web_document.h"
82#include "third_party/blink/public/web/web_frame.h"
83#include "third_party/blink/public/web/web_local_frame.h"
Blink Reformata30d4232018-04-07 15:31:0684#include "third_party/blink/public/web/web_view.h"
[email protected]b3f2b912009-04-09 16:18:5285
dalecurtisea27a3ed2016-06-24 01:41:3086#if defined(OS_ANDROID)
87#include "media/base/android/media_codec_util.h"
88#endif
89
[email protected]180ef242013-11-07 06:50:4690using blink::WebMediaPlayer;
91using blink::WebRect;
[email protected]180ef242013-11-07 06:50:4692using blink::WebString;
hubbed5f36882016-01-15 22:40:3793using gpu::gles2::GLES2Interface;
94
danakj365175c2016-02-06 00:37:3795#define STATIC_ASSERT_ENUM(a, b) \
96 static_assert(static_cast<int>(a) == static_cast<int>(b), \
97 "mismatching enums: " #a)
98
hubbed5f36882016-01-15 22:40:3799namespace media {
[email protected]ec9212f2008-12-18 21:40:36100
[email protected]8931c41a2009-07-07 17:31:49101namespace {
102
Dan Sanders6edfd782019-08-13 00:13:18103const char kWatchTimeHistogram[] = "Media.WebMediaPlayerImpl.WatchTime";
104
Xiaohan Wang48aaa192019-12-05 01:29:00105void RecordSimpleWatchTimeUMA(RendererFactoryType type) {
106 UMA_HISTOGRAM_ENUMERATION(kWatchTimeHistogram, type);
Dan Sanders6edfd782019-08-13 00:13:18107}
108
Antonio Gomes3a858b52019-05-31 02:47:52109void SetSinkIdOnMediaThread(
110 scoped_refptr<blink::WebAudioSourceProviderImpl> sink,
111 const std::string& device_id,
112 OutputDeviceStatusCB callback) {
Daniel Chengc1710b52018-10-24 03:12:28113 sink->SwitchOutputDevice(device_id, std::move(callback));
guidouc7babef2015-10-22 00:42:35114}
115
Sergey Volk8b09c2c52018-12-12 23:20:40116bool IsBackgroundSuspendEnabled(const WebMediaPlayerImpl* wmpi) {
Luke Halliwell7a8a8982018-07-25 01:07:05117 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
Dan Sanders5aeeb212020-04-10 19:37:41118 switches::kDisableBackgroundMediaSuspend)) {
Luke Halliwell7a8a8982018-07-25 01:07:05119 return false;
Dan Sanders5aeeb212020-04-10 19:37:41120 }
Sergey Volk8b09c2c52018-12-12 23:20:40121 return wmpi->IsBackgroundMediaSuspendEnabled();
dalecurtis0431cbf2016-03-12 01:19:43122}
123
avayvod48a8be52016-08-04 19:52:50124bool IsResumeBackgroundVideosEnabled() {
125 return base::FeatureList::IsEnabled(kResumeBackgroundVideo);
126}
127
avayvod01201332017-04-14 00:27:15128bool IsBackgroundVideoPauseOptimizationEnabled() {
129 return base::FeatureList::IsEnabled(kBackgroundVideoPauseOptimization);
130}
131
sandersd50a635e2016-04-04 22:50:09132bool IsNetworkStateError(blink::WebMediaPlayer::NetworkState state) {
Blink Reformat1c4d759e2017-04-09 16:34:54133 bool result = state == blink::WebMediaPlayer::kNetworkStateFormatError ||
134 state == blink::WebMediaPlayer::kNetworkStateNetworkError ||
135 state == blink::WebMediaPlayer::kNetworkStateDecodeError;
136 DCHECK_EQ(state > blink::WebMediaPlayer::kNetworkStateLoaded, result);
sandersd50a635e2016-04-04 22:50:09137 return result;
138}
139
sandersd2c478422016-08-02 01:19:25140gfx::Size GetRotatedVideoSize(VideoRotation rotation, gfx::Size natural_size) {
141 if (rotation == VIDEO_ROTATION_90 || rotation == VIDEO_ROTATION_270)
142 return gfx::Size(natural_size.height(), natural_size.width());
143 return natural_size;
144}
145
Xiaohan Wangf63505d2017-10-21 08:00:53146void RecordEncryptedEvent(bool encrypted_event_fired) {
147 UMA_HISTOGRAM_BOOLEAN("Media.EME.EncryptedEvent", encrypted_event_fired);
148}
149
sandersd35d2c3f2017-01-14 02:04:42150// How much time must have elapsed since loading last progressed before we
151// assume that the decoder will have had time to complete preroll.
152constexpr base::TimeDelta kPrerollAttemptTimeout =
watkd026f792016-11-05 00:28:51153 base::TimeDelta::FromSeconds(3);
154
Matt Wolenetz6010f6c2017-10-18 00:44:36155// Maximum number, per-WMPI, of media logs of playback rate changes.
156constexpr int kMaxNumPlaybackRateLogs = 10;
157
Gyuyoung Kimf99f5f32019-09-26 03:45:15158int GetSwitchToLocalMessage(MediaObserverClient::ReasonToSwitchToLocal reason) {
Xiangjun Zhang5e20cba42018-01-10 19:54:56159 switch (reason) {
160 case MediaObserverClient::ReasonToSwitchToLocal::NORMAL:
Gyuyoung Kimf99f5f32019-09-26 03:45:15161 return IDS_MEDIA_REMOTING_STOP_TEXT;
Xiangjun Zhang5e20cba42018-01-10 19:54:56162 case MediaObserverClient::ReasonToSwitchToLocal::POOR_PLAYBACK_QUALITY:
Gyuyoung Kimf99f5f32019-09-26 03:45:15163 return IDS_MEDIA_REMOTING_STOP_BY_PLAYBACK_QUALITY_TEXT;
Xiangjun Zhang5e20cba42018-01-10 19:54:56164 case MediaObserverClient::ReasonToSwitchToLocal::PIPELINE_ERROR:
Gyuyoung Kimf99f5f32019-09-26 03:45:15165 return IDS_MEDIA_REMOTING_STOP_BY_ERROR_TEXT;
Xiangjun Zhang5e20cba42018-01-10 19:54:56166 case MediaObserverClient::ReasonToSwitchToLocal::ROUTE_TERMINATED:
Gyuyoung Kimf99f5f32019-09-26 03:45:15167 return blink::WebMediaPlayerClient::kMediaRemotingStopNoText;
Xiangjun Zhang5e20cba42018-01-10 19:54:56168 }
169 NOTREACHED();
170 // To suppress compiler warning on Windows.
Gyuyoung Kimf99f5f32019-09-26 03:45:15171 return blink::WebMediaPlayerClient::kMediaRemotingStopNoText;
Xiangjun Zhang5e20cba42018-01-10 19:54:56172}
173
John Rummelldb5a7ef2018-05-16 00:28:01174// These values are persisted to UMA. Entries should not be renumbered and
175// numeric values should never be reused.
Yuchen Liub33bfc12019-11-08 20:16:12176// TODO(crbug.com/825041): This should use EncryptionScheme when kUnencrypted
John Rummelldb5a7ef2018-05-16 00:28:01177// removed.
178enum class EncryptionSchemeUMA { kCenc = 0, kCbcs = 1, kCount };
179
180EncryptionSchemeUMA DetermineEncryptionSchemeUMAValue(
Yuchen Liub33bfc12019-11-08 20:16:12181 EncryptionScheme encryption_scheme) {
182 if (encryption_scheme == EncryptionScheme::kCbcs)
John Rummelldb5a7ef2018-05-16 00:28:01183 return EncryptionSchemeUMA::kCbcs;
184
Yuchen Liub33bfc12019-11-08 20:16:12185 DCHECK_EQ(encryption_scheme, EncryptionScheme::kCenc);
John Rummelldb5a7ef2018-05-16 00:28:01186 return EncryptionSchemeUMA::kCenc;
187}
188
Pavel Feldman023c3e62018-08-28 17:59:47189#if BUILDFLAG(ENABLE_FFMPEG)
Dale Curtisb8139f72018-08-27 23:28:48190// Returns true if |url| represents (or is likely to) a local file.
191bool IsLocalFile(const GURL& url) {
192 return url.SchemeIsFile() || url.SchemeIsFileSystem() ||
193 url.SchemeIs(url::kContentScheme) ||
194 url.SchemeIs(url::kContentIDScheme) ||
195 url.SchemeIs("chrome-extension");
196}
Pavel Feldman023c3e62018-08-28 17:59:47197#endif
Dale Curtisb8139f72018-08-27 23:28:48198
Dale Curtisf273f8f2018-12-13 23:40:33199// Handles destruction of media::Renderer dependent components after the
200// renderer has been destructed on the media thread.
201void DestructionHelper(
202 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
203 scoped_refptr<base::SingleThreadTaskRunner> vfc_task_runner,
204 std::unique_ptr<Demuxer> demuxer,
205 std::unique_ptr<DataSource> data_source,
206 std::unique_ptr<VideoFrameCompositor> compositor,
207 std::unique_ptr<CdmContextRef> cdm_context_1,
208 std::unique_ptr<CdmContextRef> cdm_context_2,
209 std::unique_ptr<MediaLog> media_log,
210 std::unique_ptr<RendererFactorySelector> renderer_factory_selector,
211 std::unique_ptr<blink::WebSurfaceLayerBridge> bridge,
212 bool is_chunk_demuxer) {
213 // We release |bridge| after pipeline stop to ensure layout tests receive
214 // painted video frames before test harness exit.
215 main_task_runner->DeleteSoon(FROM_HERE, std::move(bridge));
216
217 // Since the media::Renderer is gone we can now destroy the compositor and
218 // renderer factory selector.
219 vfc_task_runner->DeleteSoon(FROM_HERE, std::move(compositor));
220 main_task_runner->DeleteSoon(FROM_HERE, std::move(renderer_factory_selector));
221
222 // ChunkDemuxer can be deleted on any thread, but other demuxers are bound to
223 // the main thread and must be deleted there now that the renderer is gone.
224 if (!is_chunk_demuxer) {
225 main_task_runner->DeleteSoon(FROM_HERE, std::move(demuxer));
226 main_task_runner->DeleteSoon(FROM_HERE, std::move(data_source));
227 main_task_runner->DeleteSoon(FROM_HERE, std::move(cdm_context_1));
228 main_task_runner->DeleteSoon(FROM_HERE, std::move(cdm_context_2));
229 main_task_runner->DeleteSoon(FROM_HERE, std::move(media_log));
230 return;
231 }
232
233 // ChunkDemuxer's streams may contain much buffered, compressed media that
234 // may need to be paged back in during destruction. Paging delay may exceed
235 // the renderer hang monitor's threshold on at least Windows while also
236 // blocking other work on the renderer main thread, so we do the actual
237 // destruction in the background without blocking WMPI destruction or
238 // |task_runner|. On advice of task_scheduler OWNERS, MayBlock() is not
239 // used because virtual memory overhead is not considered blocking I/O; and
240 // CONTINUE_ON_SHUTDOWN is used to allow process termination to not block on
241 // completing the task.
Gabriel Charetted5c656c2020-02-26 16:35:22242 base::ThreadPool::PostTask(
Dale Curtisf273f8f2018-12-13 23:40:33243 FROM_HERE,
Gabriel Charetted5c656c2020-02-26 16:35:22244 {base::TaskPriority::BEST_EFFORT,
Dale Curtisf273f8f2018-12-13 23:40:33245 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
246 base::BindOnce(
247 [](scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
248 std::unique_ptr<Demuxer> demuxer_to_destroy,
249 std::unique_ptr<CdmContextRef> cdm_context_1,
250 std::unique_ptr<CdmContextRef> cdm_context_2,
251 std::unique_ptr<MediaLog> media_log) {
252 SCOPED_UMA_HISTOGRAM_TIMER("Media.MSE.DemuxerDestructionTime");
253 demuxer_to_destroy.reset();
254 main_task_runner->DeleteSoon(FROM_HERE, std::move(cdm_context_1));
255 main_task_runner->DeleteSoon(FROM_HERE, std::move(cdm_context_2));
256 main_task_runner->DeleteSoon(FROM_HERE, std::move(media_log));
257 },
258 std::move(main_task_runner), std::move(demuxer),
259 std::move(cdm_context_1), std::move(cdm_context_2),
260 std::move(media_log)));
261}
262
Ted Meyer7f5b4e22019-11-21 03:21:19263std::string SanitizeUserStringProperty(blink::WebString value) {
Ted Meyera698afe2019-11-06 20:58:03264 std::string converted = value.Utf8();
Ted Meyer7f5b4e22019-11-21 03:21:19265 return base::IsStringUTF8(converted) ? converted : "[invalid property]";
Ted Meyera698afe2019-11-06 20:58:03266}
267
Eugene Zemtsov77d56262020-03-06 05:52:03268void CreateAllocation(base::trace_event::ProcessMemoryDump* pmd,
269 int32_t id,
270 const char* name,
271 int64_t bytes) {
272 if (bytes <= 0)
273 return;
274 auto full_name =
Eugene Zemtsovc4c6db262020-03-20 22:19:41275 base::StringPrintf("media/webmediaplayer/%s/player_0x%x", name, id);
Eugene Zemtsov77d56262020-03-06 05:52:03276 auto* dump = pmd->CreateAllocatorDump(full_name);
277
278 dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
279 base::trace_event::MemoryAllocatorDump::kUnitsBytes, bytes);
280
281 auto* std_allocator = base::trace_event::MemoryDumpManager::GetInstance()
282 ->system_allocator_pool_name();
Eugene Zemtsovbe7150372020-04-21 18:35:14283 if (std_allocator)
284 pmd->AddSuballocation(dump->guid(), std_allocator);
Eugene Zemtsov77d56262020-03-06 05:52:03285}
286
Wojciech Dzierżanowskide9b1272020-06-04 08:48:52287// Determine whether we should update MediaPosition in |delegate_|.
288bool MediaPositionNeedsUpdate(
289 const media_session::MediaPosition& old_position,
290 const media_session::MediaPosition& new_position) {
291 if (old_position.playback_rate() != new_position.playback_rate())
292 return true;
293
294 if (old_position.duration() != new_position.duration())
295 return true;
296
Wojciech Dzierżanowski09332fdb2020-07-09 18:48:19297 // Special handling for "infinite" position required to avoid calculations
298 // involving infinities.
299 if (new_position.GetPosition().is_max())
300 return !old_position.GetPosition().is_max();
301
Wojciech Dzierżanowskide9b1272020-06-04 08:48:52302 // MediaPosition is potentially changed upon each OnTimeUpdate() call. In
303 // practice most of these calls happen periodically during normal playback,
304 // with unchanged rate and duration. If we want to avoid updating
305 // MediaPosition unnecessarily, we need to compare the current time
306 // calculated from the old and new MediaPositions with some tolerance. That's
307 // because we don't know the exact time when GetMediaTime() calculated the
308 // media position. We choose an arbitrary tolerance that is high enough to
309 // eliminate a lot of MediaPosition updates and low enough not to make a
310 // perceptible difference.
311 const auto drift =
312 (old_position.GetPosition() - new_position.GetPosition()).magnitude();
313 return drift > base::TimeDelta::FromMilliseconds(100);
314}
315
[email protected]8931c41a2009-07-07 17:31:49316} // namespace
317
[email protected]6683e1b2014-04-10 01:45:38318class BufferedDataSourceHostImpl;
319
Takashi Toyoshima2e01e692018-11-16 03:23:27320STATIC_ASSERT_ENUM(WebMediaPlayer::kCorsModeUnspecified,
danakj365175c2016-02-06 00:37:37321 UrlData::CORS_UNSPECIFIED);
Takashi Toyoshima2e01e692018-11-16 03:23:27322STATIC_ASSERT_ENUM(WebMediaPlayer::kCorsModeAnonymous, UrlData::CORS_ANONYMOUS);
323STATIC_ASSERT_ENUM(WebMediaPlayer::kCorsModeUseCredentials,
danakj365175c2016-02-06 00:37:37324 UrlData::CORS_USE_CREDENTIALS);
[email protected]a5a01102012-06-06 17:01:24325
[email protected]5b5bb9d2010-10-22 19:57:36326WebMediaPlayerImpl::WebMediaPlayerImpl(
[email protected]35b2a972014-04-04 15:50:22327 blink::WebLocalFrame* frame,
[email protected]180ef242013-11-07 06:50:46328 blink::WebMediaPlayerClient* client,
srirama.m26f864d02015-07-14 05:21:46329 blink::WebMediaPlayerEncryptedMediaClient* encrypted_client,
Antonio Gomes423df612019-07-11 12:40:07330 blink::WebMediaPlayerDelegate* delegate,
tguilbert70d2a00a2017-04-25 00:30:44331 std::unique_ptr<RendererFactorySelector> renderer_factory_selector,
Alok Priyadarshi7166d4d2017-07-29 04:04:25332 UrlIndex* url_index,
CJ DiMegliod7ac6772017-11-10 00:19:06333 std::unique_ptr<VideoFrameCompositor> compositor,
dalecurtis9cddc0b2017-04-19 21:23:38334 std::unique_ptr<WebMediaPlayerParams> params)
[email protected]f6af7592014-02-28 10:09:11335 : frame_(frame),
Alexander Timin310368112017-09-13 10:01:44336 main_task_runner_(
337 frame->GetTaskRunner(blink::TaskType::kMediaElementEvent)),
dalecurtis9cddc0b2017-04-19 21:23:38338 media_task_runner_(params->media_task_runner()),
339 worker_task_runner_(params->worker_task_runner()),
340 media_log_(params->take_media_log()),
[email protected]5badb082010-06-11 17:40:15341 client_(client),
srirama.m26f864d02015-07-14 05:21:46342 encrypted_client_(encrypted_client),
[email protected]baff4512011-10-19 18:21:07343 delegate_(delegate),
Wojciech Dzierżanowski69bb21842020-07-22 14:21:17344 delegate_has_audio_(HasUnmutedAudio()),
dalecurtis9cddc0b2017-04-19 21:23:38345 defer_load_cb_(params->defer_load_cb()),
dalecurtis9cddc0b2017-04-19 21:23:38346 adjust_allocated_memory_cb_(params->adjust_allocated_memory_cb()),
tzik2c963b872017-12-07 06:57:24347 tick_clock_(base::DefaultTickClock::GetInstance()),
hubbe5f0ad43b2015-12-14 20:57:26348 url_index_(url_index),
Nathan Zabriskie04cec1c2020-03-12 00:55:26349 raster_context_provider_(params->raster_context_provider()),
CJ DiMegliod7ac6772017-11-10 00:19:06350 vfc_task_runner_(params->video_frame_compositor_task_runner()),
351 compositor_(std::move(compositor)),
tguilbert70d2a00a2017-04-25 00:30:44352 renderer_factory_selector_(std::move(renderer_factory_selector)),
dalecurtis9cddc0b2017-04-19 21:23:38353 observer_(params->media_observer()),
servolkf94b4602017-01-31 16:44:27354 enable_instant_source_buffer_gc_(
dalecurtis9cddc0b2017-04-19 21:23:38355 params->enable_instant_source_buffer_gc()),
shaktisahu24189c12017-03-11 17:42:55356 embedded_media_experience_enabled_(
liberato2ff93ad2017-05-17 07:28:24357 params->embedded_media_experience_enabled()),
[email protected]69db58f2018-09-26 20:27:56358 surface_layer_mode_(params->use_surface_layer_for_video()),
CJ DiMeglio96c18b692018-07-04 03:39:06359 create_bridge_callback_(params->create_bridge_callback()),
liberato2ff93ad2017-05-17 07:28:24360 request_routing_token_cb_(params->request_routing_token_cb()),
Dale Curtis1adbe6a2017-08-02 02:09:13361 overlay_routing_token_(OverlayInfo::RoutingToken()),
Sergey Volk8b09c2c52018-12-12 23:20:40362 media_metrics_provider_(params->take_metrics_provider()),
363 is_background_suspend_enabled_(params->IsBackgroundSuspendEnabled()),
Junbo Kefba620b2019-01-16 02:54:36364 is_background_video_playback_enabled_(
365 params->IsBackgroundVideoPlaybackEnabled()),
Sergey Volk8b09c2c52018-12-12 23:20:40366 is_background_video_track_optimization_supported_(
Dan Sanders6edfd782019-08-13 00:13:18367 params->IsBackgroundVideoTrackOptimizationSupported()),
Dan Sanders6edfd782019-08-13 00:13:18368 simple_watch_timer_(
369 base::BindRepeating(&WebMediaPlayerImpl::OnSimpleWatchTimerTick,
370 base::Unretained(this)),
371 base::BindRepeating(&WebMediaPlayerImpl::GetCurrentTimeInternal,
[email protected]91fd3162019-09-05 23:51:32372 base::Unretained(this))),
[email protected]dc996312019-12-19 19:26:38373 will_play_helper_(nullptr),
David Dorwin30993312020-05-07 22:39:09374 demuxer_override_(params->TakeDemuxerOverride()),
[email protected]dc996312019-12-19 19:26:38375 power_status_helper_(params->TakePowerStatusHelper()) {
xhwang51139732017-02-24 19:36:08376 DVLOG(1) << __func__;
Dale Curtise25163812018-09-21 22:13:39377 DCHECK(adjust_allocated_memory_cb_);
tguilbert70d2a00a2017-04-25 00:30:44378 DCHECK(renderer_factory_selector_);
servolkef1e5ef2016-03-25 04:55:26379 DCHECK(client_);
tguilbert1bb1c782017-01-23 21:15:11380 DCHECK(delegate_);
dalecurtis83266c72015-10-29 18:43:20381
Antonio Gomes142f32a2019-05-15 23:32:56382 weak_this_ = weak_factory_.GetWeakPtr();
383
Xiaohan Wang2480de72019-11-15 01:44:47384 // Using base::Unretained(this) is safe because the |pipeline| is owned by
385 // |this| and the callback will always be made on the main task runner.
386 // Not using BindToCurrentLoop() because CreateRenderer() is a sync call.
387 auto pipeline = std::make_unique<PipelineImpl>(
388 media_task_runner_, main_task_runner_,
389 base::BindRepeating(&WebMediaPlayerImpl::CreateRenderer,
390 base::Unretained(this)),
391 media_log_.get());
392
Antonio Gomes81b56552019-05-15 22:15:28393 pipeline_controller_ = std::make_unique<PipelineController>(
Xiaohan Wang2480de72019-11-15 01:44:47394 std::move(pipeline),
Antonio Gomes142f32a2019-05-15 23:32:56395 base::BindRepeating(&WebMediaPlayerImpl::OnPipelineSeeked, weak_this_),
396 base::BindRepeating(&WebMediaPlayerImpl::OnPipelineSuspended, weak_this_),
Antonio Gomes81b56552019-05-15 22:15:28397 base::BindRepeating(&WebMediaPlayerImpl::OnBeforePipelineResume,
Antonio Gomes142f32a2019-05-15 23:32:56398 weak_this_),
399 base::BindRepeating(&WebMediaPlayerImpl::OnPipelineResumed, weak_this_),
400 base::BindRepeating(&WebMediaPlayerImpl::OnError, weak_this_));
Antonio Gomes81b56552019-05-15 22:15:28401
402 buffered_data_source_host_ = std::make_unique<BufferedDataSourceHostImpl>(
Antonio Gomes142f32a2019-05-15 23:32:56403 base::BindRepeating(&WebMediaPlayerImpl::OnProgress, weak_this_),
Antonio Gomes81b56552019-05-15 22:15:28404 tick_clock_);
405
[email protected]c8d574722017-08-30 20:53:43406 // If we're supposed to force video overlays, then make sure that they're
407 // enabled all the time.
408 always_enable_overlays_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
tsunghungee562e92016-07-20 18:03:31409 switches::kForceVideoOverlays);
410
Jinsong Fan982342112019-11-07 23:45:45411 if (base::FeatureList::IsEnabled(kOverlayFullscreenVideo))
412 overlay_mode_ = OverlayMode::kUseAndroidOverlay;
413 else
liberato2ff93ad2017-05-17 07:28:24414 overlay_mode_ = OverlayMode::kNoOverlays;
ampea73f792017-01-19 04:05:39415
tguilbert1bb1c782017-01-23 21:15:11416 delegate_id_ = delegate_->AddObserver(this);
417 delegate_->SetIdle(delegate_id_, true);
sandersd1e49fb62015-12-12 01:18:06418
Ted Meyer0134aed2020-01-23 23:56:40419 media_log_->AddEvent<MediaLogEvent::kWebMediaPlayerCreated>(
420 url::Origin(frame_->GetSecurityOrigin()).GetURL().spec());
Ted Meyera698afe2019-11-06 20:58:03421
Ted Meyer7f5b4e22019-11-21 03:21:19422 media_log_->SetProperty<MediaLogProperty::kFrameUrl>(
423 SanitizeUserStringProperty(frame_->GetDocument().Url().GetString()));
424 media_log_->SetProperty<MediaLogProperty::kFrameTitle>(
425 SanitizeUserStringProperty(frame_->GetDocument().Title()));
[email protected]52b0b212018-10-10 05:25:28426
dalecurtis9cddc0b2017-04-19 21:23:38427 if (params->initial_cdm())
Chris Cunninghamd4a00192019-03-21 20:02:49428 SetCdmInternal(params->initial_cdm());
xhwang0ad11e512014-11-25 23:43:09429
Xiaohan Wangf63505d2017-10-21 08:00:53430 // Report a false "EncrytpedEvent" here as a baseline.
431 RecordEncryptedEvent(false);
432
Noah Rose Ledesma3cf74762020-08-27 18:36:07433 auto on_audio_source_provider_set_client_callback = base::BindOnce(
434 [](base::WeakPtr<WebMediaPlayerImpl> self,
435 blink::WebMediaPlayerDelegate* const delegate, int delegate_id) {
436 if (!self)
437 return;
438 delegate->DidDisableAudioOutputSinkChanges(self->delegate_id_);
439 },
440 weak_this_, delegate_, delegate_id_);
441
xhwangf94a634d2014-10-22 22:07:27442 // TODO(xhwang): When we use an external Renderer, many methods won't work,
xhwang6fa356202014-12-11 00:44:12443 // e.g. GetCurrentFrameFromCompositor(). See http://crbug.com/434861
Antonio Gomes3a858b52019-05-31 02:47:52444 audio_source_provider_ = new blink::WebAudioSourceProviderImpl(
Noah Rose Ledesma3cf74762020-08-27 18:36:07445 params->audio_renderer_sink(), media_log_.get(),
446 std::move(on_audio_source_provider_set_client_callback));
xjz4e5d4bf32017-02-15 21:26:35447
448 if (observer_)
449 observer_->SetClient(this);
Hajime Hoshi8bb37dc2017-12-19 09:35:10450
451 memory_usage_reporting_timer_.SetTaskRunner(
Hajime Hoshib5a26ee2018-05-14 12:47:51452 frame_->GetTaskRunner(blink::TaskType::kInternalMedia));
John Delaney6c8ecc1b2018-10-21 19:07:29453
Eugene Zemtsov77d56262020-03-06 05:52:03454 main_thread_mem_dumper_ = std::make_unique<MemoryDumpProviderProxy>(
455 "WebMediaPlayer_MainThread", main_task_runner_,
456 base::BindRepeating(&WebMediaPlayerImpl::OnMainThreadMemoryDump,
457 weak_this_, media_log_->id()));
458
Sergey Ulanovb8656772020-04-21 22:56:14459 media_metrics_provider_->AcquirePlaybackEventsRecorder(
460 playback_events_recorder_.BindNewPipeAndPassReceiver());
461
Sergey Ulanovb55701812020-04-29 01:39:37462 // MediaMetricsProvider may drop the request for PlaybackEventsRecorder if
463 // it's not interested in recording these events.
464 playback_events_recorder_.reset_on_disconnect();
465
Thomas Guilbert901808982019-07-03 20:38:18466#if defined(OS_ANDROID)
Thomas Guilbertf6d0702e2019-09-12 23:40:14467 renderer_factory_selector_->SetRemotePlayStateChangeCB(
468 BindToCurrentLoop(base::BindRepeating(
469 &WebMediaPlayerImpl::OnRemotePlayStateChange, weak_this_)));
Thomas Guilbert901808982019-07-03 20:38:18470#endif // defined (OS_ANDROID)
[email protected]ec9212f2008-12-18 21:40:36471}
472
[email protected]4e6be3f2009-05-07 02:24:44473WebMediaPlayerImpl::~WebMediaPlayerImpl() {
xhwang51139732017-02-24 19:36:08474 DVLOG(1) << __func__;
acolwellb4034942014-08-28 15:42:43475 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]d43ed912009-02-03 04:52:53476
xhwang51139732017-02-24 19:36:08477 if (set_cdm_result_) {
Chris Cunninghamd4a00192019-03-21 20:02:49478 DVLOG(2)
479 << "Resolve pending SetCdmInternal() when media player is destroyed.";
Blink Reformat1c4d759e2017-04-09 16:34:54480 set_cdm_result_->Complete();
xhwang51139732017-02-24 19:36:08481 set_cdm_result_.reset();
482 }
483
alokp1116967f2016-06-11 17:30:56484 suppress_destruction_errors_ = true;
tguilbert1bb1c782017-01-23 21:15:11485
486 delegate_->PlayerGone(delegate_id_);
487 delegate_->RemoveObserver(delegate_id_);
Dale Curtis456ebb52020-10-05 23:45:23488 delegate_ = nullptr;
[email protected]baff4512011-10-19 18:21:07489
dalecurtis04bdb582016-08-17 22:15:23490 // Finalize any watch time metrics before destroying the pipeline.
491 watch_time_reporter_.reset();
492
Eugene Zemtsov77d56262020-03-06 05:52:03493 // Unregister dump providers on their corresponding threads.
494 media_task_runner_->DeleteSoon(FROM_HERE,
495 std::move(media_thread_mem_dumper_));
496 main_thread_mem_dumper_.reset();
497
tguilbert350936ff2017-02-24 05:39:27498 // The underlying Pipeline must be stopped before it is destroyed.
Dale Curtisf273f8f2018-12-13 23:40:33499 //
500 // Note: This destruction happens synchronously on the media thread and
501 // |demuxer_|, |data_source_|, |compositor_|, and |media_log_| must outlive
502 // this process. They will be destructed by the DestructionHelper below
503 // after trampolining through the media thread.
Antonio Gomes81b56552019-05-15 22:15:28504 pipeline_controller_->Stop();
[email protected]f6af7592014-02-28 10:09:11505
dalecurtis83266c72015-10-29 18:43:20506 if (last_reported_memory_usage_)
507 adjust_allocated_memory_cb_.Run(-last_reported_memory_usage_);
508
dalecurtise1edb312016-06-22 02:33:21509 // Destruct compositor resources in the proper order.
danakj8d204a42018-05-18 18:05:35510 client_->SetCcLayer(nullptr);
CJ DiMeglio2302d202017-08-31 08:38:04511
Xiangjun Zhang5e20cba42018-01-10 19:54:56512 client_->MediaRemotingStopped(
Gyuyoung Kimf99f5f32019-09-26 03:45:15513 blink::WebMediaPlayerClient::kMediaRemotingStopNoText);
Xiangjun Zhang87299142017-09-13 20:35:03514
Dale Curtisf273f8f2018-12-13 23:40:33515 if (!surface_layer_for_video_enabled_ && video_layer_)
danakj25f030112018-05-11 18:26:54516 video_layer_->StopUsingProvider();
Matt Wolenetz95af6362018-01-04 20:23:42517
Dan Sanders6edfd782019-08-13 00:13:18518 simple_watch_timer_.Stop();
Ted Meyer257b682a2019-11-01 20:50:51519 media_log_->OnWebMediaPlayerDestroyed();
[email protected]ec9212f2008-12-18 21:40:36520
Dale Curtisf273f8f2018-12-13 23:40:33521 if (data_source_)
522 data_source_->Stop();
523
524 // Disconnect from the surface layer. We still preserve the |bridge_| until
525 // after pipeline shutdown to ensure any pending frames are painted for tests.
526 if (bridge_)
527 bridge_->ClearObserver();
528
Dale Curtisc96a3542018-12-17 22:15:23529 // Disconnect from the MediaObserver implementation since it's lifetime is
530 // tied to the RendererFactorySelector which can't be destroyed until after
531 // the Pipeline stops.
532 //
533 // Note: We can't use a WeakPtr with the RendererFactory because its methods
534 // are called on the media thread and this destruction takes place on the
535 // renderer thread.
536 if (observer_)
537 observer_->SetClient(nullptr);
538
[email protected]91fd3162019-09-05 23:51:32539 // If we're in the middle of an observation, then finish it.
540 will_play_helper_.CompleteObservationIfNeeded(learning::TargetValue(false));
541
Dale Curtisf273f8f2018-12-13 23:40:33542 // Handle destruction of things that need to be destructed after the pipeline
543 // completes stopping on the media thread.
544 media_task_runner_->PostTask(
Matt Wolenetz95af6362018-01-04 20:23:42545 FROM_HERE,
Dale Curtisf273f8f2018-12-13 23:40:33546 base::BindOnce(&DestructionHelper, std::move(main_task_runner_),
547 std::move(vfc_task_runner_), std::move(demuxer_),
548 std::move(data_source_), std::move(compositor_),
549 std::move(cdm_context_ref_),
550 std::move(pending_cdm_context_ref_), std::move(media_log_),
551 std::move(renderer_factory_selector_), std::move(bridge_),
552 !!chunk_demuxer_));
Matt Wolenetz95af6362018-01-04 20:23:42553}
554
chcunningham707b821e2018-05-29 08:42:19555WebMediaPlayer::LoadTiming WebMediaPlayerImpl::Load(
556 LoadType load_type,
557 const blink::WebMediaPlayerSource& source,
Takashi Toyoshima2e01e692018-11-16 03:23:27558 CorsMode cors_mode) {
guidou9bfe4e2f2016-04-09 08:31:19559 // Only URL or MSE blob URL is supported.
Blink Reformat1c4d759e2017-04-09 16:34:54560 DCHECK(source.IsURL());
561 blink::WebURL url = source.GetAsURL();
Xiaohan Wang81fd0022017-07-11 17:45:24562 DVLOG(1) << __func__ << "(" << load_type << ", " << GURL(url) << ", "
563 << cors_mode << ")";
chcunningham707b821e2018-05-29 08:42:19564
565 bool is_deferred = false;
566
Dale Curtise25163812018-09-21 22:13:39567 if (defer_load_cb_) {
chcunningham707b821e2018-05-29 08:42:19568 is_deferred = defer_load_cb_.Run(base::BindOnce(
Antonio Gomes142f32a2019-05-15 23:32:56569 &WebMediaPlayerImpl::DoLoad, weak_this_, load_type, url, cors_mode));
chcunningham707b821e2018-05-29 08:42:19570 } else {
571 DoLoad(load_type, url, cors_mode);
[email protected]d726eddc2013-07-02 22:25:55572 }
chcunningham707b821e2018-05-29 08:42:19573
574 return is_deferred ? LoadTiming::kDeferred : LoadTiming::kImmediate;
[email protected]62e5e682013-03-07 23:53:24575}
576
CJ DiMeglio013d4c472017-11-21 03:27:30577void WebMediaPlayerImpl::OnWebLayerUpdated() {}
578
danakj6a062b112018-05-17 16:25:45579void WebMediaPlayerImpl::RegisterContentsLayer(cc::Layer* layer) {
CJ DiMeglio2302d202017-08-31 08:38:04580 DCHECK(bridge_);
CJ DiMeglioa2b13fbc2018-06-27 00:50:59581 bridge_->SetContentsOpaque(opaque_);
danakj8d204a42018-05-18 18:05:35582 client_->SetCcLayer(layer);
CJ DiMeglio013d4c472017-11-21 03:27:30583}
584
danakj6a062b112018-05-17 16:25:45585void WebMediaPlayerImpl::UnregisterContentsLayer(cc::Layer* layer) {
586 // |client_| will unregister its cc::Layer if given a nullptr.
danakj8d204a42018-05-18 18:05:35587 client_->SetCcLayer(nullptr);
CJ DiMeglio2302d202017-08-31 08:38:04588}
589
Jennifer Apaciblec45fd052018-02-25 12:04:55590void WebMediaPlayerImpl::OnSurfaceIdUpdated(viz::SurfaceId surface_id) {
Jennifer Apaciblec45fd052018-02-25 12:04:55591 // TODO(726619): Handle the behavior when Picture-in-Picture mode is
592 // disabled.
Jennifer Apacible2b1dc5eb2018-04-27 16:23:28593 // The viz::SurfaceId may be updated when the video begins playback or when
594 // the size of the video changes.
Mounir Lamouri99ba5a62019-02-12 01:27:47595 if (client_)
596 client_->OnPictureInPictureStateChange();
Jennifer Apaciblec45fd052018-02-25 12:04:55597}
598
tsunghungee562e92016-07-20 18:03:31599void WebMediaPlayerImpl::EnableOverlay() {
600 overlay_enabled_ = true;
[email protected]f7df5b342018-07-13 20:22:13601 if (request_routing_token_cb_ &&
602 overlay_mode_ == OverlayMode::kUseAndroidOverlay) {
liberatofe8f9692017-06-08 19:17:36603 overlay_routing_token_is_pending_ = true;
liberato2ff93ad2017-05-17 07:28:24604 token_available_cb_.Reset(
Matt Reynoldsedd780b2020-07-08 20:25:02605 base::BindOnce(&WebMediaPlayerImpl::OnOverlayRoutingToken, weak_this_));
liberato2ff93ad2017-05-17 07:28:24606 request_routing_token_cb_.Run(token_available_cb_.callback());
watkf835a792016-06-24 23:24:40607 }
tsunghungee562e92016-07-20 18:03:31608
liberato2ff93ad2017-05-17 07:28:24609 // We have requested (and maybe already have) overlay information. If the
610 // restarted decoder requests overlay information, then we'll defer providing
611 // it if it hasn't arrived yet. Otherwise, this would be a race, since we
612 // don't know if the request for overlay info or restart will complete first.
tsunghungee562e92016-07-20 18:03:31613 if (decoder_requires_restart_for_overlay_)
watkdee516f2016-02-18 02:22:19614 ScheduleRestart();
615}
616
tsunghungee562e92016-07-20 18:03:31617void WebMediaPlayerImpl::DisableOverlay() {
618 overlay_enabled_ = false;
Jinsong Fan982342112019-11-07 23:45:45619 if (overlay_mode_ == OverlayMode::kUseAndroidOverlay) {
liberato2ff93ad2017-05-17 07:28:24620 token_available_cb_.Cancel();
liberatofe8f9692017-06-08 19:17:36621 overlay_routing_token_is_pending_ = false;
622 overlay_routing_token_ = OverlayInfo::RoutingToken();
liberato2ff93ad2017-05-17 07:28:24623 }
tsunghungee562e92016-07-20 18:03:31624
625 if (decoder_requires_restart_for_overlay_)
watkdee516f2016-02-18 02:22:19626 ScheduleRestart();
liberato2ff93ad2017-05-17 07:28:24627 else
628 MaybeSendOverlayInfoToDecoder();
watkdee516f2016-02-18 02:22:19629}
630
Blink Reformat1c4d759e2017-04-09 16:34:54631void WebMediaPlayerImpl::EnteredFullscreen() {
liberatofe8f9692017-06-08 19:17:36632 overlay_info_.is_fullscreen = true;
633
[email protected]c8d574722017-08-30 20:53:43634 // |always_enable_overlays_| implies that we're already in overlay mode, so
635 // take no action here. Otherwise, switch to an overlay if it's allowed and
636 // if it will display properly.
637 if (!always_enable_overlays_ && overlay_mode_ != OverlayMode::kNoOverlays &&
liberato2fd111be2017-01-04 00:25:06638 DoesOverlaySupportMetadata()) {
tsunghungee562e92016-07-20 18:03:31639 EnableOverlay();
liberato2fd111be2017-01-04 00:25:06640 }
liberato2ff93ad2017-05-17 07:28:24641
liberatofe8f9692017-06-08 19:17:36642 // We send this only if we can send multiple calls. Otherwise, either (a)
643 // we already sent it and we don't have a callback anyway (we reset it when
644 // it's called in restart mode), or (b) we'll send this later when the surface
645 // actually arrives. GVD assumes that the first overlay info will have the
646 // routing information. Note that we set |is_fullscreen_| earlier, so that
647 // if EnableOverlay() can include fullscreen info in case it sends the overlay
648 // info before returning.
649 if (!decoder_requires_restart_for_overlay_)
650 MaybeSendOverlayInfoToDecoder();
tsunghungee562e92016-07-20 18:03:31651}
652
Blink Reformat1c4d759e2017-04-09 16:34:54653void WebMediaPlayerImpl::ExitedFullscreen() {
liberatofe8f9692017-06-08 19:17:36654 overlay_info_.is_fullscreen = false;
655
[email protected]c8d574722017-08-30 20:53:43656 // If we're in overlay mode, then exit it unless we're supposed to allow
657 // overlays all the time.
658 if (!always_enable_overlays_ && overlay_enabled_)
tsunghungee562e92016-07-20 18:03:31659 DisableOverlay();
liberato2ff93ad2017-05-17 07:28:24660
liberatofe8f9692017-06-08 19:17:36661 // See EnteredFullscreen for why we do this.
662 if (!decoder_requires_restart_for_overlay_)
663 MaybeSendOverlayInfoToDecoder();
tsunghungee562e92016-07-20 18:03:31664}
665
Chisoon Jeong81080c922019-09-03 23:40:18666void WebMediaPlayerImpl::BecameDominantVisibleContent(bool is_dominant) {
xjzcdbbe732016-12-03 20:47:42667 if (observer_)
Chisoon Jeong81080c922019-09-03 23:40:18668 observer_->OnBecameDominantVisibleContent(is_dominant);
xjzcdbbe732016-12-03 20:47:42669}
670
Blink Reformat1c4d759e2017-04-09 16:34:54671void WebMediaPlayerImpl::SetIsEffectivelyFullscreen(
François Beaufortad6c5232018-02-26 11:00:44672 blink::WebFullscreenVideoStatus fullscreen_video_status) {
673 delegate_->SetIsEffectivelyFullscreen(delegate_id_, fullscreen_video_status);
[email protected]0526d5692020-02-28 00:24:31674
675 if (power_status_helper_) {
676 // We don't care about pip, so anything that's "not fullscreen" is good
677 // enough for us.
678 power_status_helper_->SetIsFullscreen(
679 fullscreen_video_status !=
680 blink::WebFullscreenVideoStatus::kNotEffectivelyFullscreen);
681 }
zqzhangabc08242017-03-02 16:07:14682}
683
Mounir Lamouri41a79c62017-06-06 12:53:16684void WebMediaPlayerImpl::OnHasNativeControlsChanged(bool has_native_controls) {
685 if (!watch_time_reporter_)
686 return;
687
688 if (has_native_controls)
689 watch_time_reporter_->OnNativeControlsEnabled();
690 else
691 watch_time_reporter_->OnNativeControlsDisabled();
692}
693
Mounir Lamourif9af74e72017-06-19 19:31:45694void WebMediaPlayerImpl::OnDisplayTypeChanged(
695 WebMediaPlayer::DisplayType display_type) {
Mounir Lamouri0484f40a2018-07-25 03:03:26696 if (surface_layer_for_video_enabled_) {
697 vfc_task_runner_->PostTask(
698 FROM_HERE,
699 base::BindOnce(
700 &VideoFrameCompositor::SetForceSubmit,
701 base::Unretained(compositor_.get()),
702 display_type == WebMediaPlayer::DisplayType::kPictureInPicture));
703 }
704
Mounir Lamourif9af74e72017-06-19 19:31:45705 if (!watch_time_reporter_)
706 return;
707
708 switch (display_type) {
709 case WebMediaPlayer::DisplayType::kInline:
710 watch_time_reporter_->OnDisplayTypeInline();
711 break;
712 case WebMediaPlayer::DisplayType::kFullscreen:
713 watch_time_reporter_->OnDisplayTypeFullscreen();
714 break;
715 case WebMediaPlayer::DisplayType::kPictureInPicture:
716 watch_time_reporter_->OnDisplayTypePictureInPicture();
François Beaufort3f62f382019-01-18 15:05:17717
718 // Resumes playback if it was paused when hidden.
719 if (paused_when_hidden_) {
720 paused_when_hidden_ = false;
721 OnPlay();
722 }
Mounir Lamourif9af74e72017-06-19 19:31:45723 break;
724 }
725}
726
[email protected]ef8394c2013-08-21 20:26:30727void WebMediaPlayerImpl::DoLoad(LoadType load_type,
[email protected]180ef242013-11-07 06:50:46728 const blink::WebURL& url,
Takashi Toyoshima2e01e692018-11-16 03:23:27729 CorsMode cors_mode) {
John Chen5b164a02017-11-01 00:36:09730 TRACE_EVENT1("media", "WebMediaPlayerImpl::DoLoad", "id", media_log_->id());
pkastingf5279482016-07-27 02:18:20731 DVLOG(1) << __func__;
acolwellb4034942014-08-28 15:42:43732 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]d726eddc2013-07-02 22:25:55733
[email protected]91fd3162019-09-05 23:51:32734 // Start a new observation. If there was one before, then we didn't play it.
735 will_play_helper_.CompleteObservationIfNeeded(learning::TargetValue(false));
736 // For now, send in an empty set of features. We should fill some in here,
737 // and / or ask blink (via |client_|) for features from the DOM.
738 learning::FeatureDictionary dict;
739 will_play_helper_.BeginObservation(dict);
740
Thomas Guilbert55b48d92019-03-25 23:16:54741#if defined(OS_ANDROID)
742 // Only allow credentials if the crossorigin attribute is unspecified
743 // (kCorsModeUnspecified) or "use-credentials" (kCorsModeUseCredentials).
744 // This value is only used by the MediaPlayerRenderer.
745 // See https://crbug.com/936566.
746 //
747 // The credentials mode also has repercussions in WouldTaintOrigin(), but we
748 // access what we need from |mb_data_source_|->cors_mode() directly, instead
749 // of storing it here.
750 allow_media_player_renderer_credentials_ = cors_mode != kCorsModeAnonymous;
751#endif // defined(OS_ANDROID)
752
Dale Curtis4841c712018-12-13 18:14:05753 // Note: |url| may be very large, take care when making copies.
754 loaded_url_ = GURL(url);
755 load_type_ = load_type;
756
757 ReportMetrics(load_type, loaded_url_, *frame_, media_log_.get());
[email protected]62e5e682013-03-07 23:53:24758
Dale Curtis4841c712018-12-13 18:14:05759 // Set subresource URL for crash reporting; will be truncated to 256 bytes.
Robert Sesekc5e91df2017-12-12 21:11:03760 static base::debug::CrashKeyString* subresource_url =
761 base::debug::AllocateCrashKeyString("subresource_url",
762 base::debug::CrashKeySize::Size256);
Dale Curtis4841c712018-12-13 18:14:05763 base::debug::SetCrashKeyString(subresource_url, loaded_url_.spec());
[email protected]ef8394c2013-08-21 20:26:30764
Blink Reformat1c4d759e2017-04-09 16:34:54765 SetNetworkState(WebMediaPlayer::kNetworkStateLoading);
766 SetReadyState(WebMediaPlayer::kReadyStateHaveNothing);
Ted Meyerc8572c32020-07-16 17:39:24767
768 // Do a truncation to kMaxUrlLength+1 at most; we can add ellipsis later.
769 media_log_->AddEvent<MediaLogEvent::kLoad>(
770 url.GetString().Substring(0, kMaxUrlLength + 1).Utf8());
Dale Curtis3899090ea2018-01-12 00:10:35771 load_start_time_ = base::TimeTicks::Now();
[email protected]d726eddc2013-07-02 22:25:55772
Ted Meyer1c37a8512020-08-06 15:16:32773 std::vector<TextTrackConfig> text_configs;
774 for (const auto& metadata : client_->GetTextTrackMetadata()) {
775 text_configs.emplace_back(TextTrackConfig::ConvertKind(metadata.kind()),
776 metadata.label(), metadata.language(),
777 metadata.id());
778 }
779 media_log_->SetProperty<MediaLogProperty::kTextTracks>(text_configs);
780
[email protected]91379332020-01-26 03:58:20781 // If we're adapting, then restart the smoothness experiment.
782 if (smoothness_helper_)
783 smoothness_helper_.reset();
784
Antonio Gomesf01cfbd2019-07-12 08:53:11785 media_metrics_provider_->Initialize(
786 load_type == kLoadTypeMediaSource,
787 load_type == kLoadTypeURL ? blink::GetMediaURLScheme(loaded_url_)
788 : mojom::MediaURLScheme::kUnknown);
Dale Curtis74612b72017-12-14 20:56:19789
David Dorwin30993312020-05-07 22:39:09790 if (demuxer_override_ || load_type == kLoadTypeMediaSource) {
791 // If a demuxer override was specified or a Media Source pipeline will be
792 // used, the pipeline can start immediately.
[email protected]ef8394c2013-08-21 20:26:30793 StartPipeline();
hubbe5f0ad43b2015-12-14 20:57:26794 } else {
Chih-Hsuan Kuo8f7209652020-06-10 16:40:18795 // If |loaded_url_| is remoting media, starting the pipeline.
796 if (loaded_url_.SchemeIs(remoting::kRemotingScheme)) {
797 StartPipeline();
798 return;
799 }
800
Dale Curtis4841c712018-12-13 18:14:05801 // Short circuit the more complex loading path for data:// URLs. Sending
802 // them through the network based loading path just wastes memory and causes
803 // worse performance since reads become asynchronous.
804 if (loaded_url_.SchemeIs(url::kDataScheme)) {
805 std::string mime_type, charset, data;
Dale Curtis44eacb72019-06-10 20:41:28806 if (!net::DataURL::Parse(loaded_url_, &mime_type, &charset, &data) ||
807 data.empty()) {
Dale Curtis4841c712018-12-13 18:14:05808 DataSourceInitialized(false);
809 return;
810 }
811
812 // Replace |loaded_url_| with an empty data:// URL since it may be large.
813 loaded_url_ = GURL("data:,");
814
815 // Mark all the data as buffered.
Antonio Gomes81b56552019-05-15 22:15:28816 buffered_data_source_host_->SetTotalBytes(data.size());
817 buffered_data_source_host_->AddBufferedByteRange(0, data.size());
Dale Curtis4841c712018-12-13 18:14:05818
819 DCHECK(!mb_data_source_);
Jose Lopes9023b6d2020-02-19 20:42:37820 data_source_ = std::make_unique<MemoryDataSource>(std::move(data));
Dale Curtis4841c712018-12-13 18:14:05821 DataSourceInitialized(true);
822 return;
823 }
824
John Delaneyb933391602018-10-17 21:50:47825 auto url_data =
Takashi Toyoshima2e01e692018-11-16 03:23:27826 url_index_->GetByUrl(url, static_cast<UrlData::CorsMode>(cors_mode));
Dale Curtis4841c712018-12-13 18:14:05827 mb_data_source_ = new MultibufferDataSource(
John Delaneyb933391602018-10-17 21:50:47828 main_task_runner_, std::move(url_data), media_log_.get(),
Antonio Gomes81b56552019-05-15 22:15:28829 buffered_data_source_host_.get(),
Dale Curtis4841c712018-12-13 18:14:05830 base::BindRepeating(&WebMediaPlayerImpl::NotifyDownloading,
Antonio Gomes142f32a2019-05-15 23:32:56831 weak_this_));
Dale Curtis4841c712018-12-13 18:14:05832 data_source_.reset(mb_data_source_);
Will Cassella8b18bcf2020-09-22 06:17:13833 mb_data_source_->OnRedirect(base::BindRepeating(
834 &WebMediaPlayerImpl::OnDataSourceRedirected, weak_this_));
Dale Curtis4841c712018-12-13 18:14:05835 mb_data_source_->SetPreload(preload_);
836 mb_data_source_->SetIsClientAudioElement(client_->IsAudioElement());
837 mb_data_source_->Initialize(
Jose Lopes6c523d42020-02-13 19:52:21838 base::BindOnce(&WebMediaPlayerImpl::DataSourceInitialized, weak_this_));
hubbe5f0ad43b2015-12-14 20:57:26839 }
[email protected]62e5e682013-03-07 23:53:24840}
841
Blink Reformat1c4d759e2017-04-09 16:34:54842void WebMediaPlayerImpl::Play() {
pkastingf5279482016-07-27 02:18:20843 DVLOG(1) << __func__;
acolwellb4034942014-08-28 15:42:43844 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]d43ed912009-02-03 04:52:53845
avayvod65fad272017-02-24 01:00:48846 // User initiated play unlocks background video playback.
Mustaq Ahmed4baa9a6e82019-12-20 23:43:46847 if (frame_->HasTransientUserActivation())
avayvod65fad272017-02-24 01:00:48848 video_locked_when_paused_when_hidden_ = false;
849
sandersd35d2c3f2017-01-14 02:04:42850 // TODO(sandersd): Do we want to reset the idle timer here?
tguilbert1bb1c782017-01-23 21:15:11851 delegate_->SetIdle(delegate_id_, false);
[email protected]49480902009-07-14 20:23:43852 paused_ = false;
Antonio Gomes81b56552019-05-15 22:15:28853 pipeline_controller_->SetPlaybackRate(playback_rate_);
dalecurtis4619cd02016-09-22 21:39:10854 background_pause_timer_.Stop();
sandersd1c0bba02016-03-04 23:14:08855
xjz48a9cb72016-12-20 04:02:49856 if (observer_)
857 observer_->OnPlaying();
858
[email protected]96665a82020-01-23 00:35:37859 // Try to create the smoothness helper, in case we were paused before.
860 UpdateSmoothnessHelper();
861
Sergey Ulanovb8656772020-04-21 22:56:14862 if (playback_events_recorder_)
863 playback_events_recorder_->OnPlaying();
864
Mounir Lamouri89c0a1b2018-03-01 15:00:44865 watch_time_reporter_->SetAutoplayInitiated(client_->WasAutoplayInitiated());
866
Dale Curtis051fdf62017-08-05 00:21:13867 // If we're seeking we'll trigger the watch time reporter upon seek completed;
868 // we don't want to start it here since the seek time is unstable. E.g., when
869 // playing content with a positive start time we would have a zero seek time.
870 if (!Seeking()) {
871 DCHECK(watch_time_reporter_);
872 watch_time_reporter_->OnPlaying();
873 }
874
Chris Cunninghamd9df58e2017-08-29 00:04:23875 if (video_decode_stats_reporter_)
876 video_decode_stats_reporter_->OnPlaying();
877
Dan Sanders6edfd782019-08-13 00:13:18878 simple_watch_timer_.Start();
Ted Meyerd5885f82019-07-16 19:19:17879 media_metrics_provider_->SetHasPlayed();
Ted Meyer0134aed2020-01-23 23:56:40880 media_log_->AddEvent<MediaLogEvent::kPlay>();
Dale Curtis18ad391b2019-05-30 23:25:09881
882 MaybeUpdateBufferSizesForPlayback();
sandersd50a635e2016-04-04 22:50:09883 UpdatePlayState();
[email protected]91fd3162019-09-05 23:51:32884
[email protected]91fd3162019-09-05 23:51:32885 // Notify the learning task, if needed.
886 will_play_helper_.CompleteObservationIfNeeded(learning::TargetValue(true));
[email protected]ec9212f2008-12-18 21:40:36887}
888
Blink Reformat1c4d759e2017-04-09 16:34:54889void WebMediaPlayerImpl::Pause() {
pkastingf5279482016-07-27 02:18:20890 DVLOG(1) << __func__;
acolwellb4034942014-08-28 15:42:43891 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]d43ed912009-02-03 04:52:53892
sandersd50a635e2016-04-04 22:50:09893 // We update the paused state even when casting, since we expect pause() to be
894 // called when casting begins, and when we exit casting we should end up in a
895 // paused state.
[email protected]49480902009-07-14 20:23:43896 paused_ = true;
hubbed5f36882016-01-15 22:40:37897
avayvodeb9098d2017-01-07 00:33:03898 // No longer paused because it was hidden.
899 paused_when_hidden_ = false;
900
[email protected]96665a82020-01-23 00:35:37901 UpdateSmoothnessHelper();
902
avayvod65fad272017-02-24 01:00:48903 // User initiated pause locks background videos.
Mustaq Ahmed4baa9a6e82019-12-20 23:43:46904 if (frame_->HasTransientUserActivation())
avayvod65fad272017-02-24 01:00:48905 video_locked_when_paused_when_hidden_ = true;
906
Antonio Gomes81b56552019-05-15 22:15:28907 pipeline_controller_->SetPlaybackRate(0.0);
Dale Curtis2fc01cc2019-05-20 22:53:52908
909 // For states <= kReadyStateHaveMetadata, we may not have a renderer yet.
910 if (highest_ready_state_ > WebMediaPlayer::kReadyStateHaveMetadata)
911 paused_time_ = pipeline_controller_->GetMediaTime();
[email protected]090f7312011-08-05 23:26:40912
xjz48a9cb72016-12-20 04:02:49913 if (observer_)
914 observer_->OnPaused();
915
Sergey Ulanovb8656772020-04-21 22:56:14916 if (playback_events_recorder_)
917 playback_events_recorder_->OnPaused();
918
dalecurtis04bdb582016-08-17 22:15:23919 DCHECK(watch_time_reporter_);
920 watch_time_reporter_->OnPaused();
Chris Cunninghamd9df58e2017-08-29 00:04:23921
922 if (video_decode_stats_reporter_)
923 video_decode_stats_reporter_->OnPaused();
924
Dan Sanders6edfd782019-08-13 00:13:18925 simple_watch_timer_.Stop();
Ted Meyer0134aed2020-01-23 23:56:40926 media_log_->AddEvent<MediaLogEvent::kPause>();
avayvod2135a642017-01-13 00:17:14927
sandersd50a635e2016-04-04 22:50:09928 UpdatePlayState();
[email protected]ec9212f2008-12-18 21:40:36929}
930
Blink Reformat1c4d759e2017-04-09 16:34:54931void WebMediaPlayerImpl::Seek(double seconds) {
pkastingf5279482016-07-27 02:18:20932 DVLOG(1) << __func__ << "(" << seconds << "s)";
acolwellb4034942014-08-28 15:42:43933 DCHECK(main_task_runner_->BelongsToCurrentThread());
Ted Meyer0134aed2020-01-23 23:56:40934 media_log_->AddEvent<MediaLogEvent::kSeek>(seconds);
sandersd1c0bba02016-03-04 23:14:08935 DoSeek(base::TimeDelta::FromSecondsD(seconds), true);
936}
937
938void WebMediaPlayerImpl::DoSeek(base::TimeDelta time, bool time_updated) {
939 DCHECK(main_task_runner_->BelongsToCurrentThread());
John Chen5b164a02017-11-01 00:36:09940 TRACE_EVENT2("media", "WebMediaPlayerImpl::DoSeek", "target",
941 time.InSecondsF(), "id", media_log_->id());
[email protected]d43ed912009-02-03 04:52:53942
srirama.mccf671812015-01-08 11:59:13943 ReadyState old_state = ready_state_;
Blink Reformat1c4d759e2017-04-09 16:34:54944 if (ready_state_ > WebMediaPlayer::kReadyStateHaveMetadata)
945 SetReadyState(WebMediaPlayer::kReadyStateHaveMetadata);
[email protected]1bb666802013-11-28 06:12:08946
Dale Curtis051fdf62017-08-05 00:21:13947 // When paused or ended, we know exactly what the current time is and can
948 // elide seeks to it. However, there are two cases that are not elided:
sandersd1c0bba02016-03-04 23:14:08949 // 1) When the pipeline state is not stable.
950 // In this case we just let |pipeline_controller_| decide what to do, as
951 // it has complete information.
952 // 2) For MSE.
953 // Because the buffers may have changed between seeks, MSE seeks are
954 // never elided.
Antonio Gomes81b56552019-05-15 22:15:28955 if (paused_ && pipeline_controller_->IsStable() &&
Dale Curtis051fdf62017-08-05 00:21:13956 (paused_time_ == time ||
957 (ended_ && time == base::TimeDelta::FromSecondsD(Duration()))) &&
sandersd1c0bba02016-03-04 23:14:08958 !chunk_demuxer_) {
959 // If the ready state was high enough before, we can indicate that the seek
960 // completed just by restoring it. Otherwise we will just wait for the real
961 // ready state change to eventually happen.
Blink Reformat1c4d759e2017-04-09 16:34:54962 if (old_state == kReadyStateHaveEnoughData) {
srirama.m8f4a37562014-12-13 08:16:18963 main_task_runner_->PostTask(
Dale Curtisee9be30012018-09-21 22:21:51964 FROM_HERE, base::BindOnce(&WebMediaPlayerImpl::OnBufferingStateChange,
Chris Cunninghamfc0d67e2019-07-22 20:29:16965 weak_this_, BUFFERING_HAVE_ENOUGH,
966 BUFFERING_CHANGE_REASON_UNKNOWN));
srirama.m36ab2682014-12-11 04:20:01967 }
sandersd1c0bba02016-03-04 23:14:08968 return;
srirama.m36ab2682014-12-11 04:20:01969 }
[email protected]44ff37c02009-10-24 01:03:03970
Sergey Ulanovb8656772020-04-21 22:56:14971 if (playback_events_recorder_)
972 playback_events_recorder_->OnSeeking();
973
dalecurtis04bdb582016-08-17 22:15:23974 // Call this before setting |seeking_| so that the current media time can be
975 // recorded by the reporter.
976 if (watch_time_reporter_)
977 watch_time_reporter_->OnSeeking();
978
rajendrant4c288462020-10-13 07:35:35979 // Send the seek updates only when the seek pipeline hasn't started,
980 // OnPipelineSeeked is not called yet.
981 if (!seeking_)
982 delegate_->DidSeek(delegate_id_);
983
sandersd35d2c3f2017-01-14 02:04:42984 // TODO(sandersd): Move |seeking_| to PipelineController.
985 // TODO(sandersd): Do we want to reset the idle timer here?
tguilbert1bb1c782017-01-23 21:15:11986 delegate_->SetIdle(delegate_id_, false);
sandersd50a635e2016-04-04 22:50:09987 ended_ = false;
[email protected]b3766a22010-12-22 17:34:13988 seeking_ = true;
sandersd1c0bba02016-03-04 23:14:08989 seek_time_ = time;
990 if (paused_)
991 paused_time_ = time;
Antonio Gomes81b56552019-05-15 22:15:28992 pipeline_controller_->Seek(time, time_updated);
[email protected]b3766a22010-12-22 17:34:13993
sandersd50a635e2016-04-04 22:50:09994 // This needs to be called after Seek() so that if a resume is triggered, it
995 // is to the correct time.
996 UpdatePlayState();
[email protected]ec9212f2008-12-18 21:40:36997}
998
Blink Reformat1c4d759e2017-04-09 16:34:54999void WebMediaPlayerImpl::SetRate(double rate) {
pkastingf5279482016-07-27 02:18:201000 DVLOG(1) << __func__ << "(" << rate << ")";
acolwellb4034942014-08-28 15:42:431001 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]d43ed912009-02-03 04:52:531002
Matt Wolenetz6010f6c2017-10-18 00:44:361003 if (rate != playback_rate_) {
1004 LIMITED_MEDIA_LOG(INFO, media_log_.get(), num_playback_rate_logs_,
1005 kMaxNumPlaybackRateLogs)
1006 << "Effective playback rate changed from " << playback_rate_ << " to "
1007 << rate;
1008 }
1009
[email protected]49480902009-07-14 20:23:431010 playback_rate_ = rate;
Dale Curtis18ad391b2019-05-30 23:25:091011 if (!paused_)
Antonio Gomes81b56552019-05-15 22:15:281012 pipeline_controller_->SetPlaybackRate(rate);
Dale Curtis18ad391b2019-05-30 23:25:091013
1014 MaybeUpdateBufferSizesForPlayback();
[email protected]ec9212f2008-12-18 21:40:361015}
1016
Blink Reformat1c4d759e2017-04-09 16:34:541017void WebMediaPlayerImpl::SetVolume(double volume) {
pkastingf5279482016-07-27 02:18:201018 DVLOG(1) << __func__ << "(" << volume << ")";
acolwellb4034942014-08-28 15:42:431019 DCHECK(main_task_runner_->BelongsToCurrentThread());
dalecurtisbb3eaac2016-01-27 21:10:251020 volume_ = volume;
Antonio Gomes81b56552019-05-15 22:15:281021 pipeline_controller_->SetVolume(volume_ * volume_multiplier_);
dalecurtis04bdb582016-08-17 22:15:231022 if (watch_time_reporter_)
1023 watch_time_reporter_->OnVolumeChange(volume);
Becca Hughes9f6fd4b82017-06-15 10:01:401024 delegate_->DidPlayerMutedStatusChange(delegate_id_, volume == 0.0);
mlamouri910111362016-11-04 11:28:241025
Wojciech Dzierżanowski69bb21842020-07-22 14:21:171026 if (delegate_has_audio_ != HasUnmutedAudio()) {
1027 delegate_has_audio_ = HasUnmutedAudio();
1028 delegate_->DidMediaMetadataChange(
1029 delegate_id_, delegate_has_audio_, HasVideo(),
1030 DurationToMediaContentType(GetPipelineMediaDuration()));
1031 }
1032
mlamouri910111362016-11-04 11:28:241033 // The play state is updated because the player might have left the autoplay
1034 // muted state.
1035 UpdatePlayState();
[email protected]ec9212f2008-12-18 21:40:361036}
[email protected]f0a51fb52009-03-05 12:46:381037
Chris Cunningham8c2ef912019-11-15 23:10:411038void WebMediaPlayerImpl::SetLatencyHint(double seconds) {
1039 DVLOG(1) << __func__ << "(" << seconds << ")";
Chris Cunninghame8c626332019-11-20 23:34:271040 DCHECK(main_task_runner_->BelongsToCurrentThread());
1041 base::Optional<base::TimeDelta> latency_hint;
1042 if (std::isfinite(seconds)) {
1043 DCHECK_GE(seconds, 0);
1044 latency_hint = base::TimeDelta::FromSecondsD(seconds);
1045 }
1046 pipeline_controller_->SetLatencyHint(latency_hint);
Chris Cunningham8c2ef912019-11-15 23:10:411047}
1048
Thomas Guilbertdd0e3e62020-06-24 23:53:421049void WebMediaPlayerImpl::SetPreservesPitch(bool preserves_pitch) {
1050 DCHECK(main_task_runner_->BelongsToCurrentThread());
1051 pipeline_controller_->SetPreservesPitch(preserves_pitch);
1052}
1053
Mounir Lamouri967f8fb72019-02-13 02:34:221054void WebMediaPlayerImpl::OnRequestPictureInPicture() {
[email protected]69db58f2018-09-26 20:27:561055 if (!surface_layer_for_video_enabled_)
1056 ActivateSurfaceLayerForVideo();
1057
Mounir Lamourib642a3c2018-07-09 15:45:311058 DCHECK(bridge_);
Mounir Lamouri99ba5a62019-02-12 01:27:471059 DCHECK(bridge_->GetSurfaceId().is_valid());
Jennifer Apacible3f0489102018-01-19 20:10:311060}
1061
Daniel Chengc1710b52018-10-24 03:12:281062void WebMediaPlayerImpl::SetSinkId(
1063 const blink::WebString& sink_id,
Antonio Gomese7813f32019-04-02 06:11:031064 blink::WebSetSinkIdCompleteCallback completion_callback) {
guidou69223ce2015-06-16 10:36:191065 DCHECK(main_task_runner_->BelongsToCurrentThread());
pkastingf5279482016-07-27 02:18:201066 DVLOG(1) << __func__;
guidouc7babef2015-10-22 00:42:351067
Dale Curtisca1b78f2019-01-15 03:11:261068 OutputDeviceStatusCB callback =
Antonio Gomese7813f32019-04-02 06:11:031069 ConvertToOutputDeviceStatusCB(std::move(completion_callback));
Noah Rose Ledesmad6fa2b92020-08-05 00:55:391070 auto sink_id_utf8 = sink_id.Utf8();
guidouc7babef2015-10-22 00:42:351071 media_task_runner_->PostTask(
Dale Curtisee9be30012018-09-21 22:21:511072 FROM_HERE, base::BindOnce(&SetSinkIdOnMediaThread, audio_source_provider_,
Noah Rose Ledesmad6fa2b92020-08-05 00:55:391073 sink_id_utf8, std::move(callback)));
1074 delegate_->DidAudioOutputSinkChange(delegate_id_, sink_id_utf8);
guidou69223ce2015-06-16 10:36:191075}
1076
Blink Reformat1c4d759e2017-04-09 16:34:541077STATIC_ASSERT_ENUM(WebMediaPlayer::kPreloadNone, MultibufferDataSource::NONE);
1078STATIC_ASSERT_ENUM(WebMediaPlayer::kPreloadMetaData,
dalecurtisb6e052f52016-08-25 00:35:551079 MultibufferDataSource::METADATA);
Blink Reformat1c4d759e2017-04-09 16:34:541080STATIC_ASSERT_ENUM(WebMediaPlayer::kPreloadAuto, MultibufferDataSource::AUTO);
[email protected]23a8b1d82011-04-05 16:28:201081
Blink Reformat1c4d759e2017-04-09 16:34:541082void WebMediaPlayerImpl::SetPreload(WebMediaPlayer::Preload preload) {
pkastingf5279482016-07-27 02:18:201083 DVLOG(1) << __func__ << "(" << preload << ")";
acolwellb4034942014-08-28 15:42:431084 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]4e6be3f2009-05-07 02:24:441085
dalecurtisb6e052f52016-08-25 00:35:551086 preload_ = static_cast<MultibufferDataSource::Preload>(preload);
Dale Curtis4841c712018-12-13 18:14:051087 if (mb_data_source_)
1088 mb_data_source_->SetPreload(preload_);
[email protected]4e6be3f2009-05-07 02:24:441089}
1090
Blink Reformat1c4d759e2017-04-09 16:34:541091bool WebMediaPlayerImpl::HasVideo() const {
acolwellb4034942014-08-28 15:42:431092 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]d43ed912009-02-03 04:52:531093
[email protected]b8877772014-03-26 20:17:151094 return pipeline_metadata_.has_video;
[email protected]d43ed912009-02-03 04:52:531095}
1096
Blink Reformat1c4d759e2017-04-09 16:34:541097bool WebMediaPlayerImpl::HasAudio() const {
acolwellb4034942014-08-28 15:42:431098 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]fc367af2009-08-14 23:06:351099
[email protected]b8877772014-03-26 20:17:151100 return pipeline_metadata_.has_audio;
[email protected]fc367af2009-08-14 23:06:351101}
1102
Blink Reformat1c4d759e2017-04-09 16:34:541103void WebMediaPlayerImpl::EnabledAudioTracksChanged(
servolkf25ceed2016-07-01 03:44:381104 const blink::WebVector<blink::WebMediaPlayer::TrackId>& enabledTrackIds) {
1105 DCHECK(main_task_runner_->BelongsToCurrentThread());
1106
1107 std::ostringstream logstr;
1108 std::vector<MediaTrack::Id> enabledMediaTrackIds;
1109 for (const auto& blinkTrackId : enabledTrackIds) {
Maciej Pawlowskif2556d122019-06-03 08:14:181110 const auto track_id = MediaTrack::Id(blinkTrackId.Utf8().data());
servolkf25ceed2016-07-01 03:44:381111 logstr << track_id << " ";
1112 enabledMediaTrackIds.push_back(track_id);
1113 }
dalecurtis9cddc0b2017-04-19 21:23:381114 MEDIA_LOG(INFO, media_log_.get())
1115 << "Enabled audio tracks: [" << logstr.str() << "]";
Antonio Gomes81b56552019-05-15 22:15:281116 pipeline_controller_->OnEnabledAudioTracksChanged(enabledMediaTrackIds);
servolkf25ceed2016-07-01 03:44:381117}
1118
Blink Reformat1c4d759e2017-04-09 16:34:541119void WebMediaPlayerImpl::SelectedVideoTrackChanged(
servolkf25ceed2016-07-01 03:44:381120 blink::WebMediaPlayer::TrackId* selectedTrackId) {
1121 DCHECK(main_task_runner_->BelongsToCurrentThread());
1122
servolk9bed6602017-02-24 01:20:111123 base::Optional<MediaTrack::Id> selected_video_track_id;
1124 if (selectedTrackId && !video_track_disabled_)
Blink Reformat1c4d759e2017-04-09 16:34:541125 selected_video_track_id = MediaTrack::Id(selectedTrackId->Utf8().data());
dalecurtis9cddc0b2017-04-19 21:23:381126 MEDIA_LOG(INFO, media_log_.get())
Maciej Pawlowskif2556d122019-06-03 08:14:181127 << "Selected video track: ["
1128 << selected_video_track_id.value_or(MediaTrack::Id()) << "]";
Antonio Gomes81b56552019-05-15 22:15:281129 pipeline_controller_->OnSelectedVideoTrackChanged(selected_video_track_id);
servolkf25ceed2016-07-01 03:44:381130}
1131
Dave Tapuskaec1a6d42020-02-12 20:57:481132gfx::Size WebMediaPlayerImpl::NaturalSize() const {
acolwellb4034942014-08-28 15:42:431133 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]d43ed912009-02-03 04:52:531134
Dave Tapuskaec1a6d42020-02-12 20:57:481135 return pipeline_metadata_.natural_size;
[email protected]d43ed912009-02-03 04:52:531136}
1137
Dave Tapuskaec1a6d42020-02-12 20:57:481138gfx::Size WebMediaPlayerImpl::VisibleSize() const {
Jiajia Qin82acdc02017-07-31 09:55:141139 DCHECK(main_task_runner_->BelongsToCurrentThread());
1140 scoped_refptr<VideoFrame> video_frame = GetCurrentFrameFromCompositor();
1141 if (!video_frame)
Dave Tapuskaec1a6d42020-02-12 20:57:481142 return gfx::Size();
Jiajia Qin82acdc02017-07-31 09:55:141143
Dave Tapuskaec1a6d42020-02-12 20:57:481144 return video_frame->visible_rect().size();
Jiajia Qin82acdc02017-07-31 09:55:141145}
1146
Blink Reformat1c4d759e2017-04-09 16:34:541147bool WebMediaPlayerImpl::Paused() const {
acolwellb4034942014-08-28 15:42:431148 DCHECK(main_task_runner_->BelongsToCurrentThread());
Antonio Gomes81b56552019-05-15 22:15:281149 return pipeline_controller_->GetPlaybackRate() == 0.0f;
[email protected]d43ed912009-02-03 04:52:531150}
1151
François Beaufort184f6042019-01-25 05:50:051152bool WebMediaPlayerImpl::PausedWhenHidden() const {
1153 return paused_when_hidden_;
1154}
1155
Blink Reformat1c4d759e2017-04-09 16:34:541156bool WebMediaPlayerImpl::Seeking() const {
acolwellb4034942014-08-28 15:42:431157 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]d43ed912009-02-03 04:52:531158
Blink Reformat1c4d759e2017-04-09 16:34:541159 if (ready_state_ == WebMediaPlayer::kReadyStateHaveNothing)
[email protected]0acebfa2009-08-21 22:45:401160 return false;
[email protected]67cd5052009-09-10 21:53:221161
[email protected]b3766a22010-12-22 17:34:131162 return seeking_;
[email protected]ec9212f2008-12-18 21:40:361163}
1164
Blink Reformat1c4d759e2017-04-09 16:34:541165double WebMediaPlayerImpl::Duration() const {
acolwellb4034942014-08-28 15:42:431166 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]39bdde32013-04-17 17:44:201167
Blink Reformat1c4d759e2017-04-09 16:34:541168 if (ready_state_ == WebMediaPlayer::kReadyStateHaveNothing)
[email protected]39bdde32013-04-17 17:44:201169 return std::numeric_limits<double>::quiet_NaN();
1170
chcunninghamb92d5062017-01-10 21:50:221171 // Use duration from ChunkDemuxer when present. MSE allows users to specify
1172 // duration as a double. This propagates to the rest of the pipeline as a
1173 // TimeDelta with potentially reduced precision (limited to Microseconds).
1174 // ChunkDemuxer returns the full-precision user-specified double. This ensures
1175 // users can "get" the exact duration they "set".
1176 if (chunk_demuxer_)
1177 return chunk_demuxer_->GetDuration();
1178
avayvodcc273dd2017-01-19 19:35:121179 base::TimeDelta pipeline_duration = GetPipelineMediaDuration();
chcunninghamb92d5062017-01-10 21:50:221180 return pipeline_duration == kInfiniteDuration
1181 ? std::numeric_limits<double>::infinity()
1182 : pipeline_duration.InSecondsF();
[email protected]d43ed912009-02-03 04:52:531183}
1184
[email protected]db66d0092014-04-16 07:15:121185double WebMediaPlayerImpl::timelineOffset() const {
acolwellb4034942014-08-28 15:42:431186 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]db66d0092014-04-16 07:15:121187
1188 if (pipeline_metadata_.timeline_offset.is_null())
1189 return std::numeric_limits<double>::quiet_NaN();
1190
1191 return pipeline_metadata_.timeline_offset.ToJsTime();
1192}
1193
Dale Curtis051fdf62017-08-05 00:21:131194base::TimeDelta WebMediaPlayerImpl::GetCurrentTimeInternal() const {
1195 DCHECK(main_task_runner_->BelongsToCurrentThread());
Dale Curtis051fdf62017-08-05 00:21:131196
1197 base::TimeDelta current_time;
1198 if (Seeking())
1199 current_time = seek_time_;
Dale Curtis051fdf62017-08-05 00:21:131200 else if (paused_)
1201 current_time = paused_time_;
1202 else
Antonio Gomes81b56552019-05-15 22:15:281203 current_time = pipeline_controller_->GetMediaTime();
Dale Curtis051fdf62017-08-05 00:21:131204
Dale Curtis77d6057b2019-05-03 01:31:561205 // It's possible for |current_time| to be kInfiniteDuration here if the page
1206 // seeks to kInfiniteDuration (2**64 - 1) when Duration() is infinite.
Dale Curtis051fdf62017-08-05 00:21:131207 DCHECK_GE(current_time, base::TimeDelta());
1208 return current_time;
1209}
1210
Blink Reformat1c4d759e2017-04-09 16:34:541211double WebMediaPlayerImpl::CurrentTime() const {
acolwellb4034942014-08-28 15:42:431212 DCHECK(main_task_runner_->BelongsToCurrentThread());
Blink Reformat1c4d759e2017-04-09 16:34:541213 DCHECK_NE(ready_state_, WebMediaPlayer::kReadyStateHaveNothing);
scherkusd2c745b2014-09-04 05:03:401214
Dale Curtis0da6f162020-02-01 01:02:591215 // Even though we have an explicit ended signal, a lot of content doesn't have
1216 // an accurate duration -- with some formats (e.g., VBR MP3, OGG) it can't be
1217 // known without a complete play-through from beginning to end.
1218 //
1219 // The HTML5 spec says that upon ended, current time must equal duration. Due
1220 // to the aforementioned issue, if we rely exclusively on current time, we can
1221 // be a few milliseconds off of the duration.
1222 const auto duration = Duration();
1223 return (ended_ && !std::isinf(duration))
1224 ? duration
Xida Chenccd11472020-01-31 20:02:021225 : GetCurrentTimeInternal().InSecondsF();
[email protected]d43ed912009-02-03 04:52:531226}
1227
Dale Curtis0da6f162020-02-01 01:02:591228bool WebMediaPlayerImpl::IsEnded() const {
1229 DCHECK(main_task_runner_->BelongsToCurrentThread());
1230 return ended_;
1231}
1232
Blink Reformat1c4d759e2017-04-09 16:34:541233WebMediaPlayer::NetworkState WebMediaPlayerImpl::GetNetworkState() const {
acolwellb4034942014-08-28 15:42:431234 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]ddb1e5a2010-12-13 20:10:451235 return network_state_;
1236}
1237
Blink Reformat1c4d759e2017-04-09 16:34:541238WebMediaPlayer::ReadyState WebMediaPlayerImpl::GetReadyState() const {
acolwellb4034942014-08-28 15:42:431239 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]ddb1e5a2010-12-13 20:10:451240 return ready_state_;
1241}
1242
CJ DiMeglio89240472018-10-18 18:21:101243blink::WebMediaPlayer::SurfaceLayerMode
1244WebMediaPlayerImpl::GetVideoSurfaceLayerMode() const {
1245 return surface_layer_mode_;
1246}
1247
wolenetzed8e7092017-04-21 16:28:591248blink::WebString WebMediaPlayerImpl::GetErrorMessage() const {
wolenetz4d39cc02016-04-05 19:54:411249 DCHECK(main_task_runner_->BelongsToCurrentThread());
wolenetzed8e7092017-04-21 16:28:591250 return blink::WebString::FromUTF8(media_log_->GetErrorMessage());
wolenetz4d39cc02016-04-05 19:54:411251}
1252
Blink Reformat1c4d759e2017-04-09 16:34:541253blink::WebTimeRanges WebMediaPlayerImpl::Buffered() const {
acolwellb4034942014-08-28 15:42:431254 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]779a8322014-08-22 21:28:371255
acolwell9e0840d2014-09-06 19:01:321256 Ranges<base::TimeDelta> buffered_time_ranges =
Antonio Gomes81b56552019-05-15 22:15:281257 pipeline_controller_->GetBufferedTimeRanges();
[email protected]779a8322014-08-22 21:28:371258
avayvodcc273dd2017-01-19 19:35:121259 const base::TimeDelta duration = GetPipelineMediaDuration();
dalecurtis39a7f932016-07-19 18:34:591260 if (duration != kInfiniteDuration) {
Antonio Gomes81b56552019-05-15 22:15:281261 buffered_data_source_host_->AddBufferedTimeRanges(&buffered_time_ranges,
1262 duration);
[email protected]779a8322014-08-22 21:28:371263 }
Antonio Gomesf01cfbd2019-07-12 08:53:111264 return blink::ConvertToWebTimeRanges(buffered_time_ranges);
[email protected]02022fc2014-05-16 00:05:311265}
1266
Blink Reformat1c4d759e2017-04-09 16:34:541267blink::WebTimeRanges WebMediaPlayerImpl::Seekable() const {
acolwellb4034942014-08-28 15:42:431268 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]39bdde32013-04-17 17:44:201269
Blink Reformat1c4d759e2017-04-09 16:34:541270 if (ready_state_ < WebMediaPlayer::kReadyStateHaveMetadata)
philipjb0e6f3f2014-09-30 09:51:531271 return blink::WebTimeRanges();
1272
Blink Reformat1c4d759e2017-04-09 16:34:541273 const double seekable_end = Duration();
dalecurtis56359cb2014-10-28 00:06:291274
1275 // Allow a special exception for seeks to zero for streaming sources with a
1276 // finite duration; this allows looping to work.
Dale Curtis4841c712018-12-13 18:14:051277 const bool is_finite_stream = mb_data_source_ &&
1278 mb_data_source_->IsStreaming() &&
tguilbertade2bcb2017-01-07 02:57:451279 std::isfinite(seekable_end);
1280
tguilbert75e2bf62017-04-26 20:13:121281 // Do not change the seekable range when using the MediaPlayerRenderer. It
1282 // will take care of dropping invalid seeks.
1283 const bool force_seeks_to_zero =
1284 !using_media_player_renderer_ && is_finite_stream;
dalecurtis56359cb2014-10-28 00:06:291285
1286 // TODO(dalecurtis): Technically this allows seeking on media which return an
tguilbertade2bcb2017-01-07 02:57:451287 // infinite duration so long as DataSource::IsStreaming() is false. While not
dalecurtis56359cb2014-10-28 00:06:291288 // expected, disabling this breaks semi-live players, http://crbug.com/427412.
1289 const blink::WebTimeRange seekable_range(
tguilbertade2bcb2017-01-07 02:57:451290 0.0, force_seeks_to_zero ? 0.0 : seekable_end);
philipjb0e6f3f2014-09-30 09:51:531291 return blink::WebTimeRanges(&seekable_range, 1);
[email protected]ec9212f2008-12-18 21:40:361292}
1293
sandersd35d2c3f2017-01-14 02:04:421294bool WebMediaPlayerImpl::IsPrerollAttemptNeeded() {
1295 // TODO(sandersd): Replace with |highest_ready_state_since_seek_| if we need
1296 // to ensure that preroll always gets a chance to complete.
1297 // See http://crbug.com/671525.
Dale Curtis69e8f302019-05-22 07:36:581298 //
1299 // Note: Even though we get play/pause signals at kReadyStateHaveMetadata, we
1300 // must attempt to preroll until kReadyStateHaveFutureData so that the
1301 // canplaythrough event will be fired to the page (which may be waiting).
1302 //
1303 // TODO(dalecurtis): We should try signaling kReadyStateHaveFutureData upon
1304 // automatic-suspend of a non-playing element to avoid wasting resources.
1305 if (highest_ready_state_ >= ReadyState::kReadyStateHaveFutureData)
sandersd35d2c3f2017-01-14 02:04:421306 return false;
1307
Fredrik Hubinette4cfb4412017-08-23 00:03:071308 // To suspend before we reach kReadyStateHaveCurrentData is only ok
1309 // if we know we're going to get woken up when we get more data, which
1310 // will only happen if the network is in the "Loading" state.
1311 // This happens when the network is fast, but multiple videos are loading
1312 // and multiplexing gets held up waiting for available threads.
1313 if (highest_ready_state_ <= ReadyState::kReadyStateHaveMetadata &&
1314 network_state_ != WebMediaPlayer::kNetworkStateLoading) {
1315 return true;
1316 }
1317
sandersd35d2c3f2017-01-14 02:04:421318 if (preroll_attempt_pending_)
1319 return true;
1320
1321 // Freshly initialized; there has never been any loading progress. (Otherwise
1322 // |preroll_attempt_pending_| would be true when the start time is null.)
1323 if (preroll_attempt_start_time_.is_null())
1324 return false;
1325
1326 base::TimeDelta preroll_attempt_duration =
1327 tick_clock_->NowTicks() - preroll_attempt_start_time_;
1328 return preroll_attempt_duration < kPrerollAttemptTimeout;
1329}
1330
Blink Reformat1c4d759e2017-04-09 16:34:541331bool WebMediaPlayerImpl::DidLoadingProgress() {
acolwellb4034942014-08-28 15:42:431332 DCHECK(main_task_runner_->BelongsToCurrentThread());
dalecurtise7120dc2016-09-03 02:54:351333
1334 // Note: Separate variables used to ensure both methods are called every time.
Antonio Gomes81b56552019-05-15 22:15:281335 const bool pipeline_progress = pipeline_controller_->DidLoadingProgress();
1336 const bool data_progress = buffered_data_source_host_->DidLoadingProgress();
hubbeb2d3efd2017-05-05 23:26:381337 return pipeline_progress || data_progress;
[email protected]d43ed912009-02-03 04:52:531338}
1339
danakjff6a0262018-06-26 19:50:311340void WebMediaPlayerImpl::Paint(cc::PaintCanvas* canvas,
[email protected]dd5c7972014-08-21 15:00:371341 const blink::WebRect& rect,
Kai Ninomiya9e8ae29b2017-09-06 23:45:161342 cc::PaintFlags& flags,
1343 int already_uploaded_id,
1344 VideoFrameUploadMetadata* out_metadata) {
acolwellb4034942014-08-28 15:42:431345 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]dd061e12014-05-06 19:21:221346 TRACE_EVENT0("media", "WebMediaPlayerImpl:paint");
[email protected]4e6be3f2009-05-07 02:24:441347
watkd16bb3e2017-04-25 01:18:311348 // We can't copy from protected frames.
Xiaohan Wang24cfe2c2018-01-22 23:16:001349 if (cdm_context_ref_)
xhwang80739452016-01-13 00:48:001350 return;
1351
mcasasf1236fc22015-05-29 22:38:561352 scoped_refptr<VideoFrame> video_frame = GetCurrentFrameFromCompositor();
[email protected]0fe5d43b2014-01-24 23:32:451353
[email protected]b49beeb2013-03-01 20:04:001354 gfx::Rect gfx_rect(rect);
mcasas265bdbf82015-06-12 18:44:071355 if (video_frame.get() && video_frame->HasTextures()) {
Nathan Zabriskie04cec1c2020-03-12 00:55:261356 if (!raster_context_provider_)
danakj53f7ec902016-05-21 01:30:101357 return; // Unable to get/create a shared main thread context.
Nathan Zabriskie04cec1c2020-03-12 00:55:261358 if (!raster_context_provider_->GrContext())
danakj53f7ec902016-05-21 01:30:101359 return; // The context has been lost since and can't setup a GrContext.
dongseong.hwang0c4e9d82015-01-08 20:11:131360 }
Kai Ninomiyaef8c9e02017-09-27 04:07:401361 if (out_metadata && video_frame) {
Kai Ninomiya9e8ae29b2017-09-06 23:45:161362 // WebGL last-uploaded-frame-metadata API enabled. https://crbug.com/639174
1363 ComputeFrameUploadMetadata(video_frame.get(), already_uploaded_id,
1364 out_metadata);
1365 if (out_metadata->skipped) {
1366 // Skip uploading this frame.
1367 return;
1368 }
1369 }
zhuoyu.qian4689dde22017-10-16 04:11:481370 video_renderer_.Paint(
Julien Isorce6c83d8de2017-10-12 13:11:291371 video_frame, canvas, gfx::RectF(gfx_rect), flags,
Ted Meyer4a427632019-05-01 19:05:271372 pipeline_metadata_.video_decoder_config.video_transformation(),
Nathan Zabriskie04cec1c2020-03-12 00:55:261373 raster_context_provider_.get());
[email protected]ec9212f2008-12-18 21:40:361374}
[email protected]5df51652009-01-17 00:03:001375
Yutaka Hirano657a0552018-11-09 00:52:551376bool WebMediaPlayerImpl::WouldTaintOrigin() const {
Thomas Guilbert153f84572018-07-19 05:03:581377 if (demuxer_found_hls_) {
1378 // HLS manifests might pull segments from a different origin. We can't know
1379 // for sure, so we conservatively say no here.
Yutaka Hiranoa9cbaa72018-10-10 08:35:221380 return true;
1381 }
1382
Dale Curtis4841c712018-12-13 18:14:051383 if (!mb_data_source_)
Yutaka Hirano657a0552018-11-09 00:52:551384 return false;
1385
1386 // When the resource is redirected to another origin we think it as
1387 // tainted. This is actually not specified, and is under discussion.
1388 // See https://github.com/whatwg/fetch/issues/737.
Dale Curtis4841c712018-12-13 18:14:051389 if (!mb_data_source_->HasSingleOrigin() &&
1390 mb_data_source_->cors_mode() == UrlData::CORS_UNSPECIFIED) {
Yutaka Hirano657a0552018-11-09 00:52:551391 return true;
1392 }
1393
Dale Curtis4841c712018-12-13 18:14:051394 return mb_data_source_->IsCorsCrossOrigin();
[email protected]3fe27112012-06-07 04:00:011395}
1396
Blink Reformat1c4d759e2017-04-09 16:34:541397double WebMediaPlayerImpl::MediaTimeForTimeValue(double timeValue) const {
qinminb4c39782015-08-10 18:43:241398 return base::TimeDelta::FromSecondsD(timeValue).InSecondsF();
[email protected]e06e16d82011-05-26 22:13:331399}
1400
Blink Reformat1c4d759e2017-04-09 16:34:541401unsigned WebMediaPlayerImpl::DecodedFrameCount() const {
acolwellb4034942014-08-28 15:42:431402 DCHECK(main_task_runner_->BelongsToCurrentThread());
Dale Curtisc7d2a7d22018-01-11 20:01:051403 return GetPipelineStatistics().video_frames_decoded;
[email protected]4c51bc662011-02-16 02:03:161404}
1405
Blink Reformat1c4d759e2017-04-09 16:34:541406unsigned WebMediaPlayerImpl::DroppedFrameCount() const {
acolwellb4034942014-08-28 15:42:431407 DCHECK(main_task_runner_->BelongsToCurrentThread());
Dale Curtisc7d2a7d22018-01-11 20:01:051408 return GetPipelineStatistics().video_frames_dropped;
[email protected]4c51bc662011-02-16 02:03:161409}
1410
Dave Tapuska6c7154912018-07-30 20:39:001411uint64_t WebMediaPlayerImpl::AudioDecodedByteCount() const {
acolwellb4034942014-08-28 15:42:431412 DCHECK(main_task_runner_->BelongsToCurrentThread());
Dale Curtisc7d2a7d22018-01-11 20:01:051413 return GetPipelineStatistics().audio_bytes_decoded;
[email protected]4c51bc662011-02-16 02:03:161414}
1415
Dave Tapuska6c7154912018-07-30 20:39:001416uint64_t WebMediaPlayerImpl::VideoDecodedByteCount() const {
acolwellb4034942014-08-28 15:42:431417 DCHECK(main_task_runner_->BelongsToCurrentThread());
Dale Curtisc7d2a7d22018-01-11 20:01:051418 return GetPipelineStatistics().video_bytes_decoded;
[email protected]4c51bc662011-02-16 02:03:161419}
1420
Dale Curtiscdfc6f22019-06-26 22:05:231421bool WebMediaPlayerImpl::HasAvailableVideoFrame() const {
1422 return has_first_frame_;
1423}
1424
Blink Reformat1c4d759e2017-04-09 16:34:541425bool WebMediaPlayerImpl::CopyVideoTextureToPlatformTexture(
danakj46070b02016-03-28 21:54:111426 gpu::gles2::GLES2Interface* gl,
jiajia.qinc2943162017-05-12 01:34:391427 unsigned int target,
zmo57d577a2015-10-30 18:28:591428 unsigned int texture,
kbr0986e622017-04-13 02:34:581429 unsigned internal_format,
1430 unsigned format,
1431 unsigned type,
jiajia.qinc2943162017-05-12 01:34:391432 int level,
zmo57d577a2015-10-30 18:28:591433 bool premultiply_alpha,
Kai Ninomiya9e8ae29b2017-09-06 23:45:161434 bool flip_y,
1435 int already_uploaded_id,
1436 VideoFrameUploadMetadata* out_metadata) {
xhwang213e50c2016-10-10 23:56:311437 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]bfc05f22013-10-19 17:55:161438 TRACE_EVENT0("media", "WebMediaPlayerImpl:copyVideoTextureToPlatformTexture");
1439
watkd16bb3e2017-04-25 01:18:311440 // We can't copy from protected frames.
Xiaohan Wang24cfe2c2018-01-22 23:16:001441 if (cdm_context_ref_)
xhwang213e50c2016-10-10 23:56:311442 return false;
[email protected]dd061e12014-05-06 19:21:221443
xhwang213e50c2016-10-10 23:56:311444 scoped_refptr<VideoFrame> video_frame = GetCurrentFrameFromCompositor();
jbauman581d041c2016-07-21 01:01:031445 if (!video_frame.get() || !video_frame->HasTextures()) {
[email protected]e56f88c72013-06-25 22:31:291446 return false;
dongseong.hwang0c4e9d82015-01-08 20:11:131447 }
Kai Ninomiya9e8ae29b2017-09-06 23:45:161448 if (out_metadata) {
1449 // WebGL last-uploaded-frame-metadata API is enabled.
1450 // https://crbug.com/639174
1451 ComputeFrameUploadMetadata(video_frame.get(), already_uploaded_id,
1452 out_metadata);
1453 if (out_metadata->skipped) {
1454 // Skip uploading this frame.
1455 return true;
1456 }
1457 }
[email protected]df41e252014-02-03 23:39:501458
zhuoyu.qian4689dde22017-10-16 04:11:481459 return video_renderer_.CopyVideoFrameTexturesToGLTexture(
Nathan Zabriskie04cec1c2020-03-12 00:55:261460 raster_context_provider_.get(), gl, video_frame.get(), target, texture,
Dan Sanders930cc1d2018-10-03 00:45:081461 internal_format, format, type, level, premultiply_alpha, flip_y);
[email protected]6523b242013-03-13 11:10:071462}
1463
Yan, Shaobo44b79ba2019-04-02 06:09:271464bool WebMediaPlayerImpl::PrepareVideoFrameForWebGL(
1465 gpu::gles2::GLES2Interface* gl,
1466 unsigned target,
1467 unsigned texture,
1468 int already_uploaded_id,
1469 WebMediaPlayer::VideoFrameUploadMetadata* out_metadata) {
1470 DCHECK(main_task_runner_->BelongsToCurrentThread());
1471 TRACE_EVENT0("media", "WebMediaPlayerImpl::PrepareVideoFrameForWebGL");
1472
1473 // TODO(crbug.com/776222): How to deal with protected frames.
1474 scoped_refptr<VideoFrame> video_frame = GetCurrentFrameFromCompositor();
1475 if (!video_frame.get() || !video_frame->HasTextures()) {
1476 return false;
1477 }
1478 if (out_metadata) {
1479 // WebGL last-uploaded-frame-metadata API is enabled.
1480 ComputeFrameUploadMetadata(video_frame.get(), already_uploaded_id,
1481 out_metadata);
1482 if (out_metadata->skipped) {
1483 // Skip uploading this frame.
1484 return true;
1485 }
1486 }
1487
Yan, Shaobo44b79ba2019-04-02 06:09:271488 return video_renderer_.PrepareVideoFrameForWebGL(
Nathan Zabriskie04cec1c2020-03-12 00:55:261489 raster_context_provider_.get(), gl, video_frame.get(), target, texture);
Yan, Shaobo44b79ba2019-04-02 06:09:271490}
1491
Matt Wolenetz95af6362018-01-04 20:23:421492// static
Kai Ninomiya9e8ae29b2017-09-06 23:45:161493void WebMediaPlayerImpl::ComputeFrameUploadMetadata(
1494 VideoFrame* frame,
1495 int already_uploaded_id,
1496 VideoFrameUploadMetadata* out_metadata) {
1497 DCHECK(out_metadata);
Kai Ninomiyaef8c9e02017-09-27 04:07:401498 DCHECK(frame);
Kai Ninomiya9e8ae29b2017-09-06 23:45:161499 out_metadata->frame_id = frame->unique_id();
1500 out_metadata->visible_rect = frame->visible_rect();
1501 out_metadata->timestamp = frame->timestamp();
Thomas Guilbert76739b6b2020-06-19 02:16:221502 if (frame->metadata()->frame_duration.has_value()) {
1503 out_metadata->expected_timestamp =
1504 frame->timestamp() + *frame->metadata()->frame_duration;
Li, Hao0fab96a2019-10-31 02:46:471505 };
Kai Ninomiya9e8ae29b2017-09-06 23:45:161506 bool skip_possible = already_uploaded_id != -1;
1507 bool same_frame_id = frame->unique_id() == already_uploaded_id;
1508 out_metadata->skipped = skip_possible && same_frame_id;
1509}
1510
Blink Reformat1c4d759e2017-04-09 16:34:541511void WebMediaPlayerImpl::SetContentDecryptionModule(
[email protected]9ebc3b03f2014-08-13 04:01:231512 blink::WebContentDecryptionModule* cdm,
1513 blink::WebContentDecryptionModuleResult result) {
xhwang51139732017-02-24 19:36:081514 DVLOG(1) << __func__ << ": cdm = " << cdm;
acolwellb4034942014-08-28 15:42:431515 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]9ebc3b03f2014-08-13 04:01:231516
jrummell06f27072015-06-08 18:12:381517 // Once the CDM is set it can't be cleared as there may be frames being
1518 // decrypted on other threads. So fail this request.
1519 // http://crbug.com/462365#c7.
xhwang97de4202014-11-25 08:44:011520 if (!cdm) {
Blink Reformat1c4d759e2017-04-09 16:34:541521 result.CompleteWithError(
1522 blink::kWebContentDecryptionModuleExceptionInvalidStateError, 0,
xhwang79b193042016-12-13 18:52:431523 "The existing ContentDecryptionModule object cannot be removed at this "
1524 "time.");
xhwang97de4202014-11-25 08:44:011525 return;
1526 }
1527
jrummell89e61d82015-07-23 20:03:341528 // Create a local copy of |result| to avoid problems with the callback
1529 // getting passed to the media thread and causing |result| to be destructed
jrummell4acbec912016-02-27 02:45:031530 // on the wrong thread in some failure conditions. Blink should prevent
1531 // multiple simultaneous calls.
1532 DCHECK(!set_cdm_result_);
Jose Lopes9023b6d2020-02-19 20:42:371533 set_cdm_result_ =
1534 std::make_unique<blink::WebContentDecryptionModuleResult>(result);
jrummell89e61d82015-07-23 20:03:341535
Chris Cunninghamd4a00192019-03-21 20:02:491536 SetCdmInternal(cdm);
xhwang97de4202014-11-25 08:44:011537}
1538
xhwange8c4181a2014-12-06 08:10:011539void WebMediaPlayerImpl::OnEncryptedMediaInitData(
jrummellcf78967c2015-04-03 05:03:581540 EmeInitDataType init_data_type,
Avi Drissman97785ea2015-12-19 01:11:311541 const std::vector<uint8_t>& init_data) {
jrummellcf78967c2015-04-03 05:03:581542 DCHECK(init_data_type != EmeInitDataType::UNKNOWN);
xhwangbab66f52014-12-02 23:49:501543
Xiaohan Wangf63505d2017-10-21 08:00:531544 RecordEncryptedEvent(true);
xhwangbab66f52014-12-02 23:49:501545
dalecurtis04bdb582016-08-17 22:15:231546 // Recreate the watch time reporter if necessary.
1547 const bool was_encrypted = is_encrypted_;
1548 is_encrypted_ = true;
Dale Curtis3899090ea2018-01-12 00:10:351549 if (!was_encrypted) {
1550 media_metrics_provider_->SetIsEME();
1551 if (watch_time_reporter_)
1552 CreateWatchTimeReporter();
dalecurtis04bdb582016-08-17 22:15:231553
Chris Cunningham6c0ec292019-04-04 18:31:111554 // |was_encrypted| = false means we didn't have a CDM prior to observing
1555 // encrypted media init data. Reset the reporter until the CDM arrives. See
1556 // SetCdmInternal().
1557 DCHECK(!cdm_config_);
1558 video_decode_stats_reporter_.reset();
1559 }
Chris Cunninghamd9df58e2017-08-29 00:04:231560
Blink Reformat1c4d759e2017-04-09 16:34:541561 encrypted_client_->Encrypted(
Antonio Gomes89b70572019-07-13 00:44:211562 init_data_type, init_data.data(),
srirama.m26f864d02015-07-14 05:21:461563 base::saturated_cast<unsigned int>(init_data.size()));
xhwangbab66f52014-12-02 23:49:501564}
1565
servolk81e01e02016-03-05 03:29:151566void WebMediaPlayerImpl::OnFFmpegMediaTracksUpdated(
dcheng3076abbf2016-04-22 20:42:391567 std::unique_ptr<MediaTracks> tracks) {
servolk81e01e02016-03-05 03:29:151568 // For MSE/chunk_demuxer case the media track updates are handled by
1569 // WebSourceBufferImpl.
1570 DCHECK(demuxer_.get());
1571 DCHECK(!chunk_demuxer_);
servolkef1e5ef2016-03-25 04:55:261572
servolk16e8bdf82017-04-11 17:00:391573 // Report the media track information to blink. Only the first audio track and
1574 // the first video track are enabled by default to match blink logic.
1575 bool is_first_audio_track = true;
1576 bool is_first_video_track = true;
servolkef1e5ef2016-03-25 04:55:261577 for (const auto& track : tracks->tracks()) {
1578 if (track->type() == MediaTrack::Audio) {
Maciej Pawlowskif2556d122019-06-03 08:14:181579 client_->AddAudioTrack(
1580 blink::WebString::FromUTF8(track->id().value()),
1581 blink::WebMediaPlayerClient::kAudioTrackKindMain,
1582 blink::WebString::FromUTF8(track->label().value()),
1583 blink::WebString::FromUTF8(track->language().value()),
1584 is_first_audio_track);
servolk16e8bdf82017-04-11 17:00:391585 is_first_audio_track = false;
servolkef1e5ef2016-03-25 04:55:261586 } else if (track->type() == MediaTrack::Video) {
Maciej Pawlowskif2556d122019-06-03 08:14:181587 client_->AddVideoTrack(
1588 blink::WebString::FromUTF8(track->id().value()),
1589 blink::WebMediaPlayerClient::kVideoTrackKindMain,
1590 blink::WebString::FromUTF8(track->label().value()),
1591 blink::WebString::FromUTF8(track->language().value()),
1592 is_first_video_track);
servolk16e8bdf82017-04-11 17:00:391593 is_first_video_track = false;
servolkef1e5ef2016-03-25 04:55:261594 } else {
1595 // Text tracks are not supported through this code path yet.
1596 NOTREACHED();
1597 }
1598 }
servolk81e01e02016-03-05 03:29:151599}
1600
Chris Cunninghamd4a00192019-03-21 20:02:491601void WebMediaPlayerImpl::SetCdmInternal(
1602 blink::WebContentDecryptionModule* cdm) {
jrummelle616ee92016-10-08 02:15:441603 DCHECK(main_task_runner_->BelongsToCurrentThread());
1604 DCHECK(cdm);
Xiaohan Wang24cfe2c2018-01-22 23:16:001605
Chris Cunninghamd4a00192019-03-21 20:02:491606 const bool was_encrypted = is_encrypted_;
1607 is_encrypted_ = true;
1608
1609 // Recreate the watch time reporter if necessary.
1610 if (!was_encrypted) {
1611 media_metrics_provider_->SetIsEME();
1612 if (watch_time_reporter_)
1613 CreateWatchTimeReporter();
1614 }
1615
Chris Cunningham6c0ec292019-04-04 18:31:111616 WebContentDecryptionModuleImpl* web_cdm =
1617 ToWebContentDecryptionModuleImpl(cdm);
1618 auto cdm_context_ref = web_cdm->GetCdmContextRef();
Xiaohan Wang24cfe2c2018-01-22 23:16:001619 if (!cdm_context_ref) {
jrummelle616ee92016-10-08 02:15:441620 NOTREACHED();
1621 OnCdmAttached(false);
xhwang80739452016-01-13 00:48:001622 return;
1623 }
1624
Chris Cunningham6c0ec292019-04-04 18:31:111625 // Arrival of |cdm_config_| and |key_system_| unblocks recording of encrypted
1626 // stats. Attempt to create the stats reporter. Note, we do NOT guard this
1627 // within !was_encypted above because often the CDM arrives after the call to
1628 // OnEncryptedMediaInitData().
1629 cdm_config_ = web_cdm->GetCdmConfig();
1630 key_system_ = web_cdm->GetKeySystem();
1631 DCHECK(!key_system_.empty());
1632 CreateVideoDecodeStatsReporter();
1633
Xiaohan Wang24cfe2c2018-01-22 23:16:001634 CdmContext* cdm_context = cdm_context_ref->GetCdmContext();
1635 DCHECK(cdm_context);
jrummelle616ee92016-10-08 02:15:441636
1637 // Keep the reference to the CDM, as it shouldn't be destroyed until
1638 // after the pipeline is done with the |cdm_context|.
Xiaohan Wang24cfe2c2018-01-22 23:16:001639 pending_cdm_context_ref_ = std::move(cdm_context_ref);
Antonio Gomes81b56552019-05-15 22:15:281640 pipeline_controller_->SetCdm(
Jose Lopes25ba7b32020-03-12 16:01:271641 cdm_context,
1642 base::BindOnce(&WebMediaPlayerImpl::OnCdmAttached, weak_this_));
xhwang97de4202014-11-25 08:44:011643}
1644
jrummell89e61d82015-07-23 20:03:341645void WebMediaPlayerImpl::OnCdmAttached(bool success) {
xhwang51139732017-02-24 19:36:081646 DVLOG(1) << __func__ << ": success = " << success;
jrummelle616ee92016-10-08 02:15:441647 DCHECK(main_task_runner_->BelongsToCurrentThread());
Xiaohan Wang24cfe2c2018-01-22 23:16:001648 DCHECK(pending_cdm_context_ref_);
jrummelle616ee92016-10-08 02:15:441649
1650 // If the CDM is set from the constructor there is no promise
1651 // (|set_cdm_result_|) to fulfill.
xhwang97de4202014-11-25 08:44:011652 if (success) {
Ted Meyer7f5b4e22019-11-21 03:21:191653 media_log_->SetProperty<MediaLogProperty::kIsVideoEncrypted>(true);
xhwang29c5ad202017-04-14 07:02:191654
jrummelle616ee92016-10-08 02:15:441655 // This will release the previously attached CDM (if any).
Xiaohan Wang24cfe2c2018-01-22 23:16:001656 cdm_context_ref_ = std::move(pending_cdm_context_ref_);
jrummelle616ee92016-10-08 02:15:441657 if (set_cdm_result_) {
Blink Reformat1c4d759e2017-04-09 16:34:541658 set_cdm_result_->Complete();
jrummelle616ee92016-10-08 02:15:441659 set_cdm_result_.reset();
1660 }
1661
xhwang97de4202014-11-25 08:44:011662 return;
1663 }
1664
Xiaohan Wang24cfe2c2018-01-22 23:16:001665 pending_cdm_context_ref_.reset();
jrummelle616ee92016-10-08 02:15:441666 if (set_cdm_result_) {
Blink Reformat1c4d759e2017-04-09 16:34:541667 set_cdm_result_->CompleteWithError(
1668 blink::kWebContentDecryptionModuleExceptionNotSupportedError, 0,
xhwang79b193042016-12-13 18:52:431669 "Unable to set ContentDecryptionModule object");
jrummelle616ee92016-10-08 02:15:441670 set_cdm_result_.reset();
1671 }
[email protected]9ebc3b03f2014-08-13 04:01:231672}
1673
sandersd1c0bba02016-03-04 23:14:081674void WebMediaPlayerImpl::OnPipelineSeeked(bool time_updated) {
John Chen5b164a02017-11-01 00:36:091675 TRACE_EVENT2("media", "WebMediaPlayerImpl::OnPipelineSeeked", "target",
1676 seek_time_.InSecondsF(), "id", media_log_->id());
[email protected]5d11eff2011-09-15 00:06:061677 seeking_ = false;
wolenetz1b2ae7d2015-06-10 00:44:211678 seek_time_ = base::TimeDelta();
avayvod2135a642017-01-13 00:17:141679
hubbe5a2dec022016-03-17 01:14:231680 if (paused_) {
Antonio Gomes81b56552019-05-15 22:15:281681 paused_time_ = pipeline_controller_->GetMediaTime();
dalecurtis04bdb582016-08-17 22:15:231682 } else {
1683 DCHECK(watch_time_reporter_);
1684 watch_time_reporter_->OnPlaying();
Sergey Ulanovb8656772020-04-21 22:56:141685 if (playback_events_recorder_)
1686 playback_events_recorder_->OnPlaying();
hubbe5a2dec022016-03-17 01:14:231687 }
sandersd1c0bba02016-03-04 23:14:081688 if (time_updated)
1689 should_notify_time_changed_ = true;
dalecurtisaf34d8712016-09-20 04:32:261690
dalecurtis4f6d14d2017-02-22 17:42:221691 // Reset underflow duration upon seek; this prevents looping videos and user
1692 // actions from artificially inflating the duration.
1693 underflow_timer_.reset();
avayvod56e1f3942017-01-21 02:06:311694
1695 // Background video optimizations are delayed when shown/hidden if pipeline
1696 // is seeking.
1697 UpdateBackgroundVideoOptimizationState();
Dale Curtis2dc6089a2018-03-26 16:47:581698
Dale Curtisd71061f02019-05-21 21:33:541699 // If we successfully completed a suspended startup, we need to make a call to
1700 // UpdatePlayState() in case any events which should trigger a resume have
1701 // occurred during startup.
Dale Curtis2dc6089a2018-03-26 16:47:581702 if (attempting_suspended_start_ &&
Antonio Gomes81b56552019-05-15 22:15:281703 pipeline_controller_->IsPipelineSuspended()) {
Dale Curtisff576552018-03-30 02:32:441704 skip_metrics_due_to_startup_suspend_ = true;
Dale Curtisd71061f02019-05-21 21:33:541705
1706 // If we successfully completed a suspended startup, signal that we have
1707 // reached BUFFERING_HAVE_ENOUGH so that canplay and canplaythrough fire
1708 // correctly. We must unfortunately always do this because it's valid for
1709 // elements to play while not visible nor even in the DOM.
1710 //
1711 // Note: This call is dual purpose, it is also responsible for triggering an
1712 // UpdatePlayState() call which may need to resume the pipeline once Blink
1713 // has been told about the ReadyState change.
Chris Cunninghamfc0d67e2019-07-22 20:29:161714 OnBufferingStateChangeInternal(BUFFERING_HAVE_ENOUGH,
1715 BUFFERING_CHANGE_REASON_UNKNOWN, true);
Dale Curtis2dc6089a2018-03-26 16:47:581716 }
1717
1718 attempting_suspended_start_ = false;
[email protected]8931c41a2009-07-07 17:31:491719}
1720
sandersd1c0bba02016-03-04 23:14:081721void WebMediaPlayerImpl::OnPipelineSuspended() {
Dale Curtis83321152018-12-01 01:22:061722 // Add a log event so the player shows up as "SUSPENDED" in media-internals.
Ted Meyer0134aed2020-01-23 23:56:401723 media_log_->AddEvent<MediaLogEvent::kSuspended>();
Dale Curtis83321152018-12-01 01:22:061724
Dale Curtis04769ca2019-05-25 00:38:101725 if (attempting_suspended_start_) {
1726 DCHECK(pipeline_controller_->IsSuspended());
1727 did_lazy_load_ = !has_poster_ && HasVideo();
1728 }
1729
sandersd2f5bb6152017-03-29 22:57:531730 // Tell the data source we have enough data so that it may release the
Dale Curtis04769ca2019-05-25 00:38:101731 // connection (unless blink is waiting on us to signal play()).
1732 if (mb_data_source_ && !client_->CouldPlayIfEnoughData()) {
1733 // |attempting_suspended_start_| will be cleared by OnPipelineSeeked() which
1734 // will occur after this method during a suspended startup.
1735 if (attempting_suspended_start_ && did_lazy_load_) {
1736 DCHECK(!has_first_frame_);
1737 DCHECK(have_enough_after_lazy_load_cb_.IsCancelled());
1738
1739 // For lazy load, we won't know if the element is non-visible until a
1740 // layout completes, so to avoid unnecessarily tearing down the network
1741 // connection, briefly (250ms chosen arbitrarily) delay signaling "have
1742 // enough" to the MultiBufferDataSource.
1743 //
1744 // base::Unretained() is safe here since the base::CancelableOnceClosure
1745 // will cancel upon destruction of this class and |mb_data_source_| is
1746 // gauranteeed to outlive us.
1747 have_enough_after_lazy_load_cb_.Reset(
1748 base::BindOnce(&MultibufferDataSource::OnBufferingHaveEnough,
1749 base::Unretained(mb_data_source_), true));
1750 main_task_runner_->PostDelayedTask(
1751 FROM_HERE, have_enough_after_lazy_load_cb_.callback(),
1752 base::TimeDelta::FromMilliseconds(250));
1753 } else {
1754 have_enough_after_lazy_load_cb_.Cancel();
1755 mb_data_source_->OnBufferingHaveEnough(true);
1756 }
1757 }
dalecurtis37fe5862016-03-15 19:29:091758
sandersd50a635e2016-04-04 22:50:091759 ReportMemoryUsage();
1760
sandersd1c0bba02016-03-04 23:14:081761 if (pending_suspend_resume_cycle_) {
watkdee516f2016-02-18 02:22:191762 pending_suspend_resume_cycle_ = false;
sandersd50a635e2016-04-04 22:50:091763 UpdatePlayState();
dalecurtis0431cbf2016-03-12 01:19:431764 }
sandersd1c0bba02016-03-04 23:14:081765}
1766
avayvod2135a642017-01-13 00:17:141767void WebMediaPlayerImpl::OnBeforePipelineResume() {
Dale Curtis04769ca2019-05-25 00:38:101768 // Since we're resuming, cancel closing of the network connection.
1769 have_enough_after_lazy_load_cb_.Cancel();
1770
Dale Curtisff576552018-03-30 02:32:441771 // We went through suspended startup, so the player is only just now spooling
1772 // up for playback. As such adjust |load_start_time_| so it reports the same
1773 // metric as what would be reported if we had not suspended at startup.
1774 if (skip_metrics_due_to_startup_suspend_) {
1775 // In the event that the call to SetReadyState() initiated after pipeline
1776 // startup immediately tries to start playback, we should not update
1777 // |load_start_time_| to avoid losing visibility into the impact of a
1778 // suspended startup on the time until first frame / play ready for cases
1779 // where suspended startup was applied incorrectly.
1780 if (!attempting_suspended_start_)
1781 load_start_time_ = base::TimeTicks::Now() - time_to_metadata_;
1782 skip_metrics_due_to_startup_suspend_ = false;
1783 }
1784
avayvod2135a642017-01-13 00:17:141785 // Enable video track if we disabled it in the background - this way the new
1786 // renderer will attach its callbacks to the video stream properly.
1787 // TODO(avayvod): Remove this when disabling and enabling video tracks in
1788 // non-playing state works correctly. See https://crbug.com/678374.
1789 EnableVideoTrackIfNeeded();
1790 is_pipeline_resuming_ = true;
1791}
1792
1793void WebMediaPlayerImpl::OnPipelineResumed() {
1794 is_pipeline_resuming_ = false;
1795
avayvod56e1f3942017-01-21 02:06:311796 UpdateBackgroundVideoOptimizationState();
avayvod2135a642017-01-13 00:17:141797}
1798
alokp967c902452016-05-06 05:21:371799void WebMediaPlayerImpl::OnDemuxerOpened() {
1800 DCHECK(main_task_runner_->BelongsToCurrentThread());
dalecurtis9cddc0b2017-04-19 21:23:381801 client_->MediaSourceOpened(new WebMediaSourceImpl(chunk_demuxer_));
alokp967c902452016-05-06 05:21:371802}
1803
servolkf94b4602017-01-31 16:44:271804void WebMediaPlayerImpl::OnMemoryPressure(
1805 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
1806 DVLOG(2) << __func__ << " memory_pressure_level=" << memory_pressure_level;
1807 DCHECK(main_task_runner_->BelongsToCurrentThread());
1808 DCHECK(base::FeatureList::IsEnabled(kMemoryPressureBasedSourceBufferGC));
1809 DCHECK(chunk_demuxer_);
1810
Sebastien Marchand6f03d35f2020-01-28 15:12:451811 if (memory_pressure_level ==
1812 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
1813 return;
1814 }
1815
servolkf94b4602017-01-31 16:44:271816 // The new value of |memory_pressure_level| will take effect on the next
1817 // garbage collection. Typically this means the next SourceBuffer append()
1818 // operation, since per MSE spec, the garbage collection must only occur
1819 // during SourceBuffer append(). But if memory pressure is critical it might
1820 // be better to perform GC immediately rather than wait for the next append
1821 // and potentially get killed due to out-of-memory.
1822 // So if this experiment is enabled and pressure level is critical, we'll pass
1823 // down force_instant_gc==true, which will force immediate GC on
1824 // SourceBufferStreams.
1825 bool force_instant_gc =
1826 (enable_instant_source_buffer_gc_ &&
1827 memory_pressure_level ==
1828 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
1829
Matt Wolenetz95af6362018-01-04 20:23:421830 // base::Unretained is safe, since |chunk_demuxer_| is actually owned by
1831 // |this| via this->demuxer_. Note the destruction of |chunk_demuxer_| is done
1832 // from ~WMPI by first hopping to |media_task_runner_| to prevent race with
1833 // this task.
servolkf94b4602017-01-31 16:44:271834 media_task_runner_->PostTask(
Dale Curtisee9be30012018-09-21 22:21:511835 FROM_HERE, base::BindOnce(&ChunkDemuxer::OnMemoryPressure,
1836 base::Unretained(chunk_demuxer_),
1837 base::TimeDelta::FromSecondsD(CurrentTime()),
1838 memory_pressure_level, force_instant_gc));
servolkf94b4602017-01-31 16:44:271839}
1840
alokp967c902452016-05-06 05:21:371841void WebMediaPlayerImpl::OnError(PipelineStatus status) {
Xiaohan Wang73b433f2019-11-05 20:13:011842 DVLOG(1) << __func__ << ": status=" << status;
alokp967c902452016-05-06 05:21:371843 DCHECK(main_task_runner_->BelongsToCurrentThread());
1844 DCHECK_NE(status, PIPELINE_OK);
1845
1846 if (suppress_destruction_errors_)
1847 return;
1848
Thomas Guilbert6b6be3d2017-08-18 03:17:271849#if defined(OS_ANDROID)
Dale Curtisf273f8f2018-12-13 23:40:331850 // |mb_data_source_| may be nullptr if someone passes in a m3u8 as a data://
1851 // URL, since MediaPlayer doesn't support data:// URLs, fail playback now.
Dan Sanders675250992019-05-11 00:07:491852 const bool found_hls = base::FeatureList::IsEnabled(kHlsPlayer) &&
1853 status == PipelineStatus::DEMUXER_ERROR_DETECTED_HLS;
Dale Curtisf273f8f2018-12-13 23:40:331854 if (found_hls && mb_data_source_) {
Dan Sandersae245e42019-03-07 23:25:291855 demuxer_found_hls_ = true;
1856
Thomas Guilbert8cb48612020-04-29 19:28:451857 if (observer_)
1858 observer_->OnHlsManifestDetected();
1859
Dan Sandersd80b1842019-02-05 00:36:161860 UMA_HISTOGRAM_BOOLEAN("Media.WebMediaPlayerImpl.HLS.IsCorsCrossOrigin",
1861 mb_data_source_->IsCorsCrossOrigin());
Dan Sandersae245e42019-03-07 23:25:291862 if (mb_data_source_->IsCorsCrossOrigin()) {
1863 UMA_HISTOGRAM_BOOLEAN("Media.WebMediaPlayerImpl.HLS.HasAccessControl",
1864 mb_data_source_->HasAccessControl());
1865 }
1866
1867 // Note: Does not consider the full redirect chain, which could contain
1868 // undetected mixed content.
Dan Sandersd80b1842019-02-05 00:36:161869 bool frame_url_is_cryptographic = url::Origin(frame_->GetSecurityOrigin())
1870 .GetURL()
1871 .SchemeIsCryptographic();
1872 bool manifest_url_is_cryptographic =
1873 loaded_url_.SchemeIsCryptographic() &&
1874 mb_data_source_->GetUrlAfterRedirects().SchemeIsCryptographic();
1875 UMA_HISTOGRAM_BOOLEAN(
1876 "Media.WebMediaPlayerImpl.HLS.IsMixedContent",
1877 frame_url_is_cryptographic && !manifest_url_is_cryptographic);
Thomas Guilbert153f84572018-07-19 05:03:581878
Xiaohan Wang3684e0c2019-10-30 01:04:301879 renderer_factory_selector_->SetBaseFactoryType(
Xiaohan Wang48aaa192019-12-05 01:29:001880 RendererFactoryType::kMediaPlayer);
Thomas Guilbert6b6be3d2017-08-18 03:17:271881
Dale Curtisf273f8f2018-12-13 23:40:331882 loaded_url_ = mb_data_source_->GetUrlAfterRedirects();
1883 DCHECK(data_source_);
1884 data_source_->Stop();
1885 mb_data_source_ = nullptr;
1886
Antonio Gomes81b56552019-05-15 22:15:281887 pipeline_controller_->Stop();
Dale Curtis8a6281322017-08-31 00:35:531888 SetMemoryReportingState(false);
Eugene Zemtsov77d56262020-03-06 05:52:031889 media_task_runner_->DeleteSoon(FROM_HERE,
1890 std::move(media_thread_mem_dumper_));
Thomas Guilbert6b6be3d2017-08-18 03:17:271891
Dale Curtisf273f8f2018-12-13 23:40:331892 // Trampoline through the media task runner to destruct the demuxer and
1893 // data source now that we're switching to HLS playback.
1894 media_task_runner_->PostTask(
Dale Curtisee9be30012018-09-21 22:21:511895 FROM_HERE,
Dale Curtisf273f8f2018-12-13 23:40:331896 BindToCurrentLoop(base::BindOnce(
1897 [](std::unique_ptr<Demuxer> demuxer,
1898 std::unique_ptr<DataSource> data_source,
1899 base::OnceClosure start_pipeline_cb) {
1900 // Release resources before starting HLS.
1901 demuxer.reset();
1902 data_source.reset();
1903
1904 std::move(start_pipeline_cb).Run();
1905 },
1906 std::move(demuxer_), std::move(data_source_),
Antonio Gomes142f32a2019-05-15 23:32:561907 base::BindOnce(&WebMediaPlayerImpl::StartPipeline, weak_this_))));
Dale Curtisf273f8f2018-12-13 23:40:331908
Thomas Guilbert6b6be3d2017-08-18 03:17:271909 return;
1910 }
Dale Curtisf273f8f2018-12-13 23:40:331911
1912 // We found hls in a data:// URL, fail immediately.
1913 if (found_hls)
1914 status = PIPELINE_ERROR_EXTERNAL_RENDERER_FAILED;
Thomas Guilbert6b6be3d2017-08-18 03:17:271915#endif
1916
David Dorwin89fe8d02020-05-01 05:42:421917 MaybeSetContainerNameForMetrics();
Dan Sanders6edfd782019-08-13 00:13:181918 simple_watch_timer_.Stop();
Ted Meyer0134aed2020-01-23 23:56:401919 media_log_->NotifyError(status);
Dale Curtis74612b72017-12-14 20:56:191920 media_metrics_provider_->OnError(status);
Sergey Ulanovb8656772020-04-21 22:56:141921 if (playback_events_recorder_)
1922 playback_events_recorder_->OnError(status);
Dale Curtisccfd0cca2017-08-31 01:27:561923 if (watch_time_reporter_)
1924 watch_time_reporter_->OnError(status);
alokp967c902452016-05-06 05:21:371925
Blink Reformat1c4d759e2017-04-09 16:34:541926 if (ready_state_ == WebMediaPlayer::kReadyStateHaveNothing) {
alokp967c902452016-05-06 05:21:371927 // Any error that occurs before reaching ReadyStateHaveMetadata should
1928 // be considered a format error.
Blink Reformat1c4d759e2017-04-09 16:34:541929 SetNetworkState(WebMediaPlayer::kNetworkStateFormatError);
alokp967c902452016-05-06 05:21:371930 } else {
Antonio Gomesf01cfbd2019-07-12 08:53:111931 SetNetworkState(blink::PipelineErrorToNetworkState(status));
alokp967c902452016-05-06 05:21:371932 }
1933
Thomas Guilbert2e591392017-08-12 00:56:381934 // PipelineController::Stop() is idempotent.
Antonio Gomes81b56552019-05-15 22:15:281935 pipeline_controller_->Stop();
Thomas Guilbert2e591392017-08-12 00:56:381936
alokp967c902452016-05-06 05:21:371937 UpdatePlayState();
1938}
1939
1940void WebMediaPlayerImpl::OnEnded() {
John Chen5b164a02017-11-01 00:36:091941 TRACE_EVENT2("media", "WebMediaPlayerImpl::OnEnded", "duration", Duration(),
1942 "id", media_log_->id());
pkastingf5279482016-07-27 02:18:201943 DVLOG(1) << __func__;
acolwellb4034942014-08-28 15:42:431944 DCHECK(main_task_runner_->BelongsToCurrentThread());
scherkusd2c745b2014-09-04 05:03:401945
sandersd1c0bba02016-03-04 23:14:081946 // Ignore state changes until we've completed all outstanding operations.
Antonio Gomes81b56552019-05-15 22:15:281947 if (!pipeline_controller_->IsStable())
scherkusd2c745b2014-09-04 05:03:401948 return;
1949
1950 ended_ = true;
Blink Reformat1c4d759e2017-04-09 16:34:541951 client_->TimeChanged();
sandersd50a635e2016-04-04 22:50:091952
Sergey Ulanovb8656772020-04-21 22:56:141953 if (playback_events_recorder_)
1954 playback_events_recorder_->OnEnded();
1955
sandersd50a635e2016-04-04 22:50:091956 // We don't actually want this to run until |client_| calls seek() or pause(),
1957 // but that should have already happened in timeChanged() and so this is
1958 // expected to be a no-op.
1959 UpdatePlayState();
[email protected]576537842009-08-12 23:52:051960}
1961
Chisoon Jeong0a8e4bc2019-05-11 00:06:141962void WebMediaPlayerImpl::OnMetadata(const PipelineMetadata& metadata) {
pkastingf5279482016-07-27 02:18:201963 DVLOG(1) << __func__;
acolwellb4034942014-08-28 15:42:431964 DCHECK(main_task_runner_->BelongsToCurrentThread());
Dale Curtisff576552018-03-30 02:32:441965
1966 // Cache the |time_to_metadata_| to use for adjusting the TimeToFirstFrame and
1967 // TimeToPlayReady metrics later if we end up doing a suspended startup.
1968 time_to_metadata_ = base::TimeTicks::Now() - load_start_time_;
1969 media_metrics_provider_->SetTimeToMetadata(time_to_metadata_);
1970 RecordTimingUMA("Media.TimeToMetadata", time_to_metadata_);
[email protected]a8e2cb82012-08-17 00:02:391971
David Dorwin89fe8d02020-05-01 05:42:421972 MaybeSetContainerNameForMetrics();
Dale Curtis5bba03232018-08-30 17:57:381973
[email protected]b8877772014-03-26 20:17:151974 pipeline_metadata_ = metadata;
[email protected]c7a918e2020-01-17 00:18:271975 if (power_status_helper_)
[email protected]dc996312019-12-19 19:26:381976 power_status_helper_->SetMetadata(metadata);
[email protected]739847c02014-01-16 00:12:251977
Ted Meyer4a427632019-05-01 19:05:271978 UMA_HISTOGRAM_ENUMERATION(
1979 "Media.VideoRotation",
1980 metadata.video_decoder_config.video_transformation().rotation,
1981 VIDEO_ROTATION_MAX + 1);
[email protected]21c3f7502013-03-23 03:29:511982
John Rummelldb5a7ef2018-05-16 00:28:011983 if (HasAudio()) {
Ted Meyerd5885f82019-07-16 19:19:171984 media_metrics_provider_->SetHasAudio(metadata.audio_decoder_config.codec());
John Rummelldb5a7ef2018-05-16 00:28:011985 RecordEncryptionScheme("Audio",
1986 metadata.audio_decoder_config.encryption_scheme());
1987 }
1988
Blink Reformat1c4d759e2017-04-09 16:34:541989 if (HasVideo()) {
Ted Meyerd5885f82019-07-16 19:19:171990 media_metrics_provider_->SetHasVideo(metadata.video_decoder_config.codec());
John Rummelldb5a7ef2018-05-16 00:28:011991 RecordEncryptionScheme("Video",
1992 metadata.video_decoder_config.encryption_scheme());
1993
liberato2fd111be2017-01-04 00:25:061994 if (overlay_enabled_) {
1995 // SurfaceView doesn't support rotated video, so transition back if
[email protected]c8d574722017-08-30 20:53:431996 // the video is now rotated. If |always_enable_overlays_|, we keep the
liberato2fd111be2017-01-04 00:25:061997 // overlay anyway so that the state machine keeps working.
[email protected]c8d574722017-08-30 20:53:431998 // TODO(liberato): verify if compositor feedback catches this. If so,
1999 // then we don't need this check.
2000 if (!always_enable_overlays_ && !DoesOverlaySupportMetadata())
liberato2fd111be2017-01-04 00:25:062001 DisableOverlay();
liberato2fd111be2017-01-04 00:25:062002 }
watkf835a792016-06-24 23:24:402003
[email protected]702d721d2018-10-25 21:55:272004 if (surface_layer_mode_ ==
Mounir Lamouri28d648102019-11-27 16:49:032005 blink::WebMediaPlayer::SurfaceLayerMode::kAlways) {
[email protected]702d721d2018-10-25 21:55:272006 ActivateSurfaceLayerForVideo();
2007 } else {
danakj6e669e782018-05-16 16:57:172008 DCHECK(!video_layer_);
Ted Meyer4a427632019-05-01 19:05:272009 // TODO(tmathmeyer) does this need support for reflections as well?
danakj25f030112018-05-11 18:26:542010 video_layer_ = cc::VideoLayer::Create(
Julien Isorce6c83d8de2017-10-12 13:11:292011 compositor_.get(),
Ted Meyer4a427632019-05-01 19:05:272012 pipeline_metadata_.video_decoder_config.video_transformation()
2013 .rotation);
danakj8bc61c72018-05-16 13:55:062014 video_layer_->SetContentsOpaque(opaque_);
danakj8d204a42018-05-18 18:05:352015 client_->SetCcLayer(video_layer_.get());
lethalantidote7f6009d2017-07-07 21:47:392016 }
[email protected]a8e2cb82012-08-17 00:02:392017 }
dalecurtis8e4dc682016-03-15 02:30:302018
xjzd3fe45a2016-10-12 18:26:372019 if (observer_)
xjz15b483f2017-01-12 00:21:362020 observer_->OnMetadataChanged(pipeline_metadata_);
xjzd3fe45a2016-10-12 18:26:372021
Wojciech Dzierżanowski69bb21842020-07-22 14:21:172022 delegate_has_audio_ = HasUnmutedAudio();
2023 delegate_->DidMediaMetadataChange(
2024 delegate_id_, delegate_has_audio_, HasVideo(),
2025 DurationToMediaContentType(GetPipelineMediaDuration()));
2026
Dale Curtisc7d2a7d22018-01-11 20:01:052027 // TODO(dalecurtis): Don't create these until kReadyStateHaveFutureData; when
2028 // we create them early we just increase the chances of needing to throw them
2029 // away unnecessarily.
dalecurtis04bdb582016-08-17 22:15:232030 CreateWatchTimeReporter();
Chris Cunninghamd9df58e2017-08-29 00:04:232031 CreateVideoDecodeStatsReporter();
Dale Curtisc7d2a7d22018-01-11 20:01:052032
Dale Curtis1e7a02b42019-05-28 20:12:242033 // SetReadyState() may trigger all sorts of calls into this class (e.g.,
2034 // Play(), Pause(), etc) so do it last to avoid unexpected states during the
2035 // calls. An exception to this is UpdatePlayState(), which is safe to call and
2036 // needs to use the new ReadyState in its calculations.
Dale Curtis2fc01cc2019-05-20 22:53:522037 SetReadyState(WebMediaPlayer::kReadyStateHaveMetadata);
Dale Curtis1e7a02b42019-05-28 20:12:242038 UpdatePlayState();
[email protected]a8e2cb82012-08-17 00:02:392039}
2040
[email protected]69db58f2018-09-26 20:27:562041void WebMediaPlayerImpl::ActivateSurfaceLayerForVideo() {
2042 // Note that we might or might not already be in VideoLayer mode.
2043 DCHECK(!bridge_);
2044
2045 surface_layer_for_video_enabled_ = true;
2046
2047 // If we're in VideoLayer mode, then get rid of the layer.
2048 if (video_layer_) {
2049 client_->SetCcLayer(nullptr);
2050 video_layer_ = nullptr;
2051 }
2052
2053 bridge_ = std::move(create_bridge_callback_)
2054 .Run(this, compositor_->GetUpdateSubmissionStateCallback());
2055 bridge_->CreateSurfaceLayer();
2056
Ted Meyer4a427632019-05-01 19:05:272057 // TODO(tmathmeyer) does this need support for the reflection transformation
2058 // as well?
[email protected]69db58f2018-09-26 20:27:562059 vfc_task_runner_->PostTask(
2060 FROM_HERE,
Ted Meyer4a427632019-05-01 19:05:272061 base::BindOnce(
2062 &VideoFrameCompositor::EnableSubmission,
2063 base::Unretained(compositor_.get()), bridge_->GetSurfaceId(),
Ted Meyer4a427632019-05-01 19:05:272064 pipeline_metadata_.video_decoder_config.video_transformation()
2065 .rotation,
2066 IsInPictureInPicture()));
[email protected]69db58f2018-09-26 20:27:562067 bridge_->SetContentsOpaque(opaque_);
2068
2069 // If the element is already in Picture-in-Picture mode, it means that it
2070 // was set in this mode prior to this load, with a different
2071 // WebMediaPlayerImpl. The new player needs to send its id, size and
2072 // surface id to the browser process to make sure the states are properly
2073 // updated.
2074 // TODO(872056): the surface should be activated but for some reasons, it
2075 // does not. It is possible that this will no longer be needed after 872056
2076 // is fixed.
Dale Curtisca1b78f2019-01-15 03:11:262077 if (IsInPictureInPicture())
[email protected]69db58f2018-09-26 20:27:562078 OnSurfaceIdUpdated(bridge_->GetSurfaceId());
[email protected]69db58f2018-09-26 20:27:562079}
2080
Chris Cunninghamfc0d67e2019-07-22 20:29:162081void WebMediaPlayerImpl::OnBufferingStateChange(
2082 BufferingState state,
2083 BufferingStateChangeReason reason) {
2084 OnBufferingStateChangeInternal(state, reason, false);
Dale Curtis2dc6089a2018-03-26 16:47:582085}
2086
Chris Cunninghamd9df58e2017-08-29 00:04:232087void WebMediaPlayerImpl::CreateVideoDecodeStatsReporter() {
2088 // TODO(chcunningham): destroy reporter if we initially have video but the
2089 // track gets disabled. Currently not possible in default desktop Chrome.
2090 if (!HasVideo())
2091 return;
2092
Chris Cunningham89b4b762019-03-27 17:13:332093 // Only record stats from the local pipeline.
2094 if (is_flinging_ || is_remote_rendering_ || using_media_player_renderer_)
2095 return;
2096
Chris Cunninghamd9df58e2017-08-29 00:04:232097 // Stats reporter requires a valid config. We may not have one for HLS cases
2098 // where URL demuxer doesn't know details of the stream.
2099 if (!pipeline_metadata_.video_decoder_config.IsValidConfig())
2100 return;
2101
Chris Cunningham6c0ec292019-04-04 18:31:112102 // Profile must be known for use as index to save the reported stats.
Chris Cunningham5b1b67eb2019-03-23 03:24:412103 if (pipeline_metadata_.video_decoder_config.profile() ==
2104 VIDEO_CODEC_PROFILE_UNKNOWN) {
2105 return;
2106 }
2107
Chris Cunningham6c0ec292019-04-04 18:31:112108 // CdmConfig must be provided for use as index to save encrypted stats.
2109 if (is_encrypted_ && !cdm_config_) {
Chris Cunninghamd9df58e2017-08-29 00:04:232110 return;
Chris Cunningham6c0ec292019-04-04 18:31:112111 } else if (cdm_config_) {
2112 DCHECK(!key_system_.empty());
2113 }
Chris Cunninghamd9df58e2017-08-29 00:04:232114
Gyuyoung Kima040bc52019-10-30 01:14:352115 mojo::PendingRemote<mojom::VideoDecodeStatsRecorder> recorder;
Dale Curtis7e8a510d2017-12-14 19:19:482116 media_metrics_provider_->AcquireVideoDecodeStatsRecorder(
Gyuyoung Kima040bc52019-10-30 01:14:352117 recorder.InitWithNewPipeAndPassReceiver());
Chris Cunninghamc7c6a6d2017-11-23 14:06:452118
Chris Cunninghamd9df58e2017-08-29 00:04:232119 // Create capabilities reporter and synchronize its initial state.
Jose Lopes9023b6d2020-02-19 20:42:372120 video_decode_stats_reporter_ = std::make_unique<VideoDecodeStatsReporter>(
Chris Cunninghamc7c6a6d2017-11-23 14:06:452121 std::move(recorder),
Jose Lopesdda34ca2020-02-19 11:56:172122 base::BindRepeating(&WebMediaPlayerImpl::GetPipelineStatistics,
2123 base::Unretained(this)),
Chris Cunningham5b1b67eb2019-03-23 03:24:412124 pipeline_metadata_.video_decoder_config.profile(),
Chris Cunningham6c0ec292019-04-04 18:31:112125 pipeline_metadata_.natural_size, key_system_, cdm_config_,
Jose Lopes9023b6d2020-02-19 20:42:372126 frame_->GetTaskRunner(blink::TaskType::kInternalMedia));
Chris Cunninghamd9df58e2017-08-29 00:04:232127
2128 if (delegate_->IsFrameHidden())
2129 video_decode_stats_reporter_->OnHidden();
2130 else
2131 video_decode_stats_reporter_->OnShown();
2132
2133 if (paused_)
2134 video_decode_stats_reporter_->OnPaused();
2135 else
2136 video_decode_stats_reporter_->OnPlaying();
2137}
2138
hubbeb2d3efd2017-05-05 23:26:382139void WebMediaPlayerImpl::OnProgress() {
Chris Watkins1ffabc5f2017-05-10 20:52:092140 DVLOG(4) << __func__;
Dale Curtisad645f72019-09-06 23:28:162141
2142 // See IsPrerollAttemptNeeded() for more details. We can't use that method
2143 // here since it considers |preroll_attempt_start_time_| and for OnProgress()
2144 // events we must make the attempt -- since there may not be another event.
2145 if (highest_ready_state_ < ReadyState::kReadyStateHaveFutureData) {
hubbeb2d3efd2017-05-05 23:26:382146 // Reset the preroll attempt clock.
2147 preroll_attempt_pending_ = true;
2148 preroll_attempt_start_time_ = base::TimeTicks();
2149
2150 // Clear any 'stale' flag and give the pipeline a chance to resume. If we
2151 // are already resumed, this will cause |preroll_attempt_start_time_| to
2152 // be set.
2153 delegate_->ClearStaleFlag(delegate_id_);
2154 UpdatePlayState();
2155 } else if (ready_state_ == ReadyState::kReadyStateHaveFutureData &&
2156 CanPlayThrough()) {
2157 SetReadyState(WebMediaPlayer::kReadyStateHaveEnoughData);
2158 }
2159}
2160
2161bool WebMediaPlayerImpl::CanPlayThrough() {
2162 if (!base::FeatureList::IsEnabled(kSpecCompliantCanPlayThrough))
2163 return true;
2164 if (chunk_demuxer_)
2165 return true;
Dale Curtis4841c712018-12-13 18:14:052166 if (data_source_ && data_source_->AssumeFullyBuffered())
hubbeb2d3efd2017-05-05 23:26:382167 return true;
2168 // If we're not currently downloading, we have as much buffer as
2169 // we're ever going to get, which means we say we can play through.
2170 if (network_state_ == WebMediaPlayer::kNetworkStateIdle)
2171 return true;
Antonio Gomes81b56552019-05-15 22:15:282172 return buffered_data_source_host_->CanPlayThrough(
hubbeb2d3efd2017-05-05 23:26:382173 base::TimeDelta::FromSecondsD(CurrentTime()),
2174 base::TimeDelta::FromSecondsD(Duration()),
2175 playback_rate_ == 0.0 ? 1.0 : playback_rate_);
2176}
2177
Dale Curtisff576552018-03-30 02:32:442178void WebMediaPlayerImpl::OnBufferingStateChangeInternal(
2179 BufferingState state,
Chris Cunninghamfc0d67e2019-07-22 20:29:162180 BufferingStateChangeReason reason,
Dale Curtisff576552018-03-30 02:32:442181 bool for_suspended_start) {
Chris Cunninghamfc0d67e2019-07-22 20:29:162182 DVLOG(1) << __func__ << "(" << state << ", " << reason << ")";
alokp967c902452016-05-06 05:21:372183 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]b8877772014-03-26 20:17:152184
Ted Meyer742212b82018-08-22 17:21:422185 // Ignore buffering state changes caused by back-to-back seeking, so as not
2186 // to assume the second seek has finished when it was only the first seek.
Antonio Gomes81b56552019-05-15 22:15:282187 if (pipeline_controller_->IsPendingSeek())
[email protected]ba7d5f92014-06-24 05:37:402188 return;
[email protected]b8877772014-03-26 20:17:152189
Ted Meyer0134aed2020-01-23 23:56:402190 media_log_->AddEvent<MediaLogEvent::kBufferingStateChanged>(
2191 SerializableBufferingState<SerializableBufferingStateType::kPipeline>{
2192 state, reason, for_suspended_start});
dalecurtis869bf2f2017-01-10 18:01:102193
Ted Meyerd5885f82019-07-16 19:19:172194 if (state == BUFFERING_HAVE_ENOUGH && !for_suspended_start)
2195 media_metrics_provider_->SetHaveEnough();
2196
chcunninghameb270c92016-07-15 01:00:452197 if (state == BUFFERING_HAVE_ENOUGH) {
John Chen5b164a02017-11-01 00:36:092198 TRACE_EVENT1("media", "WebMediaPlayerImpl::BufferingHaveEnough", "id",
2199 media_log_->id());
Dale Curtisff576552018-03-30 02:32:442200 // The SetReadyState() call below may clear
2201 // |skip_metrics_due_to_startup_suspend_| so report this first.
2202 if (!have_reported_time_to_play_ready_ &&
2203 !skip_metrics_due_to_startup_suspend_) {
2204 DCHECK(!for_suspended_start);
Dale Curtis3899090ea2018-01-12 00:10:352205 have_reported_time_to_play_ready_ = true;
2206 const base::TimeDelta elapsed = base::TimeTicks::Now() - load_start_time_;
2207 media_metrics_provider_->SetTimeToPlayReady(elapsed);
2208 RecordTimingUMA("Media.TimeToPlayReady", elapsed);
2209 }
[email protected]ba7d5f92014-06-24 05:37:402210
Dale Curtisff576552018-03-30 02:32:442211 // Warning: This call may be re-entrant.
2212 SetReadyState(CanPlayThrough() ? WebMediaPlayer::kReadyStateHaveEnoughData
2213 : WebMediaPlayer::kReadyStateHaveFutureData);
2214
Dale Curtis18ad391b2019-05-30 23:25:092215 // Let the DataSource know we have enough data -- this is the only function
2216 // during which we advance to (or past) the kReadyStateHaveEnoughData state.
2217 // It may use this information to update buffer sizes or release unused
2218 // network connections.
2219 MaybeUpdateBufferSizesForPlayback();
Dale Curtis04769ca2019-05-25 00:38:102220 if (mb_data_source_ && !client_->CouldPlayIfEnoughData()) {
2221 // For LazyLoad this will be handled during OnPipelineSuspended().
2222 if (for_suspended_start && did_lazy_load_)
2223 DCHECK(!have_enough_after_lazy_load_cb_.IsCancelled());
2224 else
2225 mb_data_source_->OnBufferingHaveEnough(false);
2226 }
dalecurtis849cf4b22015-03-27 18:35:452227
chcunninghameb270c92016-07-15 01:00:452228 // Blink expects a timeChanged() in response to a seek().
sandersd35d2c3f2017-01-14 02:04:422229 if (should_notify_time_changed_) {
2230 should_notify_time_changed_ = false;
Blink Reformat1c4d759e2017-04-09 16:34:542231 client_->TimeChanged();
sandersd35d2c3f2017-01-14 02:04:422232 }
dalecurtis0f0097a2015-12-01 17:40:472233
chcunninghameb270c92016-07-15 01:00:452234 // Once we have enough, start reporting the total memory usage. We'll also
2235 // report once playback starts.
2236 ReportMemoryUsage();
dalecurtis9d638a12016-08-30 06:20:552237
dalecurtis4f6d14d2017-02-22 17:42:222238 // Report the amount of time it took to leave the underflow state.
2239 if (underflow_timer_) {
Dale Curtisc9069622019-10-24 20:20:502240 auto elapsed = underflow_timer_->Elapsed();
2241 RecordUnderflowDuration(elapsed);
2242 watch_time_reporter_->OnUnderflowComplete(elapsed);
dalecurtis9d638a12016-08-30 06:20:552243 underflow_timer_.reset();
2244 }
Sergey Ulanovb8656772020-04-21 22:56:142245
2246 if (playback_events_recorder_)
2247 playback_events_recorder_->OnBufferingComplete();
chcunninghameb270c92016-07-15 01:00:452248 } else {
2249 // Buffering has underflowed.
2250 DCHECK_EQ(state, BUFFERING_HAVE_NOTHING);
dalecurtisacd77d62016-09-09 23:23:142251
dalecurtisd06eabc2017-02-24 23:43:292252 // Report the number of times we've entered the underflow state. Ensure we
2253 // only report the value when transitioning from HAVE_ENOUGH to
2254 // HAVE_NOTHING.
Dale Curtis6995b862017-05-31 22:20:082255 if (ready_state_ == WebMediaPlayer::kReadyStateHaveEnoughData &&
2256 !seeking_) {
Jose Lopes9023b6d2020-02-19 20:42:372257 underflow_timer_ = std::make_unique<base::ElapsedTimer>();
Dale Curtis6995b862017-05-31 22:20:082258 watch_time_reporter_->OnUnderflow();
Michael Crousee75c7a42020-07-31 21:11:552259 delegate_->DidBufferUnderflow(delegate_id_);
Sergey Ulanovb8656772020-04-21 22:56:142260
2261 if (playback_events_recorder_)
2262 playback_events_recorder_->OnBuffering();
Dale Curtis6995b862017-05-31 22:20:082263 }
dalecurtisacd77d62016-09-09 23:23:142264
chcunninghameb270c92016-07-15 01:00:452265 // It shouldn't be possible to underflow if we've not advanced past
2266 // HAVE_CURRENT_DATA.
Blink Reformat1c4d759e2017-04-09 16:34:542267 DCHECK_GT(highest_ready_state_, WebMediaPlayer::kReadyStateHaveCurrentData);
2268 SetReadyState(WebMediaPlayer::kReadyStateHaveCurrentData);
chcunninghameb270c92016-07-15 01:00:452269 }
sandersd50a635e2016-04-04 22:50:092270
[email protected]91379332020-01-26 03:58:202271 // If this is an NNR, then notify the smoothness helper about it. Note that
2272 // it's unclear what we should do if there is no smoothness helper yet. As it
2273 // is, we just discard the NNR.
2274 if (state == BUFFERING_HAVE_NOTHING && reason == DECODER_UNDERFLOW &&
2275 smoothness_helper_) {
2276 smoothness_helper_->NotifyNNR();
2277 }
2278
sandersd50a635e2016-04-04 22:50:092279 UpdatePlayState();
[email protected]b8877772014-03-26 20:17:152280}
2281
alokp967c902452016-05-06 05:21:372282void WebMediaPlayerImpl::OnDurationChange() {
acolwellb4034942014-08-28 15:42:432283 DCHECK(main_task_runner_->BelongsToCurrentThread());
alokp967c902452016-05-06 05:21:372284
John Delaney2371b452018-12-13 04:30:332285 if (frame_->IsAdSubframe()) {
2286 UMA_HISTOGRAM_CUSTOM_TIMES("Ads.Media.Duration", GetPipelineMediaDuration(),
2287 base::TimeDelta::FromMilliseconds(1),
2288 base::TimeDelta::FromDays(1),
2289 50 /* bucket_count */);
2290 }
2291
Blink Reformat1c4d759e2017-04-09 16:34:542292 if (ready_state_ == WebMediaPlayer::kReadyStateHaveNothing)
alokp967c902452016-05-06 05:21:372293 return;
2294
Blink Reformat1c4d759e2017-04-09 16:34:542295 client_->DurationChanged();
Wojciech Dzierżanowski69bb21842020-07-22 14:21:172296
2297 delegate_->DidMediaMetadataChange(
2298 delegate_id_, delegate_has_audio_, HasVideo(),
2299 DurationToMediaContentType(GetPipelineMediaDuration()));
2300
Dale Curtisaebaeea2018-07-19 23:42:112301 if (watch_time_reporter_)
2302 watch_time_reporter_->OnDurationChanged(GetPipelineMediaDuration());
[email protected]81bb3322011-07-21 15:55:502303}
2304
alokp967c902452016-05-06 05:21:372305void WebMediaPlayerImpl::OnAddTextTrack(const TextTrackConfig& config,
Raymond Toy85ad6802019-12-17 23:55:192306 AddTextTrackDoneCB done_cb) {
acolwellb4034942014-08-28 15:42:432307 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]71537722013-05-23 06:47:532308
[email protected]8a561062013-11-22 01:19:312309 const WebInbandTextTrackImpl::Kind web_kind =
2310 static_cast<WebInbandTextTrackImpl::Kind>(config.kind());
Blink Reformat1c4d759e2017-04-09 16:34:542311 const blink::WebString web_label = blink::WebString::FromUTF8(config.label());
[email protected]8a561062013-11-22 01:19:312312 const blink::WebString web_language =
Blink Reformat1c4d759e2017-04-09 16:34:542313 blink::WebString::FromUTF8(config.language());
2314 const blink::WebString web_id = blink::WebString::FromUTF8(config.id());
[email protected]71537722013-05-23 06:47:532315
dcheng3076abbf2016-04-22 20:42:392316 std::unique_ptr<WebInbandTextTrackImpl> web_inband_text_track(
henriks9cce5fa2014-12-12 09:35:302317 new WebInbandTextTrackImpl(web_kind, web_label, web_language, web_id));
[email protected]8a561062013-11-22 01:19:312318
Dale Curtisca1b78f2019-01-15 03:11:262319 std::unique_ptr<TextTrack> text_track(new TextTrackImpl(
dcheng652f5ff2015-12-27 08:54:002320 main_task_runner_, client_, std::move(web_inband_text_track)));
[email protected]8a561062013-11-22 01:19:312321
Raymond Toy85ad6802019-12-17 23:55:192322 std::move(done_cb).Run(std::move(text_track));
[email protected]71537722013-05-23 06:47:532323}
2324
Xiaohan Wang640b41d2018-12-18 19:00:462325void WebMediaPlayerImpl::OnWaiting(WaitingReason reason) {
alokp967c902452016-05-06 05:21:372326 DCHECK(main_task_runner_->BelongsToCurrentThread());
2327
Xiaohan Wang640b41d2018-12-18 19:00:462328 switch (reason) {
Xiaohan Wangb54a75282019-10-04 21:34:432329 case WaitingReason::kNoCdm:
Xiaohan Wang640b41d2018-12-18 19:00:462330 case WaitingReason::kNoDecryptionKey:
2331 encrypted_client_->DidBlockPlaybackWaitingForKey();
2332 // TODO(jrummell): didResumePlaybackBlockedForKey() should only be called
2333 // when a key has been successfully added (e.g. OnSessionKeysChange() with
2334 // |has_additional_usable_key| = true). http://crbug.com/461903
2335 encrypted_client_->DidResumePlaybackBlockedForKey();
2336 return;
Xiaohan Wang640b41d2018-12-18 19:00:462337
Xiaohan Wanga7224d62019-01-04 22:22:302338 // Ideally this should be handled by PipelineController directly without
2339 // being proxied here. But currently Pipeline::Client (|this|) is passed to
2340 // PipelineImpl directly without going through |pipeline_controller_|,
2341 // making it difficult to do.
2342 // TODO(xhwang): Handle this in PipelineController when we have a clearer
2343 // picture on how to refactor WebMediaPlayerImpl, PipelineController and
2344 // PipelineImpl.
2345 case WaitingReason::kDecoderStateLost:
Antonio Gomes81b56552019-05-15 22:15:282346 pipeline_controller_->OnDecoderStateLost();
Xiaohan Wanga7224d62019-01-04 22:22:302347 return;
2348 }
alokp967c902452016-05-06 05:21:372349}
2350
alokp5d86e9b2016-05-17 20:20:412351void WebMediaPlayerImpl::OnVideoNaturalSizeChange(const gfx::Size& size) {
2352 DCHECK(main_task_runner_->BelongsToCurrentThread());
Blink Reformat1c4d759e2017-04-09 16:34:542353 DCHECK_NE(ready_state_, WebMediaPlayer::kReadyStateHaveNothing);
alokp5d86e9b2016-05-17 20:20:412354
Chris Cunningham038548b2017-07-10 22:36:302355 TRACE_EVENT0("media", "WebMediaPlayerImpl::OnVideoNaturalSizeChange");
xhwang60802652017-04-19 07:29:582356
xjz15b483f2017-01-12 00:21:362357 // The input |size| is from the decoded video frame, which is the original
2358 // natural size and need to be rotated accordingly.
Julien Isorce6c83d8de2017-10-12 13:11:292359 gfx::Size rotated_size = GetRotatedVideoSize(
Ted Meyer4a427632019-05-01 19:05:272360 pipeline_metadata_.video_decoder_config.video_transformation().rotation,
2361 size);
sandersd2c478422016-08-02 01:19:252362
xhwang60802652017-04-19 07:29:582363 RecordVideoNaturalSize(rotated_size);
2364
2365 gfx::Size old_size = pipeline_metadata_.natural_size;
2366 if (rotated_size == old_size)
alokp5d86e9b2016-05-17 20:20:412367 return;
2368
xjz516ef6d2017-01-07 00:23:062369 pipeline_metadata_.natural_size = rotated_size;
Dan Sanders7ef5d0f2019-05-03 18:30:112370
2371 if (using_media_player_renderer_ && old_size.IsEmpty()) {
2372 // If we are using MediaPlayerRenderer and this is the first size change, we
2373 // now know that there is a video track. This condition is paired with code
2374 // in CreateWatchTimeReporter() that guesses the existence of a video track.
2375 CreateWatchTimeReporter();
2376 } else {
Dan Sanders7ef5d0f2019-05-03 18:30:112377 UpdateSecondaryProperties();
2378 }
dalecurtis25405562017-04-14 23:35:112379
Chris Cunningham5b1b67eb2019-03-23 03:24:412380 if (video_decode_stats_reporter_ &&
2381 !video_decode_stats_reporter_->MatchesBucketedNaturalSize(
2382 pipeline_metadata_.natural_size)) {
2383 CreateVideoDecodeStatsReporter();
2384 }
Chris Cunninghamd9df58e2017-08-29 00:04:232385
[email protected]96665a82020-01-23 00:35:372386 // Create or replace the smoothness helper now that we have a size.
2387 UpdateSmoothnessHelper();
2388
Blink Reformat1c4d759e2017-04-09 16:34:542389 client_->SizeChanged();
xjz516ef6d2017-01-07 00:23:062390
xjz15b483f2017-01-12 00:21:362391 if (observer_)
2392 observer_->OnMetadataChanged(pipeline_metadata_);
peconn257951522017-06-09 18:24:592393
2394 delegate_->DidPlayerSizeChange(delegate_id_, NaturalSize());
alokp5d86e9b2016-05-17 20:20:412395}
2396
2397void WebMediaPlayerImpl::OnVideoOpacityChange(bool opaque) {
2398 DCHECK(main_task_runner_->BelongsToCurrentThread());
Blink Reformat1c4d759e2017-04-09 16:34:542399 DCHECK_NE(ready_state_, WebMediaPlayer::kReadyStateHaveNothing);
alokp5d86e9b2016-05-17 20:20:412400
2401 opaque_ = opaque;
Dale Curtis357630f22019-01-18 01:10:172402 if (!surface_layer_for_video_enabled_ && video_layer_)
2403 video_layer_->SetContentsOpaque(opaque_);
2404 else if (bridge_->GetCcLayer())
CJ DiMeglioa2b13fbc2018-06-27 00:50:592405 bridge_->SetContentsOpaque(opaque_);
alokp5d86e9b2016-05-17 20:20:412406}
2407
[email protected]218ac8de2020-01-03 23:41:432408void WebMediaPlayerImpl::OnVideoFrameRateChange(base::Optional<int> fps) {
2409 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]c7a918e2020-01-17 00:18:272410 if (power_status_helper_)
2411 power_status_helper_->SetAverageFrameRate(fps);
[email protected]96665a82020-01-23 00:35:372412
2413 last_reported_fps_ = fps;
2414 UpdateSmoothnessHelper();
[email protected]218ac8de2020-01-03 23:41:432415}
2416
Chris Cunningham038548b2017-07-10 22:36:302417void WebMediaPlayerImpl::OnAudioConfigChange(const AudioDecoderConfig& config) {
2418 DCHECK(main_task_runner_->BelongsToCurrentThread());
2419 DCHECK_NE(ready_state_, WebMediaPlayer::kReadyStateHaveNothing);
2420
Dale Curtisccfd0cca2017-08-31 01:27:562421 const bool codec_change =
2422 pipeline_metadata_.audio_decoder_config.codec() != config.codec();
Dale Curtisf8afcef32020-01-18 06:23:262423 const bool codec_profile_change =
2424 pipeline_metadata_.audio_decoder_config.profile() != config.profile();
2425
Chris Cunningham038548b2017-07-10 22:36:302426 pipeline_metadata_.audio_decoder_config = config;
2427
2428 if (observer_)
2429 observer_->OnMetadataChanged(pipeline_metadata_);
Dale Curtisccfd0cca2017-08-31 01:27:562430
Dale Curtisf8afcef32020-01-18 06:23:262431 if (codec_change || codec_profile_change)
Dale Curtis96d6e9382018-07-18 18:01:072432 UpdateSecondaryProperties();
Chris Cunningham038548b2017-07-10 22:36:302433}
2434
2435void WebMediaPlayerImpl::OnVideoConfigChange(const VideoDecoderConfig& config) {
2436 DCHECK(main_task_runner_->BelongsToCurrentThread());
2437 DCHECK_NE(ready_state_, WebMediaPlayer::kReadyStateHaveNothing);
2438
Dale Curtisccfd0cca2017-08-31 01:27:562439 const bool codec_change =
2440 pipeline_metadata_.video_decoder_config.codec() != config.codec();
Chris Cunningham5b1b67eb2019-03-23 03:24:412441 const bool codec_profile_change =
2442 pipeline_metadata_.video_decoder_config.profile() != config.profile();
Dale Curtisccfd0cca2017-08-31 01:27:562443
Chris Cunningham038548b2017-07-10 22:36:302444 pipeline_metadata_.video_decoder_config = config;
2445
2446 if (observer_)
2447 observer_->OnMetadataChanged(pipeline_metadata_);
Chris Cunninghamd9df58e2017-08-29 00:04:232448
Dale Curtisf8afcef32020-01-18 06:23:262449 if (codec_change || codec_profile_change)
Dale Curtis96d6e9382018-07-18 18:01:072450 UpdateSecondaryProperties();
Chris Cunningham5b1b67eb2019-03-23 03:24:412451
2452 if (video_decode_stats_reporter_ && codec_profile_change)
2453 CreateVideoDecodeStatsReporter();
Chris Cunningham038548b2017-07-10 22:36:302454}
2455
avayvodeecec52c2017-02-14 01:25:092456void WebMediaPlayerImpl::OnVideoAverageKeyframeDistanceUpdate() {
2457 UpdateBackgroundVideoOptimizationState();
2458}
2459
Ted Meyerd5885f82019-07-16 19:19:172460void WebMediaPlayerImpl::OnAudioDecoderChange(const PipelineDecoderInfo& info) {
2461 media_metrics_provider_->SetAudioPipelineInfo(info);
2462 if (info.decoder_name == audio_decoder_name_)
Dale Curtisc7d2a7d22018-01-11 20:01:052463 return;
2464
Ted Meyerd5885f82019-07-16 19:19:172465 audio_decoder_name_ = info.decoder_name;
Dale Curtisc7d2a7d22018-01-11 20:01:052466
2467 // If there's no current reporter, there's nothing to be done.
2468 if (!watch_time_reporter_)
2469 return;
2470
Dale Curtis96d6e9382018-07-18 18:01:072471 UpdateSecondaryProperties();
Dale Curtisc7d2a7d22018-01-11 20:01:052472}
2473
Ted Meyerd5885f82019-07-16 19:19:172474void WebMediaPlayerImpl::OnVideoDecoderChange(const PipelineDecoderInfo& info) {
2475 media_metrics_provider_->SetVideoPipelineInfo(info);
2476 if (info.decoder_name == video_decoder_name_)
Dale Curtisc7d2a7d22018-01-11 20:01:052477 return;
2478
Ted Meyerd5885f82019-07-16 19:19:172479 video_decoder_name_ = info.decoder_name;
Dale Curtisc7d2a7d22018-01-11 20:01:052480
2481 // If there's no current reporter, there's nothing to be done.
2482 if (!watch_time_reporter_)
2483 return;
2484
Dale Curtis96d6e9382018-07-18 18:01:072485 UpdateSecondaryProperties();
Dale Curtisc7d2a7d22018-01-11 20:01:052486}
2487
sandersd35d2c3f2017-01-14 02:04:422488void WebMediaPlayerImpl::OnFrameHidden() {
sandersd1e49fb62015-12-12 01:18:062489 DCHECK(main_task_runner_->BelongsToCurrentThread());
avayvod39c102402016-11-23 21:43:132490
avayvod65fad272017-02-24 01:00:482491 // Backgrounding a video requires a user gesture to resume playback.
2492 if (IsHidden())
2493 video_locked_when_paused_when_hidden_ = true;
2494
dalecurtis04bdb582016-08-17 22:15:232495 if (watch_time_reporter_)
2496 watch_time_reporter_->OnHidden();
avayvod48a8be52016-08-04 19:52:502497
Chris Cunninghamd9df58e2017-08-29 00:04:232498 if (video_decode_stats_reporter_)
2499 video_decode_stats_reporter_->OnHidden();
2500
avayvod65fad272017-02-24 01:00:482501 UpdateBackgroundVideoOptimizationState();
sandersd50a635e2016-04-04 22:50:092502 UpdatePlayState();
dalecurtis8b8505e72016-06-10 21:59:172503
2504 // Schedule suspended playing media to be paused if the user doesn't come back
2505 // to it within some timeout period to avoid any autoplay surprises.
2506 ScheduleIdlePauseTimer();
Dale Curtisca1b78f2019-01-15 03:11:262507
2508 // Notify the compositor of our page visibility status.
2509 vfc_task_runner_->PostTask(
2510 FROM_HERE,
2511 base::BindOnce(&VideoFrameCompositor::SetIsPageVisible,
2512 base::Unretained(compositor_.get()), !IsHidden()));
sandersd1e49fb62015-12-12 01:18:062513}
2514
sandersd35d2c3f2017-01-14 02:04:422515void WebMediaPlayerImpl::OnFrameClosed() {
sandersd1e49fb62015-12-12 01:18:062516 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]28347e72017-06-27 17:30:112517
sandersd35d2c3f2017-01-14 02:04:422518 UpdatePlayState();
2519}
2520
2521void WebMediaPlayerImpl::OnFrameShown() {
2522 DCHECK(main_task_runner_->BelongsToCurrentThread());
2523 background_pause_timer_.Stop();
2524
avayvod65fad272017-02-24 01:00:482525 // Foreground videos don't require user gesture to continue playback.
2526 video_locked_when_paused_when_hidden_ = false;
2527
dalecurtis04bdb582016-08-17 22:15:232528 if (watch_time_reporter_)
2529 watch_time_reporter_->OnShown();
2530
Chris Cunninghamd9df58e2017-08-29 00:04:232531 if (video_decode_stats_reporter_)
2532 video_decode_stats_reporter_->OnShown();
2533
Dale Curtisca1b78f2019-01-15 03:11:262534 // Notify the compositor of our page visibility status.
2535 vfc_task_runner_->PostTask(
2536 FROM_HERE,
2537 base::BindOnce(&VideoFrameCompositor::SetIsPageVisible,
2538 base::Unretained(compositor_.get()), !IsHidden()));
2539
Dale Curtisdcbb81a2017-08-18 01:06:122540 UpdateBackgroundVideoOptimizationState();
avayvod65fad272017-02-24 01:00:482541
avayvod2135a642017-01-13 00:17:142542 if (paused_when_hidden_) {
2543 paused_when_hidden_ = false;
2544 OnPlay(); // Calls UpdatePlayState() so return afterwards.
2545 return;
2546 }
2547
sandersd50a635e2016-04-04 22:50:092548 UpdatePlayState();
sandersd1e49fb62015-12-12 01:18:062549}
2550
sandersd35d2c3f2017-01-14 02:04:422551void WebMediaPlayerImpl::OnIdleTimeout() {
dalecurtis0431cbf2016-03-12 01:19:432552 DCHECK(main_task_runner_->BelongsToCurrentThread());
2553
Dale Curtis99a9b482018-02-01 02:23:282554 // This should never be called when stale state testing overrides are used.
2555 DCHECK(!stale_state_override_for_testing_.has_value());
2556
sandersd35d2c3f2017-01-14 02:04:422557 // If we are attempting preroll, clear the stale flag.
2558 if (IsPrerollAttemptNeeded()) {
tguilbert1bb1c782017-01-23 21:15:112559 delegate_->ClearStaleFlag(delegate_id_);
sandersd35d2c3f2017-01-14 02:04:422560 return;
watkd026f792016-11-05 00:28:512561 }
sandersd50a635e2016-04-04 22:50:092562
sandersd35d2c3f2017-01-14 02:04:422563 UpdatePlayState();
dalecurtis0431cbf2016-03-12 01:19:432564}
2565
dalecurtisbb3eaac2016-01-27 21:10:252566void WebMediaPlayerImpl::OnPlay() {
Mounir Lamouri703106e2018-05-30 14:31:092567 client_->RequestPlay();
dalecurtisbb3eaac2016-01-27 21:10:252568}
2569
2570void WebMediaPlayerImpl::OnPause() {
Mounir Lamouri703106e2018-05-30 14:31:092571 client_->RequestPause();
dalecurtisbb3eaac2016-01-27 21:10:252572}
2573
François Beaufortb4fe7c62019-02-27 08:19:442574void WebMediaPlayerImpl::OnMuted(bool muted) {
2575 client_->RequestMuted(muted);
2576}
2577
Alec Douglas316cce42017-10-31 13:28:082578void WebMediaPlayerImpl::OnSeekForward(double seconds) {
2579 DCHECK_GE(seconds, 0) << "Attempted to seek by a negative number of seconds";
2580 client_->RequestSeek(CurrentTime() + seconds);
2581}
2582
2583void WebMediaPlayerImpl::OnSeekBackward(double seconds) {
2584 DCHECK_GE(seconds, 0) << "Attempted to seek by a negative number of seconds";
2585 client_->RequestSeek(CurrentTime() - seconds);
2586}
2587
Jazz Xuf8564dc2020-01-24 14:26:022588void WebMediaPlayerImpl::OnEnterPictureInPicture() {
2589 client_->RequestEnterPictureInPicture();
2590}
2591
2592void WebMediaPlayerImpl::OnExitPictureInPicture() {
2593 client_->RequestExitPictureInPicture();
2594}
2595
Noah Rose Ledesma7f853ba2020-08-05 00:41:212596void WebMediaPlayerImpl::OnSetAudioSink(const std::string& sink_id) {
2597 SetSinkId(WebString::FromASCII(sink_id),
2598 base::DoNothing::Once<base::Optional<blink::WebSetSinkIdError>>());
2599}
2600
dalecurtisbb3eaac2016-01-27 21:10:252601void WebMediaPlayerImpl::OnVolumeMultiplierUpdate(double multiplier) {
2602 volume_multiplier_ = multiplier;
Blink Reformat1c4d759e2017-04-09 16:34:542603 SetVolume(volume_);
dalecurtisbb3eaac2016-01-27 21:10:252604}
2605
zqzhang8ac49002017-03-16 21:51:352606void WebMediaPlayerImpl::OnBecamePersistentVideo(bool value) {
Blink Reformat1c4d759e2017-04-09 16:34:542607 client_->OnBecamePersistentVideo(value);
[email protected]6490e2d12019-01-14 19:18:372608 overlay_info_.is_persistent_video = value;
2609 MaybeSendOverlayInfoToDecoder();
zqzhang8ac49002017-03-16 21:51:352610}
2611
[email protected]201ce4ba2020-01-09 22:19:092612void WebMediaPlayerImpl::OnPowerExperimentState(bool state) {
2613 if (power_status_helper_)
2614 power_status_helper_->UpdatePowerExperimentState(state);
2615}
2616
watkdee516f2016-02-18 02:22:192617void WebMediaPlayerImpl::ScheduleRestart() {
sandersd50a635e2016-04-04 22:50:092618 // TODO(watk): All restart logic should be moved into PipelineController.
Antonio Gomes81b56552019-05-15 22:15:282619 if (pipeline_controller_->IsPipelineRunning() &&
2620 !pipeline_controller_->IsPipelineSuspended()) {
watkdee516f2016-02-18 02:22:192621 pending_suspend_resume_cycle_ = true;
sandersd50a635e2016-04-04 22:50:092622 UpdatePlayState();
watkdee516f2016-02-18 02:22:192623 }
2624}
2625
Blink Reformat1c4d759e2017-04-09 16:34:542626void WebMediaPlayerImpl::RequestRemotePlaybackDisabled(bool disabled) {
miu77f914c2016-11-19 23:56:182627 if (observer_)
2628 observer_->OnRemotePlaybackDisabled(disabled);
2629}
2630
Thomas Guilbert7350fdad2019-01-29 23:09:242631#if defined(OS_ANDROID)
Thomas Guilbertb341bae02018-05-09 00:02:132632void WebMediaPlayerImpl::FlingingStarted() {
2633 DCHECK(main_task_runner_->BelongsToCurrentThread());
2634 DCHECK(!disable_pipeline_auto_suspend_);
2635 disable_pipeline_auto_suspend_ = true;
2636
Thomas Guilbert29ae1a902018-10-20 01:53:382637 is_flinging_ = true;
2638
Thomas Guilbertb341bae02018-05-09 00:02:132639 // Capabilities reporting should only be performed for local playbacks.
2640 video_decode_stats_reporter_.reset();
2641
2642 // Requests to restart media pipeline. A flinging renderer will be created via
2643 // the |renderer_factory_selector_|.
2644 ScheduleRestart();
2645}
2646
2647void WebMediaPlayerImpl::FlingingStopped() {
2648 DCHECK(main_task_runner_->BelongsToCurrentThread());
2649 DCHECK(disable_pipeline_auto_suspend_);
2650 disable_pipeline_auto_suspend_ = false;
2651
Thomas Guilbert29ae1a902018-10-20 01:53:382652 is_flinging_ = false;
2653
Thomas Guilbertb341bae02018-05-09 00:02:132654 CreateVideoDecodeStatsReporter();
2655
2656 ScheduleRestart();
2657}
Thomas Guilbert901808982019-07-03 20:38:182658
2659void WebMediaPlayerImpl::OnRemotePlayStateChange(MediaStatus::State state) {
2660 DCHECK(is_flinging_);
Thomas Guilbertf6d0702e2019-09-12 23:40:142661 DCHECK(main_task_runner_->BelongsToCurrentThread());
Thomas Guilbert901808982019-07-03 20:38:182662
2663 if (state == MediaStatus::State::PLAYING && Paused()) {
2664 DVLOG(1) << __func__ << " requesting PLAY.";
2665 client_->RequestPlay();
2666 } else if (state == MediaStatus::State::PAUSED && !Paused()) {
2667 DVLOG(1) << __func__ << " requesting PAUSE.";
2668 client_->RequestPause();
2669 }
2670}
Thomas Guilbert7350fdad2019-01-29 23:09:242671#endif // defined(OS_ANDROID)
hubbee4027f92016-05-19 05:18:132672
Blink Reformat1c4d759e2017-04-09 16:34:542673void WebMediaPlayerImpl::SetPoster(const blink::WebURL& poster) {
Dan Sanderscd8981c2017-08-31 22:37:022674 has_poster_ = !poster.IsEmpty();
Dan Sanderscd8981c2017-08-31 22:37:022675}
xjzc102fd82017-01-04 20:13:532676
[email protected]fee8a902014-06-03 13:43:362677void WebMediaPlayerImpl::DataSourceInitialized(bool success) {
pkastingf5279482016-07-27 02:18:202678 DVLOG(1) << __func__;
acolwellb4034942014-08-28 15:42:432679 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]a9415292012-01-19 19:55:202680
Thomas Guilberta8a6e922019-02-01 00:02:472681 if (observer_ && mb_data_source_)
Dale Curtis4841c712018-12-13 18:14:052682 observer_->OnDataSourceInitialized(mb_data_source_->GetUrlAfterRedirects());
Anton Vayvod09fa66e2017-07-20 23:02:122683
[email protected]d250190da3b2012-07-23 22:57:302684 if (!success) {
Blink Reformat1c4d759e2017-04-09 16:34:542685 SetNetworkState(WebMediaPlayer::kNetworkStateFormatError);
Dale Curtis74612b72017-12-14 20:56:192686 media_metrics_provider_->OnError(PIPELINE_ERROR_NETWORK);
sandersd50a635e2016-04-04 22:50:092687
2688 // Not really necessary, since the pipeline was never started, but it at
2689 // least this makes sure that the error handling code is in sync.
2690 UpdatePlayState();
2691
[email protected]a9415292012-01-19 19:55:202692 return;
2693 }
2694
hubbee2cc88c092017-07-14 23:10:412695 // No point in preloading data as we'll probably just throw it away anyways.
Dale Curtis4841c712018-12-13 18:14:052696 if (IsStreaming() && preload_ > MultibufferDataSource::METADATA &&
2697 mb_data_source_) {
2698 mb_data_source_->SetPreload(MultibufferDataSource::METADATA);
hubbee2cc88c092017-07-14 23:10:412699 }
2700
[email protected]ef8394c2013-08-21 20:26:302701 StartPipeline();
[email protected]a9415292012-01-19 19:55:202702}
2703
Will Cassella8b18bcf2020-09-22 06:17:132704void WebMediaPlayerImpl::OnDataSourceRedirected() {
2705 DVLOG(1) << __func__;
2706 DCHECK(main_task_runner_->BelongsToCurrentThread());
2707 DCHECK(mb_data_source_);
2708
2709 if (WouldTaintOrigin()) {
2710 audio_source_provider_->TaintOrigin();
2711 }
2712}
2713
[email protected]122f40252012-06-12 05:01:562714void WebMediaPlayerImpl::NotifyDownloading(bool is_downloading) {
hubbeb2d3efd2017-05-05 23:26:382715 DVLOG(1) << __func__ << "(" << is_downloading << ")";
Blink Reformat1c4d759e2017-04-09 16:34:542716 if (!is_downloading && network_state_ == WebMediaPlayer::kNetworkStateLoading)
2717 SetNetworkState(WebMediaPlayer::kNetworkStateIdle);
2718 else if (is_downloading &&
2719 network_state_ == WebMediaPlayer::kNetworkStateIdle)
2720 SetNetworkState(WebMediaPlayer::kNetworkStateLoading);
hubbeb2d3efd2017-05-05 23:26:382721 if (ready_state_ == ReadyState::kReadyStateHaveFutureData && !is_downloading)
2722 SetReadyState(WebMediaPlayer::kReadyStateHaveEnoughData);
[email protected]122f40252012-06-12 05:01:562723}
2724
liberato2ff93ad2017-05-17 07:28:242725void WebMediaPlayerImpl::OnOverlayRoutingToken(
2726 const base::UnguessableToken& token) {
2727 DCHECK(overlay_mode_ == OverlayMode::kUseAndroidOverlay);
liberatofe8f9692017-06-08 19:17:362728 // TODO(liberato): |token| should already be a RoutingToken.
2729 overlay_routing_token_is_pending_ = false;
2730 overlay_routing_token_ = OverlayInfo::RoutingToken(token);
liberato2ff93ad2017-05-17 07:28:242731 MaybeSendOverlayInfoToDecoder();
2732}
2733
2734void WebMediaPlayerImpl::OnOverlayInfoRequested(
dalecurtis4b632fce22016-11-10 00:52:172735 bool decoder_requires_restart_for_overlay,
Jose Lopes689012d2020-02-27 09:04:492736 ProvideOverlayInfoCB provide_overlay_info_cb) {
watkdee516f2016-02-18 02:22:192737 DCHECK(main_task_runner_->BelongsToCurrentThread());
watkdee516f2016-02-18 02:22:192738
Chris Watkins557f84d2017-09-16 02:31:462739 // If we get a non-null cb, a decoder is initializing and requires overlay
2740 // info. If we get a null cb, a previously initialized decoder is
2741 // unregistering for overlay info updates.
Dale Curtise25163812018-09-21 22:13:392742 if (!provide_overlay_info_cb) {
tsunghungee562e92016-07-20 18:03:312743 decoder_requires_restart_for_overlay_ = false;
liberato2ff93ad2017-05-17 07:28:242744 provide_overlay_info_cb_.Reset();
watkdee516f2016-02-18 02:22:192745 return;
2746 }
2747
dalecurtis4b632fce22016-11-10 00:52:172748 // If |decoder_requires_restart_for_overlay| is true, we must restart the
2749 // pipeline for fullscreen transitions. The decoder is unable to switch
2750 // surfaces otherwise. If false, we simply need to tell the decoder about the
2751 // new surface and it will handle things seamlessly.
Chris Watkins557f84d2017-09-16 02:31:462752 // For encrypted video we pretend that the decoder doesn't require a restart
2753 // because it needs an overlay all the time anyway. We'll switch into
2754 // |always_enable_overlays_| mode below.
2755 decoder_requires_restart_for_overlay_ =
2756 (overlay_mode_ == OverlayMode::kUseAndroidOverlay && is_encrypted_)
2757 ? false
2758 : decoder_requires_restart_for_overlay;
Jose Lopes689012d2020-02-27 09:04:492759 provide_overlay_info_cb_ = std::move(provide_overlay_info_cb);
dalecurtis4b632fce22016-11-10 00:52:172760
Chris Watkins557f84d2017-09-16 02:31:462761 // If the decoder doesn't require restarts for surface transitions, and we're
2762 // using AndroidOverlay mode, we can always enable the overlay and the decoder
2763 // can choose whether or not to use it. Otherwise, we'll restart the decoder
2764 // and enable the overlay on fullscreen transitions.
[email protected]77568482017-06-21 21:16:522765 if (overlay_mode_ == OverlayMode::kUseAndroidOverlay &&
Chris Watkins557f84d2017-09-16 02:31:462766 !decoder_requires_restart_for_overlay_) {
[email protected]c8d574722017-08-30 20:53:432767 always_enable_overlays_ = true;
[email protected]77568482017-06-21 21:16:522768 if (!overlay_enabled_)
2769 EnableOverlay();
2770 }
2771
Chris Watkins557f84d2017-09-16 02:31:462772 // Send the overlay info if we already have it. If not, it will be sent later.
liberato2ff93ad2017-05-17 07:28:242773 MaybeSendOverlayInfoToDecoder();
2774}
2775
2776void WebMediaPlayerImpl::MaybeSendOverlayInfoToDecoder() {
2777 // If the decoder didn't request overlay info, then don't send it.
2778 if (!provide_overlay_info_cb_)
dalecurtis4b632fce22016-11-10 00:52:172779 return;
2780
liberato2ff93ad2017-05-17 07:28:242781 // We should send the overlay info as long as we know it. This includes the
2782 // case where |!overlay_enabled_|, since we want to tell the decoder to avoid
2783 // using overlays. Assuming that the decoder has requested info, the only
2784 // case in which we don't want to send something is if we've requested the
2785 // info but not received it yet. Then, we should wait until we do.
liberatofe8f9692017-06-08 19:17:362786 //
2787 // Initialization requires this; AVDA should start with enough info to make an
2788 // overlay, so that (pre-M) the initial codec is created with the right output
2789 // surface; it can't switch later.
[email protected]f7df5b342018-07-13 20:22:132790 if (overlay_mode_ == OverlayMode::kUseAndroidOverlay) {
liberatofe8f9692017-06-08 19:17:362791 if (overlay_routing_token_is_pending_)
liberato2ff93ad2017-05-17 07:28:242792 return;
liberatofe8f9692017-06-08 19:17:362793
2794 overlay_info_.routing_token = overlay_routing_token_;
liberato2ff93ad2017-05-17 07:28:242795 }
2796
liberato2ff93ad2017-05-17 07:28:242797 // If restart is required, the callback is one-shot only.
2798 if (decoder_requires_restart_for_overlay_) {
Dale Curtise25163812018-09-21 22:13:392799 std::move(provide_overlay_info_cb_).Run(overlay_info_);
liberato2ff93ad2017-05-17 07:28:242800 } else {
liberatofe8f9692017-06-08 19:17:362801 provide_overlay_info_cb_.Run(overlay_info_);
liberato2ff93ad2017-05-17 07:28:242802 }
watkdee516f2016-02-18 02:22:192803}
2804
Xiaohan Wangcd4df0a2019-12-04 20:59:242805std::unique_ptr<Renderer> WebMediaPlayerImpl::CreateRenderer(
Xiaohan Wang48aaa192019-12-05 01:29:002806 base::Optional<RendererFactoryType> factory_type) {
dcheng37b415b92017-01-27 20:17:432807 DCHECK(main_task_runner_->BelongsToCurrentThread());
2808
[email protected]c8d574722017-08-30 20:53:432809 // Make sure that overlays are enabled if they're always allowed.
2810 if (always_enable_overlays_)
tsunghungee562e92016-07-20 18:03:312811 EnableOverlay();
2812
liberato2ff93ad2017-05-17 07:28:242813 RequestOverlayInfoCB request_overlay_info_cb;
watkdee516f2016-02-18 02:22:192814#if defined(OS_ANDROID)
Jose Lopes689012d2020-02-27 09:04:492815 request_overlay_info_cb = BindToCurrentLoop(base::BindRepeating(
2816 &WebMediaPlayerImpl::OnOverlayInfoRequested, weak_this_));
watkdee516f2016-02-18 02:22:192817#endif
Xiaohan Wangcd4df0a2019-12-04 20:59:242818
2819 if (factory_type) {
Xiaohan Wang48aaa192019-12-05 01:29:002820 DVLOG(1) << __func__
2821 << ": factory_type=" << static_cast<int>(factory_type.value());
Xiaohan Wangcd4df0a2019-12-04 20:59:242822 renderer_factory_selector_->SetBaseFactoryType(factory_type.value());
2823 }
2824
Dan Sanders6edfd782019-08-13 00:13:182825 reported_renderer_type_ = renderer_factory_selector_->GetCurrentFactoryType();
Xiaohan Wang73b433f2019-11-05 20:13:012826
Xiaohan Wang2480de72019-11-15 01:44:472827 return renderer_factory_selector_->GetCurrentFactory()->CreateRenderer(
2828 media_task_runner_, worker_task_runner_, audio_source_provider_.get(),
Jose Lopes689012d2020-02-27 09:04:492829 compositor_.get(), std::move(request_overlay_info_cb),
2830 client_->TargetColorSpace());
sandersd1e49fb62015-12-12 01:18:062831}
2832
[email protected]ef8394c2013-08-21 20:26:302833void WebMediaPlayerImpl::StartPipeline() {
acolwellb4034942014-08-28 15:42:432834 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]ddbc6ff2013-04-19 15:28:332835
xhwange8c4181a2014-12-06 08:10:012836 Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb =
Jose Lopes066452e2020-02-19 20:45:342837 BindToCurrentLoop(base::BindRepeating(
Antonio Gomes142f32a2019-05-15 23:32:562838 &WebMediaPlayerImpl::OnEncryptedMediaInitData, weak_this_));
[email protected]2b57e2e2014-05-09 11:07:252839
Dale Curtis3899090ea2018-01-12 00:10:352840 vfc_task_runner_->PostTask(
2841 FROM_HERE,
2842 base::BindOnce(&VideoFrameCompositor::SetOnNewProcessedFrameCallback,
2843 base::Unretained(compositor_.get()),
2844 BindToCurrentLoop(base::BindOnce(
Antonio Gomes142f32a2019-05-15 23:32:562845 &WebMediaPlayerImpl::OnFirstFrame, weak_this_))));
Dale Curtis3899090ea2018-01-12 00:10:352846
Thomas Guilbert55b48d92019-03-25 23:16:542847#if defined(OS_ANDROID)
Dale Curtis65c151a2019-02-26 02:09:322848 if (demuxer_found_hls_ ||
2849 renderer_factory_selector_->GetCurrentFactory()
2850 ->GetRequiredMediaResourceType() == MediaResource::Type::URL) {
Thomas Guilbert8a42fcf2019-03-01 20:21:452851 // MediaPlayerRendererClientFactory is the only factory that a uses
2852 // MediaResource::Type::URL for the moment.
tguilbert75e2bf62017-04-26 20:13:122853 using_media_player_renderer_ = true;
2854
Chris Cunninghamd9df58e2017-08-29 00:04:232855 // MediaPlayerRenderer does not provide pipeline stats, so nuke capabilities
2856 // reporter.
2857 video_decode_stats_reporter_.reset();
2858
Eugene Zemtsov77d56262020-03-06 05:52:032859 SetDemuxer(std::make_unique<MediaUrlDemuxer>(
Maks Orlovichab27e242020-01-07 18:10:392860 media_task_runner_, loaded_url_,
2861 frame_->GetDocument().SiteForCookies().RepresentativeUrl(),
Christian Dullweberacff9bd32019-08-20 09:44:372862 frame_->GetDocument().TopFrameOrigin(),
Eugene Zemtsov77d56262020-03-06 05:52:032863 allow_media_player_renderer_credentials_, demuxer_found_hls_));
Antonio Gomes81b56552019-05-15 22:15:282864 pipeline_controller_->Start(Pipeline::StartType::kNormal, demuxer_.get(),
2865 this, false, false);
tguilbert25a4d112016-10-13 21:56:512866 return;
2867 }
Thomas Guilbert55b48d92019-03-25 23:16:542868#endif // defined(OS_ANDROID)
tguilbert25a4d112016-10-13 21:56:512869
[email protected]ddbc6ff2013-04-19 15:28:332870 // Figure out which demuxer to use.
David Dorwin30993312020-05-07 22:39:092871 if (demuxer_override_) {
2872 DCHECK(!chunk_demuxer_);
2873
2874 SetDemuxer(std::move(demuxer_override_));
2875 // TODO(https://crbug.com/1076267): Should everything else after this block
2876 // run in the demuxer override case?
2877 } else if (load_type_ != kLoadTypeMediaSource) {
[email protected]ddbc6ff2013-04-19 15:28:332878 DCHECK(!chunk_demuxer_);
[email protected]f5443ef72013-04-22 04:03:382879 DCHECK(data_source_);
2880
Dale Curtisbcf523b2018-01-17 02:59:012881#if BUILDFLAG(ENABLE_FFMPEG)
servolk81e01e02016-03-05 03:29:152882 Demuxer::MediaTracksUpdatedCB media_tracks_updated_cb =
Jose Lopes239a0912020-02-20 10:59:332883 BindToCurrentLoop(base::BindRepeating(
Antonio Gomes142f32a2019-05-15 23:32:562884 &WebMediaPlayerImpl::OnFFmpegMediaTracksUpdated, weak_this_));
servolk81e01e02016-03-05 03:29:152885
Eugene Zemtsov77d56262020-03-06 05:52:032886 SetDemuxer(std::make_unique<FFmpegDemuxer>(
dalecurtis9cddc0b2017-04-19 21:23:382887 media_task_runner_, data_source_.get(), encrypted_media_init_data_cb,
Eugene Zemtsov77d56262020-03-06 05:52:032888 media_tracks_updated_cb, media_log_.get(), IsLocalFile(loaded_url_)));
j.isorcef6778e652015-11-16 17:14:252889#else
alokp967c902452016-05-06 05:21:372890 OnError(PipelineStatus::DEMUXER_ERROR_COULD_NOT_OPEN);
j.isorcef6778e652015-11-16 17:14:252891 return;
2892#endif
[email protected]ddbc6ff2013-04-19 15:28:332893 } else {
[email protected]f5443ef72013-04-22 04:03:382894 DCHECK(!chunk_demuxer_);
2895 DCHECK(!data_source_);
2896
Antonio Gomes142f32a2019-05-15 23:32:562897 chunk_demuxer_ =
mark a. foltz58dd2d12020-08-06 21:43:242898 new ChunkDemuxer(BindToCurrentLoop(base::BindOnce(
Antonio Gomes142f32a2019-05-15 23:32:562899 &WebMediaPlayerImpl::OnDemuxerOpened, weak_this_)),
mark a. foltz58dd2d12020-08-06 21:43:242900 BindToCurrentLoop(base::BindRepeating(
Antonio Gomes142f32a2019-05-15 23:32:562901 &WebMediaPlayerImpl::OnProgress, weak_this_)),
2902 encrypted_media_init_data_cb, media_log_.get());
Eugene Zemtsov77d56262020-03-06 05:52:032903 SetDemuxer(std::unique_ptr<Demuxer>(chunk_demuxer_));
servolkf94b4602017-01-31 16:44:272904
2905 if (base::FeatureList::IsEnabled(kMemoryPressureBasedSourceBufferGC)) {
2906 // base::Unretained is safe because |this| owns memory_pressure_listener_.
2907 memory_pressure_listener_ =
Sebastien Marchand17797d92020-06-23 20:27:592908 std::make_unique<base::MemoryPressureListener>(
2909 FROM_HERE, base::Bind(&WebMediaPlayerImpl::OnMemoryPressure,
2910 base::Unretained(this)));
servolkf94b4602017-01-31 16:44:272911 }
[email protected]ddbc6ff2013-04-19 15:28:332912 }
2913
Dale Curtis2dc6089a2018-03-26 16:47:582914 // If possible attempt to avoid decoder spool up until playback starts.
2915 Pipeline::StartType start_type = Pipeline::StartType::kNormal;
Dale Curtis7c63f2e22018-09-19 21:06:272916 if (!chunk_demuxer_ && preload_ == MultibufferDataSource::METADATA &&
Dale Curtisd6ccd1992019-04-29 19:03:462917 !client_->CouldPlayIfEnoughData() && !IsStreaming()) {
Dale Curtis7c63f2e22018-09-19 21:06:272918 start_type =
2919 (has_poster_ || base::FeatureList::IsEnabled(kPreloadMetadataLazyLoad))
2920 ? Pipeline::StartType::kSuspendAfterMetadata
2921 : Pipeline::StartType::kSuspendAfterMetadataForAudioOnly;
Dale Curtis2dc6089a2018-03-26 16:47:582922 attempting_suspended_start_ = true;
2923 }
2924
Dale Curtis6e2151a2020-05-11 19:45:062925 // TODO(sandersd): FileSystem objects may also be non-static, but due to our
2926 // caching layer such situations are broken already. http://crbug.com/593159
2927 const bool is_static = !chunk_demuxer_;
2928
[email protected]f5443ef72013-04-22 04:03:382929 // ... and we're ready to go!
sandersd1e49fb62015-12-12 01:18:062930 // TODO(sandersd): On Android, defer Start() if the tab is not visible.
sandersdb8eb5f1d2016-11-19 04:04:022931 seeking_ = true;
Dale Curtis6e2151a2020-05-11 19:45:062932 pipeline_controller_->Start(start_type, demuxer_.get(), this, IsStreaming(),
Antonio Gomes81b56552019-05-15 22:15:282933 is_static);
[email protected]f5443ef72013-04-22 04:03:382934}
2935
2936void WebMediaPlayerImpl::SetNetworkState(WebMediaPlayer::NetworkState state) {
pkastingf5279482016-07-27 02:18:202937 DVLOG(1) << __func__ << "(" << state << ")";
acolwellb4034942014-08-28 15:42:432938 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]f5443ef72013-04-22 04:03:382939 network_state_ = state;
2940 // Always notify to ensure client has the latest value.
Blink Reformat1c4d759e2017-04-09 16:34:542941 client_->NetworkStateChanged();
[email protected]f5443ef72013-04-22 04:03:382942}
2943
2944void WebMediaPlayerImpl::SetReadyState(WebMediaPlayer::ReadyState state) {
pkastingf5279482016-07-27 02:18:202945 DVLOG(1) << __func__ << "(" << state << ")";
acolwellb4034942014-08-28 15:42:432946 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]f5443ef72013-04-22 04:03:382947
Blink Reformat1c4d759e2017-04-09 16:34:542948 if (state == WebMediaPlayer::kReadyStateHaveEnoughData && data_source_ &&
Dale Curtis4841c712018-12-13 18:14:052949 data_source_->AssumeFullyBuffered() &&
2950 network_state_ == WebMediaPlayer::kNetworkStateLoading) {
Blink Reformat1c4d759e2017-04-09 16:34:542951 SetNetworkState(WebMediaPlayer::kNetworkStateLoaded);
Dale Curtis4841c712018-12-13 18:14:052952 }
[email protected]f5443ef72013-04-22 04:03:382953
2954 ready_state_ = state;
sandersd50a635e2016-04-04 22:50:092955 highest_ready_state_ = std::max(highest_ready_state_, ready_state_);
2956
[email protected]f5443ef72013-04-22 04:03:382957 // Always notify to ensure client has the latest value.
Blink Reformat1c4d759e2017-04-09 16:34:542958 client_->ReadyStateChanged();
[email protected]f5443ef72013-04-22 04:03:382959}
2960
Antonio Gomesa38bb7cb2019-05-31 03:02:522961scoped_refptr<blink::WebAudioSourceProviderImpl>
2962WebMediaPlayerImpl::GetAudioSourceProvider() {
2963 return audio_source_provider_;
[email protected]f5443ef72013-04-22 04:03:382964}
2965
Jiajia Qin82acdc02017-07-31 09:55:142966scoped_refptr<VideoFrame> WebMediaPlayerImpl::GetCurrentFrameFromCompositor()
2967 const {
xhwang213e50c2016-10-10 23:56:312968 DCHECK(main_task_runner_->BelongsToCurrentThread());
[email protected]dd061e12014-05-06 19:21:222969 TRACE_EVENT0("media", "WebMediaPlayerImpl::GetCurrentFrameFromCompositor");
xhwang213e50c2016-10-10 23:56:312970
Thomas Guilbertd85407412017-11-08 05:03:462971 // Can be null.
2972 scoped_refptr<VideoFrame> video_frame =
2973 compositor_->GetCurrentFrameOnAnyThread();
[email protected]dd061e12014-05-06 19:21:222974
Thomas Guilbertd85407412017-11-08 05:03:462975 // base::Unretained is safe here because |compositor_| is destroyed on
2976 // |vfc_task_runner_|. The destruction is queued from |this|' destructor,
2977 // which also runs on |main_task_runner_|, which makes it impossible for
2978 // UpdateCurrentFrameIfStale() to be queued after |compositor_|'s dtor.
CJ DiMeglio2302d202017-08-31 08:38:042979 vfc_task_runner_->PostTask(
Dale Curtisee9be30012018-09-21 22:21:512980 FROM_HERE,
2981 base::BindOnce(&VideoFrameCompositor::UpdateCurrentFrameIfStale,
Thomas Guilbertfc241402020-09-30 21:38:322982 base::Unretained(compositor_.get()),
2983 VideoFrameCompositor::UpdateType::kNormal));
kainino36eeff82017-03-30 00:55:302984
[email protected]dd061e12014-05-06 19:21:222985 return video_frame;
2986}
2987
sandersd50a635e2016-04-04 22:50:092988void WebMediaPlayerImpl::UpdatePlayState() {
xhwang213e50c2016-10-10 23:56:312989 DCHECK(main_task_runner_->BelongsToCurrentThread());
hubbee2cc88c092017-07-14 23:10:412990 bool can_auto_suspend = !disable_pipeline_auto_suspend_;
2991 // For streaming videos, we only allow suspending at the very beginning of the
2992 // video, and only if we know the length of the video. (If we don't know
2993 // the length, it might be a dynamically generated video, and suspending
2994 // will not work at all.)
2995 if (IsStreaming()) {
2996 bool at_beginning =
2997 ready_state_ == WebMediaPlayer::kReadyStateHaveNothing ||
2998 CurrentTime() == 0.0;
2999 if (!at_beginning || GetPipelineMediaDuration() == kInfiniteDuration)
3000 can_auto_suspend = false;
3001 }
xhwang213e50c2016-10-10 23:56:313002
Antonio Gomes81b56552019-05-15 22:15:283003 bool is_suspended = pipeline_controller_->IsSuspended();
Sergey Volk8b09c2c52018-12-12 23:20:403004 bool is_backgrounded = IsBackgroundSuspendEnabled(this) && IsHidden();
sandersdaaff1a652016-11-17 01:47:253005 PlayState state = UpdatePlayState_ComputePlayState(
Thomas Guilbert7350fdad2019-01-29 23:09:243006 is_flinging_, can_auto_suspend, is_suspended, is_backgrounded);
sandersd35d2c3f2017-01-14 02:04:423007 SetDelegateState(state.delegate_state, state.is_idle);
sandersd50a635e2016-04-04 22:50:093008 SetMemoryReportingState(state.is_memory_reporting_enabled);
3009 SetSuspendState(state.is_suspended || pending_suspend_resume_cycle_);
[email protected]dc996312019-12-19 19:26:383010 if (power_status_helper_) {
3011 // Make sure that we're in something like steady-state before recording.
3012 power_status_helper_->SetIsPlaying(
3013 !paused_ && !seeking_ && !IsHidden() && !state.is_suspended &&
3014 ready_state_ == kReadyStateHaveEnoughData);
3015 }
[email protected]91379332020-01-26 03:58:203016 UpdateSmoothnessHelper();
sandersd50a635e2016-04-04 22:50:093017}
dalecurtis5bbc487e2016-02-27 04:15:053018
Wojciech Dzierżanowskide9b1272020-06-04 08:48:523019void WebMediaPlayerImpl::OnTimeUpdate() {
Becca Hughes7a85bf22019-07-24 16:16:583020 // When seeking the current time can go beyond the duration so we should
3021 // cap the current time at the duration.
3022 base::TimeDelta duration = GetPipelineMediaDuration();
3023 base::TimeDelta current_time = GetCurrentTimeInternal();
3024 if (current_time > duration)
3025 current_time = duration;
3026
Wojciech Dzierżanowski68f4a522020-04-27 09:48:123027 const double effective_playback_rate =
3028 paused_ || ready_state_ < kReadyStateHaveFutureData ? 0.0
3029 : playback_rate_;
3030
3031 media_session::MediaPosition new_position(effective_playback_rate, duration,
3032 current_time);
Becca Hughes7a85bf22019-07-24 16:16:583033
Wojciech Dzierżanowskide9b1272020-06-04 08:48:523034 if (!MediaPositionNeedsUpdate(media_position_state_, new_position))
Becca Hughes7a85bf22019-07-24 16:16:583035 return;
3036
3037 DVLOG(2) << __func__ << "(" << new_position.ToString() << ")";
3038 media_position_state_ = new_position;
3039 delegate_->DidPlayerMediaPositionStateChange(delegate_id_,
3040 media_position_state_);
3041}
3042
sandersd35d2c3f2017-01-14 02:04:423043void WebMediaPlayerImpl::SetDelegateState(DelegateState new_state,
3044 bool is_idle) {
tguilbert1bb1c782017-01-23 21:15:113045 DCHECK(delegate_);
Dale Curtis779ed842018-03-10 06:20:133046 DVLOG(2) << __func__ << "(" << static_cast<int>(new_state) << ", " << is_idle
3047 << ")";
dalecurtis5bbc487e2016-02-27 04:15:053048
sandersd35d2c3f2017-01-14 02:04:423049 // Prevent duplicate delegate calls.
3050 // TODO(sandersd): Move this deduplication into the delegate itself.
Wojciech Dzierżanowski69bb21842020-07-22 14:21:173051 if (delegate_state_ == new_state)
sandersd35d2c3f2017-01-14 02:04:423052 return;
sandersd50a635e2016-04-04 22:50:093053 delegate_state_ = new_state;
sandersd50a635e2016-04-04 22:50:093054
sandersd35d2c3f2017-01-14 02:04:423055 switch (new_state) {
sandersd50a635e2016-04-04 22:50:093056 case DelegateState::GONE:
3057 delegate_->PlayerGone(delegate_id_);
3058 break;
mlamouri910111362016-11-04 11:28:243059 case DelegateState::PLAYING: {
peconn257951522017-06-09 18:24:593060 if (HasVideo())
3061 delegate_->DidPlayerSizeChange(delegate_id_, NaturalSize());
Wojciech Dzierżanowski69bb21842020-07-22 14:21:173062 delegate_->DidPlay(delegate_id_);
sandersd50a635e2016-04-04 22:50:093063 break;
mlamouri910111362016-11-04 11:28:243064 }
sandersd50a635e2016-04-04 22:50:093065 case DelegateState::PAUSED:
Wojciech Dzierżanowski69bb21842020-07-22 14:21:173066 delegate_->DidPause(delegate_id_, ended_);
sandersd50a635e2016-04-04 22:50:093067 break;
dalecurtis0f0097a2015-12-01 17:40:473068 }
sandersd35d2c3f2017-01-14 02:04:423069
3070 delegate_->SetIdle(delegate_id_, is_idle);
dalecurtis0f0097a2015-12-01 17:40:473071}
3072
sandersd50a635e2016-04-04 22:50:093073void WebMediaPlayerImpl::SetMemoryReportingState(
3074 bool is_memory_reporting_enabled) {
3075 if (memory_usage_reporting_timer_.IsRunning() ==
3076 is_memory_reporting_enabled) {
hubbed5f36882016-01-15 22:40:373077 return;
sandersd50a635e2016-04-04 22:50:093078 }
sandersd1c0bba02016-03-04 23:14:083079
sandersd50a635e2016-04-04 22:50:093080 if (is_memory_reporting_enabled) {
3081 memory_usage_reporting_timer_.Start(FROM_HERE,
3082 base::TimeDelta::FromSeconds(2), this,
3083 &WebMediaPlayerImpl::ReportMemoryUsage);
3084 } else {
3085 memory_usage_reporting_timer_.Stop();
3086 ReportMemoryUsage();
3087 }
3088}
3089
3090void WebMediaPlayerImpl::SetSuspendState(bool is_suspended) {
xhwang213e50c2016-10-10 23:56:313091 DCHECK(main_task_runner_->BelongsToCurrentThread());
Dale Curtis779ed842018-03-10 06:20:133092 DVLOG(2) << __func__ << "(" << is_suspended << ")";
xhwang213e50c2016-10-10 23:56:313093
sandersd50a635e2016-04-04 22:50:093094 // Do not change the state after an error has occurred.
3095 // TODO(sandersd): Update PipelineController to remove the need for this.
3096 if (IsNetworkStateError(network_state_))
sandersd1c0bba02016-03-04 23:14:083097 return;
3098
jameswest451a5bb2017-01-27 03:59:393099 if (is_suspended) {
sandersd35d2c3f2017-01-14 02:04:423100 // If we were not resumed for long enough to satisfy the preroll attempt,
3101 // reset the clock.
3102 if (!preroll_attempt_pending_ && IsPrerollAttemptNeeded()) {
3103 preroll_attempt_pending_ = true;
3104 preroll_attempt_start_time_ = base::TimeTicks();
3105 }
Antonio Gomes81b56552019-05-15 22:15:283106 pipeline_controller_->Suspend();
sandersd50a635e2016-04-04 22:50:093107 } else {
sandersd35d2c3f2017-01-14 02:04:423108 // When resuming, start the preroll attempt clock.
3109 if (preroll_attempt_pending_) {
3110 preroll_attempt_pending_ = false;
3111 preroll_attempt_start_time_ = tick_clock_->NowTicks();
3112 }
Antonio Gomes81b56552019-05-15 22:15:283113 pipeline_controller_->Resume();
sandersd50a635e2016-04-04 22:50:093114 }
3115}
3116
3117WebMediaPlayerImpl::PlayState
Thomas Guilbert7350fdad2019-01-29 23:09:243118WebMediaPlayerImpl::UpdatePlayState_ComputePlayState(bool is_flinging,
xjz4e5d4bf32017-02-15 21:26:353119 bool can_auto_suspend,
dalecurtis8b8505e72016-06-10 21:59:173120 bool is_suspended,
sandersd50a635e2016-04-04 22:50:093121 bool is_backgrounded) {
3122 PlayState result;
3123
tguilbert1bb1c782017-01-23 21:15:113124 bool must_suspend = delegate_->IsFrameClosed();
Dale Curtis6438cf12018-03-29 02:34:013125 bool is_stale = delegate_->IsStale(delegate_id_);
3126
3127 if (stale_state_override_for_testing_.has_value() &&
3128 ready_state_ >= stale_state_override_for_testing_.value()) {
3129 is_stale = true;
3130 }
sandersd35d2c3f2017-01-14 02:04:423131
sandersd50a635e2016-04-04 22:50:093132 // This includes both data source (before pipeline startup) and pipeline
3133 // errors.
3134 bool has_error = IsNetworkStateError(network_state_);
3135
Dale Curtisc56158c2019-09-09 23:00:323136 // Note: Even though we get play/pause signals at kReadyStateHaveMetadata, we
3137 // must attempt to preroll until kReadyStateHaveFutureData so that the
3138 // canplaythrough event will be fired to the page (which may be waiting).
3139 bool have_future_data =
3140 highest_ready_state_ >= WebMediaPlayer::kReadyStateHaveFutureData;
sandersd50a635e2016-04-04 22:50:093141
avayvod65fad272017-02-24 01:00:483142 // Background suspend is only enabled for paused players.
3143 // In the case of players with audio the session should be kept.
3144 bool background_suspended =
Dale Curtisc56158c2019-09-09 23:00:323145 can_auto_suspend && is_backgrounded && paused_ && have_future_data;
sandersd50a635e2016-04-04 22:50:093146
Dale Curtisd71061f02019-05-21 21:33:543147 // Idle suspension is allowed prior to kReadyStateHaveMetadata since there
3148 // exist mechanisms to exit the idle state when the player is capable of
3149 // reaching the kReadyStateHaveMetadata state; see didLoadingProgress().
dalecurtiscc8baf72016-10-27 01:49:443150 //
sandersd50a635e2016-04-04 22:50:093151 // TODO(sandersd): Make the delegate suspend idle players immediately when
3152 // hidden.
Dale Curtis7c63f2e22018-09-19 21:06:273153 bool idle_suspended = can_auto_suspend && is_stale && paused_ && !seeking_ &&
3154 !overlay_enabled_ && !needs_first_frame_;
dalecurtise7120dc2016-09-03 02:54:353155
3156 // If we're already suspended, see if we can wait for user interaction. Prior
Dale Curtisd71061f02019-05-21 21:33:543157 // to kReadyStateHaveMetadata, we require |is_stale| to remain suspended.
3158 // |is_stale| will be cleared when we receive data which may take us to
3159 // kReadyStateHaveMetadata.
Dale Curtisc56158c2019-09-09 23:00:323160 bool can_stay_suspended = (is_stale || have_future_data) && is_suspended &&
Dale Curtis7c63f2e22018-09-19 21:06:273161 paused_ && !seeking_ && !needs_first_frame_;
sandersd50a635e2016-04-04 22:50:093162
3163 // Combined suspend state.
Thomas Guilbert7350fdad2019-01-29 23:09:243164 result.is_suspended = must_suspend || idle_suspended ||
avayvod65fad272017-02-24 01:00:483165 background_suspended || can_stay_suspended;
sandersd50a635e2016-04-04 22:50:093166
Thomas Guilbert7350fdad2019-01-29 23:09:243167 DVLOG(3) << __func__ << ": must_suspend=" << must_suspend
Dale Curtis779ed842018-03-10 06:20:133168 << ", idle_suspended=" << idle_suspended
3169 << ", background_suspended=" << background_suspended
3170 << ", can_stay_suspended=" << can_stay_suspended
Dale Curtisc56158c2019-09-09 23:00:323171 << ", is_stale=" << is_stale
3172 << ", have_future_data=" << have_future_data
Dale Curtis779ed842018-03-10 06:20:133173 << ", paused_=" << paused_ << ", seeking_=" << seeking_;
3174
sandersd50a635e2016-04-04 22:50:093175 // We do not treat |playback_rate_| == 0 as paused. For the media session,
3176 // being paused implies displaying a play button, which is incorrect in this
3177 // case. For memory usage reporting, we just use the same definition (but we
3178 // don't have to).
3179 //
3180 // Similarly, we don't consider |ended_| to be paused. Blink will immediately
3181 // call pause() or seek(), so |ended_| should not affect the computation.
3182 // Despite that, |ended_| does result in a separate paused state, to simplfy
3183 // the contract for SetDelegateState().
3184 //
avayvod65fad272017-02-24 01:00:483185 // |has_remote_controls| indicates if the player can be controlled outside the
3186 // page (e.g. via the notification controls or by audio focus events). Idle
sandersd50a635e2016-04-04 22:50:093187 // suspension does not destroy the media session, because we expect that the
avayvod5f34b642017-03-23 03:14:043188 // notification controls (and audio focus) remain. With some exceptions for
3189 // background videos, the player only needs to have audio to have controls
Dale Curtisd71061f02019-05-21 21:33:543190 // (requires |have_current_data|).
avayvod5f34b642017-03-23 03:14:043191 //
3192 // |alive| indicates if the player should be present (not |GONE|) to the
3193 // delegate, either paused or playing. The following must be true for the
3194 // player:
Dale Curtisd71061f02019-05-21 21:33:543195 // - |have_current_data|, since playback can't begin before that point, we
3196 // need to know whether we are paused to correctly configure the session,
3197 // and also because the tracks and duration are passed to DidPlay(),
Thomas Guilbert7350fdad2019-01-29 23:09:243198 // - |is_flinging| is false (RemotePlayback is not handled by the delegate)
avayvod5f34b642017-03-23 03:14:043199 // - |has_error| is false as player should have no errors,
3200 // - |background_suspended| is false, otherwise |has_remote_controls| must
3201 // be true.
sandersd50a635e2016-04-04 22:50:093202 //
avayvod65fad272017-02-24 01:00:483203 // TODO(sandersd): If Blink told us the paused state sooner, we could detect
3204 // if the remote controls are available sooner.
3205
3206 // Background videos with audio don't have remote controls if background
3207 // suspend is enabled and resuming background videos is not (original Android
3208 // behavior).
3209 bool backgrounded_video_has_no_remote_controls =
Sergey Volk8b09c2c52018-12-12 23:20:403210 IsBackgroundSuspendEnabled(this) && !IsResumeBackgroundVideosEnabled() &&
3211 is_backgrounded && HasVideo();
Dale Curtisd71061f02019-05-21 21:33:543212 bool have_current_data = highest_ready_state_ >= kReadyStateHaveCurrentData;
3213 bool can_play = !has_error && have_current_data;
avayvod5f34b642017-03-23 03:14:043214 bool has_remote_controls =
Blink Reformat1c4d759e2017-04-09 16:34:543215 HasAudio() && !backgrounded_video_has_no_remote_controls;
Thomas Guilbert7350fdad2019-01-29 23:09:243216 bool alive = can_play && !is_flinging && !must_suspend &&
avayvod5f34b642017-03-23 03:14:043217 (!background_suspended || has_remote_controls);
3218 if (!alive) {
Thomas Guilbert4c6feff2018-11-09 19:53:323219 // Do not mark players as idle when flinging.
sandersd50a635e2016-04-04 22:50:093220 result.delegate_state = DelegateState::GONE;
Thomas Guilbert4c6feff2018-11-09 19:53:323221 result.is_idle = delegate_->IsIdle(delegate_id_) && !is_flinging;
avayvod65fad272017-02-24 01:00:483222 } else if (paused_) {
sandersd35d2c3f2017-01-14 02:04:423223 // TODO(sandersd): Is it possible to have a suspended session, be ended,
3224 // and not be paused? If so we should be in a PLAYING state.
Wojciech Dzierżanowski69bb21842020-07-22 14:21:173225 result.delegate_state = DelegateState::PAUSED;
sandersd35d2c3f2017-01-14 02:04:423226 result.is_idle = !seeking_;
sandersd50a635e2016-04-04 22:50:093227 } else {
3228 result.delegate_state = DelegateState::PLAYING;
sandersd35d2c3f2017-01-14 02:04:423229 result.is_idle = false;
sandersd50a635e2016-04-04 22:50:093230 }
3231
dalecurtis8b8505e72016-06-10 21:59:173232 // It's not critical if some cases where memory usage can change are missed,
sandersd50a635e2016-04-04 22:50:093233 // since media memory changes are usually gradual.
Thomas Guilbert7350fdad2019-01-29 23:09:243234 result.is_memory_reporting_enabled = !has_error && can_play && !is_flinging &&
3235 !result.is_suspended &&
3236 (!paused_ || seeking_);
sandersd50a635e2016-04-04 22:50:093237
3238 return result;
dalecurtis0f0097a2015-12-01 17:40:473239}
3240
Eugene Zemtsov77d56262020-03-06 05:52:033241void WebMediaPlayerImpl::SetDemuxer(std::unique_ptr<Demuxer> demuxer) {
3242 DCHECK(main_task_runner_->BelongsToCurrentThread());
3243 DCHECK(!demuxer_);
3244 DCHECK(!media_thread_mem_dumper_);
3245 DCHECK(demuxer);
3246
3247 demuxer_ = std::move(demuxer);
3248
3249 // base::Unretained() is safe here. |demuxer_| is destroyed on the main
3250 // thread, but before doing it ~WebMediaPlayerImpl() posts a media thread task
3251 // that deletes media_thread_mem_dumper_ and waits for it to finish.
3252 media_thread_mem_dumper_ = std::make_unique<MemoryDumpProviderProxy>(
3253 "WebMediaPlayer_MediaThread", media_task_runner_,
3254 base::BindRepeating(&WebMediaPlayerImpl::OnMediaThreadMemoryDump,
3255 media_log_->id(), base::Unretained(demuxer_.get())));
3256}
3257
dalecurtis83266c72015-10-29 18:43:203258void WebMediaPlayerImpl::ReportMemoryUsage() {
3259 DCHECK(main_task_runner_->BelongsToCurrentThread());
3260
wdzierzanowskifd4cd91c52015-12-02 23:50:203261 // About base::Unretained() usage below: We destroy |demuxer_| on the main
3262 // thread. Before that, however, ~WebMediaPlayerImpl() posts a task to the
3263 // media thread and waits for it to finish. Hence, the GetMemoryUsage() task
3264 // posted here must finish earlier.
Dale Curtis8a6281322017-08-31 00:35:533265 //
3266 // The exception to the above is when OnError() has been called. If we're in
3267 // the error state we've already shut down the pipeline and can't rely on it
3268 // to cycle the media thread before we destroy |demuxer_|. In this case skip
3269 // collection of the demuxer memory stats.
3270 if (demuxer_ && !IsNetworkStateError(network_state_)) {
wdzierzanowskifd4cd91c52015-12-02 23:50:203271 base::PostTaskAndReplyWithResult(
3272 media_task_runner_.get(), FROM_HERE,
Jan Wilken Dörrie2e1d2d9a2020-01-24 17:14:183273 base::BindOnce(&Demuxer::GetMemoryUsage,
3274 base::Unretained(demuxer_.get())),
3275 base::BindOnce(&WebMediaPlayerImpl::FinishMemoryUsageReport,
3276 weak_this_));
wdzierzanowskifd4cd91c52015-12-02 23:50:203277 } else {
3278 FinishMemoryUsageReport(0);
3279 }
3280}
3281
3282void WebMediaPlayerImpl::FinishMemoryUsageReport(int64_t demuxer_memory_usage) {
3283 DCHECK(main_task_runner_->BelongsToCurrentThread());
3284
avayvodc4bfb0e62017-01-13 01:07:013285 const PipelineStatistics stats = GetPipelineStatistics();
servolk639473e492016-12-15 04:14:203286 const int64_t data_source_memory_usage =
3287 data_source_ ? data_source_->GetMemoryUsage() : 0;
dalecurtisecc76612017-04-19 00:31:203288
Dale Curtisc2c5dcb12018-04-16 23:21:293289 // If we have video and no video memory usage and we've rendered the first
3290 // frame, assume the VideoFrameCompositor is holding onto the last frame after
3291 // we've suspended the pipeline; which thus reports zero memory usage from the
3292 // video renderer.
dalecurtisecc76612017-04-19 00:31:203293 //
3294 // Technically this should use the coded size, but that requires us to hop to
3295 // the compositor to get and byte-perfect accuracy isn't important here.
3296 const int64_t video_memory_usage =
3297 stats.video_memory_usage +
Dale Curtisc2c5dcb12018-04-16 23:21:293298 ((pipeline_metadata_.has_video && !stats.video_memory_usage &&
3299 has_first_frame_)
Miguel Casas9e7766022018-01-08 16:13:133300 ? VideoFrame::AllocationSize(PIXEL_FORMAT_I420,
dalecurtisecc76612017-04-19 00:31:203301 pipeline_metadata_.natural_size)
3302 : 0);
3303
dalecurtis83266c72015-10-29 18:43:203304 const int64_t current_memory_usage =
dalecurtisecc76612017-04-19 00:31:203305 stats.audio_memory_usage + video_memory_usage + data_source_memory_usage +
3306 demuxer_memory_usage;
dalecurtis83266c72015-10-29 18:43:203307
Xiaohan Wang8859b07e2020-04-29 17:59:053308 DVLOG(3) << "Memory Usage -- Total: " << current_memory_usage
dalecurtisecc76612017-04-19 00:31:203309 << " Audio: " << stats.audio_memory_usage
3310 << ", Video: " << video_memory_usage
servolk639473e492016-12-15 04:14:203311 << ", DataSource: " << data_source_memory_usage
wdzierzanowskifd4cd91c52015-12-02 23:50:203312 << ", Demuxer: " << demuxer_memory_usage;
dalecurtis83266c72015-10-29 18:43:203313
3314 const int64_t delta = current_memory_usage - last_reported_memory_usage_;
3315 last_reported_memory_usage_ = current_memory_usage;
3316 adjust_allocated_memory_cb_.Run(delta);
dalecurtis83266c72015-10-29 18:43:203317}
3318
Eugene Zemtsov77d56262020-03-06 05:52:033319void WebMediaPlayerImpl::OnMainThreadMemoryDump(
3320 int32_t id,
Eugene Zemtsovc4c6db262020-03-20 22:19:413321 const base::trace_event::MemoryDumpArgs& args,
Eugene Zemtsov77d56262020-03-06 05:52:033322 base::trace_event::ProcessMemoryDump* pmd) {
3323 const PipelineStatistics stats = GetPipelineStatistics();
Eugene Zemtsovfc2b67a2020-04-01 02:58:173324 auto player_node_name =
3325 base::StringPrintf("media/webmediaplayer/player_0x%x", id);
3326 auto* player_node = pmd->CreateAllocatorDump(player_node_name);
3327 player_node->AddScalar(
3328 base::trace_event::MemoryAllocatorDump::kNameObjectCount,
3329 base::trace_event::MemoryAllocatorDump::kUnitsObjects, 1);
Eugene Zemtsov77d56262020-03-06 05:52:033330
Eugene Zemtsovc4c6db262020-03-20 22:19:413331 if (args.level_of_detail !=
3332 base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND) {
3333 bool suspended = pipeline_controller_->IsPipelineSuspended();
Eugene Zemtsovc4c6db262020-03-20 22:19:413334 auto player_state =
3335 base::StringPrintf("Paused: %d Ended: %d ReadyState: %d Suspended: %d",
3336 paused_, ended_, GetReadyState(), suspended);
Eugene Zemtsovfc2b67a2020-04-01 02:58:173337 player_node->AddString("player_state", "", player_state);
Eugene Zemtsovc4c6db262020-03-20 22:19:413338 }
Eugene Zemtsov77d56262020-03-06 05:52:033339
3340 CreateAllocation(pmd, id, "audio", stats.audio_memory_usage);
3341 CreateAllocation(pmd, id, "video", stats.video_memory_usage);
3342
3343 if (data_source_)
3344 CreateAllocation(pmd, id, "data_source", data_source_->GetMemoryUsage());
3345}
3346
3347// static
3348void WebMediaPlayerImpl::OnMediaThreadMemoryDump(
3349 int32_t id,
3350 Demuxer* demuxer,
Eugene Zemtsovc4c6db262020-03-20 22:19:413351 const base::trace_event::MemoryDumpArgs& args,
Eugene Zemtsov77d56262020-03-06 05:52:033352 base::trace_event::ProcessMemoryDump* pmd) {
3353 if (!demuxer)
3354 return;
3355
3356 CreateAllocation(pmd, id, "demuxer", demuxer->GetMemoryUsage());
3357}
3358
dalecurtis8b8505e72016-06-10 21:59:173359void WebMediaPlayerImpl::ScheduleIdlePauseTimer() {
avayvod65fad272017-02-24 01:00:483360 // Only schedule the pause timer if we're not paused or paused but going to
avayvod52efd282017-03-07 21:13:043361 // resume when foregrounded, and are suspended and have audio.
3362 if ((paused_ && !paused_when_hidden_) ||
Antonio Gomes81b56552019-05-15 22:15:283363 !pipeline_controller_->IsSuspended() || !HasAudio()) {
dalecurtis8b8505e72016-06-10 21:59:173364 return;
avayvod52efd282017-03-07 21:13:043365 }
dalecurtis8b8505e72016-06-10 21:59:173366
3367#if defined(OS_ANDROID)
Thomas Guilbert7350fdad2019-01-29 23:09:243368 // Don't pause videos casted as part of RemotePlayback.
3369 if (is_flinging_)
dalecurtis8b8505e72016-06-10 21:59:173370 return;
3371#endif
3372
3373 // Idle timeout chosen arbitrarily.
3374 background_pause_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(5),
3375 this, &WebMediaPlayerImpl::OnPause);
3376}
3377
dalecurtis04bdb582016-08-17 22:15:233378void WebMediaPlayerImpl::CreateWatchTimeReporter() {
wdzierzanowskia78fa9b2017-06-13 18:12:103379 if (!HasVideo() && !HasAudio())
3380 return;
3381
Dan Sanders7ef5d0f2019-05-03 18:30:113382 // MediaPlayerRenderer does not know about tracks until playback starts.
3383 // Assume audio-only unless the natural size has been detected.
3384 bool has_video = pipeline_metadata_.has_video;
3385 if (using_media_player_renderer_) {
3386 has_video = !pipeline_metadata_.natural_size.IsEmpty();
3387 }
3388
dalecurtis04bdb582016-08-17 22:15:233389 // Create the watch time reporter and synchronize its initial state.
Jose Lopes9023b6d2020-02-19 20:42:373390 watch_time_reporter_ = std::make_unique<WatchTimeReporter>(
Dan Sanders7ef5d0f2019-05-03 18:30:113391 mojom::PlaybackProperties::New(
3392 pipeline_metadata_.has_audio, has_video, false, false,
3393 !!chunk_demuxer_, is_encrypted_, embedded_media_experience_enabled_),
Dale Curtis96d6e9382018-07-18 18:01:073394 pipeline_metadata_.natural_size,
Dale Curtis051fdf62017-08-05 00:21:133395 base::BindRepeating(&WebMediaPlayerImpl::GetCurrentTimeInternal,
3396 base::Unretained(this)),
Dale Curtis0f8d7b32019-10-24 22:10:283397 base::BindRepeating(&WebMediaPlayerImpl::GetPipelineStatistics,
3398 base::Unretained(this)),
Hajime Hoshi8bb37dc2017-12-19 09:35:103399 media_metrics_provider_.get(),
Jose Lopes9023b6d2020-02-19 20:42:373400 frame_->GetTaskRunner(blink::TaskType::kInternalMedia));
dalecurtis04bdb582016-08-17 22:15:233401 watch_time_reporter_->OnVolumeChange(volume_);
Dale Curtisaebaeea2018-07-19 23:42:113402 watch_time_reporter_->OnDurationChanged(GetPipelineMediaDuration());
Mounir Lamourif9af74e72017-06-19 19:31:453403
tguilbert1bb1c782017-01-23 21:15:113404 if (delegate_->IsFrameHidden())
dalecurtis04bdb582016-08-17 22:15:233405 watch_time_reporter_->OnHidden();
3406 else
3407 watch_time_reporter_->OnShown();
Mounir Lamourif9af74e72017-06-19 19:31:453408
Mounir Lamouri41a79c62017-06-06 12:53:163409 if (client_->HasNativeControls())
3410 watch_time_reporter_->OnNativeControlsEnabled();
3411 else
3412 watch_time_reporter_->OnNativeControlsDisabled();
Mounir Lamourif9af74e72017-06-19 19:31:453413
3414 switch (client_->DisplayType()) {
3415 case WebMediaPlayer::DisplayType::kInline:
3416 watch_time_reporter_->OnDisplayTypeInline();
3417 break;
3418 case WebMediaPlayer::DisplayType::kFullscreen:
3419 watch_time_reporter_->OnDisplayTypeFullscreen();
3420 break;
3421 case WebMediaPlayer::DisplayType::kPictureInPicture:
3422 watch_time_reporter_->OnDisplayTypePictureInPicture();
3423 break;
3424 }
Dale Curtis96d6e9382018-07-18 18:01:073425
3426 UpdateSecondaryProperties();
Dale Curtis7fd27c4b2018-07-30 22:14:213427
3428 // If the WatchTimeReporter was recreated in the middle of playback, we want
3429 // to resume playback here too since we won't get another play() call. When
3430 // seeking, the seek completion will restart it if necessary.
3431 if (!paused_ && !seeking_)
3432 watch_time_reporter_->OnPlaying();
Dale Curtis96d6e9382018-07-18 18:01:073433}
3434
3435void WebMediaPlayerImpl::UpdateSecondaryProperties() {
3436 watch_time_reporter_->UpdateSecondaryProperties(
3437 mojom::SecondaryPlaybackProperties::New(
3438 pipeline_metadata_.audio_decoder_config.codec(),
Dale Curtis7140b502019-10-23 20:34:423439 pipeline_metadata_.video_decoder_config.codec(),
Dale Curtisf8afcef32020-01-18 06:23:263440 pipeline_metadata_.audio_decoder_config.profile(),
Dale Curtis7140b502019-10-23 20:34:423441 pipeline_metadata_.video_decoder_config.profile(),
3442 audio_decoder_name_, video_decoder_name_,
Yuchen Liub33bfc12019-11-08 20:16:123443 pipeline_metadata_.audio_decoder_config.encryption_scheme(),
3444 pipeline_metadata_.video_decoder_config.encryption_scheme(),
John Rummelld30555352018-09-21 20:47:253445 pipeline_metadata_.natural_size));
dalecurtis04bdb582016-08-17 22:15:233446}
3447
avayvod39c102402016-11-23 21:43:133448bool WebMediaPlayerImpl::IsHidden() const {
3449 DCHECK(main_task_runner_->BelongsToCurrentThread());
3450
tguilbert1bb1c782017-01-23 21:15:113451 return delegate_->IsFrameHidden() && !delegate_->IsFrameClosed();
avayvod39c102402016-11-23 21:43:133452}
3453
avayvod102cdb62017-01-07 03:11:093454bool WebMediaPlayerImpl::IsStreaming() const {
3455 return data_source_ && data_source_->IsStreaming();
3456}
3457
liberato2fd111be2017-01-04 00:25:063458bool WebMediaPlayerImpl::DoesOverlaySupportMetadata() const {
Ted Meyer4a427632019-05-01 19:05:273459 return pipeline_metadata_.video_decoder_config.video_transformation() ==
3460 kNoTransformation;
liberato2fd111be2017-01-04 00:25:063461}
3462
Anton Vayvod09fa66e2017-07-20 23:02:123463void WebMediaPlayerImpl::UpdateRemotePlaybackCompatibility(bool is_compatible) {
3464 DCHECK(main_task_runner_->BelongsToCurrentThread());
3465
3466 client_->RemotePlaybackCompatibilityChanged(loaded_url_, is_compatible);
3467}
3468
Dale Curtis6438cf12018-03-29 02:34:013469void WebMediaPlayerImpl::ForceStaleStateForTesting(ReadyState target_state) {
3470 stale_state_override_for_testing_.emplace(target_state);
Dale Curtis99a9b482018-02-01 02:23:283471 UpdatePlayState();
3472}
3473
3474bool WebMediaPlayerImpl::IsSuspendedForTesting() {
3475 // This intentionally uses IsPipelineSuspended since we need to know when the
3476 // pipeline has reached the suspended state, not when it's in suspending.
Antonio Gomes81b56552019-05-15 22:15:283477 return pipeline_controller_->IsPipelineSuspended();
Dale Curtis99a9b482018-02-01 02:23:283478}
3479
Dale Curtis7c63f2e22018-09-19 21:06:273480bool WebMediaPlayerImpl::DidLazyLoad() const {
3481 return did_lazy_load_;
3482}
3483
3484void WebMediaPlayerImpl::OnBecameVisible() {
Dale Curtis04769ca2019-05-25 00:38:103485 have_enough_after_lazy_load_cb_.Cancel();
Dale Curtis7c63f2e22018-09-19 21:06:273486 needs_first_frame_ = !has_first_frame_;
3487 UpdatePlayState();
3488}
3489
Miguel Casasfb63a5792018-12-04 23:50:413490bool WebMediaPlayerImpl::IsOpaque() const {
3491 return opaque_;
3492}
3493
Mounir Lamouri99ba5a62019-02-12 01:27:473494int WebMediaPlayerImpl::GetDelegateId() {
3495 return delegate_id_;
3496}
3497
3498base::Optional<viz::SurfaceId> WebMediaPlayerImpl::GetSurfaceId() {
3499 if (!surface_layer_for_video_enabled_)
3500 return base::nullopt;
3501 return bridge_->GetSurfaceId();
3502}
3503
Thomas Guilbert83fdc9b82020-04-11 01:21:543504void WebMediaPlayerImpl::RequestVideoFrameCallback() {
Thomas Guilberte95597002020-02-06 01:08:153505 compositor_->SetOnFramePresentedCallback(BindToCurrentLoop(base::BindOnce(
3506 &WebMediaPlayerImpl::OnNewFramePresentedCallback, weak_this_)));
Thomas Guilberte90fe312019-11-07 22:27:563507}
3508
Thomas Guilberte4669d42020-02-21 02:40:203509void WebMediaPlayerImpl::OnNewFramePresentedCallback() {
Thomas Guilbert83fdc9b82020-04-11 01:21:543510 client_->OnRequestVideoFrameCallback();
Thomas Guilberte4669d42020-02-21 02:40:203511}
3512
3513std::unique_ptr<blink::WebMediaPlayer::VideoFramePresentationMetadata>
3514WebMediaPlayerImpl::GetVideoFramePresentationMetadata() {
3515 return compositor_->GetLastPresentedFrameMetadata();
Thomas Guilberte90fe312019-11-07 22:27:563516}
3517
Thomas Guilbertfc241402020-09-30 21:38:323518void WebMediaPlayerImpl::UpdateFrameIfStale() {
3519 // base::Unretained is safe here because |compositor_| is destroyed on
3520 // |vfc_task_runner_|. The destruction is queued from |this|' destructor,
3521 // which also runs on |main_task_runner_|, which makes it impossible for
3522 // UpdateCurrentFrameIfStale() to be queued after |compositor_|'s dtor.
3523 vfc_task_runner_->PostTask(
3524 FROM_HERE,
3525 base::BindOnce(&VideoFrameCompositor::UpdateCurrentFrameIfStale,
3526 base::Unretained(compositor_.get()),
3527 VideoFrameCompositor::UpdateType::kBypassClient));
3528}
3529
Antonio Gomes142f32a2019-05-15 23:32:563530base::WeakPtr<blink::WebMediaPlayer> WebMediaPlayerImpl::AsWeakPtr() {
3531 return weak_this_;
3532}
3533
Yuchen Liue7813972019-04-12 22:34:243534bool WebMediaPlayerImpl::ShouldPausePlaybackWhenHidden() const {
3535 // Audio only stream is allowed to play when in background.
3536 // TODO: We should check IsBackgroundOptimizationCandidate here. But we need
3537 // to move the logic of checking video frames out of that function.
3538 if (!HasVideo())
3539 return false;
3540
chenjiadong29029882020-06-19 17:35:153541 if (using_media_player_renderer_ &&
3542 pipeline_metadata_.natural_size.IsEmpty()) {
3543 return false;
3544 }
3545
Junbo Kefba620b2019-01-16 02:54:363546 if (!is_background_video_playback_enabled_)
3547 return true;
3548
avayvod65fad272017-02-24 01:00:483549 // If suspending background video, pause any video that's not remoted or
3550 // not unlocked to play in the background.
Sergey Volk8b09c2c52018-12-12 23:20:403551 if (IsBackgroundSuspendEnabled(this)) {
avayvod65fad272017-02-24 01:00:483552#if defined(OS_ANDROID)
Thomas Guilbert7350fdad2019-01-29 23:09:243553 if (is_flinging_)
avayvod65fad272017-02-24 01:00:483554 return false;
avayvodcc273dd2017-01-19 19:35:123555#endif
avayvodeb9098d2017-01-07 00:33:033556
Blink Reformat1c4d759e2017-04-09 16:34:543557 return !HasAudio() || (IsResumeBackgroundVideosEnabled() &&
3558 video_locked_when_paused_when_hidden_);
avayvod65fad272017-02-24 01:00:483559 }
3560
3561 // Otherwise only pause if the optimization is on and it's a video-only
3562 // optimization candidate.
avayvod01201332017-04-14 00:27:153563 return IsBackgroundVideoPauseOptimizationEnabled() && !HasAudio() &&
Thomas Guilbert29ae1a902018-10-20 01:53:383564 IsBackgroundOptimizationCandidate() && !is_flinging_;
avayvodeb9098d2017-01-07 00:33:033565}
3566
avayvod2135a642017-01-13 00:17:143567bool WebMediaPlayerImpl::ShouldDisableVideoWhenHidden() const {
Ted Meyera918e592018-09-08 00:16:203568 // This optimization is behind the flag on all platforms, only for non-mse
3569 // video. MSE video track switching on hide has gone through a field test.
3570 // TODO(tmathmeyer): Passing load_type_ won't be needed after src= field
3571 // testing is finished. see: http://crbug.com/709302
Ted Meyer0e105992019-05-04 01:47:213572 if (!is_background_video_track_optimization_supported_)
avayvodc4bfb0e62017-01-13 01:07:013573 return false;
avayvodc4bfb0e62017-01-13 01:07:013574
avayvodcc273dd2017-01-19 19:35:123575 // Disable video track only for players with audio that match the criteria for
3576 // being optimized.
Blink Reformat1c4d759e2017-04-09 16:34:543577 return HasAudio() && IsBackgroundOptimizationCandidate();
avayvodcc273dd2017-01-19 19:35:123578}
3579
3580bool WebMediaPlayerImpl::IsBackgroundOptimizationCandidate() const {
3581 DCHECK(main_task_runner_->BelongsToCurrentThread());
3582
François Beaufort664c3ca72018-04-13 07:24:513583 // Don't optimize Picture-in-Picture players.
Mounir Lamouri0484f40a2018-07-25 03:03:263584 if (IsInPictureInPicture())
François Beaufort664c3ca72018-04-13 07:24:513585 return false;
3586
Thomas Guilbert7350fdad2019-01-29 23:09:243587#if defined(OS_ANDROID)
3588 // Don't optimize videos casted as part of RemotePlayback.
3589 if (is_flinging_)
avayvodcc273dd2017-01-19 19:35:123590 return false;
Thomas Guilbert7350fdad2019-01-29 23:09:243591#endif
avayvodcc273dd2017-01-19 19:35:123592
3593 // Don't optimize audio-only or streaming players.
Blink Reformat1c4d759e2017-04-09 16:34:543594 if (!HasVideo() || IsStreaming())
avayvodcc273dd2017-01-19 19:35:123595 return false;
3596
Dale Curtis3f4935b2017-09-09 00:11:593597 // Video-only players are always optimized (paused).
3598 // Don't check the keyframe distance and duration.
3599 if (!HasAudio() && HasVideo())
3600 return true;
3601
avayvodcc273dd2017-01-19 19:35:123602 // Videos shorter than the maximum allowed keyframe distance can be optimized.
3603 base::TimeDelta duration = GetPipelineMediaDuration();
Ted Meyera918e592018-09-08 00:16:203604
Dale Curtis456808a2018-10-23 17:50:213605 constexpr base::TimeDelta kMaxKeyframeDistanceToDisableBackgroundVideo =
3606 base::TimeDelta::FromMilliseconds(
3607 kMaxKeyframeDistanceToDisableBackgroundVideoMs);
3608 if (duration < kMaxKeyframeDistanceToDisableBackgroundVideo)
avayvodcc273dd2017-01-19 19:35:123609 return true;
3610
3611 // Otherwise, only optimize videos with shorter average keyframe distance.
avayvodc4bfb0e62017-01-13 01:07:013612 PipelineStatistics stats = GetPipelineStatistics();
Dale Curtis456808a2018-10-23 17:50:213613 return stats.video_keyframe_distance_average <
3614 kMaxKeyframeDistanceToDisableBackgroundVideo;
avayvod2135a642017-01-13 00:17:143615}
3616
avayvod56e1f3942017-01-21 02:06:313617void WebMediaPlayerImpl::UpdateBackgroundVideoOptimizationState() {
Takumi Fujimotof2319684e2019-12-03 18:51:513618 if (IsHidden()) {
Yuchen Liue7813972019-04-12 22:34:243619 if (ShouldPausePlaybackWhenHidden()) {
avayvod56e1f3942017-01-21 02:06:313620 PauseVideoIfNeeded();
Dale Curtisa75a7892017-08-09 20:21:513621 } else if (update_background_status_cb_.IsCancelled()) {
3622 // Only trigger updates when we don't have one already scheduled.
3623 update_background_status_cb_.Reset(
3624 base::Bind(&WebMediaPlayerImpl::DisableVideoTrackIfNeeded,
3625 base::Unretained(this)));
3626
3627 // Defer disable track until we're sure the clip will be backgrounded for
3628 // some time. Resuming may take half a second, so frequent tab switches
3629 // will yield a poor user experience otherwise. http://crbug.com/709302
3630 // may also cause AV sync issues if disable/enable happens too fast.
3631 main_task_runner_->PostDelayedTask(
3632 FROM_HERE, update_background_status_cb_.callback(),
3633 base::TimeDelta::FromSeconds(10));
3634 }
avayvod56e1f3942017-01-21 02:06:313635 } else {
Dale Curtisa75a7892017-08-09 20:21:513636 update_background_status_cb_.Cancel();
avayvod56e1f3942017-01-21 02:06:313637 EnableVideoTrackIfNeeded();
3638 }
3639}
3640
3641void WebMediaPlayerImpl::PauseVideoIfNeeded() {
3642 DCHECK(IsHidden());
3643
3644 // Don't pause video while the pipeline is stopped, resuming or seeking.
3645 // Also if the video is paused already.
Antonio Gomes81b56552019-05-15 22:15:283646 if (!pipeline_controller_->IsPipelineRunning() || is_pipeline_resuming_ ||
tguilbert350936ff2017-02-24 05:39:273647 seeking_ || paused_)
avayvod56e1f3942017-01-21 02:06:313648 return;
3649
3650 // OnPause() will set |paused_when_hidden_| to false and call
3651 // UpdatePlayState(), so set the flag to true after and then return.
3652 OnPause();
3653 paused_when_hidden_ = true;
3654}
3655
avayvod2135a642017-01-13 00:17:143656void WebMediaPlayerImpl::EnableVideoTrackIfNeeded() {
avayvod56e1f3942017-01-21 02:06:313657 // Don't change video track while the pipeline is stopped, resuming or
3658 // seeking.
Antonio Gomes81b56552019-05-15 22:15:283659 if (!pipeline_controller_->IsPipelineRunning() || is_pipeline_resuming_ ||
tguilbert350936ff2017-02-24 05:39:273660 seeking_)
avayvod2135a642017-01-13 00:17:143661 return;
3662
3663 if (video_track_disabled_) {
3664 video_track_disabled_ = false;
Blink Reformat1c4d759e2017-04-09 16:34:543665 if (client_->HasSelectedVideoTrack()) {
3666 WebMediaPlayer::TrackId trackId = client_->GetSelectedVideoTrackId();
3667 SelectedVideoTrackChanged(&trackId);
avayvod2135a642017-01-13 00:17:143668 }
3669 }
3670}
3671
3672void WebMediaPlayerImpl::DisableVideoTrackIfNeeded() {
3673 DCHECK(IsHidden());
3674
3675 // Don't change video track while the pipeline is resuming or seeking.
3676 if (is_pipeline_resuming_ || seeking_)
3677 return;
3678
3679 if (!video_track_disabled_ && ShouldDisableVideoWhenHidden()) {
3680 video_track_disabled_ = true;
Blink Reformat1c4d759e2017-04-09 16:34:543681 SelectedVideoTrackChanged(nullptr);
avayvod2135a642017-01-13 00:17:143682 }
3683}
3684
avayvodc4bfb0e62017-01-13 01:07:013685void WebMediaPlayerImpl::SetPipelineStatisticsForTest(
3686 const PipelineStatistics& stats) {
3687 pipeline_statistics_for_test_ = base::make_optional(stats);
3688}
3689
3690PipelineStatistics WebMediaPlayerImpl::GetPipelineStatistics() const {
3691 DCHECK(main_task_runner_->BelongsToCurrentThread());
3692
tguilbert350936ff2017-02-24 05:39:273693 return pipeline_statistics_for_test_.value_or(
Antonio Gomes81b56552019-05-15 22:15:283694 pipeline_controller_->GetStatistics());
avayvodc4bfb0e62017-01-13 01:07:013695}
3696
avayvodcc273dd2017-01-19 19:35:123697void WebMediaPlayerImpl::SetPipelineMediaDurationForTest(
3698 base::TimeDelta duration) {
3699 pipeline_media_duration_for_test_ = base::make_optional(duration);
3700}
3701
3702base::TimeDelta WebMediaPlayerImpl::GetPipelineMediaDuration() const {
3703 DCHECK(main_task_runner_->BelongsToCurrentThread());
3704
3705 return pipeline_media_duration_for_test_.value_or(
Antonio Gomes81b56552019-05-15 22:15:283706 pipeline_controller_->GetMediaDuration());
avayvodcc273dd2017-01-19 19:35:123707}
3708
Xiangjun Zhangba8724f482017-08-03 06:43:253709void WebMediaPlayerImpl::SwitchToRemoteRenderer(
3710 const std::string& remote_device_friendly_name) {
xjz4e5d4bf32017-02-15 21:26:353711 DCHECK(main_task_runner_->BelongsToCurrentThread());
Chris Cunningham89b4b762019-03-27 17:13:333712
3713 DCHECK(!is_remote_rendering_);
3714 is_remote_rendering_ = true;
3715
Thomas Guilbertb341bae02018-05-09 00:02:133716 DCHECK(!disable_pipeline_auto_suspend_);
Xiangjun Zhangba8724f482017-08-03 06:43:253717 disable_pipeline_auto_suspend_ = true;
Chris Cunninghamd9df58e2017-08-29 00:04:233718
3719 // Capabilities reporting should only be performed for local playbacks.
3720 video_decode_stats_reporter_.reset();
3721
Xiangjun Zhangba8724f482017-08-03 06:43:253722 // Requests to restart media pipeline. A remote renderer will be created via
3723 // the |renderer_factory_selector_|.
xjz4e5d4bf32017-02-15 21:26:353724 ScheduleRestart();
xjzdf9b67e2017-04-13 23:28:253725 if (client_) {
Xiangjun Zhangba8724f482017-08-03 06:43:253726 client_->MediaRemotingStarted(
3727 WebString::FromUTF8(remote_device_friendly_name));
xjzdf9b67e2017-04-13 23:28:253728 }
xjz4e5d4bf32017-02-15 21:26:353729}
3730
Xiangjun Zhang5e20cba42018-01-10 19:54:563731void WebMediaPlayerImpl::SwitchToLocalRenderer(
3732 MediaObserverClient::ReasonToSwitchToLocal reason) {
Xiangjun Zhangba8724f482017-08-03 06:43:253733 DCHECK(main_task_runner_->BelongsToCurrentThread());
Chris Cunningham89b4b762019-03-27 17:13:333734 if (!is_remote_rendering_)
Xiangjun Zhang12f55272018-07-30 23:19:373735 return; // Is currently with local renderer.
Chris Cunningham89b4b762019-03-27 17:13:333736 is_remote_rendering_ = false;
3737
3738 DCHECK(disable_pipeline_auto_suspend_);
Xiangjun Zhangba8724f482017-08-03 06:43:253739 disable_pipeline_auto_suspend_ = false;
Chris Cunninghamd9df58e2017-08-29 00:04:233740
3741 // Capabilities reporting may resume now that playback is local.
3742 CreateVideoDecodeStatsReporter();
3743
Xiangjun Zhangba8724f482017-08-03 06:43:253744 // Requests to restart media pipeline. A local renderer will be created via
3745 // the |renderer_factory_selector_|.
3746 ScheduleRestart();
3747 if (client_)
Xiangjun Zhang5e20cba42018-01-10 19:54:563748 client_->MediaRemotingStopped(GetSwitchToLocalMessage(reason));
Xiangjun Zhangba8724f482017-08-03 06:43:253749}
3750
dalecurtis4f6d14d2017-02-22 17:42:223751void WebMediaPlayerImpl::RecordUnderflowDuration(base::TimeDelta duration) {
3752 DCHECK(data_source_ || chunk_demuxer_);
xhwang68b3fab82017-05-17 21:31:463753
dalecurtis4f6d14d2017-02-22 17:42:223754 if (data_source_)
Jennifer Apacible82e25c92017-08-07 18:15:273755 UMA_HISTOGRAM_TIMES("Media.UnderflowDuration2.SRC", duration);
dalecurtis4f6d14d2017-02-22 17:42:223756 else
Jennifer Apacible82e25c92017-08-07 18:15:273757 UMA_HISTOGRAM_TIMES("Media.UnderflowDuration2.MSE", duration);
xhwang68b3fab82017-05-17 21:31:463758
3759 if (is_encrypted_)
Jennifer Apacible82e25c92017-08-07 18:15:273760 UMA_HISTOGRAM_TIMES("Media.UnderflowDuration2.EME", duration);
dalecurtis4f6d14d2017-02-22 17:42:223761}
3762
xhwang60802652017-04-19 07:29:583763#define UMA_HISTOGRAM_VIDEO_HEIGHT(name, sample) \
3764 UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 100, 10000, 50)
3765
3766void WebMediaPlayerImpl::RecordVideoNaturalSize(const gfx::Size& natural_size) {
3767 // Always report video natural size to MediaLog.
Ted Meyer0134aed2020-01-23 23:56:403768 media_log_->AddEvent<MediaLogEvent::kVideoSizeChanged>(natural_size);
Ted Meyer7f5b4e22019-11-21 03:21:193769 media_log_->SetProperty<MediaLogProperty::kResolution>(natural_size);
xhwang60802652017-04-19 07:29:583770
3771 if (initial_video_height_recorded_)
3772 return;
3773
3774 initial_video_height_recorded_ = true;
3775
3776 int height = natural_size.height();
3777
3778 if (load_type_ == kLoadTypeURL)
3779 UMA_HISTOGRAM_VIDEO_HEIGHT("Media.VideoHeight.Initial.SRC", height);
3780 else if (load_type_ == kLoadTypeMediaSource)
3781 UMA_HISTOGRAM_VIDEO_HEIGHT("Media.VideoHeight.Initial.MSE", height);
3782
3783 if (is_encrypted_)
3784 UMA_HISTOGRAM_VIDEO_HEIGHT("Media.VideoHeight.Initial.EME", height);
3785
3786 UMA_HISTOGRAM_VIDEO_HEIGHT("Media.VideoHeight.Initial.All", height);
Sergey Ulanovb8656772020-04-21 22:56:143787
3788 if (playback_events_recorder_)
3789 playback_events_recorder_->OnNaturalSizeChanged(natural_size);
xhwang60802652017-04-19 07:29:583790}
3791
3792#undef UMA_HISTOGRAM_VIDEO_HEIGHT
3793
Greg Thompsonaa48ce8d2018-04-03 06:11:433794void WebMediaPlayerImpl::SetTickClockForTest(
3795 const base::TickClock* tick_clock) {
tzik2c963b872017-12-07 06:57:243796 tick_clock_ = tick_clock;
Antonio Gomes81b56552019-05-15 22:15:283797 buffered_data_source_host_->SetTickClockForTest(tick_clock);
hubbeb2d3efd2017-05-05 23:26:383798}
3799
Dale Curtis3899090ea2018-01-12 00:10:353800void WebMediaPlayerImpl::OnFirstFrame(base::TimeTicks frame_time) {
3801 DCHECK(!load_start_time_.is_null());
Dale Curtisff576552018-03-30 02:32:443802 DCHECK(!skip_metrics_due_to_startup_suspend_);
Dale Curtisc2c5dcb12018-04-16 23:21:293803 has_first_frame_ = true;
Dale Curtis7c63f2e22018-09-19 21:06:273804 needs_first_frame_ = false;
Dale Curtis3899090ea2018-01-12 00:10:353805 const base::TimeDelta elapsed = frame_time - load_start_time_;
3806 media_metrics_provider_->SetTimeToFirstFrame(elapsed);
3807 RecordTimingUMA("Media.TimeToFirstFrame", elapsed);
Dale Curtiscdfc6f22019-06-26 22:05:233808
3809 // Needed to signal HTMLVideoElement that it should remove the poster image.
Dale Curtis66790f32019-07-11 00:14:243810 if (client_ && has_poster_)
Dale Curtiscdfc6f22019-06-26 22:05:233811 client_->Repaint();
Dale Curtis3899090ea2018-01-12 00:10:353812}
3813
3814void WebMediaPlayerImpl::RecordTimingUMA(const std::string& key,
3815 base::TimeDelta elapsed) {
3816 if (chunk_demuxer_)
3817 base::UmaHistogramMediumTimes(key + ".MSE", elapsed);
3818 else
3819 base::UmaHistogramMediumTimes(key + ".SRC", elapsed);
3820 if (is_encrypted_)
3821 base::UmaHistogramMediumTimes(key + ".EME", elapsed);
3822}
3823
John Rummelldb5a7ef2018-05-16 00:28:013824void WebMediaPlayerImpl::RecordEncryptionScheme(
3825 const std::string& stream_name,
Yuchen Liub33bfc12019-11-08 20:16:123826 EncryptionScheme encryption_scheme) {
John Rummelldb5a7ef2018-05-16 00:28:013827 DCHECK(stream_name == "Audio" || stream_name == "Video");
3828
3829 // If the stream is not encrypted, don't record it.
Yuchen Liub33bfc12019-11-08 20:16:123830 if (encryption_scheme == EncryptionScheme::kUnencrypted)
John Rummelldb5a7ef2018-05-16 00:28:013831 return;
3832
3833 base::UmaHistogramEnumeration(
3834 "Media.EME.EncryptionScheme.Initial." + stream_name,
3835 DetermineEncryptionSchemeUMAValue(encryption_scheme),
3836 EncryptionSchemeUMA::kCount);
3837}
3838
Mounir Lamouri0484f40a2018-07-25 03:03:263839bool WebMediaPlayerImpl::IsInPictureInPicture() const {
3840 DCHECK(client_);
3841 return client_->DisplayType() ==
3842 WebMediaPlayer::DisplayType::kPictureInPicture;
3843}
3844
Jazz Xu74690262020-01-30 14:02:273845void WebMediaPlayerImpl::OnPictureInPictureAvailabilityChanged(bool available) {
3846 delegate_->DidPictureInPictureAvailabilityChange(delegate_id_, available);
3847}
3848
David Dorwin89fe8d02020-05-01 05:42:423849void WebMediaPlayerImpl::MaybeSetContainerNameForMetrics() {
Dale Curtis5bba03232018-08-30 17:57:383850 // Pipeline startup failed before even getting a demuxer setup.
3851 if (!demuxer_)
3852 return;
3853
3854 // Container has already been set.
3855 if (highest_ready_state_ >= WebMediaPlayer::kReadyStateHaveMetadata)
3856 return;
3857
David Dorwin89fe8d02020-05-01 05:42:423858 // Only report metrics for demuxers that provide container information.
3859 auto container = demuxer_->GetContainerForMetrics();
3860 if (container.has_value())
3861 media_metrics_provider_->SetContainerName(container.value());
Dale Curtis5bba03232018-08-30 17:57:383862}
3863
Dale Curtis18ad391b2019-05-30 23:25:093864void WebMediaPlayerImpl::MaybeUpdateBufferSizesForPlayback() {
3865 // Don't increase the MultiBufferDataSource buffer size until we've reached
3866 // kReadyStateHaveEnoughData. Otherwise we will unnecessarily slow down
3867 // playback startup -- it can instead be done for free after playback starts.
3868 if (!mb_data_source_ || highest_ready_state_ < kReadyStateHaveEnoughData)
3869 return;
3870
3871 mb_data_source_->MediaPlaybackRateChanged(playback_rate_);
3872 if (!paused_)
3873 mb_data_source_->MediaIsPlaying();
Dale Curtis18ad391b2019-05-30 23:25:093874}
3875
Dan Sanders6edfd782019-08-13 00:13:183876void WebMediaPlayerImpl::OnSimpleWatchTimerTick() {
3877 RecordSimpleWatchTimeUMA(reported_renderer_type_);
Sergey Ulanovb8656772020-04-21 22:56:143878
3879 if (playback_events_recorder_)
3880 playback_events_recorder_->OnPipelineStatistics(GetPipelineStatistics());
Dan Sanders6edfd782019-08-13 00:13:183881}
3882
[email protected]dbb3ff372019-10-15 19:09:423883GURL WebMediaPlayerImpl::GetSrcAfterRedirects() {
3884 return mb_data_source_ ? mb_data_source_->GetUrlAfterRedirects() : GURL();
3885}
3886
[email protected]96665a82020-01-23 00:35:373887void WebMediaPlayerImpl::UpdateSmoothnessHelper() {
3888 // If the experiment flag is off, then do nothing.
3889 if (!base::FeatureList::IsEnabled(kMediaLearningSmoothnessExperiment))
3890 return;
3891
3892 // If we're paused, or if we can't get all the features, then clear any
3893 // smoothness helper and stop. We'll try to create it later when we're
3894 // playing and have all the features.
3895 if (paused_ || !HasVideo() || pipeline_metadata_.natural_size.IsEmpty() ||
3896 !last_reported_fps_) {
3897 smoothness_helper_.reset();
3898 return;
3899 }
3900
3901 // Fill in features.
3902 // NOTE: this is a very bad way to do this, since it memorizes the order of
3903 // features in the task. However, it'll do for now.
3904 learning::FeatureVector features;
3905 features.push_back(
3906 learning::FeatureValue(pipeline_metadata_.video_decoder_config.codec()));
3907 features.push_back(learning::FeatureValue(
3908 pipeline_metadata_.video_decoder_config.profile()));
3909 features.push_back(
3910 learning::FeatureValue(pipeline_metadata_.natural_size.width()));
3911 features.push_back(learning::FeatureValue(*last_reported_fps_));
3912
3913 // If we have a smoothness helper, and we're not changing the features, then
3914 // do nothing. This prevents restarting the helper for no reason.
3915 if (smoothness_helper_ && features == smoothness_helper_->features())
3916 return;
3917
3918 // Create or restart the smoothness helper with |features|.
[email protected]91379332020-01-26 03:58:203919 smoothness_helper_ = SmoothnessHelper::Create(
Chris Cunningham64546ed2020-02-13 17:28:493920 GetLearningTaskController(learning::tasknames::kConsecutiveBadWindows),
3921 GetLearningTaskController(learning::tasknames::kConsecutiveNNRs),
[email protected]91379332020-01-26 03:58:203922 features, this);
3923}
3924
3925std::unique_ptr<learning::LearningTaskController>
Chris Cunningham64546ed2020-02-13 17:28:493926WebMediaPlayerImpl::GetLearningTaskController(const char* task_name) {
[email protected]91379332020-01-26 03:58:203927 // Get the LearningTaskController for |task_id|.
Chris Cunningham64546ed2020-02-13 17:28:493928 learning::LearningTask task = learning::MediaLearningTasks::Get(task_name);
3929 DCHECK_EQ(task.name, task_name);
[email protected]96665a82020-01-23 00:35:373930
3931 mojo::Remote<media::learning::mojom::LearningTaskController> remote_ltc;
3932 media_metrics_provider_->AcquireLearningTaskController(
3933 task.name, remote_ltc.BindNewPipeAndPassReceiver());
[email protected]91379332020-01-26 03:58:203934 return std::make_unique<learning::MojoLearningTaskController>(
[email protected]96665a82020-01-23 00:35:373935 task, std::move(remote_ltc));
[email protected]96665a82020-01-23 00:35:373936}
3937
Wojciech Dzierżanowski69bb21842020-07-22 14:21:173938bool WebMediaPlayerImpl::HasUnmutedAudio() const {
3939 // Pretend that the media has no audio if it never played unmuted. This is to
3940 // avoid any action related to audible media such as taking audio focus or
3941 // showing a media notification. To preserve a consistent experience, it does
3942 // not apply if a media was audible so the system states do not flicker
3943 // depending on whether the user muted the player.
3944 return HasAudio() && !client_->WasAlwaysMuted();
3945}
3946
acolwell9e0840d2014-09-06 19:01:323947} // namespace media