1 /*****************************************************************************
2 * opaque.c: Android video output module using direct rendering with
4 *****************************************************************************
5 * Copyright (C) 2013 Felix Abecassis
7 * Authors: Felix Abecassis <felix.abecassis@gmail.com>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
28 #include <vlc_common.h>
29 #include <vlc_plugin.h>
30 #include <vlc_vout_display.h>
31 #include <vlc_picture_pool.h>
32 #include <vlc_filter.h>
37 #include "../codec/omxil/android_opaque.h"
40 static int Open (vlc_object_t *);
41 static void Close(vlc_object_t *);
44 set_category(CAT_VIDEO)
45 set_subcategory(SUBCAT_VIDEO_VOUT)
46 set_shortname("vout_mediacodec")
47 set_description(N_("Android MediaCodec direct rendering video output"))
48 set_capability("vout display", 200)
49 add_shortcut("androidsurface", "android")
50 set_callbacks(Open, Close)
53 #define THREAD_NAME "vout_mediacodec"
54 extern int jni_attach_thread(JNIEnv **env, const char *thread_name);
55 extern void jni_detach_thread();
56 extern jobject jni_LockAndGetSubtitlesSurface();
57 extern void jni_UnlockAndroidSurface();
59 static const vlc_fourcc_t subpicture_chromas[] =
65 /*****************************************************************************
67 *****************************************************************************/
69 static picture_pool_t *Pool (vout_display_t *, unsigned);
70 static void Display(vout_display_t *, picture_t *, subpicture_t *);
71 static int Control(vout_display_t *, int, va_list);
73 struct vout_display_sys_t
78 native_window_api_t native_window;
81 ANativeWindow *window;
85 filter_t *p_spu_blend;
86 picture_t *subtitles_picture;
88 bool b_has_subpictures;
93 static void DisplaySubpicture(vout_display_t *vd, subpicture_t *subpicture)
95 vout_display_sys_t *sys = vd->sys;
100 for (subpicture_region_t *r = subpicture->p_region; r != NULL; r = r->p_next) {
101 AddMD5(&hash, &r->i_x, sizeof(r->i_x));
102 AddMD5(&hash, &r->i_y, sizeof(r->i_y));
103 AddMD5(&hash, &r->fmt.i_visible_width, sizeof(r->fmt.i_visible_width));
104 AddMD5(&hash, &r->fmt.i_visible_height, sizeof(r->fmt.i_visible_height));
105 AddMD5(&hash, &r->fmt.i_x_offset, sizeof(r->fmt.i_x_offset));
106 AddMD5(&hash, &r->fmt.i_y_offset, sizeof(r->fmt.i_y_offset));
107 const int pixels_offset = r->fmt.i_y_offset * r->p_picture->p->i_pitch +
108 r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch;
110 for (int y = 0; y < r->fmt.i_visible_height; y++)
111 AddMD5(&hash, &r->p_picture->p->p_pixels[pixels_offset + y*r->p_picture->p->i_pitch], r->fmt.i_visible_width);
115 if (!memcmp(hash.buf, sys->hash, 16))
117 memcpy(sys->hash, hash.buf, 16);
119 jobject jsurf = jni_LockAndGetSubtitlesSurface();
120 if (sys->window && jsurf != sys->jsurf)
122 sys->native_window.winRelease(sys->window);
129 jni_attach_thread(&p_env, THREAD_NAME);
130 sys->window = sys->native_window.winFromSurface(p_env, jsurf);
134 ANativeWindow_Buffer buf = { 0 };
135 int32_t err = sys->native_window.winLock(sys->window, &buf, NULL);
137 jni_UnlockAndroidSurface();
141 if (buf.width >= sys->fmt.i_width && buf.height >= sys->fmt.i_height)
143 /* Wrap the NativeWindow corresponding to the subtitles surface in a picture_t */
144 picture_t *picture = sys->subtitles_picture;
145 picture->p[0].p_pixels = (uint8_t*)buf.bits;
146 picture->p[0].i_lines = buf.height;
147 picture->p[0].i_pitch = picture->p[0].i_pixel_pitch * buf.stride;
148 /* Clear the subtitles surface. */
149 memset(picture->p[0].p_pixels, 0, picture->p[0].i_pitch * picture->p[0].i_lines);
152 /* Allocate a blending filter if needed. */
153 if (unlikely(!sys->p_spu_blend))
154 sys->p_spu_blend = filter_NewBlend(VLC_OBJECT(vd), &picture->format);
155 picture_BlendSubpicture(picture, sys->p_spu_blend, subpicture);
159 sys->native_window.unlockAndPost(sys->window);
160 jni_UnlockAndroidSurface();
163 static int LockSurface(picture_t *);
164 static void UnlockSurface(picture_t *);
166 /* We need to allocate a picture pool of more than 30 buffers in order
167 * to be connected directly to the decoder without any intermediate
171 static int Open(vlc_object_t *p_this)
173 vout_display_t *vd = (vout_display_t*)p_this;
175 video_format_t fmt = vd->fmt;
177 if (fmt.i_chroma != VLC_CODEC_ANDROID_OPAQUE)
179 if (vout_display_IsWindowed(vd))
182 /* Allocate structure */
183 vout_display_sys_t *sys = (struct vout_display_sys_t*)calloc(1, sizeof(*sys));
187 sys->p_library = LoadNativeWindowAPI(&sys->native_window);
191 msg_Err(vd, "Could not initialize NativeWindow API.");
195 video_format_t subpicture_format = sys->fmt;
196 subpicture_format.i_chroma = VLC_CODEC_RGBA;
197 /* Create a RGBA picture for rendering subtitles. */
198 picture_resource_t rsc;
199 memset(&rsc, 0, sizeof(rsc));
200 sys->subtitles_picture = picture_NewFromResource(&subpicture_format, &rsc);
202 /* Export the subpicture capability of this vout. */
203 vd->info.subpicture_chromas = subpicture_chromas;
205 int i_pictures = POOL_SIZE;
206 picture_t** pictures = calloc(sizeof(*pictures), i_pictures);
209 for (int i = 0; i < i_pictures; i++)
211 picture_sys_t *p_picsys = calloc(1, sizeof(*p_picsys));
212 if (unlikely(p_picsys == NULL))
215 picture_resource_t resource = { .p_sys = p_picsys };
216 picture_t *picture = picture_NewFromResource(&fmt, &resource);
222 pictures[i] = picture;
225 /* Wrap it into a picture pool */
226 picture_pool_configuration_t pool_cfg;
227 memset(&pool_cfg, 0, sizeof(pool_cfg));
228 pool_cfg.picture_count = i_pictures;
229 pool_cfg.picture = pictures;
230 pool_cfg.lock = LockSurface;
231 pool_cfg.unlock = UnlockSurface;
233 sys->pool = picture_pool_NewExtended(&pool_cfg);
236 for (int i = 0; i < i_pictures; i++)
237 picture_Release(pictures[i]);
241 /* Setup vout_display */
245 vd->display = Display;
246 vd->control = Control;
250 /* Fix initial state */
251 vout_display_SendEventFullscreen(vd, false);
261 static void Close(vlc_object_t *p_this)
263 vout_display_t *vd = (vout_display_t *)p_this;
264 vout_display_sys_t *sys = vd->sys;
266 picture_pool_Delete(sys->pool);
268 sys->native_window.winRelease(sys->window);
269 dlclose(sys->p_library);
270 if (sys->subtitles_picture)
271 picture_Release(sys->subtitles_picture);
272 if (sys->p_spu_blend)
273 filter_DeleteBlend(sys->p_spu_blend);
277 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
281 return vd->sys->pool;
284 static int LockSurface(picture_t *picture)
291 static void UnlockSurface(picture_t *picture)
293 picture_sys_t *p_picsys = picture->p_sys;
294 void (*unlock_callback)(picture_sys_t*) = p_picsys->pf_unlock_callback;
296 unlock_callback(p_picsys);
299 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
302 VLC_UNUSED(subpicture);
304 picture_sys_t *p_picsys = picture->p_sys;
305 vout_display_sys_t *sys = vd->sys;
306 void (*display_callback)(picture_sys_t*) = p_picsys->pf_display_callback;
307 if (display_callback)
308 display_callback(p_picsys);
310 if (subpicture && sys->subtitles_picture)
311 sys->b_has_subpictures = true;
312 /* As long as no subpicture was received, do not call
313 DisplaySubpicture since JNI calls and clearing the subtitles
314 surface are expensive operations. */
315 if (sys->b_has_subpictures)
317 DisplaySubpicture(vd, subpicture);
320 /* The surface has been cleared and there is no new
321 subpicture to upload, do not clear again until a new
322 subpicture is received. */
323 sys->b_has_subpictures = false;
327 /* refcount lowers to 0, and pool_cfg.unlock is called */
328 picture_Release(picture);
330 subpicture_Delete(subpicture);
333 static int Control(vout_display_t *vd, int query, va_list args)
338 case VOUT_DISPLAY_HIDE_MOUSE:
342 msg_Err(vd, "Unknown request in vout mediacodec display");
344 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
345 case VOUT_DISPLAY_CHANGE_FULLSCREEN:
346 case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
347 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
348 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
349 case VOUT_DISPLAY_CHANGE_ZOOM:
350 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: