[Android] [PATCH] Add a SurfaceView for rendering subtitles above the current player surface

Felix Abecassis felix.abecassis at gmail.com
Wed Jan 22 11:44:55 CET 2014


This additional surface is needed when using opaque MediaCodec direct
rendering since the main surface is owned by MediaCodec and cannot be
used for blending or with OpenGL ES.

If this HW acceleration mode is disabled, the visibility of this
surface is set to INVISIBLE since overlaying a transparent SurfaceView
on top of another might not be supported on all devices.
---
 vlc-android/jni/vout.c                             | 22 ++++++++++++
 vlc-android/res/layout/player.xml                  |  5 +++
 vlc-android/src/org/videolan/libvlc/LibVLC.java    |  3 ++
 .../vlc/gui/video/VideoPlayerActivity.java         | 39 +++++++++++++++++++---
 4 files changed, 65 insertions(+), 4 deletions(-)

diff --git a/vlc-android/jni/vout.c b/vlc-android/jni/vout.c
index 60abe93..cac3213 100644
--- a/vlc-android/jni/vout.c
+++ b/vlc-android/jni/vout.c
@@ -31,6 +31,14 @@ pthread_cond_t vout_android_surf_attached;
 static void *vout_android_surf = NULL;
 static void *vout_android_gui = NULL;
 static jobject vout_android_java_surf = NULL;
