[vlc-devel] [PATCH 4/4] omxil: add android hw buffers support (zero copy)

Thomas Guillem guillem at archos.com
Thu Jul 24 15:11:27 CEST 2014


Activated if cfg "omxil-dr" is true. If extra android symbols are not found or
if HwBuffer_init() fails: fall back to non direct buffer mode
---
 modules/codec/omxil/omxil.c |  800 ++++++++++++++++++++++++++++++++++++++++++-
 modules/codec/omxil/omxil.h |   33 ++
 2 files changed, 816 insertions(+), 17 deletions(-)

diff --git a/modules/codec/omxil/omxil.c b/modules/codec/omxil/omxil.c
index d080dc2..b1e0c98 100644
--- a/modules/codec/omxil/omxil.c
+++ b/modules/codec/omxil/omxil.c
@@ -41,6 +41,12 @@
 #include "omxil_core.h"
 #include "OMX_Broadcom.h"
 
+#if defined(USE_IOMX)
+#include <dlfcn.h>
+#include <jni.h>
+#include "android_opaque.h"
+#endif
+
 #ifndef NDEBUG
 # define OMX_DBG(...) msg_Dbg( p_dec, __VA_ARGS__ )
 #else
@@ -57,6 +63,15 @@
 /* Defined in the broadcom version of OMX_Core.h */
 #define OMX_EventParamOrConfigChanged 0x7F000001
 
+#if defined(USE_IOMX)
+/* JNI functions to get/set an Android Surface object. */
+extern JavaVM *myVm;
+extern jobject jni_LockAndGetAndroidJavaSurface();
+extern void jni_UnlockAndroidSurface();
+extern void jni_SetAndroidSurfaceSize(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den);
+extern bool jni_IsVideoPlayerActivityCreated();
+#endif
+
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
@@ -76,9 +91,59 @@ static OMX_ERRORTYPE OmxEmptyBufferDone( OMX_HANDLETYPE, OMX_PTR,
 static OMX_ERRORTYPE OmxFillBufferDone( OMX_HANDLETYPE, OMX_PTR,
                                         OMX_BUFFERHEADERTYPE * );
 
+#if defined(USE_IOMX)
+static void *DequeueThread( void *data );
+static void DisplayCallback( picture_sys_t* p_picsys );
+static void UnlockCallback( picture_sys_t* p_picsys );
+static void HwBuffer_Init( decoder_t *p_dec, OmxPort *p_port );
+static void HwBuffer_Destroy( decoder_t *p_dec, OmxPort *p_port );
+static int  HwBuffer_AllocateBuffers( decoder_t *p_dec, OmxPort *p_port );
+static int  HwBuffer_FreeBuffers( decoder_t *p_dec, OmxPort *p_port );
+static int  HwBuffer_Start( decoder_t *p_dec, OmxPort *p_port );
+static int  HwBuffer_Stop( decoder_t *p_dec, OmxPort *p_port );
+static int  HwBuffer_Join( decoder_t *p_dec, OmxPort *p_port );
+static int  HwBuffer_GetPic( decoder_t *p_dec, OmxPort *p_port,
+                             picture_t **pp_pic );
+static void HwBuffer_SetCrop( decoder_t *p_dec, OmxPort *p_port,
+                              OMX_CONFIG_RECTTYPE *p_rect );
+static void HwBuffer_ChangeState( decoder_t *p_dec, OmxPort *p_port,
+                                  int i_index, int i_state );
+
+#define HWBUFFER_LOCK() vlc_mutex_lock( get_android_opaque_mutex() )
+#define HWBUFFER_UNLOCK() vlc_mutex_unlock( get_android_opaque_mutex() )
+#define HWBUFFER_WAIT(p_port) vlc_cond_wait( &(p_port)->p_hwbuf->wait, \
+                                              get_android_opaque_mutex() )
+#define HWBUFFER_BROADCAST(p_port) vlc_cond_broadcast( &(p_port)->p_hwbuf->wait )
+
+#else
+static inline int HwBuffer_dummy( )
+{
+    return 0;
+}
+#define HwBuffer_Init(p_dec, p_port) do { } while (0)
+#define HwBuffer_Destroy(p_dec, p_port) do { } while (0)
+#define HwBuffer_AllocateBuffers(p_dec, p_port) HwBuffer_dummy()
+#define HwBuffer_FreeBuffers(p_dec, p_port) HwBuffer_dummy()
+#define HwBuffer_Start(p_dec, p_port) HwBuffer_dummy()
+#define HwBuffer_Stop(p_dec, p_port) HwBuffer_dummy()
+#define HwBuffer_Join(p_dec, p_port) HwBuffer_dummy()
+#define HwBuffer_GetPic(p_dec, p_port, pp_pic) HwBuffer_dummy()
+#define HwBuffer_SetCrop(p_dec, p_port, p_rect) do { } while (0)
+
+#define HWBUFFER_LOCK() do { } while (0)
+#define HWBUFFER_UNLOCK() do { } while (0)
+#define HWBUFFER_WAIT(p_port) do { } while (0)
+#define HWBUFFER_BROADCAST(p_port) do { } while (0)
+#endif
+
 /*****************************************************************************
  * Module descriptor
  *****************************************************************************/
+#define DIRECTRENDERING_TEXT N_("OMX direct rendering")
+#define DIRECTRENDERING_LONGTEXT N_(\
+        "Enable OMX direct rendering.")
+
+#define CFG_PREFIX "omxil-"
 vlc_module_begin ()
     set_description( N_("Audio/Video decoder (using OpenMAX IL)") )
     set_category( CAT_INPUT )
@@ -89,6 +154,8 @@ vlc_module_begin ()
      * enable it only via the --codec iomx command line parameter when
      * wanted. */
     set_capability( "decoder", 0 )
