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