+static jobject vout_android_subtitles_surf = NULL;
+
+void *jni_LockAndGetSubtitlesSurface() {
+    pthread_mutex_lock(&vout_android_lock);
+    while (vout_android_subtitles_surf == NULL)
+        pthread_cond_wait(&vout_android_surf_attached, &vout_android_lock);
+    return vout_android_subtitles_surf;
+}
 
 void *jni_LockAndGetAndroidSurface() {
     pthread_mutex_lock(&vout_android_lock);
@@ -113,3 +121,17 @@ void Java_org_videolan_libvlc_LibVLC_detachSurface(JNIEnv *env, jobject thiz) {
     vout_android_java_surf = NULL;
     pthread_mutex_unlock(&vout_android_lock);
 }
+
+void Java_org_videolan_libvlc_LibVLC_attachSubtitlesSurface(JNIEnv *env, jobject thiz, jobject surf) {
+    pthread_mutex_lock(&vout_android_lock);
+    vout_android_subtitles_surf = (*env)->NewGlobalRef(env, surf);
+    pthread_cond_signal(&vout_android_surf_attached);
+    pthread_mutex_unlock(&vout_android_lock);
+}
+
+void Java_org_videolan_libvlc_LibVLC_detachSubtitlesSurface(JNIEnv *env, jobject thiz) {
+    pthread_mutex_lock(&vout_android_lock);
+    (*env)->DeleteGlobalRef(env, vout_android_subtitles_surf);
+    vout_android_subtitles_surf = NULL;
+    pthread_mutex_unlock(&vout_android_lock);
+}
diff --git a/vlc-android/res/layout/player.xml b/vlc-android/res/layout/player.xml
index 36b9dad..9fe1240 100644
--- a/vlc-android/res/layout/player.xml
+++ b/vlc-android/res/layout/player.xml
@@ -24,6 +24,11 @@
                 android:id="@+id/player_surface"
                 android:layout_width="1dp"
                 android:layout_height="1dp" />
+            <SurfaceView
+                android:id="@+id/subtitles_surface"
+                android:layout_width="1dp"
+                android:layout_height="1dp"
+                android:visibility="invisible" />
         </FrameLayout>
     </FrameLayout>
 
diff --git a/vlc-android/src/org/videolan/libvlc/LibVLC.java b/vlc-android/src/org/videolan/libvlc/LibVLC.java
index 033afef..8838d04 100644
--- a/vlc-android/src/org/videolan/libvlc/LibVLC.java
+++ b/vlc-android/src/org/videolan/libvlc/LibVLC.java
@@ -76,6 +76,9 @@ public class LibVLC {
 
     public native void detachSurface();
 
+    public native void attachSubtitlesSurface(Surface surface);
+    public native void detachSubtitlesSurface();
+
     /* Load library before object instantiation */
     static {
         try {
diff --git a/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.java b/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.java
index c12a099..c7fd94a 100644
--- a/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.java
+++ b/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.java
@@ -107,7 +107,9 @@ public class VideoPlayerActivity extends Activity implements IVideoPlayer {
     private final static String PLAY_FROM_VIDEOGRID = "org.videolan.vlc.gui.video.PLAY_FROM_VIDEOGRID";
 
     private SurfaceView mSurface;
+    private SurfaceView mSubtitlesSurface;
     private SurfaceHolder mSurfaceHolder;
+    private SurfaceHolder mSubtitlesSurfaceHolder;
     private FrameLayout mSurfaceFrame;
     private LibVLC mLibVLC;
     private String mLocation;
@@ -282,6 +284,12 @@ public class VideoPlayerActivity extends Activity implements IVideoPlayer {
         }
         mSurfaceHolder.addCallback(mSurfaceCallback);
 
+        mSubtitlesSurface = (SurfaceView) findViewById(R.id.subtitles_surface);
+        mSubtitlesSurfaceHolder = mSubtitlesSurface.getHolder();
+        mSubtitlesSurfaceHolder.setFormat(PixelFormat.RGBA_8888);
+        mSubtitlesSurface.setZOrderMediaOverlay(true);
+        mSubtitlesSurfaceHolder.addCallback(mSubtitlesSurfaceCallback);
+
         mSeekbar = (SeekBar) findViewById(R.id.player_overlay_seekbar);
         mSeekbar.setOnSeekBarChangeListener(mSeekListener);
 
@@ -313,6 +321,9 @@ public class VideoPlayerActivity extends Activity implements IVideoPlayer {
             Log.d(TAG, "LibVLC initialisation failed");
             return;
         }
+        /* Only show the subtitles surface when using "Full Acceleration" mode */
+        if (mLibVLC.getHardwareAcceleration() == 2)
+            mSubtitlesSurface.setVisibility(View.VISIBLE);
 
         EventHandler em = EventHandler.getInstance();
         em.addHandler(eventHandler);
@@ -874,12 +885,14 @@ public class VideoPlayerActivity extends Activity implements IVideoPlayer {
 
         // force surface buffer size
         mSurfaceHolder.setFixedSize(mVideoWidth, mVideoHeight);
+        mSubtitlesSurfaceHolder.setFixedSize(mVideoWidth, mVideoHeight);
 
         // set display size
         LayoutParams lp = mSurface.getLayoutParams();
         lp.width  = (int) Math.ceil(dw * mVideoWidth / mVideoVisibleWidth);
         lp.height = (int) Math.ceil(dh * mVideoHeight / mVideoVisibleHeight);
         mSurface.setLayoutParams(lp);
+        mSubtitlesSurface.setLayoutParams(lp);
 
         // set frame size (crop if necessary)
         lp = mSurfaceFrame.getLayoutParams();
@@ -888,6 +901,7 @@ public class VideoPlayerActivity extends Activity implements IVideoPlayer {
         mSurfaceFrame.setLayoutParams(lp);
 
         mSurface.invalidate();
+        mSubtitlesSurface.invalidate();
     }
 
     /**
@@ -1313,6 +1327,22 @@ public class VideoPlayerActivity extends Activity implements IVideoPlayer {
         }
     };
 
+    private final SurfaceHolder.Callback mSubtitlesSurfaceCallback = new Callback() {
+        @Override
+        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+            mLibVLC.attachSubtitlesSurface(holder.getSurface());
+        }
+
+        @Override
+        public void surfaceCreated(SurfaceHolder holder) {
+        }
+
+        @Override
+        public void surfaceDestroyed(SurfaceHolder holder) {
+            mLibVLC.detachSubtitlesSurface();
+        }
+    };
+
     /**
      * show overlay the the default timeout
      */
@@ -1377,11 +1407,12 @@ public class VideoPlayerActivity extends Activity implements IVideoPlayer {
         int layout = 0;
         if (!Util.hasCombBar() && Util.isJellyBeanOrLater())
             layout = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
-        mSurface.setSystemUiVisibility(
-                (dim ? (Util.hasCombBar()
-                        ? View.SYSTEM_UI_FLAG_LOW_PROFILE
+        int visibility =  (dim ? (Util.hasCombBar()
+                ? View.SYSTEM_UI_FLAG_LOW_PROFILE
                         : View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
-                    : View.SYSTEM_UI_FLAG_VISIBLE) | layout);
+                        : View.SYSTEM_UI_FLAG_VISIBLE) | layout;
+        mSurface.setSystemUiVisibility(visibility);
+        mSubtitlesSurface.setSystemUiVisibility(visibility);
     }
 
     private void updateOverlayPausePlay() {
-- 
1.8.3.2



More information about the Android mailing list