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