]> git.sesse.net Git - vlc/commitdiff
mediacodec: implementation of MediaCodec GPU direct rendering
authorFelix Abecassis <felix.abecassis@gmail.com>
Fri, 17 Jan 2014 10:46:49 +0000 (11:46 +0100)
committerJean-Baptiste Kempf <jb@videolan.org>
Fri, 17 Jan 2014 11:59:13 +0000 (12:59 +0100)
Based on the work by Martin Storsjö.

The decoder stores opaque buffers in the p_sys member of the picture
and the vout uses a callback from the decoder to render these
buffers. When the decoder flushes or closes, all the currently in
flight pictures (filled by the decoder but not displayed yet) need to
be invalidated. A mutex is required in order to prevent the vout from using
destroyed MediaCodec buffers.

In order to avoid a deadlock when exiting the decoder, a maximum number of polling
attempts was added in order to avoid blocking the decoder indefinitely if the vout
is not releasing output buffers anymore.

Signed-off-by: Jean-Baptiste Kempf <jb@videolan.org>
NEWS
configure.ac
modules/MODULES_LIST
modules/codec/Makefile.am
modules/codec/omxil/android_mediacodec.c
modules/codec/omxil/android_opaque.c [new file with mode: 0644]
modules/codec/omxil/android_opaque.h [new file with mode: 0644]
modules/video_output/Modules.am
modules/video_output/android/opaque.c [new file with mode: 0644]
modules/video_output/android/surface.c
po/POTFILES.in

diff --git a/NEWS b/NEWS
index 33d3a5090518a9b34cfc40628b742e3a34a4c261..e9faed159e62d984847d6103f1f2d46a8c67b1c6 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -55,6 +55,7 @@ Video Output:
  * New CoreGraphics video output module for NPAPI plugins
  * New OpenGL ES 2.0 through EGL video output module for Android
  * New Android native window provider module
+ * Direct rendering for MediaCodec Android hardware acceleration
 
 Video Filter:
  * New Oldmovie effect filter
index cc5c5c978d01b0c3389fd6410753d9fb78f106cb..fc3429c729f09c5c8011a8bc4e6d08ee76b5d669 100644 (file)
@@ -3234,6 +3234,8 @@ if test "${enable_android_surface}" = "yes"; then
   if test "${HAVE_ANDROID}" = "1"; then
      VLC_ADD_PLUGIN([android_surface])
      VLC_ADD_LIBS([android_surface], [-ldl])
+     VLC_ADD_PLUGIN([android_opaque])
+     VLC_ADD_LIBS([android_opaque], [-ldl])
   fi
 fi
 
index 1375b323e642b2ce46d616c1d0f49644dc33a476..98f4bbeed62c61fb699f2b494c12cbbacf54dc00 100644 (file)
@@ -30,6 +30,7 @@ $Id$
  * anaglyph: anaglyph 3d video filter
  * android_audiotrack: audio output for Android, based on AudioTrack
  * android_native_window: Android native window provider module
+ * android_opaque: Android direct GPU rendering video output
  * android_surface: video output for Android, based on Surface
  * antiflicker: anti-flicker video filter
  * araw: Pseudo audio decoder for raw PCM
index e45174c963988d356aa8bb2ab8ad4ef50cb6a045..8535bacf865ddbb37ae15c2d4211f684e53e9e8f 100644 (file)
@@ -342,7 +342,8 @@ libiomx_plugin_la_CPPFLAGS = $(libomxil_plugin_la_CPPFLAGS) -DUSE_IOMX
 libiomx_plugin_la_LIBADD = $(libomxil_plugin_la_LIBADD)
 
 libmediacodec_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/codec/omxil
-libmediacodec_plugin_la_SOURCES = codec/omxil/android_mediacodec.c codec/omxil/utils.c video_chroma/copy.c
+libmediacodec_plugin_la_SOURCES = codec/omxil/android_mediacodec.c codec/omxil/utils.c \
+       video_chroma/copy.c codec/omxil/android_opaque.c codec/omxil/android_opaque.h
 
 codec_LTLIBRARIES += $(LTLIBomxil) $(LTLIBomxil_vout)
 EXTRA_LTLIBRARIES += libomxil_plugin.la libomxil_vout_plugin.la
