1 /*****************************************************************************
2 * video_output.c : video output thread
4 * This module describes the programming interface for video output threads.
5 * It includes functions allowing to open a new thread, send pictures to a
6 * thread, and destroy a previously oppened video output thread.
7 *****************************************************************************
8 * Copyright (C) 2000-2007 VLC authors and VideoLAN
11 * Authors: Vincent Seguin <seguin@via.ecp.fr>
12 * Gildas Bazin <gbazin@videolan.org>
13 * Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
15 * This program is free software; you can redistribute it and/or modify it
16 * under the terms of the GNU Lesser General Public License as published by
17 * the Free Software Foundation; either version 2.1 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public License
26 * along with this program; if not, write to the Free Software Foundation,
27 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
28 *****************************************************************************/
30 /*****************************************************************************
32 *****************************************************************************/
37 #include <vlc_common.h>
39 #include <stdlib.h> /* free() */
45 #include <vlc_filter.h>
47 #include <vlc_vout_osd.h>
48 #include <vlc_image.h>
49 #include <vlc_plugin.h>
52 #include "vout_internal.h"
53 #include "interlacing.h"
56 #include "../misc/variables.h"
58 /*****************************************************************************
60 *****************************************************************************/
61 static void *Thread(void *);
62 static void VoutDestructor(vlc_object_t
*);
64 /* Maximum delay between 2 displayed pictures.
65 * XXX it is needed for now but should be removed in the long term.
67 #define VOUT_REDISPLAY_DELAY (INT64_C(80000))
70 * Late pictures having a delay higher than this value are thrashed.
72 #define VOUT_DISPLAY_LATE_THRESHOLD (INT64_C(20000))
74 /* Better be in advance when awakening than late... */
75 #define VOUT_MWAIT_TOLERANCE (INT64_C(4000))
78 static int VoutValidateFormat(video_format_t
*dst
,
79 const video_format_t
*src
)
81 if (src
->i_width
== 0 || src
->i_width
> 8192 ||
82 src
->i_height
== 0 || src
->i_height
> 8192)
84 if (src
->i_sar_num
<= 0 || src
->i_sar_den
<= 0)
88 video_format_Copy(dst
, src
);
89 dst
->i_chroma
= vlc_fourcc_GetCodec(VIDEO_ES
, src
->i_chroma
);
90 vlc_ureduce( &dst
->i_sar_num
, &dst
->i_sar_den
,
91 src
->i_sar_num
, src
->i_sar_den
, 50000 );
92 if (dst
->i_sar_num
<= 0 || dst
->i_sar_den
<= 0) {
96 video_format_FixRgb(dst
);
99 static void VideoFormatCopyCropAr(video_format_t
*dst
,
100 const video_format_t
*src
)
102 video_format_CopyCrop(dst
, src
);
103 dst
->i_sar_num
= src
->i_sar_num
;
104 dst
->i_sar_den
= src
->i_sar_den
;
106 static bool VideoFormatIsCropArEqual(video_format_t
*dst
,
107 const video_format_t
*src
)
109 return dst
->i_sar_num
* src
->i_sar_den
== dst
->i_sar_den
* src
->i_sar_num
&&
110 dst
->i_x_offset
== src
->i_x_offset
&&
111 dst
->i_y_offset
== src
->i_y_offset
&&
112 dst
->i_visible_width
== src
->i_visible_width
&&
113 dst
->i_visible_height
== src
->i_visible_height
;
116 static vout_thread_t
*VoutCreate(vlc_object_t
*object
,
117 const vout_configuration_t
*cfg
)
119 video_format_t original
;
120 if (VoutValidateFormat(&original
, cfg
->fmt
))
123 /* Allocate descriptor */
124 vout_thread_t
*vout
= vlc_custom_create(object
,
125 sizeof(*vout
) + sizeof(*vout
->p
),
128 video_format_Clean(&original
);
133 vout
->p
= (vout_thread_sys_t
*)&vout
[1];
135 vout
->p
->original
= original
;
136 vout
->p
->dpb_size
= cfg
->dpb_size
;
138 vout_control_Init(&vout
->p
->control
);
139 vout_control_PushVoid(&vout
->p
->control
, VOUT_CONTROL_INIT
);
141 vout_statistic_Init(&vout
->p
->statistic
);
143 vout_snapshot_Init(&vout
->p
->snapshot
);
145 /* Initialize locks */
146 vlc_mutex_init(&vout
->p
->filter
.lock
);
147 vlc_mutex_init(&vout
->p
->spu_lock
);
149 /* Take care of some "interface/control" related initialisations */
152 /* Initialize subpicture unit */
153 vout
->p
->spu
= spu_Create(vout
, vout
);
155 vout
->p
->title
.show
= var_InheritBool(vout
, "video-title-show");
156 vout
->p
->title
.timeout
= var_InheritInteger(vout
, "video-title-timeout");
157 vout
->p
->title
.position
= var_InheritInteger(vout
, "video-title-position");
159 /* Get splitter name if present */
160 vout
->p
->splitter_name
= var_InheritString(vout
, "video-splitter");
163 vout_InitInterlacingSupport(vout
, vout
->p
->displayed
.is_interlaced
);
166 if (vout
->p
->splitter_name
== NULL
) {
167 vout_window_cfg_t wcfg
= {
168 .is_standalone
= !var_InheritBool(vout
, "embedded-video"),
169 .is_fullscreen
= var_GetBool(vout
, "fullscreen"),
170 .type
= VOUT_WINDOW_TYPE_INVALID
,
171 // TODO: take pixel A/R, crop and zoom into account
173 .x
= var_InheritInteger(vout
, "video-x"),
174 .y
= var_InheritInteger(vout
, "video-y"),
176 .width
= cfg
->fmt
->i_visible_width
,
177 .height
= cfg
->fmt
->i_visible_height
,
180 vout_window_t
*window
= vout_display_window_New(vout
, &wcfg
);
183 if (var_InheritBool(vout
, "video-wallpaper"))
184 vout_window_SetState(window
, VOUT_WINDOW_STATE_BELOW
);
185 else if (var_InheritBool(vout
, "video-on-top"))
186 vout_window_SetState(window
, VOUT_WINDOW_STATE_ABOVE
);
188 vout
->p
->window
= window
;
190 vout
->p
->window
= NULL
;
193 vlc_object_set_destructor(vout
, VoutDestructor
);
196 if (vlc_clone(&vout
->p
->thread
, Thread
, vout
,
197 VLC_THREAD_PRIORITY_OUTPUT
)) {
198 if (vout
->p
->window
!= NULL
)
199 vout_display_window_Delete(vout
->p
->window
);
200 spu_Destroy(vout
->p
->spu
);
201 vlc_object_release(vout
);
205 vout_control_WaitEmpty(&vout
->p
->control
);
208 msg_Err(vout
, "video output creation failed");
209 vout_CloseAndRelease(vout
);
213 vout
->p
->input
= cfg
->input
;
215 spu_Attach(vout
->p
->spu
, vout
->p
->input
, true);
221 vout_thread_t
*vout_Request(vlc_object_t
*object
,
222 const vout_configuration_t
*cfg
)
224 vout_thread_t
*vout
= cfg
->vout
;
225 if (cfg
->change_fmt
&& !cfg
->fmt
) {
227 vout_CloseAndRelease(vout
);
231 /* If a vout is provided, try reusing it */
233 if (vout
->p
->input
!= cfg
->input
) {
235 spu_Attach(vout
->p
->spu
, vout
->p
->input
, false);
236 vout
->p
->input
= cfg
->input
;
238 spu_Attach(vout
->p
->spu
, vout
->p
->input
, true);
241 if (cfg
->change_fmt
) {
242 vout_control_cmd_t cmd
;
243 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_REINIT
);
246 vout_control_Push(&vout
->p
->control
, &cmd
);
247 vout_control_WaitEmpty(&vout
->p
->control
);
248 vout_IntfReinit(vout
);
251 if (!vout
->p
->dead
) {
252 msg_Dbg(object
, "reusing provided vout");
255 vout_CloseAndRelease(vout
);
257 msg_Warn(object
, "cannot reuse provided vout");
259 return VoutCreate(object
, cfg
);
262 void vout_Close(vout_thread_t
*vout
)
267 spu_Attach(vout
->p
->spu
, vout
->p
->input
, false);
269 vout_snapshot_End(&vout
->p
->snapshot
);
271 vout_control_PushVoid(&vout
->p
->control
, VOUT_CONTROL_CLEAN
);
272 vlc_join(vout
->p
->thread
, NULL
);
274 if (vout
->p
->window
!= NULL
)
275 vout_display_window_Delete(vout
->p
->window
);
277 vlc_mutex_lock(&vout
->p
->spu_lock
);
278 spu_Destroy(vout
->p
->spu
);
280 vlc_mutex_unlock(&vout
->p
->spu_lock
);
284 static void VoutDestructor(vlc_object_t
*object
)
286 vout_thread_t
*vout
= (vout_thread_t
*)object
;
288 /* Make sure the vout was stopped first */
289 //assert(!vout->p_module);
291 free(vout
->p
->splitter_name
);
293 /* Destroy the locks */
294 vlc_mutex_destroy(&vout
->p
->spu_lock
);
295 vlc_mutex_destroy(&vout
->p
->filter
.lock
);
296 vout_control_Clean(&vout
->p
->control
);
299 vout_statistic_Clean(&vout
->p
->statistic
);
302 vout_snapshot_Clean(&vout
->p
->snapshot
);
304 video_format_Clean(&vout
->p
->original
);
308 void vout_Cancel(vout_thread_t
*vout
, bool canceled
)
310 vout_control_PushBool(&vout
->p
->control
, VOUT_CONTROL_CANCEL
, canceled
);
311 vout_control_WaitEmpty(&vout
->p
->control
);
314 void vout_ChangePause(vout_thread_t
*vout
, bool is_paused
, mtime_t date
)
316 vout_control_cmd_t cmd
;
317 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_PAUSE
);
318 cmd
.u
.pause
.is_on
= is_paused
;
319 cmd
.u
.pause
.date
= date
;
320 vout_control_Push(&vout
->p
->control
, &cmd
);
322 vout_control_WaitEmpty(&vout
->p
->control
);
325 void vout_GetResetStatistic(vout_thread_t
*vout
, unsigned *restrict displayed
,
326 unsigned *restrict lost
)
328 vout_statistic_GetReset( &vout
->p
->statistic
, displayed
, lost
);
331 void vout_Flush(vout_thread_t
*vout
, mtime_t date
)
333 vout_control_PushTime(&vout
->p
->control
, VOUT_CONTROL_FLUSH
, date
);
334 vout_control_WaitEmpty(&vout
->p
->control
);
337 bool vout_IsEmpty(vout_thread_t
*vout
)
339 picture_t
*picture
= picture_fifo_Peek(vout
->p
->decoder_fifo
);
341 picture_Release(picture
);
346 void vout_NextPicture(vout_thread_t
*vout
, mtime_t
*duration
)
348 vout_control_cmd_t cmd
;
349 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_STEP
);
350 cmd
.u
.time_ptr
= duration
;
352 vout_control_Push(&vout
->p
->control
, &cmd
);
353 vout_control_WaitEmpty(&vout
->p
->control
);
356 void vout_DisplayTitle(vout_thread_t
*vout
, const char *title
)
359 vout_control_PushString(&vout
->p
->control
, VOUT_CONTROL_OSD_TITLE
, title
);
362 void vout_WindowMouseEvent(vout_thread_t
*vout
,
363 const vout_window_mouse_event_t
*mouse
)
366 vout_control_cmd_t cmd
;
367 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_WINDOW_MOUSE
);
368 cmd
.u
.window_mouse
= *mouse
;
370 vout_control_Push(&vout
->p
->control
, &cmd
);
373 void vout_PutSubpicture( vout_thread_t
*vout
, subpicture_t
*subpic
)
375 vout_control_cmd_t cmd
;
376 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_SUBPICTURE
);
377 cmd
.u
.subpicture
= subpic
;
379 vout_control_Push(&vout
->p
->control
, &cmd
);
381 int vout_RegisterSubpictureChannel( vout_thread_t
*vout
)
383 int channel
= VOUT_SPU_CHANNEL_AVAIL_FIRST
;
385 vlc_mutex_lock(&vout
->p
->spu_lock
);
387 channel
= spu_RegisterChannel(vout
->p
->spu
);
388 vlc_mutex_unlock(&vout
->p
->spu_lock
);
392 void vout_FlushSubpictureChannel( vout_thread_t
*vout
, int channel
)
394 vout_control_PushInteger(&vout
->p
->control
, VOUT_CONTROL_FLUSH_SUBPICTURE
,
399 * Allocates a video output picture buffer.
401 * Either vout_PutPicture() or picture_Release() must be used to return the
402 * buffer to the video output free buffer pool.
404 * You may use picture_Hold() (paired with picture_Release()) to keep a
405 * read-only reference.
407 picture_t
*vout_GetPicture(vout_thread_t
*vout
)
409 picture_t
*picture
= picture_pool_Wait(vout
->p
->decoder_pool
);
410 if (likely(picture
!= NULL
)) {
411 picture_Reset(picture
);
412 VideoFormatCopyCropAr(&picture
->format
, &vout
->p
->original
);
418 * It gives to the vout a picture to be displayed.
420 * The given picture MUST comes from vout_GetPicture.
422 * Becareful, after vout_PutPicture is called, picture_t::p_next cannot be
425 void vout_PutPicture(vout_thread_t
*vout
, picture_t
*picture
)
427 picture
->p_next
= NULL
;
428 if (picture_pool_OwnsPic(vout
->p
->decoder_pool
, picture
))
430 picture_fifo_Push(vout
->p
->decoder_fifo
, picture
);
432 vout_control_Wake(&vout
->p
->control
);
436 /* FIXME: HACK: Drop this picture because the vout changed. The old
437 * picture pool need to be kept by the new vout. This requires a major
438 * "vout display" API change. */
439 picture_Release(picture
);
444 int vout_GetSnapshot(vout_thread_t
*vout
,
445 block_t
**image_dst
, picture_t
**picture_dst
,
447 const char *type
, mtime_t timeout
)
449 picture_t
*picture
= vout_snapshot_Get(&vout
->p
->snapshot
, timeout
);
451 msg_Err(vout
, "Failed to grab a snapshot");
456 vlc_fourcc_t codec
= VLC_CODEC_PNG
;
457 if (type
&& image_Type2Fourcc(type
))
458 codec
= image_Type2Fourcc(type
);
460 const int override_width
= var_InheritInteger(vout
, "snapshot-width");
461 const int override_height
= var_InheritInteger(vout
, "snapshot-height");
463 if (picture_Export(VLC_OBJECT(vout
), image_dst
, fmt
,
464 picture
, codec
, override_width
, override_height
)) {
465 msg_Err(vout
, "Failed to convert image for snapshot");
466 picture_Release(picture
);
471 *picture_dst
= picture
;
473 picture_Release(picture
);
477 void vout_ChangeAspectRatio( vout_thread_t
*p_vout
,
478 unsigned int i_num
, unsigned int i_den
)
480 vout_ControlChangeSampleAspectRatio( p_vout
, i_num
, i_den
);
483 /* vout_Control* are usable by anyone at anytime */
484 void vout_ControlChangeFullscreen(vout_thread_t
*vout
, bool fullscreen
)
486 vout_control_PushBool(&vout
->p
->control
, VOUT_CONTROL_FULLSCREEN
,
489 void vout_ControlChangeWindowState(vout_thread_t
*vout
, unsigned st
)
491 vout_control_PushInteger(&vout
->p
->control
, VOUT_CONTROL_WINDOW_STATE
, st
);
493 void vout_ControlChangeDisplayFilled(vout_thread_t
*vout
, bool is_filled
)
495 vout_control_PushBool(&vout
->p
->control
, VOUT_CONTROL_DISPLAY_FILLED
,
498 void vout_ControlChangeZoom(vout_thread_t
*vout
, int num
, int den
)
500 vout_control_PushPair(&vout
->p
->control
, VOUT_CONTROL_ZOOM
,
503 void vout_ControlChangeSampleAspectRatio(vout_thread_t
*vout
,
504 unsigned num
, unsigned den
)
506 vout_control_PushPair(&vout
->p
->control
, VOUT_CONTROL_ASPECT_RATIO
,
509 void vout_ControlChangeCropRatio(vout_thread_t
*vout
,
510 unsigned num
, unsigned den
)
512 vout_control_PushPair(&vout
->p
->control
, VOUT_CONTROL_CROP_RATIO
,
515 void vout_ControlChangeCropWindow(vout_thread_t
*vout
,
516 int x
, int y
, int width
, int height
)
518 vout_control_cmd_t cmd
;
519 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_CROP_WINDOW
);
520 cmd
.u
.window
.x
= __MAX(x
, 0);
521 cmd
.u
.window
.y
= __MAX(y
, 0);
522 cmd
.u
.window
.width
= __MAX(width
, 0);
523 cmd
.u
.window
.height
= __MAX(height
, 0);
525 vout_control_Push(&vout
->p
->control
, &cmd
);
527 void vout_ControlChangeCropBorder(vout_thread_t
*vout
,
528 int left
, int top
, int right
, int bottom
)
530 vout_control_cmd_t cmd
;
531 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_CROP_BORDER
);
532 cmd
.u
.border
.left
= __MAX(left
, 0);
533 cmd
.u
.border
.top
= __MAX(top
, 0);
534 cmd
.u
.border
.right
= __MAX(right
, 0);
535 cmd
.u
.border
.bottom
= __MAX(bottom
, 0);
537 vout_control_Push(&vout
->p
->control
, &cmd
);
539 void vout_ControlChangeFilters(vout_thread_t
*vout
, const char *filters
)
541 vout_control_PushString(&vout
->p
->control
, VOUT_CONTROL_CHANGE_FILTERS
,
544 void vout_ControlChangeSubSources(vout_thread_t
*vout
, const char *filters
)
546 vout_control_PushString(&vout
->p
->control
, VOUT_CONTROL_CHANGE_SUB_SOURCES
,
549 void vout_ControlChangeSubFilters(vout_thread_t
*vout
, const char *filters
)
551 vout_control_PushString(&vout
->p
->control
, VOUT_CONTROL_CHANGE_SUB_FILTERS
,
554 void vout_ControlChangeSubMargin(vout_thread_t
*vout
, int margin
)
556 vout_control_PushInteger(&vout
->p
->control
, VOUT_CONTROL_CHANGE_SUB_MARGIN
,
560 void vout_ControlChangeViewpoint(vout_thread_t
*vout
,
561 const vlc_viewpoint_t
*p_viewpoint
)
563 vout_control_cmd_t cmd
;
564 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_VIEWPOINT
);
565 cmd
.u
.viewpoint
= *p_viewpoint
;
566 vout_control_Push(&vout
->p
->control
, &cmd
);
570 static void VoutGetDisplayCfg(vout_thread_t
*vout
, vout_display_cfg_t
*cfg
, const char *title
)
572 /* Load configuration */
573 #if defined(_WIN32) || defined(__OS2__)
574 cfg
->is_fullscreen
= var_GetBool(vout
, "fullscreen")
575 || var_GetBool(vout
, "video-wallpaper");
577 cfg
->viewpoint
= vout
->p
->original
.pose
;
579 cfg
->display
.title
= title
;
580 const int display_width
= var_GetInteger(vout
, "width");
581 const int display_height
= var_GetInteger(vout
, "height");
582 cfg
->display
.width
= display_width
> 0 ? display_width
: 0;
583 cfg
->display
.height
= display_height
> 0 ? display_height
: 0;
584 cfg
->is_display_filled
= var_GetBool(vout
, "autoscale");
585 unsigned msar_num
, msar_den
;
586 if (var_InheritURational(vout
, &msar_num
, &msar_den
, "monitor-par") ||
587 msar_num
<= 0 || msar_den
<= 0) {
591 cfg
->display
.sar
.num
= msar_num
;
592 cfg
->display
.sar
.den
= msar_den
;
593 unsigned zoom_den
= 1000;
594 unsigned zoom_num
= zoom_den
* var_GetFloat(vout
, "zoom");
595 vlc_ureduce(&zoom_num
, &zoom_den
, zoom_num
, zoom_den
, 0);
596 cfg
->zoom
.num
= zoom_num
;
597 cfg
->zoom
.den
= zoom_den
;
598 cfg
->align
.vertical
= VOUT_DISPLAY_ALIGN_CENTER
;
599 cfg
->align
.horizontal
= VOUT_DISPLAY_ALIGN_CENTER
;
600 const int align_mask
= var_GetInteger(vout
, "align");
601 if (align_mask
& 0x1)
602 cfg
->align
.horizontal
= VOUT_DISPLAY_ALIGN_LEFT
;
603 else if (align_mask
& 0x2)
604 cfg
->align
.horizontal
= VOUT_DISPLAY_ALIGN_RIGHT
;
605 if (align_mask
& 0x4)
606 cfg
->align
.vertical
= VOUT_DISPLAY_ALIGN_TOP
;
607 else if (align_mask
& 0x8)
608 cfg
->align
.vertical
= VOUT_DISPLAY_ALIGN_BOTTOM
;
611 vout_window_t
*vout_NewDisplayWindow(vout_thread_t
*vout
, unsigned type
)
613 vout_window_t
*window
= vout
->p
->window
;
615 assert(vout
->p
->splitter_name
== NULL
);
619 if (type
!= VOUT_WINDOW_TYPE_INVALID
&& type
!= window
->type
)
624 void vout_DeleteDisplayWindow(vout_thread_t
*vout
, vout_window_t
*window
)
626 if (window
== NULL
&& vout
->p
->window
!= NULL
) {
627 vout_display_window_Delete(vout
->p
->window
);
628 vout
->p
->window
= NULL
;
630 assert(vout
->p
->window
== window
);
633 void vout_SetDisplayWindowSize(vout_thread_t
*vout
,
634 unsigned width
, unsigned height
)
636 vout_window_t
*window
= vout
->p
->window
;
639 /* Request a resize of the window. If it fails, there is nothing to do.
640 * If it succeeds, the window will emit a resize event later. */
641 vout_window_SetSize(window
, width
, height
);
643 if (vout
->p
->display
.vd
!= NULL
)
644 /* Force a resize of window-less display. This is not allowed to fail,
645 * although the display is allowed to ignore the size anyway. */
646 /* FIXME: remove this, fix MSW and OS/2 window providers */
647 vout_display_SendEventDisplaySize(vout
->p
->display
.vd
, width
, height
);
650 int vout_HideWindowMouse(vout_thread_t
*vout
, bool hide
)
652 vout_window_t
*window
= vout
->p
->window
;
654 return window
!= NULL
? vout_window_HideMouse(window
, hide
) : VLC_EGENERIC
;
658 static int FilterRestartCallback(vlc_object_t
*p_this
, char const *psz_var
,
659 vlc_value_t oldval
, vlc_value_t newval
,
662 (void) p_this
; (void) psz_var
; (void) oldval
; (void) newval
;
663 vout_ControlChangeFilters((vout_thread_t
*)p_data
, NULL
);
667 static int ThreadDelFilterCallbacks(filter_t
*filter
, void *opaque
)
669 filter_DelProxyCallbacks((vlc_object_t
*)opaque
, filter
,
670 FilterRestartCallback
);
674 static void ThreadDelAllFilterCallbacks(vout_thread_t
*vout
)
676 assert(vout
->p
->filter
.chain_interactive
!= NULL
);
677 filter_chain_ForEach(vout
->p
->filter
.chain_interactive
,
678 ThreadDelFilterCallbacks
, vout
);
681 static picture_t
*VoutVideoFilterInteractiveNewPicture(filter_t
*filter
)
683 vout_thread_t
*vout
= filter
->owner
.sys
;
685 picture_t
*picture
= picture_pool_Get(vout
->p
->private_pool
);
687 picture_Reset(picture
);
688 VideoFormatCopyCropAr(&picture
->format
, &filter
->fmt_out
.video
);
693 static picture_t
*VoutVideoFilterStaticNewPicture(filter_t
*filter
)
695 vout_thread_t
*vout
= filter
->owner
.sys
;
697 vlc_assert_locked(&vout
->p
->filter
.lock
);
698 if (filter_chain_IsEmpty(vout
->p
->filter
.chain_interactive
))
699 return VoutVideoFilterInteractiveNewPicture(filter
);
701 return picture_NewFromFormat(&filter
->fmt_out
.video
);
704 static void ThreadFilterFlush(vout_thread_t
*vout
, bool is_locked
)
706 if (vout
->p
->displayed
.current
)
707 picture_Release( vout
->p
->displayed
.current
);
708 vout
->p
->displayed
.current
= NULL
;
710 if (vout
->p
->displayed
.next
)
711 picture_Release( vout
->p
->displayed
.next
);
712 vout
->p
->displayed
.next
= NULL
;
715 vlc_mutex_lock(&vout
->p
->filter
.lock
);
716 filter_chain_VideoFlush(vout
->p
->filter
.chain_static
);
717 filter_chain_VideoFlush(vout
->p
->filter
.chain_interactive
);
719 vlc_mutex_unlock(&vout
->p
->filter
.lock
);
727 static void ThreadChangeFilters(vout_thread_t
*vout
,
728 const video_format_t
*source
,
733 ThreadFilterFlush(vout
, is_locked
);
734 ThreadDelAllFilterCallbacks(vout
);
736 vlc_array_t array_static
;
737 vlc_array_t array_interactive
;
739 vlc_array_init(&array_static
);
740 vlc_array_init(&array_interactive
);
742 if ((vout
->p
->filter
.has_deint
=
743 deinterlace
== 1 || (deinterlace
== -1 && vout
->p
->filter
.has_deint
)))
745 vout_filter_t
*e
= malloc(sizeof(*e
));
749 free(config_ChainCreate(&e
->name
, &e
->cfg
, "deinterlace"));
750 vlc_array_append_or_abort(&array_static
, e
);
754 char *current
= filters
? strdup(filters
) : NULL
;
758 char *next
= config_ChainCreate(&name
, &cfg
, current
);
761 vout_filter_t
*e
= malloc(sizeof(*e
));
766 if (!strcmp(e
->name
, "postproc"))
767 vlc_array_append_or_abort(&array_static
, e
);
769 vlc_array_append_or_abort(&array_interactive
, e
);
773 config_ChainDestroy(cfg
);
778 config_ChainDestroy(cfg
);
786 vlc_mutex_lock(&vout
->p
->filter
.lock
);
788 es_format_t fmt_target
;
789 es_format_InitFromVideo(&fmt_target
, source
? source
: &vout
->p
->filter
.format
);
791 const es_format_t
*p_fmt_current
= &fmt_target
;
793 for (int a
= 0; a
< 2; a
++) {
794 vlc_array_t
*array
= a
== 0 ? &array_static
:
796 filter_chain_t
*chain
= a
== 0 ? vout
->p
->filter
.chain_static
:
797 vout
->p
->filter
.chain_interactive
;
799 filter_chain_Reset(chain
, p_fmt_current
, p_fmt_current
);
800 for (size_t i
= 0; i
< vlc_array_count(array
); i
++) {
801 vout_filter_t
*e
= vlc_array_item_at_index(array
, i
);
802 msg_Dbg(vout
, "Adding '%s' as %s", e
->name
, a
== 0 ? "static" : "interactive");
803 filter_t
*filter
= filter_chain_AppendFilter(chain
, e
->name
, e
->cfg
,
807 msg_Err(vout
, "Failed to add filter '%s'", e
->name
);
808 config_ChainDestroy(e
->cfg
);
810 else if (a
== 1) /* Add callbacks for interactive filters */
811 filter_AddProxyCallbacks(vout
, filter
, FilterRestartCallback
);
816 p_fmt_current
= filter_chain_GetFmtOut(chain
);
817 vlc_array_clear(array
);
820 if (!es_format_IsSimilar(p_fmt_current
, &fmt_target
)) {
821 msg_Dbg(vout
, "Adding a filter to compensate for format changes");
822 if (filter_chain_AppendConverter(vout
->p
->filter
.chain_interactive
,
823 p_fmt_current
, &fmt_target
) != 0) {
824 msg_Err(vout
, "Failed to compensate for the format changes, removing all filters");
825 ThreadDelAllFilterCallbacks(vout
);
826 filter_chain_Reset(vout
->p
->filter
.chain_static
, &fmt_target
, &fmt_target
);
827 filter_chain_Reset(vout
->p
->filter
.chain_interactive
, &fmt_target
, &fmt_target
);
831 es_format_Clean(&fmt_target
);
833 if (vout
->p
->filter
.configuration
!= filters
) {
834 free(vout
->p
->filter
.configuration
);
835 vout
->p
->filter
.configuration
= filters
? strdup(filters
) : NULL
;
838 video_format_Clean(&vout
->p
->filter
.format
);
839 video_format_Copy(&vout
->p
->filter
.format
, source
);
843 vlc_mutex_unlock(&vout
->p
->filter
.lock
);
848 static int ThreadDisplayPreparePicture(vout_thread_t
*vout
, bool reuse
, bool frame_by_frame
)
850 bool is_late_dropped
= vout
->p
->is_late_dropped
&& !vout
->p
->pause
.is_on
&& !frame_by_frame
;
852 vlc_mutex_lock(&vout
->p
->filter
.lock
);
854 picture_t
*picture
= filter_chain_VideoFilter(vout
->p
->filter
.chain_static
, NULL
);
855 assert(!reuse
|| !picture
);
859 if (reuse
&& vout
->p
->displayed
.decoded
) {
860 decoded
= picture_Hold(vout
->p
->displayed
.decoded
);
862 decoded
= picture_fifo_Pop(vout
->p
->decoder_fifo
);
864 if (is_late_dropped
&& !decoded
->b_force
) {
865 const mtime_t predicted
= mdate() + 0; /* TODO improve */
866 const mtime_t late
= predicted
- decoded
->date
;
867 if (late
> VOUT_DISPLAY_LATE_THRESHOLD
) {
868 msg_Warn(vout
, "picture is too late to be displayed (missing %"PRId64
" ms)", late
/1000);
869 picture_Release(decoded
);
870 vout_statistic_AddLost(&vout
->p
->statistic
, 1);
872 } else if (late
> 0) {
873 msg_Dbg(vout
, "picture might be displayed late (missing %"PRId64
" ms)", late
/1000);
876 if (!VideoFormatIsCropArEqual(&decoded
->format
, &vout
->p
->filter
.format
))
877 ThreadChangeFilters(vout
, &decoded
->format
, vout
->p
->filter
.configuration
, -1, true);
885 if (vout
->p
->displayed
.decoded
)
886 picture_Release(vout
->p
->displayed
.decoded
);
888 vout
->p
->displayed
.decoded
= picture_Hold(decoded
);
889 vout
->p
->displayed
.timestamp
= decoded
->date
;
890 vout
->p
->displayed
.is_interlaced
= !decoded
->b_progressive
;
892 picture
= filter_chain_VideoFilter(vout
->p
->filter
.chain_static
, decoded
);
895 vlc_mutex_unlock(&vout
->p
->filter
.lock
);
900 assert(!vout
->p
->displayed
.next
);
901 if (!vout
->p
->displayed
.current
)
902 vout
->p
->displayed
.current
= picture
;
904 vout
->p
->displayed
.next
= picture
;
908 static int ThreadDisplayRenderPicture(vout_thread_t
*vout
, bool is_forced
)
910 vout_thread_sys_t
*sys
= vout
->p
;
911 vout_display_t
*vd
= vout
->p
->display
.vd
;
913 picture_t
*torender
= picture_Hold(vout
->p
->displayed
.current
);
915 vout_chrono_Start(&vout
->p
->render
);
917 vlc_mutex_lock(&vout
->p
->filter
.lock
);
918 picture_t
*filtered
= filter_chain_VideoFilter(vout
->p
->filter
.chain_interactive
, torender
);
919 vlc_mutex_unlock(&vout
->p
->filter
.lock
);
924 if (filtered
->date
!= vout
->p
->displayed
.current
->date
)
925 msg_Warn(vout
, "Unsupported timestamp modifications done by chain_interactive");
928 * Get the subpicture to be displayed
930 const bool do_snapshot
= vout_snapshot_IsRequested(&vout
->p
->snapshot
);
931 mtime_t render_subtitle_date
;
932 if (vout
->p
->pause
.is_on
)
933 render_subtitle_date
= vout
->p
->pause
.date
;
935 render_subtitle_date
= filtered
->date
> 1 ? filtered
->date
: mdate();
936 mtime_t render_osd_date
= mdate(); /* FIXME wrong */
939 * Get the subpicture to be displayed
941 const bool do_dr_spu
= !do_snapshot
&&
942 vd
->info
.subpicture_chromas
&&
943 *vd
->info
.subpicture_chromas
!= 0;
945 //FIXME: Denying do_early_spu if vd->source.orientation != ORIENT_NORMAL
946 //will have the effect that snapshots miss the subpictures. We do this
947 //because there is currently no way to transform subpictures to match
949 const bool do_early_spu
= !do_dr_spu
&&
950 vd
->source
.orientation
== ORIENT_NORMAL
&&
952 sys
->display
.use_dr
||
954 vd
->fmt
.i_width
* vd
->fmt
.i_height
<= vd
->source
.i_width
* vd
->source
.i_height
);
956 const vlc_fourcc_t
*subpicture_chromas
;
957 video_format_t fmt_spu
;
959 vout_display_place_t place
;
960 vout_display_PlacePicture(&place
, &vd
->source
, vd
->cfg
, false);
962 fmt_spu
= vd
->source
;
963 if (fmt_spu
.i_width
* fmt_spu
.i_height
< place
.width
* place
.height
) {
964 fmt_spu
.i_sar_num
= vd
->cfg
->display
.sar
.num
;
965 fmt_spu
.i_sar_den
= vd
->cfg
->display
.sar
.den
;
967 fmt_spu
.i_visible_width
= place
.width
;
969 fmt_spu
.i_visible_height
= place
.height
;
971 subpicture_chromas
= vd
->info
.subpicture_chromas
;
974 fmt_spu
= vd
->source
;
977 fmt_spu
.i_sar_num
= vd
->cfg
->display
.sar
.num
;
978 fmt_spu
.i_sar_den
= vd
->cfg
->display
.sar
.den
;
980 subpicture_chromas
= NULL
;
982 if (vout
->p
->spu_blend
&&
983 vout
->p
->spu_blend
->fmt_out
.video
.i_chroma
!= fmt_spu
.i_chroma
) {
984 filter_DeleteBlend(vout
->p
->spu_blend
);
985 vout
->p
->spu_blend
= NULL
;
986 vout
->p
->spu_blend_chroma
= 0;
988 if (!vout
->p
->spu_blend
&& vout
->p
->spu_blend_chroma
!= fmt_spu
.i_chroma
) {
989 vout
->p
->spu_blend_chroma
= fmt_spu
.i_chroma
;
990 vout
->p
->spu_blend
= filter_NewBlend(VLC_OBJECT(vout
), &fmt_spu
);
991 if (!vout
->p
->spu_blend
)
992 msg_Err(vout
, "Failed to create blending filter, OSD/Subtitles will not work");
996 video_format_t fmt_spu_rot
;
997 video_format_ApplyRotation(&fmt_spu_rot
, &fmt_spu
);
998 subpicture_t
*subpic
= spu_Render(vout
->p
->spu
,
999 subpicture_chromas
, &fmt_spu_rot
,
1001 render_subtitle_date
, render_osd_date
,
1007 * - be sure to end up with a direct buffer.
1008 * - blend subtitles, and in a fast access buffer
1010 bool is_direct
= vout
->p
->decoder_pool
== vout
->p
->display_pool
;
1011 picture_t
*todisplay
= filtered
;
1012 if (do_early_spu
&& subpic
) {
1013 if (vout
->p
->spu_blend
) {
1014 picture_t
*blent
= picture_pool_Get(vout
->p
->private_pool
);
1016 VideoFormatCopyCropAr(&blent
->format
, &filtered
->format
);
1017 picture_Copy(blent
, filtered
);
1018 if (picture_BlendSubpicture(blent
, vout
->p
->spu_blend
, subpic
)) {
1019 picture_Release(todisplay
);
1022 picture_Release(blent
);
1025 subpicture_Delete(subpic
);
1029 assert(vout_IsDisplayFiltered(vd
) == !sys
->display
.use_dr
);
1030 if (sys
->display
.use_dr
&& !is_direct
) {
1031 picture_t
*direct
= NULL
;
1032 if (likely(vout
->p
->display_pool
!= NULL
))
1033 direct
= picture_pool_Get(vout
->p
->display_pool
);
1035 picture_Release(todisplay
);
1037 subpicture_Delete(subpic
);
1038 return VLC_EGENERIC
;
1041 /* The display uses direct rendering (no conversion), but its pool of
1042 * pictures is not usable by the decoder (too few, too slow or
1043 * subject to invalidation...). Since there are no filters, copying
1044 * pictures from the decoder to the output is unavoidable. */
1045 VideoFormatCopyCropAr(&direct
->format
, &todisplay
->format
);
1046 picture_Copy(direct
, todisplay
);
1047 picture_Release(todisplay
);
1052 * Take a snapshot if requested
1055 vout_snapshot_Set(&vout
->p
->snapshot
, &vd
->source
, todisplay
);
1057 /* Render the direct buffer */
1058 vout_UpdateDisplaySourceProperties(vd
, &todisplay
->format
);
1060 todisplay
= vout_FilterDisplay(vd
, todisplay
);
1061 if (todisplay
== NULL
) {
1063 subpicture_Delete(subpic
);
1064 return VLC_EGENERIC
;
1067 if (sys
->display
.use_dr
) {
1068 vout_display_Prepare(vd
, todisplay
, subpic
);
1070 if (!do_dr_spu
&& !do_early_spu
&& vout
->p
->spu_blend
&& subpic
)
1071 picture_BlendSubpicture(todisplay
, vout
->p
->spu_blend
, subpic
);
1072 vout_display_Prepare(vd
, todisplay
, do_dr_spu
? subpic
: NULL
);
1074 if (!do_dr_spu
&& subpic
)
1076 subpicture_Delete(subpic
);
1081 vout_chrono_Stop(&vout
->p
->render
);
1085 if (((i
++)%10) == 0)
1086 msg_Info(vout
, "render: avg %d ms var %d ms",
1087 (int)(vout
->p
->render
.avg
/1000), (int)(vout
->p
->render
.var
/1000));
1091 /* Wait the real date (for rendering jitter) */
1093 mtime_t delay
= todisplay
->date
- mdate();
1095 msg_Warn(vout
, "picture is late (%lld ms)", delay
/ 1000);
1098 mwait(todisplay
->date
);
1100 /* Display the direct buffer returned by vout_RenderPicture */
1101 vout
->p
->displayed
.date
= mdate();
1102 vout_display_Display(vd
, todisplay
, subpic
);
1104 vout_statistic_AddDisplayed(&vout
->p
->statistic
, 1);
1109 static int ThreadDisplayPicture(vout_thread_t
*vout
, mtime_t
*deadline
)
1111 bool frame_by_frame
= !deadline
;
1112 bool paused
= vout
->p
->pause
.is_on
;
1113 bool first
= !vout
->p
->displayed
.current
;
1116 if (ThreadDisplayPreparePicture(vout
, true, frame_by_frame
)) /* FIXME not sure it is ok */
1117 return VLC_EGENERIC
;
1119 if (!paused
|| frame_by_frame
)
1120 while (!vout
->p
->displayed
.next
&& !ThreadDisplayPreparePicture(vout
, false, frame_by_frame
))
1123 const mtime_t date
= mdate();
1124 const mtime_t render_delay
= vout_chrono_GetHigh(&vout
->p
->render
) + VOUT_MWAIT_TOLERANCE
;
1126 bool drop_next_frame
= frame_by_frame
;
1127 mtime_t date_next
= VLC_TS_INVALID
;
1128 if (!paused
&& vout
->p
->displayed
.next
) {
1129 date_next
= vout
->p
->displayed
.next
->date
- render_delay
;
1130 if (date_next
/* + 0 FIXME */ <= date
)
1131 drop_next_frame
= true;
1134 /* FIXME/XXX we must redisplay the last decoded picture (because
1135 * of potential vout updated, or filters update or SPU update)
1136 * For now a high update period is needed but it could be removed
1138 * - vout module emits events from theselves.
1139 * - *and* SPU is modified to emit an event or a deadline when needed.
1141 * So it will be done later.
1143 bool refresh
= false;
1145 mtime_t date_refresh
= VLC_TS_INVALID
;
1146 if (vout
->p
->displayed
.date
> VLC_TS_INVALID
) {
1147 date_refresh
= vout
->p
->displayed
.date
+ VOUT_REDISPLAY_DELAY
- render_delay
;
1148 refresh
= date_refresh
<= date
;
1150 bool force_refresh
= !drop_next_frame
&& refresh
;
1152 if (!first
&& !refresh
&& !drop_next_frame
) {
1153 if (!frame_by_frame
) {
1154 if (date_refresh
!= VLC_TS_INVALID
)
1155 *deadline
= date_refresh
;
1156 if (date_next
!= VLC_TS_INVALID
&& date_next
< *deadline
)
1157 *deadline
= date_next
;
1159 return VLC_EGENERIC
;
1162 if (drop_next_frame
) {
1163 picture_Release(vout
->p
->displayed
.current
);
1164 vout
->p
->displayed
.current
= vout
->p
->displayed
.next
;
1165 vout
->p
->displayed
.next
= NULL
;
1168 if (!vout
->p
->displayed
.current
)
1169 return VLC_EGENERIC
;
1171 /* display the picture immediately */
1172 bool is_forced
= frame_by_frame
|| force_refresh
|| vout
->p
->displayed
.current
->b_force
;
1173 int ret
= ThreadDisplayRenderPicture(vout
, is_forced
);
1174 return force_refresh
? VLC_EGENERIC
: ret
;
1177 static void ThreadDisplaySubpicture(vout_thread_t
*vout
,
1178 subpicture_t
*subpicture
)
1180 spu_PutSubpicture(vout
->p
->spu
, subpicture
);
1183 static void ThreadFlushSubpicture(vout_thread_t
*vout
, int channel
)
1185 spu_ClearChannel(vout
->p
->spu
, channel
);
1188 static void ThreadDisplayOsdTitle(vout_thread_t
*vout
, const char *string
)
1190 if (!vout
->p
->title
.show
)
1193 vout_OSDText(vout
, VOUT_SPU_CHANNEL_OSD
,
1194 vout
->p
->title
.position
, INT64_C(1000) * vout
->p
->title
.timeout
,
1198 static void ThreadChangeSubSources(vout_thread_t
*vout
, const char *filters
)
1200 spu_ChangeSources(vout
->p
->spu
, filters
);
1203 static void ThreadChangeSubFilters(vout_thread_t
*vout
, const char *filters
)
1205 spu_ChangeFilters(vout
->p
->spu
, filters
);
1208 static void ThreadChangeSubMargin(vout_thread_t
*vout
, int margin
)
1210 spu_ChangeMargin(vout
->p
->spu
, margin
);
1213 static void ThreadChangePause(vout_thread_t
*vout
, bool is_paused
, mtime_t date
)
1215 assert(!vout
->p
->pause
.is_on
|| !is_paused
);
1217 if (vout
->p
->pause
.is_on
) {
1218 const mtime_t duration
= date
- vout
->p
->pause
.date
;
1220 if (vout
->p
->step
.timestamp
> VLC_TS_INVALID
)
1221 vout
->p
->step
.timestamp
+= duration
;
1222 if (vout
->p
->step
.last
> VLC_TS_INVALID
)
1223 vout
->p
->step
.last
+= duration
;
1224 picture_fifo_OffsetDate(vout
->p
->decoder_fifo
, duration
);
1225 if (vout
->p
->displayed
.decoded
)
1226 vout
->p
->displayed
.decoded
->date
+= duration
;
1227 spu_OffsetSubtitleDate(vout
->p
->spu
, duration
);
1229 ThreadFilterFlush(vout
, false);
1231 vout
->p
->step
.timestamp
= VLC_TS_INVALID
;
1232 vout
->p
->step
.last
= VLC_TS_INVALID
;
1234 vout
->p
->pause
.is_on
= is_paused
;
1235 vout
->p
->pause
.date
= date
;
1237 vout_window_t
*window
= vout
->p
->window
;
1239 vout_window_SetInhibition(window
, !is_paused
);
1242 static void ThreadFlush(vout_thread_t
*vout
, bool below
, mtime_t date
)
1244 vout
->p
->step
.timestamp
= VLC_TS_INVALID
;
1245 vout
->p
->step
.last
= VLC_TS_INVALID
;
1247 ThreadFilterFlush(vout
, false); /* FIXME too much */
1249 picture_t
*last
= vout
->p
->displayed
.decoded
;
1251 if (( below
&& last
->date
<= date
) ||
1252 (!below
&& last
->date
>= date
)) {
1253 picture_Release(last
);
1255 vout
->p
->displayed
.decoded
= NULL
;
1256 vout
->p
->displayed
.date
= VLC_TS_INVALID
;
1257 vout
->p
->displayed
.timestamp
= VLC_TS_INVALID
;
1261 picture_fifo_Flush(vout
->p
->decoder_fifo
, date
, below
);
1262 vout_FilterFlush(vout
->p
->display
.vd
);
1265 static void ThreadStep(vout_thread_t
*vout
, mtime_t
*duration
)
1269 if (vout
->p
->step
.last
<= VLC_TS_INVALID
)
1270 vout
->p
->step
.last
= vout
->p
->displayed
.timestamp
;
1272 if (ThreadDisplayPicture(vout
, NULL
))
1275 vout
->p
->step
.timestamp
= vout
->p
->displayed
.timestamp
;
1277 if (vout
->p
->step
.last
> VLC_TS_INVALID
&&
1278 vout
->p
->step
.timestamp
> vout
->p
->step
.last
) {
1279 *duration
= vout
->p
->step
.timestamp
- vout
->p
->step
.last
;
1280 vout
->p
->step
.last
= vout
->p
->step
.timestamp
;
1281 /* TODO advance subpicture by the duration ... */
1285 static void ThreadChangeFullscreen(vout_thread_t
*vout
, bool fullscreen
)
1287 vout_window_t
*window
= vout
->p
->window
;
1289 #if !defined(_WIN32) && !defined(__OS2__)
1291 vout_window_SetFullScreen(window
, fullscreen
);
1293 bool window_fullscreen
= false;
1295 && vout_window_SetFullScreen(window
, fullscreen
) == VLC_SUCCESS
)
1296 window_fullscreen
= true;
1297 /* FIXME: remove this event */
1298 if (vout
->p
->display
.vd
!= NULL
)
1299 vout_display_SendEventFullscreen(vout
->p
->display
.vd
, fullscreen
, window_fullscreen
);
1303 static void ThreadChangeWindowState(vout_thread_t
*vout
, unsigned state
)
1305 vout_window_t
*window
= vout
->p
->window
;
1308 vout_window_SetState(window
, state
);
1309 #if defined(_WIN32) || defined(__OS2__)
1310 else /* FIXME: remove this event */
1311 if (vout
->p
->display
.vd
!= NULL
)
1312 vout_display_SendWindowState(vout
->p
->display
.vd
, state
);
1316 static void ThreadChangeWindowMouse(vout_thread_t
*vout
,
1317 const vout_window_mouse_event_t
*mouse
)
1319 vout_display_t
*vd
= vout
->p
->display
.vd
;
1320 switch (mouse
->type
)
1322 case VOUT_WINDOW_MOUSE_STATE
:
1323 case VOUT_WINDOW_MOUSE_MOVED
:
1325 vout_display_place_t place
;
1326 vout_display_PlacePicture(&place
, &vd
->source
, vd
->cfg
, false);
1328 if (place
.width
<= 0 || place
.height
<= 0)
1331 const int x
= vd
->source
.i_x_offset
+
1332 (int64_t)(mouse
->x
- place
.x
) *
1333 vd
->source
.i_visible_width
/ place
.width
;
1334 const int y
= vd
->source
.i_y_offset
+
1335 (int64_t)(mouse
->y
- place
.y
) *
1336 vd
->source
.i_visible_height
/ place
.height
;
1338 if (mouse
->type
== VOUT_WINDOW_MOUSE_STATE
)
1339 vout_display_SendEventMouseState(vd
, x
, y
, mouse
->button_mask
);
1341 vout_display_SendEventMouseMoved(vd
, x
, y
);
1344 case VOUT_WINDOW_MOUSE_PRESSED
:
1345 vout_display_SendEventMousePressed(vd
, mouse
->button_mask
);
1347 case VOUT_WINDOW_MOUSE_RELEASED
:
1348 vout_display_SendEventMouseReleased(vd
, mouse
->button_mask
);
1350 case VOUT_WINDOW_MOUSE_DOUBLE_CLICK
:
1351 if (mouse
->button_mask
== 0)
1352 vout_display_SendEventMouseDoubleClick(vd
);
1354 default: vlc_assert_unreachable();
1359 static void ThreadChangeDisplayFilled(vout_thread_t
*vout
, bool is_filled
)
1361 vout_SetDisplayFilled(vout
->p
->display
.vd
, is_filled
);
1364 static void ThreadChangeZoom(vout_thread_t
*vout
, int num
, int den
)
1366 if (num
* 10 < den
) {
1369 } else if (num
> den
* 10) {
1373 vout_SetDisplayZoom(vout
->p
->display
.vd
, num
, den
);
1376 static void ThreadChangeAspectRatio(vout_thread_t
*vout
,
1377 unsigned num
, unsigned den
)
1379 vout_SetDisplayAspect(vout
->p
->display
.vd
, num
, den
);
1383 static void ThreadExecuteCropWindow(vout_thread_t
*vout
,
1384 unsigned x
, unsigned y
,
1385 unsigned width
, unsigned height
)
1387 vout_SetDisplayCrop(vout
->p
->display
.vd
, 0, 0,
1388 x
, y
, width
, height
);
1390 static void ThreadExecuteCropBorder(vout_thread_t
*vout
,
1391 unsigned left
, unsigned top
,
1392 unsigned right
, unsigned bottom
)
1394 msg_Err(vout
, "ThreadExecuteCropBorder %d.%d %dx%d", left
, top
, right
, bottom
);
1395 vout_SetDisplayCrop(vout
->p
->display
.vd
, 0, 0,
1396 left
, top
, -(int)right
, -(int)bottom
);
1399 static void ThreadExecuteCropRatio(vout_thread_t
*vout
,
1400 unsigned num
, unsigned den
)
1402 vout_SetDisplayCrop(vout
->p
->display
.vd
, num
, den
,
1406 static void ThreadExecuteViewpoint(vout_thread_t
*vout
,
1407 const vlc_viewpoint_t
*p_viewpoint
)
1409 vout_SetDisplayViewpoint(vout
->p
->display
.vd
, p_viewpoint
);
1412 static int ThreadStart(vout_thread_t
*vout
, vout_display_state_t
*state
)
1414 vlc_mouse_Init(&vout
->p
->mouse
);
1415 vout
->p
->decoder_fifo
= picture_fifo_New();
1416 vout
->p
->decoder_pool
= NULL
;
1417 vout
->p
->display_pool
= NULL
;
1418 vout
->p
->private_pool
= NULL
;
1420 vout
->p
->filter
.configuration
= NULL
;
1421 video_format_Copy(&vout
->p
->filter
.format
, &vout
->p
->original
);
1423 filter_owner_t owner
= {
1426 .buffer_new
= VoutVideoFilterStaticNewPicture
,
1429 vout
->p
->filter
.chain_static
=
1430 filter_chain_NewVideo( vout
, true, &owner
);
1432 owner
.video
.buffer_new
= VoutVideoFilterInteractiveNewPicture
;
1433 vout
->p
->filter
.chain_interactive
=
1434 filter_chain_NewVideo( vout
, true, &owner
);
1436 vout_display_state_t state_default
;
1438 VoutGetDisplayCfg(vout
, &state_default
.cfg
, vout
->p
->display
.title
);
1440 #if defined(_WIN32) || defined(__OS2__)
1441 bool below
= var_InheritBool(vout
, "video-wallpaper");
1442 bool above
= var_InheritBool(vout
, "video-on-top");
1444 state_default
.wm_state
= below
? VOUT_WINDOW_STATE_BELOW
1445 : above
? VOUT_WINDOW_STATE_ABOVE
1446 : VOUT_WINDOW_STATE_NORMAL
;
1448 state_default
.sar
.num
= 0;
1449 state_default
.sar
.den
= 0;
1451 state
= &state_default
;
1454 if (vout_OpenWrapper(vout
, vout
->p
->splitter_name
, state
))
1456 if (vout_InitWrapper(vout
))
1458 vout_CloseWrapper(vout
, state
);
1461 assert(vout
->p
->decoder_pool
&& vout
->p
->private_pool
);
1463 vout
->p
->displayed
.current
= NULL
;
1464 vout
->p
->displayed
.next
= NULL
;
1465 vout
->p
->displayed
.decoded
= NULL
;
1466 vout
->p
->displayed
.date
= VLC_TS_INVALID
;
1467 vout
->p
->displayed
.timestamp
= VLC_TS_INVALID
;
1468 vout
->p
->displayed
.is_interlaced
= false;
1470 vout
->p
->step
.last
= VLC_TS_INVALID
;
1471 vout
->p
->step
.timestamp
= VLC_TS_INVALID
;
1473 vout
->p
->spu_blend_chroma
= 0;
1474 vout
->p
->spu_blend
= NULL
;
1476 video_format_Print(VLC_OBJECT(vout
), "original format", &vout
->p
->original
);
1479 if (vout
->p
->filter
.chain_interactive
!= NULL
)
1481 ThreadDelAllFilterCallbacks(vout
);
1482 filter_chain_Delete(vout
->p
->filter
.chain_interactive
);
1484 if (vout
->p
->filter
.chain_static
!= NULL
)
1485 filter_chain_Delete(vout
->p
->filter
.chain_static
);
1486 video_format_Clean(&vout
->p
->filter
.format
);
1487 if (vout
->p
->decoder_fifo
!= NULL
)
1488 picture_fifo_Delete(vout
->p
->decoder_fifo
);
1489 return VLC_EGENERIC
;
1492 static void ThreadStop(vout_thread_t
*vout
, vout_display_state_t
*state
)
1494 if (vout
->p
->spu_blend
)
1495 filter_DeleteBlend(vout
->p
->spu_blend
);
1497 /* Destroy translation tables */
1498 if (vout
->p
->display
.vd
) {
1499 if (vout
->p
->decoder_pool
) {
1500 ThreadFlush(vout
, true, INT64_MAX
);
1501 vout_EndWrapper(vout
);
1503 vout_CloseWrapper(vout
, state
);
1506 /* Destroy the video filters */
1507 ThreadDelAllFilterCallbacks(vout
);
1508 filter_chain_Delete(vout
->p
->filter
.chain_interactive
);
1509 filter_chain_Delete(vout
->p
->filter
.chain_static
);
1510 video_format_Clean(&vout
->p
->filter
.format
);
1511 free(vout
->p
->filter
.configuration
);
1513 if (vout
->p
->decoder_fifo
)
1514 picture_fifo_Delete(vout
->p
->decoder_fifo
);
1515 assert(!vout
->p
->decoder_pool
);
1518 static void ThreadInit(vout_thread_t
*vout
)
1520 vout
->p
->dead
= false;
1521 vout
->p
->is_late_dropped
= var_InheritBool(vout
, "drop-late-frames");
1522 vout
->p
->pause
.is_on
= false;
1523 vout
->p
->pause
.date
= VLC_TS_INVALID
;
1525 vout_chrono_Init(&vout
->p
->render
, 5, 10000); /* Arbitrary initial time */
1528 static void ThreadClean(vout_thread_t
*vout
)
1530 vout_chrono_Clean(&vout
->p
->render
);
1531 vout
->p
->dead
= true;
1532 vout_control_Dead(&vout
->p
->control
);
1535 static int ThreadReinit(vout_thread_t
*vout
,
1536 const vout_configuration_t
*cfg
)
1538 video_format_t original
;
1540 vout
->p
->pause
.is_on
= false;
1541 vout
->p
->pause
.date
= VLC_TS_INVALID
;
1543 if (VoutValidateFormat(&original
, cfg
->fmt
)) {
1544 ThreadStop(vout
, NULL
);
1546 return VLC_EGENERIC
;
1549 /* We ignore ar changes at this point, they are dynamically supported.
1550 * #19268: don't ignore crop changes (fix vouts using the crop size of the
1551 * previous format). */
1552 vout
->p
->original
.i_sar_num
= original
.i_sar_num
;
1553 vout
->p
->original
.i_sar_den
= original
.i_sar_den
;
1554 if (video_format_IsSimilar(&original
, &vout
->p
->original
)) {
1555 if (cfg
->dpb_size
<= vout
->p
->dpb_size
) {
1556 video_format_Clean(&original
);
1559 msg_Warn(vout
, "DPB need to be increased");
1562 vout_display_state_t state
;
1563 memset(&state
, 0, sizeof(state
));
1565 ThreadStop(vout
, &state
);
1567 vout_ReinitInterlacingSupport(vout
);
1569 #if defined(_WIN32) || defined(__OS2__)
1570 if (!state
.cfg
.is_fullscreen
)
1573 state
.cfg
.display
.width
= 0;
1574 state
.cfg
.display
.height
= 0;
1579 /* FIXME current vout "variables" are not in sync here anymore
1580 * and I am not sure what to do */
1581 if (state
.cfg
.display
.sar
.num
<= 0 || state
.cfg
.display
.sar
.den
<= 0) {
1582 state
.cfg
.display
.sar
.num
= 1;
1583 state
.cfg
.display
.sar
.den
= 1;
1585 if (state
.cfg
.zoom
.num
<= 0 || state
.cfg
.zoom
.den
<= 0) {
1586 state
.cfg
.zoom
.num
= 1;
1587 state
.cfg
.zoom
.den
= 1;
1590 vout
->p
->original
= original
;
1591 vout
->p
->dpb_size
= cfg
->dpb_size
;
1592 if (ThreadStart(vout
, &state
)) {
1594 return VLC_EGENERIC
;
1599 static void ThreadCancel(vout_thread_t
*vout
, bool canceled
)
1601 picture_pool_Cancel(vout
->p
->decoder_pool
, canceled
);
1604 static int ThreadControl(vout_thread_t
*vout
, vout_control_cmd_t cmd
)
1607 case VOUT_CONTROL_INIT
:
1609 if (ThreadStart(vout
, NULL
))
1615 case VOUT_CONTROL_CLEAN
:
1616 ThreadStop(vout
, NULL
);
1619 case VOUT_CONTROL_REINIT
:
1620 if (ThreadReinit(vout
, cmd
.u
.cfg
))
1623 case VOUT_CONTROL_CANCEL
:
1624 ThreadCancel(vout
, cmd
.u
.boolean
);
1626 case VOUT_CONTROL_SUBPICTURE
:
1627 ThreadDisplaySubpicture(vout
, cmd
.u
.subpicture
);
1628 cmd
.u
.subpicture
= NULL
;
1630 case VOUT_CONTROL_FLUSH_SUBPICTURE
:
1631 ThreadFlushSubpicture(vout
, cmd
.u
.integer
);
1633 case VOUT_CONTROL_OSD_TITLE
:
1634 ThreadDisplayOsdTitle(vout
, cmd
.u
.string
);
1636 case VOUT_CONTROL_CHANGE_FILTERS
:
1637 ThreadChangeFilters(vout
, NULL
,
1638 cmd
.u
.string
!= NULL
?
1639 cmd
.u
.string
: vout
->p
->filter
.configuration
,
1642 case VOUT_CONTROL_CHANGE_INTERLACE
:
1643 ThreadChangeFilters(vout
, NULL
, vout
->p
->filter
.configuration
,
1644 cmd
.u
.boolean
? 1 : 0, false);
1646 case VOUT_CONTROL_CHANGE_SUB_SOURCES
:
1647 ThreadChangeSubSources(vout
, cmd
.u
.string
);
1649 case VOUT_CONTROL_CHANGE_SUB_FILTERS
:
1650 ThreadChangeSubFilters(vout
, cmd
.u
.string
);
1652 case VOUT_CONTROL_CHANGE_SUB_MARGIN
:
1653 ThreadChangeSubMargin(vout
, cmd
.u
.integer
);
1655 case VOUT_CONTROL_PAUSE
:
1656 ThreadChangePause(vout
, cmd
.u
.pause
.is_on
, cmd
.u
.pause
.date
);
1658 case VOUT_CONTROL_FLUSH
:
1659 ThreadFlush(vout
, false, cmd
.u
.time
);
1661 case VOUT_CONTROL_STEP
:
1662 ThreadStep(vout
, cmd
.u
.time_ptr
);
1664 case VOUT_CONTROL_FULLSCREEN
:
1665 ThreadChangeFullscreen(vout
, cmd
.u
.boolean
);
1667 case VOUT_CONTROL_WINDOW_STATE
:
1668 ThreadChangeWindowState(vout
, cmd
.u
.integer
);
1670 case VOUT_CONTROL_WINDOW_MOUSE
:
1671 ThreadChangeWindowMouse(vout
, &cmd
.u
.window_mouse
);
1673 case VOUT_CONTROL_DISPLAY_FILLED
:
1674 ThreadChangeDisplayFilled(vout
, cmd
.u
.boolean
);
1676 case VOUT_CONTROL_ZOOM
:
1677 ThreadChangeZoom(vout
, cmd
.u
.pair
.a
, cmd
.u
.pair
.b
);
1679 case VOUT_CONTROL_ASPECT_RATIO
:
1680 ThreadChangeAspectRatio(vout
, cmd
.u
.pair
.a
, cmd
.u
.pair
.b
);
1682 case VOUT_CONTROL_CROP_RATIO
:
1683 ThreadExecuteCropRatio(vout
, cmd
.u
.pair
.a
, cmd
.u
.pair
.b
);
1685 case VOUT_CONTROL_CROP_WINDOW
:
1686 ThreadExecuteCropWindow(vout
,
1687 cmd
.u
.window
.x
, cmd
.u
.window
.y
,
1688 cmd
.u
.window
.width
, cmd
.u
.window
.height
);
1690 case VOUT_CONTROL_CROP_BORDER
:
1691 ThreadExecuteCropBorder(vout
,
1692 cmd
.u
.border
.left
, cmd
.u
.border
.top
,
1693 cmd
.u
.border
.right
, cmd
.u
.border
.bottom
);
1695 case VOUT_CONTROL_VIEWPOINT
:
1696 ThreadExecuteViewpoint(vout
, &cmd
.u
.viewpoint
);
1701 vout_control_cmd_Clean(&cmd
);
1705 /*****************************************************************************
1706 * Thread: video output thread
1707 *****************************************************************************
1708 * Video output thread. This function does only returns when the thread is
1709 * terminated. It handles the pictures arriving in the video heap and the
1710 * display device events.
1711 *****************************************************************************/
1712 static void *Thread(void *object
)
1714 vout_thread_t
*vout
= object
;
1715 vout_thread_sys_t
*sys
= vout
->p
;
1717 mtime_t deadline
= VLC_TS_INVALID
;
1720 vout_control_cmd_t cmd
;
1724 const mtime_t max_deadline
= mdate() + 100000;
1725 deadline
= deadline
<= VLC_TS_INVALID
? max_deadline
: __MIN(deadline
, max_deadline
);
1727 deadline
= VLC_TS_INVALID
;
1729 while (!vout_control_Pop(&sys
->control
, &cmd
, deadline
))
1730 if (ThreadControl(vout
, cmd
))
1733 deadline
= VLC_TS_INVALID
;
1734 wait
= ThreadDisplayPicture(vout
, &deadline
) != VLC_SUCCESS
;
1736 const bool picture_interlaced
= sys
->displayed
.is_interlaced
;
1738 vout_SetInterlacingState(vout
, picture_interlaced
);
1739 vout_ManageWrapper(vout
);