1 /*****************************************************************************
2 * mmal.c: MMAL-based vout plugin for Raspberry Pi
3 *****************************************************************************
4 * Copyright © 2014 jusst technologies GmbH
7 * Authors: Dennis Hamester <dennis.hamester@gmail.com>
8 * Julian Scheel <julian@jusst.de>
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.
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.
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 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_threads.h>
34 #include <vlc_vout_display.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>
43 /* This value must match the define in codec/mmal.c
44 * Think twice before changing this. Incorrect values cause havoc.
46 #define NUM_ACTUAL_OPAQUE_BUFFERS 40
48 #define MAX_BUFFERS_IN_TRANSIT 2
49 #define VC_TV_MAX_MODE_IDS 127
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.")
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.")
59 static int Open(vlc_object_t *);
60 static void Close(vlc_object_t *);
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)
74 struct dmx_region_t *next;
79 VC_DISPMANX_ALPHA_T alpha;
80 DISPMANX_ELEMENT_HANDLE_T element;
81 DISPMANX_RESOURCE_HANDLE_T resource;
86 struct vout_display_sys_t {
88 picture_pool_t *picture_pool;
90 MMAL_COMPONENT_T *component;
93 struct dmx_region_t *dmx_region;
96 vlc_mutex_t buffer_mutex;
97 vlc_mutex_t manage_mutex;
98 vlc_cond_t buffer_cond;
100 int buffers_in_transit;
101 unsigned num_buffers;
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;
114 struct picture_sys_t {
116 MMAL_BUFFER_HEADER_T *buffer;
120 static const vlc_fourcc_t subpicture_chromas[] = {
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);
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);
137 /* VLC picture pool */
138 static int picture_lock(picture_t *picture);
139 static void picture_unlock(picture_t *picture);
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);
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,
149 static void adjust_refresh_rate(vout_display_t *vd);
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);
162 static int Open(vlc_object_t *object)
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;
170 MMAL_STATUS_T status;
171 int ret = VLC_SUCCESS;
174 sys = calloc(1, sizeof(struct vout_display_sys_t));
179 sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
182 vd->info.has_hide_mouse = true;
183 sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_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;
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;
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));
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));
211 sys->input = sys->component->input[0];
212 sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
214 sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
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;
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));
233 sys->input->buffer_size = sys->input->buffer_size_recommended;
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));
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;
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;
273 sys->planes[i].p_pixels = (uint8_t *)offsets[i];
276 vlc_mutex_init(&sys->buffer_mutex);
277 vlc_cond_init(&sys->buffer_cond);
278 vlc_mutex_init(&sys->manage_mutex);
281 vd->display = vd_display;
282 vd->control = vd_control;
283 vd->manage = vd_manage;
285 vc_tv_register_callback(tvservice_cb, vd);
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);
291 sys->display_width = vd->cfg->display.width;
292 sys->display_height = vd->cfg->display.height;
295 sys->dmx_handle = vc_dispmanx_display_open(0);
296 vd->info.subpicture_chromas = subpicture_chromas;
299 if (ret != VLC_SUCCESS)
305 static void Close(vlc_object_t *object)
307 vout_display_t *vd = (vout_display_t *)object;
308 vout_display_sys_t *sys = vd->sys;
311 vc_tv_unregister_callback_full(tvservice_cb, vd);
316 if (sys->component && sys->component->control->is_enabled)
317 mmal_port_disable(sys->component->control);
319 if (sys->input && sys->input->is_enabled)
320 mmal_port_disable(sys->input);
322 if (sys->component && sys->component->is_enabled)
323 mmal_component_disable(sys->component);
326 mmal_pool_destroy(sys->pool);
329 mmal_component_release(sys->component);
331 if (sys->picture_pool)
332 picture_pool_Delete(sys->picture_pool);
334 for (i = 0; i < sys->num_buffers; ++i)
335 if (sys->pictures[i])
336 picture_Release(sys->pictures[i]);
338 vlc_mutex_destroy(&sys->buffer_mutex);
339 vlc_cond_destroy(&sys->buffer_cond);
340 vlc_mutex_destroy(&sys->manage_mutex);
347 static inline uint32_t align(uint32_t x, uint32_t y) {
348 uint32_t mod = x % y;
355 static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
356 const video_format_t *fmt)
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;
367 sys->input->format->es->video.par.num = fmt->i_sar_num;
368 sys->input->format->es->video.par.den = fmt->i_sar_den;
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));
383 vout_display_PlacePicture(&place, fmt, cfg, false);
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));
406 show_background(vd, cfg->is_fullscreen);
407 if (var_InheritBool(vd, MMAL_ADJUST_REFRESHRATE_NAME))
408 adjust_refresh_rate(vd);
411 memcpy(&vd->fmt, fmt, sizeof(video_format_t));
416 static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count)
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;
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);
434 if (count <= NUM_ACTUAL_OPAQUE_BUFFERS)
435 count = NUM_ACTUAL_OPAQUE_BUFFERS;
437 msg_Err(vd, "More picture (%u) than NUM_ACTUAL_OPAQUE_BUFFERS (%d) requested. Expect errors",
438 count, NUM_ACTUAL_OPAQUE_BUFFERS);
441 if (count < sys->input->buffer_num_recommended)
442 count = sys->input->buffer_num_recommended;
445 msg_Dbg(vd, "Creating picture pool with %u pictures", count);
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));
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));
463 sys->pool = mmal_pool_create_with_allocator(count, sys->input->buffer_size,
465 (mmal_pool_allocator_alloc_t)mmal_port_payload_alloc,
466 (mmal_pool_allocator_free_t)mmal_port_payload_free);
468 msg_Err(vd, "Failed to create MMAL pool for %u buffers of size %"PRIu32,
469 count, sys->input->buffer_size);
472 sys->num_buffers = count;
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;
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);
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;
495 sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg);
496 if (!sys->picture_pool) {
497 msg_Err(vd, "Failed to create picture pool");
502 return sys->picture_pool;
505 static void vd_display(vout_display_t *vd, picture_t *picture,
506 subpicture_t *subpicture)
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;
513 if (!pic_sys->displayed || !sys->opaque) {
515 buffer->length = sys->input->buffer_size;
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);
521 status = mmal_port_send_buffer(sys->input, buffer);
522 if (status == MMAL_SUCCESS)
523 ++sys->buffers_in_transit;
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);
531 pic_sys->displayed = true;
533 picture_Release(picture);
536 display_subpicture(vd, subpicture);
539 subpicture_Delete(subpicture);
542 static int vd_control(vout_display_t *vd, int query, va_list args)
544 vout_display_sys_t *sys = vd->sys;
545 vout_display_cfg_t cfg;
546 const vout_display_cfg_t *tmp_cfg;
548 const video_format_t *tmp_fmt;
549 int ret = VLC_EGENERIC;
552 case VOUT_DISPLAY_HIDE_MOUSE:
553 case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
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);
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) {
569 cfg.display.width = sys->display_width;
570 cfg.display.height = sys->display_height;
571 if (configure_display(vd, &cfg, NULL) >= 0)
576 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
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)
584 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
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)
593 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
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)
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);
611 msg_Warn(vd, "Unknown control query %d", query);
618 static void vd_manage(vout_display_t *vd)
620 vout_display_sys_t *sys = vd->sys;
621 unsigned width, height;
623 vlc_mutex_lock(&sys->manage_mutex);
625 if (sys->need_configure_display) {
627 sys->dmx_handle = vc_dispmanx_display_open(0);
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);
635 sys->need_configure_display = false;
638 vlc_mutex_unlock(&sys->manage_mutex);
641 static int picture_lock(picture_t *picture)
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;
647 MMAL_BUFFER_HEADER_T *buffer = mmal_queue_wait(sys->pool->queue);
651 vlc_mutex_lock(&sys->buffer_mutex);
653 mmal_buffer_header_reset(buffer);
654 buffer->user_data = picture;
655 pic_sys->buffer = buffer;
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;
662 pic_sys->displayed = false;
664 vlc_mutex_unlock(&sys->buffer_mutex);
669 static void picture_unlock(picture_t *picture)
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;
676 vlc_mutex_lock(&sys->buffer_mutex);
678 pic_sys->buffer = NULL;
680 buffer->user_data = NULL;
681 mmal_buffer_header_release(buffer);
684 vlc_mutex_unlock(&sys->buffer_mutex);
687 static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
689 vout_display_t *vd = (vout_display_t *)port->userdata;
690 MMAL_STATUS_T status;
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));
697 mmal_buffer_header_release(buffer);
700 static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
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;
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);
712 picture_Release(picture);
715 static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height)
717 TV_DISPLAY_STATE_T display_state;
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;
728 msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state);
732 msg_Warn(vd, "Failed to query display resolution");
739 static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2)
745 vout_display_t *vd = (vout_display_t *)callback_data;
746 vout_display_sys_t *sys = vd->sys;
748 vlc_mutex_lock(&sys->manage_mutex);
749 sys->need_configure_display = true;
750 vlc_mutex_unlock(&sys->manage_mutex);
753 static void adjust_refresh_rate(vout_display_t *vd)
755 TV_DISPLAY_STATE_T display_state;
756 TV_SUPPORTED_MODE_NEW_T supported_modes[VC_TV_MAX_MODE_IDS];
758 double frame_rate = (double)vd->fmt.i_frame_rate / vd->fmt.i_frame_rate_base;
760 double best_score, score;
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);
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)
774 score = fmod(supported_modes[i].frame_rate, frame_rate);
775 if((best_id < 0) || (score < best_score)) {
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);
791 static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture)
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;
799 struct dmx_region_t *dmx_region_next;
802 subpicture_region_t *region = subpicture->p_region;
804 picture = region->p_picture;
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;
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) {
824 update = vc_dispmanx_update_start(10);
825 dmx_region_update(*dmx_region, update, picture);
828 dmx_region = &(*dmx_region)->next;
829 region = region->p_next;
833 /* Remove remaining regions */
834 unused_dmx_region = *dmx_region;
835 while(unused_dmx_region) {
836 dmx_region_next = unused_dmx_region->next;
838 update = vc_dispmanx_update_start(10);
839 dmx_region_delete(unused_dmx_region, update);
840 unused_dmx_region = dmx_region_next;
845 vc_dispmanx_update_submit_sync(update);
848 static void close_dmx(vout_display_t *vd)
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;
856 dmx_region_next = dmx_region->next;
857 dmx_region_delete(dmx_region, update);
858 dmx_region = dmx_region_next;
861 vc_dispmanx_update_submit_sync(update);
862 sys->dmx_region = NULL;
864 show_background(vd, false);
866 vc_dispmanx_display_close(sys->dmx_handle);
867 sys->dmx_handle = DISPMANX_NO_HANDLE;
870 static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
871 DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region)
873 vout_display_sys_t *sys = vd->sys;
874 video_format_t *fmt = ®ion->fmt;
875 struct dmx_region_t *dmx_region = malloc(sizeof(struct dmx_region_t));
876 uint32_t image_handle;
878 dmx_region->pos_x = region->i_x;
879 dmx_region->pos_y = region->i_y;
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);
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),
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);
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);
904 dmx_region->next = NULL;
905 dmx_region->picture = region->p_picture;
910 static void dmx_region_update(struct dmx_region_t *dmx_region,
911 DISPMANX_UPDATE_HANDLE_T update, picture_t *picture)
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;
919 static void dmx_region_delete(struct dmx_region_t *dmx_region,
920 DISPMANX_UPDATE_HANDLE_T update)
922 vc_dispmanx_element_remove(update, dmx_region->element);
923 vc_dispmanx_resource_delete(dmx_region->resource);
927 static void show_background(vout_display_t *vd, bool enable)
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;
934 if (enable && !sys->bkg_element) {
935 sys->bkg_resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32, 1, 1,
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;