• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "content/renderer/media/webmediaplayer_impl.h"
6 
7 #include <algorithm>
8 #include <limits>
9 #include <string>
10 #include <vector>
11 
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/strings/utf_string_conversions.h"
23 #include "base/synchronization/waitable_event.h"
24 #include "cc/layers/video_layer.h"
25 #include "content/public/common/content_switches.h"
26 #include "content/public/renderer/render_frame.h"
27 #include "content/renderer/compositor_bindings/web_layer_impl.h"
28 #include "content/renderer/media/buffered_data_source.h"
29 #include "content/renderer/media/crypto/key_systems.h"
30 #include "content/renderer/media/render_media_log.h"
31 #include "content/renderer/media/texttrack_impl.h"
32 #include "content/renderer/media/webaudiosourceprovider_impl.h"
33 #include "content/renderer/media/webcontentdecryptionmodule_impl.h"
34 #include "content/renderer/media/webinbandtexttrack_impl.h"
35 #include "content/renderer/media/webmediaplayer_delegate.h"
36 #include "content/renderer/media/webmediaplayer_params.h"
37 #include "content/renderer/media/webmediaplayer_util.h"
38 #include "content/renderer/media/webmediasource_impl.h"
39 #include "content/renderer/pepper/pepper_webplugin_impl.h"
40 #include "content/renderer/render_thread_impl.h"
41 #include "gpu/GLES2/gl2extchromium.h"
42 #include "gpu/command_buffer/common/mailbox_holder.h"
43 #include "media/audio/null_audio_sink.h"
44 #include "media/base/audio_hardware_config.h"
45 #include "media/base/bind_to_current_loop.h"
46 #include "media/base/filter_collection.h"
47 #include "media/base/limits.h"
48 #include "media/base/media_log.h"
49 #include "media/base/media_switches.h"
50 #include "media/base/pipeline.h"
51 #include "media/base/text_renderer.h"
52 #include "media/base/video_frame.h"
53 #include "media/filters/audio_renderer_impl.h"
54 #include "media/filters/chunk_demuxer.h"
55 #include "media/filters/ffmpeg_audio_decoder.h"
56 #include "media/filters/ffmpeg_demuxer.h"
57 #include "media/filters/ffmpeg_video_decoder.h"
58 #include "media/filters/gpu_video_accelerator_factories.h"
59 #include "media/filters/gpu_video_decoder.h"
60 #include "media/filters/opus_audio_decoder.h"
61 #include "media/filters/video_renderer_impl.h"
62 #include "media/filters/vpx_video_decoder.h"
63 #include "third_party/WebKit/public/platform/WebContentDecryptionModule.h"
64 #include "third_party/WebKit/public/platform/WebMediaSource.h"
65 #include "third_party/WebKit/public/platform/WebRect.h"
66 #include "third_party/WebKit/public/platform/WebSize.h"
67 #include "third_party/WebKit/public/platform/WebString.h"
68 #include "third_party/WebKit/public/platform/WebURL.h"
69 #include "third_party/WebKit/public/web/WebDocument.h"
70 #include "third_party/WebKit/public/web/WebLocalFrame.h"
71 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
72 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
73 #include "third_party/WebKit/public/web/WebView.h"
74 #include "v8/include/v8.h"
75 
76 #if defined(ENABLE_PEPPER_CDMS)
77 #include "content/renderer/media/crypto/pepper_cdm_wrapper_impl.h"
78 #endif
79 
80 using blink::WebCanvas;
81 using blink::WebMediaPlayer;
82 using blink::WebRect;
83 using blink::WebSize;
84 using blink::WebString;
85 using media::PipelineStatus;
86 
87 namespace {
88 
89 // Amount of extra memory used by each player instance reported to V8.
90 // It is not exact number -- first, it differs on different platforms,
91 // and second, it is very hard to calculate. Instead, use some arbitrary
92 // value that will cause garbage collection from time to time. We don't want
93 // it to happen on every allocation, but don't want 5k players to sit in memory
94 // either. Looks that chosen constant achieves both goals, at least for audio
95 // objects. (Do not worry about video objects yet, JS programs do not create
96 // thousands of them...)
97 const int kPlayerExtraMemory = 1024 * 1024;
98 
99 // Limits the range of playback rate.
100 //
101 // TODO(kylep): Revisit these.
102 //
103 // Vista has substantially lower performance than XP or Windows7.  If you speed
104 // up a video too much, it can't keep up, and rendering stops updating except on
105 // the time bar. For really high speeds, audio becomes a bottleneck and we just
106 // use up the data we have, which may not achieve the speed requested, but will
107 // not crash the tab.
108 //
109 // A very slow speed, ie 0.00000001x, causes the machine to lock up. (It seems
110 // like a busy loop). It gets unresponsive, although its not completely dead.
111 //
112 // Also our timers are not very accurate (especially for ogg), which becomes
113 // evident at low speeds and on Vista. Since other speeds are risky and outside
114 // the norms, we think 1/16x to 16x is a safe and useful range for now.
115 const double kMinRate = 0.0625;
116 const double kMaxRate = 16.0;
117 
118 // Prefix for histograms related to Encrypted Media Extensions.
119 const char* kMediaEme = "Media.EME.";
120 
121 }  // namespace
122 
123 namespace content {
124 
125 class BufferedDataSourceHostImpl;
126 
127 #define COMPILE_ASSERT_MATCHING_ENUM(name) \
128   COMPILE_ASSERT(static_cast<int>(WebMediaPlayer::CORSMode ## name) == \
129                  static_cast<int>(BufferedResourceLoader::k ## name), \
130                  mismatching_enums)
131 COMPILE_ASSERT_MATCHING_ENUM(Unspecified);
132 COMPILE_ASSERT_MATCHING_ENUM(Anonymous);
133 COMPILE_ASSERT_MATCHING_ENUM(UseCredentials);
134 #undef COMPILE_ASSERT_MATCHING_ENUM
135 
136 #define BIND_TO_RENDER_LOOP(function) \
137   (DCHECK(main_loop_->BelongsToCurrentThread()), \
138   media::BindToCurrentLoop(base::Bind(function, AsWeakPtr())))
139 
LogMediaSourceError(const scoped_refptr<media::MediaLog> & media_log,const std::string & error)140 static void LogMediaSourceError(const scoped_refptr<media::MediaLog>& media_log,
141                                 const std::string& error) {
142   media_log->AddEvent(media_log->CreateMediaSourceErrorEvent(error));
143 }
144 
WebMediaPlayerImpl(blink::WebLocalFrame * frame,blink::WebMediaPlayerClient * client,base::WeakPtr<WebMediaPlayerDelegate> delegate,const WebMediaPlayerParams & params)145 WebMediaPlayerImpl::WebMediaPlayerImpl(
146     blink::WebLocalFrame* frame,
147     blink::WebMediaPlayerClient* client,
148     base::WeakPtr<WebMediaPlayerDelegate> delegate,
149     const WebMediaPlayerParams& params)
150     : frame_(frame),
151       network_state_(WebMediaPlayer::NetworkStateEmpty),
152       ready_state_(WebMediaPlayer::ReadyStateHaveNothing),
153       main_loop_(base::MessageLoopProxy::current()),
154       media_loop_(
155           RenderThreadImpl::current()->GetMediaThreadMessageLoopProxy()),
156       media_log_(new RenderMediaLog()),
157       pipeline_(media_loop_, media_log_.get()),
158       opaque_(false),
159       paused_(true),
160       seeking_(false),
161       playback_rate_(0.0f),
162       pending_seek_(false),
163       pending_seek_seconds_(0.0f),
164       client_(client),
165       delegate_(delegate),
166       defer_load_cb_(params.defer_load_cb()),
167       accelerated_compositing_reported_(false),
168       incremented_externally_allocated_memory_(false),
169       gpu_factories_(RenderThreadImpl::current()->GetGpuFactories()),
170       supports_save_(true),
171       chunk_demuxer_(NULL),
172       // Threaded compositing isn't enabled universally yet.
173       compositor_task_runner_(
174           RenderThreadImpl::current()->compositor_message_loop_proxy()
175               ? RenderThreadImpl::current()->compositor_message_loop_proxy()
176               : base::MessageLoopProxy::current()),
177       compositor_(new VideoFrameCompositor(
178           BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnNaturalSizeChanged),
179           BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnOpacityChanged))),
180       text_track_index_(0),
181       web_cdm_(NULL) {
182   media_log_->AddEvent(
183       media_log_->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_CREATED));
184 
185   // |gpu_factories_| requires that its entry points be called on its
186   // |GetTaskRunner()|.  Since |pipeline_| will own decoders created from the
187   // factories, require that their message loops are identical.
188   DCHECK(!gpu_factories_ || (gpu_factories_->GetTaskRunner() == media_loop_));
189 
190   // Let V8 know we started new thread if we did not do it yet.
191   // Made separate task to avoid deletion of player currently being created.
192   // Also, delaying GC until after player starts gets rid of starting lag --
193   // collection happens in parallel with playing.
194   //
195   // TODO(enal): remove when we get rid of per-audio-stream thread.
196   main_loop_->PostTask(
197       FROM_HERE,
198       base::Bind(&WebMediaPlayerImpl::IncrementExternallyAllocatedMemory,
199                  AsWeakPtr()));
200 
201   // Use the null sink if no sink was provided.
202   audio_source_provider_ = new WebAudioSourceProviderImpl(
203       params.audio_renderer_sink().get()
204           ? params.audio_renderer_sink()
205           : new media::NullAudioSink(media_loop_));
206 }
207 
~WebMediaPlayerImpl()208 WebMediaPlayerImpl::~WebMediaPlayerImpl() {
209   client_->setWebLayer(NULL);
210 
211   DCHECK(main_loop_->BelongsToCurrentThread());
212   media_log_->AddEvent(
213       media_log_->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_DESTROYED));
214 
215   if (delegate_.get())
216     delegate_->PlayerGone(this);
217 
218   // Abort any pending IO so stopping the pipeline doesn't get blocked.
219   if (data_source_)
220     data_source_->Abort();
221   if (chunk_demuxer_) {
222     chunk_demuxer_->Shutdown();
223     chunk_demuxer_ = NULL;
224   }
225 
226   gpu_factories_ = NULL;
227 
228   // Make sure to kill the pipeline so there's no more media threads running.
229   // Note: stopping the pipeline might block for a long time.
230   base::WaitableEvent waiter(false, false);
231   pipeline_.Stop(
232       base::Bind(&base::WaitableEvent::Signal, base::Unretained(&waiter)));
233   waiter.Wait();
234 
235   compositor_task_runner_->DeleteSoon(FROM_HERE, compositor_);
236 
237   // Let V8 know we are not using extra resources anymore.
238   if (incremented_externally_allocated_memory_) {
239     v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
240         -kPlayerExtraMemory);
241     incremented_externally_allocated_memory_ = false;
242   }
243 }
244 
load(LoadType load_type,const blink::WebURL & url,CORSMode cors_mode)245 void WebMediaPlayerImpl::load(LoadType load_type, const blink::WebURL& url,
246                               CORSMode cors_mode) {
247   DVLOG(1) << __FUNCTION__ << "(" << load_type << ", " << url << ", "
248            << cors_mode << ")";
249   if (!defer_load_cb_.is_null()) {
250     defer_load_cb_.Run(base::Bind(
251         &WebMediaPlayerImpl::DoLoad, AsWeakPtr(), load_type, url, cors_mode));
252     return;
253   }
254   DoLoad(load_type, url, cors_mode);
255 }
256 
DoLoad(LoadType load_type,const blink::WebURL & url,CORSMode cors_mode)257 void WebMediaPlayerImpl::DoLoad(LoadType load_type,
258                                 const blink::WebURL& url,
259                                 CORSMode cors_mode) {
260   DCHECK(main_loop_->BelongsToCurrentThread());
261 
262   GURL gurl(url);
263   ReportMediaSchemeUma(gurl);
264 
265   // Set subresource URL for crash reporting.
266   base::debug::SetCrashKeyValue("subresource_url", gurl.spec());
267 
268   load_type_ = load_type;
269 
270   // Handle any volume/preload changes that occurred before load().
271   setVolume(client_->volume());
272   setPreload(client_->preload());
273 
274   SetNetworkState(WebMediaPlayer::NetworkStateLoading);
275   SetReadyState(WebMediaPlayer::ReadyStateHaveNothing);
276   media_log_->AddEvent(media_log_->CreateLoadEvent(url.spec()));
277 
278   // Media source pipelines can start immediately.
279   if (load_type == LoadTypeMediaSource) {
280     supports_save_ = false;
281     StartPipeline();
282     return;
283   }
284 
285   // Otherwise it's a regular request which requires resolving the URL first.
286   data_source_.reset(new BufferedDataSource(
287       url,
288       static_cast<BufferedResourceLoader::CORSMode>(cors_mode),
289       main_loop_,
290       frame_,
291       media_log_.get(),
292       &buffered_data_source_host_,
293       base::Bind(&WebMediaPlayerImpl::NotifyDownloading, AsWeakPtr())));
294   data_source_->Initialize(
295       base::Bind(&WebMediaPlayerImpl::DataSourceInitialized, AsWeakPtr()));
296 }
297 
play()298 void WebMediaPlayerImpl::play() {
299   DVLOG(1) << __FUNCTION__;
300   DCHECK(main_loop_->BelongsToCurrentThread());
301 
302   paused_ = false;
303   pipeline_.SetPlaybackRate(playback_rate_);
304   if (data_source_)
305     data_source_->MediaIsPlaying();
306 
307   media_log_->AddEvent(media_log_->CreateEvent(media::MediaLogEvent::PLAY));
308 
309   if (delegate_.get())
310     delegate_->DidPlay(this);
311 }
312 
pause()313 void WebMediaPlayerImpl::pause() {
314   DVLOG(1) << __FUNCTION__;
315   DCHECK(main_loop_->BelongsToCurrentThread());
316 
317   paused_ = true;
318   pipeline_.SetPlaybackRate(0.0f);
319   if (data_source_)
320     data_source_->MediaIsPaused();
321   paused_time_ = pipeline_.GetMediaTime();
322 
323   media_log_->AddEvent(media_log_->CreateEvent(media::MediaLogEvent::PAUSE));
324 
325   if (delegate_.get())
326     delegate_->DidPause(this);
327 }
328 
supportsSave() const329 bool WebMediaPlayerImpl::supportsSave() const {
330   DCHECK(main_loop_->BelongsToCurrentThread());
331   return supports_save_;
332 }
333 
seek(double seconds)334 void WebMediaPlayerImpl::seek(double seconds) {
335   DVLOG(1) << __FUNCTION__ << "(" << seconds << ")";
336   DCHECK(main_loop_->BelongsToCurrentThread());
337 
338   if (ready_state_ > WebMediaPlayer::ReadyStateHaveMetadata)
339     SetReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
340 
341   base::TimeDelta seek_time = ConvertSecondsToTimestamp(seconds);
342 
343   if (seeking_) {
344     pending_seek_ = true;
345     pending_seek_seconds_ = seconds;
346     if (chunk_demuxer_)
347       chunk_demuxer_->CancelPendingSeek(seek_time);
348     return;
349   }
350 
351   media_log_->AddEvent(media_log_->CreateSeekEvent(seconds));
352 
353   // Update our paused time.
354   if (paused_)
355     paused_time_ = seek_time;
356 
357   seeking_ = true;
358 
359   if (chunk_demuxer_)
360     chunk_demuxer_->StartWaitingForSeek(seek_time);
361 
362   // Kick off the asynchronous seek!
363   pipeline_.Seek(
364       seek_time,
365       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSeek));
366 }
367 
setRate(double rate)368 void WebMediaPlayerImpl::setRate(double rate) {
369   DVLOG(1) << __FUNCTION__ << "(" << rate << ")";
370   DCHECK(main_loop_->BelongsToCurrentThread());
371 
372   // TODO(kylep): Remove when support for negatives is added. Also, modify the
373   // following checks so rewind uses reasonable values also.
374   if (rate < 0.0)
375     return;
376 
377   // Limit rates to reasonable values by clamping.
378   if (rate != 0.0) {
379     if (rate < kMinRate)
380       rate = kMinRate;
381     else if (rate > kMaxRate)
382       rate = kMaxRate;
383   }
384 
385   playback_rate_ = rate;
386   if (!paused_) {
387     pipeline_.SetPlaybackRate(rate);
388     if (data_source_)
389       data_source_->MediaPlaybackRateChanged(rate);
390   }
391 }
392 
setVolume(double volume)393 void WebMediaPlayerImpl::setVolume(double volume) {
394   DVLOG(1) << __FUNCTION__ << "(" << volume << ")";
395   DCHECK(main_loop_->BelongsToCurrentThread());
396 
397   pipeline_.SetVolume(volume);
398 }
399 
400 #define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, chromium_name) \
401     COMPILE_ASSERT(static_cast<int>(WebMediaPlayer::webkit_name) == \
402                    static_cast<int>(content::chromium_name), \
403                    mismatching_enums)
404 COMPILE_ASSERT_MATCHING_ENUM(PreloadNone, NONE);
405 COMPILE_ASSERT_MATCHING_ENUM(PreloadMetaData, METADATA);
406 COMPILE_ASSERT_MATCHING_ENUM(PreloadAuto, AUTO);
407 #undef COMPILE_ASSERT_MATCHING_ENUM
408 
setPreload(WebMediaPlayer::Preload preload)409 void WebMediaPlayerImpl::setPreload(WebMediaPlayer::Preload preload) {
410   DVLOG(1) << __FUNCTION__ << "(" << preload << ")";
411   DCHECK(main_loop_->BelongsToCurrentThread());
412 
413   if (data_source_)
414     data_source_->SetPreload(static_cast<content::Preload>(preload));
415 }
416 
hasVideo() const417 bool WebMediaPlayerImpl::hasVideo() const {
418   DCHECK(main_loop_->BelongsToCurrentThread());
419 
420   return pipeline_metadata_.has_video;
421 }
422 
hasAudio() const423 bool WebMediaPlayerImpl::hasAudio() const {
424   DCHECK(main_loop_->BelongsToCurrentThread());
425 
426   return pipeline_metadata_.has_audio;
427 }
428 
naturalSize() const429 blink::WebSize WebMediaPlayerImpl::naturalSize() const {
430   DCHECK(main_loop_->BelongsToCurrentThread());
431 
432   return blink::WebSize(pipeline_metadata_.natural_size);
433 }
434 
paused() const435 bool WebMediaPlayerImpl::paused() const {
436   DCHECK(main_loop_->BelongsToCurrentThread());
437 
438   return pipeline_.GetPlaybackRate() == 0.0f;
439 }
440 
seeking() const441 bool WebMediaPlayerImpl::seeking() const {
442   DCHECK(main_loop_->BelongsToCurrentThread());
443 
444   if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
445     return false;
446 
447   return seeking_;
448 }
449 
duration() const450 double WebMediaPlayerImpl::duration() const {
451   DCHECK(main_loop_->BelongsToCurrentThread());
452 
453   if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
454     return std::numeric_limits<double>::quiet_NaN();
455 
456   return GetPipelineDuration();
457 }
458 
timelineOffset() const459 double WebMediaPlayerImpl::timelineOffset() const {
460   DCHECK(main_loop_->BelongsToCurrentThread());
461 
462   if (pipeline_metadata_.timeline_offset.is_null())
463     return std::numeric_limits<double>::quiet_NaN();
464 
465   return pipeline_metadata_.timeline_offset.ToJsTime();
466 }
467 
currentTime() const468 double WebMediaPlayerImpl::currentTime() const {
469   DCHECK(main_loop_->BelongsToCurrentThread());
470   return (paused_ ? paused_time_ : pipeline_.GetMediaTime()).InSecondsF();
471 }
472 
networkState() const473 WebMediaPlayer::NetworkState WebMediaPlayerImpl::networkState() const {
474   DCHECK(main_loop_->BelongsToCurrentThread());
475   return network_state_;
476 }
477 
readyState() const478 WebMediaPlayer::ReadyState WebMediaPlayerImpl::readyState() const {
479   DCHECK(main_loop_->BelongsToCurrentThread());
480   return ready_state_;
481 }
482 
buffered() const483 blink::WebTimeRanges WebMediaPlayerImpl::buffered() const {
484   DCHECK(main_loop_->BelongsToCurrentThread());
485   media::Ranges<base::TimeDelta> buffered_time_ranges =
486       pipeline_.GetBufferedTimeRanges();
487   buffered_data_source_host_.AddBufferedTimeRanges(
488       &buffered_time_ranges, pipeline_.GetMediaDuration());
489   return ConvertToWebTimeRanges(buffered_time_ranges);
490 }
491 
maxTimeSeekable() const492 double WebMediaPlayerImpl::maxTimeSeekable() const {
493   DCHECK(main_loop_->BelongsToCurrentThread());
494 
495   // If we haven't even gotten to ReadyStateHaveMetadata yet then just
496   // return 0 so that the seekable range is empty.
497   if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata)
498     return 0.0;
499 
500   // We don't support seeking in streaming media.
501   if (data_source_ && data_source_->IsStreaming())
502     return 0.0;
503   return duration();
504 }
505 
didLoadingProgress()506 bool WebMediaPlayerImpl::didLoadingProgress() {
507   DCHECK(main_loop_->BelongsToCurrentThread());
508   bool pipeline_progress = pipeline_.DidLoadingProgress();
509   bool data_progress = buffered_data_source_host_.DidLoadingProgress();
510   return pipeline_progress || data_progress;
511 }
512 
paint(WebCanvas * canvas,const WebRect & rect,unsigned char alpha)513 void WebMediaPlayerImpl::paint(WebCanvas* canvas,
514                                const WebRect& rect,
515                                unsigned char alpha) {
516   DCHECK(main_loop_->BelongsToCurrentThread());
517   TRACE_EVENT0("media", "WebMediaPlayerImpl:paint");
518 
519   if (!accelerated_compositing_reported_) {
520     accelerated_compositing_reported_ = true;
521     // Normally paint() is only called in non-accelerated rendering, but there
522     // are exceptions such as webgl where compositing is used in the WebView but
523     // video frames are still rendered to a canvas.
524     UMA_HISTOGRAM_BOOLEAN(
525         "Media.AcceleratedCompositingActive",
526         frame_->view()->isAcceleratedCompositingActive());
527   }
528 
529   // TODO(scherkus): Clarify paint() API contract to better understand when and
530   // why it's being called. For example, today paint() is called when:
531   //   - We haven't reached HAVE_CURRENT_DATA and need to paint black
532   //   - We're painting to a canvas
533   // See http://crbug.com/341225 http://crbug.com/342621 for details.
534   scoped_refptr<media::VideoFrame> video_frame =
535       GetCurrentFrameFromCompositor();
536 
537   gfx::Rect gfx_rect(rect);
538   skcanvas_video_renderer_.Paint(video_frame.get(), canvas, gfx_rect, alpha);
539 }
540 
hasSingleSecurityOrigin() const541 bool WebMediaPlayerImpl::hasSingleSecurityOrigin() const {
542   if (data_source_)
543     return data_source_->HasSingleOrigin();
544   return true;
545 }
546 
didPassCORSAccessCheck() const547 bool WebMediaPlayerImpl::didPassCORSAccessCheck() const {
548   if (data_source_)
549     return data_source_->DidPassCORSAccessCheck();
550   return false;
551 }
552 
mediaTimeForTimeValue(double timeValue) const553 double WebMediaPlayerImpl::mediaTimeForTimeValue(double timeValue) const {
554   return ConvertSecondsToTimestamp(timeValue).InSecondsF();
555 }
556 
decodedFrameCount() const557 unsigned WebMediaPlayerImpl::decodedFrameCount() const {
558   DCHECK(main_loop_->BelongsToCurrentThread());
559 
560   media::PipelineStatistics stats = pipeline_.GetStatistics();
561   return stats.video_frames_decoded;
562 }
563 
droppedFrameCount() const564 unsigned WebMediaPlayerImpl::droppedFrameCount() const {
565   DCHECK(main_loop_->BelongsToCurrentThread());
566 
567   media::PipelineStatistics stats = pipeline_.GetStatistics();
568   return stats.video_frames_dropped;
569 }
570 
audioDecodedByteCount() const571 unsigned WebMediaPlayerImpl::audioDecodedByteCount() const {
572   DCHECK(main_loop_->BelongsToCurrentThread());
573 
574   media::PipelineStatistics stats = pipeline_.GetStatistics();
575   return stats.audio_bytes_decoded;
576 }
577 
videoDecodedByteCount() const578 unsigned WebMediaPlayerImpl::videoDecodedByteCount() const {
579   DCHECK(main_loop_->BelongsToCurrentThread());
580 
581   media::PipelineStatistics stats = pipeline_.GetStatistics();
582   return stats.video_bytes_decoded;
583 }
584 
copyVideoTextureToPlatformTexture(blink::WebGraphicsContext3D * web_graphics_context,unsigned int texture,unsigned int level,unsigned int internal_format,unsigned int type,bool premultiply_alpha,bool flip_y)585 bool WebMediaPlayerImpl::copyVideoTextureToPlatformTexture(
586     blink::WebGraphicsContext3D* web_graphics_context,
587     unsigned int texture,
588     unsigned int level,
589     unsigned int internal_format,
590     unsigned int type,
591     bool premultiply_alpha,
592     bool flip_y) {
593   TRACE_EVENT0("media", "WebMediaPlayerImpl:copyVideoTextureToPlatformTexture");
594 
595   scoped_refptr<media::VideoFrame> video_frame =
596       GetCurrentFrameFromCompositor();
597 
598   if (!video_frame)
599     return false;
600   if (video_frame->format() != media::VideoFrame::NATIVE_TEXTURE)
601     return false;
602 
603   const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
604   if (mailbox_holder->texture_target != GL_TEXTURE_2D)
605     return false;
606 
607   // Since this method changes which texture is bound to the TEXTURE_2D target,
608   // ideally it would restore the currently-bound texture before returning.
609   // The cost of getIntegerv is sufficiently high, however, that we want to
610   // avoid it in user builds. As a result assume (below) that |texture| is
611   // bound when this method is called, and only verify this fact when
612   // DCHECK_IS_ON.
613 #if DCHECK_IS_ON
614   GLint bound_texture = 0;
615   web_graphics_context->getIntegerv(GL_TEXTURE_BINDING_2D, &bound_texture);
616   DCHECK_EQ(static_cast<GLuint>(bound_texture), texture);
617 #endif
618 
619   uint32 source_texture = web_graphics_context->createTexture();
620 
621   web_graphics_context->waitSyncPoint(mailbox_holder->sync_point);
622   web_graphics_context->bindTexture(GL_TEXTURE_2D, source_texture);
623   web_graphics_context->consumeTextureCHROMIUM(GL_TEXTURE_2D,
624                                                mailbox_holder->mailbox.name);
625 
626   // The video is stored in a unmultiplied format, so premultiply
627   // if necessary.
628   web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
629                                     premultiply_alpha);
630   // Application itself needs to take care of setting the right flip_y
631   // value down to get the expected result.
632   // flip_y==true means to reverse the video orientation while
633   // flip_y==false means to keep the intrinsic orientation.
634   web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y);
635   web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D,
636                                             source_texture,
637                                             texture,
638                                             level,
639                                             internal_format,
640                                             type);
641   web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false);
642   web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
643                                     false);
644 
645   // Restore the state for TEXTURE_2D binding point as mentioned above.
646   web_graphics_context->bindTexture(GL_TEXTURE_2D, texture);
647 
648   web_graphics_context->deleteTexture(source_texture);
649   web_graphics_context->flush();
650   video_frame->AppendReleaseSyncPoint(web_graphics_context->insertSyncPoint());
651   return true;
652 }
653 
654 // Helper functions to report media EME related stats to UMA. They follow the
655 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and
656 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is
657 // that UMA_* macros require the names to be constant throughout the process'
658 // lifetime.
EmeUMAHistogramEnumeration(const std::string & key_system,const std::string & method,int sample,int boundary_value)659 static void EmeUMAHistogramEnumeration(const std::string& key_system,
660                                        const std::string& method,
661                                        int sample,
662                                        int boundary_value) {
663   base::LinearHistogram::FactoryGet(
664       kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
665       1, boundary_value, boundary_value + 1,
666       base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
667 }
668 
EmeUMAHistogramCounts(const std::string & key_system,const std::string & method,int sample)669 static void EmeUMAHistogramCounts(const std::string& key_system,
670                                   const std::string& method,
671                                   int sample) {
672   // Use the same parameters as UMA_HISTOGRAM_COUNTS.
673   base::Histogram::FactoryGet(
674       kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
675       1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
676 }
677 
678 // Helper enum for reporting generateKeyRequest/addKey histograms.
679 enum MediaKeyException {
680   kUnknownResultId,
681   kSuccess,
682   kKeySystemNotSupported,
683   kInvalidPlayerState,
684   kMaxMediaKeyException
685 };
686 
MediaKeyExceptionForUMA(WebMediaPlayer::MediaKeyException e)687 static MediaKeyException MediaKeyExceptionForUMA(
688     WebMediaPlayer::MediaKeyException e) {
689   switch (e) {
690     case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
691       return kKeySystemNotSupported;
692     case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
693       return kInvalidPlayerState;
694     case WebMediaPlayer::MediaKeyExceptionNoError:
695       return kSuccess;
696     default:
697       return kUnknownResultId;
698   }
699 }
700 
701 // Helper for converting |key_system| name and exception |e| to a pair of enum
702 // values from above, for reporting to UMA.
ReportMediaKeyExceptionToUMA(const std::string & method,const std::string & key_system,WebMediaPlayer::MediaKeyException e)703 static void ReportMediaKeyExceptionToUMA(const std::string& method,
704                                          const std::string& key_system,
705                                          WebMediaPlayer::MediaKeyException e) {
706   MediaKeyException result_id = MediaKeyExceptionForUMA(e);
707   DCHECK_NE(result_id, kUnknownResultId) << e;
708   EmeUMAHistogramEnumeration(
709       key_system, method, result_id, kMaxMediaKeyException);
710 }
711 
712 // Convert a WebString to ASCII, falling back on an empty string in the case
713 // of a non-ASCII string.
ToASCIIOrEmpty(const blink::WebString & string)714 static std::string ToASCIIOrEmpty(const blink::WebString& string) {
715   return base::IsStringASCII(string) ? base::UTF16ToASCII(string)
716                                      : std::string();
717 }
718 
719 WebMediaPlayer::MediaKeyException
generateKeyRequest(const WebString & key_system,const unsigned char * init_data,unsigned init_data_length)720 WebMediaPlayerImpl::generateKeyRequest(const WebString& key_system,
721                                        const unsigned char* init_data,
722                                        unsigned init_data_length) {
723   DVLOG(1) << "generateKeyRequest: " << base::string16(key_system) << ": "
724            << std::string(reinterpret_cast<const char*>(init_data),
725                           static_cast<size_t>(init_data_length));
726 
727   std::string ascii_key_system =
728       GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
729 
730   WebMediaPlayer::MediaKeyException e =
731       GenerateKeyRequestInternal(ascii_key_system, init_data, init_data_length);
732   ReportMediaKeyExceptionToUMA("generateKeyRequest", ascii_key_system, e);
733   return e;
734 }
735 
736 // Guess the type of |init_data|. This is only used to handle some corner cases
737 // so we keep it as simple as possible without breaking major use cases.
GuessInitDataType(const unsigned char * init_data,unsigned init_data_length)738 static std::string GuessInitDataType(const unsigned char* init_data,
739                                      unsigned init_data_length) {
740   // Most WebM files use KeyId of 16 bytes. MP4 init data are always >16 bytes.
741   if (init_data_length == 16)
742     return "video/webm";
743 
744   return "video/mp4";
745 }
746 
747 WebMediaPlayer::MediaKeyException
GenerateKeyRequestInternal(const std::string & key_system,const unsigned char * init_data,unsigned init_data_length)748 WebMediaPlayerImpl::GenerateKeyRequestInternal(const std::string& key_system,
749                                                const unsigned char* init_data,
750                                                unsigned init_data_length) {
751   DCHECK(main_loop_->BelongsToCurrentThread());
752 
753   if (!IsConcreteSupportedKeySystem(key_system))
754     return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
755 
756   // We do not support run-time switching between key systems for now.
757   if (current_key_system_.empty()) {
758     if (!proxy_decryptor_) {
759       proxy_decryptor_.reset(new ProxyDecryptor(
760 #if defined(ENABLE_PEPPER_CDMS)
761           // Create() must be called synchronously as |frame_| may not be
762           // valid afterwards.
763           base::Bind(&PepperCdmWrapperImpl::Create, frame_),
764 #elif defined(ENABLE_BROWSER_CDMS)
765 #error Browser side CDM in WMPI for prefixed EME API not supported yet.
766 #endif
767           BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyAdded),
768           BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyError),
769           BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyMessage)));
770     }
771 
772     GURL security_origin(frame_->document().securityOrigin().toString());
773     if (!proxy_decryptor_->InitializeCDM(key_system, security_origin))
774       return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
775 
776     if (proxy_decryptor_ && !decryptor_ready_cb_.is_null()) {
777       base::ResetAndReturn(&decryptor_ready_cb_)
778           .Run(proxy_decryptor_->GetDecryptor());
779     }
780 
781     current_key_system_ = key_system;
782   } else if (key_system != current_key_system_) {
783     return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
784   }
785 
786   std::string init_data_type = init_data_type_;
787   if (init_data_type.empty())
788     init_data_type = GuessInitDataType(init_data, init_data_length);
789 
790   // TODO(xhwang): We assume all streams are from the same container (thus have
791   // the same "type") for now. In the future, the "type" should be passed down
792   // from the application.
793   if (!proxy_decryptor_->GenerateKeyRequest(
794            init_data_type, init_data, init_data_length)) {
795     current_key_system_.clear();
796     return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
797   }
798 
799   return WebMediaPlayer::MediaKeyExceptionNoError;
800 }
801 
addKey(const WebString & key_system,const unsigned char * key,unsigned key_length,const unsigned char * init_data,unsigned init_data_length,const WebString & session_id)802 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::addKey(
803     const WebString& key_system,
804     const unsigned char* key,
805     unsigned key_length,
806     const unsigned char* init_data,
807     unsigned init_data_length,
808     const WebString& session_id) {
809   DVLOG(1) << "addKey: " << base::string16(key_system) << ": "
810            << std::string(reinterpret_cast<const char*>(key),
811                           static_cast<size_t>(key_length)) << ", "
812            << std::string(reinterpret_cast<const char*>(init_data),
813                           static_cast<size_t>(init_data_length)) << " ["
814            << base::string16(session_id) << "]";
815 
816   std::string ascii_key_system =
817       GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
818   std::string ascii_session_id = ToASCIIOrEmpty(session_id);
819 
820   WebMediaPlayer::MediaKeyException e = AddKeyInternal(ascii_key_system,
821                                                        key,
822                                                        key_length,
823                                                        init_data,
824                                                        init_data_length,
825                                                        ascii_session_id);
826   ReportMediaKeyExceptionToUMA("addKey", ascii_key_system, e);
827   return e;
828 }
829 
AddKeyInternal(const std::string & key_system,const unsigned char * key,unsigned key_length,const unsigned char * init_data,unsigned init_data_length,const std::string & session_id)830 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::AddKeyInternal(
831     const std::string& key_system,
832     const unsigned char* key,
833     unsigned key_length,
834     const unsigned char* init_data,
835     unsigned init_data_length,
836     const std::string& session_id) {
837   DCHECK(key);
838   DCHECK_GT(key_length, 0u);
839 
840   if (!IsConcreteSupportedKeySystem(key_system))
841     return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
842 
843   if (current_key_system_.empty() || key_system != current_key_system_)
844     return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
845 
846   proxy_decryptor_->AddKey(
847       key, key_length, init_data, init_data_length, session_id);
848   return WebMediaPlayer::MediaKeyExceptionNoError;
849 }
850 
cancelKeyRequest(const WebString & key_system,const WebString & session_id)851 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::cancelKeyRequest(
852     const WebString& key_system,
853     const WebString& session_id) {
854   DVLOG(1) << "cancelKeyRequest: " << base::string16(key_system) << ": "
855            << " [" << base::string16(session_id) << "]";
856 
857   std::string ascii_key_system =
858       GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
859   std::string ascii_session_id = ToASCIIOrEmpty(session_id);
860 
861   WebMediaPlayer::MediaKeyException e =
862       CancelKeyRequestInternal(ascii_key_system, ascii_session_id);
863   ReportMediaKeyExceptionToUMA("cancelKeyRequest", ascii_key_system, e);
864   return e;
865 }
866 
CancelKeyRequestInternal(const std::string & key_system,const std::string & session_id)867 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::CancelKeyRequestInternal(
868     const std::string& key_system,
869     const std::string& session_id) {
870   if (!IsConcreteSupportedKeySystem(key_system))
871     return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
872 
873   if (current_key_system_.empty() || key_system != current_key_system_)
874     return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
875 
876   proxy_decryptor_->CancelKeyRequest(session_id);
877   return WebMediaPlayer::MediaKeyExceptionNoError;
878 }
879 
setContentDecryptionModule(blink::WebContentDecryptionModule * cdm)880 void WebMediaPlayerImpl::setContentDecryptionModule(
881     blink::WebContentDecryptionModule* cdm) {
882   DCHECK(main_loop_->BelongsToCurrentThread());
883 
884   // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324
885   if (!cdm)
886     return;
887 
888   web_cdm_ = ToWebContentDecryptionModuleImpl(cdm);
889 
890   if (web_cdm_ && !decryptor_ready_cb_.is_null())
891     base::ResetAndReturn(&decryptor_ready_cb_).Run(web_cdm_->GetDecryptor());
892 }
893 
InvalidateOnMainThread()894 void WebMediaPlayerImpl::InvalidateOnMainThread() {
895   DCHECK(main_loop_->BelongsToCurrentThread());
896   TRACE_EVENT0("media", "WebMediaPlayerImpl::InvalidateOnMainThread");
897 
898   client_->repaint();
899 }
900 
OnPipelineSeek(PipelineStatus status)901 void WebMediaPlayerImpl::OnPipelineSeek(PipelineStatus status) {
902   DVLOG(1) << __FUNCTION__ << "(" << status << ")";
903   DCHECK(main_loop_->BelongsToCurrentThread());
904   seeking_ = false;
905   if (pending_seek_) {
906     pending_seek_ = false;
907     seek(pending_seek_seconds_);
908     return;
909   }
910 
911   if (status != media::PIPELINE_OK) {
912     OnPipelineError(status);
913     return;
914   }
915 
916   // Update our paused time.
917   if (paused_)
918     paused_time_ = pipeline_.GetMediaTime();
919 
920   client_->timeChanged();
921 }
922 
OnPipelineEnded()923 void WebMediaPlayerImpl::OnPipelineEnded() {
924   DVLOG(1) << __FUNCTION__;
925   DCHECK(main_loop_->BelongsToCurrentThread());
926   client_->timeChanged();
927 }
928 
OnPipelineError(PipelineStatus error)929 void WebMediaPlayerImpl::OnPipelineError(PipelineStatus error) {
930   DCHECK(main_loop_->BelongsToCurrentThread());
931   DCHECK_NE(error, media::PIPELINE_OK);
932 
933   if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing) {
934     // Any error that occurs before reaching ReadyStateHaveMetadata should
935     // be considered a format error.
936     SetNetworkState(WebMediaPlayer::NetworkStateFormatError);
937 
938     // TODO(scherkus): This should be handled by HTMLMediaElement and controls
939     // should know when to invalidate themselves http://crbug.com/337015
940     InvalidateOnMainThread();
941     return;
942   }
943 
944   SetNetworkState(PipelineErrorToNetworkState(error));
945 
946   if (error == media::PIPELINE_ERROR_DECRYPT)
947     EmeUMAHistogramCounts(current_key_system_, "DecryptError", 1);
948 
949   // TODO(scherkus): This should be handled by HTMLMediaElement and controls
950   // should know when to invalidate themselves http://crbug.com/337015
951   InvalidateOnMainThread();
952 }
953 
OnPipelineMetadata(media::PipelineMetadata metadata)954 void WebMediaPlayerImpl::OnPipelineMetadata(
955     media::PipelineMetadata metadata) {
956   DVLOG(1) << __FUNCTION__;
957 
958   pipeline_metadata_ = metadata;
959 
960   SetReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
961 
962   if (hasVideo()) {
963     DCHECK(!video_weblayer_);
964     video_weblayer_.reset(
965         new WebLayerImpl(cc::VideoLayer::Create(compositor_)));
966     video_weblayer_->setOpaque(opaque_);
967     client_->setWebLayer(video_weblayer_.get());
968   }
969 
970   // TODO(scherkus): This should be handled by HTMLMediaElement and controls
971   // should know when to invalidate themselves http://crbug.com/337015
972   InvalidateOnMainThread();
973 }
974 
OnPipelinePrerollCompleted()975 void WebMediaPlayerImpl::OnPipelinePrerollCompleted() {
976   DVLOG(1) << __FUNCTION__;
977 
978   // Only transition to ReadyStateHaveEnoughData if we don't have
979   // any pending seeks because the transition can cause Blink to
980   // report that the most recent seek has completed.
981   if (!pending_seek_) {
982     SetReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
983 
984     // TODO(scherkus): This should be handled by HTMLMediaElement and controls
985     // should know when to invalidate themselves http://crbug.com/337015
986     InvalidateOnMainThread();
987   }
988 }
989 
OnDemuxerOpened()990 void WebMediaPlayerImpl::OnDemuxerOpened() {
991   DCHECK(main_loop_->BelongsToCurrentThread());
992   client_->mediaSourceOpened(new WebMediaSourceImpl(
993       chunk_demuxer_, base::Bind(&LogMediaSourceError, media_log_)));
994 }
995 
OnKeyAdded(const std::string & session_id)996 void WebMediaPlayerImpl::OnKeyAdded(const std::string& session_id) {
997   DCHECK(main_loop_->BelongsToCurrentThread());
998   EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1);
999   client_->keyAdded(
1000       WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1001       WebString::fromUTF8(session_id));
1002 }
1003 
OnNeedKey(const std::string & type,const std::vector<uint8> & init_data)1004 void WebMediaPlayerImpl::OnNeedKey(const std::string& type,
1005                                    const std::vector<uint8>& init_data) {
1006   DCHECK(main_loop_->BelongsToCurrentThread());
1007 
1008   // Do not fire NeedKey event if encrypted media is not enabled.
1009   if (!blink::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled() &&
1010       !blink::WebRuntimeFeatures::isEncryptedMediaEnabled()) {
1011     return;
1012   }
1013 
1014   UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1);
1015 
1016   DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_);
1017   if (init_data_type_.empty())
1018     init_data_type_ = type;
1019 
1020   const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
1021   client_->keyNeeded(
1022       WebString::fromUTF8(type), init_data_ptr, init_data.size());
1023 }
1024 
OnAddTextTrack(const media::TextTrackConfig & config,const media::AddTextTrackDoneCB & done_cb)1025 void WebMediaPlayerImpl::OnAddTextTrack(
1026     const media::TextTrackConfig& config,
1027     const media::AddTextTrackDoneCB& done_cb) {
1028   DCHECK(main_loop_->BelongsToCurrentThread());
1029 
1030   const WebInbandTextTrackImpl::Kind web_kind =
1031       static_cast<WebInbandTextTrackImpl::Kind>(config.kind());
1032   const blink::WebString web_label =
1033       blink::WebString::fromUTF8(config.label());
1034   const blink::WebString web_language =
1035       blink::WebString::fromUTF8(config.language());
1036   const blink::WebString web_id =
1037       blink::WebString::fromUTF8(config.id());
1038 
1039   scoped_ptr<WebInbandTextTrackImpl> web_inband_text_track(
1040       new WebInbandTextTrackImpl(web_kind, web_label, web_language, web_id,
1041                                  text_track_index_++));
1042 
1043   scoped_ptr<media::TextTrack> text_track(
1044       new TextTrackImpl(main_loop_, client_, web_inband_text_track.Pass()));
1045 
1046   done_cb.Run(text_track.Pass());
1047 }
1048 
OnKeyError(const std::string & session_id,media::MediaKeys::KeyError error_code,uint32 system_code)1049 void WebMediaPlayerImpl::OnKeyError(const std::string& session_id,
1050                                     media::MediaKeys::KeyError error_code,
1051                                     uint32 system_code) {
1052   DCHECK(main_loop_->BelongsToCurrentThread());
1053 
1054   EmeUMAHistogramEnumeration(current_key_system_, "KeyError",
1055                              error_code, media::MediaKeys::kMaxKeyError);
1056 
1057   unsigned short short_system_code = 0;
1058   if (system_code > std::numeric_limits<unsigned short>::max()) {
1059     LOG(WARNING) << "system_code exceeds unsigned short limit.";
1060     short_system_code = std::numeric_limits<unsigned short>::max();
1061   } else {
1062     short_system_code = static_cast<unsigned short>(system_code);
1063   }
1064 
1065   client_->keyError(
1066       WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1067       WebString::fromUTF8(session_id),
1068       static_cast<blink::WebMediaPlayerClient::MediaKeyErrorCode>(error_code),
1069       short_system_code);
1070 }
1071 
OnKeyMessage(const std::string & session_id,const std::vector<uint8> & message,const GURL & destination_url)1072 void WebMediaPlayerImpl::OnKeyMessage(const std::string& session_id,
1073                                       const std::vector<uint8>& message,
1074                                       const GURL& destination_url) {
1075   DCHECK(main_loop_->BelongsToCurrentThread());
1076 
1077   DCHECK(destination_url.is_empty() || destination_url.is_valid());
1078 
1079   client_->keyMessage(
1080       WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1081       WebString::fromUTF8(session_id),
1082       message.empty() ? NULL : &message[0],
1083       message.size(),
1084       destination_url);
1085 }
1086 
DataSourceInitialized(bool success)1087 void WebMediaPlayerImpl::DataSourceInitialized(bool success) {
1088   DCHECK(main_loop_->BelongsToCurrentThread());
1089 
1090   if (!success) {
1091     SetNetworkState(WebMediaPlayer::NetworkStateFormatError);
1092 
1093     // TODO(scherkus): This should be handled by HTMLMediaElement and controls
1094     // should know when to invalidate themselves http://crbug.com/337015
1095     InvalidateOnMainThread();
1096     return;
1097   }
1098 
1099   StartPipeline();
1100 }
1101 
NotifyDownloading(bool is_downloading)1102 void WebMediaPlayerImpl::NotifyDownloading(bool is_downloading) {
1103   if (!is_downloading && network_state_ == WebMediaPlayer::NetworkStateLoading)
1104     SetNetworkState(WebMediaPlayer::NetworkStateIdle);
1105   else if (is_downloading && network_state_ == WebMediaPlayer::NetworkStateIdle)
1106     SetNetworkState(WebMediaPlayer::NetworkStateLoading);
1107   media_log_->AddEvent(
1108       media_log_->CreateBooleanEvent(
1109           media::MediaLogEvent::NETWORK_ACTIVITY_SET,
1110           "is_downloading_data", is_downloading));
1111 }
1112 
StartPipeline()1113 void WebMediaPlayerImpl::StartPipeline() {
1114   DCHECK(main_loop_->BelongsToCurrentThread());
1115   const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
1116 
1117   // Keep track if this is a MSE or non-MSE playback.
1118   UMA_HISTOGRAM_BOOLEAN("Media.MSE.Playback",
1119                         (load_type_ == LoadTypeMediaSource));
1120 
1121   media::LogCB mse_log_cb;
1122 
1123   // Figure out which demuxer to use.
1124   if (load_type_ != LoadTypeMediaSource) {
1125     DCHECK(!chunk_demuxer_);
1126     DCHECK(data_source_);
1127 
1128     demuxer_.reset(new media::FFmpegDemuxer(
1129         media_loop_, data_source_.get(),
1130         BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnNeedKey),
1131         media_log_));
1132   } else {
1133     DCHECK(!chunk_demuxer_);
1134     DCHECK(!data_source_);
1135 
1136     mse_log_cb = base::Bind(&LogMediaSourceError, media_log_);
1137 
1138     chunk_demuxer_ = new media::ChunkDemuxer(
1139         BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDemuxerOpened),
1140         BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnNeedKey),
1141         mse_log_cb,
1142         true);
1143     demuxer_.reset(chunk_demuxer_);
1144   }
1145 
1146   scoped_ptr<media::FilterCollection> filter_collection(
1147       new media::FilterCollection());
1148   filter_collection->SetDemuxer(demuxer_.get());
1149 
1150   media::SetDecryptorReadyCB set_decryptor_ready_cb =
1151       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::SetDecryptorReadyCB);
1152 
1153   // Create our audio decoders and renderer.
1154   ScopedVector<media::AudioDecoder> audio_decoders;
1155   audio_decoders.push_back(new media::FFmpegAudioDecoder(media_loop_,
1156                                                          mse_log_cb));
1157   audio_decoders.push_back(new media::OpusAudioDecoder(media_loop_));
1158 
1159   scoped_ptr<media::AudioRenderer> audio_renderer(new media::AudioRendererImpl(
1160       media_loop_,
1161       audio_source_provider_.get(),
1162       audio_decoders.Pass(),
1163       set_decryptor_ready_cb,
1164       RenderThreadImpl::current()->GetAudioHardwareConfig()));
1165   filter_collection->SetAudioRenderer(audio_renderer.Pass());
1166 
1167   // Create our video decoders and renderer.
1168   ScopedVector<media::VideoDecoder> video_decoders;
1169 
1170   if (gpu_factories_.get()) {
1171     video_decoders.push_back(
1172         new media::GpuVideoDecoder(gpu_factories_, media_log_));
1173   }
1174 
1175 #if !defined(MEDIA_DISABLE_LIBVPX)
1176   video_decoders.push_back(new media::VpxVideoDecoder(media_loop_));
1177 #endif  // !defined(MEDIA_DISABLE_LIBVPX)
1178 
1179   video_decoders.push_back(new media::FFmpegVideoDecoder(media_loop_));
1180 
1181   scoped_ptr<media::VideoRenderer> video_renderer(
1182       new media::VideoRendererImpl(
1183           media_loop_,
1184           video_decoders.Pass(),
1185           set_decryptor_ready_cb,
1186           base::Bind(&WebMediaPlayerImpl::FrameReady, base::Unretained(this)),
1187           true));
1188   filter_collection->SetVideoRenderer(video_renderer.Pass());
1189 
1190   if (cmd_line->HasSwitch(switches::kEnableInbandTextTracks)) {
1191     scoped_ptr<media::TextRenderer> text_renderer(
1192         new media::TextRenderer(
1193             media_loop_,
1194             BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnAddTextTrack)));
1195 
1196     filter_collection->SetTextRenderer(text_renderer.Pass());
1197   }
1198 
1199   // ... and we're ready to go!
1200   seeking_ = true;
1201   pipeline_.Start(
1202       filter_collection.Pass(),
1203       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineEnded),
1204       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineError),
1205       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSeek),
1206       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineMetadata),
1207       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelinePrerollCompleted),
1208       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChanged));
1209 }
1210 
SetNetworkState(WebMediaPlayer::NetworkState state)1211 void WebMediaPlayerImpl::SetNetworkState(WebMediaPlayer::NetworkState state) {
1212   DVLOG(1) << __FUNCTION__ << "(" << state << ")";
1213   DCHECK(main_loop_->BelongsToCurrentThread());
1214   network_state_ = state;
1215   // Always notify to ensure client has the latest value.
1216   client_->networkStateChanged();
1217 }
1218 
SetReadyState(WebMediaPlayer::ReadyState state)1219 void WebMediaPlayerImpl::SetReadyState(WebMediaPlayer::ReadyState state) {
1220   DVLOG(1) << __FUNCTION__ << "(" << state << ")";
1221   DCHECK(main_loop_->BelongsToCurrentThread());
1222 
1223   if (state == WebMediaPlayer::ReadyStateHaveEnoughData && data_source_ &&
1224       data_source_->assume_fully_buffered() &&
1225       network_state_ == WebMediaPlayer::NetworkStateLoading)
1226     SetNetworkState(WebMediaPlayer::NetworkStateLoaded);
1227 
1228   ready_state_ = state;
1229   // Always notify to ensure client has the latest value.
1230   client_->readyStateChanged();
1231 }
1232 
audioSourceProvider()1233 blink::WebAudioSourceProvider* WebMediaPlayerImpl::audioSourceProvider() {
1234   return audio_source_provider_.get();
1235 }
1236 
IncrementExternallyAllocatedMemory()1237 void WebMediaPlayerImpl::IncrementExternallyAllocatedMemory() {
1238   DCHECK(main_loop_->BelongsToCurrentThread());
1239   incremented_externally_allocated_memory_ = true;
1240   v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
1241       kPlayerExtraMemory);
1242 }
1243 
GetPipelineDuration() const1244 double WebMediaPlayerImpl::GetPipelineDuration() const {
1245   base::TimeDelta duration = pipeline_.GetMediaDuration();
1246 
1247   // Return positive infinity if the resource is unbounded.
1248   // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#dom-media-duration
1249   if (duration == media::kInfiniteDuration())
1250     return std::numeric_limits<double>::infinity();
1251 
1252   return duration.InSecondsF();
1253 }
1254 
OnDurationChanged()1255 void WebMediaPlayerImpl::OnDurationChanged() {
1256   if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
1257     return;
1258 
1259   client_->durationChanged();
1260 }
1261 
OnNaturalSizeChanged(gfx::Size size)1262 void WebMediaPlayerImpl::OnNaturalSizeChanged(gfx::Size size) {
1263   DCHECK(main_loop_->BelongsToCurrentThread());
1264   DCHECK_NE(ready_state_, WebMediaPlayer::ReadyStateHaveNothing);
1265   TRACE_EVENT0("media", "WebMediaPlayerImpl::OnNaturalSizeChanged");
1266 
1267   media_log_->AddEvent(
1268       media_log_->CreateVideoSizeSetEvent(size.width(), size.height()));
1269   pipeline_metadata_.natural_size = size;
1270 
1271   client_->sizeChanged();
1272 }
1273 
OnOpacityChanged(bool opaque)1274 void WebMediaPlayerImpl::OnOpacityChanged(bool opaque) {
1275   DCHECK(main_loop_->BelongsToCurrentThread());
1276   DCHECK_NE(ready_state_, WebMediaPlayer::ReadyStateHaveNothing);
1277 
1278   opaque_ = opaque;
1279   if (video_weblayer_)
1280     video_weblayer_->setOpaque(opaque_);
1281 }
1282 
FrameReady(const scoped_refptr<media::VideoFrame> & frame)1283 void WebMediaPlayerImpl::FrameReady(
1284     const scoped_refptr<media::VideoFrame>& frame) {
1285   compositor_task_runner_->PostTask(
1286       FROM_HERE,
1287       base::Bind(&VideoFrameCompositor::UpdateCurrentFrame,
1288                  base::Unretained(compositor_),
1289                  frame));
1290 }
1291 
SetDecryptorReadyCB(const media::DecryptorReadyCB & decryptor_ready_cb)1292 void WebMediaPlayerImpl::SetDecryptorReadyCB(
1293      const media::DecryptorReadyCB& decryptor_ready_cb) {
1294   DCHECK(main_loop_->BelongsToCurrentThread());
1295 
1296   // Cancels the previous decryptor request.
1297   if (decryptor_ready_cb.is_null()) {
1298     if (!decryptor_ready_cb_.is_null())
1299       base::ResetAndReturn(&decryptor_ready_cb_).Run(NULL);
1300     return;
1301   }
1302 
1303   // TODO(xhwang): Support multiple decryptor notification request (e.g. from
1304   // video and audio). The current implementation is okay for the current
1305   // media pipeline since we initialize audio and video decoders in sequence.
1306   // But WebMediaPlayerImpl should not depend on media pipeline's implementation
1307   // detail.
1308   DCHECK(decryptor_ready_cb_.is_null());
1309 
1310   // Mixed use of prefixed and unprefixed EME APIs is disallowed by Blink.
1311   DCHECK(!proxy_decryptor_ || !web_cdm_);
1312 
1313   if (proxy_decryptor_) {
1314     decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor());
1315     return;
1316   }
1317 
1318   if (web_cdm_) {
1319     decryptor_ready_cb.Run(web_cdm_->GetDecryptor());
1320     return;
1321   }
1322 
1323   decryptor_ready_cb_ = decryptor_ready_cb;
1324 }
1325 
GetCurrentFrameAndSignal(VideoFrameCompositor * compositor,scoped_refptr<media::VideoFrame> * video_frame_out,base::WaitableEvent * event)1326 static void GetCurrentFrameAndSignal(
1327     VideoFrameCompositor* compositor,
1328     scoped_refptr<media::VideoFrame>* video_frame_out,
1329     base::WaitableEvent* event) {
1330   TRACE_EVENT0("media", "GetCurrentFrameAndSignal");
1331   *video_frame_out = compositor->GetCurrentFrame();
1332   event->Signal();
1333 }
1334 
1335 scoped_refptr<media::VideoFrame>
GetCurrentFrameFromCompositor()1336 WebMediaPlayerImpl::GetCurrentFrameFromCompositor() {
1337   TRACE_EVENT0("media", "WebMediaPlayerImpl::GetCurrentFrameFromCompositor");
1338   if (compositor_task_runner_->BelongsToCurrentThread())
1339     return compositor_->GetCurrentFrame();
1340 
1341   // Use a posted task and waitable event instead of a lock otherwise
1342   // WebGL/Canvas can see different content than what the compositor is seeing.
1343   scoped_refptr<media::VideoFrame> video_frame;
1344   base::WaitableEvent event(false, false);
1345   compositor_task_runner_->PostTask(FROM_HERE,
1346                                     base::Bind(&GetCurrentFrameAndSignal,
1347                                                base::Unretained(compositor_),
1348                                                &video_frame,
1349                                                &event));
1350   event.Wait();
1351   return video_frame;
1352 }
1353 
1354 }  // namespace content
1355