index f9f18e111c3f1cd3adaa1a9c0912010b89fbee8e..499f1f508444cdeb8536c713cc1063e829852b72 100644 (file)
 #include <OMX_Core.h>
 #include <OMX_Component.h>
 #include "omxil_utils.h"
+#include "android_opaque.h"
 
 #define INFO_OUTPUT_BUFFERS_CHANGED -3
 #define INFO_OUTPUT_FORMAT_CHANGED  -2
 #define INFO_TRY_AGAIN_LATER        -1
 
 extern JavaVM *myVm;
+/* JNI functions to get/set an Android Surface object. */
+extern jobject jni_LockAndGetAndroidJavaSurface();
+extern void jni_UnlockAndroidSurface();
+extern void jni_SetAndroidSurfaceSizeEnv(JNIEnv *p_env, int width, int height, int visible_width, int visible_height, int sar_num, int sar_den);
 
 struct decoder_sys_t
 {
@@ -77,6 +82,11 @@ struct decoder_sys_t
     bool decoded;
 
     ArchitectureSpecificCopyData architecture_specific_data;
+
+    /* Direct rendering members. */
+    bool direct_rendering;
+    int i_output_buffers; /**< number of MediaCodec output buffers */
+    picture_t** inflight_picture; /**< stores the inflight picture for each output buffer or NULL */
 };
 
 enum Types
@@ -158,15 +168,25 @@ static void CloseDecoder(vlc_object_t *);
 
 static picture_t *DecodeVideo(decoder_t *, block_t **);
 
+static void InvalidateAllPictures(decoder_t *);
+
 /*****************************************************************************
  * Module descriptor
  *****************************************************************************/
+#define DIRECTRENDERING_TEXT N_("Android direct rendering")
+#define DIRECTRENDERING_LONGTEXT N_(\
+        "Enable Android direct rendering using opaque buffers.")
+
+#define CFG_PREFIX "mediacodec-"
+
 vlc_module_begin ()
     set_description( N_("Video decoder using Android MediaCodec") )
     set_category( CAT_INPUT )
     set_subcategory( SUBCAT_INPUT_VCODEC )
     set_section( N_("Decoding") , NULL )
     set_capability( "decoder", 0 ) /* Only enabled via commandline arguments */
+    add_bool(CFG_PREFIX "dr", true,
+             DIRECTRENDERING_TEXT, DIRECTRENDERING_LONGTEXT, true)
     set_callbacks( OpenDecoder, CloseDecoder )
 vlc_module_end ()
 
@@ -335,12 +355,33 @@ static int OpenDecoder(vlc_object_t *p_this)
         (*env)->DeleteLocalRef(env, bytebuf);
     }
 
