[vlc-devel] [PATCH 08/19] modules: add android_window vout
Thomas Guillem
thomas at gllm.fr
Fri Nov 14 17:09:59 CET 2014
- used for direct and non direct rendering (replace opaque.c)
- use nativewindowpriv: more control than the public api, since you can set
orientation, crop, cancel a buffer without displaying it and allocate more
than one buffers.
- fallback to nativewindow if nativewindowpriv fails (with only one buffer in
the pool then).
- Only one way to display subtitles: use a seperate android surface.
- Fix subtiles display in case or source aspect != 1.
---
configure.ac | 2 +-
modules/MODULES_LIST | 2 +-
modules/codec/omxil/android_mediacodec.c | 49 +-
modules/codec/omxil/android_opaque.h | 9 -
modules/codec/omxil/omxil.c | 63 +-
modules/video_output/Makefile.am | 12 +-
modules/video_output/android/android_window.c | 925 ++++++++++++++++++++++++++
modules/video_output/android/android_window.h | 59 ++
modules/video_output/android/opaque.c | 353 ----------
9 files changed, 1040 insertions(+), 434 deletions(-)
create mode 100644 modules/video_output/android/android_window.c
create mode 100644 modules/video_output/android/android_window.h
delete mode 100644 modules/video_output/android/opaque.c
diff --git a/configure.ac b/configure.ac
index bb2c843..3ea7a93 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3364,7 +3364,7 @@ AC_ARG_ENABLE(android-surface,
[ --enable-android-surface Android Surface video output module (default disabled)])
if test "${enable_android_surface}" = "yes"; then
VLC_ADD_PLUGIN([android_surface])
- VLC_ADD_PLUGIN([android_opaque])
+ VLC_ADD_PLUGIN([android_window])
fi
dnl
diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST
index 2cd3f61..7c8aff7 100644
--- a/modules/MODULES_LIST
+++ b/modules/MODULES_LIST
@@ -33,7 +33,7 @@ $Id$
* anaglyph: anaglyph 3d video filter
* android_audiotrack: audio output for Android, based on AudioTrack
* android_native_window: Android native window provider module
- * android_opaque: Android direct GPU rendering video output
+ * android_vout: Android direct/undirect rendering video output
* android_surface: video output for Android, based on Surface
* antiflicker: anti-flicker video filter
* araw: Pseudo audio decoder for raw PCM
diff --git a/modules/codec/omxil/android_mediacodec.c b/modules/codec/omxil/android_mediacodec.c
index 1eeee69..d248c08 100644
--- a/modules/codec/omxil/android_mediacodec.c
+++ b/modules/codec/omxil/android_mediacodec.c
@@ -42,6 +42,7 @@
#include <OMX_Component.h>
#include "omxil_utils.h"
#include "android_opaque.h"
+#include "../../video_output/android/android_window.h"
#define INFO_OUTPUT_BUFFERS_CHANGED -3
#define INFO_OUTPUT_FORMAT_CHANGED -2
@@ -617,23 +618,25 @@ static void CloseDecoder(vlc_object_t *p_this)
/*****************************************************************************
* vout callbacks
*****************************************************************************/
-static void DisplayBuffer(picture_sys_t* p_picsys, bool b_render)
+static void UnlockPicture(picture_t* p_pic)
{
- decoder_t *p_dec = p_picsys->p_dec;
+ picture_sys_t *p_picsys = p_pic->p_sys;
+ decoder_t *p_dec = p_picsys->priv.hw.p_dec;
decoder_sys_t *p_sys = p_dec->p_sys;
- if (!p_picsys->b_valid)
+ if (!p_picsys->priv.hw.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) {
+ if (!p_picsys->priv.hw.b_valid) {
vlc_mutex_unlock(get_android_opaque_mutex());
return;
}
- uint32_t i_index = p_picsys->i_index;
+ uint32_t i_index = p_picsys->priv.hw.i_index;
+ bool b_render = p_picsys->b_render;
p_sys->inflight_picture[i_index] = NULL;
/* Release the MediaCodec buffer. */
@@ -646,21 +649,11 @@ static void DisplayBuffer(picture_sys_t* p_picsys, bool b_render)
}
jni_detach_thread();
- p_picsys->b_valid = false;
+ p_picsys->priv.hw.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;
@@ -669,7 +662,7 @@ static void InvalidateAllPictures(decoder_t *p_dec)
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_pic->p_sys->priv.hw.b_valid = false;
p_sys->inflight_picture[i] = NULL;
}
}
@@ -707,7 +700,7 @@ static void GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t **pp_pic, jlong t
} 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;
+ int i_prev_index = p_picsys->priv.hw.i_index;
(*env)->CallVoidMethod(env, p_sys->codec, p_sys->release_output_buffer, i_prev_index, false);
if ((*env)->ExceptionOccurred(env)) {
msg_Err(p_dec, "Exception in MediaCodec.releaseOutputBuffer " \
@@ -738,11 +731,11 @@ static void GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t **pp_pic, jlong t
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_picsys->pf_lock_pic = NULL;
+ p_picsys->pf_unlock_pic = UnlockPicture;
+ p_picsys->priv.hw.p_dec = p_dec;
+ p_picsys->priv.hw.i_index = index;
+ p_picsys->priv.hw.b_valid = true;
p_sys->inflight_picture[index] = p_pic;
} else {
@@ -974,11 +967,11 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
if (invalid_picture) {
invalid_picture->date = VLC_TS_INVALID;
picture_sys_t *p_picsys = invalid_picture->p_sys;
- p_picsys->pf_display_callback = NULL;
- p_picsys->pf_unlock_callback = NULL;
- p_picsys->p_dec = NULL;
- p_picsys->i_index = -1;
- p_picsys->b_valid = false;
+ p_picsys->pf_lock_pic = NULL;
+ p_picsys->pf_unlock_pic = NULL;
+ p_picsys->priv.hw.p_dec = NULL;
+ p_picsys->priv.hw.i_index = -1;
+ p_picsys->priv.hw.b_valid = false;
}
else {
/* If we cannot return a picture we must free the
diff --git a/modules/codec/omxil/android_opaque.h b/modules/codec/omxil/android_opaque.h
index 30b145f..5d8b1e6 100644
--- a/modules/codec/omxil/android_opaque.h
+++ b/modules/codec/omxil/android_opaque.h
@@ -30,15 +30,6 @@
#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/codec/omxil/omxil.c b/modules/codec/omxil/omxil.c
index 19bf3f7..95e5d0a 100644
--- a/modules/codec/omxil/omxil.c
+++ b/modules/codec/omxil/omxil.c
@@ -46,6 +46,7 @@
#include <dlfcn.h>
#include <jni.h>
#include "android_opaque.h"
+#include "../../video_output/android/android_window.h"
#endif
#ifndef NDEBUG
@@ -96,8 +97,7 @@ static OMX_ERRORTYPE OmxFillBufferDone( OMX_HANDLETYPE, OMX_PTR,
#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 UnlockPicture( picture_t* p_pic );
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 );
@@ -1635,11 +1635,11 @@ static picture_t *DecodeVideo( decoder_t *p_dec, block_t **pp_block )
if (invalid_picture) {
invalid_picture->date = VLC_TS_INVALID;
picture_sys_t *p_picsys = invalid_picture->p_sys;
- p_picsys->pf_display_callback = NULL;
- p_picsys->pf_unlock_callback = NULL;
- p_picsys->p_dec = NULL;
- p_picsys->i_index = -1;
- p_picsys->b_valid = false;
+ p_picsys->pf_lock_pic = NULL;
+ p_picsys->pf_unlock_pic = NULL;
+ p_picsys->priv.hw.p_dec = NULL;
+ p_picsys->priv.hw.i_index = -1;
+ p_picsys->priv.hw.b_valid = false;
} else {
/* If we cannot return a picture we must free the
block since the decoder will proceed with the
@@ -2418,14 +2418,14 @@ static int HwBuffer_Stop( decoder_t *p_dec, OmxPort *p_port )
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;
+ void *p_handle = p_port->pp_buffers[p_picsys->priv.hw.i_index]->pBuffer;
if( p_handle )
{
p_port->p_hwbuf->anwpriv.cancel( p_port->p_hwbuf->window_priv, p_handle );
- HwBuffer_ChangeState( p_dec, p_port, p_picsys->i_index,
+ HwBuffer_ChangeState( p_dec, p_port, p_picsys->priv.hw.i_index,
BUF_STATE_NOT_OWNED );
}
- p_picsys->b_valid = false;
+ p_picsys->priv.hw.b_valid = false;
}
p_port->p_hwbuf->inflight_picture[i] = NULL;
}
@@ -2493,11 +2493,11 @@ static int HwBuffer_GetPic( decoder_t *p_dec, OmxPort *p_port,
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;
+ p_picsys->pf_lock_pic = NULL;
+ p_picsys->pf_unlock_pic = UnlockPicture;
+ p_picsys->priv.hw.p_dec = p_dec;
+ p_picsys->priv.hw.i_index = i_index;
+ p_picsys->priv.hw.b_valid = true;
HWBUFFER_LOCK();
p_port->p_hwbuf->inflight_picture[i_index] = p_pic;
@@ -2600,27 +2600,28 @@ static void *DequeueThread( void *data )
/*****************************************************************************
* vout callbacks
*****************************************************************************/
-static void DisplayBuffer( picture_sys_t* p_picsys, bool b_render )
+static void UnlockPicture( picture_t* p_pic )
{
- decoder_t *p_dec = p_picsys->p_dec;
+ picture_sys_t *p_picsys = p_pic->p_sys;
+ decoder_t *p_dec = p_picsys->priv.hw.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;
+ if( !p_picsys->priv.hw.b_valid ) return;
HWBUFFER_LOCK();
/* Picture might have been invalidated while waiting on the mutex. */
- if (!p_picsys->b_valid) {
+ if (!p_picsys->priv.hw.b_valid) {
HWBUFFER_UNLOCK();
return;
}
- p_handle = p_port->pp_buffers[p_picsys->i_index]->pBuffer;
+ p_handle = p_port->pp_buffers[p_picsys->priv.hw.i_index]->pBuffer;
OMX_DBG( "DisplayBuffer: %s %p",
- b_render ? "render" : "cancel", p_handle );
+ p_picsys->b_render ? "render" : "cancel", p_handle );
if( !p_handle )
{
@@ -2628,31 +2629,21 @@ static void DisplayBuffer( picture_sys_t* p_picsys, bool b_render )
goto end;
}
- if( b_render )
+ if( p_picsys->b_render )
p_port->p_hwbuf->anwpriv.queue( p_port->p_hwbuf->window_priv, p_handle );
else
p_port->p_hwbuf->anwpriv.cancel( p_port->p_hwbuf->window_priv, p_handle );
- HwBuffer_ChangeState( p_dec, p_port, p_picsys->i_index, BUF_STATE_NOT_OWNED );
+ HwBuffer_ChangeState( p_dec, p_port, p_picsys->priv.hw.i_index, BUF_STATE_NOT_OWNED );
HWBUFFER_BROADCAST( p_port );
- p_port->p_hwbuf->inflight_picture[p_picsys->i_index] = NULL;
+ p_port->p_hwbuf->inflight_picture[p_picsys->priv.hw.i_index] = NULL;
end:
- p_picsys->b_valid = false;
- p_picsys->i_index = -1;
+ p_picsys->priv.hw.b_valid = false;
+ p_picsys->priv.hw.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/video_output/Makefile.am b/modules/video_output/Makefile.am
index dccc3fa6..b8586db 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -250,11 +250,11 @@ libandroid_native_window_plugin_la_SOURCES = video_output/android/nativewindow.c
libandroid_native_window_plugin_la_CFLAGS = $(AM_CFLAGS)
libandroid_native_window_plugin_la_LIBADD = $(LIBDL)
-libandroid_opaque_plugin_la_SOURCES = video_output/android/opaque.c video_output/android/utils.c video_output/android/utils.h
-libandroid_opaque_plugin_la_CFLAGS = $(AM_CFLAGS)
-libandroid_opaque_plugin_la_LIBADD = $(LIBDL)
-libandroid_opaque_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(voutdir)'
-EXTRA_LTLIBRARIES += libandroid_opaque_plugin.la
+libandroid_window_plugin_la_SOURCES = video_output/android/android_window.c video_output/android/android_window.h video_output/android/utils.c video_output/android/utils.h
+libandroid_window_plugin_la_CFLAGS = $(AM_CFLAGS)
+libandroid_window_plugin_la_LIBADD = $(LIBDL)
+libandroid_window_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(voutdir)'
+EXTRA_LTLIBRARIES += libandroid_window_plugin.la
libandroid_surface_plugin_la_SOURCES = video_output/android/surface.c video_output/android/utils.c video_output/android/utils.h
libandroid_surface_plugin_la_CFLAGS = $(AM_CFLAGS)
@@ -264,7 +264,7 @@ EXTRA_LTLIBRARIES += libandroid_surface_plugin.la
if HAVE_ANDROID
vout_LTLIBRARIES += libandroid_native_window_plugin.la
-vout_LTLIBRARIES += $(LTLIBandroid_opaque)
+vout_LTLIBRARIES += $(LTLIBandroid_window)
vout_LTLIBRARIES += $(LTLIBandroid_surface)
if HAVE_EGL
vout_LTLIBRARIES += libegl_android_plugin.la
diff --git a/modules/video_output/android/android_window.c b/modules/video_output/android/android_window.c
new file mode 100644
index 0000000..8dc8c18
--- /dev/null
+++ b/modules/video_output/android/android_window.c
@@ -0,0 +1,925 @@
+/*****************************************************************************
+ * android_window.c: Android video output module
+ *****************************************************************************
+ * Copyright (C) 2014 VLC authors and VideoLAN
+ *
+ * Authors: Thomas Guillem <thomas at gllm.fr>
+ * Felix Abecassis <felix.abecassis at gmail.com>
+ * Ming Hu <tewilove at gmail.com>
+ * Ludovic Fauvet <etix at l0cal.com>
+ * Sébastien Toque <xilasz 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 <vlc_filter.h>
+#include <vlc_md5.h>
+
+#include <dlfcn.h>
+
+#include "android_window.h"
+#include "utils.h"
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+#define PRIV_WINDOW_MAX_BUFFER_COUNT 32
+#define PRIV_WINDOW_MIN_BUFFER_COUNT 2
+
+#define USE_ANWP
+#define CHROMA_TEXT N_("Chroma used")
+#define CHROMA_LONGTEXT N_(\
+ "Force use of a specific chroma for output. Default is RGB32.")
+
+#define CFG_PREFIX "androidsurface-"
+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("android_window")
+ set_description(N_("Android video output"))
+ set_capability("vout display", 200)
+ add_shortcut("androidwindow", "android")
+ set_callbacks(Open, Close)
+vlc_module_end()
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+
+#define THREAD_NAME "android_window"
+extern int jni_attach_thread(JNIEnv **env, const char *thread_name);
+extern void jni_detach_thread();
+
+extern jobject jni_LockAndGetAndroidJavaSurface();
+extern jobject jni_LockAndGetSubtitlesSurface();
+extern void jni_UnlockAndroidSurface();
+
+extern void jni_SetSurfaceLayout(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den);
+extern int jni_ConfigureSurface(jobject jsurf, int width, int height, int hal, bool *configured);
+
+static const vlc_fourcc_t subpicture_chromas[] =
+{
+ VLC_CODEC_RGBA,
+ 0
+};
+
+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);
+
+typedef struct android_window android_window;
+struct android_window
+{
+ video_format_t fmt;
+ int i_android_hal;
+ unsigned int i_angle;
+ unsigned int i_pic_count;
+ unsigned int i_min_undequeued;
+ bool b_use_priv;
+
+ jobject jsurf;
+ ANativeWindow *p_handle;
+ native_window_priv *p_handle_priv;
+};
+
+struct vout_display_sys_t
+{
+ picture_pool_t *pool;
+
+ void *p_library;
+ native_window_api_t anw;
+ native_window_priv_api_t anwp;
+ bool b_has_anwp;
+
+ android_window *p_window;
+ android_window *p_sub_window;
+
+ filter_t *p_spu_blend;
+ picture_t *p_sub_pic;
+
+ bool b_has_subpictures;
+
+ uint8_t hash[16];
+};
+
+static int UpdateWindowSize(video_format_t *p_fmt, bool b_cropped)
+{
+ unsigned int i_width, i_height;
+ unsigned int i_sar_num = 1, i_sar_den = 1;
+ video_format_t rot_fmt;
+
+ video_format_ApplyRotation(&rot_fmt, p_fmt);
+
+ if (rot_fmt.i_sar_num != 0 && rot_fmt.i_sar_den != 0) {
+ i_sar_num = rot_fmt.i_sar_num;
+ i_sar_den = rot_fmt.i_sar_den;
+ }
+ if (b_cropped) {
+ i_width = rot_fmt.i_visible_width;
+ i_height = rot_fmt.i_visible_height;
+ } else {
+ i_width = rot_fmt.i_width;
+ i_height = rot_fmt.i_height;
+ }
+
+ jni_SetSurfaceLayout(i_width, i_height,
+ rot_fmt.i_visible_width,
+ rot_fmt.i_visible_height,
+ i_sar_num,
+ i_sar_den);
+ return 0;
+}
+
+static picture_t *PictureAlloc(vout_display_sys_t *sys, video_format_t *fmt)
+{
+ picture_t *p_pic;
+ picture_resource_t rsc;
+ picture_sys_t *p_picsys = calloc(1, sizeof(*p_picsys));
+
+ if (unlikely(p_picsys == NULL))
+ return NULL;
+
+ p_picsys->p_vd_sys = sys;
+
+ memset(&rsc, 0, sizeof(picture_resource_t));
+ rsc.p_sys = p_picsys,
+
+ p_pic = picture_NewFromResource(fmt, &rsc);
+ if (!p_pic)
+ {
+ free(p_picsys);
+ return NULL;
+ }
+ return p_pic;
+}
+
+static void FixSubtitleFormat(vout_display_sys_t *sys)
+{
+ video_format_t *p_fmt = &sys->p_sub_window->fmt;
+
+ if (p_fmt->i_visible_width == 0 || p_fmt->i_visible_height == 0) {
+ p_fmt->i_visible_width = p_fmt->i_width;
+ p_fmt->i_visible_height = p_fmt->i_height;
+ }
+ if (p_fmt->i_sar_num > 0 && p_fmt->i_sar_den > 0) {
+ if (p_fmt->i_sar_num >= p_fmt->i_sar_den)
+ p_fmt->i_width = (int64_t)p_fmt->i_visible_width * p_fmt->i_sar_num / p_fmt->i_sar_den;
+ else
+ p_fmt->i_height = (int64_t)p_fmt->i_visible_height * p_fmt->i_sar_den / p_fmt->i_sar_num;
+ p_fmt->i_sar_num = 1;
+ p_fmt->i_sar_den = 1;
+ } else {
+ p_fmt->i_width = p_fmt->i_visible_width;
+ p_fmt->i_height = p_fmt->i_visible_height;
+ }
+ p_fmt->i_x_offset = 0;
+ p_fmt->i_y_offset = 0;
+}
+
+#define ALIGN_16_PIXELS( x ) ( ( ( x ) + 15 ) / 16 * 16 )
+static void SetupPictureYV12(picture_t *p_picture, uint32_t i_in_stride)
+{
+ /* according to document of android.graphics.ImageFormat.YV12 */
+ int i_stride = ALIGN_16_PIXELS(i_in_stride);
+ int i_c_stride = ALIGN_16_PIXELS(i_stride / 2);
+
+ p_picture->p->i_pitch = i_stride;
+
+ /* Fill chroma planes for planar YUV */
+ for (int n = 1; n < p_picture->i_planes; n++)
+ {
+ const plane_t *o = &p_picture->p[n-1];
+ plane_t *p = &p_picture->p[n];
+
+ p->p_pixels = o->p_pixels + o->i_lines * o->i_pitch;
+ p->i_pitch = i_c_stride;
+ p->i_lines = p_picture->format.i_height / 2;
+ /*
+ Explicitly set the padding lines of the picture to black (127 for YUV)
+ since they might be used by Android during rescaling.
+ */
+ int visible_lines = p_picture->format.i_visible_height / 2;
+ if (visible_lines < p->i_lines)
+ memset(&p->p_pixels[visible_lines * p->i_pitch], 127, (p->i_lines - visible_lines) * p->i_pitch);
+ }
+
+ if (vlc_fourcc_AreUVPlanesSwapped(p_picture->format.i_chroma,
+ VLC_CODEC_YV12)) {
+ uint8_t *p_tmp = p_picture->p[1].p_pixels;
+ p_picture->p[1].p_pixels = p_picture->p[2].p_pixels;
+ p_picture->p[2].p_pixels = p_tmp;
+ }
+}
+
+static android_window *AndroidWindow_New(vout_display_sys_t *sys,
+ video_format_t *p_fmt,
+ bool b_use_priv)
+{
+ android_window *p_window = calloc(1, sizeof(android_window));
+
+ if (!p_window)
+ return NULL;
+
+ if (p_fmt->i_chroma != VLC_CODEC_ANDROID_OPAQUE) {
+ p_window->b_use_priv = sys->b_has_anwp && b_use_priv;
+
+ p_window->i_android_hal = ChromaToAndroidHal(p_fmt->i_chroma);
+ if (p_window->i_android_hal == -1) {
+ free(p_window);
+ return NULL;
+ }
+ }
+
+ if (p_window->b_use_priv) {
+ switch (p_fmt->orientation)
+ {
+ case ORIENT_ROTATED_90:
+ p_window->i_angle = 90;
+ break;
+ case ORIENT_ROTATED_180:
+ p_window->i_angle = 180;
+ break;
+ case ORIENT_ROTATED_270:
+ p_window->i_angle = 270;
+ break;
+ default:
+ p_window->i_angle = 0;
+ }
+ p_window->fmt = *p_fmt;
+ } else {
+ video_format_ApplyRotation(&p_window->fmt, p_fmt);
+ }
+ p_window->i_pic_count = 1;
+ return p_window;
+}
+
+static void AndroidWindow_Destroy(vout_display_sys_t *sys,
+ android_window *p_window)
+{
+ if (p_window->p_handle_priv)
+ sys->anwp.disconnect(p_window->p_handle_priv);
+ if (p_window->p_handle)
+ sys->anw.winRelease(p_window->p_handle);
+ free(p_window);
+}
+
+static int AndroidWindow_UpdateCrop(vout_display_sys_t *sys,
+ android_window *p_window)
+{
+ if (!p_window->p_handle_priv)
+ return -1;
+
+ return sys->anwp.setCrop(p_window->p_handle_priv,
+ p_window->fmt.i_x_offset,
+ p_window->fmt.i_y_offset,
+ p_window->fmt.i_visible_width,
+ p_window->fmt.i_visible_height);
+}
+
+static unsigned int AndroidWindow_GetPicCount(vout_display_sys_t *sys,
+ android_window *p_window)
+{
+ VLC_UNUSED(sys);
+ return p_window->i_min_undequeued + p_window->i_pic_count;
+}
+
+static int AndroidWindow_SetSurface(vout_display_sys_t *sys,
+ android_window *p_window,
+ jobject jsurf)
+{
+ if (p_window->p_handle && jsurf != p_window->jsurf) {
+ if (p_window->p_handle_priv) {
+ sys->anwp.disconnect(p_window->p_handle_priv);
+ p_window->p_handle_priv = NULL;
+ }
+ sys->anw.winRelease(p_window->p_handle);
+ p_window->p_handle = NULL;
+ }
+
+ p_window->jsurf = jsurf;
+ if (!p_window->p_handle) {
+ JNIEnv *p_env;
+
+ jni_attach_thread(&p_env, THREAD_NAME);
+ if (!p_env)
+ return -1;
+ p_window->p_handle = sys->anw.winFromSurface(p_env, p_window->jsurf);
+ jni_detach_thread();
+ if (!p_window->p_handle)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int AndroidWindow_SetupANWP(vout_display_sys_t *sys,
+ android_window *p_window)
+{
+ if (!p_window->p_handle_priv)
+ p_window->p_handle_priv = sys->anwp.connect(p_window->p_handle);
+
+ if (!p_window->p_handle_priv)
+ goto error;
+
+ if (sys->anwp.setup(p_window->p_handle_priv,
+ p_window->fmt.i_width, p_window->fmt.i_height,
+ p_window->i_android_hal,
+ false, 0) != 0)
+ goto error;
+
+ sys->anwp.getMinUndequeued(p_window->p_handle_priv,
+ &p_window->i_min_undequeued);
+
+ if ((p_window->i_min_undequeued + p_window->i_pic_count) >
+ PRIV_WINDOW_MAX_BUFFER_COUNT)
+ p_window->i_pic_count = PRIV_WINDOW_MAX_BUFFER_COUNT
+ - p_window->i_min_undequeued;
+
+ if (sys->anwp.setBufferCount(p_window->p_handle_priv,
+ AndroidWindow_GetPicCount(sys, p_window)) != 0)
+ goto error;
+
+ if (sys->anwp.setOrientation(p_window->p_handle_priv,
+ p_window->i_angle) != 0)
+ goto error;
+
+ AndroidWindow_UpdateCrop(sys, p_window);
+
+ return 0;
+error:
+ if (p_window->p_handle_priv) {
+ sys->anwp.disconnect(p_window->p_handle_priv);
+ p_window->p_handle_priv = NULL;
+ }
+ p_window->b_use_priv = false;
+ if (p_window->i_angle != 0)
+ video_format_ApplyRotation(&p_window->fmt, &p_window->fmt);
+ p_window->i_angle = 0;
+ return -1;
+}
+
+static int AndroidWindow_SetupANW(vout_display_sys_t *sys,
+ android_window *p_window)
+{
+ int err;
+ bool configured;
+
+ p_window->i_pic_count = 1;
+ p_window->i_min_undequeued = 0;
+
+ /*
+ * anw.setBuffersGeometry is broken in gingerbread.
+ * use jni_ConfigureSurface to configure the surface on the java side
+ * synchronsouly.
+ * jni_ConfigureSurface return -1 when you don't need to call it (ie, after
+ * gingerbread).
+ * if jni_ConfigureSurface succeed, you need to get a new surface handle.
+ * That's why AndroidWindow_SetSurface is called again here.
+ */
+ err = jni_ConfigureSurface(p_window->jsurf,
+ p_window->fmt.i_width,
+ p_window->fmt.i_height,
+ p_window->i_android_hal,
+ &configured);
+ if (err == 0) {
+ if (configured) {
+ jobject jsurf = p_window->jsurf;
+ p_window->jsurf = NULL;
+ if (AndroidWindow_SetSurface(sys, p_window, jsurf) != 0)
+ return -1;
+ } else
+ return -1;
+ } else {
+ err = sys->anw.setBuffersGeometry(p_window->p_handle,
+ p_window->fmt.i_width,
+ p_window->fmt.i_height,
+ p_window->i_android_hal);
+ }
+ return err;
+}
+
+static int AndroidWindow_Setup(vout_display_sys_t *sys,
+ android_window *p_window,
+ unsigned int i_pic_count)
+{
+ int align_pixels;
+ picture_t *p_pic;
+
+ if (i_pic_count != 0)
+ p_window->i_pic_count = i_pic_count;
+
+ p_pic = PictureAlloc(sys, &p_window->fmt);
+ // For RGB (32 or 16) we need to align on 8 or 4 pixels, 16 pixels for YUV
+ align_pixels = (16 / p_pic->p[0].i_pixel_pitch) - 1;
+ p_window->fmt.i_height = p_pic->format.i_height;
+ p_window->fmt.i_width = (p_pic->format.i_width + align_pixels) & ~align_pixels;
+ picture_Release(p_pic);
+
+ if (!p_window->b_use_priv
+ || AndroidWindow_SetupANWP(sys, p_window) != 0) {
+ if (AndroidWindow_SetupANW(sys, p_window) != 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static void AndroidWindow_UnlockPicture(vout_display_sys_t *sys,
+ android_window *p_window,
+ picture_t *p_pic)
+{
+ picture_sys_t *p_picsys = p_pic->p_sys;
+
+ if (p_window->b_use_priv) {
+ int err = 0;
+ void *p_handle = p_picsys->priv.sw.p_handle;
+
+ if (p_handle == NULL)
+ return;
+
+ err = sys->anwp.unlockData(p_window->p_handle_priv, p_handle);
+
+ if (err == 0) {
+ if (p_picsys->b_render)
+ err = sys->anwp.queue(p_window->p_handle_priv, p_handle);
+ else
+ err = sys->anwp.cancel(p_window->p_handle_priv, p_handle);
+ }
+ } else
+ sys->anw.unlockAndPost(p_window->p_handle);
+}
+
+static int AndroidWindow_LockPicture(vout_display_sys_t *sys,
+ android_window *p_window,
+ picture_t *p_pic)
+{
+ picture_sys_t *p_picsys = p_pic->p_sys;
+
+ if (p_window->b_use_priv) {
+ void *p_handle;
+ int err;
+
+ err = sys->anwp.dequeue(p_window->p_handle_priv, &p_handle);
+ err = err == 0 ? sys->anwp.lock(p_window->p_handle_priv, p_handle) : err;
+ err = err == 0 ? sys->anwp.lockData(p_window->p_handle_priv,
+ p_handle,
+ &p_picsys->priv.sw.buf) : err;
+ if (err != 0)
+ return -1;
+ p_picsys->priv.sw.p_handle = p_handle;
+ } else {
+ if (sys->anw.winLock(p_window->p_handle,
+ &p_picsys->priv.sw.buf, NULL) != 0)
+ return -1;
+ }
+ if (p_picsys->priv.sw.buf.width < 0 ||
+ p_picsys->priv.sw.buf.height < 0 ||
+ (unsigned)p_picsys->priv.sw.buf.width < p_window->fmt.i_width ||
+ (unsigned)p_picsys->priv.sw.buf.height < p_window->fmt.i_height) {
+ AndroidWindow_UnlockPicture(sys, p_window, p_pic);
+ return -1;
+ }
+
+ p_pic->p[0].p_pixels = p_picsys->priv.sw.buf.bits;
+ p_pic->p[0].i_lines = p_picsys->priv.sw.buf.height;
+ p_pic->p[0].i_pitch = p_pic->p[0].i_pixel_pitch * p_picsys->priv.sw.buf.stride;
+
+ if (p_picsys->priv.sw.buf.format == PRIV_WINDOW_FORMAT_YV12)
+ SetupPictureYV12(p_pic, p_picsys->priv.sw.buf.stride);
+
+ return 0;
+}
+
+static int SetupWindowSurface(vout_display_sys_t *sys, unsigned i_pic_count)
+{
+ int err;
+ jobject jsurf = jni_LockAndGetAndroidJavaSurface();
+ err = AndroidWindow_SetSurface(sys, sys->p_window, jsurf);
+ jni_UnlockAndroidSurface();
+ err = err == 0 ? AndroidWindow_Setup(sys, sys->p_window, i_pic_count) : err;
+ return err;
+}
+
+static int SetupWindowSubtitleSurface(vout_display_sys_t *sys)
+{
+ int err;
+ jobject jsurf = jni_LockAndGetSubtitlesSurface();
+ err = AndroidWindow_SetSurface(sys, sys->p_sub_window, jsurf);
+ jni_UnlockAndroidSurface();
+ err = err == 0 ? AndroidWindow_Setup(sys, sys->p_sub_window, 1) : err;
+ return err;
+}
+
+static void SetRGBMask(video_format_t *p_fmt)
+{
+ switch(p_fmt->i_chroma) {
+ case VLC_CODEC_RGB16:
+ p_fmt->i_bmask = 0x0000001f;
+ p_fmt->i_gmask = 0x000007e0;
+ p_fmt->i_rmask = 0x0000f800;
+ break;
+
+ case VLC_CODEC_RGB32:
+ case VLC_CODEC_RGBA:
+ p_fmt->i_rmask = 0x000000ff;
+ p_fmt->i_gmask = 0x0000ff00;
+ p_fmt->i_bmask = 0x00ff0000;
+ break;
+ }
+}
+
+static int Open(vlc_object_t *p_this)
+{
+ vout_display_t *vd = (vout_display_t*)p_this;
+ vout_display_sys_t *sys;
+ video_format_t sub_fmt;
+
+ if (vout_display_IsWindowed(vd))
+ return VLC_EGENERIC;
+
+ /* Allocate structure */
+ vd->sys = sys = (struct vout_display_sys_t*)calloc(1, sizeof(*sys));
+ if (!sys)
+ return VLC_ENOMEM;
+
+ sys->p_library = LoadNativeWindowAPI(&sys->anw);
+ if (!sys->p_library) {
+ msg_Err(vd, "Could not initialize NativeWindow API.");
+ goto error;
+ }
+
+#ifdef USE_ANWP
+ if (LoadNativeWindowPrivAPI(&sys->anwp) == 0)
+ sys->b_has_anwp = true;
+ else
+ msg_Warn(vd, "Could not initialize NativeWindow Priv API.");
+#endif
+
+ if (vd->fmt.i_chroma != VLC_CODEC_ANDROID_OPAQUE) {
+ /* Setup chroma */
+ char *psz_fcc = var_InheritString(vd, CFG_PREFIX "chroma");
+ if (psz_fcc) {
+ vd->fmt.i_chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, psz_fcc);
+ free(psz_fcc);
+ } else
+ vd->fmt.i_chroma = VLC_CODEC_RGB32;
+
+ switch(vd->fmt.i_chroma) {
+ case VLC_CODEC_YV12:
+ /* avoid swscale usage by asking for I420 instead since the
+ * vout already has code to swap the buffers */
+ vd->fmt.i_chroma = VLC_CODEC_I420;
+ case VLC_CODEC_I420:
+ break;
+ case VLC_CODEC_RGB16:
+ case VLC_CODEC_RGB32:
+ case VLC_CODEC_RGBA:
+ SetRGBMask(&vd->fmt);
+ video_format_FixRgb(&vd->fmt);
+ break;
+ default:
+ goto error;
+ }
+
+ sys->p_window = AndroidWindow_New(sys, &vd->fmt, true);
+ if (!sys->p_window)
+ goto error;
+
+ if (SetupWindowSurface(sys, 0) != 0)
+ goto error;
+
+ /* use software rotation if we don't use private anw */
+ if (!sys->p_window->b_use_priv)
+ video_format_ApplyRotation(&vd->fmt, &vd->fmt);
+
+ msg_Dbg(vd, "using %s", sys->p_window->b_use_priv ? "ANWP" : "ANW");
+ } else {
+ /* vd->fmt.i_chroma == VLC_CODEC_ANDROID_OPAQUE */
+ sys->p_window = AndroidWindow_New(sys, &vd->fmt, false);
+ if (!sys->p_window)
+ goto error;
+ msg_Dbg(vd, "using opaque");
+ }
+
+ video_format_ApplyRotation(&sub_fmt, &vd->fmt);
+ sub_fmt.i_chroma = subpicture_chromas[0];
+ SetRGBMask(&sub_fmt);
+ video_format_FixRgb(&sub_fmt);
+ sys->p_sub_window = AndroidWindow_New(sys, &sub_fmt, false);
+ if (!sys->p_sub_window)
+ goto error;
+ FixSubtitleFormat(sys);
+
+ /* Export the subpicture capability of this vout. */
+ vd->info.subpicture_chromas = subpicture_chromas;
+
+ /* Setup vout_display */
+ vd->pool = Pool;
+ vd->prepare = NULL;
+ vd->display = Display;
+ vd->control = Control;
+ vd->manage = Manage;
+
+ /* Fix initial state */
+ vout_display_SendEventFullscreen(vd, false);
+
+ return VLC_SUCCESS;
+
+error:
+ 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;
+
+ if (!sys)
+ return;
+
+ if (sys->pool)
+ picture_pool_Release(sys->pool);
+ if (sys->p_window)
+ AndroidWindow_Destroy(sys, sys->p_window);
+
+ if (sys->p_sub_pic)
+ picture_Release(sys->p_sub_pic);
+ if (sys->p_spu_blend)
+ filter_DeleteBlend(sys->p_spu_blend);
+ if (sys->p_sub_window)
+ AndroidWindow_Destroy(sys, sys->p_sub_window);
+
+ if (sys->p_library)
+ dlclose(sys->p_library);
+
+ free(sys);
+}
+
+static int DefaultLockPicture(picture_t *p_pic)
+{
+ picture_sys_t *p_picsys = p_pic->p_sys;
+ vout_display_sys_t *sys = p_picsys->p_vd_sys;
+
+ return AndroidWindow_LockPicture(sys, sys->p_window, p_pic);
+}
+
+static void DefaultUnlockPicture(picture_t *p_pic)
+{
+ picture_sys_t *p_picsys = p_pic->p_sys;
+ vout_display_sys_t *sys = p_picsys->p_vd_sys;
+
+ AndroidWindow_UnlockPicture(sys, sys->p_window, p_pic);
+}
+
+static int LockPicture(picture_t *p_pic)
+{
+ picture_sys_t *p_picsys = p_pic->p_sys;
+
+ p_picsys->b_render = false;
+ if (p_picsys->pf_lock_pic)
+ return p_picsys->pf_lock_pic(p_pic);
+ return 0;
+}
+
+static void UnlockPicture(picture_t *p_pic)
+{
+ picture_sys_t *p_picsys = p_pic->p_sys;
+
+ if (p_picsys->pf_unlock_pic)
+ p_picsys->pf_unlock_pic(p_pic);
+}
+
+static picture_pool_t *PoolAlloc(vout_display_t *vd, unsigned requested_count)
+{
+ vout_display_sys_t *sys = vd->sys;
+ picture_pool_t *pool = NULL;
+ picture_t **pp_pics = NULL;
+ unsigned int i = 0;
+
+ if (vd->fmt.i_chroma != VLC_CODEC_ANDROID_OPAQUE) {
+ if (SetupWindowSurface(sys, requested_count) != 0)
+ goto error;
+
+ requested_count = AndroidWindow_GetPicCount(sys, sys->p_window);
+ msg_Dbg(vd, "PoolAlloc: request %d frames", requested_count);
+ } else {
+ requested_count = 31; // TODO:
+ }
+ UpdateWindowSize(&sys->p_window->fmt, sys->p_window->b_use_priv);
+
+ pp_pics = calloc(requested_count, sizeof(picture_t));
+
+ for (i = 0; i < requested_count; i++)
+ {
+ picture_t *p_pic = PictureAlloc(sys, &sys->p_window->fmt);
+ if (!p_pic)
+ goto error;
+ if (vd->fmt.i_chroma != VLC_CODEC_ANDROID_OPAQUE) {
+ p_pic->p_sys->pf_lock_pic = DefaultLockPicture;
+ p_pic->p_sys->pf_unlock_pic = DefaultUnlockPicture;
+ }
+
+ pp_pics[i] = p_pic;
+ }
+
+ picture_pool_configuration_t pool_cfg;
+ memset(&pool_cfg, 0, sizeof(pool_cfg));
+ pool_cfg.picture_count = requested_count;
+ pool_cfg.picture = pp_pics;
+ pool_cfg.lock = LockPicture;
+ pool_cfg.unlock = UnlockPicture;
+ pool = picture_pool_NewExtended(&pool_cfg);
+
+error:
+ if (!pool && pp_pics) {
+ for (unsigned j = 0; j < i; j++)
+ picture_Release(pp_pics[j]);
+ }
+ free(pp_pics);
+ return pool;
+}
+
+static void SubpictureDisplay(vout_display_t *vd, subpicture_t *subpicture)
+{
+ vout_display_sys_t *sys = vd->sys;
+
+ struct md5_s hash;
+ InitMD5(&hash);
+ if (subpicture) {
+ for (subpicture_region_t *r = subpicture->p_region; r != NULL; r = r->p_next) {
+ AddMD5(&hash, &r->i_x, sizeof(r->i_x));
+ AddMD5(&hash, &r->i_y, sizeof(r->i_y));
+ AddMD5(&hash, &r->fmt.i_visible_width, sizeof(r->fmt.i_visible_width));
+ AddMD5(&hash, &r->fmt.i_visible_height, sizeof(r->fmt.i_visible_height));
+ AddMD5(&hash, &r->fmt.i_x_offset, sizeof(r->fmt.i_x_offset));
+ AddMD5(&hash, &r->fmt.i_y_offset, sizeof(r->fmt.i_y_offset));
+ const int pixels_offset = r->fmt.i_y_offset * r->p_picture->p->i_pitch +
+ r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch;
+
+ for (unsigned int y = 0; y < r->fmt.i_visible_height; y++)
+ AddMD5(&hash, &r->p_picture->p->p_pixels[pixels_offset + y*r->p_picture->p->i_pitch], r->fmt.i_visible_width);
+ }
+ }
+ EndMD5(&hash);
+ if (!memcmp(hash.buf, sys->hash, 16))
+ return;
+ memcpy(sys->hash, hash.buf, 16);
+
+ if (AndroidWindow_LockPicture(sys, sys->p_sub_window, sys->p_sub_pic) != 0)
+ return;
+
+ /* Clear the subtitles surface. */
+ memset(sys->p_sub_pic->p[0].p_pixels, 0,
+ sys->p_sub_pic->p[0].i_pitch * sys->p_sub_pic->p[0].i_lines);
+
+ if (subpicture)
+ {
+ /* Allocate a blending filter if needed. */
+ if (unlikely(!sys->p_spu_blend))
+ sys->p_spu_blend = filter_NewBlend(VLC_OBJECT(vd),
+ &sys->p_sub_pic->format);
+ picture_BlendSubpicture(sys->p_sub_pic, sys->p_spu_blend, subpicture);
+ }
+ AndroidWindow_UnlockPicture(sys, sys->p_sub_window, sys->p_sub_pic);
+}
+
+static picture_pool_t *Pool(vout_display_t *vd, unsigned requested_count)
+{
+ vout_display_sys_t *sys = vd->sys;
+
+ if (sys->pool == NULL)
+ sys->pool = PoolAlloc(vd, requested_count);
+ return sys->pool;
+}
+
+static void Display(vout_display_t *vd, picture_t *picture,
+ subpicture_t *subpicture)
+{
+ vout_display_sys_t *sys = vd->sys;
+ picture_sys_t *p_picsys = picture->p_sys;
+
+ /* refcount lowers to 0, and pool_cfg.unlock is called */
+ p_picsys->b_render = true;
+ picture_Release(picture);
+
+ if (subpicture) {
+ if (!sys->p_sub_pic && SetupWindowSubtitleSurface(sys) == 0)
+ sys->p_sub_pic = PictureAlloc(sys, &sys->p_sub_window->fmt);
+
+ if (sys->p_sub_pic)
+ sys->b_has_subpictures = true;
+ }
+ /* As long as no subpicture was received, do not call
+ SubpictureDisplay since JNI calls and clearing the subtitles
+ surface are expensive operations. */
+ if (sys->b_has_subpictures)
+ {
+ SubpictureDisplay(vd, subpicture);
+ if (!subpicture)
+ {
+ /* The surface has been cleared and there is no new
+ subpicture to upload, do not clear again until a new
+ subpicture is received. */
+ sys->b_has_subpictures = false;
+ }
+ }
+
+ if (subpicture)
+ subpicture_Delete(subpicture);
+}
+
+static void CopySourceAspect(video_format_t *p_dest,
+ const video_format_t *p_src)
+{
+ p_dest->i_sar_num = p_src->i_sar_num;
+ p_dest->i_sar_den = p_src->i_sar_den;
+}
+
+static int Control(vout_display_t *vd, int query, va_list args)
+{
+ vout_display_sys_t *sys = vd->sys;
+
+ switch (query) {
+ case VOUT_DISPLAY_HIDE_MOUSE:
+ return VLC_SUCCESS;
+ case VOUT_DISPLAY_RESET_PICTURES:
+ {
+ if (sys->p_window->fmt.i_chroma == VLC_CODEC_ANDROID_OPAQUE)
+ return VLC_EGENERIC;
+
+ msg_Dbg(vd, "resetting pictures");
+
+ if (sys->pool != NULL)
+ {
+ picture_pool_Release(sys->pool);
+ sys->pool = NULL;
+ }
+ return VLC_SUCCESS;
+ }
+ case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
+ case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
+ {
+ const video_format_t *source;
+ video_format_t sub_fmt;
+
+ msg_Dbg(vd, "change source crop/aspect");
+
+ source = va_arg(args, const video_format_t *);
+ video_format_ApplyRotation(&sub_fmt, source);
+
+ if (query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) {
+ video_format_CopyCrop(&sys->p_window->fmt, source);
+ AndroidWindow_UpdateCrop(sys, sys->p_window);
+
+ video_format_CopyCrop(&sys->p_sub_window->fmt, &sub_fmt);
+ } else {
+ CopySourceAspect(&sys->p_window->fmt, source);
+
+ CopySourceAspect(&sys->p_sub_window->fmt, &sub_fmt);
+ }
+ UpdateWindowSize(&sys->p_window->fmt, sys->p_window->b_use_priv);
+ FixSubtitleFormat(sys);
+
+ if (sys->p_sub_pic) {
+ picture_Release(sys->p_sub_pic);
+ sys->p_sub_pic = NULL;
+ }
+ if (sys->p_spu_blend) {
+ filter_DeleteBlend(sys->p_spu_blend);
+ sys->p_spu_blend = NULL;
+ }
+
+ return VLC_SUCCESS;
+ }
+ default:
+ msg_Warn(vd, "Unknown request in android_window");
+ case VOUT_DISPLAY_CHANGE_ZOOM:
+ case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
+ case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
+ return VLC_EGENERIC;
+ }
+}
diff --git a/modules/video_output/android/android_window.h b/modules/video_output/android/android_window.h
new file mode 100644
index 0000000..68101c8
--- /dev/null
+++ b/modules/video_output/android/android_window.h
@@ -0,0 +1,59 @@
+/*****************************************************************************
+ * android_window.c: Android video output module
+ *****************************************************************************
+ * Copyright (C) 2014 VLC authors and VideoLAN
+ *
+ * Authors: Thomas Guillem <thomas at gllm.fr>
+ * Felix Abecassis <felix.abecassis at gmail.com>
+ * Ming Hu <tewilove at gmail.com>
+ * Ludovic Fauvet <etix at l0cal.com>
+ * Sébastien Toque <xilasz 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_WINDOW_H_
+#define ANDROID_WINDOW_H_
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_vout_display.h>
+#include <android/native_window.h>
+
+struct picture_sys_t
+{
+ vout_display_sys_t *p_vd_sys;
+
+ int (*pf_lock_pic)(picture_t *);
+ void (*pf_unlock_pic)(picture_t *);
+
+ union {
+ struct {
+ decoder_t *p_dec;
+ uint32_t i_index;
+ bool b_valid;
+ } hw;
+ struct {
+ void *p_handle;
+ ANativeWindow_Buffer buf;
+ } sw;
+ } priv;
+ bool b_render;
+};
+
+#endif
diff --git a/modules/video_output/android/opaque.c b/modules/video_output/android/opaque.c
deleted file mode 100644
index b06feb4..0000000
--- a/modules/video_output/android/opaque.c
+++ /dev/null
@@ -1,353 +0,0 @@
-/*****************************************************************************
- * 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 <vlc_filter.h>
-#include <vlc_md5.h>
-
-#include <dlfcn.h>
-
-#include "../codec/omxil/android_opaque.h"
-#include "utils.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()
-
-#define THREAD_NAME "vout_mediacodec"
-extern int jni_attach_thread(JNIEnv **env, const char *thread_name);
-extern void jni_detach_thread();
-extern jobject jni_LockAndGetSubtitlesSurface();
-extern void jni_UnlockAndroidSurface();
-
-static const vlc_fourcc_t subpicture_chromas[] =
-{
- VLC_CODEC_RGBA,
- 0
-};
-
-/*****************************************************************************
- * 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;
-
- void *p_library;
- native_window_api_t native_window;
-
- jobject jsurf;
- ANativeWindow *window;
-
- video_format_t fmt;
-
- filter_t *p_spu_blend;
- picture_t *subtitles_picture;
-
- bool b_has_subpictures;
-
- uint8_t hash[16];
-};
-
-static void DisplaySubpicture(vout_display_t *vd, subpicture_t *subpicture)
-{
- vout_display_sys_t *sys = vd->sys;
-
- struct md5_s hash;
- InitMD5(&hash);
- if (subpicture) {
- for (subpicture_region_t *r = subpicture->p_region; r != NULL; r = r->p_next) {
- AddMD5(&hash, &r->i_x, sizeof(r->i_x));
- AddMD5(&hash, &r->i_y, sizeof(r->i_y));
- AddMD5(&hash, &r->fmt.i_visible_width, sizeof(r->fmt.i_visible_width));
- AddMD5(&hash, &r->fmt.i_visible_height, sizeof(r->fmt.i_visible_height));
- AddMD5(&hash, &r->fmt.i_x_offset, sizeof(r->fmt.i_x_offset));
- AddMD5(&hash, &r->fmt.i_y_offset, sizeof(r->fmt.i_y_offset));
- const int pixels_offset = r->fmt.i_y_offset * r->p_picture->p->i_pitch +
- r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch;
-
- for (int y = 0; y < r->fmt.i_visible_height; y++)
- AddMD5(&hash, &r->p_picture->p->p_pixels[pixels_offset + y*r->p_picture->p->i_pitch], r->fmt.i_visible_width);
- }
- }
- EndMD5(&hash);
- if (!memcmp(hash.buf, sys->hash, 16))
- return;
- memcpy(sys->hash, hash.buf, 16);
-
- jobject jsurf = jni_LockAndGetSubtitlesSurface();
- if (sys->window && jsurf != sys->jsurf)
- {
- sys->native_window.winRelease(sys->window);
- sys->window = NULL;
- }
- sys->jsurf = jsurf;
- if (!sys->window)
- {
- JNIEnv *p_env;
- jni_attach_thread(&p_env, THREAD_NAME);
- sys->window = sys->native_window.winFromSurface(p_env, jsurf);
- jni_detach_thread();
- }
-
- ANativeWindow_Buffer buf = { 0 };
- int32_t err = sys->native_window.winLock(sys->window, &buf, NULL);
- if (err) {
- jni_UnlockAndroidSurface();
- return;
- }
-
- if (buf.width >= sys->fmt.i_width && buf.height >= sys->fmt.i_height)
- {
- /* Wrap the NativeWindow corresponding to the subtitles surface in a picture_t */
- picture_t *picture = sys->subtitles_picture;
- picture->p[0].p_pixels = (uint8_t*)buf.bits;
- picture->p[0].i_lines = buf.height;
- picture->p[0].i_pitch = picture->p[0].i_pixel_pitch * buf.stride;
- /* Clear the subtitles surface. */
- memset(picture->p[0].p_pixels, 0, picture->p[0].i_pitch * picture->p[0].i_lines);
- if (subpicture)
- {
- /* Allocate a blending filter if needed. */
- if (unlikely(!sys->p_spu_blend))
- sys->p_spu_blend = filter_NewBlend(VLC_OBJECT(vd), &picture->format);
- picture_BlendSubpicture(picture, sys->p_spu_blend, subpicture);
- }
- }
-
- sys->native_window.unlockAndPost(sys->window);
- jni_UnlockAndroidSurface();
-}
-
-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;
- if (vout_display_IsWindowed(vd))
- return VLC_EGENERIC;
-
- /* Allocate structure */
- vout_display_sys_t *sys = (struct vout_display_sys_t*)calloc(1, sizeof(*sys));
- if (!sys)
- return VLC_ENOMEM;
-
- sys->p_library = LoadNativeWindowAPI(&sys->native_window);
- if (!sys->p_library)
- {
- free(sys);
- msg_Err(vd, "Could not initialize NativeWindow API.");
- return VLC_EGENERIC;
- }
- sys->fmt = fmt;
- video_format_t subpicture_format = sys->fmt;
- subpicture_format.i_chroma = VLC_CODEC_RGBA;
- /* Create a RGBA picture for rendering subtitles. */
- picture_resource_t rsc;
- memset(&rsc, 0, sizeof(rsc));
- sys->subtitles_picture = picture_NewFromResource(&subpicture_format, &rsc);
-
- /* Export the subpicture capability of this vout. */
- vd->info.subpicture_chromas = subpicture_chromas;
-
- 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 = Manage;
-
- /* 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_Release(sys->pool);
- if (sys->window)
- sys->native_window.winRelease(sys->window);
- dlclose(sys->p_library);
- if (sys->subtitles_picture)
- picture_Release(sys->subtitles_picture);
- if (sys->p_spu_blend)
- filter_DeleteBlend(sys->p_spu_blend);
- 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;
- vout_display_sys_t *sys = vd->sys;
- void (*display_callback)(picture_sys_t*) = p_picsys->pf_display_callback;
- if (display_callback)
- display_callback(p_picsys);
-
- if (subpicture && sys->subtitles_picture)
- sys->b_has_subpictures = true;
- /* As long as no subpicture was received, do not call
- DisplaySubpicture since JNI calls and clearing the subtitles
- surface are expensive operations. */
- if (sys->b_has_subpictures)
- {
- DisplaySubpicture(vd, subpicture);
- if (!subpicture)
- {
- /* The surface has been cleared and there is no new
- subpicture to upload, do not clear again until a new
- subpicture is received. */
- sys->b_has_subpictures = false;
- }
- }
-
- /* refcount lowers to 0, and pool_cfg.unlock is called */
- picture_Release(picture);
- if (subpicture)
- subpicture_Delete(subpicture);
-}
-
-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:
- return VLC_EGENERIC;
- }
-}
--
2.1.1
More information about the vlc-devel
mailing list