+    add_bool(CFG_PREFIX "dr", false,
+             DIRECTRENDERING_TEXT, DIRECTRENDERING_LONGTEXT, true)
 #else
     set_capability( "decoder", 80 )
 #endif
@@ -231,6 +298,12 @@ static OMX_ERRORTYPE SetPortDefinition(decoder_t *p_dec, OmxPort *p_port,
         }
         else
         {
+            if( p_port->p_hwbuf )
+            {
+                p_fmt->i_codec = VLC_CODEC_ANDROID_OPAQUE;
+                break;
+            }
+
             if( !GetVlcChromaFormat( def->format.video.eColorFormat,
                                      &p_fmt->i_codec, 0 ) )
             {
@@ -316,7 +389,7 @@ static OMX_ERRORTYPE SetPortDefinition(decoder_t *p_dec, OmxPort *p_port,
         }
     }
     if (!strcmp(p_dec->p_sys->psz_component, "OMX.TI.DUCATI1.VIDEO.DECODER") &&
-                def->eDir == OMX_DirOutput)
+                def->eDir == OMX_DirOutput && !p_port->p_hwbuf)
     {
         /* When setting the output buffer size above, the decoder actually
          * sets the buffer size to a lower value than what was chosen. If
@@ -365,7 +438,7 @@ static OMX_ERRORTYPE AllocateBuffers(decoder_t *p_dec, OmxPort *p_port)
     decoder_sys_t *p_sys = p_dec->p_sys;
     OMX_ERRORTYPE omx_error = OMX_ErrorUndefined;
     OMX_PARAM_PORTDEFINITIONTYPE *def = &p_port->definition;
-    unsigned int i;
+    unsigned int i = 0;
 
     OMX_DBG( "AllocateBuffers(%d)", def->eDir );
 
@@ -374,6 +447,7 @@ static OMX_ERRORTYPE AllocateBuffers(decoder_t *p_dec, OmxPort *p_port)
     p_port->pp_buffers = calloc(p_port->i_buffers, sizeof(OMX_BUFFERHEADERTYPE*));
     if( !p_port->pp_buffers )
     {
+        p_port->i_buffers = 0;
         return OMX_ErrorInsufficientResources;
     }
 
@@ -386,7 +460,17 @@ static OMX_ERRORTYPE AllocateBuffers(decoder_t *p_dec, OmxPort *p_port)
         p_port->pp_buffers[i] = (void *)ALIGN((uintptr_t)p_buf, p_port->definition.nBufferAlignment);
 #endif
 
-        if(p_port->b_direct)
+        if( p_port->p_hwbuf )
+        {
+            omx_error =
+                OMX_UseBuffer( p_sys->omx_handle, &p_port->pp_buffers[i],
+                               p_port->i_port_index, 0,
+                               p_port->definition.nBufferSize,
+                               p_port->p_hwbuf->pp_handles[i] );
+            OMX_DBG( "OMX_UseBuffer(%d) %p, %p", def->eDir,
+                     p_port->pp_buffers[i], p_port->p_hwbuf->pp_handles[i] );
+        }
+        else if( p_port->b_direct )
         {
             omx_error =
                 OMX_UseBuffer( p_sys->omx_handle, &p_port->pp_buffers[i],
@@ -410,7 +494,8 @@ static OMX_ERRORTYPE AllocateBuffers(decoder_t *p_dec, OmxPort *p_port)
             p_port->i_buffers = i;
             break;
         }
-        OMX_FIFO_PUT(&p_port->fifo, p_port->pp_buffers[i]);
+        if( !p_port->p_hwbuf )
+            OMX_FIFO_PUT(&p_port->fifo, p_port->pp_buffers[i]);
     }
 
     CHECK_ERROR(omx_error, "AllocateBuffers failed (%x, %i)",
@@ -430,27 +515,50 @@ static OMX_ERRORTYPE FreeBuffers(decoder_t *p_dec, OmxPort *p_port)
     OMX_PARAM_PORTDEFINITIONTYPE *def = &p_port->definition;
     OMX_ERRORTYPE omx_error = OMX_ErrorNone;
     OMX_BUFFERHEADERTYPE *p_buffer;
-    unsigned int i;
+    unsigned int i, i_wait_buffers;
+
+    /* Normally, all buffers are in the port fifo, or given to the codec that
+     * will return them when disabling the port or changing state, therefore we
+     * normally wait for all buffers. For IOMX direct rendering (HwBuffer),
+     * only a few buffers are given to the codec at a time, thus we can only
+     * wait for that many buffers. And after that, we can still free all OMX
+     * buffers since we either got some of them returned via OMX_FIFO_GET, or
+     * never passed them to the codec at all. */
+    if( p_port->p_hwbuf )
+        i_wait_buffers = p_port->p_hwbuf->i_owned;
+    else
+        i_wait_buffers = p_port->i_buffers;
 
-    OMX_DBG( "FreeBuffers(%d)", def->eDir );
+    OMX_DBG( "FreeBuffers(%d), waiting for %u buffers", def->eDir,
+             i_wait_buffers);
 
-    for(i = 0; i < p_port->i_buffers; i++)
+    for(i = 0; i < i_wait_buffers; i++)
     {
         OMX_FIFO_GET(&p_port->fifo, p_buffer);
-        if (p_buffer->pAppPrivate != NULL)
-            decoder_DeletePicture( p_dec, p_buffer->pAppPrivate );
         if (p_buffer->nFlags & SENTINEL_FLAG) {
             free(p_buffer);
             i--;
             continue;
         }
-        omx_error = OMX_FreeBuffer( p_port->omx_handle,
-                                    p_port->i_port_index, p_buffer );
-        OMX_DBG( "OMX_FreeBuffer(%d) %p, %p", def->eDir,
-                 p_buffer, p_buffer->pBuffer );
+    }
+
+    for(i = 0; i < p_port->i_buffers; i++)
+    {
+        p_buffer = p_port->pp_buffers[i];
+        if( p_buffer )
+        {
+            if (p_buffer->pAppPrivate != NULL)
+                decoder_DeletePicture( p_dec, p_buffer->pAppPrivate );
+
+            omx_error = OMX_FreeBuffer( p_port->omx_handle,
+                                        p_port->i_port_index, p_buffer );
+            OMX_DBG( "OMX_FreeBuffer(%d) %p, %p", def->eDir,
+                     p_buffer, p_buffer->pBuffer );
 
-        if(omx_error != OMX_ErrorNone) break;
+            if(omx_error != OMX_ErrorNone) break;
+        }
     }
+
     if( omx_error != OMX_ErrorNone )
        msg_Err( p_dec, "OMX_FreeBuffer failed (%x, %i, %i)",
                 omx_error, (int)p_port->i_port_index, i );
@@ -505,6 +613,9 @@ static OMX_ERRORTYPE GetPortDefinition(decoder_t *p_dec, OmxPort *p_port,
             p_fmt->video.i_visible_height = crop_rect.nHeight;
             if (def->format.video.eColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar)
                 def->format.video.nSliceHeight -= crop_rect.nTop/2;
+
+            if( p_port->p_hwbuf )
+                HwBuffer_SetCrop( p_dec, p_port, &crop_rect );
         }
         else
         {
@@ -512,6 +623,11 @@ static OMX_ERRORTYPE GetPortDefinition(decoder_t *p_dec, OmxPort *p_port,
             omx_error = OMX_ErrorNone;
         }
 
+        if( p_port->p_hwbuf )
+        {
+            UpdatePixelAspect(p_dec);
+            break;
+        }
         /* Hack: Nexus One (stock firmware with binary OMX driver blob)
          * claims to output 420Planar even though it in in practice is
          * NV21. */
@@ -624,6 +740,9 @@ static OMX_ERRORTYPE DeinitialiseComponent(decoder_t *p_dec,
     omx_error = OMX_GetState(omx_handle, &state);
     CHECK_ERROR(omx_error, "OMX_GetState failed (%x)", omx_error );
 
+    if( p_sys->out.p_hwbuf && HwBuffer_Stop( p_dec, &p_sys->out ) != 0 )
+        msg_Warn( p_dec, "HwBuffer_Stop failed" );
+
     if(state == OMX_StateExecuting)
     {
         omx_error = OMX_SendCommand( omx_handle, OMX_CommandStateSet,
@@ -656,6 +775,11 @@ static OMX_ERRORTYPE DeinitialiseComponent(decoder_t *p_dec,
             omx_error = FreeBuffers( p_dec, p_port );
             CHECK_ERROR(omx_error, "FreeBuffers failed (%x, %i)",
                         omx_error, (int)p_port->i_port_index );
+            if( p_port->p_hwbuf )
+            {
+                HwBuffer_FreeBuffers( p_dec, p_port );
+                HwBuffer_Join( p_dec, p_port );
+            }
         }
 
         omx_error = WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, 0, 0, 0);
@@ -680,6 +804,7 @@ static OMX_ERRORTYPE DeinitialiseComponent(decoder_t *p_dec,
             }
             msg_Warn( p_dec, "Stray buffer left in fifo, %p", p_buffer );
         }
+        HwBuffer_Destroy( p_dec, p_port );
     }
     omx_error = pf_free_handle( omx_handle );
     return omx_error;
@@ -763,6 +888,7 @@ static OMX_ERRORTYPE InitialiseComponent(decoder_t *p_dec,
         p_port->i_port_index = definition.nPortIndex;
         p_port->definition = definition;
         p_port->omx_handle = omx_handle;
+        HwBuffer_Init( p_dec, p_port );
     }
 
     if(!p_sys->in.b_valid || !p_sys->out.b_valid)
@@ -771,8 +897,8 @@ static OMX_ERRORTYPE InitialiseComponent(decoder_t *p_dec,
         CHECK_ERROR(omx_error, "couldn't find an input and output port");
     }
 
-    if(!strncmp(p_sys->psz_component, "OMX.SEC.", 8) &&
-       p_dec->fmt_in.i_cat == VIDEO_ES)
+    if( !p_sys->out.p_hwbuf && !strncmp(p_sys->psz_component, "OMX.SEC.", 8) &&
+       p_dec->fmt_in.i_cat == VIDEO_ES )
     {
         OMX_INDEXTYPE index;
         omx_error = OMX_GetExtensionIndex(omx_handle, (OMX_STRING) "OMX.SEC.index.ThumbnailMode", &index);
@@ -947,7 +1073,11 @@ static int OpenGeneric( vlc_object_t *p_this, bool b_encode )
     p_sys->in.b_flushed = true;
     p_sys->in.p_fmt = &p_dec->fmt_in;
     OMX_FIFO_INIT (&p_sys->out.fifo, pInputPortPrivate );
+#if defined(USE_IOMX)
+    p_sys->out.b_direct = jni_IsVideoPlayerActivityCreated() && var_InheritBool(p_dec, CFG_PREFIX "dr");
+#else
     p_sys->out.b_direct = false;
+#endif
     p_sys->out.b_flushed = true;
     p_sys->out.p_fmt = &p_dec->fmt_out;
     p_sys->ports = 2;
@@ -1023,6 +1153,14 @@ static int OpenGeneric( vlc_object_t *p_this, bool b_encode )
     for(i = 0; i < p_sys->ports; i++)
     {
         OmxPort *p_port = &p_sys->p_ports[i];
+        if( p_port->p_hwbuf )
+        {
+            if( HwBuffer_AllocateBuffers( p_dec, p_port ) != 0 )
+            {
+                omx_error = OMX_ErrorInsufficientResources;
+                goto error;
+            }
+        }
         omx_error = AllocateBuffers( p_dec, p_port );
         CHECK_ERROR(omx_error, "AllocateBuffers failed (%x, %i)",
                     omx_error, (int)p_port->i_port_index );
@@ -1037,6 +1175,12 @@ static int OpenGeneric( vlc_object_t *p_this, bool b_encode )
     omx_error = WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, 0, 0, 0);
     CHECK_ERROR(omx_error, "Wait for Executing failed (%x)", omx_error );
 
+    if( p_sys->out.p_hwbuf && HwBuffer_Start( p_dec, &p_sys->out ) != 0 )
+    {
+        omx_error = OMX_ErrorUndefined;
+        goto error;
+    }
+
     /* Send codec configuration data */
     if( p_dec->fmt_in.i_extra )
     {
@@ -1147,6 +1291,9 @@ static OMX_ERRORTYPE PortReconfigure(decoder_t *p_dec, OmxPort *p_port)
        !definition.format.video.nFrameHeight)) )
         return OMX_ErrorUndefined;
 
+    if( p_port->p_hwbuf && HwBuffer_Stop( p_dec, p_port ) != 0 )
+        msg_Warn( p_dec, "HwBuffer_Stop failed" );
+
     omx_error = OMX_SendCommand( p_sys->omx_handle, OMX_CommandPortDisable,
                                  p_port->i_port_index, NULL);
     CHECK_ERROR(omx_error, "OMX_CommandPortDisable on %i failed (%x)",
@@ -1156,6 +1303,12 @@ static OMX_ERRORTYPE PortReconfigure(decoder_t *p_dec, OmxPort *p_port)
     CHECK_ERROR(omx_error, "FreeBuffers failed (%x, %i)",
                 omx_error, (int)p_port->i_port_index );
 
+    if( p_port->p_hwbuf )
+    {
+        HwBuffer_FreeBuffers( p_dec, p_port );
+        HwBuffer_Join( p_dec, p_port );
+    }
+
     omx_error = WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, 0, 0, 0);
     CHECK_ERROR(omx_error, "Wait for PortDisable failed (%x)", omx_error );
 
@@ -1163,7 +1316,15 @@ static OMX_ERRORTYPE PortReconfigure(decoder_t *p_dec, OmxPort *p_port)
     omx_error = GetPortDefinition(p_dec, &p_sys->out, p_sys->out.p_fmt);
     if(omx_error != OMX_ErrorNone) goto error;
 
-    if( p_dec->fmt_in.i_cat != AUDIO_ES )
+    if( p_port->p_hwbuf )
+    {
+        if( HwBuffer_AllocateBuffers( p_dec, p_port ) != 0 )
+        {
+            omx_error = OMX_ErrorInsufficientResources;
+            goto error;
+        }
+    }
+    else if( p_dec->fmt_in.i_cat != AUDIO_ES )
     {
         /* Don't explicitly set the new parameters that we got with
          * OMX_GetParameter above when using audio codecs.
@@ -1194,6 +1355,12 @@ static OMX_ERRORTYPE PortReconfigure(decoder_t *p_dec, OmxPort *p_port)
     omx_error = WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, 0, 0, 0);
     CHECK_ERROR(omx_error, "Wait for PortEnable failed (%x)", omx_error );
 
+    if( p_port->p_hwbuf && HwBuffer_Start( p_dec, p_port ) != 0 )
+    {
+        omx_error = OMX_ErrorUndefined;
+        goto error;
+    }
+
     PrintOmx(p_dec, p_sys->omx_handle, p_dec->p_sys->in.i_port_index);
     PrintOmx(p_dec, p_sys->omx_handle, p_dec->p_sys->out.i_port_index);
 
@@ -1224,6 +1391,14 @@ static int DecodeVideoOutput( decoder_t *p_dec, OmxPort *p_port, picture_t **pp_
             CHECK_ERROR(omx_error, "GetPortDefinition failed");
         }
 
+        if( p_port->p_hwbuf )
+        {
+            if( HwBuffer_GetPic( p_dec, p_port, &p_pic ) != 0 )
+                goto error;
+            else
+                continue;
+        }
+
         if(p_header->nFilledLen)
         {
             p_pic = p_header->pAppPrivate;
@@ -1789,3 +1964,594 @@ static OMX_ERRORTYPE OmxFillBufferDone( OMX_HANDLETYPE omx_handle,
 
     return OMX_ErrorNone;
 }
+
+#if defined(USE_IOMX)
+
+/* Life cycle of buffers when using IOMX direct rendering (HwBuffer):
+ *
+ * <- android display
+ * DequeueThread owned++
+ * -> OMX_FillThisBuffer
+ * ...
+ * <- FillBufferDone OMX_FIFO_PUT
+ * ...
+ * DecodeVideoOutput OMX_FIFO_GET
+ * -> vlc core
+ * ...
+ * DisplayBuffer
+ * -> android display owned--
+ */
+
+/*****************************************************************************
+ * HwBuffer_ChangeState
+ *****************************************************************************/
+static void HwBuffer_ChangeState( decoder_t *p_dec, OmxPort *p_port,
+                                  int i_index, int i_state )
+{
+    VLC_UNUSED( p_dec );
+    p_port->p_hwbuf->i_states[i_index] = i_state;
+    if( i_state == BUF_STATE_OWNED )
+        p_port->p_hwbuf->i_owned++;
+    else
+        p_port->p_hwbuf->i_owned--;
+
+    OMX_DBG( "buffer[%d]: state -> %d, owned buffers: %u",
+             i_index, i_state, p_port->p_hwbuf->i_owned );
+}
+
+/*****************************************************************************
+ * HwBuffer_Init
+ *****************************************************************************/
+static void HwBuffer_Init( decoder_t *p_dec, OmxPort *p_port )
+{
+    VLC_UNUSED( p_dec );
+    void *surf;
+    JNIEnv *p_env;
+    OMX_ERRORTYPE omx_error;
+
+    if( !p_port->b_direct || p_port->definition.eDir != OMX_DirOutput ||
+        p_port->p_fmt->i_cat != VIDEO_ES )
+        return;
+
+    msg_Dbg( p_dec, "HwBuffer_Init");
+
+    if( !(pf_enable_graphic_buffers && pf_get_graphic_buffer_usage &&
+          pf_omx_hwbuffer_connect && pf_omx_hwbuffer_disconnect &&
+          pf_omx_hwbuffer_setup && pf_omx_hwbuffer_setcrop &&
+          pf_omx_hwbuffer_dequeue && pf_omx_hwbuffer_queue &&
+          pf_omx_hwbuffer_cancel &&
+          ((OMX_COMPONENTTYPE*)p_port->omx_handle)->UseBuffer) )
+    {
+        msg_Warn( p_dec, "direct output port enabled but can't find "
+                          "extra symbols, switch back to non direct" );
+        goto error;
+    }
+
+    p_port->p_hwbuf = calloc(1, sizeof(HwBuffer));
+    if( !p_port->p_hwbuf )
+    {
+        goto error;
+    }
+    vlc_cond_init (&p_port->p_hwbuf->wait);
+    p_port->p_hwbuf->p_library = LoadNativeWindowAPI( &p_port->p_hwbuf->native_window );
+    if( !p_port->p_hwbuf->p_library )
+    {
+        msg_Warn( p_dec, "LoadNativeWindowAPI failed" );
+        goto error;
+    }
+
+    surf = jni_LockAndGetAndroidJavaSurface();
+    if( !surf ) {
+        jni_UnlockAndroidSurface();
+        msg_Warn( p_dec, "jni_LockAndGetAndroidJavaSurface failed" );
+        goto error;
+    }
+
+    (*myVm)->AttachCurrentThread( myVm, &p_env, NULL );
+    p_port->p_hwbuf->window = p_port->p_hwbuf->native_window.winFromSurface( p_env, surf );
+    (*myVm)->DetachCurrentThread( myVm );
+
+    jni_UnlockAndroidSurface();
+    if( !p_port->p_hwbuf->window ) {
+        msg_Warn( p_dec, "winFromSurface failed" );
+        goto error;
+    }
+    pf_omx_hwbuffer_connect( p_port->p_hwbuf->window );
+
+    omx_error = pf_enable_graphic_buffers( p_port->omx_handle,
+                                           p_port->i_port_index, OMX_TRUE );
+    CHECK_ERROR( omx_error, "can't enable graphic buffers" );
+
+    /* PortDefinition may change after pf_enable_graphic_buffers call */
+    omx_error = OMX_GetParameter( p_port->omx_handle,
+                                  OMX_IndexParamPortDefinition,
+                                  &p_port->definition );
+    CHECK_ERROR( omx_error, "OMX_GetParameter failed (GraphicBuffers) (%x : %s)",
+                 omx_error, ErrorToString(omx_error) );
+
+
+    msg_Dbg( p_dec, "direct output port enabled" );
+    return;
+error:
+    /* if HwBuffer_Init fails, we can fall back to non direct buffers */
+    HwBuffer_Destroy( p_dec, p_port );
+}
+
+/*****************************************************************************
+ * HwBuffer_Destroy
+ *****************************************************************************/
+static void HwBuffer_Destroy( decoder_t *p_dec, OmxPort *p_port )
+{
+    if( p_port->p_hwbuf )
+    {
+        if( p_port->p_hwbuf->p_library )
+        {
+            if( p_port->p_hwbuf->window )
+            {
+                HwBuffer_Stop( p_dec, p_port );
+                HwBuffer_FreeBuffers( p_dec, p_port );
+                HwBuffer_Join( p_dec, p_port );
+                pf_omx_hwbuffer_disconnect( p_port->p_hwbuf->window );
+                pf_enable_graphic_buffers( p_port->omx_handle,
+                                           p_port->i_port_index, OMX_FALSE );
+                p_port->p_hwbuf->native_window.winRelease( p_port->p_hwbuf->window );
+            }
+            dlclose( p_port->p_hwbuf->p_library );
+        }
+
+        vlc_cond_destroy( &p_port->p_hwbuf->wait );
+        free( p_port->p_hwbuf );
+        p_port->p_hwbuf = NULL;
+    }
+    p_port->b_direct = false;
+}
+
+/*****************************************************************************
+ * HwBuffer_AllocateBuffers
+ *****************************************************************************/
+static int HwBuffer_AllocateBuffers( decoder_t *p_dec, OmxPort *p_port )
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    OMX_PARAM_PORTDEFINITIONTYPE *def = &p_port->definition;
+    unsigned int min_undequeued = 0;
+    unsigned int num_frames = def->nBufferCountMin;
+    unsigned int i = 0;
+    int colorFormat = def->format.video.eColorFormat;
+    OMX_ERRORTYPE omx_error;
+    OMX_U32 i_hw_usage;
+
+    if( !p_port->p_hwbuf )
+        return 0;
+
+    if( !strncmp( p_sys->psz_component, "OMX.SEC.", 8 ) ) {
+        switch( colorFormat ) {
+        case OMX_COLOR_FormatYUV420SemiPlanar:
+            colorFormat = 0x105; // HAL_PIXEL_FORMAT_YCbCr_420_SP
+            break;
+        case OMX_COLOR_FormatYUV420Planar:
+            colorFormat = 0x101; // HAL_PIXEL_FORMAT_YCbCr_420_P
+            break;
+        }
+    }
+
+    omx_error = pf_get_graphic_buffer_usage( p_port->omx_handle,
+                                             p_port->i_port_index,
+                                             &i_hw_usage );
+    if( omx_error != OMX_ErrorNone )
+    {
+        msg_Warn( p_dec, "pf_get_graphic_buffer_usage failed (Not fatal)" );
+        i_hw_usage = 0;
+    }
+
+    if( pf_omx_hwbuffer_setup( p_port->p_hwbuf->window,
+                               def->format.video.nFrameWidth,
+                               def->format.video.nFrameHeight,
+                               colorFormat,
+                               (int) i_hw_usage,
+                               &num_frames, &min_undequeued) != 0 )
+    {
+        msg_Err( p_dec, "can't setup OMXHWBuffer" );
+        goto error;
+    }
+
+    if( num_frames != p_port->definition.nBufferCountActual )
+    {
+        OMX_DBG( "AllocateBuffers: video out wants more frames: %lu vs %u",
+                 p_port->definition.nBufferCountActual, num_frames);
+
+        p_port->definition.nBufferCountActual = num_frames;
+        omx_error = OMX_SetParameter( p_dec->p_sys->omx_handle,
+                                      OMX_IndexParamPortDefinition,
+                                      &p_port->definition );
+        CHECK_ERROR( omx_error, "OMX_SetParameter failed (%x : %s)",
+                     omx_error, ErrorToString(omx_error) );
+    }
+
+    jni_SetAndroidSurfaceSize( def->format.video.nFrameWidth,
+                               def->format.video.nFrameHeight,
+                               def->format.video.nFrameWidth,
+                               def->format.video.nFrameHeight,
+                               p_dec->fmt_out.video.i_sar_num,
+                               p_dec->fmt_out.video.i_sar_den );
+
+    p_port->p_hwbuf->i_buffers = p_port->definition.nBufferCountActual;
+    p_port->p_hwbuf->i_max_owned = p_port->p_hwbuf->i_buffers - min_undequeued;
+
+    p_port->p_hwbuf->pp_handles = calloc( p_port->p_hwbuf->i_buffers,
+                                          sizeof(void *) );
+    if( !p_port->p_hwbuf->pp_handles )
+        goto error;
+
+    p_port->p_hwbuf->i_states = calloc( p_port->p_hwbuf->i_buffers, sizeof(int) );
+    if( !p_port->p_hwbuf->i_states )
+        goto error;
+
+    p_port->p_hwbuf->inflight_picture = calloc( p_port->p_hwbuf->i_buffers,
+                                                sizeof(picture_t*) );
+    if( !p_port->p_hwbuf->inflight_picture )
+        goto error;
+
+    for(i = 0; i < p_port->p_hwbuf->i_buffers; i++)
+    {
+        void *p_handle = NULL;
+
+        if( pf_omx_hwbuffer_dequeue( p_port->p_hwbuf->window, &p_handle ) != 0 )
+        {
+            msg_Err( p_dec, "OMXHWBuffer_dequeue Fail" );
+            goto error;
+        }
+        p_port->p_hwbuf->pp_handles[i] = p_handle;
+    }
+    for(i = 0; i < p_port->p_hwbuf->i_max_owned; i++)
+        HwBuffer_ChangeState( p_dec, p_port, i, BUF_STATE_OWNED );
+    for(; i < p_port->p_hwbuf->i_buffers; i++)
+    {
+        OMX_DBG( "canceling buffer(%d)", i );
+        pf_omx_hwbuffer_cancel( p_port->p_hwbuf->window,
+                                p_port->p_hwbuf->pp_handles[i] );
+    }
+
+    return 0;
+
+error:
+
+    msg_Err( p_dec, "HwBuffer_AllocateBuffers(%d) failed", def->eDir );
+    return -1;
+}
+
+/*****************************************************************************
+ * HwBuffer_FreeBuffers
+ *****************************************************************************/
+static int HwBuffer_FreeBuffers( decoder_t *p_dec, OmxPort *p_port )
+{
+    msg_Dbg( p_dec, "HwBuffer_FreeBuffers");
+
+    HWBUFFER_LOCK();
+
+    p_port->p_hwbuf->b_run = false;
+
+    if( p_port->p_hwbuf->pp_handles )
+    {
+        for(unsigned int i = 0; i < p_port->p_hwbuf->i_buffers; i++)
+        {
+            void *p_handle = p_port->p_hwbuf->pp_handles[i];
+
+            if( p_handle && p_port->p_hwbuf->i_states[i] == BUF_STATE_OWNED )
+            {
+                pf_omx_hwbuffer_cancel( p_port->p_hwbuf->window, p_handle );
+                HwBuffer_ChangeState( p_dec, p_port, i, BUF_STATE_NOT_OWNED );
+            }
+        }
+    }
+    HWBUFFER_BROADCAST( p_port );
+
+    HWBUFFER_UNLOCK();
+
+    p_port->p_hwbuf->i_buffers = 0;
+
+    free( p_port->p_hwbuf->pp_handles );
+    p_port->p_hwbuf->pp_handles = NULL;
+
+    free( p_port->p_hwbuf->i_states );
+    p_port->p_hwbuf->i_states = NULL;
+
+    free( p_port->p_hwbuf->inflight_picture );
+    p_port->p_hwbuf->inflight_picture = NULL;
+
+    return 0;
+}
+
+/*****************************************************************************
+ * HwBuffer_Start
+ *****************************************************************************/
+static int HwBuffer_Start( decoder_t *p_dec, OmxPort *p_port )
+{
+    OMX_BUFFERHEADERTYPE *p_header;
+
+    msg_Dbg( p_dec, "HwBuffer_Start" );
+    HWBUFFER_LOCK();
+
+    /* fill all owned buffers dequeued by HwBuffer_AllocatesBuffers */
+    for(unsigned int i = 0; i < p_port->p_hwbuf->i_buffers; i++)
+    {
+        p_header = p_port->pp_buffers[i];
+
+        if( p_header && p_port->p_hwbuf->i_states[i] == BUF_STATE_OWNED )
+        {
+            OMX_DBG( "FillThisBuffer %p, %p", p_header, p_header->pBuffer );
+            OMX_FillThisBuffer( p_port->omx_handle, p_header );
+        }
+    }
+
+    p_port->p_hwbuf->b_run = true;
+    if( vlc_clone( &p_port->p_hwbuf->dequeue_thread,
+                   DequeueThread, p_dec, VLC_THREAD_PRIORITY_LOW ) )
+    {
+        p_port->p_hwbuf->b_run = false;
+        HWBUFFER_UNLOCK();
+        return -1;
+    }
+
+    HWBUFFER_UNLOCK();
+
+    return 0;
+}
+
+/*****************************************************************************
+ * HwBuffer_Stop: stop DequeueThread and invalidate all pictures that are sent
+ * to vlc core. The thread can be stuck in pf_omx_hwbuffer_dequeue, so don't
+ * join it now since it can be unblocked later by HwBuffer_FreeBuffers.
+ *****************************************************************************/
+static int HwBuffer_Stop( decoder_t *p_dec, OmxPort *p_port )
+{
+    VLC_UNUSED( p_dec );
+
+    msg_Dbg( p_dec, "HwBuffer_Stop" );
+    HWBUFFER_LOCK();
+
+    p_port->p_hwbuf->b_run = false;
+
+    /* invalidate and release all inflight pictures */
+    if( p_port->p_hwbuf->inflight_picture ) {
+        for( unsigned int i = 0; i < p_port->i_buffers; ++i ) {
+            picture_t *p_pic = p_port->p_hwbuf->inflight_picture[i];
+            if( p_pic ) {
+                picture_sys_t *p_picsys = p_pic->p_sys;
+                if( p_picsys ) {
+                    void *p_handle = p_port->pp_buffers[p_picsys->i_index]->pBuffer;
+                    if( p_handle )
+                    {
+                        pf_omx_hwbuffer_cancel( p_port->p_hwbuf->window, p_handle );
+                        HwBuffer_ChangeState( p_dec, p_port, p_picsys->i_index,
+                                              BUF_STATE_NOT_OWNED );
+                    }
+                    p_picsys->b_valid = false;
+                }
+                p_port->p_hwbuf->inflight_picture[i] = NULL;
+            }
+        }
+    }
+
+    HWBUFFER_BROADCAST( p_port );
+
+    HWBUFFER_UNLOCK();
+
+    return 0;
+}
+
+/*****************************************************************************
+ * HwBuffer_Join: join DequeueThread previously stopped by HwBuffer_Stop.
+ *****************************************************************************/
+static int HwBuffer_Join( decoder_t *p_dec, OmxPort *p_port )
+{
+    VLC_UNUSED( p_dec );
+
+    if( p_port->p_hwbuf->dequeue_thread )
+    {
+        vlc_join( p_port->p_hwbuf->dequeue_thread, NULL );
+        p_port->p_hwbuf->dequeue_thread = NULL;
+    }
+    return 0;
+}
+
+/*****************************************************************************
+ * HwBuffer_GetPic
+ *****************************************************************************/
+static int HwBuffer_GetPic( decoder_t *p_dec, OmxPort *p_port,
+                            picture_t **pp_pic)
+{
+    int i_index = -1;
+    picture_t *p_pic;
+    picture_sys_t *p_picsys;
+    OMX_BUFFERHEADERTYPE *p_header;
+
+    OMX_FIFO_PEEK(&p_port->fifo, p_header);
+
+    if( !p_header )
+        return 0;
+
+    for(unsigned int i = 0; i < p_port->i_buffers; i++)
+    {
+        if( p_port->pp_buffers[i] == p_header )
+        {
+            i_index = i;
+            break;
+        }
+    }
+    if( i_index == -1 )
+    {
+        msg_Err( p_dec, "output buffer not found" );
+        return -1;
+    }
+
+    p_pic = decoder_NewPicture( p_dec );
+    if(!p_pic)
+    {
+        msg_Err( p_dec, "decoder_NewPicture failed" );
+        return -1;
+    }
+    p_pic->date = FromOmxTicks( p_header->nTimeStamp );
+
+    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 = i_index;
+    p_picsys->b_valid = true;
+
+    HWBUFFER_LOCK();
+    p_port->p_hwbuf->inflight_picture[i_index] = p_pic;
+    HWBUFFER_UNLOCK();
+
+    *pp_pic = p_pic;
+    OMX_FIFO_GET( &p_port->fifo, p_header );
+    return 0;
+}
+
+/*****************************************************************************
+ * HwBuffer_SetCrop
+ *****************************************************************************/
+static void HwBuffer_SetCrop( decoder_t *p_dec, OmxPort *p_port,
+                              OMX_CONFIG_RECTTYPE *p_rect )
+{
+    VLC_UNUSED( p_dec );
+
+    pf_omx_hwbuffer_setcrop( p_port->p_hwbuf->window,
+                             p_rect->nLeft, p_rect->nTop,
+                             p_rect->nWidth, p_rect->nHeight );
+}
+
+/*****************************************************************************
+ * DequeueThread
+ *****************************************************************************/
+static void *DequeueThread( void *data )
+{
+    decoder_t *p_dec = data;
+    decoder_sys_t *p_sys = p_dec->p_sys;;
+    OmxPort *p_port = &p_sys->out;
+    unsigned int i;
+    int i_index = -1;
+    int err;
+    void *p_handle = NULL;
+    OMX_BUFFERHEADERTYPE *p_header;
+
+    msg_Dbg( p_dec, "DequeueThread running");
+    HWBUFFER_LOCK();
+    while( p_port->p_hwbuf->b_run )
+    {
+        while( p_port->p_hwbuf->b_run &&
+               p_port->p_hwbuf->i_owned >= p_port->p_hwbuf->i_max_owned )
+            HWBUFFER_WAIT( p_port );
+
+        if( !p_port->p_hwbuf->b_run ) continue;
+
+        HWBUFFER_UNLOCK();
+
+
+        /* The thread can be stuck here. It shouldn't happen since we make sure
+         * we call the dequeue function if there is at least one buffer
+         * available. */
+        err = pf_omx_hwbuffer_dequeue( p_port->p_hwbuf->window, &p_handle );
+
+        HWBUFFER_LOCK();
+
+        if( err != 0 ) {
+            if( err != -EBUSY )
+                p_port->p_hwbuf->b_run = false;
+            continue;
+        }
+
+        if( !p_port->p_hwbuf->b_run )
+        {
+            pf_omx_hwbuffer_cancel( p_port->p_hwbuf->window, p_handle );
+            continue;
+        }
+
+        for(i = 0; i < p_port->i_buffers; i++)
+        {
+            if( p_port->pp_buffers[i]->pBuffer == p_handle )
+            {
+                i_index = i;
+                p_header = p_port->pp_buffers[i_index];
+                break;
+            }
+        }
+        if( i_index == -1 )
+        {
+            msg_Err( p_dec, "pf_omx_hwbuffer_dequeue returned unknown handle" );
+            continue;
+        }
+
+        HwBuffer_ChangeState( p_dec, p_port, i_index, BUF_STATE_OWNED );
+
+        OMX_DBG( "FillThisBuffer %p, %p", p_header, p_header->pBuffer );
+        OMX_FillThisBuffer( p_sys->omx_handle, p_header );
+
+        HWBUFFER_BROADCAST( p_port );
+    }
+    HWBUFFER_UNLOCK();
+
+    msg_Dbg( p_dec, "DequeueThread stopped");
+    return NULL;
+}
+
+/*****************************************************************************
+ * 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;
+    OmxPort *p_port = &p_sys->out;
+    void *p_handle;
+
+    if( !p_picsys->b_valid ) return;
+
+    HWBUFFER_LOCK();
+
+    /* Picture might have been invalidated while waiting on the mutex. */
+    if (!p_picsys->b_valid) {
+        HWBUFFER_UNLOCK();
+        return;
+    }
+
+    p_handle = p_port->pp_buffers[p_picsys->i_index]->pBuffer;
+
+    OMX_DBG( "DisplayBuffer: %s %p",
+             b_render ? "render" : "cancel", p_handle );
+
+    if( !p_handle )
+    {
+        msg_Err( p_dec, "DisplayBuffer: buffer handle invalid" );
+        goto end;
+    }
+
+    if( b_render )
+        pf_omx_hwbuffer_queue( p_port->p_hwbuf->window, p_handle );
+    else
+        pf_omx_hwbuffer_cancel( p_port->p_hwbuf->window, p_handle );
+
+    HwBuffer_ChangeState( p_dec, p_port, p_picsys->i_index, BUF_STATE_NOT_OWNED );
+    HWBUFFER_BROADCAST( p_port );
+
+    p_port->p_hwbuf->inflight_picture[p_picsys->i_index] = NULL;
+
+end:
+    p_picsys->b_valid = false;
+    p_picsys->i_index = -1;
+
+    HWBUFFER_UNLOCK();
+}
+
+static void UnlockCallback( picture_sys_t* p_picsys )
+{
+    DisplayBuffer( p_picsys, false );
+}
+
+static void DisplayCallback( picture_sys_t* p_picsys )
+{
+    DisplayBuffer( p_picsys, true );
+}
+
+#endif // USE_IOMX
+
diff --git a/modules/codec/omxil/omxil.h b/modules/codec/omxil/omxil.h
index 6cb7abf..3e2ba70 100644
--- a/modules/codec/omxil/omxil.h
+++ b/modules/codec/omxil/omxil.h
@@ -36,6 +36,16 @@
 #include "omxil_utils.h"
 #include "omxil_core.h"
 
