• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
35 using WebKit::WebCanvas;
36 using WebKit::WebRect;
37 using WebKit::WebSize;
38 using media::PipelineStatus;
39 
40 namespace {
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.
45 const 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.
63 const float kMinRate = 0.0625f;
64 const 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.
ConvertSecondsToTimestamp(float seconds)70 base::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 
87 namespace webkit_glue {
88 
89 /////////////////////////////////////////////////////////////////////////////
90 // WebMediaPlayerImpl::Proxy implementation
91 
Proxy(MessageLoop * render_loop,WebMediaPlayerImpl * webmediaplayer)92 WebMediaPlayerImpl::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 
~Proxy()101 WebMediaPlayerImpl::Proxy::~Proxy() {
102   Detach();
103 }
104 
Repaint()105 void 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 
SetVideoRenderer(scoped_refptr<WebVideoRenderer> video_renderer)115 void WebMediaPlayerImpl::Proxy::SetVideoRenderer(
116     scoped_refptr<WebVideoRenderer> video_renderer) {
117   video_renderer_ = video_renderer;
118 }
119 
GetBuildObserver()120 WebDataSourceBuildObserverHack* WebMediaPlayerImpl::Proxy::GetBuildObserver() {
121   if (!build_observer_.get())
122     build_observer_.reset(NewCallback(this, &Proxy::AddDataSource));
123   return build_observer_.get();
124 }
125 
Paint(SkCanvas * canvas,const gfx::Rect & dest_rect)126 void 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 
SetSize(const gfx::Rect & rect)134 void 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 
HasSingleOrigin()141 bool 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 
AbortDataSources()155 void 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 
Detach()166 void 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 
PipelineInitializationCallback(PipelineStatus status)177 void WebMediaPlayerImpl::Proxy::PipelineInitializationCallback(
178     PipelineStatus status) {
179   render_loop_->PostTask(FROM_HERE, NewRunnableMethod(
180       this, &WebMediaPlayerImpl::Proxy::PipelineInitializationTask, status));
181 }
182 
PipelineSeekCallback(PipelineStatus status)183 void WebMediaPlayerImpl::Proxy::PipelineSeekCallback(PipelineStatus status) {
184   render_loop_->PostTask(FROM_HERE, NewRunnableMethod(
185       this, &WebMediaPlayerImpl::Proxy::PipelineSeekTask, status));
186 }
187 
PipelineEndedCallback(PipelineStatus status)188 void WebMediaPlayerImpl::Proxy::PipelineEndedCallback(PipelineStatus status) {
189   render_loop_->PostTask(FROM_HERE, NewRunnableMethod(
190       this, &WebMediaPlayerImpl::Proxy::PipelineEndedTask, status));
191 }
192 
PipelineErrorCallback(PipelineStatus error)193 void 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 
NetworkEventCallback(PipelineStatus status)199 void WebMediaPlayerImpl::Proxy::NetworkEventCallback(PipelineStatus status) {
200   render_loop_->PostTask(FROM_HERE, NewRunnableMethod(
201       this, &WebMediaPlayerImpl::Proxy::NetworkEventTask, status));
202 }
203 
AddDataSource(WebDataSource * data_source)204 void 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 
RepaintTask()209 void 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 
PipelineInitializationTask(PipelineStatus status)221 void WebMediaPlayerImpl::Proxy::PipelineInitializationTask(
222     PipelineStatus status) {
223   DCHECK(MessageLoop::current() == render_loop_);
224   if (webmediaplayer_) {
225     webmediaplayer_->OnPipelineInitialize(status);
226   }
227 }
228 
PipelineSeekTask(PipelineStatus status)229 void WebMediaPlayerImpl::Proxy::PipelineSeekTask(PipelineStatus status) {
230   DCHECK(MessageLoop::current() == render_loop_);
231   if (webmediaplayer_) {
232     webmediaplayer_->OnPipelineSeek(status);
233   }
234 }
235 
PipelineEndedTask(PipelineStatus status)236 void WebMediaPlayerImpl::Proxy::PipelineEndedTask(PipelineStatus status) {
237   DCHECK(MessageLoop::current() == render_loop_);
238   if (webmediaplayer_) {
239     webmediaplayer_->OnPipelineEnded(status);
240   }
241 }
242 
PipelineErrorTask(PipelineStatus error)243 void WebMediaPlayerImpl::Proxy::PipelineErrorTask(PipelineStatus error) {
244   DCHECK(MessageLoop::current() == render_loop_);
245   if (webmediaplayer_) {
246     webmediaplayer_->OnPipelineError(error);
247   }
248 }
249 
NetworkEventTask(PipelineStatus status)250 void WebMediaPlayerImpl::Proxy::NetworkEventTask(PipelineStatus status) {
251   DCHECK(MessageLoop::current() == render_loop_);
252   if (webmediaplayer_) {
253     webmediaplayer_->OnNetworkEvent(status);
254   }
255 }
256 
GetCurrentFrame(scoped_refptr<media::VideoFrame> * frame_out)257 void WebMediaPlayerImpl::Proxy::GetCurrentFrame(
258     scoped_refptr<media::VideoFrame>* frame_out) {
259   if (video_renderer_)
260     video_renderer_->GetCurrentFrame(frame_out);
261 }
262 
PutCurrentFrame(scoped_refptr<media::VideoFrame> frame)263 void 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 
WebMediaPlayerImpl(WebKit::WebMediaPlayerClient * client,media::FilterCollection * collection,media::MessageLoopFactory * message_loop_factory)272 WebMediaPlayerImpl::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 
Initialize(WebKit::WebFrame * frame,bool use_simple_data_source,scoped_refptr<WebVideoRenderer> web_video_renderer)292 bool 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 
~WebMediaPlayerImpl()362 WebMediaPlayerImpl::~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 
load(const WebKit::WebURL & url)372 void 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 
cancelLoad()402 void WebMediaPlayerImpl::cancelLoad() {
403   DCHECK(MessageLoop::current() == main_loop_);
404 }
405 
play()406 void WebMediaPlayerImpl::play() {
407   DCHECK(MessageLoop::current() == main_loop_);
408 
409   paused_ = false;
410   pipeline_->SetPlaybackRate(playback_rate_);
411 }
412 
pause()413 void WebMediaPlayerImpl::pause() {
414   DCHECK(MessageLoop::current() == main_loop_);
415 
416   paused_ = true;
417   pipeline_->SetPlaybackRate(0.0f);
418   paused_time_ = pipeline_->GetCurrentTime();
419 }
420 
supportsFullscreen() const421 bool WebMediaPlayerImpl::supportsFullscreen() const {
422   DCHECK(MessageLoop::current() == main_loop_);
423   return true;
424 }
425 
supportsSave() const426 bool WebMediaPlayerImpl::supportsSave() const {
427   DCHECK(MessageLoop::current() == main_loop_);
428   return true;
429 }
430 
seek(float seconds)431 void 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 
setEndTime(float seconds)462 void 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 
setRate(float rate)469 void 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 
setVolume(float volume)491 void WebMediaPlayerImpl::setVolume(float volume) {
492   DCHECK(MessageLoop::current() == main_loop_);
493 
494   pipeline_->SetVolume(volume);
495 }
496 
setVisible(bool visible)497 void 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)
508 COMPILE_ASSERT_MATCHING_ENUM(None, NONE);
509 COMPILE_ASSERT_MATCHING_ENUM(MetaData, METADATA);
510 COMPILE_ASSERT_MATCHING_ENUM(Auto, AUTO);
511 
setPreload(WebKit::WebMediaPlayer::Preload preload)512 void WebMediaPlayerImpl::setPreload(WebKit::WebMediaPlayer::Preload preload) {
513   DCHECK(MessageLoop::current() == main_loop_);
514 
515   pipeline_->SetPreload(static_cast<media::Preload>(preload));
516 }
517 
totalBytesKnown()518 bool WebMediaPlayerImpl::totalBytesKnown() {
519   DCHECK(MessageLoop::current() == main_loop_);
520 
521   return pipeline_->GetTotalBytes() != 0;
522 }
523 
hasVideo() const524 bool WebMediaPlayerImpl::hasVideo() const {
525   DCHECK(MessageLoop::current() == main_loop_);
526 
527   return pipeline_->HasVideo();
528 }
529 
hasAudio() const530 bool WebMediaPlayerImpl::hasAudio() const {
531   DCHECK(MessageLoop::current() == main_loop_);
532 
533   return pipeline_->HasAudio();
534 }
535 
naturalSize() const536 WebKit::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 
paused() const544 bool WebMediaPlayerImpl::paused() const {
545   DCHECK(MessageLoop::current() == main_loop_);
546 
547   return pipeline_->GetPlaybackRate() == 0.0f;
548 }
549 
seeking() const550 bool 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 
duration() const559 float 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 
currentTime() const568 float 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 
dataRate() const577 int 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 
networkState() const584 WebKit::WebMediaPlayer::NetworkState WebMediaPlayerImpl::networkState() const {
585   return network_state_;
586 }
587 
readyState() const588 WebKit::WebMediaPlayer::ReadyState WebMediaPlayerImpl::readyState() const {
589   return ready_state_;
590 }
591 
buffered()592 const 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 
maxTimeSeekable() const606 float 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 
bytesLoaded() const618 unsigned long long WebMediaPlayerImpl::bytesLoaded() const {
619   DCHECK(MessageLoop::current() == main_loop_);
620 
621   return pipeline_->GetBufferedBytes();
622 }
623 
totalBytes() const624 unsigned long long WebMediaPlayerImpl::totalBytes() const {
625   DCHECK(MessageLoop::current() == main_loop_);
626 
627   return pipeline_->GetTotalBytes();
628 }
629 
setSize(const WebSize & size)630 void 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 
paint(WebCanvas * canvas,const WebRect & rect)637 void 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 
hasSingleSecurityOrigin() const699 bool WebMediaPlayerImpl::hasSingleSecurityOrigin() const {
700   if (proxy_)
701     return proxy_->HasSingleOrigin();
702   return true;
703 }
704 
705 WebKit::WebMediaPlayer::MovieLoadType
movieLoadType() const706     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 
decodedFrameCount() const717 unsigned WebMediaPlayerImpl::decodedFrameCount() const {
718   DCHECK(MessageLoop::current() == main_loop_);
719 
720   media::PipelineStatistics stats = pipeline_->GetStatistics();
721   return stats.video_frames_decoded;
722 }
723 
droppedFrameCount() const724 unsigned WebMediaPlayerImpl::droppedFrameCount() const {
725   DCHECK(MessageLoop::current() == main_loop_);
726 
727   media::PipelineStatistics stats = pipeline_->GetStatistics();
728   return stats.video_frames_dropped;
729 }
730 
audioDecodedByteCount() const731 unsigned WebMediaPlayerImpl::audioDecodedByteCount() const {
732   DCHECK(MessageLoop::current() == main_loop_);
733 
734   media::PipelineStatistics stats = pipeline_->GetStatistics();
735   return stats.audio_bytes_decoded;
736 }
737 
videoDecodedByteCount() const738 unsigned WebMediaPlayerImpl::videoDecodedByteCount() const {
739   DCHECK(MessageLoop::current() == main_loop_);
740 
741   media::PipelineStatistics stats = pipeline_->GetStatistics();
742   return stats.video_bytes_decoded;
743 }
744 
getCurrentFrame()745 WebKit::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 
putCurrentFrame(WebKit::WebVideoFrame * web_video_frame)753 void 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 
WillDestroyCurrentMessageLoop()763 void WebMediaPlayerImpl::WillDestroyCurrentMessageLoop() {
764   Destroy();
765   main_loop_ = NULL;
766 }
767 
Repaint()768 void WebMediaPlayerImpl::Repaint() {
769   DCHECK(MessageLoop::current() == main_loop_);
770   GetClient()->repaint();
771 }
772 
OnPipelineInitialize(PipelineStatus status)773 void 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 
OnPipelineSeek(PipelineStatus status)804 void 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 
OnPipelineEnded(PipelineStatus status)818 void WebMediaPlayerImpl::OnPipelineEnded(PipelineStatus status) {
819   DCHECK(MessageLoop::current() == main_loop_);
820   if (status == media::PIPELINE_OK) {
821     GetClient()->timeChanged();
822   }
823 }
824 
OnPipelineError(PipelineStatus error)825 void 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 
OnNetworkEvent(PipelineStatus status)862 void 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 
SetNetworkState(WebKit::WebMediaPlayer::NetworkState state)882 void 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 
SetReadyState(WebKit::WebMediaPlayer::ReadyState state)890 void 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 
Destroy()898 void 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 
GetClient()924 WebKit::WebMediaPlayerClient* WebMediaPlayerImpl::GetClient() {
925   DCHECK(MessageLoop::current() == main_loop_);
926   DCHECK(client_);
927   return client_;
928 }
929 
930 }  // namespace webkit_glue
931