Removal of UpdateMultivaluedField() as we now don't have multiple values for name...
[chromium-blink-merge.git] / media / blink / webmediaplayer_impl.cc
blobaa7b07333d6e2dee4e05ae0c6a5ebb5a17bc214c
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/blink/webmediaplayer_impl.h"
7 #include <algorithm>
8 #include <cmath>
9 #include <limits>
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/callback_helpers.h"
14 #include "base/debug/alias.h"
15 #include "base/debug/crash_logging.h"
16 #include "base/metrics/histogram.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/synchronization/waitable_event.h"
19 #include "base/thread_task_runner_handle.h"
20 #include "base/trace_event/trace_event.h"
21 #include "cc/blink/web_layer_impl.h"
22 #include "cc/layers/video_layer.h"
23 #include "gpu/blink/webgraphicscontext3d_impl.h"
24 #include "media/audio/null_audio_sink.h"
25 #include "media/base/bind_to_current_loop.h"
26 #include "media/base/cdm_context.h"
27 #include "media/base/limits.h"
28 #include "media/base/media_log.h"
29 #include "media/base/text_renderer.h"
30 #include "media/base/video_frame.h"
31 #include "media/blink/texttrack_impl.h"
32 #include "media/blink/webaudiosourceprovider_impl.h"
33 #include "media/blink/webcontentdecryptionmodule_impl.h"
34 #include "media/blink/webinbandtexttrack_impl.h"
35 #include "media/blink/webmediaplayer_delegate.h"
36 #include "media/blink/webmediaplayer_util.h"
37 #include "media/blink/webmediasource_impl.h"
38 #include "media/filters/chunk_demuxer.h"
39 #include "media/filters/ffmpeg_demuxer.h"
40 #include "third_party/WebKit/public/platform/WebEncryptedMediaTypes.h"
41 #include "third_party/WebKit/public/platform/WebMediaSource.h"
42 #include "third_party/WebKit/public/platform/WebRect.h"
43 #include "third_party/WebKit/public/platform/WebSize.h"
44 #include "third_party/WebKit/public/platform/WebString.h"
45 #include "third_party/WebKit/public/platform/WebURL.h"
46 #include "third_party/WebKit/public/web/WebDocument.h"
47 #include "third_party/WebKit/public/web/WebFrame.h"
48 #include "third_party/WebKit/public/web/WebLocalFrame.h"
49 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
50 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
51 #include "third_party/WebKit/public/web/WebView.h"
53 using blink::WebCanvas;
54 using blink::WebMediaPlayer;
55 using blink::WebRect;
56 using blink::WebSize;
57 using blink::WebString;
59 namespace {
61 // Limits the range of playback rate.
63 // TODO(kylep): Revisit these.
65 // Vista has substantially lower performance than XP or Windows7. If you speed
66 // up a video too much, it can't keep up, and rendering stops updating except on
67 // the time bar. For really high speeds, audio becomes a bottleneck and we just
68 // use up the data we have, which may not achieve the speed requested, but will
69 // not crash the tab.
71 // A very slow speed, ie 0.00000001x, causes the machine to lock up. (It seems
72 // like a busy loop). It gets unresponsive, although its not completely dead.
74 // Also our timers are not very accurate (especially for ogg), which becomes
75 // evident at low speeds and on Vista. Since other speeds are risky and outside
76 // the norms, we think 1/16x to 16x is a safe and useful range for now.
77 const double kMinRate = 0.0625;
78 const double kMaxRate = 16.0;
80 } // namespace
82 namespace media {
84 class BufferedDataSourceHostImpl;
86 #define STATIC_ASSERT_MATCHING_ENUM(name) \
87 static_assert(static_cast<int>(WebMediaPlayer::CORSMode ## name) == \
88 static_cast<int>(BufferedResourceLoader::k ## name), \
89 "mismatching enum values: " #name)
90 STATIC_ASSERT_MATCHING_ENUM(Unspecified);
91 STATIC_ASSERT_MATCHING_ENUM(Anonymous);
92 STATIC_ASSERT_MATCHING_ENUM(UseCredentials);
93 #undef STATIC_ASSERT_MATCHING_ENUM
95 #define BIND_TO_RENDER_LOOP(function) \
96 (DCHECK(main_task_runner_->BelongsToCurrentThread()), \
97 BindToCurrentLoop(base::Bind(function, AsWeakPtr())))
99 #define BIND_TO_RENDER_LOOP1(function, arg1) \
100 (DCHECK(main_task_runner_->BelongsToCurrentThread()), \
101 BindToCurrentLoop(base::Bind(function, AsWeakPtr(), arg1)))
103 WebMediaPlayerImpl::WebMediaPlayerImpl(
104 blink::WebLocalFrame* frame,
105 blink::WebMediaPlayerClient* client,
106 base::WeakPtr<WebMediaPlayerDelegate> delegate,
107 scoped_ptr<RendererFactory> renderer_factory,
108 CdmFactory* cdm_factory,
109 const WebMediaPlayerParams& params)
110 : frame_(frame),
111 network_state_(WebMediaPlayer::NetworkStateEmpty),
112 ready_state_(WebMediaPlayer::ReadyStateHaveNothing),
113 preload_(BufferedDataSource::AUTO),
114 main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
115 media_task_runner_(params.media_task_runner()),
116 media_log_(params.media_log()),
117 pipeline_(media_task_runner_, media_log_.get()),
118 load_type_(LoadTypeURL),
119 opaque_(false),
120 playback_rate_(0.0),
121 paused_(true),
122 seeking_(false),
123 ended_(false),
124 pending_seek_(false),
125 should_notify_time_changed_(false),
126 client_(client),
127 delegate_(delegate),
128 defer_load_cb_(params.defer_load_cb()),
129 context_3d_cb_(params.context_3d_cb()),
130 supports_save_(true),
131 chunk_demuxer_(NULL),
132 // Threaded compositing isn't enabled universally yet.
133 compositor_task_runner_(
134 params.compositor_task_runner()
135 ? params.compositor_task_runner()
136 : base::MessageLoop::current()->task_runner()),
137 compositor_(new VideoFrameCompositor(
138 compositor_task_runner_,
139 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnNaturalSizeChanged),
140 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnOpacityChanged))),
141 encrypted_media_support_(cdm_factory,
142 client,
143 params.media_permission(),
144 base::Bind(&WebMediaPlayerImpl::SetCdm,
145 AsWeakPtr(),
146 base::Bind(&IgnoreCdmAttached))),
147 renderer_factory_(renderer_factory.Pass()) {
148 media_log_->AddEvent(
149 media_log_->CreateEvent(MediaLogEvent::WEBMEDIAPLAYER_CREATED));
151 if (params.initial_cdm()) {
152 SetCdm(base::Bind(&IgnoreCdmAttached),
153 ToWebContentDecryptionModuleImpl(params.initial_cdm())
154 ->GetCdmContext());
157 // TODO(xhwang): When we use an external Renderer, many methods won't work,
158 // e.g. GetCurrentFrameFromCompositor(). See http://crbug.com/434861
160 // Use the null sink if no sink was provided.
161 audio_source_provider_ = new WebAudioSourceProviderImpl(
162 params.audio_renderer_sink().get()
163 ? params.audio_renderer_sink()
164 : new NullAudioSink(media_task_runner_));
167 WebMediaPlayerImpl::~WebMediaPlayerImpl() {
168 client_->setWebLayer(NULL);
170 DCHECK(main_task_runner_->BelongsToCurrentThread());
172 if (delegate_)
173 delegate_->PlayerGone(this);
175 // Abort any pending IO so stopping the pipeline doesn't get blocked.
176 if (data_source_)
177 data_source_->Abort();
178 if (chunk_demuxer_) {
179 chunk_demuxer_->Shutdown();
180 chunk_demuxer_ = NULL;
183 renderer_factory_.reset();
185 // Make sure to kill the pipeline so there's no more media threads running.
186 // Note: stopping the pipeline might block for a long time.
187 base::WaitableEvent waiter(false, false);
188 pipeline_.Stop(
189 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&waiter)));
190 waiter.Wait();
192 compositor_task_runner_->DeleteSoon(FROM_HERE, compositor_);
194 media_log_->AddEvent(
195 media_log_->CreateEvent(MediaLogEvent::WEBMEDIAPLAYER_DESTROYED));
198 void WebMediaPlayerImpl::load(LoadType load_type, const blink::WebURL& url,
199 CORSMode cors_mode) {
200 DVLOG(1) << __FUNCTION__ << "(" << load_type << ", " << url << ", "
201 << cors_mode << ")";
202 if (!defer_load_cb_.is_null()) {
203 defer_load_cb_.Run(base::Bind(
204 &WebMediaPlayerImpl::DoLoad, AsWeakPtr(), load_type, url, cors_mode));
205 return;
207 DoLoad(load_type, url, cors_mode);
210 void WebMediaPlayerImpl::DoLoad(LoadType load_type,
211 const blink::WebURL& url,
212 CORSMode cors_mode) {
213 DCHECK(main_task_runner_->BelongsToCurrentThread());
215 GURL gurl(url);
216 ReportMetrics(load_type, gurl,
217 GURL(frame_->document().securityOrigin().toString()));
219 // Set subresource URL for crash reporting.
220 base::debug::SetCrashKeyValue("subresource_url", gurl.spec());
222 load_type_ = load_type;
224 SetNetworkState(WebMediaPlayer::NetworkStateLoading);
225 SetReadyState(WebMediaPlayer::ReadyStateHaveNothing);
226 media_log_->AddEvent(media_log_->CreateLoadEvent(url.spec()));
228 // Media source pipelines can start immediately.
229 if (load_type == LoadTypeMediaSource) {
230 supports_save_ = false;
231 StartPipeline();
232 return;
235 // Otherwise it's a regular request which requires resolving the URL first.
236 data_source_.reset(new BufferedDataSource(
237 url,
238 static_cast<BufferedResourceLoader::CORSMode>(cors_mode),
239 main_task_runner_,
240 frame_,
241 media_log_.get(),
242 &buffered_data_source_host_,
243 base::Bind(&WebMediaPlayerImpl::NotifyDownloading, AsWeakPtr())));
244 data_source_->SetPreload(preload_);
245 data_source_->Initialize(
246 base::Bind(&WebMediaPlayerImpl::DataSourceInitialized, AsWeakPtr()));
249 void WebMediaPlayerImpl::play() {
250 DVLOG(1) << __FUNCTION__;
251 DCHECK(main_task_runner_->BelongsToCurrentThread());
253 paused_ = false;
254 pipeline_.SetPlaybackRate(playback_rate_);
255 if (data_source_)
256 data_source_->MediaIsPlaying();
258 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::PLAY));
260 if (delegate_ && playback_rate_ > 0)
261 delegate_->DidPlay(this);
264 void WebMediaPlayerImpl::pause() {
265 DVLOG(1) << __FUNCTION__;
266 DCHECK(main_task_runner_->BelongsToCurrentThread());
268 const bool was_already_paused = paused_ || playback_rate_ == 0;
269 paused_ = true;
270 pipeline_.SetPlaybackRate(0.0);
271 if (data_source_)
272 data_source_->MediaIsPaused();
273 UpdatePausedTime();
275 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::PAUSE));
277 if (!was_already_paused && delegate_)
278 delegate_->DidPause(this);
281 bool WebMediaPlayerImpl::supportsSave() const {
282 DCHECK(main_task_runner_->BelongsToCurrentThread());
283 return supports_save_;
286 void WebMediaPlayerImpl::seek(double seconds) {
287 DVLOG(1) << __FUNCTION__ << "(" << seconds << "s)";
288 DCHECK(main_task_runner_->BelongsToCurrentThread());
290 ended_ = false;
292 ReadyState old_state = ready_state_;
293 if (ready_state_ > WebMediaPlayer::ReadyStateHaveMetadata)
294 SetReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
296 base::TimeDelta new_seek_time = ConvertSecondsToTimestamp(seconds);
298 if (seeking_) {
299 if (new_seek_time == seek_time_) {
300 if (chunk_demuxer_) {
301 if (!pending_seek_) {
302 // If using media source demuxer, only suppress redundant seeks if
303 // there is no pending seek. This enforces that any pending seek that
304 // results in a demuxer seek is preceded by matching
305 // CancelPendingSeek() and StartWaitingForSeek() calls.
306 return;
308 } else {
309 // Suppress all redundant seeks if unrestricted by media source demuxer
310 // API.
311 pending_seek_ = false;
312 pending_seek_time_ = base::TimeDelta();
313 return;
317 pending_seek_ = true;
318 pending_seek_time_ = new_seek_time;
319 if (chunk_demuxer_)
320 chunk_demuxer_->CancelPendingSeek(pending_seek_time_);
321 return;
324 media_log_->AddEvent(media_log_->CreateSeekEvent(seconds));
326 // Update our paused time.
327 // In paused state ignore the seek operations to current time if the loading
328 // is completed and generate OnPipelineBufferingStateChanged event to
329 // eventually fire seeking and seeked events
330 if (paused_) {
331 if (paused_time_ != new_seek_time) {
332 paused_time_ = new_seek_time;
333 } else if (old_state == ReadyStateHaveEnoughData) {
334 main_task_runner_->PostTask(
335 FROM_HERE,
336 base::Bind(&WebMediaPlayerImpl::OnPipelineBufferingStateChanged,
337 AsWeakPtr(), BUFFERING_HAVE_ENOUGH));
338 return;
342 seeking_ = true;
343 seek_time_ = new_seek_time;
345 if (chunk_demuxer_)
346 chunk_demuxer_->StartWaitingForSeek(seek_time_);
348 // Kick off the asynchronous seek!
349 pipeline_.Seek(seek_time_, BIND_TO_RENDER_LOOP1(
350 &WebMediaPlayerImpl::OnPipelineSeeked, true));
353 void WebMediaPlayerImpl::setRate(double rate) {
354 DVLOG(1) << __FUNCTION__ << "(" << rate << ")";
355 DCHECK(main_task_runner_->BelongsToCurrentThread());
357 // TODO(kylep): Remove when support for negatives is added. Also, modify the
358 // following checks so rewind uses reasonable values also.
359 if (rate < 0.0)
360 return;
362 // Limit rates to reasonable values by clamping.
363 if (rate != 0.0) {
364 if (rate < kMinRate)
365 rate = kMinRate;
366 else if (rate > kMaxRate)
367 rate = kMaxRate;
368 if (playback_rate_ == 0 && !paused_ && delegate_)
369 delegate_->DidPlay(this);
370 } else if (playback_rate_ != 0 && !paused_ && delegate_) {
371 delegate_->DidPause(this);
374 playback_rate_ = rate;
375 if (!paused_) {
376 pipeline_.SetPlaybackRate(rate);
377 if (data_source_)
378 data_source_->MediaPlaybackRateChanged(rate);
382 void WebMediaPlayerImpl::setVolume(double volume) {
383 DVLOG(1) << __FUNCTION__ << "(" << volume << ")";
384 DCHECK(main_task_runner_->BelongsToCurrentThread());
386 pipeline_.SetVolume(volume);
389 void WebMediaPlayerImpl::setSinkId(const blink::WebString& device_id,
390 WebSetSinkIdCB* web_callbacks) {
391 DCHECK(main_task_runner_->BelongsToCurrentThread());
392 std::string device_id_str(device_id.utf8());
393 GURL security_origin(frame_->securityOrigin().toString().utf8());
394 DVLOG(1) << __FUNCTION__
395 << "(" << device_id_str << ", " << security_origin << ")";
396 audio_source_provider_->SwitchOutputDevice(
397 device_id_str, security_origin,
398 ConvertToSwitchOutputDeviceCB(web_callbacks));
401 #define STATIC_ASSERT_MATCHING_ENUM(webkit_name, chromium_name) \
402 static_assert(static_cast<int>(WebMediaPlayer::webkit_name) == \
403 static_cast<int>(BufferedDataSource::chromium_name), \
404 "mismatching enum values: " #webkit_name)
405 STATIC_ASSERT_MATCHING_ENUM(PreloadNone, NONE);
406 STATIC_ASSERT_MATCHING_ENUM(PreloadMetaData, METADATA);
407 STATIC_ASSERT_MATCHING_ENUM(PreloadAuto, AUTO);
408 #undef STATIC_ASSERT_MATCHING_ENUM
410 void WebMediaPlayerImpl::setPreload(WebMediaPlayer::Preload preload) {
411 DVLOG(1) << __FUNCTION__ << "(" << preload << ")";
412 DCHECK(main_task_runner_->BelongsToCurrentThread());
414 preload_ = static_cast<BufferedDataSource::Preload>(preload);
415 if (data_source_)
416 data_source_->SetPreload(preload_);
419 bool WebMediaPlayerImpl::hasVideo() const {
420 DCHECK(main_task_runner_->BelongsToCurrentThread());
422 return pipeline_metadata_.has_video;
425 bool WebMediaPlayerImpl::hasAudio() const {
426 DCHECK(main_task_runner_->BelongsToCurrentThread());
428 return pipeline_metadata_.has_audio;
431 blink::WebSize WebMediaPlayerImpl::naturalSize() const {
432 DCHECK(main_task_runner_->BelongsToCurrentThread());
434 return blink::WebSize(pipeline_metadata_.natural_size);
437 bool WebMediaPlayerImpl::paused() const {
438 DCHECK(main_task_runner_->BelongsToCurrentThread());
440 return pipeline_.GetPlaybackRate() == 0.0f;
443 bool WebMediaPlayerImpl::seeking() const {
444 DCHECK(main_task_runner_->BelongsToCurrentThread());
446 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
447 return false;
449 return seeking_;
452 double WebMediaPlayerImpl::duration() const {
453 DCHECK(main_task_runner_->BelongsToCurrentThread());
455 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
456 return std::numeric_limits<double>::quiet_NaN();
458 return GetPipelineDuration();
461 double WebMediaPlayerImpl::timelineOffset() const {
462 DCHECK(main_task_runner_->BelongsToCurrentThread());
464 if (pipeline_metadata_.timeline_offset.is_null())
465 return std::numeric_limits<double>::quiet_NaN();
467 return pipeline_metadata_.timeline_offset.ToJsTime();
470 double WebMediaPlayerImpl::currentTime() const {
471 DCHECK(main_task_runner_->BelongsToCurrentThread());
472 DCHECK_NE(ready_state_, WebMediaPlayer::ReadyStateHaveNothing);
474 // TODO(scherkus): Replace with an explicit ended signal to HTMLMediaElement,
475 // see http://crbug.com/409280
476 if (ended_)
477 return duration();
479 // We know the current seek time better than pipeline: pipeline may processing
480 // an earlier seek before a pending seek has been started, or it might not yet
481 // have the current seek time returnable via GetMediaTime().
482 if (seeking()) {
483 return pending_seek_ ? pending_seek_time_.InSecondsF()
484 : seek_time_.InSecondsF();
487 return (paused_ ? paused_time_ : pipeline_.GetMediaTime()).InSecondsF();
490 WebMediaPlayer::NetworkState WebMediaPlayerImpl::networkState() const {
491 DCHECK(main_task_runner_->BelongsToCurrentThread());
492 return network_state_;
495 WebMediaPlayer::ReadyState WebMediaPlayerImpl::readyState() const {
496 DCHECK(main_task_runner_->BelongsToCurrentThread());
497 return ready_state_;
500 blink::WebTimeRanges WebMediaPlayerImpl::buffered() const {
501 DCHECK(main_task_runner_->BelongsToCurrentThread());
503 Ranges<base::TimeDelta> buffered_time_ranges =
504 pipeline_.GetBufferedTimeRanges();
506 const base::TimeDelta duration = pipeline_.GetMediaDuration();
507 if (duration != kInfiniteDuration()) {
508 buffered_data_source_host_.AddBufferedTimeRanges(
509 &buffered_time_ranges, duration);
511 return ConvertToWebTimeRanges(buffered_time_ranges);
514 blink::WebTimeRanges WebMediaPlayerImpl::seekable() const {
515 DCHECK(main_task_runner_->BelongsToCurrentThread());
517 if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata)
518 return blink::WebTimeRanges();
520 const double seekable_end = duration();
522 // Allow a special exception for seeks to zero for streaming sources with a
523 // finite duration; this allows looping to work.
524 const bool allow_seek_to_zero = data_source_ && data_source_->IsStreaming() &&
525 std::isfinite(seekable_end);
527 // TODO(dalecurtis): Technically this allows seeking on media which return an
528 // infinite duration so long as DataSource::IsStreaming() is false. While not
529 // expected, disabling this breaks semi-live players, http://crbug.com/427412.
530 const blink::WebTimeRange seekable_range(
531 0.0, allow_seek_to_zero ? 0.0 : seekable_end);
532 return blink::WebTimeRanges(&seekable_range, 1);
535 bool WebMediaPlayerImpl::didLoadingProgress() {
536 DCHECK(main_task_runner_->BelongsToCurrentThread());
537 bool pipeline_progress = pipeline_.DidLoadingProgress();
538 bool data_progress = buffered_data_source_host_.DidLoadingProgress();
539 return pipeline_progress || data_progress;
542 void WebMediaPlayerImpl::paint(blink::WebCanvas* canvas,
543 const blink::WebRect& rect,
544 unsigned char alpha,
545 SkXfermode::Mode mode) {
546 DCHECK(main_task_runner_->BelongsToCurrentThread());
547 TRACE_EVENT0("media", "WebMediaPlayerImpl:paint");
549 // TODO(scherkus): Clarify paint() API contract to better understand when and
550 // why it's being called. For example, today paint() is called when:
551 // - We haven't reached HAVE_CURRENT_DATA and need to paint black
552 // - We're painting to a canvas
553 // See http://crbug.com/341225 http://crbug.com/342621 for details.
554 scoped_refptr<VideoFrame> video_frame = GetCurrentFrameFromCompositor();
556 gfx::Rect gfx_rect(rect);
557 Context3D context_3d;
558 if (video_frame.get() && video_frame->HasTextures()) {
559 if (!context_3d_cb_.is_null())
560 context_3d = context_3d_cb_.Run();
561 // GPU Process crashed.
562 if (!context_3d.gl)
563 return;
565 skcanvas_video_renderer_.Paint(video_frame, canvas, gfx_rect, alpha, mode,
566 pipeline_metadata_.video_rotation, context_3d);
569 bool WebMediaPlayerImpl::hasSingleSecurityOrigin() const {
570 if (data_source_)
571 return data_source_->HasSingleOrigin();
572 return true;
575 bool WebMediaPlayerImpl::didPassCORSAccessCheck() const {
576 if (data_source_)
577 return data_source_->DidPassCORSAccessCheck();
578 return false;
581 double WebMediaPlayerImpl::mediaTimeForTimeValue(double timeValue) const {
582 return ConvertSecondsToTimestamp(timeValue).InSecondsF();
585 unsigned WebMediaPlayerImpl::decodedFrameCount() const {
586 DCHECK(main_task_runner_->BelongsToCurrentThread());
588 PipelineStatistics stats = pipeline_.GetStatistics();
589 return stats.video_frames_decoded;
592 unsigned WebMediaPlayerImpl::droppedFrameCount() const {
593 DCHECK(main_task_runner_->BelongsToCurrentThread());
595 PipelineStatistics stats = pipeline_.GetStatistics();
596 return stats.video_frames_dropped;
599 unsigned WebMediaPlayerImpl::audioDecodedByteCount() const {
600 DCHECK(main_task_runner_->BelongsToCurrentThread());
602 PipelineStatistics stats = pipeline_.GetStatistics();
603 return stats.audio_bytes_decoded;
606 unsigned WebMediaPlayerImpl::videoDecodedByteCount() const {
607 DCHECK(main_task_runner_->BelongsToCurrentThread());
609 PipelineStatistics stats = pipeline_.GetStatistics();
610 return stats.video_bytes_decoded;
613 bool WebMediaPlayerImpl::copyVideoTextureToPlatformTexture(
614 blink::WebGraphicsContext3D* web_graphics_context,
615 unsigned int texture,
616 unsigned int internal_format,
617 unsigned int type,
618 bool premultiply_alpha,
619 bool flip_y) {
620 TRACE_EVENT0("media", "WebMediaPlayerImpl:copyVideoTextureToPlatformTexture");
622 scoped_refptr<VideoFrame> video_frame = GetCurrentFrameFromCompositor();
624 if (!video_frame.get() || !video_frame->HasTextures() ||
625 media::VideoFrame::NumPlanes(video_frame->format()) != 1) {
626 return false;
629 // TODO(dshwang): need more elegant way to convert WebGraphicsContext3D to
630 // GLES2Interface.
631 gpu::gles2::GLES2Interface* gl =
632 static_cast<gpu_blink::WebGraphicsContext3DImpl*>(web_graphics_context)
633 ->GetGLInterface();
634 SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture(
635 gl, video_frame.get(), texture, internal_format, type, premultiply_alpha,
636 flip_y);
637 return true;
640 WebMediaPlayer::MediaKeyException
641 WebMediaPlayerImpl::generateKeyRequest(const WebString& key_system,
642 const unsigned char* init_data,
643 unsigned init_data_length) {
644 DCHECK(main_task_runner_->BelongsToCurrentThread());
646 return encrypted_media_support_.GenerateKeyRequest(
647 frame_, key_system, init_data, init_data_length);
650 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::addKey(
651 const WebString& key_system,
652 const unsigned char* key,
653 unsigned key_length,
654 const unsigned char* init_data,
655 unsigned init_data_length,
656 const WebString& session_id) {
657 DCHECK(main_task_runner_->BelongsToCurrentThread());
659 return encrypted_media_support_.AddKey(
660 key_system, key, key_length, init_data, init_data_length, session_id);
663 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::cancelKeyRequest(
664 const WebString& key_system,
665 const WebString& session_id) {
666 DCHECK(main_task_runner_->BelongsToCurrentThread());
668 return encrypted_media_support_.CancelKeyRequest(key_system, session_id);
671 void WebMediaPlayerImpl::setContentDecryptionModule(
672 blink::WebContentDecryptionModule* cdm,
673 blink::WebContentDecryptionModuleResult result) {
674 DCHECK(main_task_runner_->BelongsToCurrentThread());
676 // Once the CDM is set it can't be cleared as there may be frames being
677 // decrypted on other threads. So fail this request.
678 // http://crbug.com/462365#c7.
679 if (!cdm) {
680 result.completeWithError(
681 blink::WebContentDecryptionModuleExceptionInvalidStateError, 0,
682 "The existing MediaKeys object cannot be removed at this time.");
683 return;
686 SetCdm(BIND_TO_RENDER_LOOP1(&WebMediaPlayerImpl::OnCdmAttached, result),
687 ToWebContentDecryptionModuleImpl(cdm)->GetCdmContext());
690 void WebMediaPlayerImpl::OnEncryptedMediaInitData(
691 EmeInitDataType init_data_type,
692 const std::vector<uint8>& init_data) {
693 DCHECK(init_data_type != EmeInitDataType::UNKNOWN);
695 // Do not fire "encrypted" event if encrypted media is not enabled.
696 // TODO(xhwang): Handle this in |client_|.
697 if (!blink::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled() &&
698 !blink::WebRuntimeFeatures::isEncryptedMediaEnabled()) {
699 return;
702 // TODO(xhwang): Update this UMA name.
703 UMA_HISTOGRAM_COUNTS("Media.EME.NeedKey", 1);
705 encrypted_media_support_.SetInitDataType(init_data_type);
707 client_->encrypted(ConvertToWebInitDataType(init_data_type),
708 vector_as_array(&init_data),
709 base::saturated_cast<unsigned int>(init_data.size()));
712 void WebMediaPlayerImpl::OnWaitingForDecryptionKey() {
713 client_->didBlockPlaybackWaitingForKey();
715 // TODO(jrummell): didResumePlaybackBlockedForKey() should only be called
716 // when a key has been successfully added (e.g. OnSessionKeysChange() with
717 // |has_additional_usable_key| = true). http://crbug.com/461903
718 client_->didResumePlaybackBlockedForKey();
721 void WebMediaPlayerImpl::SetCdm(const CdmAttachedCB& cdm_attached_cb,
722 CdmContext* cdm_context) {
723 // If CDM initialization succeeded, tell the pipeline about it.
724 if (cdm_context)
725 pipeline_.SetCdm(cdm_context, cdm_attached_cb);
728 void WebMediaPlayerImpl::OnCdmAttached(
729 blink::WebContentDecryptionModuleResult result,
730 bool success) {
731 if (success) {
732 result.complete();
733 return;
736 result.completeWithError(
737 blink::WebContentDecryptionModuleExceptionNotSupportedError, 0,
738 "Unable to set MediaKeys object");
741 void WebMediaPlayerImpl::OnPipelineSeeked(bool time_changed,
742 PipelineStatus status) {
743 DVLOG(1) << __FUNCTION__ << "(" << time_changed << ", " << status << ")";
744 DCHECK(main_task_runner_->BelongsToCurrentThread());
745 seeking_ = false;
746 seek_time_ = base::TimeDelta();
747 if (pending_seek_) {
748 double pending_seek_seconds = pending_seek_time_.InSecondsF();
749 pending_seek_ = false;
750 pending_seek_time_ = base::TimeDelta();
751 seek(pending_seek_seconds);
752 return;
755 if (status != PIPELINE_OK) {
756 OnPipelineError(status);
757 return;
760 // Update our paused time.
761 if (paused_)
762 UpdatePausedTime();
764 should_notify_time_changed_ = time_changed;
767 void WebMediaPlayerImpl::OnPipelineEnded() {
768 DVLOG(1) << __FUNCTION__;
769 DCHECK(main_task_runner_->BelongsToCurrentThread());
771 // Ignore state changes until we've completed all outstanding seeks.
772 if (seeking_ || pending_seek_)
773 return;
775 ended_ = true;
776 client_->timeChanged();
779 void WebMediaPlayerImpl::OnPipelineError(PipelineStatus error) {
780 DCHECK(main_task_runner_->BelongsToCurrentThread());
781 DCHECK_NE(error, PIPELINE_OK);
783 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing) {
784 // Any error that occurs before reaching ReadyStateHaveMetadata should
785 // be considered a format error.
786 SetNetworkState(WebMediaPlayer::NetworkStateFormatError);
787 return;
790 SetNetworkState(PipelineErrorToNetworkState(error));
793 void WebMediaPlayerImpl::OnPipelineMetadata(
794 PipelineMetadata metadata) {
795 DVLOG(1) << __FUNCTION__;
797 pipeline_metadata_ = metadata;
799 UMA_HISTOGRAM_ENUMERATION("Media.VideoRotation", metadata.video_rotation,
800 VIDEO_ROTATION_MAX + 1);
801 SetReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
803 if (hasVideo()) {
804 DCHECK(!video_weblayer_);
805 scoped_refptr<cc::VideoLayer> layer =
806 cc::VideoLayer::Create(cc_blink::WebLayerImpl::LayerSettings(),
807 compositor_, pipeline_metadata_.video_rotation);
809 if (pipeline_metadata_.video_rotation == VIDEO_ROTATION_90 ||
810 pipeline_metadata_.video_rotation == VIDEO_ROTATION_270) {
811 gfx::Size size = pipeline_metadata_.natural_size;
812 pipeline_metadata_.natural_size = gfx::Size(size.height(), size.width());
815 video_weblayer_.reset(new cc_blink::WebLayerImpl(layer));
816 video_weblayer_->setOpaque(opaque_);
817 client_->setWebLayer(video_weblayer_.get());
821 void WebMediaPlayerImpl::OnPipelineBufferingStateChanged(
822 BufferingState buffering_state) {
823 DVLOG(1) << __FUNCTION__ << "(" << buffering_state << ")";
825 // Ignore buffering state changes until we've completed all outstanding seeks.
826 if (seeking_ || pending_seek_)
827 return;
829 // TODO(scherkus): Handle other buffering states when Pipeline starts using
830 // them and translate them ready state changes http://crbug.com/144683
831 DCHECK_EQ(buffering_state, BUFFERING_HAVE_ENOUGH);
832 SetReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
834 // Let the DataSource know we have enough data. It may use this information to
835 // release unused network connections.
836 if (data_source_)
837 data_source_->OnBufferingHaveEnough();
839 // Blink expects a timeChanged() in response to a seek().
840 if (should_notify_time_changed_)
841 client_->timeChanged();
844 void WebMediaPlayerImpl::OnDemuxerOpened() {
845 DCHECK(main_task_runner_->BelongsToCurrentThread());
846 client_->mediaSourceOpened(new WebMediaSourceImpl(
847 chunk_demuxer_, base::Bind(&MediaLog::AddLogEvent, media_log_)));
850 void WebMediaPlayerImpl::OnAddTextTrack(
851 const TextTrackConfig& config,
852 const AddTextTrackDoneCB& done_cb) {
853 DCHECK(main_task_runner_->BelongsToCurrentThread());
855 const WebInbandTextTrackImpl::Kind web_kind =
856 static_cast<WebInbandTextTrackImpl::Kind>(config.kind());
857 const blink::WebString web_label =
858 blink::WebString::fromUTF8(config.label());
859 const blink::WebString web_language =
860 blink::WebString::fromUTF8(config.language());
861 const blink::WebString web_id =
862 blink::WebString::fromUTF8(config.id());
864 scoped_ptr<WebInbandTextTrackImpl> web_inband_text_track(
865 new WebInbandTextTrackImpl(web_kind, web_label, web_language, web_id));
867 scoped_ptr<TextTrack> text_track(new TextTrackImpl(
868 main_task_runner_, client_, web_inband_text_track.Pass()));
870 done_cb.Run(text_track.Pass());
873 void WebMediaPlayerImpl::DataSourceInitialized(bool success) {
874 DCHECK(main_task_runner_->BelongsToCurrentThread());
876 if (!success) {
877 SetNetworkState(WebMediaPlayer::NetworkStateFormatError);
878 return;
881 StartPipeline();
884 void WebMediaPlayerImpl::NotifyDownloading(bool is_downloading) {
885 if (!is_downloading && network_state_ == WebMediaPlayer::NetworkStateLoading)
886 SetNetworkState(WebMediaPlayer::NetworkStateIdle);
887 else if (is_downloading && network_state_ == WebMediaPlayer::NetworkStateIdle)
888 SetNetworkState(WebMediaPlayer::NetworkStateLoading);
889 media_log_->AddEvent(
890 media_log_->CreateBooleanEvent(
891 MediaLogEvent::NETWORK_ACTIVITY_SET,
892 "is_downloading_data", is_downloading));
895 void WebMediaPlayerImpl::StartPipeline() {
896 DCHECK(main_task_runner_->BelongsToCurrentThread());
898 Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb =
899 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnEncryptedMediaInitData);
901 // Figure out which demuxer to use.
902 if (load_type_ != LoadTypeMediaSource) {
903 DCHECK(!chunk_demuxer_);
904 DCHECK(data_source_);
906 demuxer_.reset(new FFmpegDemuxer(media_task_runner_, data_source_.get(),
907 encrypted_media_init_data_cb, media_log_));
908 } else {
909 DCHECK(!chunk_demuxer_);
910 DCHECK(!data_source_);
912 chunk_demuxer_ = new ChunkDemuxer(
913 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDemuxerOpened),
914 encrypted_media_init_data_cb,
915 base::Bind(&MediaLog::AddLogEvent, media_log_), media_log_, true);
916 demuxer_.reset(chunk_demuxer_);
919 // ... and we're ready to go!
920 seeking_ = true;
922 pipeline_.Start(
923 demuxer_.get(),
924 renderer_factory_->CreateRenderer(
925 media_task_runner_, audio_source_provider_.get(), compositor_),
926 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineEnded),
927 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineError),
928 BIND_TO_RENDER_LOOP1(&WebMediaPlayerImpl::OnPipelineSeeked, false),
929 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineMetadata),
930 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineBufferingStateChanged),
931 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChanged),
932 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnAddTextTrack),
933 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnWaitingForDecryptionKey));
936 void WebMediaPlayerImpl::SetNetworkState(WebMediaPlayer::NetworkState state) {
937 DVLOG(1) << __FUNCTION__ << "(" << state << ")";
938 DCHECK(main_task_runner_->BelongsToCurrentThread());
939 network_state_ = state;
940 // Always notify to ensure client has the latest value.
941 client_->networkStateChanged();
944 void WebMediaPlayerImpl::SetReadyState(WebMediaPlayer::ReadyState state) {
945 DVLOG(1) << __FUNCTION__ << "(" << state << ")";
946 DCHECK(main_task_runner_->BelongsToCurrentThread());
948 if (state == WebMediaPlayer::ReadyStateHaveEnoughData && data_source_ &&
949 data_source_->assume_fully_buffered() &&
950 network_state_ == WebMediaPlayer::NetworkStateLoading)
951 SetNetworkState(WebMediaPlayer::NetworkStateLoaded);
953 ready_state_ = state;
954 // Always notify to ensure client has the latest value.
955 client_->readyStateChanged();
958 blink::WebAudioSourceProvider* WebMediaPlayerImpl::audioSourceProvider() {
959 return audio_source_provider_.get();
962 double WebMediaPlayerImpl::GetPipelineDuration() const {
963 base::TimeDelta duration = pipeline_.GetMediaDuration();
965 // Return positive infinity if the resource is unbounded.
966 // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#dom-media-duration
967 if (duration == kInfiniteDuration())
968 return std::numeric_limits<double>::infinity();
970 return duration.InSecondsF();
973 void WebMediaPlayerImpl::OnDurationChanged() {
974 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
975 return;
977 client_->durationChanged();
980 void WebMediaPlayerImpl::OnNaturalSizeChanged(gfx::Size size) {
981 DCHECK(main_task_runner_->BelongsToCurrentThread());
982 DCHECK_NE(ready_state_, WebMediaPlayer::ReadyStateHaveNothing);
983 TRACE_EVENT0("media", "WebMediaPlayerImpl::OnNaturalSizeChanged");
985 media_log_->AddEvent(
986 media_log_->CreateVideoSizeSetEvent(size.width(), size.height()));
987 pipeline_metadata_.natural_size = size;
989 client_->sizeChanged();
992 void WebMediaPlayerImpl::OnOpacityChanged(bool opaque) {
993 DCHECK(main_task_runner_->BelongsToCurrentThread());
994 DCHECK_NE(ready_state_, WebMediaPlayer::ReadyStateHaveNothing);
996 opaque_ = opaque;
997 if (video_weblayer_)
998 video_weblayer_->setOpaque(opaque_);
1001 static void GetCurrentFrameAndSignal(
1002 VideoFrameCompositor* compositor,
1003 scoped_refptr<VideoFrame>* video_frame_out,
1004 base::WaitableEvent* event) {
1005 TRACE_EVENT0("media", "GetCurrentFrameAndSignal");
1006 *video_frame_out = compositor->GetCurrentFrameAndUpdateIfStale();
1007 event->Signal();
1010 scoped_refptr<VideoFrame>
1011 WebMediaPlayerImpl::GetCurrentFrameFromCompositor() {
1012 TRACE_EVENT0("media", "WebMediaPlayerImpl::GetCurrentFrameFromCompositor");
1013 if (compositor_task_runner_->BelongsToCurrentThread())
1014 return compositor_->GetCurrentFrameAndUpdateIfStale();
1016 // Use a posted task and waitable event instead of a lock otherwise
1017 // WebGL/Canvas can see different content than what the compositor is seeing.
1018 scoped_refptr<VideoFrame> video_frame;
1019 base::WaitableEvent event(false, false);
1020 compositor_task_runner_->PostTask(FROM_HERE,
1021 base::Bind(&GetCurrentFrameAndSignal,
1022 base::Unretained(compositor_),
1023 &video_frame,
1024 &event));
1025 event.Wait();
1026 return video_frame;
1029 void WebMediaPlayerImpl::UpdatePausedTime() {
1030 DCHECK(main_task_runner_->BelongsToCurrentThread());
1032 // pause() may be called after playback has ended and the HTMLMediaElement
1033 // requires that currentTime() == duration() after ending. We want to ensure
1034 // |paused_time_| matches currentTime() in this case or a future seek() may
1035 // incorrectly discard what it thinks is a seek to the existing time.
1036 paused_time_ =
1037 ended_ ? pipeline_.GetMediaDuration() : pipeline_.GetMediaTime();
1040 } // namespace media