1// Copyright (c) 2011 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 "webkit/glue/webmediaplayer_impl.h"
6
7#include <limits>
8#include <string>
9
10#include "base/callback.h"
11#include "base/command_line.h"
12#include "media/base/composite_data_source_factory.h"
13#include "media/base/filter_collection.h"
14#include "media/base/limits.h"
15#include "media/base/media_format.h"
16#include "media/base/media_switches.h"
17#include "media/base/pipeline_impl.h"
18#include "media/base/video_frame.h"
19#include "media/filters/adaptive_demuxer.h"
20#include "media/filters/ffmpeg_audio_decoder.h"
21#include "media/filters/ffmpeg_demuxer_factory.h"
22#include "media/filters/ffmpeg_video_decoder.h"
23#include "media/filters/rtc_video_decoder.h"
24#include "media/filters/null_audio_renderer.h"
25#include "third_party/WebKit/Source/WebKit/chromium/public/WebRect.h"
26#include "third_party/WebKit/Source/WebKit/chromium/public/WebSize.h"
27#include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h"
28#include "third_party/WebKit/Source/WebKit/chromium/public/WebVideoFrame.h"
29#include "webkit/glue/media/buffered_data_source.h"
30#include "webkit/glue/media/simple_data_source.h"
31#include "webkit/glue/media/video_renderer_impl.h"
32#include "webkit/glue/media/web_video_renderer.h"
33#include "webkit/glue/webvideoframe_impl.h"
34
35using WebKit::WebCanvas;
36using WebKit::WebRect;
37using WebKit::WebSize;
38using media::PipelineStatus;
39
40namespace {
41
42// Limits the maximum outstanding repaints posted on render thread.
43// This number of 50 is a guess, it does not take too much memory on the task
44// queue but gives up a pretty good latency on repaint.
45const int kMaxOutstandingRepaints = 50;
46
47// Limits the range of playback rate.
48//
49// TODO(kylep): Revisit these.
50//
51// Vista has substantially lower performance than XP or Windows7.  If you speed
52// up a video too much, it can't keep up, and rendering stops updating except on
53// the time bar. For really high speeds, audio becomes a bottleneck and we just
54// use up the data we have, which may not achieve the speed requested, but will
55// not crash the tab.
56//
57// A very slow speed, ie 0.00000001x, causes the machine to lock up. (It seems
58// like a busy loop). It gets unresponsive, although its not completely dead.
59//
60// Also our timers are not very accurate (especially for ogg), which becomes
61// evident at low speeds and on Vista. Since other speeds are risky and outside
62// the norms, we think 1/16x to 16x is a safe and useful range for now.
63const float kMinRate = 0.0625f;
64const float kMaxRate = 16.0f;
65
66// Platform independent method for converting and rounding floating point
67// seconds to an int64 timestamp.
68//
69// Refer to https://bugs.webkit.org/show_bug.cgi?id=52697 for details.
70base::TimeDelta ConvertSecondsToTimestamp(float seconds) {
71  float microseconds = seconds * base::Time::kMicrosecondsPerSecond;
72  float integer = ceilf(microseconds);
73  float difference = integer - microseconds;
74
75  // Round down if difference is large enough.
76  if ((microseconds > 0 && difference > 0.5f) ||
77      (microseconds <= 0 && difference >= 0.5f)) {
78    integer -= 1.0f;
79  }
80
81  // Now we can safely cast to int64 microseconds.
82  return base::TimeDelta::FromMicroseconds(static_cast<int64>(integer));
83}
84
85}  // namespace
86
87namespace webkit_glue {
88
89/////////////////////////////////////////////////////////////////////////////
90// WebMediaPlayerImpl::Proxy implementation
91
92WebMediaPlayerImpl::Proxy::Proxy(MessageLoop* render_loop,
93                                 WebMediaPlayerImpl* webmediaplayer)
94    : render_loop_(render_loop),
95      webmediaplayer_(webmediaplayer),
96      outstanding_repaints_(0) {
97  DCHECK(render_loop_);
98  DCHECK(webmediaplayer_);
99}
100
101WebMediaPlayerImpl::Proxy::~Proxy() {
102  Detach();
103}
104
105void WebMediaPlayerImpl::Proxy::Repaint() {
106  base::AutoLock auto_lock(lock_);
107  if (outstanding_repaints_ < kMaxOutstandingRepaints) {
108    ++outstanding_repaints_;
109
110    render_loop_->PostTask(FROM_HERE,
111        NewRunnableMethod(this, &WebMediaPlayerImpl::Proxy::RepaintTask));
112  }
113}
114
115void WebMediaPlayerImpl::Proxy::SetVideoRenderer(
116    scoped_refptr<WebVideoRenderer> video_renderer) {
117  video_renderer_ = video_renderer;
118}
119
120WebDataSourceBuildObserverHack* WebMediaPlayerImpl::Proxy::GetBuildObserver() {
121  if (!build_observer_.get())
122    build_observer_.reset(NewCallback(this, &Proxy::AddDataSource));
123  return build_observer_.get();
124}
125
126void WebMediaPlayerImpl::Proxy::Paint(SkCanvas* canvas,
127                                      const gfx::Rect& dest_rect) {
128  DCHECK(MessageLoop::current() == render_loop_);
129  if (video_renderer_) {
130    video_renderer_->Paint(canvas, dest_rect);
131  }
132}
133
134void WebMediaPlayerImpl::Proxy::SetSize(const gfx::Rect& rect) {
135  DCHECK(MessageLoop::current() == render_loop_);
136  if (video_renderer_) {
137    video_renderer_->SetRect(rect);
138  }
139}
140
141bool WebMediaPlayerImpl::Proxy::HasSingleOrigin() {
142  DCHECK(MessageLoop::current() == render_loop_);
143
144  base::AutoLock auto_lock(data_sources_lock_);
145
146  for (DataSourceList::iterator itr = data_sources_.begin();
147       itr != data_sources_.end();
148       itr++) {
149    if (!(*itr)->HasSingleOrigin())
150      return false;
151  }
152  return true;
153}
154
155void WebMediaPlayerImpl::Proxy::AbortDataSources() {
156  DCHECK(MessageLoop::current() == render_loop_);
157  base::AutoLock auto_lock(data_sources_lock_);
158
159  for (DataSourceList::iterator itr = data_sources_.begin();
160       itr != data_sources_.end();
161       itr++) {
162    (*itr)->Abort();
163  }
164}
165
166void WebMediaPlayerImpl::Proxy::Detach() {
167  DCHECK(MessageLoop::current() == render_loop_);
168  webmediaplayer_ = NULL;
169  video_renderer_ = NULL;
170
171  {
172    base::AutoLock auto_lock(data_sources_lock_);
173    data_sources_.clear();
174  }
175}
176
177void WebMediaPlayerImpl::Proxy::PipelineInitializationCallback(
178    PipelineStatus status) {
179  render_loop_->PostTask(FROM_HERE, NewRunnableMethod(
180      this, &WebMediaPlayerImpl::Proxy::PipelineInitializationTask, status));
181}
182
183void WebMediaPlayerImpl::Proxy::PipelineSeekCallback(PipelineStatus status) {
184  render_loop_->PostTask(FROM_HERE, NewRunnableMethod(
185      this, &WebMediaPlayerImpl::Proxy::PipelineSeekTask, status));
186}
187
188void WebMediaPlayerImpl::Proxy::PipelineEndedCallback(PipelineStatus status) {
189  render_loop_->PostTask(FROM_HERE, NewRunnableMethod(
190      this, &WebMediaPlayerImpl::Proxy::PipelineEndedTask, status));
191}
192
193void WebMediaPlayerImpl::Proxy::PipelineErrorCallback(PipelineStatus error) {
194  DCHECK_NE(error, media::PIPELINE_OK);
195  render_loop_->PostTask(FROM_HERE, NewRunnableMethod(
196      this, &WebMediaPlayerImpl::Proxy::PipelineErrorTask, error));
197}
198
199void WebMediaPlayerImpl::Proxy::NetworkEventCallback(PipelineStatus status) {
200  render_loop_->PostTask(FROM_HERE, NewRunnableMethod(
201      this, &WebMediaPlayerImpl::Proxy::NetworkEventTask, status));
202}
203
204void WebMediaPlayerImpl::Proxy::AddDataSource(WebDataSource* data_source) {
205  base::AutoLock auto_lock(data_sources_lock_);
206  data_sources_.push_back(make_scoped_refptr(data_source));
207}
208
209void WebMediaPlayerImpl::Proxy::RepaintTask() {
210  DCHECK(MessageLoop::current() == render_loop_);
211  {
212    base::AutoLock auto_lock(lock_);
213    --outstanding_repaints_;
214    DCHECK_GE(outstanding_repaints_, 0);
215  }
216  if (webmediaplayer_) {
217    webmediaplayer_->Repaint();
218  }
219}
220
221void WebMediaPlayerImpl::Proxy::PipelineInitializationTask(
222    PipelineStatus status) {
223  DCHECK(MessageLoop::current() == render_loop_);
224  if (webmediaplayer_) {
225    webmediaplayer_->OnPipelineInitialize(status);
226  }
227}
228
229void WebMediaPlayerImpl::Proxy::PipelineSeekTask(PipelineStatus status) {
230  DCHECK(MessageLoop::current() == render_loop_);
231  if (webmediaplayer_) {
232    webmediaplayer_->OnPipelineSeek(status);
233  }
234}
235
236void WebMediaPlayerImpl::Proxy::PipelineEndedTask(PipelineStatus status) {
237  DCHECK(MessageLoop::current() == render_loop_);
238  if (webmediaplayer_) {
239    webmediaplayer_->OnPipelineEnded(status);
240  }
241}
242
243void WebMediaPlayerImpl::Proxy::PipelineErrorTask(PipelineStatus error) {
244  DCHECK(MessageLoop::current() == render_loop_);
245  if (webmediaplayer_) {
246    webmediaplayer_->OnPipelineError(error);
247  }
248}
249
250void WebMediaPlayerImpl::Proxy::NetworkEventTask(PipelineStatus status) {
251  DCHECK(MessageLoop::current() == render_loop_);
252  if (webmediaplayer_) {
253    webmediaplayer_->OnNetworkEvent(status);
254  }
255}
256
257void WebMediaPlayerImpl::Proxy::GetCurrentFrame(
258    scoped_refptr<media::VideoFrame>* frame_out) {
259  if (video_renderer_)
260    video_renderer_->GetCurrentFrame(frame_out);
261}
262
263void WebMediaPlayerImpl::Proxy::PutCurrentFrame(
264    scoped_refptr<media::VideoFrame> frame) {
265  if (video_renderer_)
266    video_renderer_->PutCurrentFrame(frame);
267}
268
269/////////////////////////////////////////////////////////////////////////////
270// WebMediaPlayerImpl implementation
271
272WebMediaPlayerImpl::WebMediaPlayerImpl(
273    WebKit::WebMediaPlayerClient* client,
274    media::FilterCollection* collection,
275    media::MessageLoopFactory* message_loop_factory)
276    : network_state_(WebKit::WebMediaPlayer::Empty),
277      ready_state_(WebKit::WebMediaPlayer::HaveNothing),
278      main_loop_(NULL),
279      filter_collection_(collection),
280      pipeline_(NULL),
281      message_loop_factory_(message_loop_factory),
282      paused_(true),
283      seeking_(false),
284      playback_rate_(0.0f),
285      client_(client),
286      proxy_(NULL) {
287  // Saves the current message loop.
288  DCHECK(!main_loop_);
289  main_loop_ = MessageLoop::current();
290}
291
292bool WebMediaPlayerImpl::Initialize(
293    WebKit::WebFrame* frame,
294    bool use_simple_data_source,
295    scoped_refptr<WebVideoRenderer> web_video_renderer) {
296  MessageLoop* pipeline_message_loop =
297      message_loop_factory_->GetMessageLoop("PipelineThread");
298  if (!pipeline_message_loop) {
299    NOTREACHED() << "Could not start PipelineThread";
300    return false;
301  }
302
303  pipeline_ = new media::PipelineImpl(pipeline_message_loop);
304
305  // Also we want to be notified of |main_loop_| destruction.
306  main_loop_->AddDestructionObserver(this);
307
308  // Creates the proxy.
309  proxy_ = new Proxy(main_loop_, this);
310  web_video_renderer->SetWebMediaPlayerImplProxy(proxy_);
311  proxy_->SetVideoRenderer(web_video_renderer);
312
313  // Set our pipeline callbacks.
314  pipeline_->Init(
315      NewCallback(proxy_.get(),
316                  &WebMediaPlayerImpl::Proxy::PipelineEndedCallback),
317      NewCallback(proxy_.get(),
318                  &WebMediaPlayerImpl::Proxy::PipelineErrorCallback),
319      NewCallback(proxy_.get(),
320                  &WebMediaPlayerImpl::Proxy::NetworkEventCallback));
321
322  // A simple data source that keeps all data in memory.
323  scoped_ptr<media::DataSourceFactory> simple_data_source_factory(
324      SimpleDataSource::CreateFactory(MessageLoop::current(), frame,
325                                      proxy_->GetBuildObserver()));
326
327  // A sophisticated data source that does memory caching.
328  scoped_ptr<media::DataSourceFactory> buffered_data_source_factory(
329      BufferedDataSource::CreateFactory(MessageLoop::current(), frame,
330                                        proxy_->GetBuildObserver()));
331
332  scoped_ptr<media::CompositeDataSourceFactory> data_source_factory(
333      new media::CompositeDataSourceFactory());
334
335  if (use_simple_data_source) {
336    data_source_factory->AddFactory(simple_data_source_factory.release());
337    data_source_factory->AddFactory(buffered_data_source_factory.release());
338  } else {
339    data_source_factory->AddFactory(buffered_data_source_factory.release());
340    data_source_factory->AddFactory(simple_data_source_factory.release());
341  }
342
343  scoped_ptr<media::DemuxerFactory> demuxer_factory(
344      new media::FFmpegDemuxerFactory(data_source_factory.release(),
345                                      pipeline_message_loop));
346  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAdaptive)) {
347    demuxer_factory.reset(new media::AdaptiveDemuxerFactory(
348        demuxer_factory.release()));
349  }
350  filter_collection_->SetDemuxerFactory(demuxer_factory.release());
351
352  // Add in the default filter factories.
353  filter_collection_->AddAudioDecoder(new media::FFmpegAudioDecoder(
354      message_loop_factory_->GetMessageLoop("AudioDecoderThread")));
355  filter_collection_->AddVideoDecoder(new media::FFmpegVideoDecoder(
356      message_loop_factory_->GetMessageLoop("VideoDecoderThread"), NULL));
357  filter_collection_->AddAudioRenderer(new media::NullAudioRenderer());
358
359  return true;
360}
361
362WebMediaPlayerImpl::~WebMediaPlayerImpl() {
363  Destroy();
364
365  // Finally tell the |main_loop_| we don't want to be notified of destruction
366  // event.
367  if (main_loop_) {
368    main_loop_->RemoveDestructionObserver(this);
369  }
370}
371
372void WebMediaPlayerImpl::load(const WebKit::WebURL& url) {
373  DCHECK(MessageLoop::current() == main_loop_);
374  DCHECK(proxy_);
375
376  if (media::RTCVideoDecoder::IsUrlSupported(url.spec())) {
377    // Remove the default decoder
378    scoped_refptr<media::VideoDecoder> old_videodecoder;
379    filter_collection_->SelectVideoDecoder(&old_videodecoder);
380    media::RTCVideoDecoder* rtc_video_decoder =
381        new media::RTCVideoDecoder(
382             message_loop_factory_->GetMessageLoop("VideoDecoderThread"),
383             url.spec());
384    filter_collection_->AddVideoDecoder(rtc_video_decoder);
385  }
386
387  // Handle any volume changes that occured before load().
388  setVolume(GetClient()->volume());
389  // Get the preload value.
390  setPreload(GetClient()->preload());
391
392  // Initialize the pipeline.
393  SetNetworkState(WebKit::WebMediaPlayer::Loading);
394  SetReadyState(WebKit::WebMediaPlayer::HaveNothing);
395  pipeline_->Start(
396      filter_collection_.release(),
397      url.spec(),
398      NewCallback(proxy_.get(),
399                  &WebMediaPlayerImpl::Proxy::PipelineInitializationCallback));
400}
401
402void WebMediaPlayerImpl::cancelLoad() {
403  DCHECK(MessageLoop::current() == main_loop_);
404}
405
406void WebMediaPlayerImpl::play() {
407  DCHECK(MessageLoop::current() == main_loop_);
408
409  paused_ = false;
410  pipeline_->SetPlaybackRate(playback_rate_);
411}
412
413void WebMediaPlayerImpl::pause() {
414  DCHECK(MessageLoop::current() == main_loop_);
415
416  paused_ = true;
417  pipeline_->SetPlaybackRate(0.0f);
418  paused_time_ = pipeline_->GetCurrentTime();
419}
420
421bool WebMediaPlayerImpl::supportsFullscreen() const {
422  DCHECK(MessageLoop::current() == main_loop_);
423  return true;
424}
425
426bool WebMediaPlayerImpl::supportsSave() const {
427  DCHECK(MessageLoop::current() == main_loop_);
428  return true;
429}
430
431void WebMediaPlayerImpl::seek(float seconds) {
432  DCHECK(MessageLoop::current() == main_loop_);
433
434  // WebKit fires a seek(0) at the very start, however pipeline already does a
435  // seek(0) internally.  Avoid doing seek(0) the second time because this will
436  // cause extra pre-rolling and will break servers without range request
437  // support.
438  //
439  // We still have to notify WebKit that time has changed otherwise
440  // HTMLMediaElement gets into an inconsistent state.
441  if (pipeline_->GetCurrentTime().ToInternalValue() == 0 && seconds == 0) {
442    GetClient()->timeChanged();
443    return;
444  }
445
446  base::TimeDelta seek_time = ConvertSecondsToTimestamp(seconds);
447
448  // Update our paused time.
449  if (paused_) {
450    paused_time_ = seek_time;
451  }
452
453  seeking_ = true;
454
455  // Kick off the asynchronous seek!
456  pipeline_->Seek(
457      seek_time,
458      NewCallback(proxy_.get(),
459                  &WebMediaPlayerImpl::Proxy::PipelineSeekCallback));
460}
461
462void WebMediaPlayerImpl::setEndTime(float seconds) {
463  DCHECK(MessageLoop::current() == main_loop_);
464
465  // TODO(hclam): add method call when it has been implemented.
466  return;
467}
468
469void WebMediaPlayerImpl::setRate(float rate) {
470  DCHECK(MessageLoop::current() == main_loop_);
471
472  // TODO(kylep): Remove when support for negatives is added. Also, modify the
473  // following checks so rewind uses reasonable values also.
474  if (rate < 0.0f)
475    return;
476
477  // Limit rates to reasonable values by clamping.
478  if (rate != 0.0f) {
479    if (rate < kMinRate)
480      rate = kMinRate;
481    else if (rate > kMaxRate)
482      rate = kMaxRate;
483  }
484
485  playback_rate_ = rate;
486  if (!paused_) {
487    pipeline_->SetPlaybackRate(rate);
488  }
489}
490
491void WebMediaPlayerImpl::setVolume(float volume) {
492  DCHECK(MessageLoop::current() == main_loop_);
493
494  pipeline_->SetVolume(volume);
495}
496
497void WebMediaPlayerImpl::setVisible(bool visible) {
498  DCHECK(MessageLoop::current() == main_loop_);
499
500  // TODO(hclam): add appropriate method call when pipeline has it implemented.
501  return;
502}
503
504#define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, chromium_name) \
505        COMPILE_ASSERT(int(WebKit::WebMediaPlayer::webkit_name) == \
506                       int(media::chromium_name), \
507                       mismatching_enums)
508COMPILE_ASSERT_MATCHING_ENUM(None, NONE);
509COMPILE_ASSERT_MATCHING_ENUM(MetaData, METADATA);
510COMPILE_ASSERT_MATCHING_ENUM(Auto, AUTO);
511
512void WebMediaPlayerImpl::setPreload(WebKit::WebMediaPlayer::Preload preload) {
513  DCHECK(MessageLoop::current() == main_loop_);
514
515  pipeline_->SetPreload(static_cast<media::Preload>(preload));
516}
517
518bool WebMediaPlayerImpl::totalBytesKnown() {
519  DCHECK(MessageLoop::current() == main_loop_);
520
521  return pipeline_->GetTotalBytes() != 0;
522}
523
524bool WebMediaPlayerImpl::hasVideo() const {
525  DCHECK(MessageLoop::current() == main_loop_);
526
527  return pipeline_->HasVideo();
528}
529
530bool WebMediaPlayerImpl::hasAudio() const {
531  DCHECK(MessageLoop::current() == main_loop_);
532
533  return pipeline_->HasAudio();
534}
535
536WebKit::WebSize WebMediaPlayerImpl::naturalSize() const {
537  DCHECK(MessageLoop::current() == main_loop_);
538
539  size_t width, height;
540  pipeline_->GetVideoSize(&width, &height);
541  return WebKit::WebSize(width, height);
542}
543
544bool WebMediaPlayerImpl::paused() const {
545  DCHECK(MessageLoop::current() == main_loop_);
546
547  return pipeline_->GetPlaybackRate() == 0.0f;
548}
549
550bool WebMediaPlayerImpl::seeking() const {
551  DCHECK(MessageLoop::current() == main_loop_);
552
553  if (ready_state_ == WebKit::WebMediaPlayer::HaveNothing)
554    return false;
555
556  return seeking_;
557}
558
559float WebMediaPlayerImpl::duration() const {
560  DCHECK(MessageLoop::current() == main_loop_);
561
562  base::TimeDelta duration = pipeline_->GetMediaDuration();
563  if (duration.InMicroseconds() == media::Limits::kMaxTimeInMicroseconds)
564    return std::numeric_limits<float>::infinity();
565  return static_cast<float>(duration.InSecondsF());
566}
567
568float WebMediaPlayerImpl::currentTime() const {
569  DCHECK(MessageLoop::current() == main_loop_);
570
571  if (paused_) {
572    return static_cast<float>(paused_time_.InSecondsF());
573  }
574  return static_cast<float>(pipeline_->GetCurrentTime().InSecondsF());
575}
576
577int WebMediaPlayerImpl::dataRate() const {
578  DCHECK(MessageLoop::current() == main_loop_);
579
580  // TODO(hclam): Add this method call if pipeline has it in the interface.
581  return 0;
582}
583
584WebKit::WebMediaPlayer::NetworkState WebMediaPlayerImpl::networkState() const {
585  return network_state_;
586}
587
588WebKit::WebMediaPlayer::ReadyState WebMediaPlayerImpl::readyState() const {
589  return ready_state_;
590}
591
592const WebKit::WebTimeRanges& WebMediaPlayerImpl::buffered() {
593  DCHECK(MessageLoop::current() == main_loop_);
594
595  // Update buffered_ with the most recent buffered time.
596  if (buffered_.size() > 0) {
597    float buffered_time = static_cast<float>(
598        pipeline_->GetBufferedTime().InSecondsF());
599    if (buffered_time >= buffered_[0].start)
600      buffered_[0].end = buffered_time;
601  }
602
603  return buffered_;
604}
605
606float WebMediaPlayerImpl::maxTimeSeekable() const {
607  DCHECK(MessageLoop::current() == main_loop_);
608
609  // If we are performing streaming, we report that we cannot seek at all.
610  // We are using this flag to indicate if the data source supports seeking
611  // or not. We should be able to seek even if we are performing streaming.
612  // TODO(hclam): We need to update this when we have better caching.
613  if (pipeline_->IsStreaming())
614    return 0.0f;
615  return static_cast<float>(pipeline_->GetMediaDuration().InSecondsF());
616}
617
618unsigned long long WebMediaPlayerImpl::bytesLoaded() const {
619  DCHECK(MessageLoop::current() == main_loop_);
620
621  return pipeline_->GetBufferedBytes();
622}
623
624unsigned long long WebMediaPlayerImpl::totalBytes() const {
625  DCHECK(MessageLoop::current() == main_loop_);
626
627  return pipeline_->GetTotalBytes();
628}
629
630void WebMediaPlayerImpl::setSize(const WebSize& size) {
631  DCHECK(MessageLoop::current() == main_loop_);
632  DCHECK(proxy_);
633
634  proxy_->SetSize(gfx::Rect(0, 0, size.width, size.height));
635}
636
637void WebMediaPlayerImpl::paint(WebCanvas* canvas,
638                               const WebRect& rect) {
639  DCHECK(MessageLoop::current() == main_loop_);
640  DCHECK(proxy_);
641
642#if WEBKIT_USING_SKIA
643  proxy_->Paint(canvas, rect);
644#elif WEBKIT_USING_CG
645  // Get the current scaling in X and Y.
646  CGAffineTransform mat = CGContextGetCTM(canvas);
647  float scale_x = sqrt(mat.a * mat.a + mat.b * mat.b);
648  float scale_y = sqrt(mat.c * mat.c + mat.d * mat.d);
649  float inverse_scale_x = SkScalarNearlyZero(scale_x) ? 0.0f : 1.0f / scale_x;
650  float inverse_scale_y = SkScalarNearlyZero(scale_y) ? 0.0f : 1.0f / scale_y;
651  int scaled_width = static_cast<int>(rect.width * fabs(scale_x));
652  int scaled_height = static_cast<int>(rect.height * fabs(scale_y));
653
654  // Make sure we don't create a huge canvas.
655  // TODO(hclam): Respect the aspect ratio.
656  if (scaled_width > static_cast<int>(media::Limits::kMaxCanvas))
657    scaled_width = media::Limits::kMaxCanvas;
658  if (scaled_height > static_cast<int>(media::Limits::kMaxCanvas))
659    scaled_height = media::Limits::kMaxCanvas;
660
661  // If there is no preexisting platform canvas, or if the size has
662  // changed, recreate the canvas.  This is to avoid recreating the bitmap
663  // buffer over and over for each frame of video.
664  if (!skia_canvas_.get() ||
665      skia_canvas_->getDevice()->width() != scaled_width ||
666      skia_canvas_->getDevice()->height() != scaled_height) {
667    skia_canvas_.reset(
668        new skia::PlatformCanvas(scaled_width, scaled_height, true));
669  }
670
671  // Draw to our temporary skia canvas.
672  gfx::Rect normalized_rect(scaled_width, scaled_height);
673  proxy_->Paint(skia_canvas_.get(), normalized_rect);
674
675  // The mac coordinate system is flipped vertical from the normal skia
676  // coordinates.  During painting of the frame, flip the coordinates
677  // system and, for simplicity, also translate the clip rectangle to
678  // start at 0,0.
679  CGContextSaveGState(canvas);
680  CGContextTranslateCTM(canvas, rect.x, rect.height + rect.y);
681  CGContextScaleCTM(canvas, inverse_scale_x, -inverse_scale_y);
682
683  // We need a local variable CGRect version for DrawToContext.
684  CGRect normalized_cgrect =
685      CGRectMake(normalized_rect.x(), normalized_rect.y(),
686                 normalized_rect.width(), normalized_rect.height());
687
688  // Copy the frame rendered to our temporary skia canvas onto the passed in
689  // canvas.
690  skia_canvas_->getTopPlatformDevice().DrawToContext(canvas, 0, 0,
691                                                     &normalized_cgrect);
692
693  CGContextRestoreGState(canvas);
694#else
695  NOTIMPLEMENTED() << "We only support rendering to skia or CG";
696#endif
697}
698
699bool WebMediaPlayerImpl::hasSingleSecurityOrigin() const {
700  if (proxy_)
701    return proxy_->HasSingleOrigin();
702  return true;
703}
704
705WebKit::WebMediaPlayer::MovieLoadType
706    WebMediaPlayerImpl::movieLoadType() const {
707  DCHECK(MessageLoop::current() == main_loop_);
708
709  // TODO(hclam): If the pipeline is performing streaming, we say that this is
710  // a live stream. But instead it should be a StoredStream if we have proper
711  // caching.
712  if (pipeline_->IsStreaming())
713    return WebKit::WebMediaPlayer::LiveStream;
714  return WebKit::WebMediaPlayer::Unknown;
715}
716
717unsigned WebMediaPlayerImpl::decodedFrameCount() const {
718  DCHECK(MessageLoop::current() == main_loop_);
719
720  media::PipelineStatistics stats = pipeline_->GetStatistics();
721  return stats.video_frames_decoded;
722}
723
724unsigned WebMediaPlayerImpl::droppedFrameCount() const {
725  DCHECK(MessageLoop::current() == main_loop_);
726
727  media::PipelineStatistics stats = pipeline_->GetStatistics();
728  return stats.video_frames_dropped;
729}
730
731unsigned WebMediaPlayerImpl::audioDecodedByteCount() const {
732  DCHECK(MessageLoop::current() == main_loop_);
733
734  media::PipelineStatistics stats = pipeline_->GetStatistics();
735  return stats.audio_bytes_decoded;
736}
737
738unsigned WebMediaPlayerImpl::videoDecodedByteCount() const {
739  DCHECK(MessageLoop::current() == main_loop_);
740
741  media::PipelineStatistics stats = pipeline_->GetStatistics();
742  return stats.video_bytes_decoded;
743}
744
745WebKit::WebVideoFrame* WebMediaPlayerImpl::getCurrentFrame() {
746  scoped_refptr<media::VideoFrame> video_frame;
747  proxy_->GetCurrentFrame(&video_frame);
748  if (video_frame.get())
749    return new WebVideoFrameImpl(video_frame);
750  return NULL;
751}
752
753void WebMediaPlayerImpl::putCurrentFrame(
754    WebKit::WebVideoFrame* web_video_frame) {
755  if (web_video_frame) {
756    scoped_refptr<media::VideoFrame> video_frame(
757        WebVideoFrameImpl::toVideoFrame(web_video_frame));
758    proxy_->PutCurrentFrame(video_frame);
759    delete web_video_frame;
760  }
761}
762
763void WebMediaPlayerImpl::WillDestroyCurrentMessageLoop() {
764  Destroy();
765  main_loop_ = NULL;
766}
767
768void WebMediaPlayerImpl::Repaint() {
769  DCHECK(MessageLoop::current() == main_loop_);
770  GetClient()->repaint();
771}
772
773void WebMediaPlayerImpl::OnPipelineInitialize(PipelineStatus status) {
774  DCHECK(MessageLoop::current() == main_loop_);
775  if (status == media::PIPELINE_OK) {
776    // Only keep one time range starting from 0.
777    WebKit::WebTimeRanges new_buffered(static_cast<size_t>(1));
778    new_buffered[0].start = 0.0f;
779    new_buffered[0].end =
780        static_cast<float>(pipeline_->GetMediaDuration().InSecondsF());
781    buffered_.swap(new_buffered);
782
783    // Since we have initialized the pipeline, say we have everything otherwise
784    // we'll remain either loading/idle.
785    // TODO(hclam): change this to report the correct status.
786    SetReadyState(WebKit::WebMediaPlayer::HaveMetadata);
787    SetReadyState(WebKit::WebMediaPlayer::HaveEnoughData);
788    if (pipeline_->IsLoaded()) {
789      SetNetworkState(WebKit::WebMediaPlayer::Loaded);
790    }
791  } else {
792    // TODO(hclam): should use |status| to determine the state
793    // properly and reports error using MediaError.
794    // WebKit uses FormatError to indicate an error for bogus URL or bad file.
795    // Since we are at the initialization stage we can safely treat every error
796    // as format error. Should post a task to call to |webmediaplayer_|.
797    SetNetworkState(WebKit::WebMediaPlayer::FormatError);
798  }
799
800  // Repaint to trigger UI update.
801  Repaint();
802}
803
804void WebMediaPlayerImpl::OnPipelineSeek(PipelineStatus status) {
805  DCHECK(MessageLoop::current() == main_loop_);
806  if (status == media::PIPELINE_OK) {
807    // Update our paused time.
808    if (paused_) {
809      paused_time_ = pipeline_->GetCurrentTime();
810    }
811
812    SetReadyState(WebKit::WebMediaPlayer::HaveEnoughData);
813    seeking_ = false;
814    GetClient()->timeChanged();
815  }
816}
817
818void WebMediaPlayerImpl::OnPipelineEnded(PipelineStatus status) {
819  DCHECK(MessageLoop::current() == main_loop_);
820  if (status == media::PIPELINE_OK) {
821    GetClient()->timeChanged();
822  }
823}
824
825void WebMediaPlayerImpl::OnPipelineError(PipelineStatus error) {
826  DCHECK(MessageLoop::current() == main_loop_);
827  switch (error) {
828    case media::PIPELINE_OK:
829      LOG(DFATAL) << "PIPELINE_OK isn't an error!";
830      break;
831
832    case media::PIPELINE_ERROR_INITIALIZATION_FAILED:
833    case media::PIPELINE_ERROR_REQUIRED_FILTER_MISSING:
834    case media::PIPELINE_ERROR_COULD_NOT_RENDER:
835    case media::PIPELINE_ERROR_URL_NOT_FOUND:
836    case media::PIPELINE_ERROR_NETWORK:
837    case media::PIPELINE_ERROR_READ:
838    case media::DEMUXER_ERROR_COULD_NOT_OPEN:
839    case media::DEMUXER_ERROR_COULD_NOT_PARSE:
840    case media::DEMUXER_ERROR_NO_SUPPORTED_STREAMS:
841    case media::DEMUXER_ERROR_COULD_NOT_CREATE_THREAD:
842    case media::DATASOURCE_ERROR_URL_NOT_SUPPORTED:
843      // Format error.
844      SetNetworkState(WebMediaPlayer::FormatError);
845      break;
846
847    case media::PIPELINE_ERROR_DECODE:
848    case media::PIPELINE_ERROR_ABORT:
849    case media::PIPELINE_ERROR_OUT_OF_MEMORY:
850    case media::PIPELINE_ERROR_AUDIO_HARDWARE:
851    case media::PIPELINE_ERROR_OPERATION_PENDING:
852    case media::PIPELINE_ERROR_INVALID_STATE:
853      // Decode error.
854      SetNetworkState(WebMediaPlayer::DecodeError);
855      break;
856  }
857
858  // Repaint to trigger UI update.
859  Repaint();
860}
861
862void WebMediaPlayerImpl::OnNetworkEvent(PipelineStatus status) {
863  DCHECK(MessageLoop::current() == main_loop_);
864  if (status == media::PIPELINE_OK) {
865    if (pipeline_->IsNetworkActive()) {
866      SetNetworkState(WebKit::WebMediaPlayer::Loading);
867    } else {
868      // If we are inactive because we just finished receiving all the data,
869      // do one final repaint to show final progress.
870      if (bytesLoaded() == totalBytes() &&
871          network_state_ != WebKit::WebMediaPlayer::Idle) {
872        Repaint();
873
874        SetNetworkState(WebKit::WebMediaPlayer::Loaded);
875      }
876
877      SetNetworkState(WebKit::WebMediaPlayer::Idle);
878    }
879  }
880}
881
882void WebMediaPlayerImpl::SetNetworkState(
883    WebKit::WebMediaPlayer::NetworkState state) {
884  DCHECK(MessageLoop::current() == main_loop_);
885  // Always notify to ensure client has the latest value.
886  network_state_ = state;
887  GetClient()->networkStateChanged();
888}
889
890void WebMediaPlayerImpl::SetReadyState(
891    WebKit::WebMediaPlayer::ReadyState state) {
892  DCHECK(MessageLoop::current() == main_loop_);
893  // Always notify to ensure client has the latest value.
894  ready_state_ = state;
895  GetClient()->readyStateChanged();
896}
897
898void WebMediaPlayerImpl::Destroy() {
899  DCHECK(MessageLoop::current() == main_loop_);
900
901  // Tell the data source to abort any pending reads so that the pipeline is
902  // not blocked when issuing stop commands to the other filters.
903  if (proxy_)
904    proxy_->AbortDataSources();
905
906  // Make sure to kill the pipeline so there's no more media threads running.
907  // Note: stopping the pipeline might block for a long time.
908  if (pipeline_) {
909    media::PipelineStatusNotification note;
910    pipeline_->Stop(note.Callback());
911    note.Wait();
912  }
913
914  message_loop_factory_.reset();
915
916  // And then detach the proxy, it may live on the render thread for a little
917  // longer until all the tasks are finished.
918  if (proxy_) {
919    proxy_->Detach();
920    proxy_ = NULL;
921  }
922}
923
924WebKit::WebMediaPlayerClient* WebMediaPlayerImpl::GetClient() {
925  DCHECK(MessageLoop::current() == main_loop_);
926  DCHECK(client_);
927  return client_;
928}
929
930}  // namespace webkit_glue
931