]> git.sesse.net Git - vlc/blob - modules/video_output/mmal.c
fourcc: remove unsupported RGBT and RGBA16
[vlc] / modules / video_output / mmal.c
1 /*****************************************************************************
2  * mmal.c: MMAL-based vout plugin for Raspberry Pi
3  *****************************************************************************
4  * Copyright © 2014 jusst technologies GmbH
5  * $Id$
6  *
7  * Authors: Dennis Hamester <dennis.hamester@gmail.com>
8  *          Julian Scheel <julian@jusst.de>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <math.h>
30
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_threads.h>
34 #include <vlc_vout_display.h>
35
36 #include <bcm_host.h>
37 #include <interface/mmal/mmal.h>
38 #include <interface/mmal/util/mmal_util.h>
39 #include <interface/mmal/util/mmal_default_components.h>
40 #include <interface/vmcs_host/vc_tvservice.h>
41 #include <interface/vmcs_host/vc_dispmanx.h>
42
43 /* This value must match the define in codec/mmal.c
44  * Think twice before changing this. Incorrect values cause havoc.
45  */
46 #define NUM_ACTUAL_OPAQUE_BUFFERS 40
47
48 #define MAX_BUFFERS_IN_TRANSIT 2
49 #define VC_TV_MAX_MODE_IDS 127
50
51 #define MMAL_LAYER_NAME "mmal-layer"
52 #define MMAL_LAYER_TEXT N_("VideoCore layer where the video is displayed.")
53 #define MMAL_LAYER_LONGTEXT N_("VideoCore layer where the video is displayed. Subpictures are displayed directly above and a black background directly below.")
54
55 #define MMAL_ADJUST_REFRESHRATE_NAME "mmal-adjust-refreshrate"
56 #define MMAL_ADJUST_REFRESHRATE_TEXT N_("Adjust HDMI refresh rate to the video.")
57 #define MMAL_ADJUST_REFRESHRATE_LONGTEXT N_("Adjust HDMI refresh rate to the video.")
58
59 static int Open(vlc_object_t *);
60 static void Close(vlc_object_t *);
61
62 vlc_module_begin()
63     set_shortname(N_("MMAL vout"))
64     set_description(N_("MMAL-based vout plugin for Raspberry Pi"))
65     set_capability("vout display", 90)
66     add_shortcut("mmal_vout")
67     add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false)
68     add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT,
69                     MMAL_ADJUST_REFRESHRATE_LONGTEXT, false)
70     set_callbacks(Open, Close)
71 vlc_module_end()
72
73 struct dmx_region_t {
74     struct dmx_region_t *next;
75     picture_t *picture;
76     VC_RECT_T bmp_rect;
77     VC_RECT_T src_rect;
78     VC_RECT_T dst_rect;
79     VC_DISPMANX_ALPHA_T alpha;
80     DISPMANX_ELEMENT_HANDLE_T element;
81     DISPMANX_RESOURCE_HANDLE_T resource;
82     int32_t pos_x;
83     int32_t pos_y;
84 };
85
86 struct vout_display_sys_t {
87     picture_t **pictures;
88     picture_pool_t *picture_pool;
89
90     MMAL_COMPONENT_T *component;
91     MMAL_PORT_T *input;
92     MMAL_POOL_T *pool;
93     struct dmx_region_t *dmx_region;
94     plane_t planes[3];
95
96     vlc_mutex_t buffer_mutex;
97     vlc_mutex_t manage_mutex;
98     vlc_cond_t buffer_cond;
99     uint32_t buffer_size;
100     int buffers_in_transit;
101     unsigned num_buffers;
102
103     DISPMANX_DISPLAY_HANDLE_T dmx_handle;
104     DISPMANX_ELEMENT_HANDLE_T bkg_element;
105     DISPMANX_RESOURCE_HANDLE_T bkg_resource;
106     unsigned display_width;
107     unsigned display_height;
108     bool need_configure_display;
109
110     int layer;
111     bool opaque;
112 };
113
114 struct picture_sys_t {
115     vout_display_t *vd;
116     MMAL_BUFFER_HEADER_T *buffer;
117     bool displayed;
118 };
119
120 static const vlc_fourcc_t subpicture_chromas[] = {
121     VLC_CODEC_RGBA,
122     0
123 };
124
125 /* Utility functions */
126 static inline uint32_t align(uint32_t x, uint32_t y);
127 static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
128                 const video_format_t *fmt);
129
130 /* VLC vout display callbacks */
131 static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count);
132 static void vd_display(vout_display_t *vd, picture_t *picture,
133                 subpicture_t *subpicture);
134 static int vd_control(vout_display_t *vd, int query, va_list args);
135 static void vd_manage(vout_display_t *vd);
136
137 /* VLC picture pool */
138 static int picture_lock(picture_t *picture);
139 static void picture_unlock(picture_t *picture);
140
141 /* MMAL callbacks */
142 static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
143 static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
144
145 /* TV service */
146 static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height);
147 static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1,
148                 uint32_t param2);
149 static void adjust_refresh_rate(vout_display_t *vd);
150
151 /* DispManX */
152 static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture);
153 static void close_dmx(vout_display_t *vd);
154 static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
155                 DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region);
156 static void dmx_region_update(struct dmx_region_t *dmx_region,
157                 DISPMANX_UPDATE_HANDLE_T update, picture_t *picture);
158 static void dmx_region_delete(struct dmx_region_t *dmx_region,
159                 DISPMANX_UPDATE_HANDLE_T update);
160 static void show_background(vout_display_t *vd, bool enable);
161
162 static int Open(vlc_object_t *object)
163 {
164     vout_display_t *vd = (vout_display_t *)object;
165     vout_display_sys_t *sys;
166     uint32_t buffer_pitch, buffer_height;
167     vout_display_place_t place;
168     MMAL_DISPLAYREGION_T display_region;
169     uint32_t offsets[3];
170     MMAL_STATUS_T status;
171     int ret = VLC_SUCCESS;
172     unsigned i;
173
174     sys = calloc(1, sizeof(struct vout_display_sys_t));
175     if (!sys)
176         return VLC_ENOMEM;
177     vd->sys = sys;
178
179     sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
180     bcm_host_init();
181
182     vd->info.has_hide_mouse = true;
183     sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE;
184
185     if (!sys->opaque)
186         vd->fmt.i_chroma = VLC_CODEC_I420;
187     vd->fmt.i_sar_num = vd->source.i_sar_num;
188     vd->fmt.i_sar_den = vd->source.i_sar_den;
189
190     buffer_pitch = align(vd->fmt.i_width, 32);
191     buffer_height = align(vd->fmt.i_height, 16);
192     sys->buffer_size = 3 * buffer_pitch * buffer_height / 2;
193
194     status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component);
195     if (status != MMAL_SUCCESS) {
196         msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
197                         MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status));
198         ret = VLC_EGENERIC;
199         goto out;
200     }
201
202     sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
203     status = mmal_port_enable(sys->component->control, control_port_cb);
204     if (status != MMAL_SUCCESS) {
205         msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)",
206                         sys->component->control->name, status, mmal_status_to_string(status));
207         ret = VLC_EGENERIC;
208         goto out;
209     }
210
211     sys->input = sys->component->input[0];
212     sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
213     if (sys->opaque)
214         sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
215     else
216         sys->input->format->encoding = MMAL_ENCODING_I420;
217     sys->input->format->es->video.width = vd->fmt.i_width;
218     sys->input->format->es->video.height = vd->fmt.i_height;
219     sys->input->format->es->video.crop.x = 0;
220     sys->input->format->es->video.crop.y = 0;
221     sys->input->format->es->video.crop.width = vd->fmt.i_width;
222     sys->input->format->es->video.crop.height = vd->fmt.i_height;
223     sys->input->format->es->video.par.num = vd->source.i_sar_num;
224     sys->input->format->es->video.par.den = vd->source.i_sar_den;
225
226     status = mmal_port_format_commit(sys->input);
227     if (status != MMAL_SUCCESS) {
228         msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
229                         sys->input->name, status, mmal_status_to_string(status));
230         ret = VLC_EGENERIC;
231         goto out;
232     }
233     sys->input->buffer_size = sys->input->buffer_size_recommended;
234
235     vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);
236     display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
237     display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
238     display_region.fullscreen = MMAL_FALSE;
239     display_region.src_rect.x = vd->fmt.i_x_offset;
240     display_region.src_rect.y = vd->fmt.i_y_offset;
241     display_region.src_rect.width = vd->fmt.i_visible_width;
242     display_region.src_rect.height = vd->fmt.i_visible_height;
243     display_region.dest_rect.x = place.x;
244     display_region.dest_rect.y = place.y;
245     display_region.dest_rect.width = place.width;
246     display_region.dest_rect.height = place.height;
247     display_region.layer = sys->layer;
248     display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
249             MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
250     status = mmal_port_parameter_set(sys->input, &display_region.hdr);
251     if (status != MMAL_SUCCESS) {
252         msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
253                         status, mmal_status_to_string(status));
254         ret = VLC_EGENERIC;
255         goto out;
256     }
257
258     offsets[0] = 0;
259     for (i = 0; i < 3; ++i) {
260         sys->planes[i].i_lines = buffer_height;
261         sys->planes[i].i_pitch = buffer_pitch;
262         sys->planes[i].i_visible_lines = vd->fmt.i_visible_height;
263         sys->planes[i].i_visible_pitch = vd->fmt.i_visible_width;
264
265         if (i > 0) {
266             offsets[i] = offsets[i - 1] + sys->planes[i - 1].i_pitch * sys->planes[i - 1].i_lines;
267             sys->planes[i].i_lines /= 2;
268             sys->planes[i].i_pitch /= 2;
269             sys->planes[i].i_visible_lines /= 2;
270             sys->planes[i].i_visible_pitch /= 2;
271         }
272
273         sys->planes[i].p_pixels = (uint8_t *)offsets[i];
274     }
275
276     vlc_mutex_init(&sys->buffer_mutex);
277     vlc_cond_init(&sys->buffer_cond);
278     vlc_mutex_init(&sys->manage_mutex);
279
280     vd->pool = vd_pool;
281     vd->display = vd_display;
282     vd->control = vd_control;
283     vd->manage = vd_manage;
284
285     vc_tv_register_callback(tvservice_cb, vd);
286
287     if (query_resolution(vd, &sys->display_width, &sys->display_height) >= 0) {
288         vout_display_SendEventDisplaySize(vd, sys->display_width, sys->display_height,
289                         vd->cfg->is_fullscreen);
290     } else {
291         sys->display_width = vd->cfg->display.width;
292         sys->display_height = vd->cfg->display.height;
293     }
294
295     sys->dmx_handle = vc_dispmanx_display_open(0);
296     vd->info.subpicture_chromas = subpicture_chromas;
297
298 out:
299     if (ret != VLC_SUCCESS)
300         Close(object);
301
302     return ret;
303 }
304
305 static void Close(vlc_object_t *object)
306 {
307     vout_display_t *vd = (vout_display_t *)object;
308     vout_display_sys_t *sys = vd->sys;
309     unsigned i;
310
311     vc_tv_unregister_callback_full(tvservice_cb, vd);
312
313     if (sys->dmx_handle)
314         close_dmx(vd);
315
316     if (sys->component && sys->component->control->is_enabled)
317         mmal_port_disable(sys->component->control);
318
319     if (sys->input && sys->input->is_enabled)
320         mmal_port_disable(sys->input);
321
322     if (sys->component && sys->component->is_enabled)
323         mmal_component_disable(sys->component);
324
325     if (sys->pool)
326         mmal_pool_destroy(sys->pool);
327
328     if (sys->component)
329         mmal_component_release(sys->component);
330
331     if (sys->picture_pool)
332         picture_pool_Delete(sys->picture_pool);
333     else
334         for (i = 0; i < sys->num_buffers; ++i)
335             if (sys->pictures[i])
336                 picture_Release(sys->pictures[i]);
337
338     vlc_mutex_destroy(&sys->buffer_mutex);
339     vlc_cond_destroy(&sys->buffer_cond);
340     vlc_mutex_destroy(&sys->manage_mutex);
341
342     free(sys);
343
344     bcm_host_deinit();
345 }
346
347 static inline uint32_t align(uint32_t x, uint32_t y) {
348     uint32_t mod = x % y;
349     if (mod == 0)
350         return x;
351     else
352         return x + y - mod;
353 }
354
355 static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
356                 const video_format_t *fmt)
357 {
358     vout_display_sys_t *sys = vd->sys;
359     vout_display_place_t place;
360     MMAL_DISPLAYREGION_T display_region;
361     MMAL_STATUS_T status;
362
363     if (!cfg && !fmt)
364         return -EINVAL;
365
366     if (fmt) {
367         sys->input->format->es->video.par.num = fmt->i_sar_num;
368         sys->input->format->es->video.par.den = fmt->i_sar_den;
369
370         status = mmal_port_format_commit(sys->input);
371         if (status != MMAL_SUCCESS) {
372             msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
373                             sys->input->name, status, mmal_status_to_string(status));
374             return -EINVAL;
375         }
376     } else {
377         fmt = &vd->fmt;
378     }
379
380     if (!cfg)
381         cfg = vd->cfg;
382
383     vout_display_PlacePicture(&place, fmt, cfg, false);
384
385     display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
386     display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
387     display_region.fullscreen = MMAL_FALSE;
388     display_region.src_rect.x = fmt->i_x_offset;
389     display_region.src_rect.y = fmt->i_y_offset;
390     display_region.src_rect.width = fmt->i_visible_width;
391     display_region.src_rect.height = fmt->i_visible_height;
392     display_region.dest_rect.x = place.x;
393     display_region.dest_rect.y = place.y;
394     display_region.dest_rect.width = place.width;
395     display_region.dest_rect.height = place.height;
396     display_region.layer = sys->layer;
397     display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
398             MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
399     status = mmal_port_parameter_set(sys->input, &display_region.hdr);
400     if (status != MMAL_SUCCESS) {
401         msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
402                         status, mmal_status_to_string(status));
403         return -EINVAL;
404     }
405
406     show_background(vd, cfg->is_fullscreen);
407     if (var_InheritBool(vd, MMAL_ADJUST_REFRESHRATE_NAME))
408         adjust_refresh_rate(vd);
409
410     if (fmt != &vd->fmt)
411         memcpy(&vd->fmt, fmt, sizeof(video_format_t));
412
413     return 0;
414 }
415
416 static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count)
417 {
418     vout_display_sys_t *sys = vd->sys;
419     picture_resource_t picture_res;
420     picture_pool_configuration_t picture_pool_cfg;
421     video_format_t fmt = vd->fmt;
422     MMAL_STATUS_T status;
423     unsigned i;
424
425     if (sys->picture_pool) {
426         if (sys->num_buffers < count)
427             msg_Warn(vd, "Picture pool with %u pictures requested, but we already have one with %u pictures",
428                             count, sys->num_buffers);
429
430         goto out;
431     }
432
433     if (sys->opaque) {
434         if (count <= NUM_ACTUAL_OPAQUE_BUFFERS)
435             count = NUM_ACTUAL_OPAQUE_BUFFERS;
436         else
437             msg_Err(vd, "More picture (%u) than NUM_ACTUAL_OPAQUE_BUFFERS (%d) requested. Expect errors",
438                             count, NUM_ACTUAL_OPAQUE_BUFFERS);
439     }
440
441     if (count < sys->input->buffer_num_recommended)
442         count = sys->input->buffer_num_recommended;
443
444 #ifndef NDEBUG
445     msg_Dbg(vd, "Creating picture pool with %u pictures", count);
446 #endif
447
448     sys->input->buffer_num = count;
449     status = mmal_port_enable(sys->input, input_port_cb);
450     if (status != MMAL_SUCCESS) {
451         msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)",
452                         sys->input->name, status, mmal_status_to_string(status));
453         goto out;
454     }
455
456     status = mmal_component_enable(sys->component);
457     if (status != MMAL_SUCCESS) {
458         msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)",
459                         sys->component->name, status, mmal_status_to_string(status));
460         goto out;
461     }
462
463     sys->pool = mmal_pool_create_with_allocator(count, sys->input->buffer_size,
464                     sys->input,
465                     (mmal_pool_allocator_alloc_t)mmal_port_payload_alloc,
466                     (mmal_pool_allocator_free_t)mmal_port_payload_free);
467     if (!sys->pool) {
468         msg_Err(vd, "Failed to create MMAL pool for %u buffers of size %"PRIu32,
469                         count, sys->input->buffer_size);
470         goto out;
471     }
472     sys->num_buffers = count;
473
474     memset(&picture_res, 0, sizeof(picture_resource_t));
475     sys->pictures = calloc(sys->num_buffers, sizeof(picture_t *));
476     for (i = 0; i < sys->num_buffers; ++i) {
477         picture_res.p_sys = malloc(sizeof(picture_sys_t));
478         picture_res.p_sys->vd = vd;
479         picture_res.p_sys->buffer = NULL;
480
481         sys->pictures[i] = picture_NewFromResource(&fmt, &picture_res);
482         if (!sys->pictures[i]) {
483             msg_Err(vd, "Failed to create picture");
484             free(picture_res.p_sys);
485             goto out;
486         }
487     }
488
489     memset(&picture_pool_cfg, 0, sizeof(picture_pool_configuration_t));
490     picture_pool_cfg.picture_count = sys->num_buffers;
491     picture_pool_cfg.picture = sys->pictures;
492     picture_pool_cfg.lock = picture_lock;
493     picture_pool_cfg.unlock = picture_unlock;
494
495     sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg);
496     if (!sys->picture_pool) {
497         msg_Err(vd, "Failed to create picture pool");
498         goto out;
499     }
500
501 out:
502     return sys->picture_pool;
503 }
504
505 static void vd_display(vout_display_t *vd, picture_t *picture,
506                 subpicture_t *subpicture)
507 {
508     vout_display_sys_t *sys = vd->sys;
509     picture_sys_t *pic_sys = picture->p_sys;
510     MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
511     MMAL_STATUS_T status;
512
513     if (!pic_sys->displayed || !sys->opaque) {
514         buffer->cmd = 0;
515         buffer->length = sys->input->buffer_size;
516
517         vlc_mutex_lock(&sys->buffer_mutex);
518         while (sys->buffers_in_transit >= MAX_BUFFERS_IN_TRANSIT)
519             vlc_cond_wait(&sys->buffer_cond, &sys->buffer_mutex);
520
521         status = mmal_port_send_buffer(sys->input, buffer);
522         if (status == MMAL_SUCCESS)
523             ++sys->buffers_in_transit;
524
525         vlc_mutex_unlock(&sys->buffer_mutex);
526         if (status != MMAL_SUCCESS) {
527             msg_Err(vd, "Failed to send buffer to input port. Frame dropped");
528             picture_Release(picture);
529         }
530
531         pic_sys->displayed = true;
532     } else {
533         picture_Release(picture);
534     }
535
536     display_subpicture(vd, subpicture);
537
538     if (subpicture)
539         subpicture_Delete(subpicture);
540 }
541
542 static int vd_control(vout_display_t *vd, int query, va_list args)
543 {
544     vout_display_sys_t *sys = vd->sys;
545     vout_display_cfg_t cfg;
546     const vout_display_cfg_t *tmp_cfg;
547     video_format_t fmt;
548     const video_format_t *tmp_fmt;
549     int ret = VLC_EGENERIC;
550
551     switch (query) {
552         case VOUT_DISPLAY_HIDE_MOUSE:
553         case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
554             ret = VLC_SUCCESS;
555             break;
556
557         case VOUT_DISPLAY_CHANGE_FULLSCREEN:
558             tmp_cfg = va_arg(args, const vout_display_cfg_t *);
559             vout_display_SendEventDisplaySize(vd, sys->display_width,
560                             sys->display_height, tmp_cfg->is_fullscreen);
561             ret = VLC_SUCCESS;
562             break;
563
564         case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
565             tmp_cfg = va_arg(args, const vout_display_cfg_t *);
566             if (tmp_cfg->display.width == sys->display_width &&
567                             tmp_cfg->display.height == sys->display_height) {
568                 cfg = *vd->cfg;
569                 cfg.display.width = sys->display_width;
570                 cfg.display.height = sys->display_height;
571                 if (configure_display(vd, &cfg, NULL) >= 0)
572                     ret = VLC_SUCCESS;
573             }
574             break;
575
576         case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
577             cfg = *vd->cfg;
578             tmp_cfg = va_arg(args, const vout_display_cfg_t *);
579             cfg.is_display_filled = tmp_cfg->is_display_filled;
580             if (configure_display(vd, &cfg, NULL) >= 0)
581                 ret = VLC_SUCCESS;
582             break;
583
584         case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
585             fmt = vd->fmt;
586             tmp_fmt = va_arg(args, const video_format_t *);
587             fmt.i_sar_num = tmp_fmt->i_sar_num;
588             fmt.i_sar_den = tmp_fmt->i_sar_den;
589             if (configure_display(vd, NULL, &fmt) >= 0)
590                 ret = VLC_SUCCESS;
591             break;
592
593         case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
594             fmt = vd->fmt;
595             tmp_fmt = va_arg(args, const video_format_t *);
596             fmt.i_x_offset = tmp_fmt->i_x_offset;
597             fmt.i_y_offset = tmp_fmt->i_y_offset;
598             fmt.i_visible_width = tmp_fmt->i_visible_width;
599             fmt.i_visible_height = tmp_fmt->i_visible_height;
600             if (configure_display(vd, NULL, &fmt) >= 0)
601                 ret = VLC_SUCCESS;
602             break;
603
604         case VOUT_DISPLAY_CHANGE_ZOOM:
605         case VOUT_DISPLAY_RESET_PICTURES:
606         case VOUT_DISPLAY_GET_OPENGL:
607             msg_Warn(vd, "Unsupported control query %d", query);
608             break;
609
610         default:
611             msg_Warn(vd, "Unknown control query %d", query);
612             break;
613     }
614
615     return ret;
616 }
617
618 static void vd_manage(vout_display_t *vd)
619 {
620     vout_display_sys_t *sys = vd->sys;
621     unsigned width, height;
622
623     vlc_mutex_lock(&sys->manage_mutex);
624
625     if (sys->need_configure_display) {
626         close_dmx(vd);
627         sys->dmx_handle = vc_dispmanx_display_open(0);
628
629         if (query_resolution(vd, &width, &height) >= 0) {
630             sys->display_width = width;
631             sys->display_height = height;
632             vout_display_SendEventDisplaySize(vd, width, height, vd->cfg->is_fullscreen);
633         }
634
635         sys->need_configure_display = false;
636     }
637
638     vlc_mutex_unlock(&sys->manage_mutex);
639 }
640
641 static int picture_lock(picture_t *picture)
642 {
643     vout_display_t *vd = picture->p_sys->vd;
644     vout_display_sys_t *sys = vd->sys;
645     picture_sys_t *pic_sys = picture->p_sys;
646
647     MMAL_BUFFER_HEADER_T *buffer = mmal_queue_wait(sys->pool->queue);
648     if (!buffer)
649         return VLC_EGENERIC;
650
651     vlc_mutex_lock(&sys->buffer_mutex);
652
653     mmal_buffer_header_reset(buffer);
654     buffer->user_data = picture;
655     pic_sys->buffer = buffer;
656
657     memcpy(picture->p, sys->planes, sizeof(sys->planes));
658     picture->p[0].p_pixels = buffer->data;
659     picture->p[1].p_pixels += (ptrdiff_t)buffer->data;
660     picture->p[2].p_pixels += (ptrdiff_t)buffer->data;
661
662     pic_sys->displayed = false;
663
664     vlc_mutex_unlock(&sys->buffer_mutex);
665
666     return VLC_SUCCESS;
667 }
668
669 static void picture_unlock(picture_t *picture)
670 {
671     picture_sys_t *pic_sys = picture->p_sys;
672     vout_display_t *vd = pic_sys->vd;
673     vout_display_sys_t *sys = vd->sys;
674     MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
675
676     vlc_mutex_lock(&sys->buffer_mutex);
677
678     pic_sys->buffer = NULL;
679     if (buffer) {
680         buffer->user_data = NULL;
681         mmal_buffer_header_release(buffer);
682     }
683
684     vlc_mutex_unlock(&sys->buffer_mutex);
685 }
686
687 static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
688 {
689     vout_display_t *vd = (vout_display_t *)port->userdata;
690     MMAL_STATUS_T status;
691
692     if (buffer->cmd == MMAL_EVENT_ERROR) {
693         status = *(uint32_t *)buffer->data;
694         msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
695     }
696
697     mmal_buffer_header_release(buffer);
698 }
699
700 static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
701 {
702     vout_display_t *vd = (vout_display_t *)port->userdata;
703     vout_display_sys_t *sys = vd->sys;
704     picture_t *picture = (picture_t *)buffer->user_data;
705
706     vlc_mutex_lock(&sys->buffer_mutex);
707     --sys->buffers_in_transit;
708     vlc_cond_signal(&sys->buffer_cond);
709     vlc_mutex_unlock(&sys->buffer_mutex);
710
711     if (picture)
712         picture_Release(picture);
713 }
714
715 static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height)
716 {
717     TV_DISPLAY_STATE_T display_state;
718     int ret = 0;
719
720     if (vc_tv_get_display_state(&display_state) == 0) {
721         if (display_state.state & 0xFF) {
722             *width = display_state.display.hdmi.width;
723             *height = display_state.display.hdmi.height;
724         } else if (display_state.state & 0xFF00) {
725             *width = display_state.display.sdtv.width;
726             *height = display_state.display.sdtv.height;
727         } else {
728             msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state);
729             ret = -1;
730         }
731     } else {
732         msg_Warn(vd, "Failed to query display resolution");
733         ret = -1;
734     }
735
736     return ret;
737 }
738
739 static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2)
740 {
741     VLC_UNUSED(reason);
742     VLC_UNUSED(param1);
743     VLC_UNUSED(param2);
744
745     vout_display_t *vd = (vout_display_t *)callback_data;
746     vout_display_sys_t *sys = vd->sys;
747
748     vlc_mutex_lock(&sys->manage_mutex);
749     sys->need_configure_display = true;
750     vlc_mutex_unlock(&sys->manage_mutex);
751 }
752
753 static void adjust_refresh_rate(vout_display_t *vd)
754 {
755     TV_DISPLAY_STATE_T display_state;
756     TV_SUPPORTED_MODE_NEW_T supported_modes[VC_TV_MAX_MODE_IDS];
757     int num_modes;
758     double frame_rate = (double)vd->fmt.i_frame_rate / vd->fmt.i_frame_rate_base;
759     int best_id = -1;
760     double best_score, score;
761     int i;
762
763     vc_tv_get_display_state(&display_state);
764     if(display_state.display.hdmi.mode != HDMI_MODE_OFF) {
765         num_modes = vc_tv_hdmi_get_supported_modes_new(display_state.display.hdmi.group,
766                         supported_modes, VC_TV_MAX_MODE_IDS, NULL, NULL);
767
768         for(i = 0; i < num_modes; ++i) {
769             if(supported_modes[i].width != display_state.display.hdmi.width ||
770                             supported_modes[i].height != display_state.display.hdmi.height ||
771                             supported_modes[i].scan_mode != display_state.display.hdmi.scan_mode)
772                 continue;
773
774             score = fmod(supported_modes[i].frame_rate, frame_rate);
775             if((best_id < 0) || (score < best_score)) {
776                 best_id = i;
777                 best_score = score;
778             }
779         }
780
781         if((best_id >= 0) && (display_state.display.hdmi.frame_rate != supported_modes[best_id].frame_rate)) {
782             msg_Info(vd, "Setting HDMI refresh rate to %"PRIu32,
783                             supported_modes[best_id].frame_rate);
784             vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI,
785                             supported_modes[best_id].group,
786                             supported_modes[best_id].code);
787         }
788     }
789 }
790
791 static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture)
792 {
793     vout_display_sys_t *sys = vd->sys;
794     struct dmx_region_t **dmx_region = &sys->dmx_region;
795     struct dmx_region_t *unused_dmx_region;
796     DISPMANX_UPDATE_HANDLE_T update = 0;
797     picture_t *picture;
798     video_format_t *fmt;
799     struct dmx_region_t *dmx_region_next;
800
801     if(subpicture) {
802         subpicture_region_t *region = subpicture->p_region;
803         while(region) {
804             picture = region->p_picture;
805             fmt = &region->fmt;
806
807             if(!*dmx_region) {
808                 if(!update)
809                     update = vc_dispmanx_update_start(10);
810                 *dmx_region = dmx_region_new(vd, update, region);
811             } else if(((*dmx_region)->bmp_rect.width != (int32_t)fmt->i_visible_width) ||
812                     ((*dmx_region)->bmp_rect.height != (int32_t)fmt->i_visible_height) ||
813                     ((*dmx_region)->pos_x != region->i_x) ||
814                     ((*dmx_region)->pos_y != region->i_y) ||
815                     ((*dmx_region)->alpha.opacity != (uint32_t)region->i_alpha)) {
816                 dmx_region_next = (*dmx_region)->next;
817                 if(!update)
818                     update = vc_dispmanx_update_start(10);
819                 dmx_region_delete(*dmx_region, update);
820                 *dmx_region = dmx_region_new(vd, update, region);
821                 (*dmx_region)->next = dmx_region_next;
822             } else if((*dmx_region)->picture != picture) {
823                 if(!update)
824                     update = vc_dispmanx_update_start(10);
825                 dmx_region_update(*dmx_region, update, picture);
826             }
827
828             dmx_region = &(*dmx_region)->next;
829             region = region->p_next;
830         }
831     }
832
833     /* Remove remaining regions */
834     unused_dmx_region = *dmx_region;
835     while(unused_dmx_region) {
836         dmx_region_next = unused_dmx_region->next;
837         if(!update)
838             update = vc_dispmanx_update_start(10);
839         dmx_region_delete(unused_dmx_region, update);
840         unused_dmx_region = dmx_region_next;
841     }
842     *dmx_region = NULL;
843
844     if(update)
845         vc_dispmanx_update_submit_sync(update);
846 }
847
848 static void close_dmx(vout_display_t *vd)
849 {
850     vout_display_sys_t *sys = vd->sys;
851     DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(10);
852     struct dmx_region_t *dmx_region = sys->dmx_region;
853     struct dmx_region_t *dmx_region_next;
854
855     while(dmx_region) {
856         dmx_region_next = dmx_region->next;
857         dmx_region_delete(dmx_region, update);
858         dmx_region = dmx_region_next;
859     }
860
861     vc_dispmanx_update_submit_sync(update);
862     sys->dmx_region = NULL;
863
864     show_background(vd, false);
865
866     vc_dispmanx_display_close(sys->dmx_handle);
867     sys->dmx_handle = DISPMANX_NO_HANDLE;
868 }
869
870 static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
871                 DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region)
872 {
873     vout_display_sys_t *sys = vd->sys;
874     video_format_t *fmt = &region->fmt;
875     struct dmx_region_t *dmx_region = malloc(sizeof(struct dmx_region_t));
876     uint32_t image_handle;
877
878     dmx_region->pos_x = region->i_x;
879     dmx_region->pos_y = region->i_y;
880
881     vc_dispmanx_rect_set(&dmx_region->bmp_rect, 0, 0, fmt->i_visible_width,
882                     fmt->i_visible_height);
883     vc_dispmanx_rect_set(&dmx_region->src_rect, 0, 0, fmt->i_visible_width << 16,
884                     fmt->i_visible_height << 16);
885     vc_dispmanx_rect_set(&dmx_region->dst_rect, region->i_x, region->i_y,
886                     fmt->i_visible_width, fmt->i_visible_height);
887
888     dmx_region->resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32,
889                     dmx_region->bmp_rect.width | (region->p_picture->p[0].i_pitch << 16),
890                     dmx_region->bmp_rect.height | (dmx_region->bmp_rect.height << 16),
891                     &image_handle);
892     vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
893                     region->p_picture->p[0].i_pitch,
894                     region->p_picture->p[0].p_pixels, &dmx_region->bmp_rect);
895
896     dmx_region->alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_MIX;
897     dmx_region->alpha.opacity = region->i_alpha;
898     dmx_region->alpha.mask = DISPMANX_NO_HANDLE;
899     dmx_region->element = vc_dispmanx_element_add(update, sys->dmx_handle,
900                     sys->layer + 1, &dmx_region->dst_rect, dmx_region->resource,
901                     &dmx_region->src_rect, DISPMANX_PROTECTION_NONE,
902                     &dmx_region->alpha, NULL, VC_IMAGE_ROT0);
903
904     dmx_region->next = NULL;
905     dmx_region->picture = region->p_picture;
906
907     return dmx_region;
908 }
909
910 static void dmx_region_update(struct dmx_region_t *dmx_region,
911                 DISPMANX_UPDATE_HANDLE_T update, picture_t *picture)
912 {
913     vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
914                     picture->p[0].i_pitch, picture->p[0].p_pixels, &dmx_region->bmp_rect);
915     vc_dispmanx_element_change_source(update, dmx_region->element, dmx_region->resource);
916     dmx_region->picture = picture;
917 }
918
919 static void dmx_region_delete(struct dmx_region_t *dmx_region,
920                 DISPMANX_UPDATE_HANDLE_T update)
921 {
922     vc_dispmanx_element_remove(update, dmx_region->element);
923     vc_dispmanx_resource_delete(dmx_region->resource);
924     free(dmx_region);
925 }
926
927 static void show_background(vout_display_t *vd, bool enable)
928 {
929     vout_display_sys_t *sys = vd->sys;
930     uint32_t image_ptr, color = 0xFF000000;
931     VC_RECT_T dst_rect, src_rect;
932     DISPMANX_UPDATE_HANDLE_T update;
933
934     if (enable && !sys->bkg_element) {
935         sys->bkg_resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32, 1, 1,
936                         &image_ptr);
937         vc_dispmanx_rect_set(&dst_rect, 0, 0, 1, 1);
938         vc_dispmanx_resource_write_data(sys->bkg_resource, VC_IMAGE_RGBA32,
939                         sizeof(color), &color, &dst_rect);
940         vc_dispmanx_rect_set(&src_rect, 0, 0, 1 << 16, 1 << 16);
941         vc_dispmanx_rect_set(&dst_rect, 0, 0, 0, 0);
942         update = vc_dispmanx_update_start(0);
943         sys->bkg_element = vc_dispmanx_element_add(update, sys->dmx_handle,
944                         sys->layer - 1, &dst_rect, sys->bkg_resource, &src_rect,
945                         DISPMANX_PROTECTION_NONE, NULL, NULL, VC_IMAGE_ROT0);
946         vc_dispmanx_update_submit_sync(update);
947     } else if (!enable && sys->bkg_element) {
948         update = vc_dispmanx_update_start(0);
949         vc_dispmanx_element_remove(update, sys->bkg_element);
950         vc_dispmanx_resource_delete(sys->bkg_resource);
951         vc_dispmanx_update_submit_sync(update);
952         sys->bkg_element = DISPMANX_NO_HANDLE;
953         sys->bkg_resource = DISPMANX_NO_HANDLE;
954     }
955 }