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
GetSingletonJavaContentVideoView(JNIEnv * env,jclass)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
RegisterContentVideoView(JNIEnv * env)41 bool ContentVideoView::RegisterContentVideoView(JNIEnv* env) {
42 return RegisterNativesImpl(env);
43 }
44
GetInstance()45 ContentVideoView* ContentVideoView::GetInstance() {
46 return g_content_video_view;
47 }
48
ContentVideoView(BrowserMediaPlayerManager * manager)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
~ContentVideoView()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
OpenVideo()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
OnMediaPlayerError(int error_type)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
OnVideoSizeChanged(int width,int height)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
OnBufferingUpdate(int percent)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
OnPlaybackComplete()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
OnExitFullscreen()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
RecordFullscreenPlayback(JNIEnv *,jobject,bool is_portrait_video,bool is_orientation_portrait)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
RecordExitFullscreenPlayback(JNIEnv *,jobject,bool is_portrait_video,long playback_duration_in_milliseconds_before_orientation_change,long playback_duration_in_milliseconds_after_orientation_change)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
UpdateMediaMetadata()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
GetVideoWidth(JNIEnv *,jobject obj) const174 int ContentVideoView::GetVideoWidth(JNIEnv*, jobject obj) const {
175 media::MediaPlayerAndroid* player = manager_->GetFullscreenPlayer();
176 return player ? player->GetVideoWidth() : 0;
177 }
178
GetVideoHeight(JNIEnv *,jobject obj) const179 int ContentVideoView::GetVideoHeight(JNIEnv*, jobject obj) const {
180 media::MediaPlayerAndroid* player = manager_->GetFullscreenPlayer();
181 return player ? player->GetVideoHeight() : 0;
182 }
183
GetDurationInMilliSeconds(JNIEnv *,jobject obj) const184 int ContentVideoView::GetDurationInMilliSeconds(JNIEnv*, jobject obj) const {
185 media::MediaPlayerAndroid* player = manager_->GetFullscreenPlayer();
186 return player ? player->GetDuration().InMilliseconds() : -1;
187 }
188
GetCurrentPosition(JNIEnv *,jobject obj) const189 int ContentVideoView::GetCurrentPosition(JNIEnv*, jobject obj) const {
190 media::MediaPlayerAndroid* player = manager_->GetFullscreenPlayer();
191 return player ? player->GetCurrentTime().InMilliseconds() : 0;
192 }
193
IsPlaying(JNIEnv *,jobject obj)194 bool ContentVideoView::IsPlaying(JNIEnv*, jobject obj) {
195 media::MediaPlayerAndroid* player = manager_->GetFullscreenPlayer();
196 return player ? player->IsPlaying() : false;
197 }
198
SeekTo(JNIEnv *,jobject obj,jint msec)199 void ContentVideoView::SeekTo(JNIEnv*, jobject obj, jint msec) {
200 manager_->FullscreenPlayerSeek(msec);
201 }
202
Play(JNIEnv *,jobject obj)203 void ContentVideoView::Play(JNIEnv*, jobject obj) {
204 CreatePowerSaveBlocker();
205 manager_->FullscreenPlayerPlay();
206 }
207
Pause(JNIEnv *,jobject obj)208 void ContentVideoView::Pause(JNIEnv*, jobject obj) {
209 power_save_blocker_.reset();
210 manager_->FullscreenPlayerPause();
211 }
212
ExitFullscreen(JNIEnv *,jobject,jboolean release_media_player)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
SetSurface(JNIEnv * env,jobject obj,jobject surface)220 void ContentVideoView::SetSurface(JNIEnv* env, jobject obj,
221 jobject surface) {
222 manager_->SetVideoSurface(
223 gfx::ScopedJavaSurface::AcquireExternalSurface(surface));
224 }
225
RequestMediaMetadata(JNIEnv * env,jobject obj)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
GetJavaObject(JNIEnv * env)233 ScopedJavaLocalRef<jobject> ContentVideoView::GetJavaObject(JNIEnv* env) {
234 return j_content_video_view_.get(env);
235 }
236
GetNativeView()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
CreateJavaObject()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
CreatePowerSaveBlocker()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