+#if defined(USE_IOMX)
+#include "../../video_output/android/utils.h"
+#endif
+
+enum
+{
+    BUF_STATE_NOT_OWNED = 0,
+    BUF_STATE_OWNED,
+};
+
 /*****************************************************************************
  * decoder_sys_t : omxil decoder descriptor
  *****************************************************************************/
@@ -51,6 +61,27 @@ typedef struct OmxFifo
 
 } OmxFifo;
 
+typedef struct HwBuffer
+{
+    vlc_thread_t    dequeue_thread;
+    bool            b_run;
+    vlc_cond_t      wait;
+    picture_t**     inflight_picture; /**< stores the inflight picture for each output buffer or NULL */
+
+    unsigned int    i_buffers;
+    void            **pp_handles;
+    int             *i_states;
+    unsigned int    i_max_owned;
+    unsigned int    i_owned;
+
+    void            *p_library;
+    void            *window;
+#if defined(USE_IOMX)
+    native_window_api_t native_window;
+#endif
+
+} HwBuffer;
+
 typedef struct OmxPort
 {
     bool b_valid;
@@ -75,6 +106,8 @@ typedef struct OmxPort
     OMX_BOOL b_direct;
     OMX_BOOL b_flushed;
 
+    HwBuffer *p_hwbuf;
+
 } OmxPort;
 
 struct decoder_sys_t
-- 
1.7.10.4


-- 


This email and any files transmitted with it are confidential and are 
intended solely for the use of the individual or entity to which they are 
addressed. Access to this e-mail by anyone else is unauthorised. If you are 
not the intended recipient, any disclosure, copying, distribution or any 
action taken or omitted to be taken in reliance on it, is prohibited. 
E-mail messages are not necessarily secure. Archos does not accept 
responsibility for any changes made to this message after it was sent.




More information about the vlc-devel mailing list