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