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 "content/renderer/media/webmediaplayer_impl.h"
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "base/callback_helpers.h"
15 #include "base/command_line.h"
16 #include "base/debug/alias.h"
17 #include "base/debug/crash_logging.h"
18 #include "base/debug/trace_event.h"
19 #include "base/message_loop/message_loop_proxy.h"
20 #include "base/metrics/histogram.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/synchronization/waitable_event.h"
23 #include "cc/layers/video_layer.h"
24 #include "content/public/common/content_switches.h"
25 #include "content/public/renderer/render_frame.h"
26 #include "content/renderer/media/buffered_data_source.h"
27 #include "content/renderer/media/crypto/key_systems.h"
28 #include "content/renderer/media/render_media_log.h"
29 #include "content/renderer/media/texttrack_impl.h"
30 #include "content/renderer/media/webaudiosourceprovider_impl.h"
31 #include "content/renderer/media/webcontentdecryptionmodule_impl.h"
32 #include "content/renderer/media/webinbandtexttrack_impl.h"
33 #include "content/renderer/media/webmediaplayer_delegate.h"
34 #include "content/renderer/media/webmediaplayer_params.h"
35 #include "content/renderer/media/webmediaplayer_util.h"
36 #include "content/renderer/media/webmediasource_impl.h"
37 #include "content/renderer/pepper/pepper_webplugin_impl.h"
38 #include "content/renderer/render_thread_impl.h"
39 #include "gpu/GLES2/gl2extchromium.h"
40 #include "gpu/command_buffer/common/mailbox_holder.h"
41 #include "media/audio/null_audio_sink.h"
42 #include "media/base/bind_to_current_loop.h"
43 #include "media/base/filter_collection.h"
44 #include "media/base/limits.h"
45 #include "media/base/media_log.h"
46 #include "media/base/media_switches.h"
47 #include "media/base/pipeline.h"
48 #include "media/base/text_renderer.h"
49 #include "media/base/video_frame.h"
50 #include "media/filters/audio_renderer_impl.h"
51 #include "media/filters/chunk_demuxer.h"
52 #include "media/filters/ffmpeg_audio_decoder.h"
53 #include "media/filters/ffmpeg_demuxer.h"
54 #include "media/filters/ffmpeg_video_decoder.h"
55 #include "media/filters/gpu_video_accelerator_factories.h"
56 #include "media/filters/gpu_video_decoder.h"
57 #include "media/filters/opus_audio_decoder.h"
58 #include "media/filters/video_renderer_impl.h"
59 #include "media/filters/vpx_video_decoder.h"
60 #include "third_party/WebKit/public/platform/WebContentDecryptionModule.h"
61 #include "third_party/WebKit/public/platform/WebMediaSource.h"
62 #include "third_party/WebKit/public/platform/WebRect.h"
63 #include "third_party/WebKit/public/platform/WebSize.h"
64 #include "third_party/WebKit/public/platform/WebString.h"
65 #include "third_party/WebKit/public/platform/WebURL.h"
66 #include "third_party/WebKit/public/web/WebDocument.h"
67 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
68 #include "third_party/WebKit/public/web/WebView.h"
69 #include "v8/include/v8.h"
70 #include "webkit/renderer/compositor_bindings/web_layer_impl.h"
72 using blink::WebCanvas;
73 using blink::WebMediaPlayer;
76 using blink::WebString;
77 using media::PipelineStatus;
81 // Amount of extra memory used by each player instance reported to V8.
82 // It is not exact number -- first, it differs on different platforms,
83 // and second, it is very hard to calculate. Instead, use some arbitrary
84 // value that will cause garbage collection from time to time. We don't want
85 // it to happen on every allocation, but don't want 5k players to sit in memory
86 // either. Looks that chosen constant achieves both goals, at least for audio
87 // objects. (Do not worry about video objects yet, JS programs do not create
88 // thousands of them...)
89 const int kPlayerExtraMemory = 1024 * 1024;
91 // Limits the range of playback rate.
93 // TODO(kylep): Revisit these.
95 // Vista has substantially lower performance than XP or Windows7. If you speed
96 // up a video too much, it can't keep up, and rendering stops updating except on
97 // the time bar. For really high speeds, audio becomes a bottleneck and we just
98 // use up the data we have, which may not achieve the speed requested, but will
101 // A very slow speed, ie 0.00000001x, causes the machine to lock up. (It seems
102 // like a busy loop). It gets unresponsive, although its not completely dead.
104 // Also our timers are not very accurate (especially for ogg), which becomes
105 // evident at low speeds and on Vista. Since other speeds are risky and outside
106 // the norms, we think 1/16x to 16x is a safe and useful range for now.
107 const double kMinRate = 0.0625;
108 const double kMaxRate = 16.0;
110 // Prefix for histograms related to Encrypted Media Extensions.
111 const char* kMediaEme = "Media.EME.";
117 #define COMPILE_ASSERT_MATCHING_ENUM(name) \
118 COMPILE_ASSERT(static_cast<int>(WebMediaPlayer::CORSMode ## name) == \
119 static_cast<int>(BufferedResourceLoader::k ## name), \
121 COMPILE_ASSERT_MATCHING_ENUM(Unspecified);
122 COMPILE_ASSERT_MATCHING_ENUM(Anonymous);
123 COMPILE_ASSERT_MATCHING_ENUM(UseCredentials);
124 #undef COMPILE_ASSERT_MATCHING_ENUM
126 #define BIND_TO_RENDER_LOOP(function) \
127 (DCHECK(main_loop_->BelongsToCurrentThread()), \
128 media::BindToCurrentLoop(base::Bind(function, AsWeakPtr())))
130 static void LogMediaSourceError(const scoped_refptr<media::MediaLog>& media_log,
131 const std::string& error) {
132 media_log->AddEvent(media_log->CreateMediaSourceErrorEvent(error));
135 WebMediaPlayerImpl::WebMediaPlayerImpl(
136 blink::WebFrame* frame,
137 blink::WebMediaPlayerClient* client,
138 base::WeakPtr<WebMediaPlayerDelegate> delegate,
139 const WebMediaPlayerParams& params)
140 : RenderFrameObserver(RenderFrame::FromWebFrame(frame)),
142 network_state_(WebMediaPlayer::NetworkStateEmpty),
143 ready_state_(WebMediaPlayer::ReadyStateHaveNothing),
144 main_loop_(base::MessageLoopProxy::current()),
146 RenderThreadImpl::current()->GetMediaThreadMessageLoopProxy()),
149 playback_rate_(0.0f),
150 pending_seek_(false),
151 pending_seek_seconds_(0.0f),
154 defer_load_cb_(params.defer_load_cb()),
155 media_log_(new RenderMediaLog()),
156 accelerated_compositing_reported_(false),
157 incremented_externally_allocated_memory_(false),
158 gpu_factories_(RenderThreadImpl::current()->GetGpuFactories()),
159 is_local_source_(false),
160 supports_save_(true),
162 chunk_demuxer_(NULL),
164 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::InvalidateOnMainThread),
165 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnNaturalSizeChange)),
166 video_frame_provider_client_(NULL),
167 text_track_index_(0),
169 destroy_reason_(0u) {
170 media_log_->AddEvent(
171 media_log_->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_CREATED));
173 pipeline_.reset(new media::Pipeline(media_loop_, media_log_.get()));
175 // |gpu_factories_| requires that its entry points be called on its
176 // |GetTaskRunner()|. Since |pipeline_| will own decoders created from the
177 // factories, require that their message loops are identical.
178 DCHECK(!gpu_factories_ || (gpu_factories_->GetTaskRunner() == media_loop_));
180 // Let V8 know we started new thread if we did not do it yet.
181 // Made separate task to avoid deletion of player currently being created.
182 // Also, delaying GC until after player starts gets rid of starting lag --
183 // collection happens in parallel with playing.
185 // TODO(enal): remove when we get rid of per-audio-stream thread.
186 main_loop_->PostTask(
188 base::Bind(&WebMediaPlayerImpl::IncrementExternallyAllocatedMemory,
191 // Use the null sink if no sink was provided.
192 audio_source_provider_ = new WebAudioSourceProviderImpl(
193 params.audio_renderer_sink().get()
194 ? params.audio_renderer_sink()
195 : new media::NullAudioSink(media_loop_));
198 WebMediaPlayerImpl::~WebMediaPlayerImpl() {
199 SetVideoFrameProviderClient(NULL);
200 client_->setWebLayer(NULL);
202 DCHECK(main_loop_->BelongsToCurrentThread());
203 media_log_->AddEvent(
204 media_log_->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_DESTROYED));
207 delegate_->PlayerGone(this);
209 Destroy(WEBMEDIAPLAYER_DESTROYED);
214 // Helper enum for reporting scheme histograms.
215 enum URLSchemeForHistogram {
221 kChromeExtensionURLScheme,
222 kJavascriptURLScheme,
227 kMaxURLScheme = kFileSystemScheme // Must be equal to highest enum value.
230 URLSchemeForHistogram URLScheme(const GURL& url) {
231 if (!url.has_scheme()) return kMissingURLScheme;
232 if (url.SchemeIs("http")) return kHttpURLScheme;
233 if (url.SchemeIs("https")) return kHttpsURLScheme;
234 if (url.SchemeIs("ftp")) return kFtpURLScheme;
235 if (url.SchemeIs("chrome-extension")) return kChromeExtensionURLScheme;
236 if (url.SchemeIs("javascript")) return kJavascriptURLScheme;
237 if (url.SchemeIs("file")) return kFileURLScheme;
238 if (url.SchemeIs("blob")) return kBlobURLScheme;
239 if (url.SchemeIs("data")) return kDataURLScheme;
240 if (url.SchemeIs("filesystem")) return kFileSystemScheme;
241 return kUnknownURLScheme;
246 void WebMediaPlayerImpl::load(LoadType load_type, const blink::WebURL& url,
247 CORSMode cors_mode) {
248 if (!defer_load_cb_.is_null()) {
249 defer_load_cb_.Run(base::Bind(
250 &WebMediaPlayerImpl::DoLoad, AsWeakPtr(), load_type, url, cors_mode));
253 DoLoad(load_type, url, cors_mode);
256 void WebMediaPlayerImpl::DoLoad(LoadType load_type,
257 const blink::WebURL& url,
258 CORSMode cors_mode) {
259 DCHECK(main_loop_->BelongsToCurrentThread());
262 UMA_HISTOGRAM_ENUMERATION("Media.URLScheme", URLScheme(gurl), kMaxURLScheme);
264 // Set subresource URL for crash reporting.
265 base::debug::SetCrashKeyValue("subresource_url", gurl.spec());
267 load_type_ = load_type;
269 // Handle any volume/preload changes that occurred before load().
270 setVolume(client_->volume());
271 setPreload(client_->preload());
273 SetNetworkState(WebMediaPlayer::NetworkStateLoading);
274 SetReadyState(WebMediaPlayer::ReadyStateHaveNothing);
275 media_log_->AddEvent(media_log_->CreateLoadEvent(url.spec()));
277 // Media source pipelines can start immediately.
278 if (load_type == LoadTypeMediaSource) {
279 supports_save_ = false;
284 // Otherwise it's a regular request which requires resolving the URL first.
285 data_source_.reset(new BufferedDataSource(
289 base::Bind(&WebMediaPlayerImpl::NotifyDownloading, AsWeakPtr())));
290 data_source_->Initialize(
291 url, static_cast<BufferedResourceLoader::CORSMode>(cors_mode),
293 &WebMediaPlayerImpl::DataSourceInitialized,
296 is_local_source_ = !gurl.SchemeIsHTTPOrHTTPS();
299 void WebMediaPlayerImpl::play() {
300 DCHECK(main_loop_->BelongsToCurrentThread());
303 pipeline_->SetPlaybackRate(playback_rate_);
305 data_source_->MediaIsPlaying();
307 media_log_->AddEvent(media_log_->CreateEvent(media::MediaLogEvent::PLAY));
310 delegate_->DidPlay(this);
313 void WebMediaPlayerImpl::pause() {
314 DCHECK(main_loop_->BelongsToCurrentThread());
317 pipeline_->SetPlaybackRate(0.0f);
319 data_source_->MediaIsPaused();
320 paused_time_ = pipeline_->GetMediaTime();
322 media_log_->AddEvent(media_log_->CreateEvent(media::MediaLogEvent::PAUSE));
325 delegate_->DidPause(this);
328 bool WebMediaPlayerImpl::supportsSave() const {
329 DCHECK(main_loop_->BelongsToCurrentThread());
330 return supports_save_;
333 void WebMediaPlayerImpl::seek(double seconds) {
334 DCHECK(main_loop_->BelongsToCurrentThread());
336 if (ready_state_ > WebMediaPlayer::ReadyStateHaveMetadata)
337 SetReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
339 base::TimeDelta seek_time = ConvertSecondsToTimestamp(seconds);
341 if (starting_ || seeking_) {
342 pending_seek_ = true;
343 pending_seek_seconds_ = seconds;
345 chunk_demuxer_->CancelPendingSeek(seek_time);
349 media_log_->AddEvent(media_log_->CreateSeekEvent(seconds));
351 // Update our paused time.
353 paused_time_ = seek_time;
358 chunk_demuxer_->StartWaitingForSeek(seek_time);
360 // Kick off the asynchronous seek!
363 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSeek));
366 void WebMediaPlayerImpl::setRate(double rate) {
367 DCHECK(main_loop_->BelongsToCurrentThread());
369 // TODO(kylep): Remove when support for negatives is added. Also, modify the
370 // following checks so rewind uses reasonable values also.
374 // Limit rates to reasonable values by clamping.
378 else if (rate > kMaxRate)
382 playback_rate_ = rate;
384 pipeline_->SetPlaybackRate(rate);
386 data_source_->MediaPlaybackRateChanged(rate);
390 void WebMediaPlayerImpl::setVolume(double volume) {
391 DCHECK(main_loop_->BelongsToCurrentThread());
393 pipeline_->SetVolume(volume);
396 #define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, chromium_name) \
397 COMPILE_ASSERT(static_cast<int>(WebMediaPlayer::webkit_name) == \
398 static_cast<int>(content::chromium_name), \
400 COMPILE_ASSERT_MATCHING_ENUM(PreloadNone, NONE);
401 COMPILE_ASSERT_MATCHING_ENUM(PreloadMetaData, METADATA);
402 COMPILE_ASSERT_MATCHING_ENUM(PreloadAuto, AUTO);
403 #undef COMPILE_ASSERT_MATCHING_ENUM
405 void WebMediaPlayerImpl::setPreload(WebMediaPlayer::Preload preload) {
406 DCHECK(main_loop_->BelongsToCurrentThread());
409 data_source_->SetPreload(static_cast<content::Preload>(preload));
412 bool WebMediaPlayerImpl::hasVideo() const {
413 DCHECK(main_loop_->BelongsToCurrentThread());
415 return pipeline_->HasVideo();
418 bool WebMediaPlayerImpl::hasAudio() const {
419 DCHECK(main_loop_->BelongsToCurrentThread());
421 return pipeline_->HasAudio();
424 blink::WebSize WebMediaPlayerImpl::naturalSize() const {
425 DCHECK(main_loop_->BelongsToCurrentThread());
427 return blink::WebSize(natural_size_);
430 bool WebMediaPlayerImpl::paused() const {
431 DCHECK(main_loop_->BelongsToCurrentThread());
433 return pipeline_->GetPlaybackRate() == 0.0f;
436 bool WebMediaPlayerImpl::seeking() const {
437 DCHECK(main_loop_->BelongsToCurrentThread());
439 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
445 double WebMediaPlayerImpl::duration() const {
446 DCHECK(main_loop_->BelongsToCurrentThread());
448 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
449 return std::numeric_limits<double>::quiet_NaN();
451 return GetPipelineDuration();
454 double WebMediaPlayerImpl::currentTime() const {
455 DCHECK(main_loop_->BelongsToCurrentThread());
456 return (paused_ ? paused_time_ : pipeline_->GetMediaTime()).InSecondsF();
459 WebMediaPlayer::NetworkState WebMediaPlayerImpl::networkState() const {
460 DCHECK(main_loop_->BelongsToCurrentThread());
461 return network_state_;
464 WebMediaPlayer::ReadyState WebMediaPlayerImpl::readyState() const {
465 DCHECK(main_loop_->BelongsToCurrentThread());
469 const blink::WebTimeRanges& WebMediaPlayerImpl::buffered() {
470 DCHECK(main_loop_->BelongsToCurrentThread());
471 blink::WebTimeRanges web_ranges(
472 ConvertToWebTimeRanges(pipeline_->GetBufferedTimeRanges()));
473 buffered_.swap(web_ranges);
477 double WebMediaPlayerImpl::maxTimeSeekable() const {
478 DCHECK(main_loop_->BelongsToCurrentThread());
480 // If we haven't even gotten to ReadyStateHaveMetadata yet then just
481 // return 0 so that the seekable range is empty.
482 if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata)
485 // We don't support seeking in streaming media.
486 if (data_source_ && data_source_->IsStreaming())
491 bool WebMediaPlayerImpl::didLoadingProgress() const {
492 DCHECK(main_loop_->BelongsToCurrentThread());
494 // TODO(scherkus): Remove after tracking down cause for crashes
495 // http://crbug.com/341184 http://crbug.com/341186
496 uint32 reason = this->destroy_reason_;
497 base::debug::Alias(&reason);
500 return pipeline_->DidLoadingProgress();
503 void WebMediaPlayerImpl::paint(WebCanvas* canvas,
505 unsigned char alpha) {
506 DCHECK(main_loop_->BelongsToCurrentThread());
508 if (!accelerated_compositing_reported_) {
509 accelerated_compositing_reported_ = true;
510 // Normally paint() is only called in non-accelerated rendering, but there
511 // are exceptions such as webgl where compositing is used in the WebView but
512 // video frames are still rendered to a canvas.
513 UMA_HISTOGRAM_BOOLEAN(
514 "Media.AcceleratedCompositingActive",
515 frame_->view()->isAcceleratedCompositingActive());
519 TRACE_EVENT0("media", "WebMediaPlayerImpl:paint");
520 scoped_refptr<media::VideoFrame> video_frame = painter_.GetCurrentFrame(true);
521 gfx::Rect gfx_rect(rect);
522 skcanvas_video_renderer_.Paint(video_frame.get(), canvas, gfx_rect, alpha);
525 bool WebMediaPlayerImpl::hasSingleSecurityOrigin() const {
527 return data_source_->HasSingleOrigin();
531 bool WebMediaPlayerImpl::didPassCORSAccessCheck() const {
533 return data_source_->DidPassCORSAccessCheck();
537 double WebMediaPlayerImpl::mediaTimeForTimeValue(double timeValue) const {
538 return ConvertSecondsToTimestamp(timeValue).InSecondsF();
541 unsigned WebMediaPlayerImpl::decodedFrameCount() const {
542 DCHECK(main_loop_->BelongsToCurrentThread());
544 media::PipelineStatistics stats = pipeline_->GetStatistics();
545 return stats.video_frames_decoded;
548 unsigned WebMediaPlayerImpl::droppedFrameCount() const {
549 DCHECK(main_loop_->BelongsToCurrentThread());
551 media::PipelineStatistics stats = pipeline_->GetStatistics();
553 unsigned frames_dropped = stats.video_frames_dropped +
554 const_cast<media::VideoFramePainter*>(&painter_)
555 ->GetFramesDroppedBeforePaint();
556 DCHECK_LE(frames_dropped, stats.video_frames_decoded);
557 return frames_dropped;
560 unsigned WebMediaPlayerImpl::audioDecodedByteCount() const {
561 DCHECK(main_loop_->BelongsToCurrentThread());
563 media::PipelineStatistics stats = pipeline_->GetStatistics();
564 return stats.audio_bytes_decoded;
567 unsigned WebMediaPlayerImpl::videoDecodedByteCount() const {
568 DCHECK(main_loop_->BelongsToCurrentThread());
570 media::PipelineStatistics stats = pipeline_->GetStatistics();
571 return stats.video_bytes_decoded;
574 void WebMediaPlayerImpl::SetVideoFrameProviderClient(
575 cc::VideoFrameProvider::Client* client) {
576 // This is called from both the main renderer thread and the compositor
577 // thread (when the main thread is blocked).
578 if (video_frame_provider_client_)
579 video_frame_provider_client_->StopUsingProvider();
580 video_frame_provider_client_ = client;
583 scoped_refptr<media::VideoFrame> WebMediaPlayerImpl::GetCurrentFrame() {
584 scoped_refptr<media::VideoFrame> current_frame =
585 painter_.GetCurrentFrame(true);
586 TRACE_EVENT_ASYNC_BEGIN0(
587 "media", "WebMediaPlayerImpl:compositing", this);
588 return current_frame;
591 void WebMediaPlayerImpl::PutCurrentFrame(
592 const scoped_refptr<media::VideoFrame>& frame) {
593 if (!accelerated_compositing_reported_) {
594 accelerated_compositing_reported_ = true;
595 DCHECK(frame_->view()->isAcceleratedCompositingActive());
596 UMA_HISTOGRAM_BOOLEAN("Media.AcceleratedCompositingActive", true);
598 TRACE_EVENT_ASYNC_END0("media", "WebMediaPlayerImpl:compositing", this);
601 bool WebMediaPlayerImpl::copyVideoTextureToPlatformTexture(
602 blink::WebGraphicsContext3D* web_graphics_context,
603 unsigned int texture,
605 unsigned int internal_format,
607 bool premultiply_alpha,
609 scoped_refptr<media::VideoFrame> video_frame =
610 painter_.GetCurrentFrame(false);
612 TRACE_EVENT0("media", "WebMediaPlayerImpl:copyVideoTextureToPlatformTexture");
616 if (video_frame->format() != media::VideoFrame::NATIVE_TEXTURE)
619 gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
620 if (mailbox_holder->texture_target != GL_TEXTURE_2D)
623 // Since this method changes which texture is bound to the TEXTURE_2D target,
624 // ideally it would restore the currently-bound texture before returning.
625 // The cost of getIntegerv is sufficiently high, however, that we want to
626 // avoid it in user builds. As a result assume (below) that |texture| is
627 // bound when this method is called, and only verify this fact when
629 if (DCHECK_IS_ON()) {
630 GLint bound_texture = 0;
631 web_graphics_context->getIntegerv(GL_TEXTURE_BINDING_2D, &bound_texture);
632 DCHECK_EQ(static_cast<GLuint>(bound_texture), texture);
635 uint32 source_texture = web_graphics_context->createTexture();
637 web_graphics_context->waitSyncPoint(mailbox_holder->sync_point);
638 web_graphics_context->bindTexture(GL_TEXTURE_2D, source_texture);
639 web_graphics_context->consumeTextureCHROMIUM(GL_TEXTURE_2D,
640 mailbox_holder->mailbox.name);
642 // The video is stored in a unmultiplied format, so premultiply
644 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
646 // Application itself needs to take care of setting the right flip_y
647 // value down to get the expected result.
648 // flip_y==true means to reverse the video orientation while
649 // flip_y==false means to keep the intrinsic orientation.
650 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y);
651 web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D,
657 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false);
658 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
661 // Restore the state for TEXTURE_2D binding point as mentioned above.
662 web_graphics_context->bindTexture(GL_TEXTURE_2D, texture);
664 web_graphics_context->deleteTexture(source_texture);
666 // The flush() operation is not necessary here. It is kept since the
667 // performance will be better when it is added than not.
668 web_graphics_context->flush();
672 // Helper functions to report media EME related stats to UMA. They follow the
673 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and
674 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is
675 // that UMA_* macros require the names to be constant throughout the process'
677 static void EmeUMAHistogramEnumeration(const blink::WebString& key_system,
678 const std::string& method,
680 int boundary_value) {
681 base::LinearHistogram::FactoryGet(
682 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
683 1, boundary_value, boundary_value + 1,
684 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
687 static void EmeUMAHistogramCounts(const blink::WebString& key_system,
688 const std::string& method,
690 // Use the same parameters as UMA_HISTOGRAM_COUNTS.
691 base::Histogram::FactoryGet(
692 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
693 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
696 // Helper enum for reporting generateKeyRequest/addKey histograms.
697 enum MediaKeyException {
700 kKeySystemNotSupported,
702 kMaxMediaKeyException
705 static MediaKeyException MediaKeyExceptionForUMA(
706 WebMediaPlayer::MediaKeyException e) {
708 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
709 return kKeySystemNotSupported;
710 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
711 return kInvalidPlayerState;
712 case WebMediaPlayer::MediaKeyExceptionNoError:
715 return kUnknownResultId;
719 // Helper for converting |key_system| name and exception |e| to a pair of enum
720 // values from above, for reporting to UMA.
721 static void ReportMediaKeyExceptionToUMA(
722 const std::string& method,
723 const WebString& key_system,
724 WebMediaPlayer::MediaKeyException e) {
725 MediaKeyException result_id = MediaKeyExceptionForUMA(e);
726 DCHECK_NE(result_id, kUnknownResultId) << e;
727 EmeUMAHistogramEnumeration(
728 key_system, method, result_id, kMaxMediaKeyException);
731 WebMediaPlayer::MediaKeyException
732 WebMediaPlayerImpl::generateKeyRequest(const WebString& key_system,
733 const unsigned char* init_data,
734 unsigned init_data_length) {
735 WebMediaPlayer::MediaKeyException e =
736 GenerateKeyRequestInternal(key_system, init_data, init_data_length);
737 ReportMediaKeyExceptionToUMA("generateKeyRequest", key_system, e);
741 WebMediaPlayer::MediaKeyException
742 WebMediaPlayerImpl::GenerateKeyRequestInternal(
743 const WebString& key_system,
744 const unsigned char* init_data,
745 unsigned init_data_length) {
746 DCHECK(main_loop_->BelongsToCurrentThread());
748 DVLOG(1) << "generateKeyRequest: " << key_system.utf8().data() << ": "
749 << std::string(reinterpret_cast<const char*>(init_data),
750 static_cast<size_t>(init_data_length));
752 if (!IsConcreteSupportedKeySystem(key_system))
753 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
755 // We do not support run-time switching between key systems for now.
756 if (current_key_system_.isEmpty()) {
757 if (!proxy_decryptor_) {
758 proxy_decryptor_.reset(new ProxyDecryptor(
759 #if defined(ENABLE_PEPPER_CDMS)
763 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyAdded),
764 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyError),
765 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyMessage)));
768 if (!proxy_decryptor_->InitializeCDM(key_system.utf8(),
769 frame_->document().url()))
770 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
772 if (proxy_decryptor_ && !decryptor_ready_cb_.is_null()) {
773 base::ResetAndReturn(&decryptor_ready_cb_)
774 .Run(proxy_decryptor_->GetDecryptor());
777 current_key_system_ = key_system;
778 } else if (key_system != current_key_system_) {
779 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
782 // TODO(xhwang): We assume all streams are from the same container (thus have
783 // the same "type") for now. In the future, the "type" should be passed down
784 // from the application.
785 if (!proxy_decryptor_->GenerateKeyRequest(
786 init_data_type_, init_data, init_data_length)) {
787 current_key_system_.reset();
788 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
791 return WebMediaPlayer::MediaKeyExceptionNoError;
794 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::addKey(
795 const WebString& key_system,
796 const unsigned char* key,
798 const unsigned char* init_data,
799 unsigned init_data_length,
800 const WebString& session_id) {
801 WebMediaPlayer::MediaKeyException e = AddKeyInternal(
802 key_system, key, key_length, init_data, init_data_length, session_id);
803 ReportMediaKeyExceptionToUMA("addKey", key_system, e);
807 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::AddKeyInternal(
808 const WebString& key_system,
809 const unsigned char* key,
811 const unsigned char* init_data,
812 unsigned init_data_length,
813 const WebString& session_id) {
815 DCHECK_GT(key_length, 0u);
816 DVLOG(1) << "addKey: " << key_system.utf8().data() << ": "
817 << std::string(reinterpret_cast<const char*>(key),
818 static_cast<size_t>(key_length)) << ", "
819 << std::string(reinterpret_cast<const char*>(init_data),
820 static_cast<size_t>(init_data_length))
821 << " [" << session_id.utf8().data() << "]";
824 if (!IsConcreteSupportedKeySystem(key_system))
825 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
827 if (current_key_system_.isEmpty() || key_system != current_key_system_)
828 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
830 proxy_decryptor_->AddKey(
831 key, key_length, init_data, init_data_length, session_id.utf8());
832 return WebMediaPlayer::MediaKeyExceptionNoError;
835 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::cancelKeyRequest(
836 const WebString& key_system,
837 const WebString& session_id) {
838 WebMediaPlayer::MediaKeyException e =
839 CancelKeyRequestInternal(key_system, session_id);
840 ReportMediaKeyExceptionToUMA("cancelKeyRequest", key_system, e);
844 WebMediaPlayer::MediaKeyException
845 WebMediaPlayerImpl::CancelKeyRequestInternal(
846 const WebString& key_system,
847 const WebString& session_id) {
848 if (!IsConcreteSupportedKeySystem(key_system))
849 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
851 if (current_key_system_.isEmpty() || key_system != current_key_system_)
852 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
854 proxy_decryptor_->CancelKeyRequest(session_id.utf8());
855 return WebMediaPlayer::MediaKeyExceptionNoError;
858 void WebMediaPlayerImpl::setContentDecryptionModule(
859 blink::WebContentDecryptionModule* cdm) {
860 DCHECK(main_loop_->BelongsToCurrentThread());
862 // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324
866 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm);
868 if (web_cdm_ && !decryptor_ready_cb_.is_null())
869 base::ResetAndReturn(&decryptor_ready_cb_).Run(web_cdm_->GetDecryptor());
872 void WebMediaPlayerImpl::OnDestruct() {
873 Destroy(RENDERFRAME_DESTROYED);
876 void WebMediaPlayerImpl::InvalidateOnMainThread() {
877 DCHECK(main_loop_->BelongsToCurrentThread());
878 TRACE_EVENT0("media", "WebMediaPlayerImpl::InvalidateOnMainThread");
880 painter_.DidFinishInvalidating();
884 void WebMediaPlayerImpl::OnPipelineSeek(PipelineStatus status) {
885 DCHECK(main_loop_->BelongsToCurrentThread());
889 pending_seek_ = false;
890 seek(pending_seek_seconds_);
894 if (status != media::PIPELINE_OK) {
895 OnPipelineError(status);
899 // Update our paused time.
901 paused_time_ = pipeline_->GetMediaTime();
903 client_->timeChanged();
906 void WebMediaPlayerImpl::OnPipelineEnded() {
907 DCHECK(main_loop_->BelongsToCurrentThread());
908 client_->timeChanged();
911 void WebMediaPlayerImpl::OnPipelineError(PipelineStatus error) {
912 DCHECK(main_loop_->BelongsToCurrentThread());
913 DCHECK_NE(error, media::PIPELINE_OK);
915 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing) {
916 // Any error that occurs before reaching ReadyStateHaveMetadata should
917 // be considered a format error.
918 SetNetworkState(WebMediaPlayer::NetworkStateFormatError);
920 // TODO(scherkus): This should be handled by HTMLMediaElement and controls
921 // should know when to invalidate themselves http://crbug.com/337015
922 InvalidateOnMainThread();
926 SetNetworkState(PipelineErrorToNetworkState(error));
928 if (error == media::PIPELINE_ERROR_DECRYPT)
929 EmeUMAHistogramCounts(current_key_system_, "DecryptError", 1);
931 // TODO(scherkus): This should be handled by HTMLMediaElement and controls
932 // should know when to invalidate themselves http://crbug.com/337015
933 InvalidateOnMainThread();
936 void WebMediaPlayerImpl::OnPipelineBufferingState(
937 media::Pipeline::BufferingState buffering_state) {
938 DVLOG(1) << "OnPipelineBufferingState(" << buffering_state << ")";
940 // TODO(scherkus): Remove after tracking down cause for crashes
941 // http://crbug.com/341184 http://crbug.com/341186
944 switch (buffering_state) {
945 case media::Pipeline::kHaveMetadata:
946 // TODO(scherkus): Would be better to have a metadata changed callback
947 // that contained the size information as well whether audio/video is
948 // present. Doing so would let us remove more methods off Pipeline.
949 natural_size_ = pipeline_->GetInitialNaturalSize();
951 SetReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
953 if (hasVideo() && client_->needsWebLayerForVideo()) {
954 DCHECK(!video_weblayer_);
955 video_weblayer_.reset(
956 new webkit::WebLayerImpl(cc::VideoLayer::Create(this)));
957 client_->setWebLayer(video_weblayer_.get());
960 case media::Pipeline::kPrerollCompleted:
961 // Only transition to ReadyStateHaveEnoughData if we don't have
962 // any pending seeks because the transition can cause Blink to
963 // report that the most recent seek has completed.
965 SetReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
969 // TODO(scherkus): This should be handled by HTMLMediaElement and controls
970 // should know when to invalidate themselves http://crbug.com/337015
971 InvalidateOnMainThread();
974 void WebMediaPlayerImpl::OnDemuxerOpened() {
975 DCHECK(main_loop_->BelongsToCurrentThread());
976 client_->mediaSourceOpened(new WebMediaSourceImpl(
977 chunk_demuxer_, base::Bind(&LogMediaSourceError, media_log_)));
980 void WebMediaPlayerImpl::OnKeyAdded(const std::string& session_id) {
981 DCHECK(main_loop_->BelongsToCurrentThread());
982 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1);
983 client_->keyAdded(current_key_system_, WebString::fromUTF8(session_id));
986 void WebMediaPlayerImpl::OnNeedKey(const std::string& type,
987 const std::vector<uint8>& init_data) {
988 DCHECK(main_loop_->BelongsToCurrentThread());
990 // Do not fire NeedKey event if encrypted media is not enabled.
991 if (!blink::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled() &&
992 !blink::WebRuntimeFeatures::isEncryptedMediaEnabled()) {
996 UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1);
998 DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_);
999 if (init_data_type_.empty())
1000 init_data_type_ = type;
1002 const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
1004 WebString::fromUTF8(type), init_data_ptr, init_data.size());
1007 void WebMediaPlayerImpl::OnAddTextTrack(
1008 const media::TextTrackConfig& config,
1009 const media::AddTextTrackDoneCB& done_cb) {
1010 DCHECK(main_loop_->BelongsToCurrentThread());
1012 const WebInbandTextTrackImpl::Kind web_kind =
1013 static_cast<WebInbandTextTrackImpl::Kind>(config.kind());
1014 const blink::WebString web_label =
1015 blink::WebString::fromUTF8(config.label());
1016 const blink::WebString web_language =
1017 blink::WebString::fromUTF8(config.language());
1018 const blink::WebString web_id =
1019 blink::WebString::fromUTF8(config.id());
1021 scoped_ptr<WebInbandTextTrackImpl> web_inband_text_track(
1022 new WebInbandTextTrackImpl(web_kind, web_label, web_language, web_id,
1023 text_track_index_++));
1025 scoped_ptr<media::TextTrack> text_track(
1026 new TextTrackImpl(main_loop_, client_, web_inband_text_track.Pass()));
1028 done_cb.Run(text_track.Pass());
1031 void WebMediaPlayerImpl::OnKeyError(const std::string& session_id,
1032 media::MediaKeys::KeyError error_code,
1034 DCHECK(main_loop_->BelongsToCurrentThread());
1036 EmeUMAHistogramEnumeration(current_key_system_, "KeyError",
1037 error_code, media::MediaKeys::kMaxKeyError);
1040 current_key_system_,
1041 WebString::fromUTF8(session_id),
1042 static_cast<blink::WebMediaPlayerClient::MediaKeyErrorCode>(error_code),
1046 void WebMediaPlayerImpl::OnKeyMessage(const std::string& session_id,
1047 const std::vector<uint8>& message,
1048 const std::string& default_url) {
1049 DCHECK(main_loop_->BelongsToCurrentThread());
1051 const GURL default_url_gurl(default_url);
1052 DLOG_IF(WARNING, !default_url.empty() && !default_url_gurl.is_valid())
1053 << "Invalid URL in default_url: " << default_url;
1055 client_->keyMessage(current_key_system_,
1056 WebString::fromUTF8(session_id),
1057 message.empty() ? NULL : &message[0],
1062 void WebMediaPlayerImpl::SetOpaque(bool opaque) {
1063 DCHECK(main_loop_->BelongsToCurrentThread());
1065 client_->setOpaque(opaque);
1068 void WebMediaPlayerImpl::DataSourceInitialized(const GURL& gurl, bool success) {
1069 DCHECK(main_loop_->BelongsToCurrentThread());
1072 SetNetworkState(WebMediaPlayer::NetworkStateFormatError);
1074 // TODO(scherkus): This should be handled by HTMLMediaElement and controls
1075 // should know when to invalidate themselves http://crbug.com/337015
1076 InvalidateOnMainThread();
1083 void WebMediaPlayerImpl::NotifyDownloading(bool is_downloading) {
1084 if (!is_downloading && network_state_ == WebMediaPlayer::NetworkStateLoading)
1085 SetNetworkState(WebMediaPlayer::NetworkStateIdle);
1086 else if (is_downloading && network_state_ == WebMediaPlayer::NetworkStateIdle)
1087 SetNetworkState(WebMediaPlayer::NetworkStateLoading);
1088 media_log_->AddEvent(
1089 media_log_->CreateBooleanEvent(
1090 media::MediaLogEvent::NETWORK_ACTIVITY_SET,
1091 "is_downloading_data", is_downloading));
1094 void WebMediaPlayerImpl::StartPipeline() {
1095 DCHECK(main_loop_->BelongsToCurrentThread());
1096 const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
1098 // Keep track if this is a MSE or non-MSE playback.
1099 UMA_HISTOGRAM_BOOLEAN("Media.MSE.Playback",
1100 (load_type_ == LoadTypeMediaSource));
1102 // Figure out which demuxer to use.
1103 if (load_type_ != LoadTypeMediaSource) {
1104 DCHECK(!chunk_demuxer_);
1105 DCHECK(data_source_);
1107 demuxer_.reset(new media::FFmpegDemuxer(
1108 media_loop_, data_source_.get(),
1109 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnNeedKey),
1112 DCHECK(!chunk_demuxer_);
1113 DCHECK(!data_source_);
1115 chunk_demuxer_ = new media::ChunkDemuxer(
1116 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDemuxerOpened),
1117 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnNeedKey),
1118 base::Bind(&LogMediaSourceError, media_log_));
1119 demuxer_.reset(chunk_demuxer_);
1122 scoped_ptr<media::FilterCollection> filter_collection(
1123 new media::FilterCollection());
1124 filter_collection->SetDemuxer(demuxer_.get());
1126 media::SetDecryptorReadyCB set_decryptor_ready_cb =
1127 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::SetDecryptorReadyCB);
1129 // Create our audio decoders and renderer.
1130 ScopedVector<media::AudioDecoder> audio_decoders;
1131 audio_decoders.push_back(new media::FFmpegAudioDecoder(media_loop_));
1132 if (!cmd_line->HasSwitch(switches::kDisableOpusPlayback)) {
1133 audio_decoders.push_back(new media::OpusAudioDecoder(media_loop_));
1136 scoped_ptr<media::AudioRenderer> audio_renderer(
1137 new media::AudioRendererImpl(media_loop_,
1138 audio_source_provider_.get(),
1139 audio_decoders.Pass(),
1140 set_decryptor_ready_cb));
1141 filter_collection->SetAudioRenderer(audio_renderer.Pass());
1143 // Create our video decoders and renderer.
1144 ScopedVector<media::VideoDecoder> video_decoders;
1146 if (gpu_factories_.get()) {
1147 video_decoders.push_back(
1148 new media::GpuVideoDecoder(gpu_factories_, media_log_));
1151 #if !defined(MEDIA_DISABLE_LIBVPX)
1152 video_decoders.push_back(new media::VpxVideoDecoder(media_loop_));
1153 #endif // !defined(MEDIA_DISABLE_LIBVPX)
1155 video_decoders.push_back(new media::FFmpegVideoDecoder(media_loop_));
1157 scoped_ptr<media::VideoRenderer> video_renderer(
1158 new media::VideoRendererImpl(
1160 video_decoders.Pass(),
1161 set_decryptor_ready_cb,
1162 base::Bind(&WebMediaPlayerImpl::FrameReady, base::Unretained(this)),
1163 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::SetOpaque),
1165 filter_collection->SetVideoRenderer(video_renderer.Pass());
1167 if (cmd_line->HasSwitch(switches::kEnableInbandTextTracks)) {
1168 scoped_ptr<media::TextRenderer> text_renderer(
1169 new media::TextRenderer(
1171 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnAddTextTrack)));
1173 filter_collection->SetTextRenderer(text_renderer.Pass());
1176 // ... and we're ready to go!
1179 filter_collection.Pass(),
1180 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineEnded),
1181 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineError),
1182 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSeek),
1183 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineBufferingState),
1184 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChange));
1187 void WebMediaPlayerImpl::SetNetworkState(WebMediaPlayer::NetworkState state) {
1188 DCHECK(main_loop_->BelongsToCurrentThread());
1189 DVLOG(1) << "SetNetworkState: " << state;
1190 network_state_ = state;
1191 // Always notify to ensure client has the latest value.
1192 client_->networkStateChanged();
1195 void WebMediaPlayerImpl::SetReadyState(WebMediaPlayer::ReadyState state) {
1196 DCHECK(main_loop_->BelongsToCurrentThread());
1197 DVLOG(1) << "SetReadyState: " << state;
1199 if (state == WebMediaPlayer::ReadyStateHaveEnoughData &&
1201 network_state_ == WebMediaPlayer::NetworkStateLoading)
1202 SetNetworkState(WebMediaPlayer::NetworkStateLoaded);
1204 ready_state_ = state;
1205 // Always notify to ensure client has the latest value.
1206 client_->readyStateChanged();
1209 void WebMediaPlayerImpl::Destroy(DestroyReason reason) {
1210 DCHECK(main_loop_->BelongsToCurrentThread());
1212 // TODO(scherkus): Remove after tracking down cause for crashes
1213 // http://crbug.com/341184 http://crbug.com/341186
1214 CHECK((destroy_reason_ & reason) == 0);
1215 destroy_reason_ |= reason;
1217 // Abort any pending IO so stopping the pipeline doesn't get blocked.
1219 data_source_->Abort();
1220 if (chunk_demuxer_) {
1221 chunk_demuxer_->Shutdown();
1222 chunk_demuxer_ = NULL;
1225 gpu_factories_ = NULL;
1228 // Make sure to kill the pipeline so there's no more media threads running.
1229 // Note: stopping the pipeline might block for a long time.
1230 base::WaitableEvent waiter(false, false);
1232 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&waiter)));
1235 // Let V8 know we are not using extra resources anymore.
1236 if (incremented_externally_allocated_memory_) {
1237 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
1238 -kPlayerExtraMemory);
1239 incremented_externally_allocated_memory_ = false;
1243 // Release any final references now that everything has stopped.
1246 data_source_.reset();
1249 blink::WebAudioSourceProvider* WebMediaPlayerImpl::audioSourceProvider() {
1250 return audio_source_provider_.get();
1253 void WebMediaPlayerImpl::IncrementExternallyAllocatedMemory() {
1254 DCHECK(main_loop_->BelongsToCurrentThread());
1255 incremented_externally_allocated_memory_ = true;
1256 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
1257 kPlayerExtraMemory);
1260 double WebMediaPlayerImpl::GetPipelineDuration() const {
1261 base::TimeDelta duration = pipeline_->GetMediaDuration();
1263 // Return positive infinity if the resource is unbounded.
1264 // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#dom-media-duration
1265 if (duration == media::kInfiniteDuration())
1266 return std::numeric_limits<double>::infinity();
1268 return duration.InSecondsF();
1271 void WebMediaPlayerImpl::OnDurationChange() {
1272 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
1275 client_->durationChanged();
1278 void WebMediaPlayerImpl::OnNaturalSizeChange(gfx::Size size) {
1279 DCHECK(main_loop_->BelongsToCurrentThread());
1280 DCHECK_NE(ready_state_, WebMediaPlayer::ReadyStateHaveNothing);
1281 TRACE_EVENT0("media", "WebMediaPlayerImpl::OnNaturalSizeChanged");
1283 media_log_->AddEvent(
1284 media_log_->CreateVideoSizeSetEvent(size.width(), size.height()));
1285 natural_size_ = size;
1287 client_->sizeChanged();
1290 void WebMediaPlayerImpl::FrameReady(
1291 const scoped_refptr<media::VideoFrame>& frame) {
1292 // TODO(scherkus): Today we always invalidate on the main thread even when
1293 // compositing is available, which is less efficient and involves more
1294 // thread hops. Refer to http://crbug.com/335345 for details.
1295 painter_.UpdateCurrentFrame(frame);
1298 void WebMediaPlayerImpl::SetDecryptorReadyCB(
1299 const media::DecryptorReadyCB& decryptor_ready_cb) {
1300 DCHECK(main_loop_->BelongsToCurrentThread());
1302 // Cancels the previous decryptor request.
1303 if (decryptor_ready_cb.is_null()) {
1304 if (!decryptor_ready_cb_.is_null())
1305 base::ResetAndReturn(&decryptor_ready_cb_).Run(NULL);
1309 // TODO(xhwang): Support multiple decryptor notification request (e.g. from
1310 // video and audio). The current implementation is okay for the current
1311 // media pipeline since we initialize audio and video decoders in sequence.
1312 // But WebMediaPlayerImpl should not depend on media pipeline's implementation
1314 DCHECK(decryptor_ready_cb_.is_null());
1316 // Mixed use of prefixed and unprefixed EME APIs is disallowed by Blink.
1317 DCHECK(!(proxy_decryptor_ && web_cdm_));
1319 if (proxy_decryptor_) {
1320 decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor());
1325 decryptor_ready_cb.Run(web_cdm_->GetDecryptor());
1329 decryptor_ready_cb_ = decryptor_ready_cb;
1332 } // namespace content