[vlc-devel] [PATCH 3/3] mediacodec: implementation of MediaCodec direct rendering based on the work by Martin Storsjö.

Felix Abecassis felix.abecassis at gmail.com
Fri Dec 13 18:51:30 CET 2013


Ping.
Since decoder buffering has been removed, this patch should work on all devices.
I'm now waiting for feedbacks from Martin but I think this patch could
be merged soon if no one has further objections.

2013/11/18 Felix Abecassis <felix.abecassis at gmail.com>:
> Somehow the new files were not added to the patch after rebasing.
> Resending.
>
> 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.
> ---
>  modules/codec/Makefile.am                |   2 +-
>  modules/codec/omxil/android_mediacodec.c | 222 +++++++++++++++++++++++++++----
>  modules/codec/omxil/android_opaque.c     |  30 +++++
>  modules/codec/omxil/android_opaque.h     |  44 ++++++
>  modules/video_output/Modules.am          |   1 +
>  modules/video_output/android/opaque.c    | 204 ++++++++++++++++++++++++++++
>  modules/video_output/android/surface.c   |   3 +
>  7 files changed, 477 insertions(+), 29 deletions(-)
>  create mode 100644 modules/codec/omxil/android_opaque.c
>  create mode 100644 modules/codec/omxil/android_opaque.h
>  create mode 100644 modules/video_output/android/opaque.c
>
> diff --git a/modules/codec/Makefile.am b/modules/codec/Makefile.am
> index 12f9f4a..7b2eb2c 100644
> --- a/modules/codec/Makefile.am
> +++ b/modules/codec/Makefile.am
> @@ -333,7 +333,7 @@ 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_LTLIBRARIES += $(LTLIBomxil) $(LTLIBomxil_vout)
>  EXTRA_LTLIBRARIES += libomxil_plugin.la libomxil_vout_plugin.la
> diff --git a/modules/codec/omxil/android_mediacodec.c b/modules/codec/omxil/android_mediacodec.c
> index 3c94984..c0919e8 100644
> --- a/modules/codec/omxil/android_mediacodec.c
> +++ b/modules/codec/omxil/android_mediacodec.c
> @@ -40,12 +40,17 @@
>  #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 "directrendering", 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 "directrendering");
> +    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,9 @@ 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. */
> +    InvalidateAllPictures(p_dec);
>      (*myVm)->AttachCurrentThread(myVm, &env, NULL);
>      if (p_sys->input_buffers)
>          (*env)->DeleteGlobalRef(env, p_sys->input_buffers);
> @@ -392,9 +440,79 @@ 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);
> +    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 (ReleaseOutputBuffer)");
> +            (*env)->ExceptionClear(env);
> +            (*env)->DeleteLocalRef(env, illegalStateException);
> +        }
> +    }
> +
> +    (*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 +525,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 +623,13 @@ 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 +681,11 @@ 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. */
> +            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");
> diff --git a/modules/codec/omxil/android_opaque.c b/modules/codec/omxil/android_opaque.c
> new file mode 100644
> index 0000000..7a06842
> --- /dev/null
> +++ b/modules/codec/omxil/android_opaque.c
> @@ -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 at 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
> index 0000000..30b145f
> --- /dev/null
> +++ b/modules/codec/omxil/android_opaque.h
> @@ -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 at 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
> diff --git a/modules/video_output/Modules.am b/modules/video_output/Modules.am
> index 89304f9..3fed845 100644
> --- a/modules/video_output/Modules.am
> +++ b/modules/video_output/Modules.am
> @@ -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
> +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
> index 0000000..7334e02
> --- /dev/null
> +++ b/modules/video_output/android/opaque.c
> @@ -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 at 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;
> +    }
> +}
> diff --git a/modules/video_output/android/surface.c b/modules/video_output/android/surface.c
> index d5a9c39..6e03247 100644
> --- a/modules/video_output/android/surface.c
> +++ b/modules/video_output/android/surface.c
> @@ -235,6 +235,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);
> --
> 1.8.3.2
>



-- 
Félix Abecassis
http://felix.abecassis.me



More information about the vlc-devel mailing list