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