-    (*env)->CallVoidMethod(env, p_sys->codec, p_sys->configure, format, NULL, NULL, 0);
-    if ((*env)->ExceptionOccurred(env)) {
-        msg_Warn(p_dec, "Exception occurred in MediaCodec.configure");
-        (*env)->ExceptionClear(env);
-        goto error;
+    p_sys->direct_rendering = var_InheritBool(p_dec, CFG_PREFIX "dr");
+    if (p_sys->direct_rendering) {
+        jobject surf = jni_LockAndGetAndroidJavaSurface();
+        if (surf) {
+            // Configure MediaCodec with the Android surface.
+            (*env)->CallVoidMethod(env, p_sys->codec, p_sys->configure, format, surf, NULL, 0);
+            if ((*env)->ExceptionOccurred(env)) {
+                msg_Warn(p_dec, "Exception occurred in MediaCodec.configure");
+                (*env)->ExceptionClear(env);
+                goto error;
+            }
+            p_dec->fmt_out.i_codec = VLC_CODEC_ANDROID_OPAQUE;
+        } else {
+            msg_Warn(p_dec, "Failed to get the Android Surface, disabling direct rendering.");
+            p_sys->direct_rendering = false;
+        }
+        jni_UnlockAndroidSurface();
     }
+    if (!p_sys->direct_rendering) {
+        (*env)->CallVoidMethod(env, p_sys->codec, p_sys->configure, format, NULL, NULL, 0);
+        if ((*env)->ExceptionOccurred(env)) {
+            msg_Warn(p_dec, "Exception occurred in MediaCodec.configure");
+            (*env)->ExceptionClear(env);
+            goto error;
+        }
+    }
+
     (*env)->CallVoidMethod(env, p_sys->codec, p_sys->start);
     if ((*env)->ExceptionOccurred(env)) {
         msg_Warn(p_dec, "Exception occurred in MediaCodec.start");
@@ -355,6 +396,10 @@ static int OpenDecoder(vlc_object_t *p_this)
     p_sys->input_buffers = (*env)->NewGlobalRef(env, p_sys->input_buffers);
     p_sys->output_buffers = (*env)->NewGlobalRef(env, p_sys->output_buffers);
     p_sys->buffer_info = (*env)->NewGlobalRef(env, p_sys->buffer_info);
+    p_sys->i_output_buffers = (*env)->GetArrayLength(env, p_sys->output_buffers);
+    p_sys->inflight_picture = calloc(1, sizeof(picture_t*) * p_sys->i_output_buffers);
+    if (!p_sys->inflight_picture)
+        goto error;
     (*env)->DeleteLocalRef(env, format);
 
     (*myVm)->DetachCurrentThread(myVm);
@@ -375,6 +420,10 @@ static void CloseDecoder(vlc_object_t *p_this)
     if (!p_sys)
         return;
 
+    /* Invalidate all pictures that are currently in flight in order
+     * to prevent the vout from using destroyed output buffers. */
+    if (p_sys->direct_rendering)
+        InvalidateAllPictures(p_dec);
     (*myVm)->AttachCurrentThread(myVm, &env, NULL);
     if (p_sys->input_buffers)
         (*env)->DeleteGlobalRef(env, p_sys->input_buffers);
@@ -392,9 +441,74 @@ static void CloseDecoder(vlc_object_t *p_this)
 
     free(p_sys->name);
     ArchitectureSpecificCopyHooksDestroy(p_sys->pixel_format, &p_sys->architecture_specific_data);
+    free(p_sys->inflight_picture);
     free(p_sys);
 }
 
+/*****************************************************************************
+ * vout callbacks
+ *****************************************************************************/
+static void DisplayBuffer(picture_sys_t* p_picsys, bool b_render)
+{
+    decoder_t *p_dec = p_picsys->p_dec;
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    if (!p_picsys->b_valid)
+        return;
+
+    vlc_mutex_lock(get_android_opaque_mutex());
+
+    /* Picture might have been invalidated while waiting on the mutex. */
+    if (!p_picsys->b_valid)
+    {
+        vlc_mutex_unlock(get_android_opaque_mutex());
+        return;
+    }
+
+    uint32_t i_index = p_picsys->i_index;
+    p_sys->inflight_picture[i_index] = NULL;
+
+    /* Release the MediaCodec buffer. */
+    JNIEnv *env = NULL;
+    (*myVm)->AttachCurrentThread(myVm, &env, NULL);
+    (*env)->CallVoidMethod(env, p_sys->codec, p_sys->release_output_buffer, i_index, b_render);
+    if ((*env)->ExceptionOccurred(env)) {
+        msg_Err(p_dec, "Exception in MediaCodec.releaseOutputBuffer (DisplayBuffer)");
+        (*env)->ExceptionClear(env);
+    }
+
+    (*myVm)->DetachCurrentThread(myVm);
+    p_picsys->b_valid = false;
+
+    vlc_mutex_unlock(get_android_opaque_mutex());
+}
+
+static void UnlockCallback(picture_sys_t* p_picsys)
+{
+    DisplayBuffer(p_picsys, false);
+}
+
+static void DisplayCallback(picture_sys_t* p_picsys)
+{
+    DisplayBuffer(p_picsys, true);
+}
+
+static void InvalidateAllPictures(decoder_t *p_dec)
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    vlc_mutex_lock(get_android_opaque_mutex());
+    for (int i = 0; i < p_sys->i_output_buffers; ++i)
+    {
+        picture_t *p_pic = p_sys->inflight_picture[i];
+        if (p_pic) {
+            p_pic->p_sys->b_valid = false;
+            p_sys->inflight_picture[i] = NULL;
+        }
+    }
+    vlc_mutex_unlock(get_android_opaque_mutex());
+}
+
 static void GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t **pp_pic)
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
@@ -407,44 +521,81 @@ static void GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t **pp_pic)
                 (*env)->CallVoidMethod(env, p_sys->codec, p_sys->release_output_buffer, index, false);
                 continue;
             }
