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. 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 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 101 WebMediaPlayerImpl::Proxy::~Proxy() { 102 Detach(); 103 } 104 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 115 void WebMediaPlayerImpl::Proxy::SetVideoRenderer( 116 scoped_refptr<WebVideoRenderer> video_renderer) { 117 video_renderer_ = video_renderer; 118 } 119 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 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 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 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 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 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 177 void WebMediaPlayerImpl::Proxy::PipelineInitializationCallback( 178 PipelineStatus status) { 179 render_loop_->PostTask(FROM_HERE, NewRunnableMethod( 180 this, &WebMediaPlayerImpl::Proxy::PipelineInitializationTask, status)); 181 } 182 183 void WebMediaPlayerImpl::Proxy::PipelineSeekCallback(PipelineStatus status) { 184 render_loop_->PostTask(FROM_HERE, NewRunnableMethod( 185 this, &WebMediaPlayerImpl::Proxy::PipelineSeekTask, status)); 186 } 187 188 void WebMediaPlayerImpl::Proxy::PipelineEndedCallback(PipelineStatus status) { 189 render_loop_->PostTask(FROM_HERE, NewRunnableMethod( 190 this, &WebMediaPlayerImpl::Proxy::PipelineEndedTask, status)); 191 } 192 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 199 void WebMediaPlayerImpl::Proxy::NetworkEventCallback(PipelineStatus status) { 200 render_loop_->PostTask(FROM_HERE, NewRunnableMethod( 201 this, &WebMediaPlayerImpl::Proxy::NetworkEventTask, status)); 202 } 203 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 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 221 void WebMediaPlayerImpl::Proxy::PipelineInitializationTask( 222 PipelineStatus status) { 223 DCHECK(MessageLoop::current() == render_loop_); 224 if (webmediaplayer_) { 225 webmediaplayer_->OnPipelineInitialize(status); 226 } 227 } 228 229 void WebMediaPlayerImpl::Proxy::PipelineSeekTask(PipelineStatus status) { 230 DCHECK(MessageLoop::current() == render_loop_); 231 if (webmediaplayer_) { 232 webmediaplayer_->OnPipelineSeek(status); 233 } 234 } 235 236 void WebMediaPlayerImpl::Proxy::PipelineEndedTask(PipelineStatus status) { 237 DCHECK(MessageLoop::current() == render_loop_); 238 if (webmediaplayer_) { 239 webmediaplayer_->OnPipelineEnded(status); 240 } 241 } 242 243 void WebMediaPlayerImpl::Proxy::PipelineErrorTask(PipelineStatus error) { 244 DCHECK(MessageLoop::current() == render_loop_); 245 if (webmediaplayer_) { 246 webmediaplayer_->OnPipelineError(error); 247 } 248 } 249 250 void WebMediaPlayerImpl::Proxy::NetworkEventTask(PipelineStatus status) { 251 DCHECK(MessageLoop::current() == render_loop_); 252 if (webmediaplayer_) { 253 webmediaplayer_->OnNetworkEvent(status); 254 } 255 } 256 257 void WebMediaPlayerImpl::Proxy::GetCurrentFrame( 258 scoped_refptr<media::VideoFrame>* frame_out) { 259 if (video_renderer_) 260 video_renderer_->GetCurrentFrame(frame_out); 261 } 262 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 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 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 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 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 402 void WebMediaPlayerImpl::cancelLoad() { 403 DCHECK(MessageLoop::current() == main_loop_); 404 } 405 406 void WebMediaPlayerImpl::play() { 407 DCHECK(MessageLoop::current() == main_loop_); 408 409 paused_ = false; 410 pipeline_->SetPlaybackRate(playback_rate_); 411 } 412 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 421 bool WebMediaPlayerImpl::supportsFullscreen() const { 422 DCHECK(MessageLoop::current() == main_loop_); 423 return true; 424 } 425 426 bool WebMediaPlayerImpl::supportsSave() const { 427 DCHECK(MessageLoop::current() == main_loop_); 428 return true; 429 } 430 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 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 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 491 void WebMediaPlayerImpl::setVolume(float volume) { 492 DCHECK(MessageLoop::current() == main_loop_); 493 494 pipeline_->SetVolume(volume); 495 } 496 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 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 518 bool WebMediaPlayerImpl::totalBytesKnown() { 519 DCHECK(MessageLoop::current() == main_loop_); 520 521 return pipeline_->GetTotalBytes() != 0; 522 } 523 524 bool WebMediaPlayerImpl::hasVideo() const { 525 DCHECK(MessageLoop::current() == main_loop_); 526 527 return pipeline_->HasVideo(); 528 } 529 530 bool WebMediaPlayerImpl::hasAudio() const { 531 DCHECK(MessageLoop::current() == main_loop_); 532 533 return pipeline_->HasAudio(); 534 } 535 536 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 544 bool WebMediaPlayerImpl::paused() const { 545 DCHECK(MessageLoop::current() == main_loop_); 546 547 return pipeline_->GetPlaybackRate() == 0.0f; 548 } 549 550 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 559 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 568 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 577 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 584 WebKit::WebMediaPlayer::NetworkState WebMediaPlayerImpl::networkState() const { 585 return network_state_; 586 } 587 588 WebKit::WebMediaPlayer::ReadyState WebMediaPlayerImpl::readyState() const { 589 return ready_state_; 590 } 591 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 606 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 618 unsigned long long WebMediaPlayerImpl::bytesLoaded() const { 619 DCHECK(MessageLoop::current() == main_loop_); 620 621 return pipeline_->GetBufferedBytes(); 622 } 623 624 unsigned long long WebMediaPlayerImpl::totalBytes() const { 625 DCHECK(MessageLoop::current() == main_loop_); 626 627 return pipeline_->GetTotalBytes(); 628 } 629 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 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 699 bool WebMediaPlayerImpl::hasSingleSecurityOrigin() const { 700 if (proxy_) 701 return proxy_->HasSingleOrigin(); 702 return true; 703 } 704 705 WebKit::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 717 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 724 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 731 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 738 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 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 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 763 void WebMediaPlayerImpl::WillDestroyCurrentMessageLoop() { 764 Destroy(); 765 main_loop_ = NULL; 766 } 767 768 void WebMediaPlayerImpl::Repaint() { 769 DCHECK(MessageLoop::current() == main_loop_); 770 GetClient()->repaint(); 771 } 772 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 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 818 void WebMediaPlayerImpl::OnPipelineEnded(PipelineStatus status) { 819 DCHECK(MessageLoop::current() == main_loop_); 820 if (status == media::PIPELINE_OK) { 821 GetClient()->timeChanged(); 822 } 823 } 824 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 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 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 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 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 924 WebKit::WebMediaPlayerClient* WebMediaPlayerImpl::GetClient() { 925 DCHECK(MessageLoop::current() == main_loop_); 926 DCHECK(client_); 927 return client_; 928 } 929 930 } // namespace webkit_glue 931