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/command_line.h"
15#include "base/debug/crash_logging.h"
16#include "base/message_loop/message_loop_proxy.h"
17#include "base/metrics/histogram.h"
18#include "base/strings/string_number_conversions.h"
19#include "base/synchronization/waitable_event.h"
20#include "cc/layers/video_layer.h"
21#include "content/public/common/content_switches.h"
22#include "content/renderer/media/buffered_data_source.h"
23#include "content/renderer/media/crypto/key_systems.h"
24#include "content/renderer/media/texttrack_impl.h"
25#include "content/renderer/media/webaudiosourceprovider_impl.h"
26#include "content/renderer/media/webinbandtexttrack_impl.h"
27#include "content/renderer/media/webmediaplayer_delegate.h"
28#include "content/renderer/media/webmediaplayer_params.h"
29#include "content/renderer/media/webmediaplayer_util.h"
30#include "content/renderer/media/webmediasourceclient_impl.h"
31#include "content/renderer/pepper/pepper_webplugin_impl.h"
32#include "gpu/GLES2/gl2extchromium.h"
33#include "media/audio/null_audio_sink.h"
34#include "media/base/bind_to_loop.h"
35#include "media/base/filter_collection.h"
36#include "media/base/limits.h"
37#include "media/base/media_log.h"
38#include "media/base/media_switches.h"
39#include "media/base/pipeline.h"
40#include "media/base/video_frame.h"
41#include "media/filters/audio_renderer_impl.h"
42#include "media/filters/chunk_demuxer.h"
43#include "media/filters/ffmpeg_audio_decoder.h"
44#include "media/filters/ffmpeg_demuxer.h"
45#include "media/filters/ffmpeg_video_decoder.h"
46#include "media/filters/gpu_video_decoder.h"
47#include "media/filters/gpu_video_decoder_factories.h"
48#include "media/filters/opus_audio_decoder.h"
49#include "media/filters/video_renderer_base.h"
50#include "media/filters/vpx_video_decoder.h"
51#include "third_party/WebKit/public/platform/WebRect.h"
52#include "third_party/WebKit/public/platform/WebSize.h"
53#include "third_party/WebKit/public/platform/WebString.h"
54#include "third_party/WebKit/public/platform/WebURL.h"
55#include "third_party/WebKit/public/web/WebMediaSource.h"
56#include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
57#include "third_party/WebKit/public/web/WebView.h"
58#include "v8/include/v8.h"
59#include "webkit/renderer/compositor_bindings/web_layer_impl.h"
60
61using WebKit::WebCanvas;
62using WebKit::WebMediaPlayer;
63using WebKit::WebRect;
64using WebKit::WebSize;
65using WebKit::WebString;
66using media::PipelineStatus;
67
68namespace {
69
70// Amount of extra memory used by each player instance reported to V8.
71// It is not exact number -- first, it differs on different platforms,
72// and second, it is very hard to calculate. Instead, use some arbitrary
73// value that will cause garbage collection from time to time. We don't want
74// it to happen on every allocation, but don't want 5k players to sit in memory
75// either. Looks that chosen constant achieves both goals, at least for audio
76// objects. (Do not worry about video objects yet, JS programs do not create
77// thousands of them...)
78const int kPlayerExtraMemory = 1024 * 1024;
79
80// Limits the range of playback rate.
81//
82// TODO(kylep): Revisit these.
83//
84// Vista has substantially lower performance than XP or Windows7.  If you speed
85// up a video too much, it can't keep up, and rendering stops updating except on
86// the time bar. For really high speeds, audio becomes a bottleneck and we just
87// use up the data we have, which may not achieve the speed requested, but will
88// not crash the tab.
89//
90// A very slow speed, ie 0.00000001x, causes the machine to lock up. (It seems
91// like a busy loop). It gets unresponsive, although its not completely dead.
92//
93// Also our timers are not very accurate (especially for ogg), which becomes
94// evident at low speeds and on Vista. Since other speeds are risky and outside
95// the norms, we think 1/16x to 16x is a safe and useful range for now.
96const double kMinRate = 0.0625;
97const double kMaxRate = 16.0;
98
99// Prefix for histograms related to Encrypted Media Extensions.
100const char* kMediaEme = "Media.EME.";
101
102}  // namespace
103
104namespace content {
105
106#define COMPILE_ASSERT_MATCHING_ENUM(name) \
107  COMPILE_ASSERT(static_cast<int>(WebMediaPlayer::CORSMode ## name) == \
108                 static_cast<int>(BufferedResourceLoader::k ## name), \
109                 mismatching_enums)
110COMPILE_ASSERT_MATCHING_ENUM(Unspecified);
111COMPILE_ASSERT_MATCHING_ENUM(Anonymous);
112COMPILE_ASSERT_MATCHING_ENUM(UseCredentials);
113#undef COMPILE_ASSERT_MATCHING_ENUM
114
115#define BIND_TO_RENDER_LOOP(function) \
116  media::BindToLoop(main_loop_, base::Bind(function, AsWeakPtr()))
117
118#define BIND_TO_RENDER_LOOP_1(function, arg1) \
119  media::BindToLoop(main_loop_, base::Bind(function, AsWeakPtr(), arg1))
120
121#define BIND_TO_RENDER_LOOP_2(function, arg1, arg2) \
122  media::BindToLoop(main_loop_, base::Bind(function, AsWeakPtr(), arg1, arg2))
123
124static void LogMediaSourceError(const scoped_refptr<media::MediaLog>& media_log,
125                                const std::string& error) {
126  media_log->AddEvent(media_log->CreateMediaSourceErrorEvent(error));
127}
128
129WebMediaPlayerImpl::WebMediaPlayerImpl(
130    WebKit::WebFrame* frame,
131    WebKit::WebMediaPlayerClient* client,
132    base::WeakPtr<WebMediaPlayerDelegate> delegate,
133    const WebMediaPlayerParams& params)
134    : frame_(frame),
135      network_state_(WebMediaPlayer::NetworkStateEmpty),
136      ready_state_(WebMediaPlayer::ReadyStateHaveNothing),
137      main_loop_(base::MessageLoopProxy::current()),
138      media_loop_(params.message_loop_proxy()),
139      paused_(true),
140      seeking_(false),
141      playback_rate_(0.0f),
142      pending_seek_(false),
143      pending_seek_seconds_(0.0f),
144      client_(client),
145      delegate_(delegate),
146      defer_load_cb_(params.defer_load_cb()),
147      media_log_(params.media_log()),
148      accelerated_compositing_reported_(false),
149      incremented_externally_allocated_memory_(false),
150      gpu_factories_(params.gpu_factories()),
151      is_local_source_(false),
152      supports_save_(true),
153      starting_(false),
154      chunk_demuxer_(NULL),
155      pending_repaint_(false),
156      pending_size_change_(false),
157      video_frame_provider_client_(NULL),
158      text_track_index_(0) {
159  media_log_->AddEvent(
160      media_log_->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_CREATED));
161
162  pipeline_.reset(new media::Pipeline(media_loop_, media_log_.get()));
163
164  // Let V8 know we started new thread if we did not do it yet.
165  // Made separate task to avoid deletion of player currently being created.
166  // Also, delaying GC until after player starts gets rid of starting lag --
167  // collection happens in parallel with playing.
168  //
169  // TODO(enal): remove when we get rid of per-audio-stream thread.
170  main_loop_->PostTask(
171      FROM_HERE,
172      base::Bind(&WebMediaPlayerImpl::IncrementExternallyAllocatedMemory,
173                 AsWeakPtr()));
174
175  // Also we want to be notified of |main_loop_| destruction.
176  base::MessageLoop::current()->AddDestructionObserver(this);
177
178  if (WebKit::WebRuntimeFeatures::isLegacyEncryptedMediaEnabled()) {
179    decryptor_.reset(new ProxyDecryptor(
180#if defined(ENABLE_PEPPER_CDMS)
181        client,
182        frame,
183#endif
184        BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyAdded),
185        BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyError),
186        BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyMessage)));
187  }
188
189  // Use the null sink if no sink was provided.
190  audio_source_provider_ = new WebAudioSourceProviderImpl(
191      params.audio_renderer_sink().get()
192          ? params.audio_renderer_sink()
193          : new media::NullAudioSink(media_loop_));
194}
195
196WebMediaPlayerImpl::~WebMediaPlayerImpl() {
197  SetVideoFrameProviderClient(NULL);
198  GetClient()->setWebLayer(NULL);
199
200  DCHECK(main_loop_->BelongsToCurrentThread());
201  media_log_->AddEvent(
202      media_log_->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_DESTROYED));
203
204  if (delegate_.get())
205    delegate_->PlayerGone(this);
206
207  Destroy();
208
209  // Remove destruction observer if we're being destroyed but the main thread is
210  // still running.
211  if (base::MessageLoop::current())
212    base::MessageLoop::current()->RemoveDestructionObserver(this);
213}
214
215namespace {
216
217// Helper enum for reporting scheme histograms.
218enum URLSchemeForHistogram {
219  kUnknownURLScheme,
220  kMissingURLScheme,
221  kHttpURLScheme,
222  kHttpsURLScheme,
223  kFtpURLScheme,
224  kChromeExtensionURLScheme,
225  kJavascriptURLScheme,
226  kFileURLScheme,
227  kBlobURLScheme,
228  kDataURLScheme,
229  kFileSystemScheme,
230  kMaxURLScheme = kFileSystemScheme  // Must be equal to highest enum value.
231};
232
233URLSchemeForHistogram URLScheme(const GURL& url) {
234  if (!url.has_scheme()) return kMissingURLScheme;
235  if (url.SchemeIs("http")) return kHttpURLScheme;
236  if (url.SchemeIs("https")) return kHttpsURLScheme;
237  if (url.SchemeIs("ftp")) return kFtpURLScheme;
238  if (url.SchemeIs("chrome-extension")) return kChromeExtensionURLScheme;
239  if (url.SchemeIs("javascript")) return kJavascriptURLScheme;
240  if (url.SchemeIs("file")) return kFileURLScheme;
241  if (url.SchemeIs("blob")) return kBlobURLScheme;
242  if (url.SchemeIs("data")) return kDataURLScheme;
243  if (url.SchemeIs("filesystem")) return kFileSystemScheme;
244  return kUnknownURLScheme;
245}
246
247}  // anonymous namespace
248
249void WebMediaPlayerImpl::load(const WebKit::WebURL& url, CORSMode cors_mode) {
250  load(url, NULL, cors_mode);
251}
252
253void WebMediaPlayerImpl::load(const WebKit::WebURL& url,
254                              WebKit::WebMediaSource* media_source,
255                              CORSMode cors_mode) {
256  if (!defer_load_cb_.is_null()) {
257    defer_load_cb_.Run(base::Bind(
258        &WebMediaPlayerImpl::DoLoad, AsWeakPtr(), url, media_source,
259        cors_mode));
260    return;
261  }
262  DoLoad(url, media_source, cors_mode);
263}
264
265void WebMediaPlayerImpl::DoLoad(const WebKit::WebURL& url,
266                                WebKit::WebMediaSource* media_source,
267                                CORSMode cors_mode) {
268  DCHECK(main_loop_->BelongsToCurrentThread());
269
270  GURL gurl(url);
271  UMA_HISTOGRAM_ENUMERATION("Media.URLScheme", URLScheme(gurl), kMaxURLScheme);
272
273  // Set subresource URL for crash reporting.
274  base::debug::SetCrashKeyValue("subresource_url", gurl.spec());
275
276  // Handle any volume/preload changes that occurred before load().
277  setVolume(GetClient()->volume());
278  setPreload(GetClient()->preload());
279
280  SetNetworkState(WebMediaPlayer::NetworkStateLoading);
281  SetReadyState(WebMediaPlayer::ReadyStateHaveNothing);
282  media_log_->AddEvent(media_log_->CreateLoadEvent(url.spec()));
283
284  // Media source pipelines can start immediately.
285  if (media_source) {
286    supports_save_ = false;
287    StartPipeline(media_source);
288    return;
289  }
290
291  // Otherwise it's a regular request which requires resolving the URL first.
292  data_source_.reset(new BufferedDataSource(
293      main_loop_,
294      frame_,
295      media_log_.get(),
296      base::Bind(&WebMediaPlayerImpl::NotifyDownloading, AsWeakPtr())));
297  data_source_->Initialize(
298      url, static_cast<BufferedResourceLoader::CORSMode>(cors_mode),
299      base::Bind(
300          &WebMediaPlayerImpl::DataSourceInitialized,
301          AsWeakPtr(), gurl));
302
303  is_local_source_ = !gurl.SchemeIs("http") && !gurl.SchemeIs("https");
304}
305
306void WebMediaPlayerImpl::play() {
307  DCHECK(main_loop_->BelongsToCurrentThread());
308
309  paused_ = false;
310  pipeline_->SetPlaybackRate(playback_rate_);
311
312  media_log_->AddEvent(media_log_->CreateEvent(media::MediaLogEvent::PLAY));
313
314  if (delegate_.get())
315    delegate_->DidPlay(this);
316}
317
318void WebMediaPlayerImpl::pause() {
319  DCHECK(main_loop_->BelongsToCurrentThread());
320
321  paused_ = true;
322  pipeline_->SetPlaybackRate(0.0f);
323  paused_time_ = pipeline_->GetMediaTime();
324
325  media_log_->AddEvent(media_log_->CreateEvent(media::MediaLogEvent::PAUSE));
326
327  if (delegate_.get())
328    delegate_->DidPause(this);
329}
330
331bool WebMediaPlayerImpl::supportsFullscreen() const {
332  DCHECK(main_loop_->BelongsToCurrentThread());
333  return true;
334}
335
336bool WebMediaPlayerImpl::supportsSave() const {
337  DCHECK(main_loop_->BelongsToCurrentThread());
338  return supports_save_;
339}
340
341void WebMediaPlayerImpl::seek(double seconds) {
342  DCHECK(main_loop_->BelongsToCurrentThread());
343
344  base::TimeDelta seek_time = ConvertSecondsToTimestamp(seconds);
345
346  if (starting_ || seeking_) {
347    pending_seek_ = true;
348    pending_seek_seconds_ = seconds;
349    if (chunk_demuxer_)
350      chunk_demuxer_->CancelPendingSeek(seek_time);
351    return;
352  }
353
354  media_log_->AddEvent(media_log_->CreateSeekEvent(seconds));
355
356  // Update our paused time.
357  if (paused_)
358    paused_time_ = seek_time;
359
360  seeking_ = true;
361
362  if (chunk_demuxer_)
363    chunk_demuxer_->StartWaitingForSeek(seek_time);
364
365  // Kick off the asynchronous seek!
366  pipeline_->Seek(
367      seek_time,
368      BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSeek));
369}
370
371void WebMediaPlayerImpl::setRate(double rate) {
372  DCHECK(main_loop_->BelongsToCurrentThread());
373
374  // TODO(kylep): Remove when support for negatives is added. Also, modify the
375  // following checks so rewind uses reasonable values also.
376  if (rate < 0.0)
377    return;
378
379  // Limit rates to reasonable values by clamping.
380  if (rate != 0.0) {
381    if (rate < kMinRate)
382      rate = kMinRate;
383    else if (rate > kMaxRate)
384      rate = kMaxRate;
385  }
386
387  playback_rate_ = rate;
388  if (!paused_) {
389    pipeline_->SetPlaybackRate(rate);
390  }
391}
392
393void WebMediaPlayerImpl::setVolume(double volume) {
394  DCHECK(main_loop_->BelongsToCurrentThread());
395
396  pipeline_->SetVolume(volume);
397}
398
399#define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, chromium_name) \
400    COMPILE_ASSERT(static_cast<int>(WebMediaPlayer::webkit_name) == \
401                   static_cast<int>(content::chromium_name), \
402                   mismatching_enums)
403COMPILE_ASSERT_MATCHING_ENUM(PreloadNone, NONE);
404COMPILE_ASSERT_MATCHING_ENUM(PreloadMetaData, METADATA);
405COMPILE_ASSERT_MATCHING_ENUM(PreloadAuto, AUTO);
406#undef COMPILE_ASSERT_MATCHING_ENUM
407
408void WebMediaPlayerImpl::setPreload(WebMediaPlayer::Preload preload) {
409  DCHECK(main_loop_->BelongsToCurrentThread());
410
411  if (data_source_)
412    data_source_->SetPreload(static_cast<content::Preload>(preload));
413}
414
415bool WebMediaPlayerImpl::hasVideo() const {
416  DCHECK(main_loop_->BelongsToCurrentThread());
417
418  return pipeline_->HasVideo();
419}
420
421bool WebMediaPlayerImpl::hasAudio() const {
422  DCHECK(main_loop_->BelongsToCurrentThread());
423
424  return pipeline_->HasAudio();
425}
426
427WebKit::WebSize WebMediaPlayerImpl::naturalSize() const {
428  DCHECK(main_loop_->BelongsToCurrentThread());
429
430  gfx::Size size;
431  pipeline_->GetNaturalVideoSize(&size);
432  return WebKit::WebSize(size);
433}
434
435bool WebMediaPlayerImpl::paused() const {
436  DCHECK(main_loop_->BelongsToCurrentThread());
437
438  return pipeline_->GetPlaybackRate() == 0.0f;
439}
440
441bool WebMediaPlayerImpl::seeking() const {
442  DCHECK(main_loop_->BelongsToCurrentThread());
443
444  if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
445    return false;
446
447  return seeking_;
448}
449
450double 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
459double WebMediaPlayerImpl::currentTime() const {
460  DCHECK(main_loop_->BelongsToCurrentThread());
461  return (paused_ ? paused_time_ : pipeline_->GetMediaTime()).InSecondsF();
462}
463
464WebMediaPlayer::NetworkState WebMediaPlayerImpl::networkState() const {
465  DCHECK(main_loop_->BelongsToCurrentThread());
466  return network_state_;
467}
468
469WebMediaPlayer::ReadyState WebMediaPlayerImpl::readyState() const {
470  DCHECK(main_loop_->BelongsToCurrentThread());
471  return ready_state_;
472}
473
474const WebKit::WebTimeRanges& WebMediaPlayerImpl::buffered() {
475  DCHECK(main_loop_->BelongsToCurrentThread());
476  WebKit::WebTimeRanges web_ranges(
477      ConvertToWebTimeRanges(pipeline_->GetBufferedTimeRanges()));
478  buffered_.swap(web_ranges);
479  return buffered_;
480}
481
482double WebMediaPlayerImpl::maxTimeSeekable() const {
483  DCHECK(main_loop_->BelongsToCurrentThread());
484
485  // If we haven't even gotten to ReadyStateHaveMetadata yet then just
486  // return 0 so that the seekable range is empty.
487  if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata)
488    return 0.0;
489
490  // We don't support seeking in streaming media.
491  if (data_source_ && data_source_->IsStreaming())
492    return 0.0;
493  return duration();
494}
495
496bool WebMediaPlayerImpl::didLoadingProgress() const {
497  DCHECK(main_loop_->BelongsToCurrentThread());
498  return pipeline_->DidLoadingProgress();
499}
500
501void WebMediaPlayerImpl::paint(WebCanvas* canvas,
502                               const WebRect& rect,
503                               unsigned char alpha) {
504  DCHECK(main_loop_->BelongsToCurrentThread());
505
506  if (!accelerated_compositing_reported_) {
507    accelerated_compositing_reported_ = true;
508    // Normally paint() is only called in non-accelerated rendering, but there
509    // are exceptions such as webgl where compositing is used in the WebView but
510    // video frames are still rendered to a canvas.
511    UMA_HISTOGRAM_BOOLEAN(
512        "Media.AcceleratedCompositingActive",
513        frame_->view()->isAcceleratedCompositingActive());
514  }
515
516  // Avoid locking and potentially blocking the video rendering thread while
517  // painting in software.
518  scoped_refptr<media::VideoFrame> video_frame;
519  {
520    base::AutoLock auto_lock(lock_);
521    video_frame = current_frame_;
522  }
523  gfx::Rect gfx_rect(rect);
524  skcanvas_video_renderer_.Paint(video_frame.get(), canvas, gfx_rect, alpha);
525}
526
527bool WebMediaPlayerImpl::hasSingleSecurityOrigin() const {
528  if (data_source_)
529    return data_source_->HasSingleOrigin();
530  return true;
531}
532
533bool WebMediaPlayerImpl::didPassCORSAccessCheck() const {
534  if (data_source_)
535    return data_source_->DidPassCORSAccessCheck();
536  return false;
537}
538
539double WebMediaPlayerImpl::mediaTimeForTimeValue(double timeValue) const {
540  return ConvertSecondsToTimestamp(timeValue).InSecondsF();
541}
542
543unsigned WebMediaPlayerImpl::decodedFrameCount() const {
544  DCHECK(main_loop_->BelongsToCurrentThread());
545
546  media::PipelineStatistics stats = pipeline_->GetStatistics();
547  return stats.video_frames_decoded;
548}
549
550unsigned WebMediaPlayerImpl::droppedFrameCount() const {
551  DCHECK(main_loop_->BelongsToCurrentThread());
552
553  media::PipelineStatistics stats = pipeline_->GetStatistics();
554  return stats.video_frames_dropped;
555}
556
557unsigned WebMediaPlayerImpl::audioDecodedByteCount() const {
558  DCHECK(main_loop_->BelongsToCurrentThread());
559
560  media::PipelineStatistics stats = pipeline_->GetStatistics();
561  return stats.audio_bytes_decoded;
562}
563
564unsigned WebMediaPlayerImpl::videoDecodedByteCount() const {
565  DCHECK(main_loop_->BelongsToCurrentThread());
566
567  media::PipelineStatistics stats = pipeline_->GetStatistics();
568  return stats.video_bytes_decoded;
569}
570
571void WebMediaPlayerImpl::SetVideoFrameProviderClient(
572    cc::VideoFrameProvider::Client* client) {
573  // This is called from both the main renderer thread and the compositor
574  // thread (when the main thread is blocked).
575  if (video_frame_provider_client_)
576    video_frame_provider_client_->StopUsingProvider();
577  video_frame_provider_client_ = client;
578}
579
580scoped_refptr<media::VideoFrame> WebMediaPlayerImpl::GetCurrentFrame() {
581  base::AutoLock auto_lock(lock_);
582  return current_frame_;
583}
584
585void WebMediaPlayerImpl::PutCurrentFrame(
586    const scoped_refptr<media::VideoFrame>& frame) {
587  if (!accelerated_compositing_reported_) {
588    accelerated_compositing_reported_ = true;
589    DCHECK(frame_->view()->isAcceleratedCompositingActive());
590    UMA_HISTOGRAM_BOOLEAN("Media.AcceleratedCompositingActive", true);
591  }
592}
593
594bool WebMediaPlayerImpl::copyVideoTextureToPlatformTexture(
595    WebKit::WebGraphicsContext3D* web_graphics_context,
596    unsigned int texture,
597    unsigned int level,
598    unsigned int internal_format,
599    unsigned int type,
600    bool premultiply_alpha,
601    bool flip_y) {
602  scoped_refptr<media::VideoFrame> video_frame;
603  {
604    base::AutoLock auto_lock(lock_);
605    video_frame = current_frame_;
606  }
607
608  if (!video_frame.get())
609    return false;
610  if (video_frame->format() != media::VideoFrame::NATIVE_TEXTURE)
611    return false;
612  if (video_frame->texture_target() != GL_TEXTURE_2D)
613    return false;
614
615  scoped_refptr<media::VideoFrame::MailboxHolder> mailbox_holder =
616      video_frame->texture_mailbox();
617
618  uint32 source_texture = web_graphics_context->createTexture();
619
620  web_graphics_context->waitSyncPoint(mailbox_holder->sync_point());
621  web_graphics_context->bindTexture(GL_TEXTURE_2D, source_texture);
622  web_graphics_context->consumeTextureCHROMIUM(GL_TEXTURE_2D,
623                                               mailbox_holder->mailbox().name);
624
625  // The video is stored in a unmultiplied format, so premultiply
626  // if necessary.
627  web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
628                                    premultiply_alpha);
629  // Application itself needs to take care of setting the right flip_y
630  // value down to get the expected result.
631  // flip_y==true means to reverse the video orientation while
632  // flip_y==false means to keep the intrinsic orientation.
633  web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y);
634  web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D,
635                                            source_texture,
636                                            texture,
637                                            level,
638                                            internal_format,
639                                            type);
640  web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false);
641  web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
642                                    false);
643
644  web_graphics_context->deleteTexture(source_texture);
645
646  // The flush() operation is not necessary here. It is kept since the
647  // performance will be better when it is added than not.
648  web_graphics_context->flush();
649  return true;
650}
651
652// Helper functions to report media EME related stats to UMA. They follow the
653// convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and
654// UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is
655// that UMA_* macros require the names to be constant throughout the process'
656// lifetime.
657static void EmeUMAHistogramEnumeration(const WebKit::WebString& key_system,
658                                       const std::string& method,
659                                       int sample,
660                                       int boundary_value) {
661  base::LinearHistogram::FactoryGet(
662      kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
663      1, boundary_value, boundary_value + 1,
664      base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
665}
666
667static void EmeUMAHistogramCounts(const WebKit::WebString& key_system,
668                                  const std::string& method,
669                                  int sample) {
670  // Use the same parameters as UMA_HISTOGRAM_COUNTS.
671  base::Histogram::FactoryGet(
672      kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
673      1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
674}
675
676// Helper enum for reporting generateKeyRequest/addKey histograms.
677enum MediaKeyException {
678  kUnknownResultId,
679  kSuccess,
680  kKeySystemNotSupported,
681  kInvalidPlayerState,
682  kMaxMediaKeyException
683};
684
685static MediaKeyException MediaKeyExceptionForUMA(
686    WebMediaPlayer::MediaKeyException e) {
687  switch (e) {
688    case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
689      return kKeySystemNotSupported;
690    case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
691      return kInvalidPlayerState;
692    case WebMediaPlayer::MediaKeyExceptionNoError:
693      return kSuccess;
694    default:
695      return kUnknownResultId;
696  }
697}
698
699// Helper for converting |key_system| name and exception |e| to a pair of enum
700// values from above, for reporting to UMA.
701static void ReportMediaKeyExceptionToUMA(
702    const std::string& method,
703    const WebString& key_system,
704    WebMediaPlayer::MediaKeyException e) {
705  MediaKeyException result_id = MediaKeyExceptionForUMA(e);
706  DCHECK_NE(result_id, kUnknownResultId) << e;
707  EmeUMAHistogramEnumeration(
708      key_system, method, result_id, kMaxMediaKeyException);
709}
710
711WebMediaPlayer::MediaKeyException
712WebMediaPlayerImpl::generateKeyRequest(const WebString& key_system,
713                                       const unsigned char* init_data,
714                                       unsigned init_data_length) {
715  WebMediaPlayer::MediaKeyException e =
716      GenerateKeyRequestInternal(key_system, init_data, init_data_length);
717  ReportMediaKeyExceptionToUMA("generateKeyRequest", key_system, e);
718  return e;
719}
720
721WebMediaPlayer::MediaKeyException
722WebMediaPlayerImpl::GenerateKeyRequestInternal(
723    const WebString& key_system,
724    const unsigned char* init_data,
725    unsigned init_data_length) {
726  DVLOG(1) << "generateKeyRequest: " << key_system.utf8().data() << ": "
727           << std::string(reinterpret_cast<const char*>(init_data),
728                          static_cast<size_t>(init_data_length));
729
730  if (!IsSupportedKeySystem(key_system))
731    return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
732
733  // We do not support run-time switching between key systems for now.
734  if (current_key_system_.isEmpty()) {
735    if (!decryptor_->InitializeCDM(key_system.utf8()))
736      return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
737    current_key_system_ = key_system;
738  }
739  else if (key_system != current_key_system_) {
740    return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
741  }
742
743  // TODO(xhwang): We assume all streams are from the same container (thus have
744  // the same "type") for now. In the future, the "type" should be passed down
745  // from the application.
746  if (!decryptor_->GenerateKeyRequest(init_data_type_,
747                                      init_data, init_data_length)) {
748    current_key_system_.reset();
749    return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
750  }
751
752  return WebMediaPlayer::MediaKeyExceptionNoError;
753}
754
755WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::addKey(
756    const WebString& key_system,
757    const unsigned char* key,
758    unsigned key_length,
759    const unsigned char* init_data,
760    unsigned init_data_length,
761    const WebString& session_id) {
762  WebMediaPlayer::MediaKeyException e = AddKeyInternal(
763      key_system, key, key_length, init_data, init_data_length, session_id);
764  ReportMediaKeyExceptionToUMA("addKey", key_system, e);
765  return e;
766}
767
768WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::AddKeyInternal(
769    const WebString& key_system,
770    const unsigned char* key,
771    unsigned key_length,
772    const unsigned char* init_data,
773    unsigned init_data_length,
774    const WebString& session_id) {
775  DCHECK(key);
776  DCHECK_GT(key_length, 0u);
777  DVLOG(1) << "addKey: " << key_system.utf8().data() << ": "
778           << std::string(reinterpret_cast<const char*>(key),
779                          static_cast<size_t>(key_length)) << ", "
780           << std::string(reinterpret_cast<const char*>(init_data),
781                          static_cast<size_t>(init_data_length))
782           << " [" << session_id.utf8().data() << "]";
783
784
785  if (!IsSupportedKeySystem(key_system))
786    return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
787
788  if (current_key_system_.isEmpty() || key_system != current_key_system_)
789    return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
790
791  decryptor_->AddKey(key, key_length,
792                     init_data, init_data_length, session_id.utf8());
793  return WebMediaPlayer::MediaKeyExceptionNoError;
794}
795
796WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::cancelKeyRequest(
797    const WebString& key_system,
798    const WebString& session_id) {
799  WebMediaPlayer::MediaKeyException e =
800      CancelKeyRequestInternal(key_system, session_id);
801  ReportMediaKeyExceptionToUMA("cancelKeyRequest", key_system, e);
802  return e;
803}
804
805WebMediaPlayer::MediaKeyException
806WebMediaPlayerImpl::CancelKeyRequestInternal(
807    const WebString& key_system,
808    const WebString& session_id) {
809  if (!IsSupportedKeySystem(key_system))
810    return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
811
812  if (current_key_system_.isEmpty() || key_system != current_key_system_)
813    return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
814
815  decryptor_->CancelKeyRequest(session_id.utf8());
816  return WebMediaPlayer::MediaKeyExceptionNoError;
817}
818
819void WebMediaPlayerImpl::WillDestroyCurrentMessageLoop() {
820  Destroy();
821}
822
823void WebMediaPlayerImpl::Repaint() {
824  DCHECK(main_loop_->BelongsToCurrentThread());
825
826  bool size_changed = false;
827  {
828    base::AutoLock auto_lock(lock_);
829    std::swap(pending_size_change_, size_changed);
830    pending_repaint_ = false;
831  }
832
833  if (size_changed)
834    GetClient()->sizeChanged();
835
836  GetClient()->repaint();
837}
838
839void WebMediaPlayerImpl::OnPipelineSeek(PipelineStatus status) {
840  DCHECK(main_loop_->BelongsToCurrentThread());
841  starting_ = false;
842  seeking_ = false;
843  if (pending_seek_) {
844    pending_seek_ = false;
845    seek(pending_seek_seconds_);
846    return;
847  }
848
849  if (status != media::PIPELINE_OK) {
850    OnPipelineError(status);
851    return;
852  }
853
854  // Update our paused time.
855  if (paused_)
856    paused_time_ = pipeline_->GetMediaTime();
857
858  GetClient()->timeChanged();
859}
860
861void WebMediaPlayerImpl::OnPipelineEnded() {
862  DCHECK(main_loop_->BelongsToCurrentThread());
863  GetClient()->timeChanged();
864}
865
866void WebMediaPlayerImpl::OnPipelineError(PipelineStatus error) {
867  DCHECK(main_loop_->BelongsToCurrentThread());
868  DCHECK_NE(error, media::PIPELINE_OK);
869
870  if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing) {
871    // Any error that occurs before reaching ReadyStateHaveMetadata should
872    // be considered a format error.
873    SetNetworkState(WebMediaPlayer::NetworkStateFormatError);
874    Repaint();
875    return;
876  }
877
878  SetNetworkState(PipelineErrorToNetworkState(error));
879
880  if (error == media::PIPELINE_ERROR_DECRYPT)
881    EmeUMAHistogramCounts(current_key_system_, "DecryptError", 1);
882
883  // Repaint to trigger UI update.
884  Repaint();
885}
886
887void WebMediaPlayerImpl::OnPipelineBufferingState(
888    media::Pipeline::BufferingState buffering_state) {
889  DVLOG(1) << "OnPipelineBufferingState(" << buffering_state << ")";
890
891  switch (buffering_state) {
892    case media::Pipeline::kHaveMetadata:
893      SetReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
894
895      if (hasVideo() && GetClient()->needsWebLayerForVideo()) {
896        DCHECK(!video_weblayer_);
897        video_weblayer_.reset(
898            new webkit::WebLayerImpl(cc::VideoLayer::Create(this)));
899        GetClient()->setWebLayer(video_weblayer_.get());
900      }
901      break;
902    case media::Pipeline::kPrerollCompleted:
903      // Only transition to ReadyStateHaveEnoughData if we don't have
904      // any pending seeks because the transition can cause Blink to
905      // report that the most recent seek has completed.
906      if (!pending_seek_)
907        SetReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
908      break;
909  }
910
911  // Repaint to trigger UI update.
912  Repaint();
913}
914
915void WebMediaPlayerImpl::OnDemuxerOpened(
916    scoped_ptr<WebKit::WebMediaSource> media_source) {
917  DCHECK(main_loop_->BelongsToCurrentThread());
918  media_source->open(new WebMediaSourceClientImpl(
919      chunk_demuxer_, base::Bind(&LogMediaSourceError, media_log_)));
920}
921
922void WebMediaPlayerImpl::OnKeyAdded(const std::string& session_id) {
923  DCHECK(main_loop_->BelongsToCurrentThread());
924  EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1);
925  GetClient()->keyAdded(current_key_system_,
926                        WebString::fromUTF8(session_id));
927}
928
929void WebMediaPlayerImpl::OnNeedKey(const std::string& session_id,
930                                   const std::string& type,
931                                   scoped_ptr<uint8[]> init_data,
932                                   int init_data_size) {
933  DCHECK(main_loop_->BelongsToCurrentThread());
934
935  // Do not fire NeedKey event if encrypted media is not enabled.
936  if (!decryptor_)
937    return;
938
939  UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1);
940
941  DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_);
942  if (init_data_type_.empty())
943    init_data_type_ = type;
944
945  GetClient()->keyNeeded(WebString(),
946                         WebString::fromUTF8(session_id),
947                         init_data.get(),
948                         init_data_size);
949}
950
951scoped_ptr<media::TextTrack>
952WebMediaPlayerImpl::OnTextTrack(media::TextKind kind,
953                                const std::string& label,
954                                const std::string& language) {
955  typedef WebInbandTextTrackImpl::Kind webkind_t;
956  const webkind_t webkind = static_cast<webkind_t>(kind);
957  const WebKit::WebString weblabel = WebKit::WebString::fromUTF8(label);
958  const WebKit::WebString weblanguage = WebKit::WebString::fromUTF8(language);
959
960  WebInbandTextTrackImpl* const text_track =
961    new WebInbandTextTrackImpl(webkind, weblabel, weblanguage,
962                               text_track_index_++);
963
964  return scoped_ptr<media::TextTrack>(new TextTrackImpl(GetClient(),
965                                                        text_track));
966}
967
968void WebMediaPlayerImpl::OnKeyError(const std::string& session_id,
969                                    media::MediaKeys::KeyError error_code,
970                                    int system_code) {
971  DCHECK(main_loop_->BelongsToCurrentThread());
972
973  EmeUMAHistogramEnumeration(current_key_system_, "KeyError",
974                             error_code, media::MediaKeys::kMaxKeyError);
975
976  GetClient()->keyError(
977      current_key_system_,
978      WebString::fromUTF8(session_id),
979      static_cast<WebKit::WebMediaPlayerClient::MediaKeyErrorCode>(error_code),
980      system_code);
981}
982
983void WebMediaPlayerImpl::OnKeyMessage(const std::string& session_id,
984                                      const std::vector<uint8>& message,
985                                      const std::string& default_url) {
986  DCHECK(main_loop_->BelongsToCurrentThread());
987
988  const GURL default_url_gurl(default_url);
989  DLOG_IF(WARNING, !default_url.empty() && !default_url_gurl.is_valid())
990      << "Invalid URL in default_url: " << default_url;
991
992  GetClient()->keyMessage(current_key_system_,
993                          WebString::fromUTF8(session_id),
994                          message.empty() ? NULL : &message[0],
995                          message.size(),
996                          default_url_gurl);
997}
998
999void WebMediaPlayerImpl::SetOpaque(bool opaque) {
1000  DCHECK(main_loop_->BelongsToCurrentThread());
1001
1002  GetClient()->setOpaque(opaque);
1003}
1004
1005void WebMediaPlayerImpl::DataSourceInitialized(const GURL& gurl, bool success) {
1006  DCHECK(main_loop_->BelongsToCurrentThread());
1007
1008  if (!success) {
1009    SetNetworkState(WebMediaPlayer::NetworkStateFormatError);
1010    Repaint();
1011    return;
1012  }
1013
1014  StartPipeline(NULL);
1015}
1016
1017void WebMediaPlayerImpl::NotifyDownloading(bool is_downloading) {
1018  if (!is_downloading && network_state_ == WebMediaPlayer::NetworkStateLoading)
1019    SetNetworkState(WebMediaPlayer::NetworkStateIdle);
1020  else if (is_downloading && network_state_ == WebMediaPlayer::NetworkStateIdle)
1021    SetNetworkState(WebMediaPlayer::NetworkStateLoading);
1022  media_log_->AddEvent(
1023      media_log_->CreateBooleanEvent(
1024          media::MediaLogEvent::NETWORK_ACTIVITY_SET,
1025          "is_downloading_data", is_downloading));
1026}
1027
1028void WebMediaPlayerImpl::StartPipeline(WebKit::WebMediaSource* media_source) {
1029  const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
1030  bool increase_preroll_on_underflow = true;
1031
1032  // Keep track if this is a MSE or non-MSE playback.
1033  UMA_HISTOGRAM_BOOLEAN("Media.MSE.Playback", (media_source != NULL));
1034
1035  // Figure out which demuxer to use.
1036  if (!media_source) {
1037    DCHECK(!chunk_demuxer_);
1038    DCHECK(data_source_);
1039
1040    demuxer_.reset(new media::FFmpegDemuxer(
1041        media_loop_, data_source_.get(),
1042        BIND_TO_RENDER_LOOP_1(&WebMediaPlayerImpl::OnNeedKey, ""),
1043        media_log_));
1044  } else {
1045    DCHECK(!chunk_demuxer_);
1046    DCHECK(!data_source_);
1047
1048    media::AddTextTrackCB add_text_track_cb;
1049
1050    if (cmd_line->HasSwitch(switches::kEnableInbandTextTracks)) {
1051      add_text_track_cb =
1052          base::Bind(&WebMediaPlayerImpl::OnTextTrack, base::Unretained(this));
1053    }
1054
1055    scoped_ptr<WebKit::WebMediaSource> ms(media_source);
1056    chunk_demuxer_ = new media::ChunkDemuxer(
1057        BIND_TO_RENDER_LOOP_1(&WebMediaPlayerImpl::OnDemuxerOpened,
1058                              base::Passed(&ms)),
1059        BIND_TO_RENDER_LOOP_1(&WebMediaPlayerImpl::OnNeedKey, ""),
1060        add_text_track_cb,
1061        base::Bind(&LogMediaSourceError, media_log_));
1062    demuxer_.reset(chunk_demuxer_);
1063
1064#if !defined(OS_CHROMEOS)
1065    // Disable GpuVideoDecoder creation on platforms other than CrOS until
1066    // they support codec config changes.
1067    // TODO(acolwell): Remove this once http://crbug.com/151045 is fixed.
1068    gpu_factories_ = NULL;
1069#endif
1070
1071    // Disable preroll increases on underflow since the web application has no
1072    // way to detect that this is happening and runs the risk of triggering
1073    // unwanted garbage collection if it is to aggressive about appending data.
1074    // TODO(acolwell): Remove this once http://crbug.com/144683 is fixed.
1075    increase_preroll_on_underflow = false;
1076  }
1077
1078  scoped_ptr<media::FilterCollection> filter_collection(
1079      new media::FilterCollection());
1080  filter_collection->SetDemuxer(demuxer_.get());
1081
1082  // Figure out if EME is enabled.
1083  media::SetDecryptorReadyCB set_decryptor_ready_cb;
1084  if (decryptor_) {
1085    set_decryptor_ready_cb = base::Bind(&ProxyDecryptor::SetDecryptorReadyCB,
1086                                        base::Unretained(decryptor_.get()));
1087  }
1088
1089  // Create our audio decoders and renderer.
1090  ScopedVector<media::AudioDecoder> audio_decoders;
1091  audio_decoders.push_back(new media::FFmpegAudioDecoder(media_loop_));
1092  if (cmd_line->HasSwitch(switches::kEnableOpusPlayback)) {
1093    audio_decoders.push_back(new media::OpusAudioDecoder(media_loop_));
1094  }
1095
1096  scoped_ptr<media::AudioRenderer> audio_renderer(
1097      new media::AudioRendererImpl(media_loop_,
1098                                   audio_source_provider_.get(),
1099                                   audio_decoders.Pass(),
1100                                   set_decryptor_ready_cb,
1101                                   increase_preroll_on_underflow));
1102  filter_collection->SetAudioRenderer(audio_renderer.Pass());
1103
1104  // Create our video decoders and renderer.
1105  ScopedVector<media::VideoDecoder> video_decoders;
1106
1107  if (gpu_factories_.get())
1108    video_decoders.push_back(new media::GpuVideoDecoder(gpu_factories_));
1109
1110  // TODO(phajdan.jr): Remove ifdefs when libvpx with vp9 support is released
1111  // (http://crbug.com/174287) .
1112#if !defined(MEDIA_DISABLE_LIBVPX)
1113  video_decoders.push_back(new media::VpxVideoDecoder(media_loop_));
1114#endif  // !defined(MEDIA_DISABLE_LIBVPX)
1115
1116  video_decoders.push_back(new media::FFmpegVideoDecoder(media_loop_));
1117
1118  scoped_ptr<media::VideoRenderer> video_renderer(
1119      new media::VideoRendererBase(
1120          media_loop_,
1121          video_decoders.Pass(),
1122          set_decryptor_ready_cb,
1123          base::Bind(&WebMediaPlayerImpl::FrameReady, base::Unretained(this)),
1124          BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::SetOpaque),
1125          true));
1126  filter_collection->SetVideoRenderer(video_renderer.Pass());
1127
1128  // ... and we're ready to go!
1129  starting_ = true;
1130  pipeline_->Start(
1131      filter_collection.Pass(),
1132      BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineEnded),
1133      BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineError),
1134      BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSeek),
1135      BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineBufferingState),
1136      BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChange));
1137}
1138
1139void WebMediaPlayerImpl::SetNetworkState(WebMediaPlayer::NetworkState state) {
1140  DCHECK(main_loop_->BelongsToCurrentThread());
1141  DVLOG(1) << "SetNetworkState: " << state;
1142  network_state_ = state;
1143  // Always notify to ensure client has the latest value.
1144  GetClient()->networkStateChanged();
1145}
1146
1147void WebMediaPlayerImpl::SetReadyState(WebMediaPlayer::ReadyState state) {
1148  DCHECK(main_loop_->BelongsToCurrentThread());
1149  DVLOG(1) << "SetReadyState: " << state;
1150
1151  if (state == WebMediaPlayer::ReadyStateHaveEnoughData &&
1152      is_local_source_ &&
1153      network_state_ == WebMediaPlayer::NetworkStateLoading)
1154    SetNetworkState(WebMediaPlayer::NetworkStateLoaded);
1155
1156  ready_state_ = state;
1157  // Always notify to ensure client has the latest value.
1158  GetClient()->readyStateChanged();
1159}
1160
1161void WebMediaPlayerImpl::Destroy() {
1162  DCHECK(main_loop_->BelongsToCurrentThread());
1163
1164  // Abort any pending IO so stopping the pipeline doesn't get blocked.
1165  if (data_source_)
1166    data_source_->Abort();
1167  if (chunk_demuxer_) {
1168    chunk_demuxer_->Shutdown();
1169    chunk_demuxer_ = NULL;
1170  }
1171
1172  if (gpu_factories_.get()) {
1173    gpu_factories_->Abort();
1174    gpu_factories_ = NULL;
1175  }
1176
1177  // Make sure to kill the pipeline so there's no more media threads running.
1178  // Note: stopping the pipeline might block for a long time.
1179  base::WaitableEvent waiter(false, false);
1180  pipeline_->Stop(base::Bind(
1181      &base::WaitableEvent::Signal, base::Unretained(&waiter)));
1182  waiter.Wait();
1183
1184  // Let V8 know we are not using extra resources anymore.
1185  if (incremented_externally_allocated_memory_) {
1186    v8::V8::AdjustAmountOfExternalAllocatedMemory(-kPlayerExtraMemory);
1187    incremented_externally_allocated_memory_ = false;
1188  }
1189
1190  // Release any final references now that everything has stopped.
1191  pipeline_.reset();
1192  demuxer_.reset();
1193  data_source_.reset();
1194}
1195
1196WebKit::WebMediaPlayerClient* WebMediaPlayerImpl::GetClient() {
1197  DCHECK(main_loop_->BelongsToCurrentThread());
1198  DCHECK(client_);
1199  return client_;
1200}
1201
1202WebKit::WebAudioSourceProvider* WebMediaPlayerImpl::audioSourceProvider() {
1203  return audio_source_provider_.get();
1204}
1205
1206void WebMediaPlayerImpl::IncrementExternallyAllocatedMemory() {
1207  DCHECK(main_loop_->BelongsToCurrentThread());
1208  incremented_externally_allocated_memory_ = true;
1209  v8::V8::AdjustAmountOfExternalAllocatedMemory(kPlayerExtraMemory);
1210}
1211
1212double WebMediaPlayerImpl::GetPipelineDuration() const {
1213  base::TimeDelta duration = pipeline_->GetMediaDuration();
1214
1215  // Return positive infinity if the resource is unbounded.
1216  // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#dom-media-duration
1217  if (duration == media::kInfiniteDuration())
1218    return std::numeric_limits<double>::infinity();
1219
1220  return duration.InSecondsF();
1221}
1222
1223void WebMediaPlayerImpl::OnDurationChange() {
1224  if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
1225    return;
1226
1227  GetClient()->durationChanged();
1228}
1229
1230void WebMediaPlayerImpl::FrameReady(
1231    const scoped_refptr<media::VideoFrame>& frame) {
1232  base::AutoLock auto_lock(lock_);
1233
1234  if (current_frame_.get() &&
1235      current_frame_->natural_size() != frame->natural_size() &&
1236      !pending_size_change_) {
1237    pending_size_change_ = true;
1238  }
1239
1240  current_frame_ = frame;
1241
1242  if (pending_repaint_)
1243    return;
1244
1245  pending_repaint_ = true;
1246  main_loop_->PostTask(FROM_HERE, base::Bind(
1247      &WebMediaPlayerImpl::Repaint, AsWeakPtr()));
1248}
1249
1250}  // namespace content
1251