summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcramya@chromium.org <cramya@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-18 19:48:37 +0000
committercramya@chromium.org <cramya@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-18 19:48:37 +0000
commit722e0dff34891a578169a98b0d985432b66b3fbf (patch)
tree4be9c8e8ae3135d8edd00a1ab3bae54957a507e7
parent010469ee9d819a3ad20ef072fcab090fbe182244 (diff)
downloadchromium_src-722e0dff34891a578169a98b0d985432b66b3fbf.zip
chromium_src-722e0dff34891a578169a98b0d985432b66b3fbf.tar.gz
chromium_src-722e0dff34891a578169a98b0d985432b66b3fbf.tar.bz2
Upstream ContentVideoView.java content_video_view.* media_metadata_android.*
BUG= TEST= Review URL: https://chromiumcodereview.appspot.com/10680008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@147296 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--content/browser/android/browser_jni_registrar.cc2
-rw-r--r--content/browser/android/content_video_view.cc291
-rw-r--r--content/browser/android/content_video_view.h88
-rw-r--r--content/common/view_messages.h17
-rw-r--r--content/content_browser.gypi2
-rw-r--r--content/content_jni.gypi2
-rw-r--r--content/public/android/java/src/org/chromium/content/app/AppResource.java12
-rw-r--r--content/public/android/java/src/org/chromium/content/browser/ContentVideoView.java629
-rw-r--r--webkit/media/android/media_metadata_android.cc39
-rw-r--r--webkit/media/android/media_metadata_android.h38
-rw-r--r--webkit/media/webkit_media.gypi2
11 files changed, 1122 insertions, 0 deletions
diff --git a/content/browser/android/browser_jni_registrar.cc b/content/browser/android/browser_jni_registrar.cc
index e966930..90cacb4 100644
--- a/content/browser/android/browser_jni_registrar.cc
+++ b/content/browser/android/browser_jni_registrar.cc
@@ -8,6 +8,7 @@
#include "base/android/jni_registrar.h"
#include "content/browser/android/android_browser_process.h"
#include "content/browser/android/content_settings.h"
+#include "content/browser/android/content_video_view.h"
#include "content/browser/android/content_view_client.h"
#include "content/browser/android/content_view_core_impl.h"
#include "content/browser/android/content_view_statics.h"
@@ -23,6 +24,7 @@ base::android::RegistrationMethod kContentRegisteredMethods[] = {
AndroidLocationApiAdapter::RegisterGeolocationService },
{ "AndroidBrowserProcess", content::RegisterAndroidBrowserProcess },
{ "ContentSettings", content::ContentSettings::RegisterContentSettings },
+ { "ContentVideoView", content::ContentVideoView::RegisterContentVideoView },
{ "ContentViewClient", content::RegisterContentViewClient },
{ "ContentViewCore", content::RegisterContentViewCore },
{ "DeviceInfo", content::RegisterDeviceInfo },
diff --git a/content/browser/android/content_video_view.cc b/content/browser/android/content_video_view.cc
new file mode 100644
index 0000000..88599b2
--- /dev/null
+++ b/content/browser/android/content_video_view.cc
@@ -0,0 +1,291 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/android/content_video_view.h"
+
+#include "base/android/jni_android.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#if !defined(ANDROID_UPSTREAM_BRINGUP)
+#include "content/browser/media/media_player_delegate_android.h"
+#endif
+#include "content/common/android/surface_callback.h"
+#include "content/common/android/surface_texture_peer.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/common/content_switches.h"
+#include "jni/content_video_view_jni.h"
+#include "webkit/media/android/media_metadata_android.h"
+
+using base::android::AttachCurrentThread;
+using base::android::CheckException;
+using base::android::ScopedJavaGlobalRef;
+
+// The timeout value we should wait after user click the back button on the
+// fullscreen view.
+static const int kTimeoutMillseconds = 1000;
+
+// ----------------------------------------------------------------------------
+// Methods that call to Java via JNI
+// ----------------------------------------------------------------------------
+
+namespace content {
+
+// static
+bool ContentVideoView::RegisterContentVideoView(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+void ContentVideoView::CreateContentVideoView(
+ MediaPlayerDelegateAndroid* player) {
+ player_ = player;
+ if (j_content_video_view_.is_null()) {
+ JNIEnv* env = AttachCurrentThread();
+ j_content_video_view_.Reset(Java_ContentVideoView_createContentVideoView(
+ env, reinterpret_cast<jint>(this)));
+ } else {
+ // Just ask video view to reopen the video.
+ Java_ContentVideoView_openVideo(AttachCurrentThread(),
+ j_content_video_view_.obj());
+ }
+}
+
+void ContentVideoView::DestroyContentVideoView() {
+ if (!j_content_video_view_.is_null()) {
+ Java_ContentVideoView_destroyContentVideoView(AttachCurrentThread());
+ j_content_video_view_.Reset();
+ }
+}
+
+void ContentVideoView::OnMediaPlayerError(int error_type) {
+ if (!j_content_video_view_.is_null()) {
+ Java_ContentVideoView_onMediaPlayerError(AttachCurrentThread(),
+ j_content_video_view_.obj(),
+ error_type);
+ }
+}
+
+void ContentVideoView::OnVideoSizeChanged(int width, int height) {
+ if (!j_content_video_view_.is_null()) {
+ Java_ContentVideoView_onVideoSizeChanged(AttachCurrentThread(),
+ j_content_video_view_.obj(),
+ width,
+ height);
+ }
+}
+
+void ContentVideoView::OnBufferingUpdate(int percent) {
+ if (!j_content_video_view_.is_null()) {
+ Java_ContentVideoView_onBufferingUpdate(AttachCurrentThread(),
+ j_content_video_view_.obj(),
+ percent);
+ }
+}
+
+void ContentVideoView::OnPlaybackComplete() {
+ if (!j_content_video_view_.is_null()) {
+ Java_ContentVideoView_onPlaybackComplete(AttachCurrentThread(),
+ j_content_video_view_.obj());
+ }
+}
+
+void ContentVideoView::UpdateMediaMetadata() {
+ if (!j_content_video_view_.is_null())
+ UpdateMediaMetadata(AttachCurrentThread(), j_content_video_view_.obj());
+}
+
+// ----------------------------------------------------------------------------
+
+ContentVideoView::ContentVideoView() : player_(NULL) {
+}
+
+ContentVideoView::~ContentVideoView() {
+ player_ = NULL;
+
+ // If the browser process crashed, just kill the fullscreen view.
+ DestroyContentVideoView();
+}
+
+// ----------------------------------------------------------------------------
+// All these functions are called on UI thread
+
+int ContentVideoView::GetVideoWidth(JNIEnv*, jobject obj) const {
+#if !defined(ANDROID_UPSTREAM_BRINGUP)
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return player_ ? player_->GetVideoWidth() : 0;
+#else
+ return 0;
+#endif
+}
+
+int ContentVideoView::GetVideoHeight(JNIEnv*, jobject obj) const {
+#if !defined(ANDROID_UPSTREAM_BRINGUP)
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return player_ ? player_->GetVideoHeight() : 0;
+#else
+ return 0;
+#endif
+}
+
+int ContentVideoView::GetDurationInMilliSeconds(JNIEnv*, jobject obj) const {
+#if !defined(ANDROID_UPSTREAM_BRINGUP)
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return player_ ? ConvertSecondsToMilliSeconds(player_->Duration()) : -1;
+#else
+ return -1;
+#endif
+}
+
+int ContentVideoView::GetCurrentPosition(JNIEnv*, jobject obj) const {
+#if !defined(ANDROID_UPSTREAM_BRINGUP)
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return player_ ? ConvertSecondsToMilliSeconds(player_->CurrentTime()) : 0;
+#else
+ return 0;
+#endif
+}
+
+bool ContentVideoView::IsPlaying(JNIEnv*, jobject obj) {
+#if !defined(ANDROID_UPSTREAM_BRINGUP)
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return player_ ? player_->IsPlaying() : false;
+#else
+ return false;
+#endif
+}
+
+void ContentVideoView::SeekTo(JNIEnv*, jobject obj, jint msec) {
+#if !defined(ANDROID_UPSTREAM_BRINGUP)
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (player_)
+ player_->Seek(static_cast<float>(msec / 1000.0));
+#endif
+}
+
+webkit_media::MediaMetadataAndroid* ContentVideoView::GetMediaMetadata() {
+#if !defined(ANDROID_UPSTREAM_BRINGUP)
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (!player_)
+ return NULL;
+
+ return new webkit_media::MediaMetadataAndroid(player_->GetVideoWidth(),
+ player_->GetVideoHeight(),
+ base::TimeDelta::FromSeconds(
+ player_->Duration()),
+ base::TimeDelta::FromSeconds(
+ player_->CurrentTime()),
+ !player_->IsPlaying(),
+ player_->CanPause(),
+ player_->CanSeekForward(),
+ player_->CanSeekForward());
+#else
+ return NULL;
+#endif
+}
+
+int ContentVideoView::GetPlayerId(JNIEnv*, jobject obj) const {
+#if !defined(ANDROID_UPSTREAM_BRINGUP)
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return player_ ? player_->player_id() : -1;
+#else
+ return -1;
+#endif
+}
+
+int ContentVideoView::GetRouteId(JNIEnv*, jobject obj) const {
+#if !defined(ANDROID_UPSTREAM_BRINGUP)
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return player_ ? player_->route_id() : -1;
+#else
+ return -1;
+#endif
+}
+
+int ContentVideoView::GetRenderHandle(JNIEnv*, jobject obj) const {
+#if !defined(ANDROID_UPSTREAM_BRINGUP)
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ static bool single_process =
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
+ if (!player_)
+ return -1;
+
+ if (single_process)
+ return 0;
+ return player_->render_handle();
+#else
+ return -1;
+#endif
+}
+
+void ContentVideoView::Play(JNIEnv*, jobject obj) {
+#if !defined(ANDROID_UPSTREAM_BRINGUP)
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (player_)
+ player_->Play();
+#endif
+}
+
+void ContentVideoView::Pause(JNIEnv*, jobject obj) {
+#if !defined(ANDROID_UPSTREAM_BRINGUP)
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (player_)
+ player_->Pause();
+#endif
+}
+
+void ContentVideoView::OnTimeout() {
+#if !defined(ANDROID_UPSTREAM_BRINGUP)
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+#endif
+ timeout_timer_.Stop();
+ DestroyContentVideoView();
+}
+
+int ContentVideoView::ConvertSecondsToMilliSeconds(float seconds) const {
+ return static_cast<int>(seconds * 1000);
+}
+
+// ----------------------------------------------------------------------------
+// Methods called from Java via JNI
+// ----------------------------------------------------------------------------
+
+void ContentVideoView::DestroyContentVideoView(JNIEnv*, jobject,
+ jboolean release_media_player) {
+#if !defined(ANDROID_UPSTREAM_BRINGUP)
+ if (player_) {
+ player_->ExitFullscreen(release_media_player);
+
+ // Fire off a timer so that we will close the fullscreen view in case the
+ // renderer crashes.
+ timeout_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kTimeoutMillseconds),
+ this, &ContentVideoView::OnTimeout);
+ }
+#endif
+}
+
+void ContentVideoView::SetSurface(JNIEnv* env, jobject obj,
+ jobject surface,
+ jint route_id,
+ jint player_id) {
+ SetSurfaceAsync(env,
+ surface,
+ SurfaceTexturePeer::SET_VIDEO_SURFACE_TEXTURE,
+ route_id,
+ player_id,
+ NULL);
+}
+
+void ContentVideoView::UpdateMediaMetadata(JNIEnv* env, jobject obj) {
+ scoped_ptr<webkit_media::MediaMetadataAndroid> metadata(GetMediaMetadata());
+ Java_ContentVideoView_updateMediaMetadata(env,
+ obj,
+ metadata->width,
+ metadata->height,
+ metadata->duration.InMilliseconds(),
+ metadata->can_pause,
+ metadata->can_seek_forward,
+ metadata->can_seek_backward);
+}
+
+} // namespace content
diff --git a/content/browser/android/content_video_view.h b/content/browser/android/content_video_view.h
new file mode 100644
index 0000000..b42b3c6
--- /dev/null
+++ b/content/browser/android/content_video_view.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_ANDROID_CONTENT_VIDEO_VIEW_H_
+#define CONTENT_BROWSER_ANDROID_CONTENT_VIDEO_VIEW_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/timer.h"
+
+namespace webkit_media {
+struct MediaMetadataAndroid;
+}
+
+namespace content {
+
+class MediaPlayerDelegateAndroid;
+
+// Native mirror of ContentVideoView.java.
+class ContentVideoView {
+ public:
+ ContentVideoView();
+ ~ContentVideoView();
+
+ static bool RegisterContentVideoView(JNIEnv* env);
+ void Init(JNIEnv*, jobject obj, jobject weak_this);
+
+ // --------------------------------------------------------------------------
+ // All these functions are called on UI thread
+ int GetVideoWidth(JNIEnv*, jobject obj) const;
+ int GetVideoHeight(JNIEnv*, jobject obj) const;
+ int GetDurationInMilliSeconds(JNIEnv*, jobject obj) const;
+ int GetCurrentPosition(JNIEnv*, jobject obj) const;
+ bool IsPlaying(JNIEnv*, jobject obj);
+ void SeekTo(JNIEnv*, jobject obj, jint msec);
+ int GetPlayerId(JNIEnv*, jobject obj) const;
+ int GetRouteId(JNIEnv*, jobject obj) const;
+ int GetRenderHandle(JNIEnv*, jobject obj) const;
+ void Play(JNIEnv*, jobject obj);
+ void Pause(JNIEnv*, jobject obj);
+ // --------------------------------------------------------------------------
+
+ void PrepareAsync();
+ void DestroyContentVideoView();
+ void UpdateMediaMetadata(JNIEnv*, jobject obj);
+ void DestroyContentVideoView(JNIEnv*, jobject);
+ void DestroyContentVideoView(JNIEnv*, jobject, jboolean release_media_player);
+ void CreateContentVideoView(MediaPlayerDelegateAndroid* player);
+ void SetSurface(JNIEnv*,
+ jobject obj,
+ jobject surface,
+ jint route_id,
+ jint player_id);
+ void UpdateMediaMetadata();
+
+ void OnMediaPlayerError(int errorType);
+ void OnVideoSizeChanged(int width, int height);
+ void OnBufferingUpdate(int percent);
+ void OnPlaybackComplete();
+
+ private:
+ // In some certain cases if the renderer crashes, the ExitFullscreen message
+ // will never acknowledged by the renderer.
+ void OnTimeout();
+
+ webkit_media::MediaMetadataAndroid* GetMediaMetadata();
+
+ int ConvertSecondsToMilliSeconds(float seconds) const;
+
+ MediaPlayerDelegateAndroid* player_;
+
+ base::android::ScopedJavaGlobalRef<jobject> j_content_video_view_;
+
+ // A timer to keep track of when the acknowledgement of exitfullscreen
+ // message times out.
+ base::OneShotTimer<ContentVideoView> timeout_timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContentVideoView);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ANDROID_CONTENT_VIDEO_VIEW_H_
diff --git a/content/common/view_messages.h b/content/common/view_messages.h
index 5aea217..31f8061 100644
--- a/content/common/view_messages.h
+++ b/content/common/view_messages.h
@@ -51,6 +51,10 @@
#include "content/common/mac/font_descriptor.h"
#endif
+#if defined(OS_ANDROID)
+#include "webkit/media/android/media_metadata_android.h"
+#endif
+
#undef IPC_MESSAGE_EXPORT
#define IPC_MESSAGE_EXPORT CONTENT_EXPORT
@@ -360,6 +364,19 @@ IPC_STRUCT_TRAITS_BEGIN(ui::SelectedFileInfo)
IPC_STRUCT_TRAITS_MEMBER(display_name)
IPC_STRUCT_TRAITS_END()
+#if defined(OS_ANDROID)
+IPC_STRUCT_TRAITS_BEGIN(webkit_media::MediaMetadataAndroid)
+ IPC_STRUCT_TRAITS_MEMBER(width)
+ IPC_STRUCT_TRAITS_MEMBER(height)
+ IPC_STRUCT_TRAITS_MEMBER(duration)
+ IPC_STRUCT_TRAITS_MEMBER(current_time)
+ IPC_STRUCT_TRAITS_MEMBER(paused)
+ IPC_STRUCT_TRAITS_MEMBER(can_pause)
+ IPC_STRUCT_TRAITS_MEMBER(can_seek_forward)
+ IPC_STRUCT_TRAITS_MEMBER(can_seek_backward)
+IPC_STRUCT_TRAITS_END()
+#endif
+
IPC_STRUCT_BEGIN(ViewHostMsg_CreateWindow_Params)
// Routing ID of the view initiating the open.
IPC_STRUCT_MEMBER(int, opener_id)
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index 4f013ae..fdae232 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -198,6 +198,8 @@
'browser/android/content_startup_flags.h',
'browser/android/content_util.cc',
'browser/android/content_util.h',
+ 'browser/android/content_video_view.cc',
+ 'browser/android/content_video_view.h',
'browser/android/content_view_client.cc',
'browser/android/content_view_client.h',
'browser/android/content_view_core_impl.cc',
diff --git a/content/content_jni.gypi b/content/content_jni.gypi
index 6368ee2..ba35902 100644
--- a/content/content_jni.gypi
+++ b/content/content_jni.gypi
@@ -15,6 +15,7 @@
'public/android/java/src/org/chromium/content/app/UserAgent.java',
'public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java',
'public/android/java/src/org/chromium/content/browser/ContentSettings.java',
+ 'public/android/java/src/org/chromium/content/browser/ContentVideoView.java',
'public/android/java/src/org/chromium/content/browser/ContentViewClient.java',
'public/android/java/src/org/chromium/content/browser/ContentViewCore.java',
'public/android/java/src/org/chromium/content/browser/ContentViewStatics.java',
@@ -37,6 +38,7 @@
'<(SHARED_INTERMEDIATE_DIR)/content/jni/user_agent_jni.h',
'<(SHARED_INTERMEDIATE_DIR)/content/jni/android_browser_process_jni.h',
'<(SHARED_INTERMEDIATE_DIR)/content/jni/content_settings_jni.h',
+ '<(SHARED_INTERMEDIATE_DIR)/content/jni/content_video_view_jni.h',
'<(SHARED_INTERMEDIATE_DIR)/content/jni/content_view_client_jni.h',
'<(SHARED_INTERMEDIATE_DIR)/content/jni/content_view_core_jni.h',
'<(SHARED_INTERMEDIATE_DIR)/content/jni/content_view_statics_jni.h',
diff --git a/content/public/android/java/src/org/chromium/content/app/AppResource.java b/content/public/android/java/src/org/chromium/content/app/AppResource.java
index 3bd7f60..24720bc 100644
--- a/content/public/android/java/src/org/chromium/content/app/AppResource.java
+++ b/content/public/android/java/src/org/chromium/content/app/AppResource.java
@@ -57,6 +57,18 @@ public class AppResource {
/** String for the title of the month picker dialog. */
public static int STRING_MONTH_PICKER_DIALOG_TITLE;
+ /** String for playback errors in the media player. */
+ public static int STRING_MEDIA_PLAYER_MESSAGE_PLAYBACK_ERROR;
+
+ /** String for unknown errors in the media player. */
+ public static int STRING_MEDIA_PLAYER_MESSAGE_UNKNOWN_ERROR;
+
+ /** String for the button contents in the media player error dialog. */
+ public static int STRING_MEDIA_PLAYER_ERROR_BUTTON;
+
+ /** String for the title of the media player error dialog. */
+ public static int STRING_MEDIA_PLAYER_ERROR_TITLE;
+
/**
* Iterates through all the resources ids and verifies they have values other than zero.
* @return true if all the resources have been registered.
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentVideoView.java b/content/public/android/java/src/org/chromium/content/browser/ContentVideoView.java
new file mode 100644
index 0000000..47a42a4
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentVideoView.java
@@ -0,0 +1,629 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content.browser;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.MediaController;
+import android.widget.MediaController.MediaPlayerControl;
+
+import java.lang.ref.WeakReference;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+import org.chromium.content.app.AppResource;
+import org.chromium.content.common.ISandboxedProcessService;
+
+@JNINamespace("content")
+public class ContentVideoView extends FrameLayout implements MediaPlayerControl,
+ SurfaceHolder.Callback, View.OnTouchListener, View.OnKeyListener {
+
+ private static final String TAG = "ContentVideoView";
+
+ /* Do not change these values without updating their counterparts
+ * in include/media/mediaplayer.h!
+ */
+ private static final int MEDIA_NOP = 0; // interface test message
+ private static final int MEDIA_PREPARED = 1;
+ private static final int MEDIA_PLAYBACK_COMPLETE = 2;
+ private static final int MEDIA_BUFFERING_UPDATE = 3;
+ private static final int MEDIA_SEEK_COMPLETE = 4;
+ private static final int MEDIA_SET_VIDEO_SIZE = 5;
+ private static final int MEDIA_ERROR = 100;
+ private static final int MEDIA_INFO = 200;
+
+ // Type needs to be kept in sync with surface_texture_peer.h.
+ private static final int SET_VIDEO_SURFACE_TEXTURE = 1;
+
+ /** Unspecified media player error.
+ * @see android.media.MediaPlayer.OnErrorListener
+ */
+ public static final int MEDIA_ERROR_UNKNOWN = 0;
+
+ /** Media server died. In this case, the application must release the
+ * MediaPlayer object and instantiate a new one.
+ */
+ public static final int MEDIA_ERROR_SERVER_DIED = 1;
+
+ /** The video is streamed and its container is not valid for progressive
+ * playback i.e the video's index (e.g moov atom) is not at the start of the
+ * file.
+ */
+ public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 2;
+
+ // all possible internal states
+ private static final int STATE_ERROR = -1;
+ private static final int STATE_IDLE = 0;
+ private static final int STATE_PREPARING = 1;
+ private static final int STATE_PREPARED = 2;
+ private static final int STATE_PLAYING = 3;
+ private static final int STATE_PAUSED = 4;
+ private static final int STATE_PLAYBACK_COMPLETED = 5;
+
+ private SurfaceHolder mSurfaceHolder = null;
+ private int mVideoWidth;
+ private int mVideoHeight;
+ private int mCurrentBufferPercentage;
+ private int mDuration;
+ private MediaController mMediaController = null;
+ private boolean mCanPause;
+ private boolean mCanSeekBack;
+ private boolean mCanSeekForward;
+ private boolean mHasMediaMetadata = false;
+
+ // Native pointer to C++ ContentVideoView object.
+ private int mNativeContentVideoView = 0;
+
+ // webkit should have prepared the media
+ private int mCurrentState = STATE_IDLE;
+
+ // Strings for displaying media player errors
+ static String mPlaybackErrorText;
+ static String mUnknownErrorText;
+ static String mErrorButton;
+ static String mErrorTitle;
+
+ // This view will contain the video.
+ private static VideoSurfaceView sVideoSurfaceView;
+
+ private Surface mSurface = null;
+
+ private static Activity sChromeActivity;
+ private static FrameLayout sRootLayout;
+ private static ViewGroup sContentContainer;
+ private static ViewGroup sControlContainer;
+
+ // There are can be at most 1 fullscreen video
+ // TODO(qinmin): will change this once we move the creation of this class
+ // to the host application
+ private static ContentVideoView sContentVideoView = null;
+
+ private class VideoSurfaceView extends SurfaceView {
+
+ public VideoSurfaceView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
+ int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
+ if (mVideoWidth > 0 && mVideoHeight > 0) {
+ if ( mVideoWidth * height > width * mVideoHeight ) {
+ height = width * mVideoHeight / mVideoWidth;
+ } else if ( mVideoWidth * height < width * mVideoHeight ) {
+ width = height * mVideoWidth / mVideoHeight;
+ }
+ }
+ setMeasuredDimension(width, height);
+ }
+ }
+
+ public ContentVideoView(Context context) {
+ this(context, 0);
+ }
+
+ public ContentVideoView(Context context, int nativeContentVideoView) {
+ super(context);
+ initResources(context);
+
+ if (nativeContentVideoView == 0) return;
+ mNativeContentVideoView = nativeContentVideoView;
+
+ mCurrentBufferPercentage = 0;
+ sVideoSurfaceView = new VideoSurfaceView(context);
+ mCurrentState = isPlaying() ? STATE_PLAYING : STATE_PAUSED;
+ }
+
+ private static void initResources(Context context) {
+ if (mPlaybackErrorText != null) return;
+
+ assert AppResource.STRING_MEDIA_PLAYER_MESSAGE_PLAYBACK_ERROR != 0;
+ assert AppResource.STRING_MEDIA_PLAYER_MESSAGE_UNKNOWN_ERROR != 0;
+ assert AppResource.STRING_MEDIA_PLAYER_ERROR_BUTTON != 0;
+ assert AppResource.STRING_MEDIA_PLAYER_ERROR_TITLE != 0;
+
+ mPlaybackErrorText = context.getString(
+ AppResource.STRING_MEDIA_PLAYER_MESSAGE_PLAYBACK_ERROR);
+ mUnknownErrorText = context.getString(
+ AppResource.STRING_MEDIA_PLAYER_MESSAGE_UNKNOWN_ERROR);
+ mErrorButton = context.getString(AppResource.STRING_MEDIA_PLAYER_ERROR_BUTTON);
+ mErrorTitle = context.getString(AppResource.STRING_MEDIA_PLAYER_ERROR_TITLE);
+ }
+
+ void showContentVideoView() {
+ this.addView(sVideoSurfaceView,
+ new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER));
+ sVideoSurfaceView.setOnKeyListener(this);
+ sVideoSurfaceView.setOnTouchListener(this);
+ sVideoSurfaceView.getHolder().addCallback(this);
+ sVideoSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+ sVideoSurfaceView.setFocusable(true);
+ sVideoSurfaceView.setFocusableInTouchMode(true);
+ sVideoSurfaceView.requestFocus();
+ }
+
+ @CalledByNative
+ public void onMediaPlayerError(int errorType) {
+ Log.d(TAG, "OnMediaPlayerError: " + errorType);
+ if (mCurrentState == STATE_ERROR || mCurrentState == STATE_PLAYBACK_COMPLETED) {
+ return;
+ }
+
+ mCurrentState = STATE_ERROR;
+ if (mMediaController != null) {
+ mMediaController.hide();
+ }
+
+ /* Pop up an error dialog so the user knows that
+ * something bad has happened. Only try and pop up the dialog
+ * if we're attached to a window. When we're going away and no
+ * longer have a window, don't bother showing the user an error.
+ *
+ * TODO(qinmin): We need to review whether this Dialog is OK with
+ * the rest of the browser UI elements.
+ */
+ if (getWindowToken() != null) {
+ String message;
+
+ if (errorType == MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
+ message = mPlaybackErrorText;
+ } else {
+ message = mUnknownErrorText;
+ }
+
+ new AlertDialog.Builder(getContext())
+ .setTitle(mErrorTitle)
+ .setMessage(message)
+ .setPositiveButton(mErrorButton,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ /* Inform that the video is over.
+ */
+ onCompletion();
+ }
+ })
+ .setCancelable(false)
+ .show();
+ }
+ }
+
+ @CalledByNative
+ public void onVideoSizeChanged(int width, int height) {
+ mVideoWidth = width;
+ mVideoHeight = height;
+ if (mVideoWidth != 0 && mVideoHeight != 0) {
+ sVideoSurfaceView.getHolder().setFixedSize(mVideoWidth, mVideoHeight);
+ }
+ }
+
+ @CalledByNative
+ public void onBufferingUpdate(int percent) {
+ mCurrentBufferPercentage = percent;
+ }
+
+ @CalledByNative
+ public void onPlaybackComplete() {
+ onCompletion();
+ }
+
+ @CalledByNative
+ public void updateMediaMetadata(
+ int videoWidth,
+ int videoHeight,
+ int duration,
+ boolean canPause,
+ boolean canSeekBack,
+ boolean canSeekForward) {
+ mDuration = duration;
+ mCanPause = canPause;
+ mCanSeekBack = canSeekBack;
+ mCanSeekForward = canSeekForward;
+ mHasMediaMetadata = true;
+
+ if (mMediaController != null) {
+ mMediaController.setEnabled(true);
+ // If paused , should show the controller for ever.
+ if (isPlaying())
+ mMediaController.show();
+ else
+ mMediaController.show(0);
+ }
+
+ onVideoSizeChanged(videoWidth, videoHeight);
+ }
+
+ public void destroyNativeView() {
+ if (mNativeContentVideoView != 0) {
+ mNativeContentVideoView = 0;
+ }
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ sVideoSurfaceView.setFocusable(true);
+ sVideoSurfaceView.setFocusableInTouchMode(true);
+ if (isInPlaybackState() && mMediaController != null) {
+ if (mMediaController.isShowing()) {
+ // ensure the controller will get repositioned later
+ mMediaController.hide();
+ }
+ mMediaController.show();
+ }
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mSurfaceHolder = holder;
+ openVideo();
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mSurfaceHolder = null;
+ if (mNativeContentVideoView != 0) {
+ nativeDestroyContentVideoView(mNativeContentVideoView, true);
+ destroyNativeView();
+ }
+ if (mMediaController != null) {
+ mMediaController.hide();
+ mMediaController = null;
+ }
+ }
+
+ public void setMediaController(MediaController controller) {
+ if (mMediaController != null) {
+ mMediaController.hide();
+ }
+ mMediaController = controller;
+ attachMediaController();
+ }
+
+ private void attachMediaController() {
+ if (mMediaController != null) {
+ mMediaController.setMediaPlayer(this);
+ mMediaController.setAnchorView(sVideoSurfaceView);
+ mMediaController.setEnabled(mHasMediaMetadata);
+ }
+ }
+
+ @CalledByNative
+ public void openVideo() {
+ if (mSurfaceHolder != null) {
+ if (mNativeContentVideoView != 0) {
+ nativeUpdateMediaMetadata(mNativeContentVideoView);
+ }
+ mCurrentBufferPercentage = 0;
+ if (mNativeContentVideoView != 0) {
+ int renderHandle = nativeGetRenderHandle(mNativeContentVideoView);
+ if (renderHandle == 0) {
+ nativeSetSurface(mNativeContentVideoView,
+ mSurfaceHolder.getSurface(),
+ nativeGetRouteId(mNativeContentVideoView),
+ nativeGetPlayerId(mNativeContentVideoView));
+ return;
+ }
+ ISandboxedProcessService service =
+ SandboxedProcessLauncher.getSandboxedService(renderHandle);
+ if (service == null) {
+ Log.e(TAG, "Unable to get SandboxedProcessService from pid.");
+ return;
+ }
+ try {
+ service.setSurface(
+ SET_VIDEO_SURFACE_TEXTURE,
+ mSurfaceHolder.getSurface(),
+ nativeGetRouteId(mNativeContentVideoView),
+ nativeGetPlayerId(mNativeContentVideoView));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to call setSurfaceTexture: " + e);
+ return;
+ }
+ }
+ requestLayout();
+ invalidate();
+ setMediaController(new MediaController(getChromeActivity()));
+
+ if (mMediaController != null) {
+ mMediaController.show();
+ }
+ }
+ }
+
+ private void onCompletion() {
+ mCurrentState = STATE_PLAYBACK_COMPLETED;
+ if (mMediaController != null) {
+ mMediaController.hide();
+ }
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (isInPlaybackState() && mMediaController != null &&
+ event.getAction() == MotionEvent.ACTION_DOWN) {
+ toggleMediaControlsVisiblity();
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent ev) {
+ if (isInPlaybackState() && mMediaController != null) {
+ toggleMediaControlsVisiblity();
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK &&
+ keyCode != KeyEvent.KEYCODE_VOLUME_UP &&
+ keyCode != KeyEvent.KEYCODE_VOLUME_DOWN &&
+ keyCode != KeyEvent.KEYCODE_VOLUME_MUTE &&
+ keyCode != KeyEvent.KEYCODE_CALL &&
+ keyCode != KeyEvent.KEYCODE_MENU &&
+ keyCode != KeyEvent.KEYCODE_SEARCH &&
+ keyCode != KeyEvent.KEYCODE_ENDCALL;
+ if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {
+ if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||
+ keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
+ if (isPlaying()) {
+ pause();
+ mMediaController.show();
+ } else {
+ start();
+ mMediaController.hide();
+ }
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
+ if (!isPlaying()) {
+ start();
+ mMediaController.hide();
+ }
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
+ || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
+ if (isPlaying()) {
+ pause();
+ mMediaController.show();
+ }
+ return true;
+ } else {
+ toggleMediaControlsVisiblity();
+ }
+ } else if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
+ if (mNativeContentVideoView != 0) {
+ nativeDestroyContentVideoView(mNativeContentVideoView, false);
+ destroyNativeView();
+ }
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_SEARCH) {
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ private void toggleMediaControlsVisiblity() {
+ if (mMediaController.isShowing()) {
+ mMediaController.hide();
+ } else {
+ mMediaController.show();
+ }
+ }
+
+ private boolean isInPlaybackState() {
+ return (mCurrentState != STATE_ERROR &&
+ mCurrentState != STATE_IDLE &&
+ mCurrentState != STATE_PREPARING);
+ }
+
+ public void start() {
+ if (isInPlaybackState()) {
+ if (mNativeContentVideoView != 0) {
+ nativePlay(mNativeContentVideoView);
+ }
+ mCurrentState = STATE_PLAYING;
+ }
+ }
+
+ public void pause() {
+ if (isInPlaybackState()) {
+ if (isPlaying()) {
+ if (mNativeContentVideoView != 0) {
+ nativePause(mNativeContentVideoView);
+ }
+ mCurrentState = STATE_PAUSED;
+ }
+ }
+ }
+
+ // cache duration as mDuration for faster access
+ public int getDuration() {
+ if (isInPlaybackState()) {
+ if (mDuration > 0) {
+ return mDuration;
+ }
+ if (mNativeContentVideoView != 0) {
+ mDuration = nativeGetDurationInMilliSeconds(mNativeContentVideoView);
+ } else {
+ mDuration = 0;
+ }
+ return mDuration;
+ }
+ mDuration = -1;
+ return mDuration;
+ }
+
+ public int getCurrentPosition() {
+ if (isInPlaybackState() && mNativeContentVideoView != 0) {
+ return nativeGetCurrentPosition(mNativeContentVideoView);
+ }
+ return 0;
+ }
+
+ public void seekTo(int msec) {
+ if (mNativeContentVideoView != 0) {
+ nativeSeekTo(mNativeContentVideoView, msec);
+ }
+ }
+
+ public boolean isPlaying() {
+ return mNativeContentVideoView != 0 && nativeIsPlaying(mNativeContentVideoView);
+ }
+
+ public int getBufferPercentage() {
+ return mCurrentBufferPercentage;
+ }
+ public boolean canPause() {
+ return mCanPause;
+ }
+
+ public boolean canSeekBackward() {
+ return mCanSeekBack;
+ }
+
+ public boolean canSeekForward() {
+ return mCanSeekForward;
+ }
+
+ @CalledByNative
+ public static ContentVideoView createContentVideoView(int nativeContentVideoView) {
+ if (getChromeActivity() != null) {
+ ContentVideoView videoView = new ContentVideoView(getChromeActivity(),
+ nativeContentVideoView);
+ if (sContentVideoView != null) {
+ return videoView;
+ }
+
+ sContentVideoView = videoView;
+
+ sContentContainer.setVisibility(View.GONE);
+ sControlContainer.setVisibility(View.GONE);
+
+ sChromeActivity.getWindow().setFlags(
+ WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ sChromeActivity.getWindow().addContentView(videoView,
+ new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ Gravity.CENTER));
+
+ videoView.setBackgroundColor(Color.BLACK);
+ sContentVideoView.showContentVideoView();
+ videoView.setVisibility(View.VISIBLE);
+ return videoView;
+ }
+ return null;
+ }
+
+ public static Activity getChromeActivity() {
+ return sChromeActivity;
+ }
+
+ public static void showFullScreen(ContentVideoView fullscreenView) {
+
+ }
+
+ @CalledByNative
+ public static void destroyContentVideoView() {
+ sChromeActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ if (sContentVideoView != null) {
+ sContentVideoView.removeView(sVideoSurfaceView);
+ sVideoSurfaceView = null;
+ sContentVideoView.setVisibility(View.GONE);
+ sRootLayout.removeView(sContentVideoView);
+ }
+
+ sContentContainer.setVisibility(View.VISIBLE);
+ sControlContainer.setVisibility(View.VISIBLE);
+
+ sContentVideoView = null;
+ }
+
+ public static ContentVideoView getContentVideoView() {
+ return sContentVideoView;
+ }
+
+ public static void registerChromeActivity(Activity activity, FrameLayout rootLayout,
+ ViewGroup controlContainer, ViewGroup contentContainer) {
+ sChromeActivity = activity;
+ sRootLayout = rootLayout;
+ sControlContainer = controlContainer;
+ sContentContainer = contentContainer;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return false;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
+ destroyContentVideoView();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ private native void nativeDestroyContentVideoView(int nativeContentVideoView,
+ boolean relaseMediaPlayer);
+ private native int nativeGetCurrentPosition(int nativeContentVideoView);
+ private native int nativeGetDurationInMilliSeconds(int nativeContentVideoView);
+ private native void nativeUpdateMediaMetadata(int nativeContentVideoView);
+ private native int nativeGetVideoWidth(int nativeContentVideoView);
+ private native int nativeGetVideoHeight(int nativeContentVideoView);
+ private native int nativeGetPlayerId(int nativeContentVideoView);
+ private native int nativeGetRouteId(int nativeContentVideoView);
+ private native int nativeGetRenderHandle(int nativeContentVideoView);
+ private native boolean nativeIsPlaying(int nativeContentVideoView);
+ private native void nativePause(int nativeContentVideoView);
+ private native void nativePlay(int nativeContentVideoView);
+ private native void nativeSeekTo(int nativeContentVideoView, int msec);
+ private native void nativeSetSurface(int nativeContentVideoView,
+ Surface surface, int routeId, int playerId);
+}
diff --git a/webkit/media/android/media_metadata_android.cc b/webkit/media/android/media_metadata_android.cc
new file mode 100644
index 0000000..4f6e169
--- /dev/null
+++ b/webkit/media/android/media_metadata_android.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/media/android/media_metadata_android.h"
+
+namespace webkit_media {
+
+MediaMetadataAndroid::MediaMetadataAndroid()
+ : width(0),
+ height(0),
+ paused(false),
+ can_pause(false),
+ can_seek_forward(false),
+ can_seek_backward(false) {
+}
+
+MediaMetadataAndroid::MediaMetadataAndroid(int w,
+ int h,
+ base::TimeDelta d,
+ base::TimeDelta ct,
+ bool p,
+ bool cp,
+ bool csf,
+ bool csb)
+ : width(w),
+ height(h),
+ duration(d),
+ current_time(ct),
+ paused(p),
+ can_pause(cp),
+ can_seek_forward(csf),
+ can_seek_backward(csb) {
+}
+
+MediaMetadataAndroid::~MediaMetadataAndroid() {
+}
+
+} // namespace webkit_media
diff --git a/webkit/media/android/media_metadata_android.h b/webkit/media/android/media_metadata_android.h
new file mode 100644
index 0000000..3298f934
--- /dev/null
+++ b/webkit/media/android/media_metadata_android.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_MEDIA_ANDROID_MEDIA_METADATA_ANDROID_H_
+#define WEBKIT_MEDIA_ANDROID_MEDIA_METADATA_ANDROID_H_
+
+#include "base/basictypes.h"
+#include "base/time.h"
+
+namespace webkit_media {
+
+// Provides the initial media metadata to ContentVideoView.
+struct MediaMetadataAndroid {
+ MediaMetadataAndroid();
+ MediaMetadataAndroid(int w,
+ int h,
+ base::TimeDelta d,
+ base::TimeDelta ct,
+ bool p,
+ bool cp,
+ bool csf,
+ bool csb);
+ ~MediaMetadataAndroid();
+
+ int width;
+ int height;
+ base::TimeDelta duration;
+ base::TimeDelta current_time;
+ bool paused;
+ bool can_pause;
+ bool can_seek_forward;
+ bool can_seek_backward;
+};
+
+} // namespace webkit_media
+
+#endif // WEBKIT_MEDIA_ANDROID_MEDIA_METADATA_ANDROID_H_
diff --git a/webkit/media/webkit_media.gypi b/webkit/media/webkit_media.gypi
index 702031a..203b700e 100644
--- a/webkit/media/webkit_media.gypi
+++ b/webkit/media/webkit_media.gypi
@@ -15,6 +15,8 @@
],
'sources': [
'android/audio_decoder_android.cc',
+ 'android/media_metadata_android.cc',
+ 'android/media_metadata_android.h',
'android/stream_texture_factory_android.h',
'android/webmediaplayer_android.cc',
'android/webmediaplayer_android.h',