1 // Copyright (c) 2012 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/android/content_video_view.h" 6 7 #include "base/command_line.h" 8 #include "base/logging.h" 9 #include "base/message_loop/message_loop.h" 10 #include "base/metrics/histogram.h" 11 #include "content/browser/android/content_view_core_impl.h" 12 #include "content/browser/media/android/browser_media_player_manager.h" 13 #include "content/browser/power_save_blocker_impl.h" 14 #include "content/common/android/surface_texture_peer.h" 15 #include "content/public/browser/user_metrics.h" 16 #include "content/public/common/content_switches.h" 17 #include "jni/ContentVideoView_jni.h" 18 19 using base::android::AttachCurrentThread; 20 using base::android::CheckException; 21 using base::android::ScopedJavaGlobalRef; 22 using base::UserMetricsAction; 23 using content::RecordAction; 24 25 namespace content { 26 27 namespace { 28 // There can only be one content video view at a time, this holds onto that 29 // singleton instance. 30 ContentVideoView* g_content_video_view = NULL; 31 32 } // namespace 33 34 static jobject GetSingletonJavaContentVideoView(JNIEnv*env, jclass) { 35 if (g_content_video_view) 36 return g_content_video_view->GetJavaObject(env).Release(); 37 else 38 return NULL; 39 } 40 41 bool ContentVideoView::RegisterContentVideoView(JNIEnv* env) { 42 return RegisterNativesImpl(env); 43 } 44 45 ContentVideoView* ContentVideoView::GetInstance() { 46 return g_content_video_view; 47 } 48 49 ContentVideoView::ContentVideoView( 50 BrowserMediaPlayerManager* manager) 51 : manager_(manager), 52 weak_factory_(this) { 53 DCHECK(!g_content_video_view); 54 j_content_video_view_ = CreateJavaObject(); 55 g_content_video_view = this; 56 CreatePowerSaveBlocker(); 57 } 58 59 ContentVideoView::~ContentVideoView() { 60 DCHECK(g_content_video_view); 61 JNIEnv* env = AttachCurrentThread(); 62 ScopedJavaLocalRef<jobject> content_video_view = GetJavaObject(env); 63 if (!content_video_view.is_null()) { 64 Java_ContentVideoView_destroyContentVideoView(env, 65 content_video_view.obj(), true); 66 j_content_video_view_.reset(); 67 } 68 g_content_video_view = NULL; 69 } 70 71 void ContentVideoView::OpenVideo() { 72 JNIEnv* env = AttachCurrentThread(); 73 ScopedJavaLocalRef<jobject> content_video_view = GetJavaObject(env); 74 if (!content_video_view.is_null()) { 75 CreatePowerSaveBlocker(); 76 Java_ContentVideoView_openVideo(env, content_video_view.obj()); 77 } 78 } 79 80 void ContentVideoView::OnMediaPlayerError(int error_type) { 81 JNIEnv* env = AttachCurrentThread(); 82 ScopedJavaLocalRef<jobject> content_video_view = GetJavaObject(env); 83 if (!content_video_view.is_null()) { 84 power_save_blocker_.reset(); 85 Java_ContentVideoView_onMediaPlayerError(env, content_video_view.obj(), 86 error_type); 87 } 88 } 89 90 void ContentVideoView::OnVideoSizeChanged(int width, int height) { 91 JNIEnv* env = AttachCurrentThread(); 92 ScopedJavaLocalRef<jobject> content_video_view = GetJavaObject(env); 93 if (!content_video_view.is_null()) { 94 Java_ContentVideoView_onVideoSizeChanged(env, content_video_view.obj(), 95 width, height); 96 } 97 } 98 99 void ContentVideoView::OnBufferingUpdate(int percent) { 100 JNIEnv* env = AttachCurrentThread(); 101 ScopedJavaLocalRef<jobject> content_video_view = GetJavaObject(env); 102 if (!content_video_view.is_null()) { 103 Java_ContentVideoView_onBufferingUpdate(env, content_video_view.obj(), 104 percent); 105 } 106 } 107 108 void ContentVideoView::OnPlaybackComplete() { 109 JNIEnv* env = AttachCurrentThread(); 110 ScopedJavaLocalRef<jobject> content_video_view = GetJavaObject(env); 111 if (!content_video_view.is_null()) { 112 power_save_blocker_.reset(); 113 Java_ContentVideoView_onPlaybackComplete(env, content_video_view.obj()); 114 } 115 } 116 117 void ContentVideoView::OnExitFullscreen() { 118 JNIEnv* env = AttachCurrentThread(); 119 ScopedJavaLocalRef<jobject> content_video_view = GetJavaObject(env); 120 if (!content_video_view.is_null()) 121 Java_ContentVideoView_onExitFullscreen(env, content_video_view.obj()); 122 } 123 124 void ContentVideoView::RecordFullscreenPlayback( 125 JNIEnv*, jobject, bool is_portrait_video, bool is_orientation_portrait) { 126 UMA_HISTOGRAM_BOOLEAN("MobileFullscreenVideo.OrientationPortrait", 127 is_orientation_portrait); 128 UMA_HISTOGRAM_BOOLEAN("MobileFullscreenVideo.VideoPortrait", 129 is_portrait_video); 130 } 131 132 void ContentVideoView::RecordExitFullscreenPlayback( 133 JNIEnv*, jobject, bool is_portrait_video, 134 long playback_duration_in_milliseconds_before_orientation_change, 135 long playback_duration_in_milliseconds_after_orientation_change) { 136 bool orientation_changed = ( 137 playback_duration_in_milliseconds_after_orientation_change != 0); 138 if (is_portrait_video) { 139 UMA_HISTOGRAM_COUNTS( 140 "MobileFullscreenVideo.PortraitDuration", 141 playback_duration_in_milliseconds_before_orientation_change); 142 UMA_HISTOGRAM_COUNTS( 143 "MobileFullscreenVideo.PortraitRotation", orientation_changed); 144 if (orientation_changed) { 145 UMA_HISTOGRAM_COUNTS( 146 "MobileFullscreenVideo.DurationAfterPotraitRotation", 147 playback_duration_in_milliseconds_after_orientation_change); 148 } 149 } else { 150 UMA_HISTOGRAM_COUNTS( 151 "MobileFullscreenVideo.LandscapeDuration", 152 playback_duration_in_milliseconds_before_orientation_change); 153 UMA_HISTOGRAM_COUNTS( 154 "MobileFullscreenVideo.LandscapeRotation", orientation_changed); 155 } 156 } 157 158 void ContentVideoView::UpdateMediaMetadata() { 159 JNIEnv* env = AttachCurrentThread(); 160 ScopedJavaLocalRef<jobject> content_video_view = GetJavaObject(env); 161 if (content_video_view.is_null()) 162 return; 163 164 media::MediaPlayerAndroid* player = manager_->GetFullscreenPlayer(); 165 if (player && player->IsPlayerReady()) { 166 Java_ContentVideoView_onUpdateMediaMetadata( 167 env, content_video_view.obj(), player->GetVideoWidth(), 168 player->GetVideoHeight(), 169 static_cast<int>(player->GetDuration().InMilliseconds()), 170 player->CanPause(),player->CanSeekForward(), player->CanSeekBackward()); 171 } 172 } 173 174 int ContentVideoView::GetVideoWidth(JNIEnv*, jobject obj) const { 175 media::MediaPlayerAndroid* player = manager_->GetFullscreenPlayer(); 176 return player ? player->GetVideoWidth() : 0; 177 } 178 179 int ContentVideoView::GetVideoHeight(JNIEnv*, jobject obj) const { 180 media::MediaPlayerAndroid* player = manager_->GetFullscreenPlayer(); 181 return player ? player->GetVideoHeight() : 0; 182 } 183 184 int ContentVideoView::GetDurationInMilliSeconds(JNIEnv*, jobject obj) const { 185 media::MediaPlayerAndroid* player = manager_->GetFullscreenPlayer(); 186 return player ? player->GetDuration().InMilliseconds() : -1; 187 } 188 189 int ContentVideoView::GetCurrentPosition(JNIEnv*, jobject obj) const { 190 media::MediaPlayerAndroid* player = manager_->GetFullscreenPlayer(); 191 return player ? player->GetCurrentTime().InMilliseconds() : 0; 192 } 193 194 bool ContentVideoView::IsPlaying(JNIEnv*, jobject obj) { 195 media::MediaPlayerAndroid* player = manager_->GetFullscreenPlayer(); 196 return player ? player->IsPlaying() : false; 197 } 198 199 void ContentVideoView::SeekTo(JNIEnv*, jobject obj, jint msec) { 200 manager_->FullscreenPlayerSeek(msec); 201 } 202 203 void ContentVideoView::Play(JNIEnv*, jobject obj) { 204 CreatePowerSaveBlocker(); 205 manager_->FullscreenPlayerPlay(); 206 } 207 208 void ContentVideoView::Pause(JNIEnv*, jobject obj) { 209 power_save_blocker_.reset(); 210 manager_->FullscreenPlayerPause(); 211 } 212 213 void ContentVideoView::ExitFullscreen( 214 JNIEnv*, jobject, jboolean release_media_player) { 215 power_save_blocker_.reset(); 216 j_content_video_view_.reset(); 217 manager_->ExitFullscreen(release_media_player); 218 } 219 220 void ContentVideoView::SetSurface(JNIEnv* env, jobject obj, 221 jobject surface) { 222 manager_->SetVideoSurface( 223 gfx::ScopedJavaSurface::AcquireExternalSurface(surface)); 224 } 225 226 void ContentVideoView::RequestMediaMetadata(JNIEnv* env, jobject obj) { 227 base::MessageLoop::current()->PostTask( 228 FROM_HERE, 229 base::Bind(&ContentVideoView::UpdateMediaMetadata, 230 weak_factory_.GetWeakPtr())); 231 } 232 233 ScopedJavaLocalRef<jobject> ContentVideoView::GetJavaObject(JNIEnv* env) { 234 return j_content_video_view_.get(env); 235 } 236 237 gfx::NativeView ContentVideoView::GetNativeView() { 238 JNIEnv* env = AttachCurrentThread(); 239 ScopedJavaLocalRef<jobject> content_video_view = GetJavaObject(env); 240 if (content_video_view.is_null()) 241 return NULL; 242 243 return reinterpret_cast<gfx::NativeView>( 244 Java_ContentVideoView_getNativeViewAndroid(env, 245 content_video_view.obj())); 246 247 } 248 249 JavaObjectWeakGlobalRef ContentVideoView::CreateJavaObject() { 250 ContentViewCoreImpl* content_view_core = manager_->GetContentViewCore(); 251 JNIEnv* env = AttachCurrentThread(); 252 return JavaObjectWeakGlobalRef( 253 env, 254 Java_ContentVideoView_createContentVideoView( 255 env, 256 content_view_core->GetContext().obj(), 257 reinterpret_cast<intptr_t>(this), 258 content_view_core->GetContentVideoViewClient().obj()).obj()); 259 } 260 261 void ContentVideoView::CreatePowerSaveBlocker() { 262 if (base::CommandLine::ForCurrentProcess()->HasSwitch( 263 switches::kEnableContentVideoViewPowerSaveBlocker)) { 264 // In fullscreen Clank reuses the power save blocker attached to the 265 // container view that was created for embedded video. The WebView cannot 266 // reuse that so we create a new blocker instead. 267 if (power_save_blocker_) return; 268 power_save_blocker_ = PowerSaveBlocker::Create( 269 PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep, 270 "Playing video").Pass(); 271 static_cast<PowerSaveBlockerImpl*>(power_save_blocker_.get())-> 272 InitDisplaySleepBlocker(GetNativeView()); 273 } 274 } 275 276 } // namespace content 277