-            jobject buf = (*env)->GetObjectArrayElement(env, p_sys->output_buffers, index);
-            jsize buf_size = (*env)->GetDirectBufferCapacity(env, buf);
-            uint8_t *ptr = (*env)->GetDirectBufferAddress(env, buf);
-            if (!*pp_pic)
+
+            if (!*pp_pic) {
                 *pp_pic = decoder_NewPicture(p_dec);
+            } else if (p_sys->direct_rendering) {
+                picture_t *p_pic = *pp_pic;
+                picture_sys_t *p_picsys = p_pic->p_sys;
+                int i_prev_index = p_picsys->i_index;
+                (*env)->CallVoidMethod(env, p_sys->codec, p_sys->release_output_buffer, i_prev_index, false);
+
+                // No need to lock here since the previous picture was not sent.
+                p_sys->inflight_picture[i_prev_index] = NULL;
+            }
             if (*pp_pic) {
+
                 picture_t *p_pic = *pp_pic;
-                int size = (*env)->GetIntField(env, p_sys->buffer_info, p_sys->size_field);
-                int offset = (*env)->GetIntField(env, p_sys->buffer_info, p_sys->offset_field);
-                ptr += offset; // Check the size parameter as well
                 // TODO: Use crop_top/crop_left as well? Or is that already taken into account?
                 // On OMX_TI_COLOR_FormatYUV420PackedSemiPlanar the offset already incldues
                 // the cropping, so the top/left cropping params should just be ignored.
-                unsigned int chroma_div;
                 p_pic->date = (*env)->GetLongField(env, p_sys->buffer_info, p_sys->pts_field);
-                GetVlcChromaSizes(p_dec->fmt_out.i_codec, p_dec->fmt_out.video.i_width,
-                                  p_dec->fmt_out.video.i_height, NULL, NULL, &chroma_div);
-                CopyOmxPicture(p_sys->pixel_format, p_pic, p_sys->slice_height, p_sys->stride,
-                               ptr, chroma_div, &p_sys->architecture_specific_data);
-            }
-            (*env)->CallVoidMethod(env, p_sys->codec, p_sys->release_output_buffer, index, false);
-            jthrowable exception = (*env)->ExceptionOccurred(env);
-            if(exception != NULL) {
-                jclass illegalStateException = (*env)->FindClass(env, "java/lang/IllegalStateException");
-                if((*env)->IsInstanceOf(env, exception, illegalStateException)) {
-                    msg_Err(p_dec, "Codec error (IllegalStateException) in MediaCodec.releaseOutputBuffer");
-                    (*env)->ExceptionClear(env);
-                    (*env)->DeleteLocalRef(env, illegalStateException);
+                if (p_sys->direct_rendering) {
+                    picture_sys_t *p_picsys = p_pic->p_sys;
+                    p_picsys->pf_display_callback = DisplayCallback;
+                    p_picsys->pf_unlock_callback = UnlockCallback;
+                    p_picsys->p_dec = p_dec;
+                    p_picsys->i_index = index;
+                    p_picsys->b_valid = true;
+
+                    p_sys->inflight_picture[index] = p_pic;
+                } else {
+                    jobject buf = (*env)->GetObjectArrayElement(env, p_sys->output_buffers, index);
+                    jsize buf_size = (*env)->GetDirectBufferCapacity(env, buf);
+                    uint8_t *ptr = (*env)->GetDirectBufferAddress(env, buf);
+
+                    int size = (*env)->GetIntField(env, p_sys->buffer_info, p_sys->size_field);
+                    int offset = (*env)->GetIntField(env, p_sys->buffer_info, p_sys->offset_field);
+                    ptr += offset; // Check the size parameter as well
+
+                    unsigned int chroma_div;
+                    GetVlcChromaSizes(p_dec->fmt_out.i_codec, p_dec->fmt_out.video.i_width,
+                                      p_dec->fmt_out.video.i_height, NULL, NULL, &chroma_div);
+                    CopyOmxPicture(p_sys->pixel_format, p_pic, p_sys->slice_height, p_sys->stride,
+                                   ptr, chroma_div, &p_sys->architecture_specific_data);
+                    (*env)->CallVoidMethod(env, p_sys->codec, p_sys->release_output_buffer, index, false);
+
+                    jthrowable exception = (*env)->ExceptionOccurred(env);
+                    if(exception != NULL) {
+                        jclass illegalStateException = (*env)->FindClass(env, "java/lang/IllegalStateException");
+                        if((*env)->IsInstanceOf(env, exception, illegalStateException)) {
+                            msg_Err(p_dec, "Codec error (IllegalStateException) in MediaCodec.releaseOutputBuffer");
+                            (*env)->ExceptionClear(env);
+                            (*env)->DeleteLocalRef(env, illegalStateException);
+                        }
+                    }
+                    (*env)->DeleteLocalRef(env, buf);
                 }
+            } else {
+                msg_Warn(p_dec, "NewPicture failed");
+                (*env)->CallVoidMethod(env, p_sys->codec, p_sys->release_output_buffer, index, false);
             }
-            (*env)->DeleteLocalRef(env, buf);
+
             return;
         } else if (index == INFO_OUTPUT_BUFFERS_CHANGED) {
             msg_Dbg(p_dec, "output buffers changed");
             (*env)->DeleteGlobalRef(env, p_sys->output_buffers);
+
             p_sys->output_buffers = (*env)->CallObjectMethod(env, p_sys->codec,
                                                              p_sys->get_output_buffers);
             p_sys->output_buffers = (*env)->NewGlobalRef(env, p_sys->output_buffers);
+
+            vlc_mutex_lock(get_android_opaque_mutex());
+            free(p_sys->inflight_picture);
+            p_sys->i_output_buffers = (*env)->GetArrayLength(env, p_sys->output_buffers);
+            p_sys->inflight_picture = calloc(1, sizeof(picture_t*) * p_sys->i_output_buffers);
+            vlc_mutex_unlock(get_android_opaque_mutex());
+
         } else if (index == INFO_OUTPUT_FORMAT_CHANGED) {
 
             jobject format = (*env)->CallObjectMethod(env, p_sys->codec, p_sys->get_output_format);
@@ -468,7 +619,11 @@ static void GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t **pp_pic)
             int crop_bottom     = GET_INTEGER(format, "crop-bottom");
 
             const char *name = "unknown";
