1
/*****************************************************************************
2
* display.c: "vout display" managment
3
*****************************************************************************
4
* Copyright (C) 2009 Laurent Aimar
5
* $Id: 198cd8f6d82f96cdeb073825106e530acf099570 $
7
* Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
9
* This program is free software; you can redistribute it and/or modify
10
* it under the terms of the GNU General Public License as published by
11
* the Free Software Foundation; either version 2 of the License, or
12
* (at your option) any later version.
14
* This program is distributed in the hope that it will be useful,
15
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
* GNU General Public License for more details.
19
* You should have received a copy of the GNU General Public License
20
* along with this program; if not, write to the Free Software
21
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
*****************************************************************************/
24
/*****************************************************************************
26
*****************************************************************************/
32
#include <vlc_common.h>
33
#include <vlc_video_splitter.h>
34
#include <vlc_vout_display.h>
43
/* It must be present as long as a vout_display_t must be created using a dummy
44
* vout (as an opengl provider) */
45
#define ALLOW_DUMMY_VOUT
47
static void SplitterClose(vout_display_t *vd);
49
/*****************************************************************************
50
* FIXME/TODO see how to have direct rendering here (interact with vout.c)
51
*****************************************************************************/
52
static picture_t *VideoBufferNew(filter_t *filter)
54
vout_display_t *vd = (vout_display_t*)filter->p_owner;
55
const video_format_t *fmt = &filter->fmt_out.video;
57
assert(vd->fmt.i_chroma == fmt->i_chroma &&
58
vd->fmt.i_width == fmt->i_width &&
59
vd->fmt.i_height == fmt->i_height);
61
picture_pool_t *pool = vout_display_Pool(vd, 1);
64
return picture_pool_Get(pool);
66
static void VideoBufferDelete(filter_t *filter, picture_t *picture)
69
picture_Release(picture);
72
static int FilterAllocationInit(filter_t *filter, void *vd)
74
filter->pf_video_buffer_new = VideoBufferNew;
75
filter->pf_video_buffer_del = VideoBufferDelete;
80
static void FilterAllocationClean(filter_t *filter)
82
filter->pf_video_buffer_new = NULL;
83
filter->pf_video_buffer_del = NULL;
84
filter->p_owner = NULL;
87
/*****************************************************************************
89
*****************************************************************************/
92
* It creates a new vout_display_t using the given configuration.
94
static vout_display_t *vout_display_New(vlc_object_t *obj,
95
const char *module, bool load_module,
96
const video_format_t *fmt,
97
const vout_display_cfg_t *cfg,
98
vout_display_owner_t *owner)
101
vout_display_t *vd = vlc_object_create(obj, sizeof(*vd));
104
video_format_Copy(&vd->source, fmt);
106
/* Picture buffer does not have the concept of aspect ratio */
107
video_format_Copy(&vd->fmt, fmt);
108
vd->fmt.i_sar_num = 0;
109
vd->fmt.i_sar_den = 0;
111
vd->info.is_slow = false;
112
vd->info.has_double_click = false;
113
vd->info.has_hide_mouse = false;
114
vd->info.has_pictures_invalid = false;
126
vlc_object_attach(vd, obj);
129
vd->module = module_need(vd, "vout display", module, module && *module != '\0');
131
vlc_object_release(vd);
141
* It deletes a vout_display_t
143
static void vout_display_Delete(vout_display_t *vd)
146
module_unneed(vd, vd->module);
148
vlc_object_release(vd);
152
* It controls a vout_display_t
154
static int vout_display_Control(vout_display_t *vd, int query, ...)
159
va_start(args, query);
160
result = vd->control(vd, query, args);
165
static void vout_display_Manage(vout_display_t *vd)
172
void vout_display_GetDefaultDisplaySize(unsigned *width, unsigned *height,
173
const video_format_t *source,
174
const vout_display_cfg_t *cfg)
176
if (cfg->display.width > 0 && cfg->display.height > 0) {
177
*width = cfg->display.width;
178
*height = cfg->display.height;
179
} else if (cfg->display.width > 0) {
180
*width = cfg->display.width;
181
*height = (int64_t)source->i_visible_height * source->i_sar_den * cfg->display.width * cfg->display.sar.num /
182
source->i_visible_width / source->i_sar_num / cfg->display.sar.den;
183
} else if (cfg->display.height > 0) {
184
*width = (int64_t)source->i_visible_width * source->i_sar_num * cfg->display.height * cfg->display.sar.den /
185
source->i_visible_height / source->i_sar_den / cfg->display.sar.num;
186
*height = cfg->display.height;
187
} else if (source->i_sar_num >= source->i_sar_den) {
188
*width = (int64_t)source->i_visible_width * source->i_sar_num * cfg->display.sar.den / source->i_sar_den / cfg->display.sar.num;
189
*height = source->i_visible_height;
191
*width = source->i_visible_width;
192
*height = (int64_t)source->i_visible_height * source->i_sar_den * cfg->display.sar.num / source->i_sar_num / cfg->display.sar.den;
195
*width = *width * cfg->zoom.num / cfg->zoom.den;
196
*height = *height * cfg->zoom.num / cfg->zoom.den;
200
void vout_display_PlacePicture(vout_display_place_t *place,
201
const video_format_t *source,
202
const vout_display_cfg_t *cfg,
206
memset(place, 0, sizeof(*place));
207
if (cfg->display.width <= 0 || cfg->display.height <= 0)
211
unsigned display_width;
212
unsigned display_height;
214
if (cfg->is_display_filled) {
215
display_width = cfg->display.width;
216
display_height = cfg->display.height;
218
vout_display_cfg_t cfg_tmp = *cfg;
220
cfg_tmp.display.width = 0;
221
cfg_tmp.display.height = 0;
222
vout_display_GetDefaultDisplaySize(&display_width, &display_height,
226
display_width = __MIN(display_width, cfg->display.width);
227
display_height = __MIN(display_height, cfg->display.height);
231
const unsigned width = source->i_visible_width;
232
const unsigned height = source->i_visible_height;
233
/* Compute the height if we use the width to fill up display_width */
234
const int64_t scaled_height = (int64_t)height * display_width * cfg->display.sar.num * source->i_sar_den / width / source->i_sar_num / cfg->display.sar.den;
235
/* And the same but switching width/height */
236
const int64_t scaled_width = (int64_t)width * display_height * cfg->display.sar.den * source->i_sar_num / height / source->i_sar_den / cfg->display.sar.num;
238
/* We keep the solution that avoid filling outside the display */
239
if (scaled_width <= cfg->display.width) {
240
place->width = scaled_width;
241
place->height = display_height;
243
place->width = display_width;
244
place->height = scaled_height;
247
/* Compute position */
248
switch (cfg->align.horizontal) {
249
case VOUT_DISPLAY_ALIGN_LEFT:
252
case VOUT_DISPLAY_ALIGN_RIGHT:
253
place->x = cfg->display.width - place->width;
256
place->x = ((int)cfg->display.width - (int)place->width) / 2;
260
switch (cfg->align.vertical) {
261
case VOUT_DISPLAY_ALIGN_TOP:
264
case VOUT_DISPLAY_ALIGN_BOTTOM:
265
place->y = cfg->display.height - place->height;
268
place->y = ((int)cfg->display.height - (int)place->height) / 2;
273
struct vout_display_owner_sys_t {
275
bool is_wrapper; /* Is the current display a wrapper */
276
vout_display_t *wrapper; /* Vout display wrapper */
279
vout_display_cfg_t cfg;
280
unsigned wm_state_initial;
296
bool ch_display_filled;
297
bool is_display_filled;
325
video_format_t source;
326
filter_chain_t *filters;
328
/* Lock protecting the variables used by
329
* VoutDisplayEvent(ie vout_display_SendEvent) */
336
mtime_t last_pressed;
342
mtime_t double_click_timeout;
343
mtime_t hide_timeout;
351
bool ch_display_size;
354
bool display_is_fullscreen;
355
bool display_is_forced;
359
#ifdef ALLOW_DUMMY_VOUT
360
vlc_mouse_t vout_mouse;
364
static void DummyVoutSendDisplayEventMouse(vout_thread_t *, vlc_mouse_t *fallback, const vlc_mouse_t *m);
366
static void VoutDisplayCreateRender(vout_display_t *vd)
368
vout_display_owner_sys_t *osys = vd->owner.sys;
370
osys->filters = NULL;
372
video_format_t v_src = vd->source;
376
video_format_t v_dst = vd->fmt;
380
video_format_t v_dst_cmp = v_dst;
381
if ((v_src.i_chroma == VLC_CODEC_J420 && v_dst.i_chroma == VLC_CODEC_I420) ||
382
(v_src.i_chroma == VLC_CODEC_J422 && v_dst.i_chroma == VLC_CODEC_I422) ||
383
(v_src.i_chroma == VLC_CODEC_J440 && v_dst.i_chroma == VLC_CODEC_I440) ||
384
(v_src.i_chroma == VLC_CODEC_J444 && v_dst.i_chroma == VLC_CODEC_I444))
385
v_dst_cmp.i_chroma = v_src.i_chroma;
387
const bool convert = memcmp(&v_src, &v_dst_cmp, sizeof(v_src)) != 0;
391
msg_Dbg(vd, "A filter to adapt decoder to display is needed");
393
osys->filters = filter_chain_New(vd, "video filter2", false,
394
FilterAllocationInit,
395
FilterAllocationClean, vd);
396
assert(osys->filters); /* TODO critical */
400
es_format_InitFromVideo(&src, &v_src);
406
for (int i = 0; i < 1 + (v_dst_cmp.i_chroma != v_dst.i_chroma); i++) {
408
es_format_InitFromVideo(&dst, i == 0 ? &v_dst : &v_dst_cmp);
410
filter_chain_Reset(osys->filters, &src, &dst);
411
filter = filter_chain_AppendFilter(osys->filters,
412
NULL, NULL, &src, &dst);
418
msg_Err(vd, "VoutDisplayCreateRender FAILED");
424
static void VoutDisplayDestroyRender(vout_display_t *vd)
426
vout_display_owner_sys_t *osys = vd->owner.sys;
429
filter_chain_Delete(osys->filters);
432
static void VoutDisplayResetRender(vout_display_t *vd)
434
VoutDisplayDestroyRender(vd);
435
VoutDisplayCreateRender(vd);
437
static void VoutDisplayEventMouse(vout_display_t *vd, int event, va_list args)
439
vout_display_owner_sys_t *osys = vd->owner.sys;
441
vlc_mutex_lock(&osys->lock);
444
vlc_mouse_t m = osys->mouse.state;
445
bool is_ignored = false;
448
case VOUT_DISPLAY_EVENT_MOUSE_STATE: {
449
const int x = (int)va_arg(args, int);
450
const int y = (int)va_arg(args, int);
451
const int button_mask = (int)va_arg(args, int);
456
m.i_pressed = button_mask;
459
case VOUT_DISPLAY_EVENT_MOUSE_MOVED: {
460
const int x = (int)va_arg(args, int);
461
const int y = (int)va_arg(args, int);
462
if (x != osys->mouse.state.i_x || y != osys->mouse.state.i_y) {
463
//msg_Dbg(vd, "VoutDisplayEvent 'mouse' @%d,%d", x, y);
467
m.b_double_click = false;
473
case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
474
case VOUT_DISPLAY_EVENT_MOUSE_RELEASED: {
475
const int button = (int)va_arg(args, int);
476
const int button_mask = 1 << button;
478
/* Ignore inconsistent event */
479
if ((event == VOUT_DISPLAY_EVENT_MOUSE_PRESSED && (osys->mouse.state.i_pressed & button_mask)) ||
480
(event == VOUT_DISPLAY_EVENT_MOUSE_RELEASED && !(osys->mouse.state.i_pressed & button_mask))) {
486
msg_Dbg(vd, "VoutDisplayEvent 'mouse button' %d t=%d", button, event);
488
m.b_double_click = false;
489
if (event == VOUT_DISPLAY_EVENT_MOUSE_PRESSED)
490
m.i_pressed |= button_mask;
492
m.i_pressed &= ~button_mask;
495
case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
496
msg_Dbg(vd, "VoutDisplayEvent 'double click'");
498
m.b_double_click = true;
505
vlc_mutex_unlock(&osys->lock);
509
/* Emulate double-click if needed */
510
if (!vd->info.has_double_click &&
511
vlc_mouse_HasPressed(&osys->mouse.state, &m, MOUSE_BUTTON_LEFT)) {
512
const mtime_t i_date = mdate();
514
if (i_date - osys->mouse.last_pressed < osys->mouse.double_click_timeout ) {
515
m.b_double_click = true;
516
osys->mouse.last_pressed = 0;
518
osys->mouse.last_pressed = mdate();
523
osys->mouse.state = m;
526
osys->mouse.ch_activity = true;
527
if (!vd->info.has_hide_mouse)
528
osys->mouse.last_moved = mdate();
531
vlc_mutex_unlock(&osys->lock);
534
vout_SendEventMouseVisible(osys->vout);
535
#ifdef ALLOW_DUMMY_VOUT
536
DummyVoutSendDisplayEventMouse(osys->vout, &osys->vout_mouse, &m);
538
vout_SendDisplayEventMouse(osys->vout, &m);
542
static void VoutDisplayEvent(vout_display_t *vd, int event, va_list args)
544
vout_display_owner_sys_t *osys = vd->owner.sys;
547
case VOUT_DISPLAY_EVENT_CLOSE: {
548
msg_Dbg(vd, "VoutDisplayEvent 'close'");
549
vout_SendEventClose(osys->vout);
552
case VOUT_DISPLAY_EVENT_KEY: {
553
const int key = (int)va_arg(args, int);
554
msg_Dbg(vd, "VoutDisplayEvent 'key' 0x%2.2x", key);
555
vout_SendEventKey(osys->vout, key);
558
case VOUT_DISPLAY_EVENT_MOUSE_STATE:
559
case VOUT_DISPLAY_EVENT_MOUSE_MOVED:
560
case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
561
case VOUT_DISPLAY_EVENT_MOUSE_RELEASED:
562
case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
563
VoutDisplayEventMouse(vd, event, args);
566
case VOUT_DISPLAY_EVENT_FULLSCREEN: {
567
const int is_fullscreen = (int)va_arg(args, int);
569
msg_Dbg(vd, "VoutDisplayEvent 'fullscreen' %d", is_fullscreen);
571
vlc_mutex_lock(&osys->lock);
572
if (!is_fullscreen != !osys->is_fullscreen) {
573
osys->ch_fullscreen = true;
574
osys->is_fullscreen = is_fullscreen;
576
vlc_mutex_unlock(&osys->lock);
580
case VOUT_DISPLAY_EVENT_WINDOW_STATE: {
581
const unsigned state = va_arg(args, unsigned);
583
msg_Dbg(vd, "VoutDisplayEvent 'window state' %u", state);
585
vlc_mutex_lock(&osys->lock);
586
if (state != osys->wm_state) {
587
osys->ch_wm_state = true;
588
osys->wm_state = state;
590
vlc_mutex_unlock(&osys->lock);
594
case VOUT_DISPLAY_EVENT_DISPLAY_SIZE: {
595
const int width = (int)va_arg(args, int);
596
const int height = (int)va_arg(args, int);
597
const bool is_fullscreen = (bool)va_arg(args, int);
598
msg_Dbg(vd, "VoutDisplayEvent 'resize' %dx%d %s",
599
width, height, is_fullscreen ? "fullscreen" : "window");
602
vlc_mutex_lock(&osys->lock);
604
osys->ch_display_size = true;
605
osys->display_width = width;
606
osys->display_height = height;
607
osys->display_is_fullscreen = is_fullscreen;
608
osys->display_is_forced = false;
610
vlc_mutex_unlock(&osys->lock);
614
case VOUT_DISPLAY_EVENT_PICTURES_INVALID: {
615
msg_Warn(vd, "VoutDisplayEvent 'pictures invalid'");
618
assert(vd->info.has_pictures_invalid);
620
vlc_mutex_lock(&osys->lock);
621
osys->reset_pictures = true;
622
vlc_mutex_unlock(&osys->lock);
626
msg_Err(vd, "VoutDisplayEvent received event %d", event);
627
/* TODO add an assert when all event are handled */
632
static vout_window_t *VoutDisplayNewWindow(vout_display_t *vd, const vout_window_cfg_t *cfg)
634
vout_display_owner_sys_t *osys = vd->owner.sys;
636
return vout_NewDisplayWindow(osys->vout, vd, cfg);
638
static void VoutDisplayDelWindow(vout_display_t *vd, vout_window_t *window)
640
vout_display_owner_sys_t *osys = vd->owner.sys;
642
vout_DeleteDisplayWindow(osys->vout, vd, window);
645
static void VoutDisplayFitWindow(vout_display_t *vd, bool default_size)
647
vout_display_owner_sys_t *osys = vd->owner.sys;
648
vout_display_cfg_t cfg = osys->cfg;
650
if (!cfg.is_display_filled)
653
cfg.display.width = 0;
655
cfg.display.height = 0;
657
cfg.display.height = osys->height_saved;
662
unsigned display_width;
663
unsigned display_height;
664
vout_display_GetDefaultDisplaySize(&display_width, &display_height,
667
vlc_mutex_lock(&osys->lock);
669
osys->ch_display_size = true;
670
osys->display_width = display_width;
671
osys->display_height = display_height;
672
osys->display_is_fullscreen = osys->cfg.is_fullscreen;
673
osys->display_is_forced = true;
675
vlc_mutex_unlock(&osys->lock);
678
void vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
680
vout_display_owner_sys_t *osys = vd->owner.sys;
682
vout_display_Manage(vd);
684
/* Handle mouse timeout */
685
const mtime_t date = mdate();
686
bool hide_mouse = false;
688
vlc_mutex_lock(&osys->lock);
690
if (!osys->mouse.is_hidden &&
691
osys->mouse.last_moved + osys->mouse.hide_timeout < date) {
692
osys->mouse.is_hidden = hide_mouse = true;
693
} else if (osys->mouse.ch_activity) {
694
osys->mouse.is_hidden = false;
696
osys->mouse.ch_activity = false;
697
vlc_mutex_unlock(&osys->lock);
700
if (!vd->info.has_hide_mouse) {
701
msg_Dbg(vd, "auto hidding mouse");
702
vout_display_Control(vd, VOUT_DISPLAY_HIDE_MOUSE);
704
vout_SendEventMouseHidden(osys->vout);
707
bool reset_render = false;
710
vlc_mutex_lock(&osys->lock);
712
bool ch_fullscreen = osys->ch_fullscreen;
713
bool is_fullscreen = osys->is_fullscreen;
714
osys->ch_fullscreen = false;
716
bool ch_wm_state = osys->ch_wm_state;
717
unsigned wm_state = osys->wm_state;
718
osys->ch_wm_state = false;
720
bool ch_display_size = osys->ch_display_size;
721
int display_width = osys->display_width;
722
int display_height = osys->display_height;
723
bool display_is_fullscreen = osys->display_is_fullscreen;
724
bool display_is_forced = osys->display_is_forced;
725
osys->ch_display_size = false;
728
if (allow_reset_pictures) {
729
reset_pictures = osys->reset_pictures;
730
osys->reset_pictures = false;
732
reset_pictures = false;
735
vlc_mutex_unlock(&osys->lock);
737
if (!ch_fullscreen &&
740
!osys->ch_display_filled &&
746
if (!osys->cfg.is_fullscreen && osys->fit_window != 0) {
747
VoutDisplayFitWindow(vd, osys->fit_window == -1);
748
osys->fit_window = 0;
756
vout_display_cfg_t cfg = osys->cfg;
758
cfg.is_fullscreen = is_fullscreen;
759
cfg.display.width = cfg.is_fullscreen ? 0 : osys->width_saved;
760
cfg.display.height = cfg.is_fullscreen ? 0 : osys->height_saved;
762
if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_FULLSCREEN, &cfg)) {
763
msg_Err(vd, "Failed to set fullscreen");
764
is_fullscreen = osys->cfg.is_fullscreen;
766
osys->cfg.is_fullscreen = is_fullscreen;
769
vout_SendEventFullscreen(osys->vout, osys->cfg.is_fullscreen);
773
if (ch_display_size) {
774
vout_display_cfg_t cfg = osys->cfg;
775
cfg.display.width = display_width;
776
cfg.display.height = display_height;
778
if (!cfg.is_fullscreen != !display_is_fullscreen ||
779
vout_display_Control(vd, VOUT_DISPLAY_CHANGE_DISPLAY_SIZE, &cfg, display_is_forced)) {
780
if (!cfg.is_fullscreen == !display_is_fullscreen)
781
msg_Err(vd, "Failed to resize display");
783
/* We ignore the resized */
784
display_width = osys->cfg.display.width;
785
display_height = osys->cfg.display.height;
787
osys->cfg.display.width = display_width;
788
osys->cfg.display.height = display_height;
790
if (!display_is_fullscreen) {
791
osys->width_saved = display_width;
792
osys->height_saved = display_height;
796
if (osys->ch_display_filled) {
797
vout_display_cfg_t cfg = osys->cfg;
799
cfg.is_display_filled = osys->is_display_filled;
801
if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_DISPLAY_FILLED, &cfg)) {
802
msg_Err(vd, "Failed to change display filled state");
803
osys->is_display_filled = osys->cfg.is_display_filled;
805
osys->cfg.is_display_filled = osys->is_display_filled;
806
osys->ch_display_filled = false;
808
vout_SendEventDisplayFilled(osys->vout, osys->cfg.is_display_filled);
812
vout_display_cfg_t cfg = osys->cfg;
814
cfg.zoom.num = osys->zoom.num;
815
cfg.zoom.den = osys->zoom.den;
817
if (10 * cfg.zoom.num <= cfg.zoom.den) {
820
} else if (cfg.zoom.num >= 10 * cfg.zoom.den) {
825
if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_ZOOM, &cfg)) {
826
msg_Err(vd, "Failed to change zoom");
827
osys->zoom.num = osys->cfg.zoom.num;
828
osys->zoom.den = osys->cfg.zoom.den;
830
osys->fit_window = -1;
833
osys->cfg.zoom.num = osys->zoom.num;
834
osys->cfg.zoom.den = osys->zoom.den;
835
osys->ch_zoom = false;
837
vout_SendEventZoom(osys->vout, osys->cfg.zoom.num, osys->cfg.zoom.den);
841
if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_WINDOW_STATE, wm_state)) {
842
msg_Err(vd, "Failed to set on top");
843
wm_state = osys->wm_state;
845
osys->wm_state_initial = wm_state;
848
vout_SendEventOnTop(osys->vout, osys->wm_state_initial);
852
video_format_t source = vd->source;
854
if (osys->sar.num > 0 && osys->sar.den > 0) {
855
source.i_sar_num = osys->sar.num;
856
source.i_sar_den = osys->sar.den;
858
source.i_sar_num = osys->source.i_sar_num;
859
source.i_sar_den = osys->source.i_sar_den;
862
if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_SOURCE_ASPECT, &source)) {
863
/* There nothing much we can do. The only reason a vout display
864
* does not support it is because it need the core to add black border
865
* to the video for it.
866
* TODO add black borders ?
868
msg_Err(vd, "Failed to change source AR");
870
} else if (!osys->fit_window) {
871
osys->fit_window = 1;
874
osys->sar.num = source.i_sar_num;
875
osys->sar.den = source.i_sar_den;
876
osys->ch_sar = false;
879
if (osys->sar.num == osys->source.i_sar_num &&
880
osys->sar.den == osys->source.i_sar_den)
882
vout_SendEventSourceAspect(osys->vout, 0, 0);
886
unsigned dar_num, dar_den;
887
vlc_ureduce( &dar_num, &dar_den,
888
osys->sar.num * vd->source.i_visible_width,
889
osys->sar.den * vd->source.i_visible_height,
891
vout_SendEventSourceAspect(osys->vout, dar_num, dar_den);
896
video_format_t source = vd->source;
897
unsigned crop_num = osys->crop.num;
898
unsigned crop_den = osys->crop.den;
900
source.i_x_offset = osys->crop.x;
901
source.i_y_offset = osys->crop.y;
902
source.i_visible_width = osys->crop.width;
903
source.i_visible_height = osys->crop.height;
906
const bool is_valid = source.i_x_offset < source.i_width &&
907
source.i_y_offset < source.i_height &&
908
source.i_x_offset + source.i_visible_width <= source.i_width &&
909
source.i_y_offset + source.i_visible_height <= source.i_height &&
910
source.i_visible_width > 0 && source.i_visible_height > 0;
912
if (!is_valid || vout_display_Control(vd, VOUT_DISPLAY_CHANGE_SOURCE_CROP, &source)) {
914
msg_Err(vd, "Failed to change source crop TODO implement crop at core");
916
msg_Err(vd, "Invalid crop requested");
919
crop_num = osys->crop_saved.num;
920
crop_den = osys->crop_saved.den;
921
/* FIXME implement cropping in the core if not supported by the
924
} else if (!osys->fit_window) {
925
osys->fit_window = 1;
928
osys->crop.x = source.i_x_offset;
929
osys->crop.y = source.i_y_offset;
930
osys->crop.width = source.i_visible_width;
931
osys->crop.height = source.i_visible_height;
932
osys->crop.num = crop_num;
933
osys->crop.den = crop_den;
934
osys->ch_crop = false;
936
/* TODO fix when a ratio is used (complicated). */
937
const unsigned left = osys->crop.x - osys->source.i_x_offset;
938
const unsigned top = osys->crop.y - osys->source.i_y_offset;
939
const unsigned right = osys->source.i_visible_width - (osys->crop.width + osys->crop.x);
940
const unsigned bottom = osys->source.i_visible_height - (osys->crop.height + osys->crop.y);
941
vout_SendEventSourceCrop(osys->vout,
942
osys->crop.num, osys->crop.den,
943
left, top, right, bottom);
947
if (reset_pictures) {
948
if (vout_display_Control(vd, VOUT_DISPLAY_RESET_PICTURES)) {
949
/* FIXME what to do here ? */
950
msg_Err(vd, "Failed to reset pictures (probably fatal)");
956
VoutDisplayResetRender(vd);
959
bool vout_AreDisplayPicturesInvalid(vout_display_t *vd)
961
vout_display_owner_sys_t *osys = vd->owner.sys;
963
vlc_mutex_lock(&osys->lock);
964
const bool reset_pictures = osys->reset_pictures;
965
vlc_mutex_unlock(&osys->lock);
967
return reset_pictures;
970
bool vout_IsDisplayFiltered(vout_display_t *vd)
972
vout_display_owner_sys_t *osys = vd->owner.sys;
974
return osys->filters != NULL;
977
picture_t *vout_FilterDisplay(vout_display_t *vd, picture_t *picture)
979
vout_display_owner_sys_t *osys = vd->owner.sys;
981
assert(osys->filters);
982
return filter_chain_VideoFilter(osys->filters, picture);
985
void vout_SetDisplayFullscreen(vout_display_t *vd, bool is_fullscreen)
987
vout_display_owner_sys_t *osys = vd->owner.sys;
989
vlc_mutex_lock(&osys->lock);
990
if (!osys->is_fullscreen != !is_fullscreen) {
991
osys->ch_fullscreen = true;
992
osys->is_fullscreen = is_fullscreen;
994
vlc_mutex_unlock(&osys->lock);
997
void vout_SetDisplayFilled(vout_display_t *vd, bool is_filled)
999
vout_display_owner_sys_t *osys = vd->owner.sys;
1001
if (!osys->is_display_filled != !is_filled) {
1002
osys->ch_display_filled = true;
1003
osys->is_display_filled = is_filled;
1007
void vout_SetDisplayZoom(vout_display_t *vd, int num, int den)
1009
vout_display_owner_sys_t *osys = vd->owner.sys;
1011
if (osys->is_display_filled ||
1012
osys->zoom.num != num || osys->zoom.den != den) {
1013
osys->ch_zoom = true;
1014
osys->zoom.num = num;
1015
osys->zoom.den = den;
1019
void vout_SetWindowState(vout_display_t *vd, unsigned state)
1021
vout_display_owner_sys_t *osys = vd->owner.sys;
1023
vlc_mutex_lock(&osys->lock);
1024
if (osys->wm_state != state) {
1025
osys->ch_wm_state = true;
1026
osys->wm_state = state;
1028
vlc_mutex_unlock(&osys->lock);
1031
void vout_SetDisplayAspect(vout_display_t *vd, unsigned sar_num, unsigned sar_den)
1033
vout_display_owner_sys_t *osys = vd->owner.sys;
1035
if (osys->sar.num != sar_num || osys->sar.den != sar_den) {
1036
osys->ch_sar = true;
1037
osys->sar.num = sar_num;
1038
osys->sar.den = sar_den;
1041
void vout_SetDisplayCrop(vout_display_t *vd,
1042
unsigned crop_num, unsigned crop_den,
1043
unsigned x, unsigned y, unsigned width, unsigned height)
1045
vout_display_owner_sys_t *osys = vd->owner.sys;
1047
if (osys->crop.x != x || osys->crop.y != y ||
1048
osys->crop.width != width || osys->crop.height != height) {
1052
osys->crop.width = width;
1053
osys->crop.height = height;
1054
osys->crop.num = crop_num;
1055
osys->crop.den = crop_den;
1057
osys->ch_crop = true;
1060
vout_opengl_t *vout_GetDisplayOpengl(vout_display_t *vd)
1063
if (vout_display_Control(vd, VOUT_DISPLAY_GET_OPENGL, &gl))
1068
static vout_display_t *DisplayNew(vout_thread_t *vout,
1069
const video_format_t *source_org,
1070
const vout_display_state_t *state,
1072
bool is_wrapper, vout_display_t *wrapper,
1073
mtime_t double_click_timeout,
1074
mtime_t hide_timeout,
1075
const vout_display_owner_t *owner_ptr)
1078
vout_display_owner_sys_t *osys = calloc(1, sizeof(*osys));
1079
vout_display_cfg_t *cfg = &osys->cfg;
1082
osys->wm_state_initial = VOUT_WINDOW_STATE_NORMAL;
1083
osys->sar_initial.num = state->sar.num;
1084
osys->sar_initial.den = state->sar.den;
1085
vout_display_GetDefaultDisplaySize(&cfg->display.width, &cfg->display.height,
1089
osys->is_wrapper = is_wrapper;
1090
osys->wrapper = wrapper;
1092
vlc_mutex_init(&osys->lock);
1094
vlc_mouse_Init(&osys->mouse.state);
1095
osys->mouse.last_moved = mdate();
1096
osys->mouse.double_click_timeout = double_click_timeout;
1097
osys->mouse.hide_timeout = hide_timeout;
1098
osys->is_fullscreen = cfg->is_fullscreen;
1100
osys->display_width = cfg->display.width;
1101
osys->height_saved =
1102
osys->display_height = cfg->display.height;
1103
osys->is_display_filled = cfg->is_display_filled;
1104
osys->zoom.num = cfg->zoom.num;
1105
osys->zoom.den = cfg->zoom.den;
1106
osys->wm_state = state->is_on_top ? VOUT_WINDOW_STATE_ABOVE
1107
: VOUT_WINDOW_STATE_NORMAL;
1108
osys->fit_window = 0;
1110
osys->source = *source_org;
1112
video_format_t source = *source_org;
1118
source.i_visible_width =
1119
osys->crop.width = source.i_width;
1120
source.i_visible_height =
1121
osys->crop.height = source.i_height;
1122
osys->crop_saved.num = 0;
1123
osys->crop_saved.den = 0;
1127
osys->sar.num = osys->sar_initial.num ? osys->sar_initial.num : source.i_sar_num;
1128
osys->sar.den = osys->sar_initial.den ? osys->sar_initial.den : source.i_sar_den;
1129
#ifdef ALLOW_DUMMY_VOUT
1130
vlc_mouse_Init(&osys->vout_mouse);
1133
vout_display_owner_t owner;
1137
owner.event = VoutDisplayEvent;
1138
owner.window_new = VoutDisplayNewWindow;
1139
owner.window_del = VoutDisplayDelWindow;
1144
vout_display_t *p_display = vout_display_New(VLC_OBJECT(vout),
1145
module, !is_wrapper,
1146
&source, cfg, &owner);
1152
VoutDisplayCreateRender(p_display);
1154
/* Setup delayed request */
1155
if (osys->sar.num != source_org->i_sar_num ||
1156
osys->sar.den != source_org->i_sar_den)
1157
osys->ch_sar = true;
1158
if (osys->wm_state != osys->wm_state_initial)
1159
osys->ch_wm_state = true;
1160
if (osys->crop.x != source_org->i_x_offset ||
1161
osys->crop.y != source_org->i_y_offset ||
1162
osys->crop.width != source_org->i_visible_width ||
1163
osys->crop.height != source_org->i_visible_height)
1164
osys->ch_crop = true;
1169
void vout_DeleteDisplay(vout_display_t *vd, vout_display_state_t *state)
1171
vout_display_owner_sys_t *osys = vd->owner.sys;
1174
if (!osys->is_wrapper )
1175
state->cfg = osys->cfg;
1176
state->is_on_top = (osys->wm_state & VOUT_WINDOW_STATE_ABOVE) != 0;
1177
state->sar.num = osys->sar_initial.num;
1178
state->sar.den = osys->sar_initial.den;
1181
VoutDisplayDestroyRender(vd);
1182
if (osys->is_wrapper)
1184
vout_display_Delete(vd);
1185
vlc_mutex_destroy(&osys->lock);
1189
/*****************************************************************************
1191
*****************************************************************************/
1192
vout_display_t *vout_NewDisplay(vout_thread_t *vout,
1193
const video_format_t *source,
1194
const vout_display_state_t *state,
1196
mtime_t double_click_timeout,
1197
mtime_t hide_timeout)
1199
return DisplayNew(vout, source, state, module, false, NULL,
1200
double_click_timeout, hide_timeout, NULL);
1203
static void SplitterClose(vout_display_t *vd)
1210
/*****************************************************************************
1212
*****************************************************************************/
1213
struct vout_display_sys_t {
1214
video_splitter_t *splitter;
1218
picture_t **picture;
1219
vout_display_t **display;
1221
struct video_splitter_owner_t {
1222
vout_display_t *wrapper;
1225
static vout_window_t *SplitterNewWindow(vout_display_t *vd, const vout_window_cfg_t *cfg_ptr)
1227
vout_display_owner_sys_t *osys = vd->owner.sys;
1229
vout_window_cfg_t cfg = *cfg_ptr;
1230
cfg.is_standalone = true;
1231
cfg.x += 0;//output->window.i_x; FIXME
1232
cfg.y += 0;//output->window.i_y;
1234
return vout_NewDisplayWindow(osys->vout, vd, &cfg);
1236
static void SplitterDelWindow(vout_display_t *vd, vout_window_t *window)
1238
vout_display_owner_sys_t *osys = vd->owner.sys;
1240
vout_DeleteDisplayWindow(osys->vout, vd, window);
1242
static void SplitterEvent(vout_display_t *vd, int event, va_list args)
1244
vout_display_owner_sys_t *osys = vd->owner.sys;
1248
case VOUT_DISPLAY_EVENT_MOUSE_STATE:
1249
case VOUT_DISPLAY_EVENT_MOUSE_MOVED:
1250
case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
1251
case VOUT_DISPLAY_EVENT_MOUSE_RELEASED:
1255
case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
1256
case VOUT_DISPLAY_EVENT_KEY:
1257
case VOUT_DISPLAY_EVENT_CLOSE:
1258
case VOUT_DISPLAY_EVENT_FULLSCREEN:
1259
case VOUT_DISPLAY_EVENT_DISPLAY_SIZE:
1260
case VOUT_DISPLAY_EVENT_PICTURES_INVALID:
1261
VoutDisplayEvent(vd, event, args);
1265
msg_Err(vd, "SplitterEvent TODO");
1270
static picture_t *SplitterGet(vout_display_t *vd)
1273
return picture_NewFromFormat(&vd->fmt);
1275
static void SplitterPrepare(vout_display_t *vd, picture_t *picture)
1277
vout_display_sys_t *sys = vd->sys;
1279
picture_Hold(picture);
1281
if (video_splitter_Filter(sys->splitter, sys->picture, picture)) {
1282
for (int i = 0; i < sys->count; i++)
1283
sys->picture[i] = NULL;
1284
picture_Release(picture);
1288
for (int i = 0; i < sys->count; i++) {
1290
/* FIXME now vout_FilterDisplay already return a direct buffer FIXME */
1291
sys->picture[i] = vout_FilterDisplay(sys->display[i], sys->picture[i]);
1292
if (!sys->picture[i])
1296
picture_t *direct = vout_display_Get(sys->display[i]);
1298
msg_Err(vd, "Failed to get a direct buffer");
1299
picture_Release(sys->picture[i]);
1300
sys->picture[i] = NULL;
1304
/* FIXME not always needed (easy when there is a osys->filters) */
1305
picture_Copy(direct, sys->picture[i]);
1306
picture_Release(sys->picture[i]);
1307
sys->picture[i] = direct;
1309
vout_display_Prepare(sys->display[i], sys->picture[i]);
1312
static void SplitterDisplay(vout_display_t *vd, picture_t *picture)
1314
vout_display_sys_t *sys = vd->sys;
1316
for (int i = 0; i < sys->count; i++) {
1317
if (sys->picture[i])
1318
vout_display_Display(sys->display[i], sys->picture[i]);
1320
picture_Release(picture);
1322
static int SplitterControl(vout_display_t *vd, int query, va_list args)
1324
return VLC_EGENERIC;
1326
static void SplitterManage(vout_display_t *vd)
1328
vout_display_sys_t *sys = vd->sys;
1330
for (int i = 0; i < sys->count; i++)
1331
vout_ManageDisplay(sys->display[i]);
1334
static int SplitterPictureNew(video_splitter_t *splitter, picture_t *picture[])
1336
vout_display_sys_t *wsys = splitter->p_owner->wrapper->sys;
1338
for (int i = 0; i < wsys->count; i++) {
1340
picture[i] = picture_NewFromFormat(&wsys->display[i]->source);
1342
for (int j = 0; j < i; j++)
1343
picture_Release(picture[j]);
1344
return VLC_EGENERIC;
1349
static void SplitterPictureDel(video_splitter_t *splitter, picture_t *picture[])
1351
vout_display_sys_t *wsys = splitter->p_owner->wrapper->sys;
1353
for (int i = 0; i < wsys->count; i++)
1354
picture_Release(picture[i]);
1356
static void SplitterClose(vout_display_t *vd)
1358
vout_display_sys_t *sys = vd->sys;
1361
video_splitter_t *splitter = sys->splitter;
1362
free(splitter->p_owner);
1363
video_splitter_Delete(splitter);
1366
for (int i = 0; i < sys->count; i++)
1367
vout_DeleteDisplay(sys->display[i], NULL);
1368
TAB_CLEAN(sys->count, sys->display);
1374
vout_display_t *vout_NewSplitter(vout_thread_t *vout,
1375
const video_format_t *source,
1376
const vout_display_state_t *state,
1378
const char *splitter_module,
1379
mtime_t double_click_timeout,
1380
mtime_t hide_timeout)
1382
video_splitter_t *splitter =
1383
video_splitter_New(VLC_OBJECT(vout), splitter_module, source);
1388
vout_display_t *wrapper =
1389
DisplayNew(vout, source, state, module, true, NULL,
1390
double_click_timeout, hide_timeout, NULL);
1392
video_splitter_Delete(splitter);
1395
vout_display_sys_t *sys = malloc(sizeof(*sys));
1398
sys->picture = calloc(splitter->i_output, sizeof(*sys->picture));
1401
sys->splitter = splitter;
1403
wrapper->get = SplitterGet;
1404
wrapper->prepare = SplitterPrepare;
1405
wrapper->display = SplitterDisplay;
1406
wrapper->control = SplitterControl;
1407
wrapper->manage = SplitterManage;
1411
video_splitter_owner_t *owner = malloc(sizeof(*owner));
1414
owner->wrapper = wrapper;
1415
splitter->p_owner = owner;
1416
splitter->pf_picture_new = SplitterPictureNew;
1417
splitter->pf_picture_del = SplitterPictureDel;
1420
TAB_INIT(sys->count, sys->display);
1421
for (int i = 0; i < splitter->i_output; i++) {
1422
vout_display_owner_t owner;
1424
owner.event = SplitterEvent;
1425
owner.window_new = SplitterNewWindow;
1426
owner.window_del = SplitterDelWindow;
1428
const video_splitter_output_t *output = &splitter->p_output[i];
1429
vout_display_state_t ostate;
1431
memset(&ostate, 0, sizeof(ostate));
1432
ostate.cfg.is_fullscreen = false;
1433
ostate.cfg.display = state->cfg.display;
1434
ostate.cfg.align.horizontal = 0; /* TODO */
1435
ostate.cfg.align.vertical = 0; /* TODO */
1436
ostate.cfg.is_display_filled = true;
1437
ostate.cfg.zoom.num = 1;
1438
ostate.cfg.zoom.den = 1;
1440
vout_display_t *vd = DisplayNew(vout, &output->fmt, &ostate,
1441
output->psz_module ? output->psz_module : module,
1443
double_click_timeout, hide_timeout, &owner);
1445
vout_DeleteDisplay(wrapper, NULL);
1448
TAB_APPEND(sys->count, sys->display, vd);
1455
/*****************************************************************************
1457
*****************************************************************************/
1458
#include "vout_internal.h"
1459
void vout_SendDisplayEventMouse(vout_thread_t *vout, const vlc_mouse_t *m)
1461
if (vlc_mouse_HasMoved(&vout->p->mouse, m)) {
1462
vout_SendEventMouseMoved(vout, m->i_x, m->i_y);
1464
if (vlc_mouse_HasButton(&vout->p->mouse, m)) {
1465
static const int buttons[] = {
1467
MOUSE_BUTTON_CENTER,
1469
MOUSE_BUTTON_WHEEL_UP,
1470
MOUSE_BUTTON_WHEEL_DOWN,
1473
for (int i = 0; buttons[i] >= 0; i++) {
1474
const int button = buttons[i];
1475
if (vlc_mouse_HasPressed(&vout->p->mouse, m, button))
1476
vout_SendEventMousePressed(vout, button);
1477
else if (vlc_mouse_HasReleased(&vout->p->mouse, m, button))
1478
vout_SendEventMouseReleased(vout, button);
1481
if (m->b_double_click)
1482
vout_SendEventMouseDoubleClick(vout);
1483
vout->p->mouse = *m;
1485
#ifdef ALLOW_DUMMY_VOUT
1486
static void DummyVoutSendDisplayEventMouse(vout_thread_t *vout, vlc_mouse_t *fallback, const vlc_mouse_t *m)
1488
vout_thread_sys_t p;
1491
p.mouse = *fallback;
1494
vout_SendDisplayEventMouse(vout, m);
1495
if (vout->p == &p) {
1496
*fallback = p.mouse;
1501
vout_window_t * vout_NewDisplayWindow(vout_thread_t *vout, vout_display_t *vd, const vout_window_cfg_t *cfg)
1504
vout_window_cfg_t cfg_override = *cfg;
1506
if( !var_InheritBool( vout, "embedded-video" ) )
1507
cfg_override.is_standalone = true;
1509
return vout_window_New(VLC_OBJECT(vout), NULL, &cfg_override);
1511
void vout_DeleteDisplayWindow(vout_thread_t *vout, vout_display_t *vd, vout_window_t *window)
1515
vout_window_Delete(window);