]> git.sesse.net Git - vlc/blob - modules/video_output/android/opaque.c
build system: fix object names for vout_ios2 and ios dialog provider
[vlc] / modules / video_output / android / opaque.c
1 /*****************************************************************************
2  * opaque.c: Android video output module using direct rendering with
3  * opaque buffers
4  *****************************************************************************
5  * Copyright (C) 2013 Felix Abecassis
6  *
7  * Authors: Felix Abecassis <felix.abecassis@gmail.com>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
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>
33 #include <vlc_md5.h>
34
35 #include <dlfcn.h>
36
37 #include "../codec/omxil/android_opaque.h"
38 #include "utils.h"
39
40 static int  Open (vlc_object_t *);
41 static void Close(vlc_object_t *);
42
43 vlc_module_begin()
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)
51 vlc_module_end()
52
53 extern JavaVM *myVm;
54 extern jobject jni_LockAndGetSubtitlesSurface();
55 extern void  jni_UnlockAndroidSurface();
56
57 static const vlc_fourcc_t subpicture_chromas[] =
58 {
59     VLC_CODEC_RGBA,
60     0
61 };
62
63 /*****************************************************************************
64  * Local prototypes
65  *****************************************************************************/
66
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);
70
71 struct vout_display_sys_t
72 {
73     picture_pool_t *pool;
74
75     void *p_library;
76     native_window_api_t native_window;
77
78     jobject jsurf;
79     ANativeWindow *window;
80
81     video_format_t fmt;
82
83     filter_t *p_spu_blend;
84     picture_t *subtitles_picture;
85
86     bool b_has_subpictures;
87
88     uint8_t hash[16];
89 };
90
91 static void DisplaySubpicture(vout_display_t *vd, subpicture_t *subpicture)
92 {
93     vout_display_sys_t *sys = vd->sys;
94
95     struct md5_s hash;
96     InitMD5(&hash);
97     if (subpicture) {
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;
107
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);
110         }
111     }
112     EndMD5(&hash);
113     if (!memcmp(hash.buf, sys->hash, 16))
114         return;
115     memcpy(sys->hash, hash.buf, 16);
116
117     jobject jsurf = jni_LockAndGetSubtitlesSurface();
118     if (sys->window && jsurf != sys->jsurf)
119     {
120         sys->native_window.winRelease(sys->window);
121         sys->window = NULL;
122     }
123     sys->jsurf = jsurf;
124     if (!sys->window)
125     {
126         JNIEnv *p_env;
127         (*myVm)->AttachCurrentThread(myVm, &p_env, NULL);
128         sys->window = sys->native_window.winFromSurface(p_env, jsurf);
129         (*myVm)->DetachCurrentThread(myVm);
130     }
131
132     ANativeWindow_Buffer buf = { 0 };
133     sys->native_window.winLock(sys->window, &buf, NULL);
134
135     if (buf.width >= sys->fmt.i_width && buf.height >= sys->fmt.i_height)
136     {
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);
144         if (subpicture)
145         {
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);
150         }
151     }
152
153     sys->native_window.unlockAndPost(sys->window);
154     jni_UnlockAndroidSurface();
155 }
156
157 static int  LockSurface(picture_t *);
158 static void UnlockSurface(picture_t *);
159
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
162  * buffer pool. */
163 #define POOL_SIZE 31
164
165 static int Open(vlc_object_t *p_this)
166 {
167     vout_display_t *vd = (vout_display_t*)p_this;
168
169     video_format_t fmt = vd->fmt;
170
171     if (fmt.i_chroma != VLC_CODEC_ANDROID_OPAQUE)
172         return VLC_EGENERIC;
173
174     /* Allocate structure */
175     vout_display_sys_t *sys = (struct vout_display_sys_t*)calloc(1, sizeof(*sys));
176     if (!sys)
177         return VLC_ENOMEM;
178
179     sys->p_library = LoadNativeWindowAPI(&sys->native_window);
180     if (!sys->p_library)
181     {
182         free(sys);
183         msg_Err(vd, "Could not initialize NativeWindow API.");
184         return VLC_EGENERIC;
185     }
186     sys->fmt = fmt;
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);
191
192     /* Export the subpicture capability of this vout. */
193     vd->info.subpicture_chromas = subpicture_chromas;
194
195     int i_pictures = POOL_SIZE;
196     picture_t** pictures = calloc(sizeof(*pictures), i_pictures);
197     if (!pictures)
198         goto error;
199     for (int i = 0; i < i_pictures; i++)
200     {
201         picture_sys_t *p_picsys = calloc(1, sizeof(*p_picsys));
202         if (unlikely(p_picsys == NULL))
203             goto error;
204
205         picture_resource_t resource = { .p_sys = p_picsys };
206         picture_t *picture = picture_NewFromResource(&fmt, &resource);
207         if (!picture)
208         {
209             free(p_picsys);
210             goto error;
211         }
212         pictures[i] = picture;
213     }
214
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;
222
223     sys->pool = picture_pool_NewExtended(&pool_cfg);
224     if (!sys->pool)
225     {
226         for (int i = 0; i < i_pictures; i++)
227             picture_Release(pictures[i]);
228         goto error;
229     }
230
231     /* Setup vout_display */
232     vd->sys     = sys;
233     vd->fmt     = fmt;
234     vd->pool    = Pool;
235     vd->display = Display;
236     vd->control = Control;
237     vd->prepare = NULL;
238     vd->manage  = NULL;
239
240     /* Fix initial state */
241     vout_display_SendEventFullscreen(vd, false);
242
243     return VLC_SUCCESS;
244
245 error:
246     free(pictures);
247     Close(p_this);
248     return VLC_ENOMEM;
249 }
250
251 static void Close(vlc_object_t *p_this)
252 {
253     vout_display_t *vd = (vout_display_t *)p_this;
254     vout_display_sys_t *sys = vd->sys;
255
256     picture_pool_Delete(sys->pool);
257     if (sys->window)
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);
263     free(sys);
264 }
265
266 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
267 {
268     VLC_UNUSED(count);
269
270     return vd->sys->pool;
271 }
272
273 static int LockSurface(picture_t *picture)
274 {
275     VLC_UNUSED(picture);
276
277     return VLC_SUCCESS;
278 }
279
280 static void UnlockSurface(picture_t *picture)
281 {
282     picture_sys_t *p_picsys = picture->p_sys;
283     void (*unlock_callback)(picture_sys_t*) = p_picsys->pf_unlock_callback;
284     if (unlock_callback)
285         unlock_callback(p_picsys);
286 }
287
288 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
289 {
290     VLC_UNUSED(vd);
291     VLC_UNUSED(subpicture);
292
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);
298
299     if (subpicture)
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)
305     {
306         DisplaySubpicture(vd, subpicture);
307         if (!subpicture)
308         {
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;
313         }
314     }
315
316     /* refcount lowers to 0, and pool_cfg.unlock is called */
317     picture_Release(picture);
318     if (subpicture)
319         subpicture_Delete(subpicture);
320 }
321
322 static int Control(vout_display_t *vd, int query, va_list args)
323 {
324     VLC_UNUSED(args);
325
326     switch (query) {
327     case VOUT_DISPLAY_HIDE_MOUSE:
328         return VLC_SUCCESS;
329
330     default:
331         msg_Err(vd, "Unknown request in vout mediacodec display");
332
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:
341         return VLC_EGENERIC;
342     }
343 }