-            GetVlcChromaFormat(p_sys->pixel_format, &p_dec->fmt_out.i_codec, &name);
+            if (p_sys->direct_rendering)
+                jni_SetAndroidSurfaceSizeEnv(env, width, height, width, height, 1, 1);
+            else
+                GetVlcChromaFormat(p_sys->pixel_format, &p_dec->fmt_out.i_codec, &name);
+
             msg_Dbg(p_dec, "output: %d %s, %dx%d stride %d %d, crop %d %d %d %d",
                     p_sys->pixel_format, name, width, height, p_sys->stride, p_sys->slice_height,
                     p_sys->crop_left, p_sys->crop_top, crop_right, crop_bottom);
@@ -520,6 +675,12 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
     if (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) {
         block_Release(p_block);
         if (p_sys->decoded) {
+            /* Invalidate all pictures that are currently in flight
+             * since flushing make all previous indices returned by
+             * MediaCodec invalid. */
+            if (p_sys->direct_rendering)
+                InvalidateAllPictures(p_dec);
+
             (*env)->CallVoidMethod(env, p_sys->codec, p_sys->flush);
             if ((*env)->ExceptionOccurred(env)) {
                 msg_Warn(p_dec, "Exception occurred in MediaCodec.flush");
@@ -532,6 +693,8 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
     }
 
     jlong timeout = 0;
+    const int max_polling_attempts = 50;
+    int attempts = 0;
     while (true) {
         int index = (*env)->CallIntMethod(env, p_sys->codec, p_sys->dequeue_input_buffer, timeout);
         if (index < 0) {
@@ -546,6 +709,31 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
                 return p_pic;
             }
             timeout = 30*1000;
+            ++attempts;
+            /* With opaque DR the output buffers are released by the
+               vout therefore we implement a timeout for polling in
+               order to avoid being indefinitely stalled in this loop. */
+            if (p_sys->direct_rendering && attempts == max_polling_attempts) {
+                picture_t *invalid_picture = decoder_NewPicture(p_dec);
+                if (invalid_picture) {
+                    invalid_picture->date = VLC_TS_INVALID;
+                    picture_sys_t *p_picsys = invalid_picture->p_sys;
+                    p_picsys->pf_display_callback = NULL;
+                    p_picsys->pf_unlock_callback = NULL;
+                    p_picsys->p_dec = NULL;
+                    p_picsys->i_index = -1;
+                    p_picsys->b_valid = false;
+                }
+                else {
+                    /* If we cannot return a picture we must free the
+                       block since the decoder will proceed with the
+                       next block. */
+                    block_Release(p_block);
+                    *pp_block = NULL;
+                }
+                (*myVm)->DetachCurrentThread(myVm);
+                return invalid_picture;
+            }
             continue;
         }
         jobject buf = (*env)->GetObjectArrayElement(env, p_sys->input_buffers, index);
diff --git a/modules/codec/omxil/android_opaque.c b/modules/codec/omxil/android_opaque.c
new file mode 100644 (file)
index 0000000..7a06842
--- /dev/null
@@ -0,0 +1,30 @@
+/*****************************************************************************
+ * android_opaque.c: shared structures between MediaCodec decoder
+ * and MediaCodec video output
+ *****************************************************************************
+ * Copyright (C) 2013 Felix Abecassis
+ *
+ * Authors: Felix Abecassis <felix.abecassis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include "android_opaque.h"
+
+vlc_mutex_t* get_android_opaque_mutex()
+{
+    static vlc_mutex_t s_mutex = VLC_STATIC_MUTEX;
+    return &s_mutex;
+}
diff --git a/modules/codec/omxil/android_opaque.h b/modules/codec/omxil/android_opaque.h
new file mode 100644 (file)
index 0000000..30b145f
--- /dev/null
@@ -0,0 +1,44 @@
+/*****************************************************************************
+ * android_opaque.h: shared structures between MediaCodec decoder
+ * and MediaCodec video output
+ *****************************************************************************
+ * Copyright (C) 2013 Felix Abecassis
+ *
+ * Authors: Felix Abecassis <felix.abecassis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef ANDROID_OPAQUE_H_
+#define ANDROID_OPAQUE_H_
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+
+struct picture_sys_t
+{
+    void (*pf_display_callback)(picture_sys_t*);
+    void (*pf_unlock_callback)(picture_sys_t*);
+    decoder_t *p_dec;
+    uint32_t i_index;
+    int b_valid;
+};
+
+vlc_mutex_t* get_android_opaque_mutex(void);
+
+#endif
index fcc1bcba5e58e65d1773feeb58424a99ec7387df..4b0b2ed034354db8bda0ce97cb3d16f0878ae54d 100644 (file)
@@ -13,6 +13,7 @@ SOURCES_vout_macosx = macosx.m opengl.h opengl.c
 SOURCES_vout_coregraphicslayer = coregraphicslayer.m
 SOURCES_vout_ios2 = ios2.m opengl.h opengl.c
 SOURCES_android_surface = android/surface.c android/utils.c
+SOURCES_android_opaque = android/opaque.c
 
 if HAVE_DECKLINK
 libdecklinkoutput_plugin_la_SOURCES = decklink.cpp
diff --git a/modules/video_output/android/opaque.c b/modules/video_output/android/opaque.c
new file mode 100644 (file)
index 0000000..7334e02
--- /dev/null
@@ -0,0 +1,204 @@
+/*****************************************************************************
+ * opaque.c: Android video output module using direct rendering with
+ * opaque buffers
+ *****************************************************************************
+ * Copyright (C) 2013 Felix Abecassis
+ *
+ * Authors: Felix Abecassis <felix.abecassis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_vout_display.h>
+#include <vlc_picture_pool.h>
+#include "../codec/omxil/android_opaque.h"
+
+static int  Open (vlc_object_t *);
+static void Close(vlc_object_t *);
+
+vlc_module_begin()
+    set_category(CAT_VIDEO)
+    set_subcategory(SUBCAT_VIDEO_VOUT)
+    set_shortname("vout_mediacodec")
+    set_description(N_("Android MediaCodec direct rendering video output"))
+    set_capability("vout display", 200)
+    add_shortcut("androidsurface", "android")
+    set_callbacks(Open, Close)
+vlc_module_end()
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+
+static picture_pool_t   *Pool  (vout_display_t *, unsigned);
+static void             Display(vout_display_t *, picture_t *, subpicture_t *);
+static int              Control(vout_display_t *, int, va_list);
+
+struct vout_display_sys_t
+{
+    picture_pool_t *pool;
+};
+
+static int  LockSurface(picture_t *);
+static void UnlockSurface(picture_t *);
+
+/* We need to allocate a picture pool of more than 30 buffers in order
+ * to be connected directly to the decoder without any intermediate
+ * buffer pool. */
+#define POOL_SIZE 31
+
+static int Open(vlc_object_t *p_this)
+{
+    vout_display_t *vd = (vout_display_t*)p_this;
+
+    video_format_t fmt = vd->fmt;
+
+    if (fmt.i_chroma != VLC_CODEC_ANDROID_OPAQUE)
+        return VLC_EGENERIC;
+
+    /* Allocate structure */
+    vout_display_sys_t *sys = (struct vout_display_sys_t*)calloc(1, sizeof(*sys));
+    if (!sys)
+        return VLC_ENOMEM;
+
+    int i_pictures = POOL_SIZE;
+    picture_t** pictures = calloc(sizeof(*pictures), i_pictures);
+    if (!pictures)
+        goto error;
+    for (int i = 0; i < i_pictures; i++)
+    {
+        picture_sys_t *p_picsys = calloc(1, sizeof(*p_picsys));
+        if (unlikely(p_picsys == NULL))
+            goto error;
+
+        picture_resource_t resource = { .p_sys = p_picsys };
+        picture_t *picture = picture_NewFromResource(&fmt, &resource);
+        if (!picture)
+        {
+            free(p_picsys);
+            goto error;
+        }
+        pictures[i] = picture;
+    }
+
+    /* Wrap it into a picture pool */
+    picture_pool_configuration_t pool_cfg;
+    memset(&pool_cfg, 0, sizeof(pool_cfg));
+    pool_cfg.picture_count = i_pictures;
+    pool_cfg.picture       = pictures;
+    pool_cfg.lock          = LockSurface;
+    pool_cfg.unlock        = UnlockSurface;
+
+    sys->pool = picture_pool_NewExtended(&pool_cfg);
+    if (!sys->pool)
+    {
+        for (int i = 0; i < i_pictures; i++)
+            picture_Release(pictures[i]);
+        goto error;
+    }
+
+    /* Setup vout_display */
+    vd->sys     = sys;
+    vd->fmt     = fmt;
+    vd->pool    = Pool;
+    vd->display = Display;
+    vd->control = Control;
+    vd->prepare = NULL;
+    vd->manage  = NULL;
+
+    /* Fix initial state */
+    vout_display_SendEventFullscreen(vd, false);
+
+    return VLC_SUCCESS;
+
+error:
+    free(pictures);
+    Close(p_this);
+    return VLC_ENOMEM;
+}
+
+static void Close(vlc_object_t *p_this)
+{
+    vout_display_t *vd = (vout_display_t *)p_this;
+    vout_display_sys_t *sys = vd->sys;
+
+    picture_pool_Delete(sys->pool);
+    free(sys);
+}
+
+static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
+{
+    VLC_UNUSED(count);
+
+    return vd->sys->pool;
+}
+
+static int LockSurface(picture_t *picture)
+{
+    VLC_UNUSED(picture);
+
+    return VLC_SUCCESS;
+}
+
+static void UnlockSurface(picture_t *picture)
+{
+    picture_sys_t *p_picsys = picture->p_sys;
+    void (*unlock_callback)(picture_sys_t*) = p_picsys->pf_unlock_callback;
+    if (unlock_callback)
+        unlock_callback(p_picsys);
+}
+
+static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
+{
+    VLC_UNUSED(vd);
+    VLC_UNUSED(subpicture);
+
+    picture_sys_t *p_picsys = picture->p_sys;
+    void (*display_callback)(picture_sys_t*) = p_picsys->pf_display_callback;
+    if (display_callback)
+        display_callback(p_picsys);
+
+    /* refcount lowers to 0, and pool_cfg.unlock is called */
+    picture_Release(picture);
+}
+
+static int Control(vout_display_t *vd, int query, va_list args)
+{
+    VLC_UNUSED(args);
+
+    switch (query) {
+    case VOUT_DISPLAY_HIDE_MOUSE:
+        return VLC_SUCCESS;
+
+    default:
+        msg_Err(vd, "Unknown request in vout mediacodec display");
+
+    case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
+    case VOUT_DISPLAY_CHANGE_FULLSCREEN:
+    case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
+    case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
+    case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
+    case VOUT_DISPLAY_CHANGE_ZOOM:
+    case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
+    case VOUT_DISPLAY_GET_OPENGL:
+        return VLC_EGENERIC;
+    }
+}
index 874d7b64d247fe6a43a0652c536dc35edc9fa5ec..8325fe5510d95adeed1c84a455efff1687f40dd5 100644 (file)
@@ -202,6 +202,9 @@ static int Open(vlc_object_t *p_this)
     /* Setup chroma */
     video_format_t fmt = vd->fmt;
 
+    if (fmt.i_chroma == VLC_CODEC_ANDROID_OPAQUE)
+        return VLC_EGENERIC;
+
     char *psz_fcc = var_InheritString(vd, CFG_PREFIX "chroma");
     if( psz_fcc ) {
         fmt.i_chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, psz_fcc);
index 5e75a6e7f0a14ef8e75241099a38c0aaef00b3ef..cc4d8cbe9f121499399757d672cd0e7f15b5ab1f 100644 (file)
@@ -1136,8 +1136,9 @@ modules/video_filter/vhs.c
 modules/video_filter/wave.c
 modules/video_filter/yuvp.c
 modules/video_output/aa.c
-modules/video_output/android/surface.c
 modules/video_output/android/nativewindow.c
+modules/video_output/android/opaque.c
+modules/video_output/android/surface.c
 modules/video_output/caca.c
 modules/video_output/coregraphicslayer.m
 modules/video_output/decklink.cpp