| 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/android/webmediaplayer_android.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/command_line.h" | |
| 9 #include "base/files/file_path.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/metrics/histogram.h" | |
| 12 #include "base/strings/string_number_conversions.h" | |
| 13 #include "cc/layers/video_layer.h" | |
| 14 #include "gpu/GLES2/gl2extchromium.h" | |
| 15 #include "media/base/android/media_player_android.h" | |
| 16 #include "media/base/bind_to_loop.h" | |
| 17 #include "media/base/media_switches.h" | |
| 18 #include "media/base/video_frame.h" | |
| 19 #include "net/base/mime_util.h" | |
| 20 #include "third_party/WebKit/public/platform/WebString.h" | |
| 21 #include "third_party/WebKit/public/web/WebDocument.h" | |
| 22 #include "third_party/WebKit/public/web/WebFrame.h" | |
| 23 #include "third_party/WebKit/public/web/WebMediaPlayerClient.h" | |
| 24 #include "third_party/WebKit/public/web/WebMediaSource.h" | |
| 25 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" | |
| 26 #include "third_party/WebKit/public/web/WebView.h" | |
| 27 #include "webkit/renderer/compositor_bindings/web_layer_impl.h" | |
| 28 #include "webkit/renderer/media/android/webmediaplayer_manager_android.h" | |
| 29 #include "webkit/renderer/media/android/webmediaplayer_proxy_android.h" | |
| 30 #include "webkit/renderer/media/crypto/key_systems.h" | |
| 31 #include "webkit/renderer/media/webmediaplayer_delegate.h" | |
| 32 #include "webkit/renderer/media/webmediaplayer_util.h" | |
| 33 | |
| 34 #if defined(GOOGLE_TV) | |
| 35 #include "webkit/renderer/media/media_stream_audio_renderer.h" | |
| 36 #include "webkit/renderer/media/media_stream_client.h" | |
| 37 #endif | |
| 38 | |
| 39 static const uint32 kGLTextureExternalOES = 0x8D65; | |
| 40 | |
| 41 using WebKit::WebMediaPlayer; | |
| 42 using WebKit::WebMediaSource; | |
| 43 using WebKit::WebSize; | |
| 44 using WebKit::WebString; | |
| 45 using WebKit::WebTimeRanges; | |
| 46 using WebKit::WebURL; | |
| 47 using media::MediaPlayerAndroid; | |
| 48 using media::VideoFrame; | |
| 49 | |
| 50 namespace { | |
| 51 // Prefix for histograms related to Encrypted Media Extensions. | |
| 52 const char* kMediaEme = "Media.EME."; | |
| 53 } // namespace | |
| 54 | |
| 55 namespace webkit_media { | |
| 56 | |
| 57 #define BIND_TO_RENDER_LOOP(function) \ | |
| 58 media::BindToLoop(main_loop_, base::Bind(function, AsWeakPtr())) | |
| 59 | |
| 60 WebMediaPlayerAndroid::WebMediaPlayerAndroid( | |
| 61 WebKit::WebFrame* frame, | |
| 62 WebKit::WebMediaPlayerClient* client, | |
| 63 base::WeakPtr<WebMediaPlayerDelegate> delegate, | |
| 64 WebMediaPlayerManagerAndroid* manager, | |
| 65 WebMediaPlayerProxyAndroid* proxy, | |
| 66 StreamTextureFactory* factory, | |
| 67 media::MediaLog* media_log) | |
| 68 : frame_(frame), | |
| 69 client_(client), | |
| 70 delegate_(delegate), | |
| 71 buffered_(1u), | |
| 72 main_loop_(base::MessageLoopProxy::current()), | |
| 73 ignore_metadata_duration_change_(false), | |
| 74 pending_seek_(0), | |
| 75 seeking_(false), | |
| 76 did_loading_progress_(false), | |
| 77 manager_(manager), | |
| 78 network_state_(WebMediaPlayer::NetworkStateEmpty), | |
| 79 ready_state_(WebMediaPlayer::ReadyStateHaveNothing), | |
| 80 is_playing_(false), | |
| 81 needs_establish_peer_(true), | |
| 82 stream_texture_proxy_initialized_(false), | |
| 83 has_size_info_(false), | |
| 84 has_media_metadata_(false), | |
| 85 has_media_info_(false), | |
| 86 stream_texture_factory_(factory), | |
| 87 needs_external_surface_(false), | |
| 88 video_frame_provider_client_(NULL), | |
| 89 #if defined(GOOGLE_TV) | |
| 90 demuxer_(NULL), | |
| 91 #endif // defined(GOOGLE_TV) | |
| 92 source_type_(MediaPlayerAndroid::SOURCE_TYPE_URL), | |
| 93 proxy_(proxy), | |
| 94 current_time_(0), | |
| 95 media_log_(media_log), | |
| 96 media_stream_client_(NULL) { | |
| 97 DCHECK(proxy_); | |
| 98 DCHECK(manager_); | |
| 99 | |
| 100 // We want to be notified of |main_loop_| destruction. | |
| 101 base::MessageLoop::current()->AddDestructionObserver(this); | |
| 102 | |
| 103 player_id_ = manager_->RegisterMediaPlayer(this); | |
| 104 | |
| 105 if (stream_texture_factory_) { | |
| 106 stream_texture_proxy_.reset(stream_texture_factory_->CreateProxy()); | |
| 107 stream_id_ = stream_texture_factory_->CreateStreamTexture(&texture_id_); | |
| 108 ReallocateVideoFrame(); | |
| 109 } | |
| 110 | |
| 111 if (WebKit::WebRuntimeFeatures::isLegacyEncryptedMediaEnabled()) { | |
| 112 // |decryptor_| is owned, so Unretained() is safe here. | |
| 113 decryptor_.reset(new ProxyDecryptor( | |
| 114 #if defined(ENABLE_PEPPER_CDMS) | |
| 115 client, | |
| 116 frame, | |
| 117 #endif // defined(ENABLE_PEPPER_CDMS) | |
| 118 #if defined(OS_ANDROID) && !defined(GOOGLE_TV) | |
| 119 proxy_, | |
| 120 player_id_, // TODO(xhwang): Use media_keys_id when MediaKeys are | |
| 121 // separated from WebMediaPlayer. | |
| 122 #endif // defined(OS_ANDROID) && !defined(GOOGLE_TV) | |
| 123 base::Bind(&WebMediaPlayerAndroid::OnKeyAdded, base::Unretained(this)), | |
| 124 base::Bind(&WebMediaPlayerAndroid::OnKeyError, base::Unretained(this)), | |
| 125 base::Bind(&WebMediaPlayerAndroid::OnKeyMessage, | |
| 126 base::Unretained(this)))); | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 WebMediaPlayerAndroid::~WebMediaPlayerAndroid() { | |
| 131 SetVideoFrameProviderClient(NULL); | |
| 132 client_->setWebLayer(NULL); | |
| 133 | |
| 134 if (proxy_) | |
| 135 proxy_->DestroyPlayer(player_id_); | |
| 136 | |
| 137 if (stream_id_) | |
| 138 stream_texture_factory_->DestroyStreamTexture(texture_id_); | |
| 139 | |
| 140 if (manager_) | |
| 141 manager_->UnregisterMediaPlayer(player_id_); | |
| 142 | |
| 143 if (base::MessageLoop::current()) | |
| 144 base::MessageLoop::current()->RemoveDestructionObserver(this); | |
| 145 | |
| 146 if (source_type_ == MediaPlayerAndroid::SOURCE_TYPE_MSE && delegate_) | |
| 147 delegate_->PlayerGone(this); | |
| 148 | |
| 149 #if defined(GOOGLE_TV) | |
| 150 if (audio_renderer_) { | |
| 151 if (audio_renderer_->IsLocalRenderer()) { | |
| 152 audio_renderer_->Stop(); | |
| 153 } else if (!paused()) { | |
| 154 // The |audio_renderer_| can be shared by multiple remote streams, and | |
| 155 // it will be stopped when WebRtcAudioDeviceImpl goes away. So we simply | |
| 156 // pause the |audio_renderer_| here to avoid re-creating the | |
| 157 // |audio_renderer_|. | |
| 158 audio_renderer_->Pause(); | |
| 159 } | |
| 160 } | |
| 161 if (demuxer_ && !destroy_demuxer_cb_.is_null()) { | |
| 162 media_source_delegate_.reset(); | |
| 163 destroy_demuxer_cb_.Run(); | |
| 164 } | |
| 165 #endif | |
| 166 } | |
| 167 | |
| 168 void WebMediaPlayerAndroid::load(const WebURL& url, CORSMode cors_mode) { | |
| 169 load(url, NULL, cors_mode); | |
| 170 } | |
| 171 | |
| 172 void WebMediaPlayerAndroid::load(const WebURL& url, | |
| 173 WebMediaSource* media_source, | |
| 174 CORSMode cors_mode) { | |
| 175 source_type_ = MediaPlayerAndroid::SOURCE_TYPE_URL; | |
| 176 has_media_metadata_ = false; | |
| 177 has_media_info_ = false; | |
| 178 | |
| 179 if (media_source) | |
| 180 source_type_ = MediaPlayerAndroid::SOURCE_TYPE_MSE; | |
| 181 #if defined(GOOGLE_TV) | |
| 182 if (media_stream_client_) { | |
| 183 DCHECK(!media_source); | |
| 184 source_type_ = MediaPlayerAndroid::SOURCE_TYPE_STREAM; | |
| 185 } | |
| 186 #endif | |
| 187 | |
| 188 if (source_type_ != MediaPlayerAndroid::SOURCE_TYPE_URL) { | |
| 189 has_media_info_ = true; | |
| 190 media_source_delegate_.reset( | |
| 191 new MediaSourceDelegate(proxy_, player_id_, media_log_)); | |
| 192 // |media_source_delegate_| is owned, so Unretained() is safe here. | |
| 193 if (source_type_ == MediaPlayerAndroid::SOURCE_TYPE_MSE) { | |
| 194 media_source_delegate_->InitializeMediaSource( | |
| 195 media_source, | |
| 196 base::Bind(&WebMediaPlayerAndroid::OnNeedKey, base::Unretained(this)), | |
| 197 base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState, | |
| 198 base::Unretained(this)), | |
| 199 BIND_TO_RENDER_LOOP(&WebMediaPlayerAndroid::OnDurationChange)); | |
| 200 } | |
| 201 #if defined(GOOGLE_TV) | |
| 202 if (source_type_ == MediaPlayerAndroid::SOURCE_TYPE_STREAM) { | |
| 203 media_source_delegate_->InitializeMediaStream( | |
| 204 demuxer_, | |
| 205 base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState, | |
| 206 base::Unretained(this))); | |
| 207 audio_renderer_ = media_stream_client_->GetAudioRenderer(url); | |
| 208 if (audio_renderer_) | |
| 209 audio_renderer_->Start(); | |
| 210 } | |
| 211 #endif | |
| 212 } else { | |
| 213 info_loader_.reset( | |
| 214 new MediaInfoLoader( | |
| 215 url, | |
| 216 cors_mode, | |
| 217 base::Bind(&WebMediaPlayerAndroid::DidLoadMediaInfo, | |
| 218 base::Unretained(this)))); | |
| 219 info_loader_->Start(frame_); | |
| 220 } | |
| 221 | |
| 222 InitializeMediaPlayer(url); | |
| 223 } | |
| 224 | |
| 225 void WebMediaPlayerAndroid::InitializeMediaPlayer(const WebURL& url) { | |
| 226 url_ = url; | |
| 227 GURL first_party_url = frame_->document().firstPartyForCookies(); | |
| 228 proxy_->Initialize(player_id_, url, source_type_, first_party_url); | |
| 229 if (manager_->IsInFullscreen(frame_)) | |
| 230 proxy_->EnterFullscreen(player_id_); | |
| 231 | |
| 232 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading); | |
| 233 UpdateReadyState(WebMediaPlayer::ReadyStateHaveNothing); | |
| 234 } | |
| 235 | |
| 236 void WebMediaPlayerAndroid::DidLoadMediaInfo( | |
| 237 MediaInfoLoader::Status status) { | |
| 238 DCHECK(!media_source_delegate_); | |
| 239 if (status == MediaInfoLoader::kFailed) { | |
| 240 info_loader_.reset(); | |
| 241 UpdateNetworkState(WebMediaPlayer::NetworkStateNetworkError); | |
| 242 return; | |
| 243 } | |
| 244 | |
| 245 has_media_info_ = true; | |
| 246 if (has_media_metadata_ && | |
| 247 ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) { | |
| 248 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata); | |
| 249 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData); | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 void WebMediaPlayerAndroid::play() { | |
| 254 #if defined(GOOGLE_TV) | |
| 255 if (hasVideo() && needs_external_surface_) { | |
| 256 DCHECK(!needs_establish_peer_); | |
| 257 proxy_->RequestExternalSurface(player_id_, last_computed_rect_); | |
| 258 } | |
| 259 if (audio_renderer_ && paused()) | |
| 260 audio_renderer_->Play(); | |
| 261 #endif | |
| 262 if (hasVideo() && needs_establish_peer_) | |
| 263 EstablishSurfaceTexturePeer(); | |
| 264 | |
| 265 if (paused()) | |
| 266 proxy_->Start(player_id_); | |
| 267 UpdatePlayingState(true); | |
| 268 } | |
| 269 | |
| 270 void WebMediaPlayerAndroid::pause() { | |
| 271 #if defined(GOOGLE_TV) | |
| 272 if (audio_renderer_ && !paused()) | |
| 273 audio_renderer_->Pause(); | |
| 274 #endif | |
| 275 proxy_->Pause(player_id_); | |
| 276 UpdatePlayingState(false); | |
| 277 } | |
| 278 | |
| 279 void WebMediaPlayerAndroid::seek(double seconds) { | |
| 280 pending_seek_ = seconds; | |
| 281 if (seeking_ && media_source_delegate_) | |
| 282 media_source_delegate_->CancelPendingSeek(); | |
| 283 seeking_ = true; | |
| 284 | |
| 285 base::TimeDelta seek_time = ConvertSecondsToTimestamp(seconds); | |
| 286 #if defined(GOOGLE_TV) | |
| 287 // TODO(qinmin): check if GTV can also defer the seek until the browser side | |
| 288 // player is ready. | |
| 289 if (media_source_delegate_) | |
| 290 media_source_delegate_->Seek(seek_time); | |
| 291 #endif | |
| 292 proxy_->Seek(player_id_, seek_time); | |
| 293 } | |
| 294 | |
| 295 bool WebMediaPlayerAndroid::supportsFullscreen() const { | |
| 296 return true; | |
| 297 } | |
| 298 | |
| 299 bool WebMediaPlayerAndroid::supportsSave() const { | |
| 300 return false; | |
| 301 } | |
| 302 | |
| 303 void WebMediaPlayerAndroid::setRate(double rate) { | |
| 304 NOTIMPLEMENTED(); | |
| 305 } | |
| 306 | |
| 307 void WebMediaPlayerAndroid::setVolume(double volume) { | |
| 308 NOTIMPLEMENTED(); | |
| 309 } | |
| 310 | |
| 311 bool WebMediaPlayerAndroid::hasVideo() const { | |
| 312 // If we have obtained video size information before, use it. | |
| 313 if (has_size_info_) | |
| 314 return !natural_size_.isEmpty(); | |
| 315 | |
| 316 // TODO(qinmin): need a better method to determine whether the current media | |
| 317 // content contains video. Android does not provide any function to do | |
| 318 // this. | |
| 319 // We don't know whether the current media content has video unless | |
| 320 // the player is prepared. If the player is not prepared, we fall back | |
| 321 // to the mime-type. There may be no mime-type on a redirect URL. | |
| 322 // In that case, we conservatively assume it contains video so that | |
| 323 // enterfullscreen call will not fail. | |
| 324 if (!url_.has_path()) | |
| 325 return false; | |
| 326 std::string mime; | |
| 327 if(!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime)) | |
| 328 return true; | |
| 329 return mime.find("audio/") == std::string::npos; | |
| 330 } | |
| 331 | |
| 332 bool WebMediaPlayerAndroid::hasAudio() const { | |
| 333 // TODO(hclam): Query status of audio and return the actual value. | |
| 334 return true; | |
| 335 } | |
| 336 | |
| 337 bool WebMediaPlayerAndroid::paused() const { | |
| 338 return !is_playing_; | |
| 339 } | |
| 340 | |
| 341 bool WebMediaPlayerAndroid::seeking() const { | |
| 342 return seeking_; | |
| 343 } | |
| 344 | |
| 345 double WebMediaPlayerAndroid::duration() const { | |
| 346 // HTML5 spec requires duration to be NaN if readyState is HAVE_NOTHING | |
| 347 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing) | |
| 348 return std::numeric_limits<double>::quiet_NaN(); | |
| 349 | |
| 350 // TODO(wolenetz): Correctly handle durations that MediaSourcePlayer | |
| 351 // considers unseekable, including kInfiniteDuration(). | |
| 352 // See http://crbug.com/248396 | |
| 353 return duration_.InSecondsF(); | |
| 354 } | |
| 355 | |
| 356 double WebMediaPlayerAndroid::currentTime() const { | |
| 357 // If the player is pending for a seek, return the seek time. | |
| 358 if (seeking()) | |
| 359 return pending_seek_; | |
| 360 return current_time_; | |
| 361 } | |
| 362 | |
| 363 WebSize WebMediaPlayerAndroid::naturalSize() const { | |
| 364 return natural_size_; | |
| 365 } | |
| 366 | |
| 367 WebMediaPlayer::NetworkState WebMediaPlayerAndroid::networkState() const { | |
| 368 return network_state_; | |
| 369 } | |
| 370 | |
| 371 WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const { | |
| 372 return ready_state_; | |
| 373 } | |
| 374 | |
| 375 const WebTimeRanges& WebMediaPlayerAndroid::buffered() { | |
| 376 if (media_source_delegate_) | |
| 377 return media_source_delegate_->Buffered(); | |
| 378 return buffered_; | |
| 379 } | |
| 380 | |
| 381 double WebMediaPlayerAndroid::maxTimeSeekable() const { | |
| 382 // If we haven't even gotten to ReadyStateHaveMetadata yet then just | |
| 383 // return 0 so that the seekable range is empty. | |
| 384 if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata) | |
| 385 return 0.0; | |
| 386 | |
| 387 // TODO(hclam): If this stream is not seekable this should return 0. | |
| 388 return duration(); | |
| 389 } | |
| 390 | |
| 391 bool WebMediaPlayerAndroid::didLoadingProgress() const { | |
| 392 bool ret = did_loading_progress_; | |
| 393 did_loading_progress_ = false; | |
| 394 return ret; | |
| 395 } | |
| 396 | |
| 397 void WebMediaPlayerAndroid::paint(WebKit::WebCanvas* canvas, | |
| 398 const WebKit::WebRect& rect, | |
| 399 unsigned char alpha) { | |
| 400 NOTIMPLEMENTED(); | |
| 401 } | |
| 402 | |
| 403 bool WebMediaPlayerAndroid::copyVideoTextureToPlatformTexture( | |
| 404 WebKit::WebGraphicsContext3D* web_graphics_context, | |
| 405 unsigned int texture, | |
| 406 unsigned int level, | |
| 407 unsigned int internal_format, | |
| 408 unsigned int type, | |
| 409 bool premultiply_alpha, | |
| 410 bool flip_y) { | |
| 411 if (!texture_id_) | |
| 412 return false; | |
| 413 | |
| 414 // The video is stored in an unmultiplied format, so premultiply if | |
| 415 // necessary. | |
| 416 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, | |
| 417 premultiply_alpha); | |
| 418 | |
| 419 // Application itself needs to take care of setting the right flip_y | |
| 420 // value down to get the expected result. | |
| 421 // flip_y==true means to reverse the video orientation while | |
| 422 // flip_y==false means to keep the intrinsic orientation. | |
| 423 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y); | |
| 424 web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D, texture_id_, | |
| 425 texture, level, internal_format, | |
| 426 type); | |
| 427 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false); | |
| 428 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, | |
| 429 false); | |
| 430 return true; | |
| 431 } | |
| 432 | |
| 433 bool WebMediaPlayerAndroid::hasSingleSecurityOrigin() const { | |
| 434 if (info_loader_) | |
| 435 return info_loader_->HasSingleOrigin(); | |
| 436 // The info loader may have failed. | |
| 437 if (source_type_ == MediaPlayerAndroid::SOURCE_TYPE_URL) | |
| 438 return false; | |
| 439 return true; | |
| 440 } | |
| 441 | |
| 442 bool WebMediaPlayerAndroid::didPassCORSAccessCheck() const { | |
| 443 if (info_loader_) | |
| 444 return info_loader_->DidPassCORSAccessCheck(); | |
| 445 return false; | |
| 446 } | |
| 447 | |
| 448 double WebMediaPlayerAndroid::mediaTimeForTimeValue(double timeValue) const { | |
| 449 return ConvertSecondsToTimestamp(timeValue).InSecondsF(); | |
| 450 } | |
| 451 | |
| 452 unsigned WebMediaPlayerAndroid::decodedFrameCount() const { | |
| 453 if (media_source_delegate_) | |
| 454 return media_source_delegate_->DecodedFrameCount(); | |
| 455 NOTIMPLEMENTED(); | |
| 456 return 0; | |
| 457 } | |
| 458 | |
| 459 unsigned WebMediaPlayerAndroid::droppedFrameCount() const { | |
| 460 if (media_source_delegate_) | |
| 461 return media_source_delegate_->DroppedFrameCount(); | |
| 462 NOTIMPLEMENTED(); | |
| 463 return 0; | |
| 464 } | |
| 465 | |
| 466 unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const { | |
| 467 if (media_source_delegate_) | |
| 468 return media_source_delegate_->AudioDecodedByteCount(); | |
| 469 NOTIMPLEMENTED(); | |
| 470 return 0; | |
| 471 } | |
| 472 | |
| 473 unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const { | |
| 474 if (media_source_delegate_) | |
| 475 return media_source_delegate_->VideoDecodedByteCount(); | |
| 476 NOTIMPLEMENTED(); | |
| 477 return 0; | |
| 478 } | |
| 479 | |
| 480 void WebMediaPlayerAndroid::OnMediaMetadataChanged( | |
| 481 base::TimeDelta duration, int width, int height, bool success) { | |
| 482 bool need_to_signal_duration_changed = false; | |
| 483 | |
| 484 if (url_.SchemeIs("file")) | |
| 485 UpdateNetworkState(WebMediaPlayer::NetworkStateLoaded); | |
| 486 | |
| 487 // Update duration, if necessary, prior to ready state updates that may | |
| 488 // cause duration() query. | |
| 489 // TODO(wolenetz): Correctly handle durations that MediaSourcePlayer | |
| 490 // considers unseekable, including kInfiniteDuration(). | |
| 491 // See http://crbug.com/248396 | |
| 492 if (!ignore_metadata_duration_change_ && duration_ != duration) { | |
| 493 duration_ = duration; | |
| 494 | |
| 495 // Client readyState transition from HAVE_NOTHING to HAVE_METADATA | |
| 496 // already triggers a durationchanged event. If this is a different | |
| 497 // transition, remember to signal durationchanged. | |
| 498 // Do not ever signal durationchanged on metadata change in MSE case | |
| 499 // because OnDurationChange() handles this. | |
| 500 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing && | |
| 501 source_type_ != MediaPlayerAndroid::SOURCE_TYPE_MSE) { | |
| 502 need_to_signal_duration_changed = true; | |
| 503 } | |
| 504 } | |
| 505 | |
| 506 has_media_metadata_ = true; | |
| 507 if (has_media_info_ && | |
| 508 ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) { | |
| 509 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata); | |
| 510 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData); | |
| 511 } | |
| 512 | |
| 513 // TODO(wolenetz): Should we just abort early and set network state to an | |
| 514 // error if success == false? See http://crbug.com/248399 | |
| 515 if (success) | |
| 516 OnVideoSizeChanged(width, height); | |
| 517 | |
| 518 if (hasVideo() && !video_weblayer_ && client_->needsWebLayerForVideo()) { | |
| 519 video_weblayer_.reset( | |
| 520 new webkit::WebLayerImpl(cc::VideoLayer::Create(this))); | |
| 521 client_->setWebLayer(video_weblayer_.get()); | |
| 522 } | |
| 523 | |
| 524 if (need_to_signal_duration_changed) | |
| 525 client_->durationChanged(); | |
| 526 } | |
| 527 | |
| 528 void WebMediaPlayerAndroid::OnPlaybackComplete() { | |
| 529 // When playback is about to finish, android media player often stops | |
| 530 // at a time which is smaller than the duration. This makes webkit never | |
| 531 // know that the playback has finished. To solve this, we set the | |
| 532 // current time to media duration when OnPlaybackComplete() get called. | |
| 533 OnTimeUpdate(duration_); | |
| 534 client_->timeChanged(); | |
| 535 } | |
| 536 | |
| 537 void WebMediaPlayerAndroid::OnBufferingUpdate(int percentage) { | |
| 538 buffered_[0].end = duration() * percentage / 100; | |
| 539 did_loading_progress_ = true; | |
| 540 } | |
| 541 | |
| 542 void WebMediaPlayerAndroid::OnSeekComplete(base::TimeDelta current_time) { | |
| 543 seeking_ = false; | |
| 544 | |
| 545 OnTimeUpdate(current_time); | |
| 546 | |
| 547 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData); | |
| 548 | |
| 549 client_->timeChanged(); | |
| 550 } | |
| 551 | |
| 552 void WebMediaPlayerAndroid::OnMediaError(int error_type) { | |
| 553 switch (error_type) { | |
| 554 case MediaPlayerAndroid::MEDIA_ERROR_FORMAT: | |
| 555 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError); | |
| 556 break; | |
| 557 case MediaPlayerAndroid::MEDIA_ERROR_DECODE: | |
| 558 UpdateNetworkState(WebMediaPlayer::NetworkStateDecodeError); | |
| 559 break; | |
| 560 case MediaPlayerAndroid::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK: | |
| 561 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError); | |
| 562 break; | |
| 563 case MediaPlayerAndroid::MEDIA_ERROR_INVALID_CODE: | |
| 564 break; | |
| 565 } | |
| 566 client_->repaint(); | |
| 567 } | |
| 568 | |
| 569 void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) { | |
| 570 has_size_info_ = true; | |
| 571 if (natural_size_.width == width && natural_size_.height == height) | |
| 572 return; | |
| 573 | |
| 574 #if defined(GOOGLE_TV) | |
| 575 static bool has_switch = CommandLine::ForCurrentProcess()->HasSwitch( | |
| 576 switches::kUseExternalVideoSurfaceThresholdInPixels); | |
| 577 static int threshold = 0; | |
| 578 static bool parsed_arg = | |
| 579 has_switch && | |
| 580 base::StringToInt( | |
| 581 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
| 582 switches::kUseExternalVideoSurfaceThresholdInPixels), | |
| 583 &threshold); | |
| 584 | |
| 585 if ((parsed_arg && threshold <= width * height) || | |
| 586 // Use H/W surface for MSE as the content is protected. | |
| 587 media_source_delegate_) { | |
| 588 if (stream_texture_factory_) { | |
| 589 stream_texture_factory_->DestroyStreamTexture(texture_id_); | |
| 590 stream_id_ = 0; | |
| 591 texture_id_ = 0; | |
| 592 } | |
| 593 needs_external_surface_ = true; | |
| 594 SetNeedsEstablishPeer(false); | |
| 595 if (!paused()) | |
| 596 proxy_->RequestExternalSurface(player_id_, last_computed_rect_); | |
| 597 } | |
| 598 #endif | |
| 599 | |
| 600 natural_size_.width = width; | |
| 601 natural_size_.height = height; | |
| 602 ReallocateVideoFrame(); | |
| 603 } | |
| 604 | |
| 605 void WebMediaPlayerAndroid::OnTimeUpdate(base::TimeDelta current_time) { | |
| 606 current_time_ = current_time.InSecondsF(); | |
| 607 } | |
| 608 | |
| 609 void WebMediaPlayerAndroid::OnDidEnterFullscreen() { | |
| 610 if (!manager_->IsInFullscreen(frame_)) { | |
| 611 frame_->view()->willEnterFullScreen(); | |
| 612 frame_->view()->didEnterFullScreen(); | |
| 613 manager_->DidEnterFullscreen(frame_); | |
| 614 } | |
| 615 } | |
| 616 | |
| 617 void WebMediaPlayerAndroid::OnDidExitFullscreen() { | |
| 618 // |needs_external_surface_| is always false on non-TV devices. | |
| 619 if (!needs_external_surface_) | |
| 620 SetNeedsEstablishPeer(true); | |
| 621 // We had the fullscreen surface connected to Android MediaPlayer, | |
| 622 // so reconnect our surface texture for embedded playback. | |
| 623 if (!paused()) | |
| 624 EstablishSurfaceTexturePeer(); | |
| 625 | |
| 626 frame_->view()->willExitFullScreen(); | |
| 627 frame_->view()->didExitFullScreen(); | |
| 628 manager_->DidExitFullscreen(); | |
| 629 client_->repaint(); | |
| 630 } | |
| 631 | |
| 632 void WebMediaPlayerAndroid::OnMediaPlayerPlay() { | |
| 633 UpdatePlayingState(true); | |
| 634 client_->playbackStateChanged(); | |
| 635 } | |
| 636 | |
| 637 void WebMediaPlayerAndroid::OnMediaPlayerPause() { | |
| 638 UpdatePlayingState(false); | |
| 639 client_->playbackStateChanged(); | |
| 640 } | |
| 641 | |
| 642 void WebMediaPlayerAndroid::OnMediaSeekRequest(base::TimeDelta time_to_seek) { | |
| 643 if (!media_source_delegate_) | |
| 644 return; | |
| 645 | |
| 646 if (!seeking_) | |
| 647 media_source_delegate_->CancelPendingSeek(); | |
| 648 media_source_delegate_->Seek(time_to_seek); | |
| 649 OnTimeUpdate(time_to_seek); | |
| 650 } | |
| 651 | |
| 652 void WebMediaPlayerAndroid::OnMediaConfigRequest() { | |
| 653 if (!media_source_delegate_) | |
| 654 return; | |
| 655 | |
| 656 media_source_delegate_->OnMediaConfigRequest(); | |
| 657 } | |
| 658 | |
| 659 void WebMediaPlayerAndroid::OnDurationChange(const base::TimeDelta& duration) { | |
| 660 // Only MSE |source_type_| registers this callback. | |
| 661 DCHECK(source_type_ == MediaPlayerAndroid::SOURCE_TYPE_MSE); | |
| 662 | |
| 663 // Cache the new duration value and trust it over any subsequent duration | |
| 664 // values received in OnMediaMetadataChanged(). | |
| 665 // TODO(wolenetz): Correctly handle durations that MediaSourcePlayer | |
| 666 // considers unseekable, including kInfiniteDuration(). | |
| 667 // See http://crbug.com/248396 | |
| 668 duration_ = duration; | |
| 669 ignore_metadata_duration_change_ = true; | |
| 670 | |
| 671 // Send message to Android MediaSourcePlayer to update duration. | |
| 672 if (proxy_) | |
| 673 proxy_->DurationChanged(player_id_, duration_); | |
| 674 | |
| 675 // Notify MediaPlayerClient that duration has changed, if > HAVE_NOTHING. | |
| 676 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing) | |
| 677 client_->durationChanged(); | |
| 678 } | |
| 679 | |
| 680 void WebMediaPlayerAndroid::UpdateNetworkState( | |
| 681 WebMediaPlayer::NetworkState state) { | |
| 682 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing && | |
| 683 (state == WebMediaPlayer::NetworkStateNetworkError || | |
| 684 state == WebMediaPlayer::NetworkStateDecodeError)) { | |
| 685 // Any error that occurs before reaching ReadyStateHaveMetadata should | |
| 686 // be considered a format error. | |
| 687 network_state_ = WebMediaPlayer::NetworkStateFormatError; | |
| 688 } else { | |
| 689 network_state_ = state; | |
| 690 } | |
| 691 client_->networkStateChanged(); | |
| 692 } | |
| 693 | |
| 694 void WebMediaPlayerAndroid::UpdateReadyState( | |
| 695 WebMediaPlayer::ReadyState state) { | |
| 696 ready_state_ = state; | |
| 697 client_->readyStateChanged(); | |
| 698 } | |
| 699 | |
| 700 void WebMediaPlayerAndroid::OnPlayerReleased() { | |
| 701 // |needs_external_surface_| is always false on non-TV devices. | |
| 702 if (!needs_external_surface_) | |
| 703 needs_establish_peer_ = true; | |
| 704 } | |
| 705 | |
| 706 void WebMediaPlayerAndroid::ReleaseMediaResources() { | |
| 707 switch (network_state_) { | |
| 708 // Pause the media player and inform WebKit if the player is in a good | |
| 709 // shape. | |
| 710 case WebMediaPlayer::NetworkStateIdle: | |
| 711 case WebMediaPlayer::NetworkStateLoading: | |
| 712 case WebMediaPlayer::NetworkStateLoaded: | |
| 713 pause(); | |
| 714 client_->playbackStateChanged(); | |
| 715 break; | |
| 716 // If a WebMediaPlayer instance has entered into one of these states, | |
| 717 // the internal network state in HTMLMediaElement could be set to empty. | |
| 718 // And calling playbackStateChanged() could get this object deleted. | |
| 719 case WebMediaPlayer::NetworkStateEmpty: | |
| 720 case WebMediaPlayer::NetworkStateFormatError: | |
| 721 case WebMediaPlayer::NetworkStateNetworkError: | |
| 722 case WebMediaPlayer::NetworkStateDecodeError: | |
| 723 break; | |
| 724 } | |
| 725 proxy_->ReleaseResources(player_id_); | |
| 726 OnPlayerReleased(); | |
| 727 } | |
| 728 | |
| 729 void WebMediaPlayerAndroid::WillDestroyCurrentMessageLoop() { | |
| 730 if (manager_) | |
| 731 manager_->UnregisterMediaPlayer(player_id_); | |
| 732 Detach(); | |
| 733 } | |
| 734 | |
| 735 void WebMediaPlayerAndroid::Detach() { | |
| 736 if (stream_id_) { | |
| 737 stream_texture_factory_->DestroyStreamTexture(texture_id_); | |
| 738 stream_id_ = 0; | |
| 739 } | |
| 740 | |
| 741 media_source_delegate_.reset(); | |
| 742 current_frame_ = NULL; | |
| 743 manager_ = NULL; | |
| 744 proxy_ = NULL; | |
| 745 } | |
| 746 | |
| 747 void WebMediaPlayerAndroid::ReallocateVideoFrame() { | |
| 748 if (needs_external_surface_) { | |
| 749 // VideoFrame::CreateHoleFrame is only defined under GOOGLE_TV. | |
| 750 #if defined(GOOGLE_TV) | |
| 751 if (!natural_size_.isEmpty()) { | |
| 752 current_frame_ = VideoFrame::CreateHoleFrame(natural_size_); | |
| 753 // Force the client to grab the hole frame. | |
| 754 client_->repaint(); | |
| 755 } | |
| 756 #else | |
| 757 NOTIMPLEMENTED() << "Hole punching not supported outside of Google TV"; | |
| 758 #endif | |
| 759 } else if (texture_id_) { | |
| 760 current_frame_ = VideoFrame::WrapNativeTexture( | |
| 761 texture_id_, kGLTextureExternalOES, natural_size_, | |
| 762 gfx::Rect(natural_size_), natural_size_, base::TimeDelta(), | |
| 763 VideoFrame::ReadPixelsCB(), | |
| 764 base::Closure()); | |
| 765 } | |
| 766 } | |
| 767 | |
| 768 void WebMediaPlayerAndroid::SetVideoFrameProviderClient( | |
| 769 cc::VideoFrameProvider::Client* client) { | |
| 770 // This is called from both the main renderer thread and the compositor | |
| 771 // thread (when the main thread is blocked). | |
| 772 if (video_frame_provider_client_) | |
| 773 video_frame_provider_client_->StopUsingProvider(); | |
| 774 video_frame_provider_client_ = client; | |
| 775 | |
| 776 // Set the callback target when a frame is produced. | |
| 777 if (stream_texture_proxy_) | |
| 778 stream_texture_proxy_->SetClient(client); | |
| 779 } | |
| 780 | |
| 781 scoped_refptr<media::VideoFrame> WebMediaPlayerAndroid::GetCurrentFrame() { | |
| 782 if (!stream_texture_proxy_initialized_ && stream_texture_proxy_ && | |
| 783 stream_id_ && !needs_external_surface_) { | |
| 784 gfx::Size natural_size = current_frame_->natural_size(); | |
| 785 stream_texture_proxy_->BindToCurrentThread( | |
| 786 stream_id_, natural_size.width(), natural_size.height()); | |
| 787 stream_texture_proxy_initialized_ = true; | |
| 788 } | |
| 789 return current_frame_; | |
| 790 } | |
| 791 | |
| 792 void WebMediaPlayerAndroid::PutCurrentFrame( | |
| 793 const scoped_refptr<media::VideoFrame>& frame) { | |
| 794 } | |
| 795 | |
| 796 void WebMediaPlayerAndroid::EstablishSurfaceTexturePeer() { | |
| 797 if (media_source_delegate_ && stream_texture_factory_) { | |
| 798 // MediaCodec will release the old surface when it goes away, we need to | |
| 799 // recreate a new one each time this is called. | |
| 800 stream_texture_factory_->DestroyStreamTexture(texture_id_); | |
| 801 stream_id_ = 0; | |
| 802 texture_id_ = 0; | |
| 803 stream_id_ = stream_texture_factory_->CreateStreamTexture(&texture_id_); | |
| 804 ReallocateVideoFrame(); | |
| 805 stream_texture_proxy_initialized_ = false; | |
| 806 } | |
| 807 if (stream_texture_factory_.get() && stream_id_) | |
| 808 stream_texture_factory_->EstablishPeer(stream_id_, player_id_); | |
| 809 needs_establish_peer_ = false; | |
| 810 } | |
| 811 | |
| 812 void WebMediaPlayerAndroid::SetNeedsEstablishPeer(bool needs_establish_peer) { | |
| 813 needs_establish_peer_ = needs_establish_peer; | |
| 814 } | |
| 815 | |
| 816 void WebMediaPlayerAndroid::UpdatePlayingState(bool is_playing) { | |
| 817 is_playing_ = is_playing; | |
| 818 if (source_type_ != MediaPlayerAndroid::SOURCE_TYPE_MSE || !delegate_) | |
| 819 return; | |
| 820 if (is_playing) | |
| 821 delegate_->DidPlay(this); | |
| 822 else | |
| 823 delegate_->DidPause(this); | |
| 824 } | |
| 825 | |
| 826 #if defined(GOOGLE_TV) | |
| 827 bool WebMediaPlayerAndroid::RetrieveGeometryChange(gfx::RectF* rect) { | |
| 828 if (!video_weblayer_) | |
| 829 return false; | |
| 830 | |
| 831 // Compute the geometry of video frame layer. | |
| 832 cc::Layer* layer = video_weblayer_->layer(); | |
| 833 rect->set_size(layer->bounds()); | |
| 834 while (layer) { | |
| 835 rect->Offset(layer->position().OffsetFromOrigin()); | |
| 836 layer = layer->parent(); | |
| 837 } | |
| 838 | |
| 839 // Return false when the geometry hasn't been changed from the last time. | |
| 840 if (last_computed_rect_ == *rect) | |
| 841 return false; | |
| 842 | |
| 843 // Store the changed geometry information when it is actually changed. | |
| 844 last_computed_rect_ = *rect; | |
| 845 return true; | |
| 846 } | |
| 847 #endif | |
| 848 | |
| 849 // The following EME related code is copied from WebMediaPlayerImpl. | |
| 850 // TODO(xhwang): Remove duplicate code between WebMediaPlayerAndroid and | |
| 851 // WebMediaPlayerImpl. | |
| 852 // TODO(kjyoun): Update Google TV EME implementation to use IPC. | |
| 853 | |
| 854 // Helper functions to report media EME related stats to UMA. They follow the | |
| 855 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and | |
| 856 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is | |
| 857 // that UMA_* macros require the names to be constant throughout the process' | |
| 858 // lifetime. | |
| 859 static void EmeUMAHistogramEnumeration(const std::string& key_system, | |
| 860 const std::string& method, | |
| 861 int sample, | |
| 862 int boundary_value) { | |
| 863 base::LinearHistogram::FactoryGet( | |
| 864 kMediaEme + KeySystemNameForUMA(key_system) + "." + method, | |
| 865 1, boundary_value, boundary_value + 1, | |
| 866 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample); | |
| 867 } | |
| 868 | |
| 869 static void EmeUMAHistogramCounts(const std::string& key_system, | |
| 870 const std::string& method, | |
| 871 int sample) { | |
| 872 // Use the same parameters as UMA_HISTOGRAM_COUNTS. | |
| 873 base::Histogram::FactoryGet( | |
| 874 kMediaEme + KeySystemNameForUMA(key_system) + "." + method, | |
| 875 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample); | |
| 876 } | |
| 877 | |
| 878 // Helper enum for reporting generateKeyRequest/addKey histograms. | |
| 879 enum MediaKeyException { | |
| 880 kUnknownResultId, | |
| 881 kSuccess, | |
| 882 kKeySystemNotSupported, | |
| 883 kInvalidPlayerState, | |
| 884 kMaxMediaKeyException | |
| 885 }; | |
| 886 | |
| 887 static MediaKeyException MediaKeyExceptionForUMA( | |
| 888 WebMediaPlayer::MediaKeyException e) { | |
| 889 switch (e) { | |
| 890 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported: | |
| 891 return kKeySystemNotSupported; | |
| 892 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState: | |
| 893 return kInvalidPlayerState; | |
| 894 case WebMediaPlayer::MediaKeyExceptionNoError: | |
| 895 return kSuccess; | |
| 896 default: | |
| 897 return kUnknownResultId; | |
| 898 } | |
| 899 } | |
| 900 | |
| 901 // Helper for converting |key_system| name and exception |e| to a pair of enum | |
| 902 // values from above, for reporting to UMA. | |
| 903 static void ReportMediaKeyExceptionToUMA( | |
| 904 const std::string& method, | |
| 905 const WebString& key_system, | |
| 906 WebMediaPlayer::MediaKeyException e) { | |
| 907 MediaKeyException result_id = MediaKeyExceptionForUMA(e); | |
| 908 DCHECK_NE(result_id, kUnknownResultId) << e; | |
| 909 EmeUMAHistogramEnumeration( | |
| 910 key_system.utf8(), method, result_id, kMaxMediaKeyException); | |
| 911 } | |
| 912 | |
| 913 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::generateKeyRequest( | |
| 914 const WebString& key_system, | |
| 915 const unsigned char* init_data, | |
| 916 unsigned init_data_length) { | |
| 917 WebMediaPlayer::MediaKeyException e = | |
| 918 GenerateKeyRequestInternal(key_system, init_data, init_data_length); | |
| 919 ReportMediaKeyExceptionToUMA("generateKeyRequest", key_system, e); | |
| 920 return e; | |
| 921 } | |
| 922 | |
| 923 WebMediaPlayer::MediaKeyException | |
| 924 WebMediaPlayerAndroid::GenerateKeyRequestInternal( | |
| 925 const WebString& key_system, | |
| 926 const unsigned char* init_data, | |
| 927 unsigned init_data_length) { | |
| 928 DVLOG(1) << "generateKeyRequest: " << key_system.utf8().data() << ": " | |
| 929 << std::string(reinterpret_cast<const char*>(init_data), | |
| 930 static_cast<size_t>(init_data_length)); | |
| 931 | |
| 932 if (!IsSupportedKeySystem(key_system)) | |
| 933 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
| 934 | |
| 935 // We do not support run-time switching between key systems for now. | |
| 936 if (current_key_system_.isEmpty()) { | |
| 937 if (!decryptor_->InitializeCDM(key_system.utf8())) | |
| 938 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
| 939 current_key_system_ = key_system; | |
| 940 } else if (key_system != current_key_system_) { | |
| 941 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; | |
| 942 } | |
| 943 | |
| 944 // TODO(xhwang): We assume all streams are from the same container (thus have | |
| 945 // the same "type") for now. In the future, the "type" should be passed down | |
| 946 // from the application. | |
| 947 if (!decryptor_->GenerateKeyRequest(init_data_type_, | |
| 948 init_data, init_data_length)) { | |
| 949 current_key_system_.reset(); | |
| 950 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
| 951 } | |
| 952 | |
| 953 return WebMediaPlayer::MediaKeyExceptionNoError; | |
| 954 } | |
| 955 | |
| 956 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::addKey( | |
| 957 const WebString& key_system, | |
| 958 const unsigned char* key, | |
| 959 unsigned key_length, | |
| 960 const unsigned char* init_data, | |
| 961 unsigned init_data_length, | |
| 962 const WebString& session_id) { | |
| 963 WebMediaPlayer::MediaKeyException e = AddKeyInternal( | |
| 964 key_system, key, key_length, init_data, init_data_length, session_id); | |
| 965 ReportMediaKeyExceptionToUMA("addKey", key_system, e); | |
| 966 return e; | |
| 967 } | |
| 968 | |
| 969 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::AddKeyInternal( | |
| 970 const WebString& key_system, | |
| 971 const unsigned char* key, | |
| 972 unsigned key_length, | |
| 973 const unsigned char* init_data, | |
| 974 unsigned init_data_length, | |
| 975 const WebString& session_id) { | |
| 976 DCHECK(key); | |
| 977 DCHECK_GT(key_length, 0u); | |
| 978 DVLOG(1) << "addKey: " << key_system.utf8().data() << ": " | |
| 979 << std::string(reinterpret_cast<const char*>(key), | |
| 980 static_cast<size_t>(key_length)) << ", " | |
| 981 << std::string(reinterpret_cast<const char*>(init_data), | |
| 982 static_cast<size_t>(init_data_length)) | |
| 983 << " [" << session_id.utf8().data() << "]"; | |
| 984 | |
| 985 if (!IsSupportedKeySystem(key_system)) | |
| 986 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
| 987 | |
| 988 if (current_key_system_.isEmpty() || key_system != current_key_system_) | |
| 989 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; | |
| 990 | |
| 991 decryptor_->AddKey(key, key_length, init_data, init_data_length, | |
| 992 session_id.utf8()); | |
| 993 return WebMediaPlayer::MediaKeyExceptionNoError; | |
| 994 } | |
| 995 | |
| 996 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::cancelKeyRequest( | |
| 997 const WebString& key_system, | |
| 998 const WebString& session_id) { | |
| 999 WebMediaPlayer::MediaKeyException e = | |
| 1000 CancelKeyRequestInternal(key_system, session_id); | |
| 1001 ReportMediaKeyExceptionToUMA("cancelKeyRequest", key_system, e); | |
| 1002 return e; | |
| 1003 } | |
| 1004 | |
| 1005 WebMediaPlayer::MediaKeyException | |
| 1006 WebMediaPlayerAndroid::CancelKeyRequestInternal( | |
| 1007 const WebString& key_system, | |
| 1008 const WebString& session_id) { | |
| 1009 if (!IsSupportedKeySystem(key_system)) | |
| 1010 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
| 1011 | |
| 1012 if (current_key_system_.isEmpty() || key_system != current_key_system_) | |
| 1013 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; | |
| 1014 | |
| 1015 decryptor_->CancelKeyRequest(session_id.utf8()); | |
| 1016 return WebMediaPlayer::MediaKeyExceptionNoError; | |
| 1017 } | |
| 1018 | |
| 1019 void WebMediaPlayerAndroid::OnKeyAdded(const std::string& session_id) { | |
| 1020 EmeUMAHistogramCounts(current_key_system_.utf8(), "KeyAdded", 1); | |
| 1021 | |
| 1022 if (media_source_delegate_) | |
| 1023 media_source_delegate_->NotifyDemuxerReady(current_key_system_.utf8()); | |
| 1024 | |
| 1025 client_->keyAdded(current_key_system_, WebString::fromUTF8(session_id)); | |
| 1026 } | |
| 1027 | |
| 1028 void WebMediaPlayerAndroid::OnKeyError(const std::string& session_id, | |
| 1029 media::MediaKeys::KeyError error_code, | |
| 1030 int system_code) { | |
| 1031 EmeUMAHistogramEnumeration(current_key_system_.utf8(), "KeyError", | |
| 1032 error_code, media::MediaKeys::kMaxKeyError); | |
| 1033 | |
| 1034 client_->keyError( | |
| 1035 current_key_system_, | |
| 1036 WebString::fromUTF8(session_id), | |
| 1037 static_cast<WebKit::WebMediaPlayerClient::MediaKeyErrorCode>(error_code), | |
| 1038 system_code); | |
| 1039 } | |
| 1040 | |
| 1041 void WebMediaPlayerAndroid::OnKeyMessage(const std::string& session_id, | |
| 1042 const std::string& message, | |
| 1043 const std::string& destination_url) { | |
| 1044 const GURL destination_url_gurl(destination_url); | |
| 1045 DLOG_IF(WARNING, !destination_url.empty() && !destination_url_gurl.is_valid()) | |
| 1046 << "Invalid URL in destination_url: " << destination_url; | |
| 1047 | |
| 1048 client_->keyMessage(current_key_system_, | |
| 1049 WebString::fromUTF8(session_id), | |
| 1050 reinterpret_cast<const uint8*>(message.data()), | |
| 1051 message.size(), | |
| 1052 destination_url_gurl); | |
| 1053 } | |
| 1054 | |
| 1055 void WebMediaPlayerAndroid::OnNeedKey(const std::string& session_id, | |
| 1056 const std::string& type, | |
| 1057 scoped_ptr<uint8[]> init_data, | |
| 1058 int init_data_size) { | |
| 1059 // Do not fire NeedKey event if encrypted media is not enabled. | |
| 1060 if (!WebKit::WebRuntimeFeatures::isEncryptedMediaEnabled() && | |
| 1061 !WebKit::WebRuntimeFeatures::isLegacyEncryptedMediaEnabled()) { | |
| 1062 return; | |
| 1063 } | |
| 1064 | |
| 1065 UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1); | |
| 1066 | |
| 1067 DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_); | |
| 1068 if (init_data_type_.empty()) | |
| 1069 init_data_type_ = type; | |
| 1070 | |
| 1071 client_->keyNeeded(WebString(), | |
| 1072 WebString::fromUTF8(session_id), | |
| 1073 init_data.get(), | |
| 1074 init_data_size); | |
| 1075 } | |
| 1076 | |
| 1077 #if defined(GOOGLE_TV) | |
| 1078 bool WebMediaPlayerAndroid::InjectMediaStream( | |
| 1079 MediaStreamClient* media_stream_client, | |
| 1080 media::Demuxer* demuxer, | |
| 1081 const base::Closure& destroy_demuxer_cb) { | |
| 1082 DCHECK(!demuxer); | |
| 1083 media_stream_client_ = media_stream_client; | |
| 1084 demuxer_ = demuxer; | |
| 1085 destroy_demuxer_cb_ = destroy_demuxer_cb; | |
| 1086 return true; | |
| 1087 } | |
| 1088 #endif | |
| 1089 | |
| 1090 void WebMediaPlayerAndroid::OnReadFromDemuxer( | |
| 1091 media::DemuxerStream::Type type, bool seek_done) { | |
| 1092 if (media_source_delegate_) | |
| 1093 media_source_delegate_->OnReadFromDemuxer(type, seek_done); | |
| 1094 else | |
| 1095 NOTIMPLEMENTED(); | |
| 1096 } | |
| 1097 | |
| 1098 void WebMediaPlayerAndroid::enterFullscreen() { | |
| 1099 if (manager_->CanEnterFullscreen(frame_)) { | |
| 1100 proxy_->EnterFullscreen(player_id_); | |
| 1101 SetNeedsEstablishPeer(false); | |
| 1102 } | |
| 1103 } | |
| 1104 | |
| 1105 void WebMediaPlayerAndroid::exitFullscreen() { | |
| 1106 proxy_->ExitFullscreen(player_id_); | |
| 1107 } | |
| 1108 | |
| 1109 bool WebMediaPlayerAndroid::canEnterFullscreen() const { | |
| 1110 return manager_->CanEnterFullscreen(frame_); | |
| 1111 } | |
| 1112 | |
| 1113 } // namespace webkit_media | |
| OLD | NEW |