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)
54 extern jobject jni_LockAndGetSubtitlesSurface();
55 extern void jni_UnlockAndroidSurface();
57 static const vlc_fourcc_t subpicture_chromas[] =
63 /*****************************************************************************
65 *****************************************************************************/
67 static picture_pool_t *Pool (vout_display_t *, unsigned);
68 static void Display(vout_display_t *, picture_t *, subpicture_t *);
69 static int Control(vout_display_t *, int, va_list);
71 struct vout_display_sys_t
76 native_window_api_t native_window;
79 ANativeWindow *window;
83 filter_t *p_spu_blend;
84 picture_t *subtitles_picture;
86 bool b_has_subpictures;
91 static void DisplaySubpicture(vout_display_t *vd, subpicture_t *subpicture)
93 vout_display_sys_t *sys = vd->sys;
98 for (subpicture_region_t *r = subpicture->p_region; r != NULL; r = r->p_next) {
99 AddMD5(&hash, &r->i_x, sizeof(r->i_x));
100 AddMD5(&hash, &r->i_y, sizeof(r->i_y));
101 AddMD5(&hash, &r->fmt.i_visible_width, sizeof(r->fmt.i_visible_width));
102 AddMD5(&hash, &r->fmt.i_visible_height, sizeof(r->fmt.i_visible_height));
103 AddMD5(&hash, &r->fmt.i_x_offset, sizeof(r->fmt.i_x_offset));
104 AddMD5(&hash, &r->fmt.i_y_offset, sizeof(r->fmt.i_y_offset));
105 const int pixels_offset = r->fmt.i_y_offset * r->p_picture->p->i_pitch +
106 r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch;
108 for (int y = 0; y < r->fmt.i_visible_height; y++)
109 AddMD5(&hash, &r->p_picture->p->p_pixels[pixels_offset + y*r->p_picture->p->i_pitch], r->fmt.i_visible_width);
113 if (!memcmp(hash.buf, sys->hash, 16))
115 memcpy(sys->hash, hash.buf, 16);
117 jobject jsurf = jni_LockAndGetSubtitlesSurface();
118 if (sys->window && jsurf != sys->jsurf)
120 sys->native_window.winRelease(sys->window);
127 (*myVm)->AttachCurrentThread(myVm, &p_env, NULL);
128 sys->window = sys->native_window.winFromSurface(p_env, jsurf);
129 (*myVm)->DetachCurrentThread(myVm);
132 ANativeWindow_Buffer buf = { 0 };
133 sys->native_window.winLock(sys->window, &buf, NULL);
135 if (buf.width >= sys->fmt.i_width && buf.height >= sys->fmt.i_height)
137 /* Wrap the NativeWindow corresponding to the subtitles surface in a picture_t */
138 picture_t *picture = sys->subtitles_picture;
139 picture->p[0].p_pixels = (uint8_t*)buf.bits;
140 picture->p[0].i_lines = buf.height;
141 picture->p[0].i_pitch = picture->p[0].i_pixel_pitch * buf.stride;
142 /* Clear the subtitles surface. */
143 memset(picture->p[0].p_pixels, 0, picture->p[0].i_pitch * picture->p[0].i_lines);
146 /* Allocate a blending filter if needed. */
147 if (unlikely(!sys->p_spu_blend))
148 sys->p_spu_blend = filter_NewBlend(VLC_OBJECT(vd), &picture->format);
149 picture_BlendSubpicture(picture, sys->p_spu_blend, subpicture);
153 sys->native_window.unlockAndPost(sys->window);
154 jni_UnlockAndroidSurface();
157 static int LockSurface(picture_t *);
158 static void UnlockSurface(picture_t *);
160 /* We need to allocate a picture pool of more than 30 buffers in order
161 * to be connected directly to the decoder without any intermediate
165 static int Open(vlc_object_t *p_this)
167 vout_display_t *vd = (vout_display_t*)p_this;
169 video_format_t fmt = vd->fmt;
171 if (fmt.i_chroma != VLC_CODEC_ANDROID_OPAQUE)
174 /* Allocate structure */
175 vout_display_sys_t *sys = (struct vout_display_sys_t*)calloc(1, sizeof(*sys));
179 sys->p_library = LoadNativeWindowAPI(&sys->native_window);
183 msg_Err(vd, "Could not initialize NativeWindow API.");
187 video_format_t subpicture_format = sys->fmt;
188 subpicture_format.i_chroma = VLC_CODEC_RGBA;
189 /* Create a RGBA picture for rendering subtitles. */
190 sys->subtitles_picture = picture_NewFromFormat(&subpicture_format);
192 /* Export the subpicture capability of this vout. */
193 vd->info.subpicture_chromas = subpicture_chromas;
195 int i_pictures = POOL_SIZE;
196 picture_t** pictures = calloc(sizeof(*pictures), i_pictures);
199 for (int i = 0; i < i_pictures; i++)
201 picture_sys_t *p_picsys = calloc(1, sizeof(*p_picsys));
202 if (unlikely(p_picsys == NULL))
205 picture_resource_t resource = { .p_sys = p_picsys };
206 picture_t *picture = picture_NewFromResource(&fmt, &resource);
212 pictures[i] = picture;
215 /* Wrap it into a picture pool */
216 picture_pool_configuration_t pool_cfg;
217 memset(&pool_cfg, 0, sizeof(pool_cfg));
218 pool_cfg.picture_count = i_pictures;
219 pool_cfg.picture = pictures;
220 pool_cfg.lock = LockSurface;
221 pool_cfg.unlock = UnlockSurface;
223 sys->pool = picture_pool_NewExtended(&pool_cfg);
226 for (int i = 0; i < i_pictures; i++)
227 picture_Release(pictures[i]);
231 /* Setup vout_display */
235 vd->display = Display;
236 vd->control = Control;
240 /* Fix initial state */
241 vout_display_SendEventFullscreen(vd, false);
251 static void Close(vlc_object_t *p_this)
253 vout_display_t *vd = (vout_display_t *)p_this;
254 vout_display_sys_t *sys = vd->sys;
256 picture_pool_Delete(sys->pool);
258 sys->native_window.winRelease(sys->window);
259 dlclose(sys->p_library);
260 picture_Release(sys->subtitles_picture);
261 if (sys->p_spu_blend)
262 filter_DeleteBlend(sys->p_spu_blend);
266 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
270 return vd->sys->pool;
273 static int LockSurface(picture_t *picture)
280 static void UnlockSurface(picture_t *picture)
282 picture_sys_t *p_picsys = picture->p_sys;
283 void (*unlock_callback)(picture_sys_t*) = p_picsys->pf_unlock_callback;
285 unlock_callback(p_picsys);
288 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
291 VLC_UNUSED(subpicture);
293 picture_sys_t *p_picsys = picture->p_sys;
294 vout_display_sys_t *sys = vd->sys;
295 void (*display_callback)(picture_sys_t*) = p_picsys->pf_display_callback;
296 if (display_callback)
297 display_callback(p_picsys);
300 sys->b_has_subpictures = true;
301 /* As long as no subpicture was received, do not call
302 DisplaySubpicture since JNI calls and clearing the subtitles
303 surface are expensive operations. */
304 if (sys->b_has_subpictures)
306 DisplaySubpicture(vd, subpicture);
309 /* The surface has been cleared and there is no new
310 subpicture to upload, do not clear again until a new
311 subpicture is received. */
312 sys->b_has_subpictures = false;
316 /* refcount lowers to 0, and pool_cfg.unlock is called */
317 picture_Release(picture);
319 subpicture_Delete(subpicture);
322 static int Control(vout_display_t *vd, int query, va_list args)
327 case VOUT_DISPLAY_HIDE_MOUSE:
331 msg_Err(vd, "Unknown request in vout mediacodec display");
333 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
334 case VOUT_DISPLAY_CHANGE_FULLSCREEN:
335 case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
336 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
337 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
338 case VOUT_DISPLAY_CHANGE_ZOOM:
339 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
340 case VOUT_DISPLAY_GET_OPENGL: