[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