2
2
* display.c: "vout display" managment
3
3
*****************************************************************************
4
4
* Copyright (C) 2009 Laurent Aimar
5
* $Id: 858fefb1a4aab69cf47d27860f36559838a3a2a5 $
5
* $Id: 50599d0db1783e62a3592ba99424a84a3457da1c $
7
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
9
* This program is free software; you can redistribute it and/or modify it
10
* under the terms of the GNU Lesser General Public License as published by
11
* the Free Software Foundation; either version 2.1 of the License, or
12
12
* (at your option) any later version.
14
14
* This program is distributed in the hope that it will be useful,
15
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.
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
* GNU Lesser 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.
19
* You should have received a copy of the GNU Lesser General Public License
20
* along with this program; if not, write to the Free Software Foundation,
21
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
22
*****************************************************************************/
24
24
/*****************************************************************************
521
529
osys->mouse.last_moved = mdate();
524
vlc_mutex_unlock(&osys->lock);
527
532
vout_SendEventMouseVisible(osys->vout);
528
533
#ifdef ALLOW_DUMMY_VOUT
529
534
DummyVoutSendDisplayEventMouse(osys->vout, &osys->vout_mouse, &m);
531
536
vout_SendDisplayEventMouse(osys->vout, &m);
538
vlc_mutex_unlock(&osys->lock);
542
static void *VoutDisplayEventKeyDispatch(void *data)
544
vout_display_owner_sys_t *osys = data;
547
block_t *event = block_FifoGet(osys->event.fifo);
549
int cancel = vlc_savecancel();
552
memcpy(&key, event->p_buffer, sizeof(key));
553
vout_SendEventKey(osys->vout, key);
554
block_Release(event);
556
vlc_restorecancel(cancel);
560
static void VoutDisplayEventKey(vout_display_t *vd, int key)
562
vout_display_owner_sys_t *osys = vd->owner.sys;
564
if (!osys->event.fifo) {
565
osys->event.fifo = block_FifoNew();
566
if (!osys->event.fifo)
568
if (vlc_clone(&osys->event.thread, VoutDisplayEventKeyDispatch,
569
osys, VLC_THREAD_PRIORITY_LOW)) {
570
block_FifoRelease(osys->event.fifo);
571
osys->event.fifo = NULL;
575
block_t *event = block_Alloc(sizeof(key));
577
memcpy(event->p_buffer, &key, sizeof(key));
578
block_FifoPut(osys->event.fifo, event);
535
582
static void VoutDisplayEvent(vout_display_t *vd, int event, va_list args)
545
592
case VOUT_DISPLAY_EVENT_KEY: {
546
593
const int key = (int)va_arg(args, int);
547
594
msg_Dbg(vd, "VoutDisplayEvent 'key' 0x%2.2x", key);
548
vout_SendEventKey(osys->vout, key);
595
if (vd->info.has_event_thread)
596
vout_SendEventKey(osys->vout, key);
598
VoutDisplayEventKey(vd, key);
551
601
case VOUT_DISPLAY_EVENT_MOUSE_STATE:
633
683
if (!var_InheritBool(osys->vout, "embedded-video"))
634
684
cfg_override.is_standalone = true;
636
return vout_window_New(VLC_OBJECT(osys->vout), NULL, &cfg_override);
686
return vout_window_New(VLC_OBJECT(osys->vout), "$window", &cfg_override);
639
689
return vout_NewDisplayWindow(osys->vout, vd, cfg);
685
735
vlc_mutex_unlock(&osys->lock);
738
static void VoutDisplayCropRatio(int *left, int *top, int *right, int *bottom,
739
const video_format_t *source,
740
unsigned num, unsigned den)
742
unsigned scaled_width = (uint64_t)source->i_visible_height * num * source->i_sar_den / den / source->i_sar_num;
743
unsigned scaled_height = (uint64_t)source->i_visible_width * den * source->i_sar_num / num / source->i_sar_den;
745
if (scaled_width < source->i_visible_width) {
746
*left = (source->i_visible_width - scaled_width) / 2;
748
*right = *left + scaled_width;
749
*bottom = *top + source->i_visible_height;
752
*top = (source->i_visible_height - scaled_height) / 2;
753
*right = *left + source->i_visible_width;
754
*bottom = *top + scaled_height;
688
758
void vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
690
760
vout_display_owner_sys_t *osys = vd->owner.sys;
772
842
if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_FULLSCREEN, &cfg)) {
773
843
msg_Err(vd, "Failed to set fullscreen");
774
844
is_fullscreen = osys->cfg.is_fullscreen;
845
} else if (!is_fullscreen) {
846
vout_display_Control(vd, VOUT_DISPLAY_CHANGE_DISPLAY_SIZE, &cfg, true);
776
848
osys->cfg.is_fullscreen = is_fullscreen;
901
973
vout_SendEventSourceAspect(osys->vout, dar_num, dar_den);
975
/* If a crop ratio is requested, recompute the parameters */
976
if (osys->crop.num > 0 && osys->crop.den > 0)
977
osys->ch_crop = true;
905
980
if (osys->ch_crop) {
906
981
video_format_t source = vd->source;
907
983
unsigned crop_num = osys->crop.num;
908
984
unsigned crop_den = osys->crop.den;
910
source.i_x_offset = osys->crop.x;
911
source.i_y_offset = osys->crop.y;
912
source.i_visible_width = osys->crop.width;
913
source.i_visible_height = osys->crop.height;
916
const bool is_valid = source.i_x_offset < source.i_width &&
917
source.i_y_offset < source.i_height &&
918
source.i_x_offset + source.i_visible_width <= source.i_width &&
919
source.i_y_offset + source.i_visible_height <= source.i_height &&
920
source.i_visible_width > 0 && source.i_visible_height > 0;
922
if (!is_valid || vout_display_Control(vd, VOUT_DISPLAY_CHANGE_SOURCE_CROP, &source)) {
924
msg_Err(vd, "Failed to change source crop TODO implement crop at core");
926
msg_Err(vd, "Invalid crop requested");
985
if (crop_num > 0 && crop_den > 0) {
986
video_format_t fmt = osys->source;
987
fmt.i_sar_num = source.i_sar_num;
988
fmt.i_sar_den = source.i_sar_den;
989
VoutDisplayCropRatio(&osys->crop.left, &osys->crop.top,
990
&osys->crop.right, &osys->crop.bottom,
991
&fmt, crop_num, crop_den);
993
const int right_max = osys->source.i_x_offset + osys->source.i_visible_width;
994
const int bottom_max = osys->source.i_y_offset + osys->source.i_visible_height;
995
int left = VLC_CLIP((int)osys->source.i_x_offset + osys->crop.left,
997
int top = VLC_CLIP((int)osys->source.i_y_offset + osys->crop.top,
1000
if (osys->crop.right <= 0)
1001
right = (int)(osys->source.i_x_offset + osys->source.i_visible_width) + osys->crop.right;
1003
right = (int)osys->source.i_x_offset + osys->crop.right;
1004
right = VLC_CLIP(right, left + 1, right_max);
1005
if (osys->crop.bottom <= 0)
1006
bottom = (int)(osys->source.i_y_offset + osys->source.i_visible_height) + osys->crop.bottom;
1008
bottom = (int)osys->source.i_y_offset + osys->crop.bottom;
1009
bottom = VLC_CLIP(bottom, top + 1, bottom_max);
1011
source.i_x_offset = left;
1012
source.i_y_offset = top;
1013
source.i_visible_width = right - left;
1014
source.i_visible_height = bottom - top;
1015
video_format_Print(VLC_OBJECT(vd), "SOURCE ", &osys->source);
1016
video_format_Print(VLC_OBJECT(vd), "CROPPED", &source);
1017
if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_SOURCE_CROP, &source)) {
1018
msg_Err(vd, "Failed to change source crop TODO implement crop at core");
928
1020
source = vd->source;
929
1021
crop_num = osys->crop_saved.num;
935
1027
osys->fit_window = 1;
937
1029
vd->source = source;
938
osys->crop.x = source.i_x_offset;
939
osys->crop.y = source.i_y_offset;
940
osys->crop.width = source.i_visible_width;
941
osys->crop.height = source.i_visible_height;
1030
osys->crop.left = source.i_x_offset - osys->source.i_x_offset;
1031
osys->crop.top = source.i_y_offset - osys->source.i_y_offset;
1032
/* FIXME for right/bottom we should keep the 'type' border vs window */
1033
osys->crop.right = (source.i_x_offset + source.i_visible_width) -
1034
(osys->source.i_x_offset + osys->source.i_visible_width);
1035
osys->crop.bottom = (source.i_y_offset + source.i_visible_height) -
1036
(osys->source.i_y_offset + osys->source.i_visible_height);
942
1037
osys->crop.num = crop_num;
943
1038
osys->crop.den = crop_den;
944
1039
osys->ch_crop = false;
946
/* TODO fix when a ratio is used (complicated). */
947
const unsigned left = osys->crop.x - osys->source.i_x_offset;
948
const unsigned top = osys->crop.y - osys->source.i_y_offset;
949
const unsigned right = osys->source.i_visible_width - (osys->crop.width + osys->crop.x);
950
const unsigned bottom = osys->source.i_visible_height - (osys->crop.height + osys->crop.y);
951
1041
vout_SendEventSourceCrop(osys->vout,
952
1042
osys->crop.num, osys->crop.den,
953
left, top, right, bottom);
1043
osys->crop.left, osys->crop.top,
1044
-osys->crop.right, -osys->crop.bottom);
996
1087
return filter_chain_VideoFilter(osys->filters, picture);
1090
void vout_UpdateDisplaySourceProperties(vout_display_t *vd, const video_format_t *source)
1092
vout_display_owner_sys_t *osys = vd->owner.sys;
1094
if (source->i_sar_num * osys->source.i_sar_den !=
1095
source->i_sar_den * osys->source.i_sar_num) {
1097
osys->source.i_sar_num = source->i_sar_num;
1098
osys->source.i_sar_den = source->i_sar_den;
1099
vlc_ureduce(&osys->source.i_sar_num, &osys->source.i_sar_den,
1100
osys->source.i_sar_num, osys->source.i_sar_den, 0);
1102
/* FIXME it will override any AR that the user would have forced */
1103
osys->ch_sar = true;
1104
osys->sar.num = osys->source.i_sar_num;
1105
osys->sar.den = osys->source.i_sar_den;
1107
if (source->i_x_offset != osys->source.i_x_offset ||
1108
source->i_y_offset != osys->source.i_y_offset ||
1109
source->i_visible_width != osys->source.i_visible_width ||
1110
source->i_visible_height != osys->source.i_visible_height) {
1112
video_format_CopyCrop(&osys->source, source);
1114
/* Force the vout to reapply the current user crop settings over the new decoder
1116
osys->ch_crop = true;
999
1120
void vout_SetDisplayFullscreen(vout_display_t *vd, bool is_fullscreen)
1001
1122
vout_display_owner_sys_t *osys = vd->owner.sys;
1042
1163
vlc_mutex_unlock(&osys->lock);
1045
void vout_SetDisplayAspect(vout_display_t *vd, unsigned sar_num, unsigned sar_den)
1166
void vout_SetDisplayAspect(vout_display_t *vd, unsigned dar_num, unsigned dar_den)
1047
1168
vout_display_owner_sys_t *osys = vd->owner.sys;
1170
unsigned sar_num, sar_den;
1171
if (dar_num > 0 && dar_den > 0) {
1172
sar_num = dar_num * osys->source.i_visible_height;
1173
sar_den = dar_den * osys->source.i_visible_width;
1174
vlc_ureduce(&sar_num, &sar_den, sar_num, sar_den, 0);
1049
1180
if (osys->sar.num != sar_num || osys->sar.den != sar_den) {
1050
1181
osys->ch_sar = true;
1051
1182
osys->sar.num = sar_num;
1055
1186
void vout_SetDisplayCrop(vout_display_t *vd,
1056
1187
unsigned crop_num, unsigned crop_den,
1057
unsigned x, unsigned y, unsigned width, unsigned height)
1188
unsigned left, unsigned top, int right, int bottom)
1059
1190
vout_display_owner_sys_t *osys = vd->owner.sys;
1061
if (osys->crop.x != x || osys->crop.y != y ||
1062
osys->crop.width != width || osys->crop.height != height) {
1192
if (osys->crop.left != (int)left || osys->crop.top != (int)top ||
1193
osys->crop.right != right || osys->crop.bottom != bottom ||
1194
(crop_num > 0 && crop_den > 0 &&
1195
(crop_num != osys->crop.num || crop_den != osys->crop.den))) {
1066
osys->crop.width = width;
1067
osys->crop.height = height;
1197
osys->crop.left = left;
1198
osys->crop.top = top;
1199
osys->crop.right = right;
1200
osys->crop.bottom = bottom;
1068
1201
osys->crop.num = crop_num;
1069
1202
osys->crop.den = crop_den;
1071
1204
osys->ch_crop = true;
1074
vout_opengl_t *vout_GetDisplayOpengl(vout_display_t *vd)
1208
struct vlc_gl_t *vout_GetDisplayOpengl(vout_display_t *vd)
1210
struct vlc_gl_t *gl;
1077
1211
if (vout_display_Control(vd, VOUT_DISPLAY_GET_OPENGL, &gl))
1110
1244
osys->mouse.double_click_timeout = double_click_timeout;
1111
1245
osys->mouse.hide_timeout = hide_timeout;
1112
1246
osys->is_fullscreen = cfg->is_fullscreen;
1114
1247
osys->display_width = cfg->display.width;
1115
osys->height_saved =
1116
1248
osys->display_height = cfg->display.height;
1117
1249
osys->is_display_filled = cfg->is_display_filled;
1250
osys->width_saved = cfg->display.width;
1251
osys->height_saved = cfg->display.height;
1252
if (osys->is_fullscreen) {
1253
vout_display_cfg_t cfg_windowed = *cfg;
1254
cfg_windowed.is_fullscreen = false;
1255
cfg_windowed.display.width = 0;
1256
cfg_windowed.display.height = 0;
1257
vout_display_GetDefaultDisplaySize(&osys->width_saved,
1258
&osys->height_saved,
1259
source_org, &cfg_windowed);
1118
1261
osys->zoom.num = cfg->zoom.num;
1119
1262
osys->zoom.den = cfg->zoom.den;
1120
osys->wm_state = state->is_on_top ? VOUT_WINDOW_STATE_ABOVE
1121
: VOUT_WINDOW_STATE_NORMAL;
1263
osys->wm_state = state->wm_state;
1122
1264
osys->fit_window = 0;
1265
osys->event.fifo = NULL;
1124
1267
osys->source = *source_org;
1126
video_format_t source = *source_org;
1132
source.i_visible_width =
1133
osys->crop.width = source.i_width;
1134
source.i_visible_height =
1135
osys->crop.height = source.i_height;
1268
osys->crop.left = 0;
1270
osys->crop.right = 0;
1271
osys->crop.bottom = 0;
1136
1272
osys->crop_saved.num = 0;
1137
1273
osys->crop_saved.den = 0;
1138
1274
osys->crop.num = 0;
1139
1275
osys->crop.den = 0;
1141
osys->sar.num = osys->sar_initial.num ? osys->sar_initial.num : source.i_sar_num;
1142
osys->sar.den = osys->sar_initial.den ? osys->sar_initial.den : source.i_sar_den;
1277
osys->sar.num = osys->sar_initial.num ? osys->sar_initial.num : source_org->i_sar_num;
1278
osys->sar.den = osys->sar_initial.den ? osys->sar_initial.den : source_org->i_sar_den;
1143
1279
#ifdef ALLOW_DUMMY_VOUT
1144
1280
vlc_mouse_Init(&osys->vout_mouse);
1166
1309
VoutDisplayCreateRender(p_display);
1168
1311
/* Setup delayed request */
1169
if (osys->sar.num != source_org->i_sar_num ||
1170
osys->sar.den != source_org->i_sar_den)
1312
if (osys->sar.num != source.i_sar_num ||
1313
osys->sar.den != source.i_sar_den)
1171
1314
osys->ch_sar = true;
1172
1315
if (osys->wm_state != osys->wm_state_initial)
1173
1316
osys->ch_wm_state = true;
1174
if (osys->crop.x != source_org->i_x_offset ||
1175
osys->crop.y != source_org->i_y_offset ||
1176
osys->crop.width != source_org->i_visible_width ||
1177
osys->crop.height != source_org->i_visible_height)
1317
if (source.i_x_offset != source_org->i_x_offset ||
1318
source.i_y_offset != source_org->i_y_offset ||
1319
source.i_visible_width != source_org->i_visible_width ||
1320
source.i_visible_height != source_org->i_visible_height)
1178
1321
osys->ch_crop = true;
1180
1323
return p_display;
1188
1331
if (!osys->is_wrapper )
1189
1332
state->cfg = osys->cfg;
1190
state->is_on_top = (osys->wm_state & VOUT_WINDOW_STATE_ABOVE) != 0;
1191
state->sar.num = osys->sar_initial.num;
1192
state->sar.den = osys->sar_initial.den;
1333
state->wm_state = osys->wm_state;
1334
state->sar.num = osys->sar_initial.num;
1335
state->sar.den = osys->sar_initial.den;
1195
1338
VoutDisplayDestroyRender(vd);
1196
1339
if (osys->is_wrapper)
1197
1340
SplitterClose(vd);
1198
1341
vout_display_Delete(vd);
1342
if (osys->event.fifo) {
1343
vlc_cancel(osys->event.thread);
1344
vlc_join(osys->event.thread, NULL);
1345
block_FifoRelease(osys->event.fifo);
1199
1347
vlc_mutex_destroy(&osys->lock);
1284
static picture_t *SplitterGet(vout_display_t *vd)
1426
static picture_pool_t *SplitterPool(vout_display_t *vd, unsigned count)
1287
return picture_NewFromFormat(&vd->fmt);
1428
vout_display_sys_t *sys = vd->sys;
1430
sys->pool = picture_pool_NewFromFormat(&vd->fmt, count);
1289
static void SplitterPrepare(vout_display_t *vd, picture_t *picture)
1433
static void SplitterPrepare(vout_display_t *vd,
1435
subpicture_t *subpicture)
1291
1437
vout_display_sys_t *sys = vd->sys;
1293
1439
picture_Hold(picture);
1440
assert(!subpicture);
1295
1442
if (video_splitter_Filter(sys->splitter, sys->picture, picture)) {
1296
1443
for (int i = 0; i < sys->count; i++)
1302
1449
for (int i = 0; i < sys->count; i++) {
1304
/* FIXME now vout_FilterDisplay already return a direct buffer FIXME */
1305
sys->picture[i] = vout_FilterDisplay(sys->display[i], sys->picture[i]);
1306
if (!sys->picture[i])
1310
picture_t *direct = vout_display_Get(sys->display[i]);
1312
msg_Err(vd, "Failed to get a direct buffer");
1313
picture_Release(sys->picture[i]);
1314
sys->picture[i] = NULL;
1318
/* FIXME not always needed (easy when there is a osys->filters) */
1319
picture_Copy(direct, sys->picture[i]);
1320
picture_Release(sys->picture[i]);
1321
sys->picture[i] = direct;
1323
vout_display_Prepare(sys->display[i], sys->picture[i]);
1450
if (vout_IsDisplayFiltered(sys->display[i]))
1451
sys->picture[i] = vout_FilterDisplay(sys->display[i], sys->picture[i]);
1452
if (sys->picture[i])
1453
vout_display_Prepare(sys->display[i], sys->picture[i], NULL);
1326
static void SplitterDisplay(vout_display_t *vd, picture_t *picture)
1456
static void SplitterDisplay(vout_display_t *vd,
1458
subpicture_t *subpicture)
1328
1460
vout_display_sys_t *sys = vd->sys;
1462
assert(!subpicture);
1330
1463
for (int i = 0; i < sys->count; i++) {
1331
1464
if (sys->picture[i])
1332
vout_display_Display(sys->display[i], sys->picture[i]);
1465
vout_display_Display(sys->display[i], sys->picture[i], NULL);
1334
1467
picture_Release(picture);
1336
1469
static int SplitterControl(vout_display_t *vd, int query, va_list args)
1471
(void)vd; (void)query; (void)args;
1338
1472
return VLC_EGENERIC;
1340
1474
static void SplitterManage(vout_display_t *vd)
1350
1484
vout_display_sys_t *wsys = splitter->p_owner->wrapper->sys;
1352
1486
for (int i = 0; i < wsys->count; i++) {
1354
picture[i] = picture_NewFromFormat(&wsys->display[i]->source);
1487
if (vout_IsDisplayFiltered(wsys->display[i])) {
1488
/* TODO use a pool ? */
1489
picture[i] = picture_NewFromFormat(&wsys->display[i]->source);
1491
picture_pool_t *pool = vout_display_Pool(wsys->display[i], 1);
1492
picture[i] = pool ? picture_pool_Get(pool) : NULL;
1355
1494
if (!picture[i]) {
1356
1495
for (int j = 0; j < i; j++)
1357
1496
picture_Release(picture[j]);
1472
1614
#include "vout_internal.h"
1473
1615
void vout_SendDisplayEventMouse(vout_thread_t *vout, const vlc_mouse_t *m)
1617
vlc_mouse_t tmp1, tmp2;
1619
/* The check on spu is needed as long as ALLOW_DUMMY_VOUT is defined */
1620
if (vout->p->spu && spu_ProcessMouse( vout->p->spu, m, &vout->p->display.vd->source))
1623
vlc_mutex_lock( &vout->p->filter.lock );
1624
if (vout->p->filter.chain_static && vout->p->filter.chain_interactive) {
1625
if (!filter_chain_MouseFilter(vout->p->filter.chain_interactive, &tmp1, m))
1627
if (!filter_chain_MouseFilter(vout->p->filter.chain_static, &tmp2, m))
1630
vlc_mutex_unlock( &vout->p->filter.lock );
1475
1632
if (vlc_mouse_HasMoved(&vout->p->mouse, m)) {
1476
1633
vout_SendEventMouseMoved(vout, m->i_x, m->i_y);
1478
1635
if (vlc_mouse_HasButton(&vout->p->mouse, m)) {
1479
static const int buttons[] = {
1481
MOUSE_BUTTON_CENTER,
1483
MOUSE_BUTTON_WHEEL_UP,
1484
MOUSE_BUTTON_WHEEL_DOWN,
1487
for (int i = 0; buttons[i] >= 0; i++) {
1488
const int button = buttons[i];
1636
for (unsigned button = 0; button < MOUSE_BUTTON_MAX; button++) {
1489
1637
if (vlc_mouse_HasPressed(&vout->p->mouse, m, button))
1490
1638
vout_SendEventMousePressed(vout, button);
1491
1639
else if (vlc_mouse_HasReleased(&vout->p->mouse, m, button))
1504
1652
if (!vout->p) {
1505
1653
p.mouse = *fallback;
1654
vlc_mutex_init(&p.filter.lock);
1655
p.filter.chain_static = NULL;
1656
p.filter.chain_interactive = NULL;
1508
1660
vout_SendDisplayEventMouse(vout, m);
1509
1661
if (vout->p == &p) {
1662
vlc_mutex_destroy(&p.filter.lock);
1510
1663
*fallback = p.mouse;
1511
1664
vout->p = NULL;
1515
vout_window_t * vout_NewDisplayWindow(vout_thread_t *vout, vout_display_t *vd, const vout_window_cfg_t *cfg)
1518
vout_window_cfg_t cfg_override = *cfg;
1520
if( !var_InheritBool( vout, "embedded-video" ) )
1521
cfg_override.is_standalone = true;
1523
return vout_window_New(VLC_OBJECT(vout), NULL, &cfg_override);
1525
void vout_DeleteDisplayWindow(vout_thread_t *vout, vout_display_t *vd, vout_window_t *window)
1529
vout_window_Delete(window);