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 picture_fifo_Push(vout
->p
->decoder_fifo
, picture
);
430 vout_control_Wake(&vout
->p
->control
);
434 int vout_GetSnapshot(vout_thread_t
*vout
,
435 block_t
**image_dst
, picture_t
**picture_dst
,
437 const char *type
, mtime_t timeout
)
439 picture_t
*picture
= vout_snapshot_Get(&vout
->p
->snapshot
, timeout
);
441 msg_Err(vout
, "Failed to grab a snapshot");
446 vlc_fourcc_t codec
= VLC_CODEC_PNG
;
447 if (type
&& image_Type2Fourcc(type
))
448 codec
= image_Type2Fourcc(type
);
450 const int override_width
= var_InheritInteger(vout
, "snapshot-width");
451 const int override_height
= var_InheritInteger(vout
, "snapshot-height");
453 if (picture_Export(VLC_OBJECT(vout
), image_dst
, fmt
,
454 picture
, codec
, override_width
, override_height
)) {
455 msg_Err(vout
, "Failed to convert image for snapshot");
456 picture_Release(picture
);
461 *picture_dst
= picture
;
463 picture_Release(picture
);
467 void vout_ChangeAspectRatio( vout_thread_t
*p_vout
,
468 unsigned int i_num
, unsigned int i_den
)
470 vout_ControlChangeSampleAspectRatio( p_vout
, i_num
, i_den
);
473 /* vout_Control* are usable by anyone at anytime */
474 void vout_ControlChangeFullscreen(vout_thread_t
*vout
, bool fullscreen
)
476 vout_control_PushBool(&vout
->p
->control
, VOUT_CONTROL_FULLSCREEN
,
479 void vout_ControlChangeWindowState(vout_thread_t
*vout
, unsigned st
)
481 vout_control_PushInteger(&vout
->p
->control
, VOUT_CONTROL_WINDOW_STATE
, st
);
483 void vout_ControlChangeDisplayFilled(vout_thread_t
*vout
, bool is_filled
)
485 vout_control_PushBool(&vout
->p
->control
, VOUT_CONTROL_DISPLAY_FILLED
,
488 void vout_ControlChangeZoom(vout_thread_t
*vout
, int num
, int den
)
490 vout_control_PushPair(&vout
->p
->control
, VOUT_CONTROL_ZOOM
,
493 void vout_ControlChangeSampleAspectRatio(vout_thread_t
*vout
,
494 unsigned num
, unsigned den
)
496 vout_control_PushPair(&vout
->p
->control
, VOUT_CONTROL_ASPECT_RATIO
,
499 void vout_ControlChangeCropRatio(vout_thread_t
*vout
,
500 unsigned num
, unsigned den
)
502 vout_control_PushPair(&vout
->p
->control
, VOUT_CONTROL_CROP_RATIO
,
505 void vout_ControlChangeCropWindow(vout_thread_t
*vout
,
506 int x
, int y
, int width
, int height
)
508 vout_control_cmd_t cmd
;
509 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_CROP_WINDOW
);
510 cmd
.u
.window
.x
= __MAX(x
, 0);
511 cmd
.u
.window
.y
= __MAX(y
, 0);
512 cmd
.u
.window
.width
= __MAX(width
, 0);
513 cmd
.u
.window
.height
= __MAX(height
, 0);
515 vout_control_Push(&vout
->p
->control
, &cmd
);
517 void vout_ControlChangeCropBorder(vout_thread_t
*vout
,
518 int left
, int top
, int right
, int bottom
)
520 vout_control_cmd_t cmd
;
521 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_CROP_BORDER
);
522 cmd
.u
.border
.left
= __MAX(left
, 0);
523 cmd
.u
.border
.top
= __MAX(top
, 0);
524 cmd
.u
.border
.right
= __MAX(right
, 0);
525 cmd
.u
.border
.bottom
= __MAX(bottom
, 0);
527 vout_control_Push(&vout
->p
->control
, &cmd
);
529 void vout_ControlChangeFilters(vout_thread_t
*vout
, const char *filters
)
531 vout_control_PushString(&vout
->p
->control
, VOUT_CONTROL_CHANGE_FILTERS
,
534 void vout_ControlChangeSubSources(vout_thread_t
*vout
, const char *filters
)
536 vout_control_PushString(&vout
->p
->control
, VOUT_CONTROL_CHANGE_SUB_SOURCES
,
539 void vout_ControlChangeSubFilters(vout_thread_t
*vout
, const char *filters
)
541 vout_control_PushString(&vout
->p
->control
, VOUT_CONTROL_CHANGE_SUB_FILTERS
,
544 void vout_ControlChangeSubMargin(vout_thread_t
*vout
, int margin
)
546 vout_control_PushInteger(&vout
->p
->control
, VOUT_CONTROL_CHANGE_SUB_MARGIN
,
550 void vout_ControlChangeViewpoint(vout_thread_t
*vout
,
551 const vlc_viewpoint_t
*p_viewpoint
)
553 vout_control_cmd_t cmd
;
554 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_VIEWPOINT
);
555 cmd
.u
.viewpoint
= *p_viewpoint
;
556 vout_control_Push(&vout
->p
->control
, &cmd
);
560 static void VoutGetDisplayCfg(vout_thread_t
*vout
, vout_display_cfg_t
*cfg
, const char *title
)
562 /* Load configuration */
563 #if defined(_WIN32) || defined(__OS2__)
564 cfg
->is_fullscreen
= var_GetBool(vout
, "fullscreen")
565 || var_GetBool(vout
, "video-wallpaper");
567 cfg
->viewpoint
= vout
->p
->original
.pose
;
569 cfg
->display
.title
= title
;
570 const int display_width
= var_GetInteger(vout
, "width");
571 const int display_height
= var_GetInteger(vout
, "height");
572 cfg
->display
.width
= display_width
> 0 ? display_width
: 0;
573 cfg
->display
.height
= display_height
> 0 ? display_height
: 0;
574 cfg
->is_display_filled
= var_GetBool(vout
, "autoscale");
575 unsigned msar_num
, msar_den
;
576 if (var_InheritURational(vout
, &msar_num
, &msar_den
, "monitor-par") ||
577 msar_num
<= 0 || msar_den
<= 0) {
581 cfg
->display
.sar
.num
= msar_num
;
582 cfg
->display
.sar
.den
= msar_den
;
583 unsigned zoom_den
= 1000;
584 unsigned zoom_num
= zoom_den
* var_GetFloat(vout
, "zoom");
585 vlc_ureduce(&zoom_num
, &zoom_den
, zoom_num
, zoom_den
, 0);
586 cfg
->zoom
.num
= zoom_num
;
587 cfg
->zoom
.den
= zoom_den
;
588 cfg
->align
.vertical
= VOUT_DISPLAY_ALIGN_CENTER
;
589 cfg
->align
.horizontal
= VOUT_DISPLAY_ALIGN_CENTER
;
590 const int align_mask
= var_GetInteger(vout
, "align");
591 if (align_mask
& 0x1)
592 cfg
->align
.horizontal
= VOUT_DISPLAY_ALIGN_LEFT
;
593 else if (align_mask
& 0x2)
594 cfg
->align
.horizontal
= VOUT_DISPLAY_ALIGN_RIGHT
;
595 if (align_mask
& 0x4)
596 cfg
->align
.vertical
= VOUT_DISPLAY_ALIGN_TOP
;
597 else if (align_mask
& 0x8)
598 cfg
->align
.vertical
= VOUT_DISPLAY_ALIGN_BOTTOM
;
601 vout_window_t
*vout_NewDisplayWindow(vout_thread_t
*vout
, unsigned type
)
603 vout_window_t
*window
= vout
->p
->window
;
605 assert(vout
->p
->splitter_name
== NULL
);
609 if (type
!= VOUT_WINDOW_TYPE_INVALID
&& type
!= window
->type
)
614 void vout_DeleteDisplayWindow(vout_thread_t
*vout
, vout_window_t
*window
)
616 if (window
== NULL
&& vout
->p
->window
!= NULL
) {
617 vout_display_window_Delete(vout
->p
->window
);
618 vout
->p
->window
= NULL
;
620 assert(vout
->p
->window
== window
);
623 void vout_SetDisplayWindowSize(vout_thread_t
*vout
,
624 unsigned width
, unsigned height
)
626 vout_window_t
*window
= vout
->p
->window
;
629 /* Request a resize of the window. If it fails, there is nothing to do.
630 * If it succeeds, the window will emit a resize event later. */
631 vout_window_SetSize(window
, width
, height
);
633 if (vout
->p
->display
.vd
!= NULL
)
634 /* Force a resize of window-less display. This is not allowed to fail,
635 * although the display is allowed to ignore the size anyway. */
636 /* FIXME: remove this, fix MSW and OS/2 window providers */
637 vout_display_SendEventDisplaySize(vout
->p
->display
.vd
, width
, height
);
640 int vout_HideWindowMouse(vout_thread_t
*vout
, bool hide
)
642 vout_window_t
*window
= vout
->p
->window
;
644 return window
!= NULL
? vout_window_HideMouse(window
, hide
) : VLC_EGENERIC
;
648 static int FilterRestartCallback(vlc_object_t
*p_this
, char const *psz_var
,
649 vlc_value_t oldval
, vlc_value_t newval
,
652 (void) p_this
; (void) psz_var
; (void) oldval
; (void) newval
;
653 vout_ControlChangeFilters((vout_thread_t
*)p_data
, NULL
);
657 static int ThreadDelFilterCallbacks(filter_t
*filter
, void *opaque
)
659 filter_DelProxyCallbacks((vlc_object_t
*)opaque
, filter
,
660 FilterRestartCallback
);
664 static void ThreadDelAllFilterCallbacks(vout_thread_t
*vout
)
666 assert(vout
->p
->filter
.chain_interactive
!= NULL
);
667 filter_chain_ForEach(vout
->p
->filter
.chain_interactive
,
668 ThreadDelFilterCallbacks
, vout
);
671 static picture_t
*VoutVideoFilterInteractiveNewPicture(filter_t
*filter
)
673 vout_thread_t
*vout
= filter
->owner
.sys
;
675 picture_t
*picture
= picture_pool_Get(vout
->p
->private_pool
);
677 picture_Reset(picture
);
678 VideoFormatCopyCropAr(&picture
->format
, &filter
->fmt_out
.video
);
683 static picture_t
*VoutVideoFilterStaticNewPicture(filter_t
*filter
)
685 vout_thread_t
*vout
= filter
->owner
.sys
;
687 vlc_assert_locked(&vout
->p
->filter
.lock
);
688 if (filter_chain_IsEmpty(vout
->p
->filter
.chain_interactive
))
689 return VoutVideoFilterInteractiveNewPicture(filter
);
691 return picture_NewFromFormat(&filter
->fmt_out
.video
);
694 static void ThreadFilterFlush(vout_thread_t
*vout
, bool is_locked
)
696 if (vout
->p
->displayed
.current
)
697 picture_Release( vout
->p
->displayed
.current
);
698 vout
->p
->displayed
.current
= NULL
;
700 if (vout
->p
->displayed
.next
)
701 picture_Release( vout
->p
->displayed
.next
);
702 vout
->p
->displayed
.next
= NULL
;
705 vlc_mutex_lock(&vout
->p
->filter
.lock
);
706 filter_chain_VideoFlush(vout
->p
->filter
.chain_static
);
707 filter_chain_VideoFlush(vout
->p
->filter
.chain_interactive
);
709 vlc_mutex_unlock(&vout
->p
->filter
.lock
);
717 static void ThreadChangeFilters(vout_thread_t
*vout
,
718 const video_format_t
*source
,
723 ThreadFilterFlush(vout
, is_locked
);
724 ThreadDelAllFilterCallbacks(vout
);
726 vlc_array_t array_static
;
727 vlc_array_t array_interactive
;
729 vlc_array_init(&array_static
);
730 vlc_array_init(&array_interactive
);
732 if ((vout
->p
->filter
.has_deint
=
733 deinterlace
== 1 || (deinterlace
== -1 && vout
->p
->filter
.has_deint
)))
735 vout_filter_t
*e
= malloc(sizeof(*e
));
739 free(config_ChainCreate(&e
->name
, &e
->cfg
, "deinterlace"));
740 vlc_array_append(&array_static
, e
);
744 char *current
= filters
? strdup(filters
) : NULL
;
748 char *next
= config_ChainCreate(&name
, &cfg
, current
);
751 vout_filter_t
*e
= malloc(sizeof(*e
));
756 if (!strcmp(e
->name
, "postproc"))
757 vlc_array_append(&array_static
, e
);
759 vlc_array_append(&array_interactive
, e
);
763 config_ChainDestroy(cfg
);
768 config_ChainDestroy(cfg
);
776 vlc_mutex_lock(&vout
->p
->filter
.lock
);
778 es_format_t fmt_target
;
779 es_format_InitFromVideo(&fmt_target
, source
? source
: &vout
->p
->filter
.format
);
781 const es_format_t
*p_fmt_current
= &fmt_target
;
783 for (int a
= 0; a
< 2; a
++) {
784 vlc_array_t
*array
= a
== 0 ? &array_static
:
786 filter_chain_t
*chain
= a
== 0 ? vout
->p
->filter
.chain_static
:
787 vout
->p
->filter
.chain_interactive
;
789 filter_chain_Reset(chain
, p_fmt_current
, p_fmt_current
);
790 for (size_t i
= 0; i
< vlc_array_count(array
); i
++) {
791 vout_filter_t
*e
= vlc_array_item_at_index(array
, i
);
792 msg_Dbg(vout
, "Adding '%s' as %s", e
->name
, a
== 0 ? "static" : "interactive");
793 filter_t
*filter
= filter_chain_AppendFilter(chain
, e
->name
, e
->cfg
,
797 msg_Err(vout
, "Failed to add filter '%s'", e
->name
);
798 config_ChainDestroy(e
->cfg
);
800 else if (a
== 1) /* Add callbacks for interactive filters */
801 filter_AddProxyCallbacks(vout
, filter
, FilterRestartCallback
);
806 p_fmt_current
= filter_chain_GetFmtOut(chain
);
807 vlc_array_clear(array
);
810 if (!es_format_IsSimilar(p_fmt_current
, &fmt_target
)) {
811 msg_Dbg(vout
, "Adding a filter to compensate for format changes");
812 if (filter_chain_AppendConverter(vout
->p
->filter
.chain_interactive
,
813 p_fmt_current
, &fmt_target
) != 0) {
814 msg_Err(vout
, "Failed to compensate for the format changes, removing all filters");
815 ThreadDelAllFilterCallbacks(vout
);
816 filter_chain_Reset(vout
->p
->filter
.chain_static
, &fmt_target
, &fmt_target
);
817 filter_chain_Reset(vout
->p
->filter
.chain_interactive
, &fmt_target
, &fmt_target
);
821 es_format_Clean(&fmt_target
);
823 if (vout
->p
->filter
.configuration
!= filters
) {
824 free(vout
->p
->filter
.configuration
);
825 vout
->p
->filter
.configuration
= filters
? strdup(filters
) : NULL
;
828 video_format_Clean(&vout
->p
->filter
.format
);
829 video_format_Copy(&vout
->p
->filter
.format
, source
);
833 vlc_mutex_unlock(&vout
->p
->filter
.lock
);
838 static int ThreadDisplayPreparePicture(vout_thread_t
*vout
, bool reuse
, bool frame_by_frame
)
840 bool is_late_dropped
= vout
->p
->is_late_dropped
&& !vout
->p
->pause
.is_on
&& !frame_by_frame
;
842 vlc_mutex_lock(&vout
->p
->filter
.lock
);
844 picture_t
*picture
= filter_chain_VideoFilter(vout
->p
->filter
.chain_static
, NULL
);
845 assert(!reuse
|| !picture
);
849 if (reuse
&& vout
->p
->displayed
.decoded
) {
850 decoded
= picture_Hold(vout
->p
->displayed
.decoded
);
852 decoded
= picture_fifo_Pop(vout
->p
->decoder_fifo
);
854 if (is_late_dropped
&& !decoded
->b_force
) {
855 const mtime_t predicted
= mdate() + 0; /* TODO improve */
856 const mtime_t late
= predicted
- decoded
->date
;
857 if (late
> VOUT_DISPLAY_LATE_THRESHOLD
) {
858 msg_Warn(vout
, "picture is too late to be displayed (missing %"PRId64
" ms)", late
/1000);
859 picture_Release(decoded
);
860 vout_statistic_AddLost(&vout
->p
->statistic
, 1);
862 } else if (late
> 0) {
863 msg_Dbg(vout
, "picture might be displayed late (missing %"PRId64
" ms)", late
/1000);
866 if (!VideoFormatIsCropArEqual(&decoded
->format
, &vout
->p
->filter
.format
))
867 ThreadChangeFilters(vout
, &decoded
->format
, vout
->p
->filter
.configuration
, -1, true);
875 if (vout
->p
->displayed
.decoded
)
876 picture_Release(vout
->p
->displayed
.decoded
);
878 vout
->p
->displayed
.decoded
= picture_Hold(decoded
);
879 vout
->p
->displayed
.timestamp
= decoded
->date
;
880 vout
->p
->displayed
.is_interlaced
= !decoded
->b_progressive
;
882 picture
= filter_chain_VideoFilter(vout
->p
->filter
.chain_static
, decoded
);
885 vlc_mutex_unlock(&vout
->p
->filter
.lock
);
890 assert(!vout
->p
->displayed
.next
);
891 if (!vout
->p
->displayed
.current
)
892 vout
->p
->displayed
.current
= picture
;
894 vout
->p
->displayed
.next
= picture
;
898 static int ThreadDisplayRenderPicture(vout_thread_t
*vout
, bool is_forced
)
900 vout_thread_sys_t
*sys
= vout
->p
;
901 vout_display_t
*vd
= vout
->p
->display
.vd
;
903 picture_t
*torender
= picture_Hold(vout
->p
->displayed
.current
);
905 vout_chrono_Start(&vout
->p
->render
);
907 vlc_mutex_lock(&vout
->p
->filter
.lock
);
908 picture_t
*filtered
= filter_chain_VideoFilter(vout
->p
->filter
.chain_interactive
, torender
);
909 vlc_mutex_unlock(&vout
->p
->filter
.lock
);
914 if (filtered
->date
!= vout
->p
->displayed
.current
->date
)
915 msg_Warn(vout
, "Unsupported timestamp modifications done by chain_interactive");
918 * Get the subpicture to be displayed
920 const bool do_snapshot
= vout_snapshot_IsRequested(&vout
->p
->snapshot
);
921 mtime_t render_subtitle_date
;
922 if (vout
->p
->pause
.is_on
)
923 render_subtitle_date
= vout
->p
->pause
.date
;
925 render_subtitle_date
= filtered
->date
> 1 ? filtered
->date
: mdate();
926 mtime_t render_osd_date
= mdate(); /* FIXME wrong */
929 * Get the subpicture to be displayed
931 const bool do_dr_spu
= !do_snapshot
&&
932 vd
->info
.subpicture_chromas
&&
933 *vd
->info
.subpicture_chromas
!= 0;
935 //FIXME: Denying do_early_spu if vd->source.orientation != ORIENT_NORMAL
936 //will have the effect that snapshots miss the subpictures. We do this
937 //because there is currently no way to transform subpictures to match
939 const bool do_early_spu
= !do_dr_spu
&&
940 vd
->source
.orientation
== ORIENT_NORMAL
&&
942 sys
->display
.use_dr
||
944 vd
->fmt
.i_width
* vd
->fmt
.i_height
<= vd
->source
.i_width
* vd
->source
.i_height
);
946 const vlc_fourcc_t
*subpicture_chromas
;
947 video_format_t fmt_spu
;
949 vout_display_place_t place
;
950 vout_display_PlacePicture(&place
, &vd
->source
, vd
->cfg
, false);
952 fmt_spu
= vd
->source
;
953 if (fmt_spu
.i_width
* fmt_spu
.i_height
< place
.width
* place
.height
) {
954 fmt_spu
.i_sar_num
= vd
->cfg
->display
.sar
.num
;
955 fmt_spu
.i_sar_den
= vd
->cfg
->display
.sar
.den
;
957 fmt_spu
.i_visible_width
= place
.width
;
959 fmt_spu
.i_visible_height
= place
.height
;
961 subpicture_chromas
= vd
->info
.subpicture_chromas
;
964 fmt_spu
= vd
->source
;
967 fmt_spu
.i_sar_num
= vd
->cfg
->display
.sar
.num
;
968 fmt_spu
.i_sar_den
= vd
->cfg
->display
.sar
.den
;
970 subpicture_chromas
= NULL
;
972 if (vout
->p
->spu_blend
&&
973 vout
->p
->spu_blend
->fmt_out
.video
.i_chroma
!= fmt_spu
.i_chroma
) {
974 filter_DeleteBlend(vout
->p
->spu_blend
);
975 vout
->p
->spu_blend
= NULL
;
976 vout
->p
->spu_blend_chroma
= 0;
978 if (!vout
->p
->spu_blend
&& vout
->p
->spu_blend_chroma
!= fmt_spu
.i_chroma
) {
979 vout
->p
->spu_blend_chroma
= fmt_spu
.i_chroma
;
980 vout
->p
->spu_blend
= filter_NewBlend(VLC_OBJECT(vout
), &fmt_spu
);
981 if (!vout
->p
->spu_blend
)
982 msg_Err(vout
, "Failed to create blending filter, OSD/Subtitles will not work");
986 video_format_t fmt_spu_rot
;
987 video_format_ApplyRotation(&fmt_spu_rot
, &fmt_spu
);
988 subpicture_t
*subpic
= spu_Render(vout
->p
->spu
,
989 subpicture_chromas
, &fmt_spu_rot
,
991 render_subtitle_date
, render_osd_date
,
997 * - be sure to end up with a direct buffer.
998 * - blend subtitles, and in a fast access buffer
1000 bool is_direct
= vout
->p
->decoder_pool
== vout
->p
->display_pool
;
1001 picture_t
*todisplay
= filtered
;
1002 if (do_early_spu
&& subpic
) {
1003 if (vout
->p
->spu_blend
) {
1004 picture_t
*blent
= picture_pool_Get(vout
->p
->private_pool
);
1006 VideoFormatCopyCropAr(&blent
->format
, &filtered
->format
);
1007 picture_Copy(blent
, filtered
);
1008 if (picture_BlendSubpicture(blent
, vout
->p
->spu_blend
, subpic
)) {
1009 picture_Release(todisplay
);
1012 picture_Release(blent
);
1015 subpicture_Delete(subpic
);
1019 assert(vout_IsDisplayFiltered(vd
) == !sys
->display
.use_dr
);
1020 if (sys
->display
.use_dr
&& !is_direct
) {
1021 picture_t
*direct
= NULL
;
1022 if (likely(vout
->p
->display_pool
!= NULL
))
1023 direct
= picture_pool_Get(vout
->p
->display_pool
);
1025 picture_Release(todisplay
);
1027 subpicture_Delete(subpic
);
1028 return VLC_EGENERIC
;
1031 /* The display uses direct rendering (no conversion), but its pool of
1032 * pictures is not usable by the decoder (too few, too slow or
1033 * subject to invalidation...). Since there are no filters, copying
1034 * pictures from the decoder to the output is unavoidable. */
1035 VideoFormatCopyCropAr(&direct
->format
, &todisplay
->format
);
1036 picture_Copy(direct
, todisplay
);
1037 picture_Release(todisplay
);
1042 * Take a snapshot if requested
1045 vout_snapshot_Set(&vout
->p
->snapshot
, &vd
->source
, todisplay
);
1047 /* Render the direct buffer */
1048 vout_UpdateDisplaySourceProperties(vd
, &todisplay
->format
);
1050 todisplay
= vout_FilterDisplay(vd
, todisplay
);
1051 if (todisplay
== NULL
) {
1053 subpicture_Delete(subpic
);
1054 return VLC_EGENERIC
;
1057 if (sys
->display
.use_dr
) {
1058 vout_display_Prepare(vd
, todisplay
, subpic
);
1060 if (!do_dr_spu
&& !do_early_spu
&& vout
->p
->spu_blend
&& subpic
)
1061 picture_BlendSubpicture(todisplay
, vout
->p
->spu_blend
, subpic
);
1062 vout_display_Prepare(vd
, todisplay
, do_dr_spu
? subpic
: NULL
);
1064 if (!do_dr_spu
&& subpic
)
1066 subpicture_Delete(subpic
);
1071 vout_chrono_Stop(&vout
->p
->render
);
1075 if (((i
++)%10) == 0)
1076 msg_Info(vout
, "render: avg %d ms var %d ms",
1077 (int)(vout
->p
->render
.avg
/1000), (int)(vout
->p
->render
.var
/1000));
1081 /* Wait the real date (for rendering jitter) */
1083 mtime_t delay
= direct
->date
- mdate();
1085 msg_Warn(vout
, "picture is late (%lld ms)", delay
/ 1000);
1088 mwait(todisplay
->date
);
1090 /* Display the direct buffer returned by vout_RenderPicture */
1091 vout
->p
->displayed
.date
= mdate();
1092 vout_display_Display(vd
, todisplay
, subpic
);
1094 vout_statistic_AddDisplayed(&vout
->p
->statistic
, 1);
1099 static int ThreadDisplayPicture(vout_thread_t
*vout
, mtime_t
*deadline
)
1101 bool frame_by_frame
= !deadline
;
1102 bool paused
= vout
->p
->pause
.is_on
;
1103 bool first
= !vout
->p
->displayed
.current
;
1106 if (ThreadDisplayPreparePicture(vout
, true, frame_by_frame
)) /* FIXME not sure it is ok */
1107 return VLC_EGENERIC
;
1109 if (!paused
|| frame_by_frame
)
1110 while (!vout
->p
->displayed
.next
&& !ThreadDisplayPreparePicture(vout
, false, frame_by_frame
))
1113 const mtime_t date
= mdate();
1114 const mtime_t render_delay
= vout_chrono_GetHigh(&vout
->p
->render
) + VOUT_MWAIT_TOLERANCE
;
1116 bool drop_next_frame
= frame_by_frame
;
1117 mtime_t date_next
= VLC_TS_INVALID
;
1118 if (!paused
&& vout
->p
->displayed
.next
) {
1119 date_next
= vout
->p
->displayed
.next
->date
- render_delay
;
1120 if (date_next
/* + 0 FIXME */ <= date
)
1121 drop_next_frame
= true;
1124 /* FIXME/XXX we must redisplay the last decoded picture (because
1125 * of potential vout updated, or filters update or SPU update)
1126 * For now a high update period is needed but it could be removed
1128 * - vout module emits events from theselves.
1129 * - *and* SPU is modified to emit an event or a deadline when needed.
1131 * So it will be done later.
1133 bool refresh
= false;
1135 mtime_t date_refresh
= VLC_TS_INVALID
;
1136 if (vout
->p
->displayed
.date
> VLC_TS_INVALID
) {
1137 date_refresh
= vout
->p
->displayed
.date
+ VOUT_REDISPLAY_DELAY
- render_delay
;
1138 refresh
= date_refresh
<= date
;
1140 bool force_refresh
= !drop_next_frame
&& refresh
;
1142 if (!first
&& !refresh
&& !drop_next_frame
) {
1143 if (!frame_by_frame
) {
1144 if (date_refresh
!= VLC_TS_INVALID
)
1145 *deadline
= date_refresh
;
1146 if (date_next
!= VLC_TS_INVALID
&& date_next
< *deadline
)
1147 *deadline
= date_next
;
1149 return VLC_EGENERIC
;
1152 if (drop_next_frame
) {
1153 picture_Release(vout
->p
->displayed
.current
);
1154 vout
->p
->displayed
.current
= vout
->p
->displayed
.next
;
1155 vout
->p
->displayed
.next
= NULL
;
1158 if (!vout
->p
->displayed
.current
)
1159 return VLC_EGENERIC
;
1161 /* display the picture immediately */
1162 bool is_forced
= frame_by_frame
|| force_refresh
|| vout
->p
->displayed
.current
->b_force
;
1163 int ret
= ThreadDisplayRenderPicture(vout
, is_forced
);
1164 return force_refresh
? VLC_EGENERIC
: ret
;
1167 static void ThreadDisplaySubpicture(vout_thread_t
*vout
,
1168 subpicture_t
*subpicture
)
1170 spu_PutSubpicture(vout
->p
->spu
, subpicture
);
1173 static void ThreadFlushSubpicture(vout_thread_t
*vout
, int channel
)
1175 spu_ClearChannel(vout
->p
->spu
, channel
);
1178 static void ThreadDisplayOsdTitle(vout_thread_t
*vout
, const char *string
)
1180 if (!vout
->p
->title
.show
)
1183 vout_OSDText(vout
, VOUT_SPU_CHANNEL_OSD
,
1184 vout
->p
->title
.position
, INT64_C(1000) * vout
->p
->title
.timeout
,
1188 static void ThreadChangeSubSources(vout_thread_t
*vout
, const char *filters
)
1190 spu_ChangeSources(vout
->p
->spu
, filters
);
1193 static void ThreadChangeSubFilters(vout_thread_t
*vout
, const char *filters
)
1195 spu_ChangeFilters(vout
->p
->spu
, filters
);
1198 static void ThreadChangeSubMargin(vout_thread_t
*vout
, int margin
)
1200 spu_ChangeMargin(vout
->p
->spu
, margin
);
1203 static void ThreadChangePause(vout_thread_t
*vout
, bool is_paused
, mtime_t date
)
1205 assert(!vout
->p
->pause
.is_on
|| !is_paused
);
1207 if (vout
->p
->pause
.is_on
) {
1208 const mtime_t duration
= date
- vout
->p
->pause
.date
;
1210 if (vout
->p
->step
.timestamp
> VLC_TS_INVALID
)
1211 vout
->p
->step
.timestamp
+= duration
;
1212 if (vout
->p
->step
.last
> VLC_TS_INVALID
)
1213 vout
->p
->step
.last
+= duration
;
1214 picture_fifo_OffsetDate(vout
->p
->decoder_fifo
, duration
);
1215 if (vout
->p
->displayed
.decoded
)
1216 vout
->p
->displayed
.decoded
->date
+= duration
;
1217 spu_OffsetSubtitleDate(vout
->p
->spu
, duration
);
1219 ThreadFilterFlush(vout
, false);
1221 vout
->p
->step
.timestamp
= VLC_TS_INVALID
;
1222 vout
->p
->step
.last
= VLC_TS_INVALID
;
1224 vout
->p
->pause
.is_on
= is_paused
;
1225 vout
->p
->pause
.date
= date
;
1227 vout_window_t
*window
= vout
->p
->window
;
1229 vout_window_SetInhibition(window
, !is_paused
);
1232 static void ThreadFlush(vout_thread_t
*vout
, bool below
, mtime_t date
)
1234 vout
->p
->step
.timestamp
= VLC_TS_INVALID
;
1235 vout
->p
->step
.last
= VLC_TS_INVALID
;
1237 ThreadFilterFlush(vout
, false); /* FIXME too much */
1239 picture_t
*last
= vout
->p
->displayed
.decoded
;
1241 if (( below
&& last
->date
<= date
) ||
1242 (!below
&& last
->date
>= date
)) {
1243 picture_Release(last
);
1245 vout
->p
->displayed
.decoded
= NULL
;
1246 vout
->p
->displayed
.date
= VLC_TS_INVALID
;
1247 vout
->p
->displayed
.timestamp
= VLC_TS_INVALID
;
1251 picture_fifo_Flush(vout
->p
->decoder_fifo
, date
, below
);
1254 static void ThreadStep(vout_thread_t
*vout
, mtime_t
*duration
)
1258 if (vout
->p
->step
.last
<= VLC_TS_INVALID
)
1259 vout
->p
->step
.last
= vout
->p
->displayed
.timestamp
;
1261 if (ThreadDisplayPicture(vout
, NULL
))
1264 vout
->p
->step
.timestamp
= vout
->p
->displayed
.timestamp
;
1266 if (vout
->p
->step
.last
> VLC_TS_INVALID
&&
1267 vout
->p
->step
.timestamp
> vout
->p
->step
.last
) {
1268 *duration
= vout
->p
->step
.timestamp
- vout
->p
->step
.last
;
1269 vout
->p
->step
.last
= vout
->p
->step
.timestamp
;
1270 /* TODO advance subpicture by the duration ... */
1274 static void ThreadChangeFullscreen(vout_thread_t
*vout
, bool fullscreen
)
1276 vout_window_t
*window
= vout
->p
->window
;
1278 #if !defined(_WIN32) && !defined(__OS2__)
1280 vout_window_SetFullScreen(window
, fullscreen
);
1282 bool window_fullscreen
= false;
1284 && vout_window_SetFullScreen(window
, fullscreen
) == VLC_SUCCESS
)
1285 window_fullscreen
= true;
1286 /* FIXME: remove this event */
1287 if (vout
->p
->display
.vd
!= NULL
)
1288 vout_display_SendEventFullscreen(vout
->p
->display
.vd
, fullscreen
, window_fullscreen
);
1292 static void ThreadChangeWindowState(vout_thread_t
*vout
, unsigned state
)
1294 vout_window_t
*window
= vout
->p
->window
;
1297 vout_window_SetState(window
, state
);
1298 #if defined(_WIN32) || defined(__OS2__)
1299 else /* FIXME: remove this event */
1300 if (vout
->p
->display
.vd
!= NULL
)
1301 vout_display_SendWindowState(vout
->p
->display
.vd
, state
);
1305 static void ThreadChangeWindowMouse(vout_thread_t
*vout
,
1306 const vout_window_mouse_event_t
*mouse
)
1308 vout_display_t
*vd
= vout
->p
->display
.vd
;
1309 switch (mouse
->type
)
1311 case VOUT_WINDOW_MOUSE_STATE
:
1312 case VOUT_WINDOW_MOUSE_MOVED
:
1314 vout_display_place_t place
;
1315 vout_display_PlacePicture(&place
, &vd
->source
, vd
->cfg
, false);
1317 if (place
.width
<= 0 || place
.height
<= 0)
1320 const int x
= vd
->source
.i_x_offset
+
1321 (int64_t)(mouse
->x
- place
.x
) *
1322 vd
->source
.i_visible_width
/ place
.width
;
1323 const int y
= vd
->source
.i_y_offset
+
1324 (int64_t)(mouse
->y
- place
.y
) *
1325 vd
->source
.i_visible_height
/ place
.height
;
1327 if (mouse
->type
== VOUT_WINDOW_MOUSE_STATE
)
1328 vout_display_SendEventMouseState(vd
, x
, y
, mouse
->button_mask
);
1330 vout_display_SendEventMouseMoved(vd
, x
, y
);
1333 case VOUT_WINDOW_MOUSE_PRESSED
:
1334 vout_display_SendEventMousePressed(vd
, mouse
->button_mask
);
1336 case VOUT_WINDOW_MOUSE_RELEASED
:
1337 vout_display_SendEventMouseReleased(vd
, mouse
->button_mask
);
1339 case VOUT_WINDOW_MOUSE_DOUBLE_CLICK
:
1340 if (mouse
->button_mask
== 0)
1341 vout_display_SendEventMouseDoubleClick(vd
);
1343 default: vlc_assert_unreachable();
1348 static void ThreadChangeDisplayFilled(vout_thread_t
*vout
, bool is_filled
)
1350 vout_SetDisplayFilled(vout
->p
->display
.vd
, is_filled
);
1353 static void ThreadChangeZoom(vout_thread_t
*vout
, int num
, int den
)
1355 if (num
* 10 < den
) {
1358 } else if (num
> den
* 10) {
1362 vout_SetDisplayZoom(vout
->p
->display
.vd
, num
, den
);
1365 static void ThreadChangeAspectRatio(vout_thread_t
*vout
,
1366 unsigned num
, unsigned den
)
1368 vout_SetDisplayAspect(vout
->p
->display
.vd
, num
, den
);
1372 static void ThreadExecuteCropWindow(vout_thread_t
*vout
,
1373 unsigned x
, unsigned y
,
1374 unsigned width
, unsigned height
)
1376 vout_SetDisplayCrop(vout
->p
->display
.vd
, 0, 0,
1377 x
, y
, width
, height
);
1379 static void ThreadExecuteCropBorder(vout_thread_t
*vout
,
1380 unsigned left
, unsigned top
,
1381 unsigned right
, unsigned bottom
)
1383 msg_Err(vout
, "ThreadExecuteCropBorder %d.%d %dx%d", left
, top
, right
, bottom
);
1384 vout_SetDisplayCrop(vout
->p
->display
.vd
, 0, 0,
1385 left
, top
, -(int)right
, -(int)bottom
);
1388 static void ThreadExecuteCropRatio(vout_thread_t
*vout
,
1389 unsigned num
, unsigned den
)
1391 vout_SetDisplayCrop(vout
->p
->display
.vd
, num
, den
,
1395 static void ThreadExecuteViewpoint(vout_thread_t
*vout
,
1396 const vlc_viewpoint_t
*p_viewpoint
)
1398 vout_SetDisplayViewpoint(vout
->p
->display
.vd
, p_viewpoint
);
1401 static int ThreadStart(vout_thread_t
*vout
, vout_display_state_t
*state
)
1403 vlc_mouse_Init(&vout
->p
->mouse
);
1404 vout
->p
->decoder_fifo
= picture_fifo_New();
1405 vout
->p
->decoder_pool
= NULL
;
1406 vout
->p
->display_pool
= NULL
;
1407 vout
->p
->private_pool
= NULL
;
1409 vout
->p
->filter
.configuration
= NULL
;
1410 video_format_Copy(&vout
->p
->filter
.format
, &vout
->p
->original
);
1412 filter_owner_t owner
= {
1415 .buffer_new
= VoutVideoFilterStaticNewPicture
,
1418 vout
->p
->filter
.chain_static
=
1419 filter_chain_NewVideo( vout
, true, &owner
);
1421 owner
.video
.buffer_new
= VoutVideoFilterInteractiveNewPicture
;
1422 vout
->p
->filter
.chain_interactive
=
1423 filter_chain_NewVideo( vout
, true, &owner
);
1425 vout_display_state_t state_default
;
1427 VoutGetDisplayCfg(vout
, &state_default
.cfg
, vout
->p
->display
.title
);
1429 #if defined(_WIN32) || defined(__OS2__)
1430 bool below
= var_InheritBool(vout
, "video-wallpaper");
1431 bool above
= var_InheritBool(vout
, "video-on-top");
1433 state_default
.wm_state
= below
? VOUT_WINDOW_STATE_BELOW
1434 : above
? VOUT_WINDOW_STATE_ABOVE
1435 : VOUT_WINDOW_STATE_NORMAL
;
1437 state_default
.sar
.num
= 0;
1438 state_default
.sar
.den
= 0;
1440 state
= &state_default
;
1443 if (vout_OpenWrapper(vout
, vout
->p
->splitter_name
, state
))
1445 if (vout_InitWrapper(vout
))
1447 vout_CloseWrapper(vout
, state
);
1450 assert(vout
->p
->decoder_pool
&& vout
->p
->private_pool
);
1452 vout
->p
->displayed
.current
= NULL
;
1453 vout
->p
->displayed
.next
= NULL
;
1454 vout
->p
->displayed
.decoded
= NULL
;
1455 vout
->p
->displayed
.date
= VLC_TS_INVALID
;
1456 vout
->p
->displayed
.timestamp
= VLC_TS_INVALID
;
1457 vout
->p
->displayed
.is_interlaced
= false;
1459 vout
->p
->step
.last
= VLC_TS_INVALID
;
1460 vout
->p
->step
.timestamp
= VLC_TS_INVALID
;
1462 vout
->p
->spu_blend_chroma
= 0;
1463 vout
->p
->spu_blend
= NULL
;
1465 video_format_Print(VLC_OBJECT(vout
), "original format", &vout
->p
->original
);
1468 if (vout
->p
->filter
.chain_interactive
!= NULL
)
1470 ThreadDelAllFilterCallbacks(vout
);
1471 filter_chain_Delete(vout
->p
->filter
.chain_interactive
);
1473 if (vout
->p
->filter
.chain_static
!= NULL
)
1474 filter_chain_Delete(vout
->p
->filter
.chain_static
);
1475 video_format_Clean(&vout
->p
->filter
.format
);
1476 if (vout
->p
->decoder_fifo
!= NULL
)
1477 picture_fifo_Delete(vout
->p
->decoder_fifo
);
1478 return VLC_EGENERIC
;
1481 static void ThreadStop(vout_thread_t
*vout
, vout_display_state_t
*state
)
1483 if (vout
->p
->spu_blend
)
1484 filter_DeleteBlend(vout
->p
->spu_blend
);
1486 /* Destroy translation tables */
1487 if (vout
->p
->display
.vd
) {
1488 if (vout
->p
->decoder_pool
) {
1489 ThreadFlush(vout
, true, INT64_MAX
);
1490 vout_EndWrapper(vout
);
1492 vout_CloseWrapper(vout
, state
);
1495 /* Destroy the video filters */
1496 ThreadDelAllFilterCallbacks(vout
);
1497 filter_chain_Delete(vout
->p
->filter
.chain_interactive
);
1498 filter_chain_Delete(vout
->p
->filter
.chain_static
);
1499 video_format_Clean(&vout
->p
->filter
.format
);
1500 free(vout
->p
->filter
.configuration
);
1502 if (vout
->p
->decoder_fifo
)
1503 picture_fifo_Delete(vout
->p
->decoder_fifo
);
1504 assert(!vout
->p
->decoder_pool
);
1507 static void ThreadInit(vout_thread_t
*vout
)
1509 vout
->p
->dead
= false;
1510 vout
->p
->is_late_dropped
= var_InheritBool(vout
, "drop-late-frames");
1511 vout
->p
->pause
.is_on
= false;
1512 vout
->p
->pause
.date
= VLC_TS_INVALID
;
1514 vout_chrono_Init(&vout
->p
->render
, 5, 10000); /* Arbitrary initial time */
1517 static void ThreadClean(vout_thread_t
*vout
)
1519 vout_chrono_Clean(&vout
->p
->render
);
1520 vout
->p
->dead
= true;
1521 vout_control_Dead(&vout
->p
->control
);
1524 static int ThreadReinit(vout_thread_t
*vout
,
1525 const vout_configuration_t
*cfg
)
1527 video_format_t original
;
1529 vout
->p
->pause
.is_on
= false;
1530 vout
->p
->pause
.date
= VLC_TS_INVALID
;
1532 if (VoutValidateFormat(&original
, cfg
->fmt
)) {
1533 ThreadStop(vout
, NULL
);
1535 return VLC_EGENERIC
;
1537 /* We ignore crop/ar changes at this point, they are dynamically supported */
1538 VideoFormatCopyCropAr(&vout
->p
->original
, &original
);
1539 if (video_format_IsSimilar(&original
, &vout
->p
->original
)) {
1540 if (cfg
->dpb_size
<= vout
->p
->dpb_size
) {
1541 video_format_Clean(&original
);
1544 msg_Warn(vout
, "DPB need to be increased");
1547 vout_display_state_t state
;
1548 memset(&state
, 0, sizeof(state
));
1550 ThreadStop(vout
, &state
);
1552 vout_ReinitInterlacingSupport(vout
);
1554 #if defined(_WIN32) || defined(__OS2__)
1555 if (!state
.cfg
.is_fullscreen
)
1558 state
.cfg
.display
.width
= 0;
1559 state
.cfg
.display
.height
= 0;
1564 /* FIXME current vout "variables" are not in sync here anymore
1565 * and I am not sure what to do */
1566 if (state
.cfg
.display
.sar
.num
<= 0 || state
.cfg
.display
.sar
.den
<= 0) {
1567 state
.cfg
.display
.sar
.num
= 1;
1568 state
.cfg
.display
.sar
.den
= 1;
1570 if (state
.cfg
.zoom
.num
<= 0 || state
.cfg
.zoom
.den
<= 0) {
1571 state
.cfg
.zoom
.num
= 1;
1572 state
.cfg
.zoom
.den
= 1;
1575 vout
->p
->original
= original
;
1576 vout
->p
->dpb_size
= cfg
->dpb_size
;
1577 if (ThreadStart(vout
, &state
)) {
1579 return VLC_EGENERIC
;
1584 static void ThreadCancel(vout_thread_t
*vout
, bool canceled
)
1586 picture_pool_Cancel(vout
->p
->decoder_pool
, canceled
);
1589 static int ThreadControl(vout_thread_t
*vout
, vout_control_cmd_t cmd
)
1592 case VOUT_CONTROL_INIT
:
1594 if (ThreadStart(vout
, NULL
))
1600 case VOUT_CONTROL_CLEAN
:
1601 ThreadStop(vout
, NULL
);
1604 case VOUT_CONTROL_REINIT
:
1605 if (ThreadReinit(vout
, cmd
.u
.cfg
))
1608 case VOUT_CONTROL_CANCEL
:
1609 ThreadCancel(vout
, cmd
.u
.boolean
);
1611 case VOUT_CONTROL_SUBPICTURE
:
1612 ThreadDisplaySubpicture(vout
, cmd
.u
.subpicture
);
1613 cmd
.u
.subpicture
= NULL
;
1615 case VOUT_CONTROL_FLUSH_SUBPICTURE
:
1616 ThreadFlushSubpicture(vout
, cmd
.u
.integer
);
1618 case VOUT_CONTROL_OSD_TITLE
:
1619 ThreadDisplayOsdTitle(vout
, cmd
.u
.string
);
1621 case VOUT_CONTROL_CHANGE_FILTERS
:
1622 ThreadChangeFilters(vout
, NULL
,
1623 cmd
.u
.string
!= NULL
?
1624 cmd
.u
.string
: vout
->p
->filter
.configuration
,
1627 case VOUT_CONTROL_CHANGE_INTERLACE
:
1628 ThreadChangeFilters(vout
, NULL
, vout
->p
->filter
.configuration
,
1629 cmd
.u
.boolean
? 1 : 0, false);
1631 case VOUT_CONTROL_CHANGE_SUB_SOURCES
:
1632 ThreadChangeSubSources(vout
, cmd
.u
.string
);
1634 case VOUT_CONTROL_CHANGE_SUB_FILTERS
:
1635 ThreadChangeSubFilters(vout
, cmd
.u
.string
);
1637 case VOUT_CONTROL_CHANGE_SUB_MARGIN
:
1638 ThreadChangeSubMargin(vout
, cmd
.u
.integer
);
1640 case VOUT_CONTROL_PAUSE
:
1641 ThreadChangePause(vout
, cmd
.u
.pause
.is_on
, cmd
.u
.pause
.date
);
1643 case VOUT_CONTROL_FLUSH
:
1644 ThreadFlush(vout
, false, cmd
.u
.time
);
1646 case VOUT_CONTROL_STEP
:
1647 ThreadStep(vout
, cmd
.u
.time_ptr
);
1649 case VOUT_CONTROL_FULLSCREEN
:
1650 ThreadChangeFullscreen(vout
, cmd
.u
.boolean
);
1652 case VOUT_CONTROL_WINDOW_STATE
:
1653 ThreadChangeWindowState(vout
, cmd
.u
.integer
);
1655 case VOUT_CONTROL_WINDOW_MOUSE
:
1656 ThreadChangeWindowMouse(vout
, &cmd
.u
.window_mouse
);
1658 case VOUT_CONTROL_DISPLAY_FILLED
:
1659 ThreadChangeDisplayFilled(vout
, cmd
.u
.boolean
);
1661 case VOUT_CONTROL_ZOOM
:
1662 ThreadChangeZoom(vout
, cmd
.u
.pair
.a
, cmd
.u
.pair
.b
);
1664 case VOUT_CONTROL_ASPECT_RATIO
:
1665 ThreadChangeAspectRatio(vout
, cmd
.u
.pair
.a
, cmd
.u
.pair
.b
);
1667 case VOUT_CONTROL_CROP_RATIO
:
1668 ThreadExecuteCropRatio(vout
, cmd
.u
.pair
.a
, cmd
.u
.pair
.b
);
1670 case VOUT_CONTROL_CROP_WINDOW
:
1671 ThreadExecuteCropWindow(vout
,
1672 cmd
.u
.window
.x
, cmd
.u
.window
.y
,
1673 cmd
.u
.window
.width
, cmd
.u
.window
.height
);
1675 case VOUT_CONTROL_CROP_BORDER
:
1676 ThreadExecuteCropBorder(vout
,
1677 cmd
.u
.border
.left
, cmd
.u
.border
.top
,
1678 cmd
.u
.border
.right
, cmd
.u
.border
.bottom
);
1680 case VOUT_CONTROL_VIEWPOINT
:
1681 ThreadExecuteViewpoint(vout
, &cmd
.u
.viewpoint
);
1686 vout_control_cmd_Clean(&cmd
);
1690 /*****************************************************************************
1691 * Thread: video output thread
1692 *****************************************************************************
1693 * Video output thread. This function does only returns when the thread is
1694 * terminated. It handles the pictures arriving in the video heap and the
1695 * display device events.
1696 *****************************************************************************/
1697 static void *Thread(void *object
)
1699 vout_thread_t
*vout
= object
;
1700 vout_thread_sys_t
*sys
= vout
->p
;
1702 mtime_t deadline
= VLC_TS_INVALID
;
1705 vout_control_cmd_t cmd
;
1709 const mtime_t max_deadline
= mdate() + 100000;
1710 deadline
= deadline
<= VLC_TS_INVALID
? max_deadline
: __MIN(deadline
, max_deadline
);
1712 deadline
= VLC_TS_INVALID
;
1714 while (!vout_control_Pop(&sys
->control
, &cmd
, deadline
))
1715 if (ThreadControl(vout
, cmd
))
1718 deadline
= VLC_TS_INVALID
;
1719 wait
= ThreadDisplayPicture(vout
, &deadline
) != VLC_SUCCESS
;
1721 const bool picture_interlaced
= sys
->displayed
.is_interlaced
;
1723 vout_SetInterlacingState(vout
, picture_interlaced
);
1724 vout_ManageWrapper(vout
);