mmal/vout: Fix build
[vlc.git] / 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_atomic.h>
33 #include <vlc_plugin.h>
34 #include <vlc_threads.h>
35 #include <vlc_vout_display.h>
36
37 #include "mmal_picture.h"
38
39 #include <bcm_host.h>
40 #include <interface/mmal/mmal.h>
41 #include <interface/mmal/util/mmal_util.h>
42 #include <interface/mmal/util/mmal_default_components.h>
43 #include <interface/vmcs_host/vc_tvservice.h>
44 #include <interface/vmcs_host/vc_dispmanx.h>
45
46 #define MAX_BUFFERS_IN_TRANSIT 1
47 #define VC_TV_MAX_MODE_IDS 127
48
49 #define MMAL_LAYER_NAME "mmal-layer"
50 #define MMAL_LAYER_TEXT N_("VideoCore layer where the video is displayed.")
51 #define MMAL_LAYER_LONGTEXT N_("VideoCore layer where the video is displayed. Subpictures are displayed directly above and a black background directly below.")
52
53 #define MMAL_BLANK_BACKGROUND_NAME "mmal-blank-background"
54 #define MMAL_BLANK_BACKGROUND_TEXT N_("Blank screen below video.")
55 #define MMAL_BLANK_BACKGROUND_LONGTEXT N_("Render blank screen below video. " \
56         "Increases VideoCore load.")
57
58 #define MMAL_ADJUST_REFRESHRATE_NAME "mmal-adjust-refreshrate"
59 #define MMAL_ADJUST_REFRESHRATE_TEXT N_("Adjust HDMI refresh rate to the video.")
60 #define MMAL_ADJUST_REFRESHRATE_LONGTEXT N_("Adjust HDMI refresh rate to the video.")
61
62 #define MMAL_NATIVE_INTERLACED "mmal-native-interlaced"
63 #define MMAL_NATIVE_INTERLACE_TEXT N_("Force interlaced video mode.")
64 #define MMAL_NATIVE_INTERLACE_LONGTEXT N_("Force the HDMI output into an " \
65         "interlaced video mode for interlaced video content.")
66
67 /* Ideal rendering phase target is at rough 25% of frame duration */
68 #define PHASE_OFFSET_TARGET ((double)0.25)
69 #define PHASE_CHECK_INTERVAL 100
70
71 static int Open(vlc_object_t *);
72 static void Close(vlc_object_t *);
73
74 vlc_module_begin()
75     set_shortname(N_("MMAL vout"))
76     set_description(N_("MMAL-based vout plugin for Raspberry Pi"))
77     set_capability("vout display", 90)
78     add_shortcut("mmal_vout")
79     add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false)
80     add_bool(MMAL_BLANK_BACKGROUND_NAME, true, MMAL_BLANK_BACKGROUND_TEXT,
81                     MMAL_BLANK_BACKGROUND_LONGTEXT, true);
82     add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT,
83                     MMAL_ADJUST_REFRESHRATE_LONGTEXT, false)
84     add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT,
85                     MMAL_NATIVE_INTERLACE_LONGTEXT, false)
86     set_callbacks(Open, Close)
87 vlc_module_end()
88
89 struct dmx_region_t {
90     struct dmx_region_t *next;
91     picture_t *picture;
92     VC_RECT_T bmp_rect;
93     VC_RECT_T src_rect;
94     VC_RECT_T dst_rect;
95     VC_DISPMANX_ALPHA_T alpha;
96     DISPMANX_ELEMENT_HANDLE_T element;
97     DISPMANX_RESOURCE_HANDLE_T resource;
98     int32_t pos_x;
99     int32_t pos_y;
100 };
101
102 struct vout_display_sys_t {
103     vlc_cond_t buffer_cond;
104     vlc_mutex_t buffer_mutex;
105     vlc_mutex_t manage_mutex;
106
107     plane_t planes[3]; /* Depending on video format up to 3 planes are used */
108     picture_t **pictures; /* Actual list of alloced pictures passed into picture_pool */
109     picture_pool_t *picture_pool;
110
111     MMAL_COMPONENT_T *component;
112     MMAL_PORT_T *input;
113     MMAL_POOL_T *pool; /* mmal buffer headers, used for pushing pictures to component*/
114     struct dmx_region_t *dmx_region;
115     int i_planes; /* Number of actually used planes, 1 for opaque, 3 for i420 */
116
117     uint32_t buffer_size; /* size of actual mmal buffers */
118     int buffers_in_transit; /* number of buffers currently pushed to mmal component */
119     unsigned num_buffers; /* number of buffers allocated at mmal port */
120
121     DISPMANX_DISPLAY_HANDLE_T dmx_handle;
122     DISPMANX_ELEMENT_HANDLE_T bkg_element;
123     DISPMANX_RESOURCE_HANDLE_T bkg_resource;
124     unsigned display_width;
125     unsigned display_height;
126
127     int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */
128     int i_frame_rate;
129
130     int next_phase_check; /* lowpass for phase check frequency */
131     int phase_offset; /* currently applied offset to presentation time in ns */
132     int layer; /* the dispman layer (z-index) used for video rendering */
133
134     bool need_configure_display; /* indicates a required display reconfigure to main thread */
135     bool adjust_refresh_rate;
136     bool native_interlaced;
137     bool b_top_field_first; /* cached interlaced settings to detect changes for native mode */
138     bool b_progressive;
139     bool opaque; /* indicated use of opaque picture format (zerocopy) */
140 };
141
142 static const vlc_fourcc_t subpicture_chromas[] = {
143     VLC_CODEC_RGBA,
144     0
145 };
146
147 /* Utility functions */
148 static inline uint32_t align(uint32_t x, uint32_t y);
149 static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
150                 const video_format_t *fmt);
151
152 /* VLC vout display callbacks */
153 static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count);
154 static void vd_prepare(vout_display_t *vd, picture_t *picture,
155                 subpicture_t *subpicture);
156 static void vd_display(vout_display_t *vd, picture_t *picture,
157                 subpicture_t *subpicture);
158 static int vd_control(vout_display_t *vd, int query, va_list args);
159 static void vd_manage(vout_display_t *vd);
160
161 /* MMAL callbacks */
162 static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
163 static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
164
165 /* TV service */
166 static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height);
167 static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1,
168                 uint32_t param2);
169 static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt);
170 static int set_latency_target(vout_display_t *vd, bool enable);
171
172 /* DispManX */
173 static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture);
174 static void close_dmx(vout_display_t *vd);
175 static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
176                 DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region);
177 static void dmx_region_update(struct dmx_region_t *dmx_region,
178                 DISPMANX_UPDATE_HANDLE_T update, picture_t *picture);
179 static void dmx_region_delete(struct dmx_region_t *dmx_region,
180                 DISPMANX_UPDATE_HANDLE_T update);
181 static void show_background(vout_display_t *vd, bool enable);
182 static void maintain_phase_sync(vout_display_t *vd);
183
184 static int Open(vlc_object_t *object)
185 {
186     vout_display_t *vd = (vout_display_t *)object;
187     vout_display_sys_t *sys;
188     uint32_t buffer_pitch, buffer_height;
189     vout_display_place_t place;
190     MMAL_DISPLAYREGION_T display_region;
191     MMAL_STATUS_T status;
192     int ret = VLC_SUCCESS;
193     unsigned i;
194
195     if (vout_display_IsWindowed(vd))
196         return VLC_EGENERIC;
197
198     sys = calloc(1, sizeof(struct vout_display_sys_t));
199     if (!sys)
200         return VLC_ENOMEM;
201     vd->sys = sys;
202
203     sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
204     bcm_host_init();
205
206     sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE;
207
208     status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component);
209     if (status != MMAL_SUCCESS) {
210         msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
211                         MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status));
212         ret = VLC_EGENERIC;
213         goto out;
214     }
215
216     sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
217     status = mmal_port_enable(sys->component->control, control_port_cb);
218     if (status != MMAL_SUCCESS) {
219         msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)",
220                         sys->component->control->name, status, mmal_status_to_string(status));
221         ret = VLC_EGENERIC;
222         goto out;
223     }
224
225     sys->input = sys->component->input[0];
226     sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
227
228     if (sys->opaque) {
229         sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
230         sys->i_planes = 1;
231         sys->buffer_size = sys->input->buffer_size_recommended;
232     } else {
233         sys->input->format->encoding = MMAL_ENCODING_I420;
234         vd->fmt.i_chroma = VLC_CODEC_I420;
235         buffer_pitch = align(vd->fmt.i_width, 32);
236         buffer_height = align(vd->fmt.i_height, 16);
237         sys->i_planes = 3;
238         sys->buffer_size = 3 * buffer_pitch * buffer_height / 2;
239     }
240
241     sys->input->format->es->video.width = vd->fmt.i_width;
242     sys->input->format->es->video.height = vd->fmt.i_height;
243     sys->input->format->es->video.crop.x = 0;
244     sys->input->format->es->video.crop.y = 0;
245     sys->input->format->es->video.crop.width = vd->fmt.i_width;
246     sys->input->format->es->video.crop.height = vd->fmt.i_height;
247     sys->input->format->es->video.par.num = vd->source.i_sar_num;
248     sys->input->format->es->video.par.den = vd->source.i_sar_den;
249
250     status = mmal_port_format_commit(sys->input);
251     if (status != MMAL_SUCCESS) {
252         msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
253                         sys->input->name, status, mmal_status_to_string(status));
254         ret = VLC_EGENERIC;
255         goto out;
256     }
257     sys->input->buffer_size = sys->input->buffer_size_recommended;
258
259     vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);
260     display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
261     display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
262     display_region.fullscreen = MMAL_FALSE;
263     display_region.src_rect.x = vd->fmt.i_x_offset;
264     display_region.src_rect.y = vd->fmt.i_y_offset;
265     display_region.src_rect.width = vd->fmt.i_visible_width;
266     display_region.src_rect.height = vd->fmt.i_visible_height;
267     display_region.dest_rect.x = place.x;
268     display_region.dest_rect.y = place.y;
269     display_region.dest_rect.width = place.width;
270     display_region.dest_rect.height = place.height;
271     display_region.layer = sys->layer;
272     display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
273             MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
274     status = mmal_port_parameter_set(sys->input, &display_region.hdr);
275     if (status != MMAL_SUCCESS) {
276         msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
277                         status, mmal_status_to_string(status));
278         ret = VLC_EGENERIC;
279         goto out;
280     }
281
282     for (i = 0; i < sys->i_planes; ++i) {
283         sys->planes[i].i_lines = buffer_height;
284         sys->planes[i].i_pitch = buffer_pitch;
285         sys->planes[i].i_visible_lines = vd->fmt.i_visible_height;
286         sys->planes[i].i_visible_pitch = vd->fmt.i_visible_width;
287
288         if (i > 0) {
289             sys->planes[i].i_lines /= 2;
290             sys->planes[i].i_pitch /= 2;
291             sys->planes[i].i_visible_lines /= 2;
292             sys->planes[i].i_visible_pitch /= 2;
293         }
294     }
295
296     vlc_mutex_init(&sys->buffer_mutex);
297     vlc_cond_init(&sys->buffer_cond);
298     vlc_mutex_init(&sys->manage_mutex);
299
300     vd->pool = vd_pool;
301     vd->prepare = vd_prepare;
302     vd->display = vd_display;
303     vd->control = vd_control;
304     vd->manage = vd_manage;
305
306     vc_tv_register_callback(tvservice_cb, vd);
307
308     if (query_resolution(vd, &sys->display_width, &sys->display_height) >= 0) {
309         vout_display_SendEventDisplaySize(vd, sys->display_width, sys->display_height);
310     } else {
311         sys->display_width = vd->cfg->display.width;
312         sys->display_height = vd->cfg->display.height;
313     }
314
315     sys->dmx_handle = vc_dispmanx_display_open(0);
316     vd->info.subpicture_chromas = subpicture_chromas;
317
318 out:
319     if (ret != VLC_SUCCESS)
320         Close(object);
321
322     return ret;
323 }
324
325 static void Close(vlc_object_t *object)
326 {
327     vout_display_t *vd = (vout_display_t *)object;
328     vout_display_sys_t *sys = vd->sys;
329     char response[20]; /* answer is hvs_update_fields=%1d */
330     unsigned i;
331
332     vc_tv_unregister_callback_full(tvservice_cb, vd);
333
334     if (sys->dmx_handle)
335         close_dmx(vd);
336
337     if (sys->component && sys->component->control->is_enabled)
338         mmal_port_disable(sys->component->control);
339
340     if (sys->input && sys->input->is_enabled)
341         mmal_port_disable(sys->input);
342
343     if (sys->component && sys->component->is_enabled)
344         mmal_component_disable(sys->component);
345
346     if (sys->pool)
347         mmal_port_pool_destroy(sys->input, sys->pool);
348
349     if (sys->component)
350         mmal_component_release(sys->component);
351
352     if (sys->picture_pool)
353         picture_pool_Release(sys->picture_pool);
354     else
355         for (i = 0; i < sys->num_buffers; ++i)
356             if (sys->pictures[i]) {
357                 mmal_buffer_header_release(sys->pictures[i]->p_sys->buffer);
358                 picture_Release(sys->pictures[i]);
359             }
360
361     vlc_mutex_destroy(&sys->buffer_mutex);
362     vlc_cond_destroy(&sys->buffer_cond);
363     vlc_mutex_destroy(&sys->manage_mutex);
364
365     if (sys->native_interlaced) {
366         if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 ||
367                 response[18] != '0')
368             msg_Warn(vd, "Could not reset hvs field mode");
369     }
370
371     free(sys->pictures);
372     free(sys);
373
374     bcm_host_deinit();
375 }
376
377 static inline uint32_t align(uint32_t x, uint32_t y) {
378     uint32_t mod = x % y;
379     if (mod == 0)
380         return x;
381     else
382         return x + y - mod;
383 }
384
385 static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
386                 const video_format_t *fmt)
387 {
388     vout_display_sys_t *sys = vd->sys;
389     vout_display_place_t place;
390     MMAL_DISPLAYREGION_T display_region;
391     MMAL_STATUS_T status;
392
393     if (!cfg && !fmt)
394         return -EINVAL;
395
396     if (fmt) {
397         sys->input->format->es->video.par.num = fmt->i_sar_num;
398         sys->input->format->es->video.par.den = fmt->i_sar_den;
399
400         status = mmal_port_format_commit(sys->input);
401         if (status != MMAL_SUCCESS) {
402             msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
403                             sys->input->name, status, mmal_status_to_string(status));
404             return -EINVAL;
405         }
406     } else {
407         fmt = &vd->source;
408     }
409
410     if (!cfg)
411         cfg = vd->cfg;
412
413     vout_display_PlacePicture(&place, fmt, cfg, false);
414
415     display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
416     display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
417     display_region.fullscreen = MMAL_FALSE;
418     display_region.src_rect.x = fmt->i_x_offset;
419     display_region.src_rect.y = fmt->i_y_offset;
420     display_region.src_rect.width = fmt->i_visible_width;
421     display_region.src_rect.height = fmt->i_visible_height;
422     display_region.dest_rect.x = place.x;
423     display_region.dest_rect.y = place.y;
424     display_region.dest_rect.width = place.width;
425     display_region.dest_rect.height = place.height;
426     display_region.layer = sys->layer;
427     display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
428             MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
429     status = mmal_port_parameter_set(sys->input, &display_region.hdr);
430     if (status != MMAL_SUCCESS) {
431         msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
432                         status, mmal_status_to_string(status));
433         return -EINVAL;
434     }
435
436     show_background(vd, var_InheritBool(vd, MMAL_BLANK_BACKGROUND_NAME));
437     sys->adjust_refresh_rate = var_InheritBool(vd, MMAL_ADJUST_REFRESHRATE_NAME);
438     sys->native_interlaced = var_InheritBool(vd, MMAL_NATIVE_INTERLACED);
439     if (sys->adjust_refresh_rate) {
440         adjust_refresh_rate(vd, fmt);
441         set_latency_target(vd, true);
442     }
443
444     return 0;
445 }
446
447 static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count)
448 {
449     vout_display_sys_t *sys = vd->sys;
450     picture_resource_t picture_res;
451     picture_pool_configuration_t picture_pool_cfg;
452     video_format_t fmt = vd->fmt;
453     MMAL_STATUS_T status;
454     unsigned i;
455
456     if (sys->picture_pool) {
457         if (sys->num_buffers < count)
458             msg_Warn(vd, "Picture pool with %u pictures requested, but we already have one with %u pictures",
459                             count, sys->num_buffers);
460
461         goto out;
462     }
463
464     if (sys->opaque) {
465         if (count <= NUM_ACTUAL_OPAQUE_BUFFERS)
466             count = NUM_ACTUAL_OPAQUE_BUFFERS;
467
468         MMAL_PARAMETER_BOOLEAN_T zero_copy = {
469             { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
470             1
471         };
472
473         status = mmal_port_parameter_set(sys->input, &zero_copy.hdr);
474         if (status != MMAL_SUCCESS) {
475            msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
476                     sys->input->name, status, mmal_status_to_string(status));
477            goto out;
478         }
479     }
480
481     if (count < sys->input->buffer_num_recommended)
482         count = sys->input->buffer_num_recommended;
483
484 #ifndef NDEBUG
485     msg_Dbg(vd, "Creating picture pool with %u pictures", count);
486 #endif
487
488     sys->input->buffer_num = count;
489     status = mmal_port_enable(sys->input, input_port_cb);
490     if (status != MMAL_SUCCESS) {
491         msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)",
492                         sys->input->name, status, mmal_status_to_string(status));
493         goto out;
494     }
495
496     status = mmal_component_enable(sys->component);
497     if (status != MMAL_SUCCESS) {
498         msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)",
499                         sys->component->name, status, mmal_status_to_string(status));
500         goto out;
501     }
502
503     sys->num_buffers = count;
504     sys->pool = mmal_port_pool_create(sys->input, sys->num_buffers,
505             sys->input->buffer_size);
506     if (!sys->pool) {
507         msg_Err(vd, "Failed to create MMAL pool for %u buffers of size %"PRIu32,
508                         count, sys->input->buffer_size);
509         goto out;
510     }
511
512     memset(&picture_res, 0, sizeof(picture_resource_t));
513     sys->pictures = calloc(sys->num_buffers, sizeof(picture_t *));
514     for (i = 0; i < sys->num_buffers; ++i) {
515         picture_res.p_sys = calloc(1, sizeof(picture_sys_t));
516         picture_res.p_sys->owner = (vlc_object_t *)vd;
517         picture_res.p_sys->buffer = mmal_queue_get(sys->pool->queue);
518
519         sys->pictures[i] = picture_NewFromResource(&fmt, &picture_res);
520         if (!sys->pictures[i]) {
521             msg_Err(vd, "Failed to create picture");
522             free(picture_res.p_sys);
523             goto out;
524         }
525
526         sys->pictures[i]->i_planes = sys->i_planes;
527         memcpy(sys->pictures[i]->p, sys->planes, sys->i_planes * sizeof(plane_t));
528     }
529
530     memset(&picture_pool_cfg, 0, sizeof(picture_pool_configuration_t));
531     picture_pool_cfg.picture_count = sys->num_buffers;
532     picture_pool_cfg.picture = sys->pictures;
533     picture_pool_cfg.lock = mmal_picture_lock;
534
535     sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg);
536     if (!sys->picture_pool) {
537         msg_Err(vd, "Failed to create picture pool");
538         goto out;
539     }
540
541 out:
542     return sys->picture_pool;
543 }
544
545 static void vd_prepare(vout_display_t *vd, picture_t *picture,
546                 subpicture_t *subpicture)
547 {
548     vout_display_sys_t *sys = vd->sys;
549     picture_sys_t *pic_sys = picture->p_sys;
550
551     if (!sys->adjust_refresh_rate || pic_sys->displayed)
552         return;
553
554     /* Apply the required phase_offset to the picture, so that vd_display()
555      * will be called at the corrected time from the core */
556     picture->date += sys->phase_offset;
557 }
558
559 static void vd_display(vout_display_t *vd, picture_t *picture,
560                 subpicture_t *subpicture)
561 {
562     vout_display_sys_t *sys = vd->sys;
563     picture_sys_t *pic_sys = picture->p_sys;
564     MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
565     MMAL_STATUS_T status;
566
567     if (picture->format.i_frame_rate != sys->i_frame_rate ||
568         picture->format.i_frame_rate_base != sys->i_frame_rate_base ||
569         picture->b_progressive != sys->b_progressive ||
570         picture->b_top_field_first != sys->b_top_field_first) {
571         sys->b_top_field_first = picture->b_top_field_first;
572         sys->b_progressive = picture->b_progressive;
573         sys->i_frame_rate = picture->format.i_frame_rate;
574         sys->i_frame_rate_base = picture->format.i_frame_rate_base;
575         configure_display(vd, NULL, &picture->format);
576     }
577
578     if (!pic_sys->displayed || !sys->opaque) {
579         buffer->cmd = 0;
580         buffer->length = sys->input->buffer_size;
581         buffer->user_data = picture;
582
583         status = mmal_port_send_buffer(sys->input, buffer);
584         if (status == MMAL_SUCCESS)
585             atomic_fetch_add(&sys->buffers_in_transit, 1);
586
587         if (status != MMAL_SUCCESS) {
588             msg_Err(vd, "Failed to send buffer to input port. Frame dropped");
589             picture_Release(picture);
590         }
591
592         pic_sys->displayed = true;
593     } else {
594         picture_Release(picture);
595     }
596
597     display_subpicture(vd, subpicture);
598
599     if (subpicture)
600         subpicture_Delete(subpicture);
601
602     if (sys->next_phase_check == 0 && sys->adjust_refresh_rate)
603         maintain_phase_sync(vd);
604     sys->next_phase_check = (sys->next_phase_check + 1) % PHASE_CHECK_INTERVAL;
605
606     if (sys->opaque) {
607         vlc_mutex_lock(&sys->buffer_mutex);
608         while (atomic_load(&sys->buffers_in_transit) >= MAX_BUFFERS_IN_TRANSIT)
609             vlc_cond_wait(&sys->buffer_cond, &sys->buffer_mutex);
610         vlc_mutex_unlock(&sys->buffer_mutex);
611     }
612 }
613
614 static int vd_control(vout_display_t *vd, int query, va_list args)
615 {
616     vout_display_sys_t *sys = vd->sys;
617     vout_display_cfg_t cfg;
618     const vout_display_cfg_t *tmp_cfg;
619     int ret = VLC_EGENERIC;
620
621     switch (query) {
622         case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
623             tmp_cfg = va_arg(args, const vout_display_cfg_t *);
624             if (tmp_cfg->display.width == sys->display_width &&
625                             tmp_cfg->display.height == sys->display_height) {
626                 cfg = *vd->cfg;
627                 cfg.display.width = sys->display_width;
628                 cfg.display.height = sys->display_height;
629                 if (configure_display(vd, &cfg, NULL) >= 0)
630                     ret = VLC_SUCCESS;
631             }
632             break;
633
634         case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
635         case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
636             if (configure_display(vd, NULL, &vd->source) >= 0)
637                 ret = VLC_SUCCESS;
638             break;
639
640         case VOUT_DISPLAY_RESET_PICTURES:
641             vlc_assert_unreachable();
642         case VOUT_DISPLAY_CHANGE_ZOOM:
643             msg_Warn(vd, "Unsupported control query %d", query);
644             break;
645
646         default:
647             msg_Warn(vd, "Unknown control query %d", query);
648             break;
649     }
650
651     return ret;
652 }
653
654 static void vd_manage(vout_display_t *vd)
655 {
656     vout_display_sys_t *sys = vd->sys;
657     unsigned width, height;
658
659     vlc_mutex_lock(&sys->manage_mutex);
660
661     if (sys->need_configure_display) {
662         close_dmx(vd);
663         sys->dmx_handle = vc_dispmanx_display_open(0);
664
665         if (query_resolution(vd, &width, &height) >= 0) {
666             sys->display_width = width;
667             sys->display_height = height;
668             vout_display_SendEventDisplaySize(vd, width, height);
669         }
670
671         sys->need_configure_display = false;
672     }
673
674     vlc_mutex_unlock(&sys->manage_mutex);
675 }
676
677 static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
678 {
679     vout_display_t *vd = (vout_display_t *)port->userdata;
680     MMAL_STATUS_T status;
681
682     if (buffer->cmd == MMAL_EVENT_ERROR) {
683         status = *(uint32_t *)buffer->data;
684         msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
685     }
686
687     mmal_buffer_header_release(buffer);
688 }
689
690 static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
691 {
692     vout_display_t *vd = (vout_display_t *)port->userdata;
693     vout_display_sys_t *sys = vd->sys;
694     picture_t *picture = (picture_t *)buffer->user_data;
695
696     if (picture)
697         picture_Release(picture);
698
699     vlc_mutex_lock(&sys->buffer_mutex);
700     atomic_fetch_sub(&sys->buffers_in_transit, 1);
701     vlc_cond_signal(&sys->buffer_cond);
702     vlc_mutex_unlock(&sys->buffer_mutex);
703 }
704
705 static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height)
706 {
707     TV_DISPLAY_STATE_T display_state;
708     int ret = 0;
709
710     if (vc_tv_get_display_state(&display_state) == 0) {
711         if (display_state.state & 0xFF) {
712             *width = display_state.display.hdmi.width;
713             *height = display_state.display.hdmi.height;
714         } else if (display_state.state & 0xFF00) {
715             *width = display_state.display.sdtv.width;
716             *height = display_state.display.sdtv.height;
717         } else {
718             msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state);
719             ret = -1;
720         }
721     } else {
722         msg_Warn(vd, "Failed to query display resolution");
723         ret = -1;
724     }
725
726     return ret;
727 }
728
729 static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2)
730 {
731     VLC_UNUSED(reason);
732     VLC_UNUSED(param1);
733     VLC_UNUSED(param2);
734
735     vout_display_t *vd = (vout_display_t *)callback_data;
736     vout_display_sys_t *sys = vd->sys;
737
738     vlc_mutex_lock(&sys->manage_mutex);
739     sys->need_configure_display = true;
740     vlc_mutex_unlock(&sys->manage_mutex);
741 }
742
743 static int set_latency_target(vout_display_t *vd, bool enable)
744 {
745     vout_display_sys_t *sys = vd->sys;
746     MMAL_STATUS_T status;
747
748     MMAL_PARAMETER_AUDIO_LATENCY_TARGET_T latency_target = {
749         .hdr = { MMAL_PARAMETER_AUDIO_LATENCY_TARGET, sizeof(latency_target) },
750         .enable = enable ? MMAL_TRUE : MMAL_FALSE,
751         .filter = 2,
752         .target = 4000,
753         .shift = 3,
754         .speed_factor = -135,
755         .inter_factor = 500,
756         .adj_cap = 20
757     };
758
759     status = mmal_port_parameter_set(sys->input, &latency_target.hdr);
760     if (status != MMAL_SUCCESS) {
761         msg_Err(vd, "Failed to configure latency target on input port %s (status=%"PRIx32" %s)",
762                         sys->input->name, status, mmal_status_to_string(status));
763         return VLC_EGENERIC;
764     }
765
766     return VLC_SUCCESS;
767 }
768
769 static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt)
770 {
771     vout_display_sys_t *sys = vd->sys;
772     TV_DISPLAY_STATE_T display_state;
773     TV_SUPPORTED_MODE_NEW_T supported_modes[VC_TV_MAX_MODE_IDS];
774     char response[20]; /* answer is hvs_update_fields=%1d */
775     int num_modes;
776     double frame_rate = (double)fmt->i_frame_rate / fmt->i_frame_rate_base;
777     int best_id = -1;
778     double best_score, score;
779     int i;
780
781     vc_tv_get_display_state(&display_state);
782     if(display_state.display.hdmi.mode != HDMI_MODE_OFF) {
783         num_modes = vc_tv_hdmi_get_supported_modes_new(display_state.display.hdmi.group,
784                         supported_modes, VC_TV_MAX_MODE_IDS, NULL, NULL);
785
786         for (i = 0; i < num_modes; ++i) {
787             TV_SUPPORTED_MODE_NEW_T *mode = &supported_modes[i];
788             if (!sys->native_interlaced) {
789                 if (mode->width != display_state.display.hdmi.width ||
790                                 mode->height != display_state.display.hdmi.height ||
791                                 mode->scan_mode == HDMI_INTERLACED)
792                     continue;
793             } else {
794                 if (mode->width != vd->fmt.i_visible_width ||
795                         mode->height != vd->fmt.i_visible_height)
796                     continue;
797                 if (mode->scan_mode != sys->b_progressive ? HDMI_NONINTERLACED : HDMI_INTERLACED)
798                     continue;
799             }
800
801             score = fmod(supported_modes[i].frame_rate, frame_rate);
802             if((best_id < 0) || (score < best_score)) {
803                 best_id = i;
804                 best_score = score;
805             }
806         }
807
808         if((best_id >= 0) && (display_state.display.hdmi.mode != supported_modes[best_id].code)) {
809             msg_Info(vd, "Setting HDMI refresh rate to %"PRIu32,
810                             supported_modes[best_id].frame_rate);
811             vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI,
812                             supported_modes[best_id].group,
813                             supported_modes[best_id].code);
814         }
815
816         if (sys->native_interlaced &&
817                 supported_modes[best_id].scan_mode == HDMI_INTERLACED) {
818             char hvs_mode = sys->b_top_field_first ? '1' : '2';
819             if (vc_gencmd(response, sizeof(response), "hvs_update_fields %c",
820                     hvs_mode) != 0 || response[18] != hvs_mode)
821                 msg_Warn(vd, "Could not set hvs field mode");
822             else
823                 msg_Info(vd, "Configured hvs field mode for interlaced %s playback",
824                         sys->b_top_field_first ? "tff" : "bff");
825         }
826     }
827 }
828
829 static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture)
830 {
831     vout_display_sys_t *sys = vd->sys;
832     struct dmx_region_t **dmx_region = &sys->dmx_region;
833     struct dmx_region_t *unused_dmx_region;
834     DISPMANX_UPDATE_HANDLE_T update = 0;
835     picture_t *picture;
836     video_format_t *fmt;
837     struct dmx_region_t *dmx_region_next;
838
839     if(subpicture) {
840         subpicture_region_t *region = subpicture->p_region;
841         while(region) {
842             picture = region->p_picture;
843             fmt = &region->fmt;
844
845             if(!*dmx_region) {
846                 if(!update)
847                     update = vc_dispmanx_update_start(10);
848                 *dmx_region = dmx_region_new(vd, update, region);
849             } else if(((*dmx_region)->bmp_rect.width != (int32_t)fmt->i_visible_width) ||
850                     ((*dmx_region)->bmp_rect.height != (int32_t)fmt->i_visible_height) ||
851                     ((*dmx_region)->pos_x != region->i_x) ||
852                     ((*dmx_region)->pos_y != region->i_y) ||
853                     ((*dmx_region)->alpha.opacity != (uint32_t)region->i_alpha)) {
854                 dmx_region_next = (*dmx_region)->next;
855                 if(!update)
856                     update = vc_dispmanx_update_start(10);
857                 dmx_region_delete(*dmx_region, update);
858                 *dmx_region = dmx_region_new(vd, update, region);
859                 (*dmx_region)->next = dmx_region_next;
860             } else if((*dmx_region)->picture != picture) {
861                 if(!update)
862                     update = vc_dispmanx_update_start(10);
863                 dmx_region_update(*dmx_region, update, picture);
864             }
865
866             dmx_region = &(*dmx_region)->next;
867             region = region->p_next;
868         }
869     }
870
871     /* Remove remaining regions */
872     unused_dmx_region = *dmx_region;
873     while(unused_dmx_region) {
874         dmx_region_next = unused_dmx_region->next;
875         if(!update)
876             update = vc_dispmanx_update_start(10);
877         dmx_region_delete(unused_dmx_region, update);
878         unused_dmx_region = dmx_region_next;
879     }
880     *dmx_region = NULL;
881
882     if(update)
883         vc_dispmanx_update_submit_sync(update);
884 }
885
886 static void close_dmx(vout_display_t *vd)
887 {
888     vout_display_sys_t *sys = vd->sys;
889     DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(10);
890     struct dmx_region_t *dmx_region = sys->dmx_region;
891     struct dmx_region_t *dmx_region_next;
892
893     while(dmx_region) {
894         dmx_region_next = dmx_region->next;
895         dmx_region_delete(dmx_region, update);
896         dmx_region = dmx_region_next;
897     }
898
899     vc_dispmanx_update_submit_sync(update);
900     sys->dmx_region = NULL;
901
902     show_background(vd, false);
903
904     vc_dispmanx_display_close(sys->dmx_handle);
905     sys->dmx_handle = DISPMANX_NO_HANDLE;
906 }
907
908 static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
909                 DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region)
910 {
911     vout_display_sys_t *sys = vd->sys;
912     video_format_t *fmt = &region->fmt;
913     struct dmx_region_t *dmx_region = malloc(sizeof(struct dmx_region_t));
914     uint32_t image_handle;
915
916     dmx_region->pos_x = region->i_x;
917     dmx_region->pos_y = region->i_y;
918
919     vc_dispmanx_rect_set(&dmx_region->bmp_rect, 0, 0, fmt->i_visible_width,
920                     fmt->i_visible_height);
921     vc_dispmanx_rect_set(&dmx_region->src_rect, 0, 0, fmt->i_visible_width << 16,
922                     fmt->i_visible_height << 16);
923     vc_dispmanx_rect_set(&dmx_region->dst_rect, region->i_x, region->i_y,
924                     fmt->i_visible_width, fmt->i_visible_height);
925
926     dmx_region->resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32,
927                     dmx_region->bmp_rect.width | (region->p_picture->p[0].i_pitch << 16),
928                     dmx_region->bmp_rect.height | (dmx_region->bmp_rect.height << 16),
929                     &image_handle);
930     vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
931                     region->p_picture->p[0].i_pitch,
932                     region->p_picture->p[0].p_pixels, &dmx_region->bmp_rect);
933
934     dmx_region->alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_MIX;
935     dmx_region->alpha.opacity = region->i_alpha;
936     dmx_region->alpha.mask = DISPMANX_NO_HANDLE;
937     dmx_region->element = vc_dispmanx_element_add(update, sys->dmx_handle,
938                     sys->layer + 1, &dmx_region->dst_rect, dmx_region->resource,
939                     &dmx_region->src_rect, DISPMANX_PROTECTION_NONE,
940                     &dmx_region->alpha, NULL, VC_IMAGE_ROT0);
941
942     dmx_region->next = NULL;
943     dmx_region->picture = region->p_picture;
944
945     return dmx_region;
946 }
947
948 static void dmx_region_update(struct dmx_region_t *dmx_region,
949                 DISPMANX_UPDATE_HANDLE_T update, picture_t *picture)
950 {
951     vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
952                     picture->p[0].i_pitch, picture->p[0].p_pixels, &dmx_region->bmp_rect);
953     vc_dispmanx_element_change_source(update, dmx_region->element, dmx_region->resource);
954     dmx_region->picture = picture;
955 }
956
957 static void dmx_region_delete(struct dmx_region_t *dmx_region,
958                 DISPMANX_UPDATE_HANDLE_T update)
959 {
960     vc_dispmanx_element_remove(update, dmx_region->element);
961     vc_dispmanx_resource_delete(dmx_region->resource);
962     free(dmx_region);
963 }
964
965 static void maintain_phase_sync(vout_display_t *vd)
966 {
967     MMAL_PARAMETER_VIDEO_RENDER_STATS_T render_stats = {
968         .hdr = { MMAL_PARAMETER_VIDEO_RENDER_STATS, sizeof(render_stats) },
969     };
970     int32_t frame_duration = 1000000 /
971         ((double)vd->sys->i_frame_rate /
972         vd->sys->i_frame_rate_base);
973     vout_display_sys_t *sys = vd->sys;
974     int32_t phase_offset;
975     MMAL_STATUS_T status;
976
977     status = mmal_port_parameter_get(sys->input, &render_stats.hdr);
978     if (status != MMAL_SUCCESS) {
979         msg_Err(vd, "Failed to read render stats on control port %s (status=%"PRIx32" %s)",
980                         sys->input->name, status, mmal_status_to_string(status));
981         return;
982     }
983
984     if (render_stats.valid) {
985 #ifndef NDEBUG
986         msg_Dbg(vd, "render_stats: match: %u, period: %u ms, phase: %u ms, hvs: %u",
987                 render_stats.match, render_stats.period / 1000, render_stats.phase / 1000,
988                 render_stats.hvs_status);
989 #endif
990
991         if (render_stats.phase > 0.1 * frame_duration &&
992                 render_stats.phase < 0.75 * frame_duration)
993             return;
994
995         phase_offset = frame_duration * PHASE_OFFSET_TARGET - render_stats.phase;
996         if (phase_offset < 0)
997             phase_offset += frame_duration;
998         else
999             phase_offset %= frame_duration;
1000
1001         sys->phase_offset += phase_offset;
1002         sys->phase_offset %= frame_duration;
1003         msg_Dbg(vd, "Apply phase offset of %"PRId32" ms (total offset %"PRId32" ms)",
1004                 phase_offset / 1000, sys->phase_offset / 1000);
1005
1006         /* Reset the latency target, so that it does not get confused
1007          * by the jump in the offset */
1008         set_latency_target(vd, false);
1009         set_latency_target(vd, true);
1010     }
1011 }
1012
1013 static void show_background(vout_display_t *vd, bool enable)
1014 {
1015     vout_display_sys_t *sys = vd->sys;
1016     uint32_t image_ptr, color = 0xFF000000;
1017     VC_RECT_T dst_rect, src_rect;
1018     DISPMANX_UPDATE_HANDLE_T update;
1019
1020     if (enable && !sys->bkg_element) {
1021         sys->bkg_resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32, 1, 1,
1022                         &image_ptr);
1023         vc_dispmanx_rect_set(&dst_rect, 0, 0, 1, 1);
1024         vc_dispmanx_resource_write_data(sys->bkg_resource, VC_IMAGE_RGBA32,
1025                         sizeof(color), &color, &dst_rect);
1026         vc_dispmanx_rect_set(&src_rect, 0, 0, 1 << 16, 1 << 16);
1027         vc_dispmanx_rect_set(&dst_rect, 0, 0, 0, 0);
1028         update = vc_dispmanx_update_start(0);
1029         sys->bkg_element = vc_dispmanx_element_add(update, sys->dmx_handle,
1030                         sys->layer - 1, &dst_rect, sys->bkg_resource, &src_rect,
1031                         DISPMANX_PROTECTION_NONE, NULL, NULL, VC_IMAGE_ROT0);
1032         vc_dispmanx_update_submit_sync(update);
1033     } else if (!enable && sys->bkg_element) {
1034         update = vc_dispmanx_update_start(0);
1035         vc_dispmanx_element_remove(update, sys->bkg_element);
1036         vc_dispmanx_resource_delete(sys->bkg_resource);
1037         vc_dispmanx_update_submit_sync(update);
1038         sys->bkg_element = DISPMANX_NO_HANDLE;
1039         sys->bkg_resource = DISPMANX_NO_HANDLE;
1040     }
1041 }