1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/media/android/browser_media_player_manager.h" 6 7 #include "base/android/scoped_java_ref.h" 8 #include "base/command_line.h" 9 #include "content/browser/android/content_view_core_impl.h" 10 #include "content/browser/media/android/browser_demuxer_android.h" 11 #include "content/browser/media/android/media_resource_getter_impl.h" 12 #include "content/browser/renderer_host/render_view_host_impl.h" 13 #include "content/browser/web_contents/web_contents_view_android.h" 14 #include "content/common/media/media_player_messages_android.h" 15 #include "content/public/browser/android/content_view_core.h" 16 #include "content/public/browser/android/external_video_surface_container.h" 17 #include "content/public/browser/browser_context.h" 18 #include "content/public/browser/content_browser_client.h" 19 #include "content/public/browser/render_frame_host.h" 20 #include "content/public/browser/render_process_host.h" 21 #include "content/public/browser/render_view_host.h" 22 #include "content/public/browser/storage_partition.h" 23 #include "content/public/browser/web_contents.h" 24 #include "content/public/browser/web_contents_delegate.h" 25 #include "content/public/common/content_client.h" 26 #include "content/public/common/content_switches.h" 27 #include "media/base/android/media_player_bridge.h" 28 #include "media/base/android/media_source_player.h" 29 #include "media/base/android/media_url_interceptor.h" 30 #include "media/base/media_switches.h" 31 32 using media::MediaPlayerAndroid; 33 using media::MediaPlayerBridge; 34 using media::MediaPlayerManager; 35 using media::MediaSourcePlayer; 36 37 namespace content { 38 39 // Threshold on the number of media players per renderer before we start 40 // attempting to release inactive media players. 41 const int kMediaPlayerThreshold = 1; 42 43 static BrowserMediaPlayerManager::Factory g_factory = NULL; 44 static media::MediaUrlInterceptor* media_url_interceptor_ = NULL; 45 46 // static 47 void BrowserMediaPlayerManager::RegisterFactory(Factory factory) { 48 g_factory = factory; 49 } 50 51 // static 52 void BrowserMediaPlayerManager::RegisterMediaUrlInterceptor( 53 media::MediaUrlInterceptor* media_url_interceptor) { 54 media_url_interceptor_ = media_url_interceptor; 55 } 56 57 // static 58 BrowserMediaPlayerManager* BrowserMediaPlayerManager::Create( 59 RenderFrameHost* rfh) { 60 if (g_factory) 61 return g_factory(rfh); 62 return new BrowserMediaPlayerManager(rfh); 63 } 64 65 ContentViewCoreImpl* BrowserMediaPlayerManager::GetContentViewCore() const { 66 return ContentViewCoreImpl::FromWebContents(web_contents()); 67 } 68 69 MediaPlayerAndroid* BrowserMediaPlayerManager::CreateMediaPlayer( 70 const MediaPlayerHostMsg_Initialize_Params& media_player_params, 71 bool hide_url_log, 72 MediaPlayerManager* manager, 73 BrowserDemuxerAndroid* demuxer) { 74 switch (media_player_params.type) { 75 case MEDIA_PLAYER_TYPE_URL: { 76 const std::string user_agent = GetContentClient()->GetUserAgent(); 77 MediaPlayerBridge* media_player_bridge = new MediaPlayerBridge( 78 media_player_params.player_id, 79 media_player_params.url, 80 media_player_params.first_party_for_cookies, 81 user_agent, 82 hide_url_log, 83 manager, 84 base::Bind(&BrowserMediaPlayerManager::OnMediaResourcesRequested, 85 weak_ptr_factory_.GetWeakPtr()), 86 base::Bind(&BrowserMediaPlayerManager::OnMediaResourcesReleased, 87 weak_ptr_factory_.GetWeakPtr()), 88 media_player_params.frame_url, 89 media_player_params.allow_credentials); 90 BrowserMediaPlayerManager* browser_media_player_manager = 91 static_cast<BrowserMediaPlayerManager*>(manager); 92 ContentViewCoreImpl* content_view_core_impl = 93 static_cast<ContentViewCoreImpl*>(ContentViewCore::FromWebContents( 94 browser_media_player_manager->web_contents_)); 95 if (!content_view_core_impl) { 96 // May reach here due to prerendering. Don't extract the metadata 97 // since it is expensive. 98 // TODO(qinmin): extract the metadata once the user decided to load 99 // the page. 100 browser_media_player_manager->OnMediaMetadataChanged( 101 media_player_params.player_id, base::TimeDelta(), 0, 0, false); 102 } else if (!content_view_core_impl->ShouldBlockMediaRequest( 103 media_player_params.url)) { 104 media_player_bridge->Initialize(); 105 } 106 return media_player_bridge; 107 } 108 109 case MEDIA_PLAYER_TYPE_MEDIA_SOURCE: { 110 return new MediaSourcePlayer( 111 media_player_params.player_id, 112 manager, 113 base::Bind(&BrowserMediaPlayerManager::OnMediaResourcesRequested, 114 weak_ptr_factory_.GetWeakPtr()), 115 base::Bind(&BrowserMediaPlayerManager::OnMediaResourcesReleased, 116 weak_ptr_factory_.GetWeakPtr()), 117 demuxer->CreateDemuxer(media_player_params.demuxer_client_id), 118 media_player_params.frame_url); 119 } 120 } 121 122 NOTREACHED(); 123 return NULL; 124 } 125 126 BrowserMediaPlayerManager::BrowserMediaPlayerManager( 127 RenderFrameHost* render_frame_host) 128 : render_frame_host_(render_frame_host), 129 fullscreen_player_id_(-1), 130 fullscreen_player_is_released_(false), 131 web_contents_(WebContents::FromRenderFrameHost(render_frame_host)), 132 weak_ptr_factory_(this) { 133 } 134 135 BrowserMediaPlayerManager::~BrowserMediaPlayerManager() { 136 // During the tear down process, OnDestroyPlayer() may or may not be called 137 // (e.g. the WebContents may be destroyed before the render process). So 138 // we cannot DCHECK(players_.empty()) here. Instead, all media players in 139 // |players_| will be destroyed here because |player_| is a ScopedVector. 140 } 141 142 void BrowserMediaPlayerManager::FullscreenPlayerPlay() { 143 MediaPlayerAndroid* player = GetFullscreenPlayer(); 144 if (player) { 145 if (fullscreen_player_is_released_) { 146 video_view_->OpenVideo(); 147 fullscreen_player_is_released_ = false; 148 } 149 player->Start(); 150 Send(new MediaPlayerMsg_DidMediaPlayerPlay(RoutingID(), 151 fullscreen_player_id_)); 152 } 153 } 154 155 void BrowserMediaPlayerManager::FullscreenPlayerPause() { 156 MediaPlayerAndroid* player = GetFullscreenPlayer(); 157 if (player) { 158 player->Pause(true); 159 Send(new MediaPlayerMsg_DidMediaPlayerPause(RoutingID(), 160 fullscreen_player_id_)); 161 } 162 } 163 164 void BrowserMediaPlayerManager::FullscreenPlayerSeek(int msec) { 165 MediaPlayerAndroid* player = GetFullscreenPlayer(); 166 if (player) { 167 // TODO(kbalazs): if |fullscreen_player_is_released_| is true 168 // at this point, player->GetCurrentTime() will be wrong until 169 // FullscreenPlayerPlay (http://crbug.com/322798). 170 OnSeekRequest(fullscreen_player_id_, 171 base::TimeDelta::FromMilliseconds(msec)); 172 } 173 } 174 175 void BrowserMediaPlayerManager::ExitFullscreen(bool release_media_player) { 176 if (WebContentsDelegate* delegate = web_contents_->GetDelegate()) 177 delegate->ToggleFullscreenModeForTab(web_contents_, false); 178 if (!CommandLine::ForCurrentProcess()->HasSwitch( 179 switches::kDisableOverlayFullscreenVideoSubtitle)) { 180 if (RenderWidgetHostViewAndroid* view_android = 181 static_cast<RenderWidgetHostViewAndroid*>( 182 web_contents_->GetRenderWidgetHostView())) { 183 view_android->SetOverlayVideoMode(false); 184 } 185 } 186 187 Send( 188 new MediaPlayerMsg_DidExitFullscreen(RoutingID(), fullscreen_player_id_)); 189 video_view_.reset(); 190 MediaPlayerAndroid* player = GetFullscreenPlayer(); 191 fullscreen_player_id_ = -1; 192 if (!player) 193 return; 194 if (release_media_player) 195 ReleaseFullscreenPlayer(player); 196 else 197 player->SetVideoSurface(gfx::ScopedJavaSurface()); 198 } 199 200 void BrowserMediaPlayerManager::OnTimeUpdate(int player_id, 201 base::TimeDelta current_time) { 202 Send( 203 new MediaPlayerMsg_MediaTimeUpdate(RoutingID(), player_id, current_time)); 204 } 205 206 void BrowserMediaPlayerManager::SetVideoSurface( 207 gfx::ScopedJavaSurface surface) { 208 MediaPlayerAndroid* player = GetFullscreenPlayer(); 209 if (!player) 210 return; 211 212 bool empty_surface = surface.IsEmpty(); 213 player->SetVideoSurface(surface.Pass()); 214 if (empty_surface) 215 return; 216 217 Send(new MediaPlayerMsg_DidEnterFullscreen(RoutingID(), player->player_id())); 218 if (CommandLine::ForCurrentProcess()->HasSwitch( 219 switches::kDisableOverlayFullscreenVideoSubtitle)) { 220 return; 221 } 222 if (RenderWidgetHostViewAndroid* view_android = 223 static_cast<RenderWidgetHostViewAndroid*>( 224 web_contents_->GetRenderWidgetHostView())) { 225 view_android->SetOverlayVideoMode(true); 226 } 227 } 228 229 void BrowserMediaPlayerManager::OnMediaMetadataChanged( 230 int player_id, base::TimeDelta duration, int width, int height, 231 bool success) { 232 Send(new MediaPlayerMsg_MediaMetadataChanged( 233 RoutingID(), player_id, duration, width, height, success)); 234 if (fullscreen_player_id_ == player_id) 235 video_view_->UpdateMediaMetadata(); 236 } 237 238 void BrowserMediaPlayerManager::OnPlaybackComplete(int player_id) { 239 Send(new MediaPlayerMsg_MediaPlaybackCompleted(RoutingID(), player_id)); 240 if (fullscreen_player_id_ == player_id) 241 video_view_->OnPlaybackComplete(); 242 } 243 244 void BrowserMediaPlayerManager::OnMediaInterrupted(int player_id) { 245 // Tell WebKit that the audio should be paused, then release all resources 246 Send(new MediaPlayerMsg_MediaPlayerReleased(RoutingID(), player_id)); 247 OnReleaseResources(player_id); 248 } 249 250 void BrowserMediaPlayerManager::OnBufferingUpdate( 251 int player_id, int percentage) { 252 Send(new MediaPlayerMsg_MediaBufferingUpdate( 253 RoutingID(), player_id, percentage)); 254 if (fullscreen_player_id_ == player_id) 255 video_view_->OnBufferingUpdate(percentage); 256 } 257 258 void BrowserMediaPlayerManager::OnSeekRequest( 259 int player_id, 260 const base::TimeDelta& time_to_seek) { 261 Send(new MediaPlayerMsg_SeekRequest(RoutingID(), player_id, time_to_seek)); 262 } 263 264 void BrowserMediaPlayerManager::PauseVideo() { 265 Send(new MediaPlayerMsg_PauseVideo(RoutingID())); 266 } 267 268 void BrowserMediaPlayerManager::OnSeekComplete( 269 int player_id, 270 const base::TimeDelta& current_time) { 271 Send(new MediaPlayerMsg_SeekCompleted(RoutingID(), player_id, current_time)); 272 } 273 274 void BrowserMediaPlayerManager::OnError(int player_id, int error) { 275 Send(new MediaPlayerMsg_MediaError(RoutingID(), player_id, error)); 276 if (fullscreen_player_id_ == player_id) 277 video_view_->OnMediaPlayerError(error); 278 } 279 280 void BrowserMediaPlayerManager::OnVideoSizeChanged( 281 int player_id, int width, int height) { 282 Send(new MediaPlayerMsg_MediaVideoSizeChanged(RoutingID(), player_id, 283 width, height)); 284 if (fullscreen_player_id_ == player_id) 285 video_view_->OnVideoSizeChanged(width, height); 286 } 287 288 media::MediaResourceGetter* 289 BrowserMediaPlayerManager::GetMediaResourceGetter() { 290 if (!media_resource_getter_.get()) { 291 RenderProcessHost* host = web_contents()->GetRenderProcessHost(); 292 BrowserContext* context = host->GetBrowserContext(); 293 StoragePartition* partition = host->GetStoragePartition(); 294 fileapi::FileSystemContext* file_system_context = 295 partition ? partition->GetFileSystemContext() : NULL; 296 // Eventually this needs to be fixed to pass the correct frame rather 297 // than just using the main frame. 298 media_resource_getter_.reset(new MediaResourceGetterImpl( 299 context, 300 file_system_context, 301 host->GetID(), 302 web_contents()->GetMainFrame()->GetRoutingID())); 303 } 304 return media_resource_getter_.get(); 305 } 306 307 media::MediaUrlInterceptor* 308 BrowserMediaPlayerManager::GetMediaUrlInterceptor() { 309 return media_url_interceptor_; 310 } 311 312 MediaPlayerAndroid* BrowserMediaPlayerManager::GetFullscreenPlayer() { 313 return GetPlayer(fullscreen_player_id_); 314 } 315 316 MediaPlayerAndroid* BrowserMediaPlayerManager::GetPlayer(int player_id) { 317 for (ScopedVector<MediaPlayerAndroid>::iterator it = players_.begin(); 318 it != players_.end(); ++it) { 319 if ((*it)->player_id() == player_id) 320 return *it; 321 } 322 return NULL; 323 } 324 325 void BrowserMediaPlayerManager::RequestFullScreen(int player_id) { 326 if (fullscreen_player_id_ == player_id) 327 return; 328 329 if (fullscreen_player_id_ != -1) { 330 // TODO(qinmin): Determine the correct error code we should report to WMPA. 331 OnError(player_id, MediaPlayerAndroid::MEDIA_ERROR_DECODE); 332 return; 333 } 334 335 Send(new MediaPlayerMsg_RequestFullscreen(RoutingID(), player_id)); 336 } 337 338 #if defined(VIDEO_HOLE) 339 bool 340 BrowserMediaPlayerManager::ShouldUseVideoOverlayForEmbeddedEncryptedVideo() { 341 RendererPreferences* prefs = web_contents_->GetMutableRendererPrefs(); 342 return prefs->use_video_overlay_for_embedded_encrypted_video; 343 } 344 345 void BrowserMediaPlayerManager::AttachExternalVideoSurface(int player_id, 346 jobject surface) { 347 MediaPlayerAndroid* player = GetPlayer(player_id); 348 if (player) { 349 player->SetVideoSurface( 350 gfx::ScopedJavaSurface::AcquireExternalSurface(surface)); 351 } 352 } 353 354 void BrowserMediaPlayerManager::DetachExternalVideoSurface(int player_id) { 355 MediaPlayerAndroid* player = GetPlayer(player_id); 356 if (player) 357 player->SetVideoSurface(gfx::ScopedJavaSurface()); 358 } 359 360 void BrowserMediaPlayerManager::OnFrameInfoUpdated() { 361 if (external_video_surface_container_) 362 external_video_surface_container_->OnFrameInfoUpdated(); 363 } 364 365 void BrowserMediaPlayerManager::OnNotifyExternalSurface( 366 int player_id, bool is_request, const gfx::RectF& rect) { 367 if (!web_contents_) 368 return; 369 370 if (is_request) { 371 OnRequestExternalSurface(player_id, rect); 372 } 373 if (external_video_surface_container_) { 374 external_video_surface_container_->OnExternalVideoSurfacePositionChanged( 375 player_id, rect); 376 } 377 } 378 379 void BrowserMediaPlayerManager::OnRequestExternalSurface( 380 int player_id, const gfx::RectF& rect) { 381 if (!external_video_surface_container_) { 382 ContentBrowserClient* client = GetContentClient()->browser(); 383 external_video_surface_container_.reset( 384 client->OverrideCreateExternalVideoSurfaceContainer(web_contents_)); 385 } 386 // It's safe to use base::Unretained(this), because the callbacks will not 387 // be called after running ReleaseExternalVideoSurface(). 388 if (external_video_surface_container_) { 389 external_video_surface_container_->RequestExternalVideoSurface( 390 player_id, 391 base::Bind(&BrowserMediaPlayerManager::AttachExternalVideoSurface, 392 base::Unretained(this)), 393 base::Bind(&BrowserMediaPlayerManager::DetachExternalVideoSurface, 394 base::Unretained(this))); 395 } 396 } 397 #endif // defined(VIDEO_HOLE) 398 399 void BrowserMediaPlayerManager::OnEnterFullscreen(int player_id) { 400 DCHECK_EQ(fullscreen_player_id_, -1); 401 #if defined(VIDEO_HOLE) 402 if (external_video_surface_container_) 403 external_video_surface_container_->ReleaseExternalVideoSurface(player_id); 404 #endif // defined(VIDEO_HOLE) 405 if (video_view_.get()) { 406 fullscreen_player_id_ = player_id; 407 video_view_->OpenVideo(); 408 return; 409 } else if (!ContentVideoView::GetInstance()) { 410 if (!GetPlayer(player_id)) { 411 // If a player doesn't exist, it must be waiting for CORS check. 412 // As a result, just request the tab to enter fullscreen mode without 413 // creating the surface view. This is only needed for M37. 414 Send(new MediaPlayerMsg_DidEnterFullscreen(RoutingID(), player_id)); 415 if (CommandLine::ForCurrentProcess()->HasSwitch( 416 switches::kDisableOverlayFullscreenVideoSubtitle)) { 417 return; 418 } 419 if (RenderWidgetHostViewAndroid* view_android = 420 static_cast<RenderWidgetHostViewAndroid*>( 421 web_contents_->GetRenderWidgetHostView())) { 422 view_android->SetOverlayVideoMode(true); 423 } 424 if (WebContentsDelegate* delegate = web_contents_->GetDelegate()) 425 delegate->ToggleFullscreenModeForTab(web_contents_, true); 426 } 427 428 // In Android WebView, two ContentViewCores could both try to enter 429 // fullscreen video, we just ignore the second one. 430 video_view_.reset(new ContentVideoView(this)); 431 base::android::ScopedJavaLocalRef<jobject> j_content_video_view = 432 video_view_->GetJavaObject(base::android::AttachCurrentThread()); 433 if (!j_content_video_view.is_null()) { 434 fullscreen_player_id_ = player_id; 435 return; 436 } 437 } 438 439 // Force the second video to exit fullscreen. 440 // TODO(qinmin): There is no need to send DidEnterFullscreen message. 441 // However, if we don't send the message, page layers will not be 442 // correctly restored. http:crbug.com/367346. 443 Send(new MediaPlayerMsg_DidEnterFullscreen(RoutingID(), player_id)); 444 Send(new MediaPlayerMsg_DidExitFullscreen(RoutingID(), player_id)); 445 video_view_.reset(); 446 } 447 448 void BrowserMediaPlayerManager::OnExitFullscreen(int player_id) { 449 if (fullscreen_player_id_ == player_id) { 450 MediaPlayerAndroid* player = GetPlayer(player_id); 451 if (player) 452 player->SetVideoSurface(gfx::ScopedJavaSurface()); 453 video_view_->OnExitFullscreen(); 454 } 455 } 456 457 void BrowserMediaPlayerManager::OnInitialize( 458 const MediaPlayerHostMsg_Initialize_Params& media_player_params) { 459 DCHECK(media_player_params.type != MEDIA_PLAYER_TYPE_MEDIA_SOURCE || 460 media_player_params.demuxer_client_id > 0) 461 << "Media source players must have positive demuxer client IDs: " 462 << media_player_params.demuxer_client_id; 463 464 RemovePlayer(media_player_params.player_id); 465 466 RenderProcessHostImpl* host = static_cast<RenderProcessHostImpl*>( 467 web_contents()->GetRenderProcessHost()); 468 MediaPlayerAndroid* player = CreateMediaPlayer( 469 media_player_params, 470 471 host->GetBrowserContext()->IsOffTheRecord(), this, 472 host->browser_demuxer_android()); 473 474 if (!player) 475 return; 476 477 AddPlayer(player); 478 } 479 480 void BrowserMediaPlayerManager::OnStart(int player_id) { 481 MediaPlayerAndroid* player = GetPlayer(player_id); 482 if (!player) 483 return; 484 player->Start(); 485 if (fullscreen_player_id_ == player_id && fullscreen_player_is_released_) { 486 video_view_->OpenVideo(); 487 fullscreen_player_is_released_ = false; 488 } 489 } 490 491 void BrowserMediaPlayerManager::OnSeek( 492 int player_id, 493 const base::TimeDelta& time) { 494 MediaPlayerAndroid* player = GetPlayer(player_id); 495 if (player) 496 player->SeekTo(time); 497 } 498 499 void BrowserMediaPlayerManager::OnPause( 500 int player_id, 501 bool is_media_related_action) { 502 MediaPlayerAndroid* player = GetPlayer(player_id); 503 if (player) 504 player->Pause(is_media_related_action); 505 } 506 507 void BrowserMediaPlayerManager::OnSetVolume(int player_id, double volume) { 508 MediaPlayerAndroid* player = GetPlayer(player_id); 509 if (player) 510 player->SetVolume(volume); 511 } 512 513 void BrowserMediaPlayerManager::OnSetPoster(int player_id, const GURL& url) { 514 // To be overridden by subclasses. 515 } 516 517 void BrowserMediaPlayerManager::OnReleaseResources(int player_id) { 518 MediaPlayerAndroid* player = GetPlayer(player_id); 519 if (player) 520 player->Release(); 521 if (player_id == fullscreen_player_id_) 522 fullscreen_player_is_released_ = true; 523 } 524 525 void BrowserMediaPlayerManager::OnDestroyPlayer(int player_id) { 526 RemovePlayer(player_id); 527 if (fullscreen_player_id_ == player_id) 528 fullscreen_player_id_ = -1; 529 } 530 531 void BrowserMediaPlayerManager::AddPlayer(MediaPlayerAndroid* player) { 532 DCHECK(!GetPlayer(player->player_id())); 533 players_.push_back(player); 534 } 535 536 void BrowserMediaPlayerManager::RemovePlayer(int player_id) { 537 for (ScopedVector<MediaPlayerAndroid>::iterator it = players_.begin(); 538 it != players_.end(); ++it) { 539 MediaPlayerAndroid* player = *it; 540 if (player->player_id() == player_id) { 541 players_.erase(it); 542 break; 543 } 544 } 545 } 546 547 scoped_ptr<media::MediaPlayerAndroid> BrowserMediaPlayerManager::SwapPlayer( 548 int player_id, media::MediaPlayerAndroid* player) { 549 media::MediaPlayerAndroid* previous_player = NULL; 550 for (ScopedVector<MediaPlayerAndroid>::iterator it = players_.begin(); 551 it != players_.end(); ++it) { 552 if ((*it)->player_id() == player_id) { 553 previous_player = *it; 554 players_.weak_erase(it); 555 players_.push_back(player); 556 break; 557 } 558 } 559 return scoped_ptr<media::MediaPlayerAndroid>(previous_player); 560 } 561 562 int BrowserMediaPlayerManager::RoutingID() { 563 return render_frame_host_->GetRoutingID(); 564 } 565 566 bool BrowserMediaPlayerManager::Send(IPC::Message* msg) { 567 return render_frame_host_->Send(msg); 568 } 569 570 void BrowserMediaPlayerManager::ReleaseFullscreenPlayer( 571 MediaPlayerAndroid* player) { 572 player->Release(); 573 } 574 575 void BrowserMediaPlayerManager::OnMediaResourcesRequested(int player_id) { 576 int num_active_player = 0; 577 ScopedVector<MediaPlayerAndroid>::iterator it; 578 for (it = players_.begin(); it != players_.end(); ++it) { 579 if (!(*it)->IsPlayerReady()) 580 continue; 581 582 // The player is already active, ignore it. 583 if ((*it)->player_id() == player_id) 584 return; 585 else 586 num_active_player++; 587 } 588 589 // Number of active players are less than the threshold, do nothing. 590 if (num_active_player < kMediaPlayerThreshold) 591 return; 592 593 for (it = players_.begin(); it != players_.end(); ++it) { 594 if ((*it)->IsPlayerReady() && !(*it)->IsPlaying() && 595 fullscreen_player_id_ != (*it)->player_id()) { 596 (*it)->Release(); 597 Send(new MediaPlayerMsg_MediaPlayerReleased(RoutingID(), 598 (*it)->player_id())); 599 } 600 } 601 } 602 603 void BrowserMediaPlayerManager::OnMediaResourcesReleased(int player_id) { 604 #if defined(VIDEO_HOLE) 605 MediaPlayerAndroid* player = GetPlayer(player_id); 606 if (player && player->IsSurfaceInUse()) 607 return; 608 if (external_video_surface_container_) 609 external_video_surface_container_->ReleaseExternalVideoSurface(player_id); 610 #endif // defined(VIDEO_HOLE) 611 } 612 613 } // namespace content 614