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 the VideoLAN team
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
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 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 General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, 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>
46 #include <vlc_vout_osd.h>
47 #include <vlc_image.h>
50 #include "vout_internal.h"
51 #include "interlacing.h"
52 #include "postprocessing.h"
55 /*****************************************************************************
57 *****************************************************************************/
58 static void *Thread(void *);
59 static void VoutDestructor(vlc_object_t
*);
61 /* Maximum delay between 2 displayed pictures.
62 * XXX it is needed for now but should be removed in the long term.
64 #define VOUT_REDISPLAY_DELAY (INT64_C(80000))
67 * Late pictures having a delay higher than this value are thrashed.
69 #define VOUT_DISPLAY_LATE_THRESHOLD (INT64_C(20000))
71 /* Better be in advance when awakening than late... */
72 #define VOUT_MWAIT_TOLERANCE (INT64_C(4000))
75 static int VoutValidateFormat(video_format_t
*dst
,
76 const video_format_t
*src
)
78 if (src
->i_width
<= 0 || src
->i_height
<= 0)
80 if (src
->i_sar_num
<= 0 || src
->i_sar_den
<= 0)
84 video_format_Copy(dst
, src
);
85 dst
->i_chroma
= vlc_fourcc_GetCodec(VIDEO_ES
, src
->i_chroma
);
86 vlc_ureduce( &dst
->i_sar_num
, &dst
->i_sar_den
,
87 src
->i_sar_num
, src
->i_sar_den
, 50000 );
88 if (dst
->i_sar_num
<= 0 || dst
->i_sar_den
<= 0) {
92 video_format_FixRgb(dst
);
95 static void VideoFormatCopyCropAr(video_format_t
*dst
,
96 const video_format_t
*src
)
98 video_format_CopyCrop(dst
, src
);
99 dst
->i_sar_num
= src
->i_sar_num
;
100 dst
->i_sar_den
= src
->i_sar_den
;
102 static bool VideoFormatIsCropArEqual(video_format_t
*dst
,
103 const video_format_t
*src
)
105 return dst
->i_sar_num
* src
->i_sar_den
== dst
->i_sar_den
* src
->i_sar_num
&&
106 dst
->i_x_offset
== src
->i_x_offset
&&
107 dst
->i_y_offset
== src
->i_y_offset
&&
108 dst
->i_visible_width
== src
->i_visible_width
&&
109 dst
->i_visible_height
== src
->i_visible_height
;
112 static vout_thread_t
*VoutCreate(vlc_object_t
*object
,
113 const vout_configuration_t
*cfg
)
115 video_format_t original
;
116 if (VoutValidateFormat(&original
, cfg
->fmt
))
119 /* Allocate descriptor */
120 vout_thread_t
*vout
= vlc_custom_create(object
,
121 sizeof(*vout
) + sizeof(*vout
->p
),
122 VLC_OBJECT_VOUT
, "video output");
124 video_format_Clean(&original
);
129 vout
->p
= (vout_thread_sys_t
*)&vout
[1];
131 vout
->p
->original
= original
;
132 vout
->p
->dpb_size
= cfg
->dpb_size
;
134 vout_control_Init(&vout
->p
->control
);
135 vout_control_PushVoid(&vout
->p
->control
, VOUT_CONTROL_INIT
);
137 vout_statistic_Init(&vout
->p
->statistic
);
139 vout_snapshot_Init(&vout
->p
->snapshot
);
141 /* Initialize locks */
142 vlc_mutex_init(&vout
->p
->picture_lock
);
143 vlc_mutex_init(&vout
->p
->filter
.lock
);
144 vlc_mutex_init(&vout
->p
->spu_lock
);
146 /* Attach the new object now so we can use var inheritance below */
147 vlc_object_attach(vout
, object
);
149 /* Initialize subpicture unit */
150 vout
->p
->spu
= spu_Create(vout
);
152 /* Take care of some "interface/control" related initialisations */
155 vout
->p
->title
.show
= var_GetBool(vout
, "video-title-show");
156 vout
->p
->title
.timeout
= var_GetInteger(vout
, "video-title-timeout");
157 vout
->p
->title
.position
= var_GetInteger(vout
, "video-title-position");
159 /* Get splitter name if present */
160 char *splitter_name
= var_InheritString(vout
, "video-splitter");
161 if (splitter_name
&& *splitter_name
) {
162 vout
->p
->splitter_name
= splitter_name
;
168 vout_InitInterlacingSupport(vout
, vout
->p
->displayed
.is_interlaced
);
171 vlc_object_set_destructor(vout
, VoutDestructor
);
174 if (vlc_clone(&vout
->p
->thread
, Thread
, vout
,
175 VLC_THREAD_PRIORITY_OUTPUT
)) {
176 spu_Destroy(vout
->p
->spu
);
177 vlc_object_release(vout
);
181 vout_control_WaitEmpty(&vout
->p
->control
);
184 msg_Err(vout
, "video output creation failed");
185 vout_CloseAndRelease(vout
);
189 vout
->p
->input
= cfg
->input
;
191 spu_Attach(vout
->p
->spu
, vout
->p
->input
, true);
196 vout_thread_t
*(vout_Request
)(vlc_object_t
*object
,
197 const vout_configuration_t
*cfg
)
199 vout_thread_t
*vout
= cfg
->vout
;
200 if (cfg
->change_fmt
&& !cfg
->fmt
) {
202 vout_CloseAndRelease(vout
);
206 /* If a vout is provided, try reusing it */
208 if (vout
->p
->input
!= cfg
->input
) {
210 spu_Attach(vout
->p
->spu
, vout
->p
->input
, false);
211 vout
->p
->input
= cfg
->input
;
213 spu_Attach(vout
->p
->spu
, vout
->p
->input
, true);
216 if (cfg
->change_fmt
) {
217 vout_control_cmd_t cmd
;
218 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_REINIT
);
221 vout_control_Push(&vout
->p
->control
, &cmd
);
222 vout_control_WaitEmpty(&vout
->p
->control
);
225 if (!vout
->p
->dead
) {
226 msg_Dbg(object
, "reusing provided vout");
229 vout_CloseAndRelease(vout
);
231 msg_Warn(object
, "cannot reuse provided vout");
233 return VoutCreate(object
, cfg
);
236 void vout_Close(vout_thread_t
*vout
)
241 spu_Attach(vout
->p
->spu
, vout
->p
->input
, false);
243 vout_snapshot_End(&vout
->p
->snapshot
);
245 vout_control_PushVoid(&vout
->p
->control
, VOUT_CONTROL_CLEAN
);
246 vlc_join(vout
->p
->thread
, NULL
);
248 vlc_mutex_lock(&vout
->p
->spu_lock
);
249 spu_Destroy(vout
->p
->spu
);
251 vlc_mutex_unlock(&vout
->p
->spu_lock
);
255 static void VoutDestructor(vlc_object_t
*object
)
257 vout_thread_t
*vout
= (vout_thread_t
*)object
;
259 /* Make sure the vout was stopped first */
260 //assert(!vout->p_module);
262 free(vout
->p
->splitter_name
);
264 /* Destroy the locks */
265 vlc_mutex_destroy(&vout
->p
->spu_lock
);
266 vlc_mutex_destroy(&vout
->p
->picture_lock
);
267 vlc_mutex_destroy(&vout
->p
->filter
.lock
);
268 vout_control_Clean(&vout
->p
->control
);
271 vout_statistic_Clean(&vout
->p
->statistic
);
274 vout_snapshot_Clean(&vout
->p
->snapshot
);
276 video_format_Clean(&vout
->p
->original
);
280 void vout_ChangePause(vout_thread_t
*vout
, bool is_paused
, mtime_t date
)
282 vout_control_cmd_t cmd
;
283 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_PAUSE
);
284 cmd
.u
.pause
.is_on
= is_paused
;
285 cmd
.u
.pause
.date
= date
;
286 vout_control_Push(&vout
->p
->control
, &cmd
);
288 vout_control_WaitEmpty(&vout
->p
->control
);
291 void vout_GetResetStatistic(vout_thread_t
*vout
, int *displayed
, int *lost
)
293 vout_statistic_GetReset( &vout
->p
->statistic
, displayed
, lost
);
296 void vout_Flush(vout_thread_t
*vout
, mtime_t date
)
298 vout_control_PushTime(&vout
->p
->control
, VOUT_CONTROL_FLUSH
, date
);
299 vout_control_WaitEmpty(&vout
->p
->control
);
302 void vout_Reset(vout_thread_t
*vout
)
304 vout_control_PushVoid(&vout
->p
->control
, VOUT_CONTROL_RESET
);
305 vout_control_WaitEmpty(&vout
->p
->control
);
308 bool vout_IsEmpty(vout_thread_t
*vout
)
310 vlc_mutex_lock(&vout
->p
->picture_lock
);
312 picture_t
*picture
= picture_fifo_Peek(vout
->p
->decoder_fifo
);
314 picture_Release(picture
);
316 vlc_mutex_unlock(&vout
->p
->picture_lock
);
321 void vout_FixLeaks( vout_thread_t
*vout
)
323 vlc_mutex_lock(&vout
->p
->picture_lock
);
325 picture_t
*picture
= picture_fifo_Peek(vout
->p
->decoder_fifo
);
327 picture
= picture_pool_Get(vout
->p
->decoder_pool
);
331 picture_Release(picture
);
332 /* Not all pictures has been displayed yet or some are
334 vlc_mutex_unlock(&vout
->p
->picture_lock
);
338 /* There is no reason that no pictures are available, force one
339 * from the pool, becarefull with it though */
340 msg_Err(vout
, "pictures leaked, trying to workaround");
343 picture_pool_NonEmpty(vout
->p
->decoder_pool
, false);
345 vlc_mutex_unlock(&vout
->p
->picture_lock
);
347 void vout_NextPicture(vout_thread_t
*vout
, mtime_t
*duration
)
349 vout_control_cmd_t cmd
;
350 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_STEP
);
351 cmd
.u
.time_ptr
= duration
;
353 vout_control_Push(&vout
->p
->control
, &cmd
);
354 vout_control_WaitEmpty(&vout
->p
->control
);
357 void vout_DisplayTitle(vout_thread_t
*vout
, const char *title
)
360 vout_control_PushString(&vout
->p
->control
, VOUT_CONTROL_OSD_TITLE
, title
);
363 void vout_PutSubpicture( vout_thread_t
*vout
, subpicture_t
*subpic
)
365 vout_control_cmd_t cmd
;
366 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_SUBPICTURE
);
367 cmd
.u
.subpicture
= subpic
;
369 vout_control_Push(&vout
->p
->control
, &cmd
);
371 int vout_RegisterSubpictureChannel( vout_thread_t
*vout
)
373 int channel
= SPU_DEFAULT_CHANNEL
;
375 vlc_mutex_lock(&vout
->p
->spu_lock
);
377 channel
= spu_RegisterChannel(vout
->p
->spu
);
378 vlc_mutex_unlock(&vout
->p
->spu_lock
);
382 void vout_FlushSubpictureChannel( vout_thread_t
*vout
, int channel
)
384 vout_control_PushInteger(&vout
->p
->control
, VOUT_CONTROL_FLUSH_SUBPICTURE
,
389 * It retreives a picture from the vout or NULL if no pictures are
392 * You MUST call vout_PutPicture or vout_ReleasePicture on it.
394 * You may use vout_HoldPicture(paired with vout_ReleasePicture) to keep a
395 * read-only reference.
397 picture_t
*vout_GetPicture(vout_thread_t
*vout
)
400 vlc_mutex_lock(&vout
->p
->picture_lock
);
401 picture_t
*picture
= picture_pool_Get(vout
->p
->decoder_pool
);
403 picture_Reset(picture
);
404 VideoFormatCopyCropAr(&picture
->format
, &vout
->p
->original
);
406 vlc_mutex_unlock(&vout
->p
->picture_lock
);
412 * It gives to the vout a picture to be displayed.
414 * The given picture MUST comes from vout_GetPicture.
416 * Becareful, after vout_PutPicture is called, picture_t::p_next cannot be
419 void vout_PutPicture(vout_thread_t
*vout
, picture_t
*picture
)
421 vlc_mutex_lock(&vout
->p
->picture_lock
);
423 picture
->p_next
= NULL
;
424 picture_fifo_Push(vout
->p
->decoder_fifo
, picture
);
426 vlc_mutex_unlock(&vout
->p
->picture_lock
);
428 vout_control_Wake(&vout
->p
->control
);
432 * It releases a picture retreived by vout_GetPicture.
434 void vout_ReleasePicture(vout_thread_t
*vout
, picture_t
*picture
)
436 vlc_mutex_lock(&vout
->p
->picture_lock
);
438 picture_Release(picture
);
440 vlc_mutex_unlock(&vout
->p
->picture_lock
);
442 vout_control_Wake(&vout
->p
->control
);
446 * It increment the reference counter of a picture retreived by
449 void vout_HoldPicture(vout_thread_t
*vout
, picture_t
*picture
)
451 vlc_mutex_lock(&vout
->p
->picture_lock
);
453 picture_Hold(picture
);
455 vlc_mutex_unlock(&vout
->p
->picture_lock
);
459 int vout_GetSnapshot(vout_thread_t
*vout
,
460 block_t
**image_dst
, picture_t
**picture_dst
,
462 const char *type
, mtime_t timeout
)
464 picture_t
*picture
= vout_snapshot_Get(&vout
->p
->snapshot
, timeout
);
466 msg_Err(vout
, "Failed to grab a snapshot");
471 vlc_fourcc_t codec
= VLC_CODEC_PNG
;
472 if (type
&& image_Type2Fourcc(type
))
473 codec
= image_Type2Fourcc(type
);
475 const int override_width
= var_GetInteger(vout
, "snapshot-width");
476 const int override_height
= var_GetInteger(vout
, "snapshot-height");
478 if (picture_Export(VLC_OBJECT(vout
), image_dst
, fmt
,
479 picture
, codec
, override_width
, override_height
)) {
480 msg_Err(vout
, "Failed to convert image for snapshot");
481 picture_Release(picture
);
486 *picture_dst
= picture
;
488 picture_Release(picture
);
492 /* vout_Control* are usable by anyone at anytime */
493 void vout_ControlChangeFullscreen(vout_thread_t
*vout
, bool fullscreen
)
495 vout_control_PushBool(&vout
->p
->control
, VOUT_CONTROL_FULLSCREEN
,
498 void vout_ControlChangeOnTop(vout_thread_t
*vout
, bool is_on_top
)
500 vout_control_PushBool(&vout
->p
->control
, VOUT_CONTROL_ON_TOP
,
503 void vout_ControlChangeDisplayFilled(vout_thread_t
*vout
, bool is_filled
)
505 vout_control_PushBool(&vout
->p
->control
, VOUT_CONTROL_DISPLAY_FILLED
,
508 void vout_ControlChangeZoom(vout_thread_t
*vout
, int num
, int den
)
510 vout_control_PushPair(&vout
->p
->control
, VOUT_CONTROL_ZOOM
,
513 void vout_ControlChangeSampleAspectRatio(vout_thread_t
*vout
,
514 unsigned num
, unsigned den
)
516 vout_control_PushPair(&vout
->p
->control
, VOUT_CONTROL_ASPECT_RATIO
,
519 void vout_ControlChangeCropRatio(vout_thread_t
*vout
,
520 unsigned num
, unsigned den
)
522 vout_control_PushPair(&vout
->p
->control
, VOUT_CONTROL_CROP_RATIO
,
525 void vout_ControlChangeCropWindow(vout_thread_t
*vout
,
526 int x
, int y
, int width
, int height
)
528 vout_control_cmd_t cmd
;
529 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_CROP_WINDOW
);
530 cmd
.u
.window
.x
= __MAX(x
, 0);
531 cmd
.u
.window
.y
= __MAX(y
, 0);
532 cmd
.u
.window
.width
= __MAX(width
, 0);
533 cmd
.u
.window
.height
= __MAX(height
, 0);
535 vout_control_Push(&vout
->p
->control
, &cmd
);
537 void vout_ControlChangeCropBorder(vout_thread_t
*vout
,
538 int left
, int top
, int right
, int bottom
)
540 vout_control_cmd_t cmd
;
541 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_CROP_BORDER
);
542 cmd
.u
.border
.left
= __MAX(left
, 0);
543 cmd
.u
.border
.top
= __MAX(top
, 0);
544 cmd
.u
.border
.right
= __MAX(right
, 0);
545 cmd
.u
.border
.bottom
= __MAX(bottom
, 0);
547 vout_control_Push(&vout
->p
->control
, &cmd
);
549 void vout_ControlChangeFilters(vout_thread_t
*vout
, const char *filters
)
551 vout_control_PushString(&vout
->p
->control
, VOUT_CONTROL_CHANGE_FILTERS
,
554 void vout_ControlChangeSubFilters(vout_thread_t
*vout
, const char *filters
)
556 vout_control_PushString(&vout
->p
->control
, VOUT_CONTROL_CHANGE_SUB_FILTERS
,
559 void vout_ControlChangeSubMargin(vout_thread_t
*vout
, int margin
)
561 vout_control_PushInteger(&vout
->p
->control
, VOUT_CONTROL_CHANGE_SUB_MARGIN
,
566 static void VoutGetDisplayCfg(vout_thread_t
*vout
, vout_display_cfg_t
*cfg
, const char *title
)
568 /* Load configuration */
569 cfg
->is_fullscreen
= var_CreateGetBool(vout
, "fullscreen");
570 cfg
->display
.title
= title
;
571 const int display_width
= var_CreateGetInteger(vout
, "width");
572 const int display_height
= var_CreateGetInteger(vout
, "height");
573 cfg
->display
.width
= display_width
> 0 ? display_width
: 0;
574 cfg
->display
.height
= display_height
> 0 ? display_height
: 0;
575 cfg
->is_display_filled
= var_CreateGetBool(vout
, "autoscale");
576 unsigned msar_num
, msar_den
;
577 if (var_InheritURational(vout
, &msar_num
, &msar_den
, "monitor-par") ||
578 msar_num
<= 0 || msar_den
<= 0) {
582 cfg
->display
.sar
.num
= msar_num
;
583 cfg
->display
.sar
.den
= msar_den
;
584 unsigned zoom_den
= 1000;
585 unsigned zoom_num
= zoom_den
* var_CreateGetFloat(vout
, "scale");
586 vlc_ureduce(&zoom_num
, &zoom_den
, zoom_num
, zoom_den
, 0);
587 cfg
->zoom
.num
= zoom_num
;
588 cfg
->zoom
.den
= zoom_den
;
589 cfg
->align
.vertical
= VOUT_DISPLAY_ALIGN_CENTER
;
590 cfg
->align
.horizontal
= VOUT_DISPLAY_ALIGN_CENTER
;
591 const int align_mask
= var_CreateGetInteger(vout
, "align");
592 if (align_mask
& 0x1)
593 cfg
->align
.horizontal
= VOUT_DISPLAY_ALIGN_LEFT
;
594 else if (align_mask
& 0x2)
595 cfg
->align
.horizontal
= VOUT_DISPLAY_ALIGN_RIGHT
;
596 if (align_mask
& 0x4)
597 cfg
->align
.vertical
= VOUT_DISPLAY_ALIGN_TOP
;
598 else if (align_mask
& 0x8)
599 cfg
->align
.vertical
= VOUT_DISPLAY_ALIGN_BOTTOM
;
602 vout_window_t
* vout_NewDisplayWindow(vout_thread_t
*vout
, vout_display_t
*vd
,
603 const vout_window_cfg_t
*cfg
)
606 vout_window_cfg_t cfg_override
= *cfg
;
608 if (!var_InheritBool( vout
, "embedded-video"))
609 cfg_override
.is_standalone
= true;
611 if (vout
->p
->window
.is_unused
&& vout
->p
->window
.object
) {
612 assert(!vout
->p
->splitter_name
);
613 if (!cfg_override
.is_standalone
== !vout
->p
->window
.cfg
.is_standalone
&&
614 cfg_override
.type
== vout
->p
->window
.cfg
.type
) {
615 /* Reuse the stored window */
616 msg_Dbg(vout
, "Reusing previous vout window");
617 vout_window_t
*window
= vout
->p
->window
.object
;
618 if (cfg_override
.width
!= vout
->p
->window
.cfg
.width
||
619 cfg_override
.height
!= vout
->p
->window
.cfg
.height
)
620 vout_window_SetSize(window
,
621 cfg_override
.width
, cfg_override
.height
);
622 vout
->p
->window
.is_unused
= false;
623 vout
->p
->window
.cfg
= cfg_override
;
627 vout_window_Delete(vout
->p
->window
.object
);
628 vout
->p
->window
.is_unused
= true;
629 vout
->p
->window
.object
= NULL
;
632 vout_window_t
*window
= vout_window_New(VLC_OBJECT(vout
), "$window",
636 if (!vout
->p
->splitter_name
) {
637 vout
->p
->window
.is_unused
= false;
638 vout
->p
->window
.cfg
= cfg_override
;
639 vout
->p
->window
.object
= window
;
644 void vout_DeleteDisplayWindow(vout_thread_t
*vout
, vout_display_t
*vd
,
645 vout_window_t
*window
)
648 if (!vout
->p
->window
.is_unused
&& vout
->p
->window
.object
== window
) {
649 vout
->p
->window
.is_unused
= true;
650 } else if (vout
->p
->window
.is_unused
&& vout
->p
->window
.object
&& !window
) {
651 vout_window_Delete(vout
->p
->window
.object
);
652 vout
->p
->window
.is_unused
= true;
653 vout
->p
->window
.object
= NULL
;
655 vout_window_Delete(window
);
660 static picture_t
*VoutVideoFilterInteractiveNewPicture(filter_t
*filter
)
662 vout_thread_t
*vout
= (vout_thread_t
*)filter
->p_owner
;
664 picture_t
*picture
= picture_pool_Get(vout
->p
->private_pool
);
666 picture_Reset(picture
);
667 VideoFormatCopyCropAr(&picture
->format
, &filter
->fmt_out
.video
);
671 static picture_t
*VoutVideoFilterStaticNewPicture(filter_t
*filter
)
673 vout_thread_t
*vout
= (vout_thread_t
*)filter
->p_owner
;
675 vlc_assert_locked(&vout
->p
->filter
.lock
);
676 if (filter_chain_GetLength(vout
->p
->filter
.chain_interactive
) == 0)
677 return VoutVideoFilterInteractiveNewPicture(filter
);
679 return picture_NewFromFormat(&filter
->fmt_out
.video
);
681 static void VoutVideoFilterDelPicture(filter_t
*filter
, picture_t
*picture
)
684 picture_Release(picture
);
686 static int VoutVideoFilterStaticAllocationSetup(filter_t
*filter
, void *data
)
688 filter
->pf_video_buffer_new
= VoutVideoFilterStaticNewPicture
;
689 filter
->pf_video_buffer_del
= VoutVideoFilterDelPicture
;
690 filter
->p_owner
= data
; /* vout */
693 static int VoutVideoFilterInteractiveAllocationSetup(filter_t
*filter
, void *data
)
695 filter
->pf_video_buffer_new
= VoutVideoFilterInteractiveNewPicture
;
696 filter
->pf_video_buffer_del
= VoutVideoFilterDelPicture
;
697 filter
->p_owner
= data
; /* vout */
700 static void ThreadFilterFlush(vout_thread_t
*vout
, bool is_locked
)
702 if (vout
->p
->displayed
.current
)
703 picture_Release( vout
->p
->displayed
.current
);
704 vout
->p
->displayed
.current
= NULL
;
706 if (vout
->p
->displayed
.next
)
707 picture_Release( vout
->p
->displayed
.next
);
708 vout
->p
->displayed
.next
= NULL
;
711 vlc_mutex_lock(&vout
->p
->filter
.lock
);
712 filter_chain_VideoFlush(vout
->p
->filter
.chain_static
);
713 filter_chain_VideoFlush(vout
->p
->filter
.chain_interactive
);
715 vlc_mutex_unlock(&vout
->p
->filter
.lock
);
723 static void ThreadChangeFilters(vout_thread_t
*vout
,
724 const video_format_t
*source
,
728 ThreadFilterFlush(vout
, is_locked
);
730 vlc_array_t array_static
;
731 vlc_array_t array_interactive
;
733 vlc_array_init(&array_static
);
734 vlc_array_init(&array_interactive
);
735 char *current
= filters
? strdup(filters
) : NULL
;
739 char *next
= config_ChainCreate(&name
, &cfg
, current
);
742 vout_filter_t
*e
= xmalloc(sizeof(*e
));
745 if (!strcmp(e
->name
, "deinterlace") ||
746 !strcmp(e
->name
, "postproc")) {
747 vlc_array_append(&array_static
, e
);
749 vlc_array_append(&array_interactive
, e
);
753 config_ChainDestroy(cfg
);
761 vlc_mutex_lock(&vout
->p
->filter
.lock
);
763 es_format_t fmt_target
;
764 es_format_InitFromVideo(&fmt_target
, source
? source
: &vout
->p
->filter
.format
);
766 es_format_t fmt_current
= fmt_target
;
768 for (int a
= 0; a
< 2; a
++) {
769 vlc_array_t
*array
= a
== 0 ? &array_static
:
771 filter_chain_t
*chain
= a
== 0 ? vout
->p
->filter
.chain_static
:
772 vout
->p
->filter
.chain_interactive
;
774 filter_chain_Reset(chain
, &fmt_current
, &fmt_current
);
775 for (int i
= 0; i
< vlc_array_count(array
); i
++) {
776 vout_filter_t
*e
= vlc_array_item_at_index(array
, i
);
777 msg_Dbg(vout
, "Adding '%s' as %s", e
->name
, a
== 0 ? "static" : "interactive");
778 if (!filter_chain_AppendFilter(chain
, e
->name
, e
->cfg
, NULL
, NULL
)) {
779 msg_Err(vout
, "Failed to add filter '%s'", e
->name
);
780 config_ChainDestroy(e
->cfg
);
785 fmt_current
= *filter_chain_GetFmtOut(chain
);
786 vlc_array_clear(array
);
788 VideoFormatCopyCropAr(&fmt_target
.video
, &fmt_current
.video
);
789 if (!es_format_IsSimilar(&fmt_current
, &fmt_target
)) {
790 msg_Dbg(vout
, "Adding a filter to compensate for format changes");
791 if (!filter_chain_AppendFilter(vout
->p
->filter
.chain_interactive
, NULL
, NULL
,
792 &fmt_current
, &fmt_target
)) {
793 msg_Err(vout
, "Failed to compensate for the format changes, removing all filters");
794 filter_chain_Reset(vout
->p
->filter
.chain_static
, &fmt_target
, &fmt_target
);
795 filter_chain_Reset(vout
->p
->filter
.chain_interactive
, &fmt_target
, &fmt_target
);
799 if (vout
->p
->filter
.configuration
!= filters
) {
800 free(vout
->p
->filter
.configuration
);
801 vout
->p
->filter
.configuration
= filters
? strdup(filters
) : NULL
;
804 video_format_Clean(&vout
->p
->filter
.format
);
805 video_format_Copy(&vout
->p
->filter
.format
, source
);
809 vlc_mutex_unlock(&vout
->p
->filter
.lock
);
814 static int ThreadDisplayPreparePicture(vout_thread_t
*vout
, bool reuse
, bool is_late_dropped
)
818 vlc_mutex_lock(&vout
->p
->filter
.lock
);
820 picture_t
*picture
= filter_chain_VideoFilter(vout
->p
->filter
.chain_static
, NULL
);
821 assert(!reuse
|| !picture
);
825 if (reuse
&& vout
->p
->displayed
.decoded
) {
826 decoded
= picture_Hold(vout
->p
->displayed
.decoded
);
828 decoded
= picture_fifo_Pop(vout
->p
->decoder_fifo
);
829 if (is_late_dropped
&& decoded
&& !decoded
->b_force
) {
830 const mtime_t predicted
= mdate() + 0; /* TODO improve */
831 const mtime_t late
= predicted
- decoded
->date
;
832 if (late
> VOUT_DISPLAY_LATE_THRESHOLD
) {
833 msg_Warn(vout
, "picture is too late to be displayed (missing %d ms)", (int)(late
/1000));
834 picture_Release(decoded
);
837 } else if (late
> 0) {
838 msg_Dbg(vout
, "picture might be displayed late (missing %d ms)", (int)(late
/1000));
842 !VideoFormatIsCropArEqual(&decoded
->format
, &vout
->p
->filter
.format
))
843 ThreadChangeFilters(vout
, &decoded
->format
, vout
->p
->filter
.configuration
, true);
849 if (vout
->p
->displayed
.decoded
)
850 picture_Release(vout
->p
->displayed
.decoded
);
852 vout
->p
->displayed
.decoded
= picture_Hold(decoded
);
853 vout
->p
->displayed
.timestamp
= decoded
->date
;
854 vout
->p
->displayed
.is_interlaced
= !decoded
->b_progressive
;
855 vout
->p
->displayed
.qtype
= decoded
->i_qtype
;
857 picture
= filter_chain_VideoFilter(vout
->p
->filter
.chain_static
, decoded
);
860 vlc_mutex_unlock(&vout
->p
->filter
.lock
);
862 vout_statistic_Update(&vout
->p
->statistic
, 0, lost_count
);
866 assert(!vout
->p
->displayed
.next
);
867 if (!vout
->p
->displayed
.current
)
868 vout
->p
->displayed
.current
= picture
;
870 vout
->p
->displayed
.next
= picture
;
874 static int ThreadDisplayRenderPicture(vout_thread_t
*vout
, bool is_forced
)
876 vout_thread_sys_t
*sys
= vout
->p
;
877 vout_display_t
*vd
= vout
->p
->display
.vd
;
879 picture_t
*torender
= picture_Hold(vout
->p
->displayed
.current
);
881 vout_chrono_Start(&vout
->p
->render
);
883 vlc_mutex_lock(&vout
->p
->filter
.lock
);
884 picture_t
*filtered
= filter_chain_VideoFilter(vout
->p
->filter
.chain_interactive
, torender
);
885 vlc_mutex_unlock(&vout
->p
->filter
.lock
);
890 if (filtered
->date
!= vout
->p
->displayed
.current
->date
)
891 msg_Warn(vout
, "Unsupported timestamp modifications done by chain_interactive");
894 * Get the subpicture to be displayed
896 const bool do_snapshot
= vout_snapshot_IsRequested(&vout
->p
->snapshot
);
897 mtime_t render_subtitle_date
;
898 if (vout
->p
->pause
.is_on
)
899 render_subtitle_date
= vout
->p
->pause
.date
;
901 render_subtitle_date
= filtered
->date
> 1 ? filtered
->date
: mdate();
902 mtime_t render_osd_date
= mdate(); /* FIXME wrong */
904 subpicture_t
*subpic
= spu_Render(vout
->p
->spu
, NULL
, &vd
->source
, &vd
->source
,
905 render_subtitle_date
, render_osd_date
,
912 * - be sure to end up with a direct buffer.
913 * - blend subtitles, and in a fast access buffer
915 picture_t
*direct
= NULL
;
917 (vout
->p
->decoder_pool
!= vout
->p
->display_pool
|| subpic
)) {
919 if (vout
->p
->is_decoder_pool_slow
)
920 render
= picture_NewFromFormat(&vd
->source
);
921 else if (vout
->p
->decoder_pool
!= vout
->p
->display_pool
)
922 render
= picture_pool_Get(vout
->p
->display_pool
);
924 render
= picture_pool_Get(vout
->p
->private_pool
);
927 picture_Copy(render
, filtered
);
929 if (vout
->p
->spu_blend
&& subpic
)
930 picture_BlendSubpicture(render
, vout
->p
->spu_blend
, subpic
);
932 if (vout
->p
->is_decoder_pool_slow
) {
933 direct
= picture_pool_Get(vout
->p
->display_pool
);
935 picture_Copy(direct
, render
);
936 picture_Release(render
);
941 VideoFormatCopyCropAr(&direct
->format
, &filtered
->format
);
942 picture_Release(filtered
);
948 subpicture_Delete(subpic
);
954 * Take a snapshot if requested
957 vout_snapshot_Set(&vout
->p
->snapshot
, &vd
->source
, direct
);
959 /* Render the direct buffer */
960 assert(vout_IsDisplayFiltered(vd
) == !sys
->display
.use_dr
);
961 vout_UpdateDisplaySourceProperties(vd
, &direct
->format
);
962 if (sys
->display
.use_dr
) {
963 vout_display_Prepare(vd
, direct
, NULL
);
965 sys
->display
.filtered
= vout_FilterDisplay(vd
, direct
);
966 if (sys
->display
.filtered
)
967 vout_display_Prepare(vd
, sys
->display
.filtered
, NULL
);
970 vout_chrono_Stop(&vout
->p
->render
);
975 msg_Info(vout
, "render: avg %d ms var %d ms",
976 (int)(vout
->p
->render
.avg
/1000), (int)(vout
->p
->render
.var
/1000));
980 /* Wait the real date (for rendering jitter) */
982 mtime_t delay
= direct
->date
- mdate();
984 msg_Warn(vout
, "picture is late (%lld ms)", delay
/ 1000);
989 /* Display the direct buffer returned by vout_RenderPicture */
990 vout
->p
->displayed
.date
= mdate();
991 vout_display_Display(vd
,
992 sys
->display
.filtered
? sys
->display
.filtered
995 sys
->display
.filtered
= NULL
;
997 vout_statistic_Update(&vout
->p
->statistic
, 1, 0);
1002 static int ThreadDisplayPicture(vout_thread_t
*vout
,
1003 bool now
, mtime_t
*deadline
)
1005 bool is_late_dropped
= vout
->p
->is_late_dropped
&& !vout
->p
->pause
.is_on
&& !now
;
1006 bool first
= !vout
->p
->displayed
.current
;
1007 if (first
&& ThreadDisplayPreparePicture(vout
, true, is_late_dropped
)) /* FIXME not sure it is ok */
1008 return VLC_EGENERIC
;
1009 if (!vout
->p
->pause
.is_on
|| now
) {
1010 while (!vout
->p
->displayed
.next
) {
1011 if (ThreadDisplayPreparePicture(vout
, false, is_late_dropped
)) {
1017 const mtime_t date
= mdate();
1018 const mtime_t render_delay
= vout_chrono_GetHigh(&vout
->p
->render
) + VOUT_MWAIT_TOLERANCE
;
1020 mtime_t date_next
= VLC_TS_INVALID
;
1021 if (!vout
->p
->pause
.is_on
&& vout
->p
->displayed
.next
)
1022 date_next
= vout
->p
->displayed
.next
->date
- render_delay
;
1024 /* FIXME/XXX we must redisplay the last decoded picture (because
1025 * of potential vout updated, or filters update or SPU update)
1026 * For now a high update period is needed but it coulmd be removed
1028 * - vout module emits events from theselves.
1029 * - *and* SPU is modified to emit an event or a deadline when needed.
1031 * So it will be done latter.
1033 mtime_t date_refresh
= VLC_TS_INVALID
;
1034 if (vout
->p
->displayed
.date
> VLC_TS_INVALID
)
1035 date_refresh
= vout
->p
->displayed
.date
+ VOUT_REDISPLAY_DELAY
- render_delay
;
1038 if (date_next
!= VLC_TS_INVALID
)
1039 drop
|= date_next
+ 0 <= date
;
1041 bool refresh
= false;
1042 if (date_refresh
> VLC_TS_INVALID
)
1043 refresh
= date_refresh
<= date
;
1045 if (!first
&& !refresh
&& !drop
) {
1046 if (date_next
!= VLC_TS_INVALID
&& date_refresh
!= VLC_TS_INVALID
)
1047 *deadline
= __MIN(date_next
, date_refresh
);
1048 else if (date_next
!= VLC_TS_INVALID
)
1049 *deadline
= date_next
;
1050 else if (date_refresh
!= VLC_TS_INVALID
)
1051 *deadline
= date_refresh
;
1052 return VLC_EGENERIC
;
1056 picture_Release(vout
->p
->displayed
.current
);
1057 vout
->p
->displayed
.current
= vout
->p
->displayed
.next
;
1058 vout
->p
->displayed
.next
= NULL
;
1060 if (!vout
->p
->displayed
.current
)
1061 return VLC_EGENERIC
;
1063 bool is_forced
= now
|| (!drop
&& refresh
) || vout
->p
->displayed
.current
->b_force
;
1064 return ThreadDisplayRenderPicture(vout
, is_forced
);
1067 static void ThreadManage(vout_thread_t
*vout
,
1069 vout_interlacing_support_t
*interlacing
,
1070 vout_postprocessing_support_t
*postprocessing
)
1072 vlc_mutex_lock(&vout
->p
->picture_lock
);
1074 *deadline
= VLC_TS_INVALID
;
1076 if (ThreadDisplayPicture(vout
, false, deadline
))
1080 const int picture_qtype
= vout
->p
->displayed
.qtype
;
1081 const bool picture_interlaced
= vout
->p
->displayed
.is_interlaced
;
1083 vlc_mutex_unlock(&vout
->p
->picture_lock
);
1085 /* Post processing */
1086 vout_SetPostProcessingState(vout
, postprocessing
, picture_qtype
);
1089 vout_SetInterlacingState(vout
, interlacing
, picture_interlaced
);
1091 vout_ManageWrapper(vout
);
1094 static void ThreadDisplaySubpicture(vout_thread_t
*vout
,
1095 subpicture_t
*subpicture
)
1097 spu_PutSubpicture(vout
->p
->spu
, subpicture
);
1100 static void ThreadFlushSubpicture(vout_thread_t
*vout
, int channel
)
1102 spu_ClearChannel(vout
->p
->spu
, channel
);
1105 static void ThreadDisplayOsdTitle(vout_thread_t
*vout
, const char *string
)
1107 if (!vout
->p
->title
.show
)
1110 vout_OSDText(vout
, SPU_DEFAULT_CHANNEL
,
1111 vout
->p
->title
.position
, INT64_C(1000) * vout
->p
->title
.timeout
,
1115 static void ThreadChangeSubFilters(vout_thread_t
*vout
, const char *filters
)
1117 spu_ChangeFilters(vout
->p
->spu
, filters
);
1119 static void ThreadChangeSubMargin(vout_thread_t
*vout
, int margin
)
1121 spu_ChangeMargin(vout
->p
->spu
, margin
);
1124 static void ThreadChangePause(vout_thread_t
*vout
, bool is_paused
, mtime_t date
)
1126 assert(!vout
->p
->pause
.is_on
|| !is_paused
);
1128 if (vout
->p
->pause
.is_on
) {
1129 const mtime_t duration
= date
- vout
->p
->pause
.date
;
1131 if (vout
->p
->step
.timestamp
> VLC_TS_INVALID
)
1132 vout
->p
->step
.timestamp
+= duration
;
1133 if (vout
->p
->step
.last
> VLC_TS_INVALID
)
1134 vout
->p
->step
.last
+= duration
;
1135 picture_fifo_OffsetDate(vout
->p
->decoder_fifo
, duration
);
1136 if (vout
->p
->displayed
.decoded
)
1137 vout
->p
->displayed
.decoded
->date
+= duration
;
1138 spu_OffsetSubtitleDate(vout
->p
->spu
, duration
);
1140 ThreadFilterFlush(vout
, false);
1142 vout
->p
->step
.timestamp
= VLC_TS_INVALID
;
1143 vout
->p
->step
.last
= VLC_TS_INVALID
;
1145 vout
->p
->pause
.is_on
= is_paused
;
1146 vout
->p
->pause
.date
= date
;
1149 static void ThreadFlush(vout_thread_t
*vout
, bool below
, mtime_t date
)
1151 vout
->p
->step
.timestamp
= VLC_TS_INVALID
;
1152 vout
->p
->step
.last
= VLC_TS_INVALID
;
1154 ThreadFilterFlush(vout
, false); /* FIXME too much */
1156 picture_t
*last
= vout
->p
->displayed
.decoded
;
1158 if (( below
&& last
->date
<= date
) ||
1159 (!below
&& last
->date
>= date
)) {
1160 picture_Release(last
);
1162 vout
->p
->displayed
.decoded
= NULL
;
1163 vout
->p
->displayed
.date
= VLC_TS_INVALID
;
1164 vout
->p
->displayed
.timestamp
= VLC_TS_INVALID
;
1168 picture_fifo_Flush(vout
->p
->decoder_fifo
, date
, below
);
1171 static void ThreadReset(vout_thread_t
*vout
)
1173 ThreadFlush(vout
, true, INT64_MAX
);
1174 if (vout
->p
->decoder_pool
)
1175 picture_pool_NonEmpty(vout
->p
->decoder_pool
, true);
1176 vout
->p
->pause
.is_on
= false;
1177 vout
->p
->pause
.date
= mdate();
1180 static void ThreadStep(vout_thread_t
*vout
, mtime_t
*duration
)
1184 if (vout
->p
->step
.last
<= VLC_TS_INVALID
)
1185 vout
->p
->step
.last
= vout
->p
->displayed
.timestamp
;
1188 if (ThreadDisplayPicture(vout
, true, &dummy
))
1191 vout
->p
->step
.timestamp
= vout
->p
->displayed
.timestamp
;
1193 if (vout
->p
->step
.last
> VLC_TS_INVALID
&&
1194 vout
->p
->step
.timestamp
> vout
->p
->step
.last
) {
1195 *duration
= vout
->p
->step
.timestamp
- vout
->p
->step
.last
;
1196 vout
->p
->step
.last
= vout
->p
->step
.timestamp
;
1197 /* TODO advance subpicture by the duration ... */
1201 static void ThreadChangeFullscreen(vout_thread_t
*vout
, bool fullscreen
)
1203 /* FIXME not sure setting "fullscreen" is good ... */
1204 var_SetBool(vout
, "fullscreen", fullscreen
);
1205 vout_SetDisplayFullscreen(vout
->p
->display
.vd
, fullscreen
);
1208 static void ThreadChangeOnTop(vout_thread_t
*vout
, bool is_on_top
)
1210 vout_SetWindowState(vout
->p
->display
.vd
,
1211 is_on_top
? VOUT_WINDOW_STATE_ABOVE
:
1212 VOUT_WINDOW_STATE_NORMAL
);
1215 static void ThreadChangeDisplayFilled(vout_thread_t
*vout
, bool is_filled
)
1217 vout_SetDisplayFilled(vout
->p
->display
.vd
, is_filled
);
1220 static void ThreadChangeZoom(vout_thread_t
*vout
, int num
, int den
)
1222 if (num
* 10 < den
) {
1225 } else if (num
> den
* 10) {
1229 vout_SetDisplayZoom(vout
->p
->display
.vd
, num
, den
);
1232 static void ThreadChangeAspectRatio(vout_thread_t
*vout
,
1233 unsigned num
, unsigned den
)
1235 vout_SetDisplayAspect(vout
->p
->display
.vd
, num
, den
);
1239 static void ThreadExecuteCropWindow(vout_thread_t
*vout
,
1240 unsigned x
, unsigned y
,
1241 unsigned width
, unsigned height
)
1243 vout_SetDisplayCrop(vout
->p
->display
.vd
, 0, 0,
1244 x
, y
, width
, height
);
1246 static void ThreadExecuteCropBorder(vout_thread_t
*vout
,
1247 unsigned left
, unsigned top
,
1248 unsigned right
, unsigned bottom
)
1250 msg_Err(vout
, "ThreadExecuteCropBorder %d.%d %dx%d", left
, top
, right
, bottom
);
1251 vout_SetDisplayCrop(vout
->p
->display
.vd
, 0, 0,
1252 left
, top
, -(int)right
, -(int)bottom
);
1255 static void ThreadExecuteCropRatio(vout_thread_t
*vout
,
1256 unsigned num
, unsigned den
)
1258 vout_SetDisplayCrop(vout
->p
->display
.vd
, num
, den
,
1262 static int ThreadStart(vout_thread_t
*vout
, const vout_display_state_t
*state
)
1264 vlc_mouse_Init(&vout
->p
->mouse
);
1265 vout
->p
->decoder_fifo
= picture_fifo_New();
1266 vout
->p
->decoder_pool
= NULL
;
1267 vout
->p
->display_pool
= NULL
;
1268 vout
->p
->private_pool
= NULL
;
1270 vout
->p
->filter
.configuration
= NULL
;
1271 video_format_Copy(&vout
->p
->filter
.format
, &vout
->p
->original
);
1272 vout
->p
->filter
.chain_static
=
1273 filter_chain_New( vout
, "video filter2", true,
1274 VoutVideoFilterStaticAllocationSetup
, NULL
, vout
);
1275 vout
->p
->filter
.chain_interactive
=
1276 filter_chain_New( vout
, "video filter2", true,
1277 VoutVideoFilterInteractiveAllocationSetup
, NULL
, vout
);
1279 vout_display_state_t state_default
;
1281 VoutGetDisplayCfg(vout
, &state_default
.cfg
, vout
->p
->display
.title
);
1282 state_default
.wm_state
= var_CreateGetBool(vout
, "video-on-top") ? VOUT_WINDOW_STATE_ABOVE
:
1283 VOUT_WINDOW_STATE_NORMAL
;
1284 state_default
.sar
.num
= 0;
1285 state_default
.sar
.den
= 0;
1287 state
= &state_default
;
1290 if (vout_OpenWrapper(vout
, vout
->p
->splitter_name
, state
))
1291 return VLC_EGENERIC
;
1292 if (vout_InitWrapper(vout
))
1293 return VLC_EGENERIC
;
1294 assert(vout
->p
->decoder_pool
);
1296 vout
->p
->displayed
.current
= NULL
;
1297 vout
->p
->displayed
.next
= NULL
;
1298 vout
->p
->displayed
.decoded
= NULL
;
1299 vout
->p
->displayed
.date
= VLC_TS_INVALID
;
1300 vout
->p
->displayed
.timestamp
= VLC_TS_INVALID
;
1301 vout
->p
->displayed
.qtype
= QTYPE_NONE
;
1302 vout
->p
->displayed
.is_interlaced
= false;
1304 vout
->p
->step
.last
= VLC_TS_INVALID
;
1305 vout
->p
->step
.timestamp
= VLC_TS_INVALID
;
1308 vout
->p
->spu_blend
= filter_NewBlend(VLC_OBJECT(vout
), &vout
->p
->original
);
1309 if (!vout
->p
->spu_blend
)
1310 msg_Err(vout
, "Failed to create blending filter, OSD/Subtitles will not work");
1312 video_format_Print(VLC_OBJECT(vout
), "original format", &vout
->p
->original
);
1316 static void ThreadStop(vout_thread_t
*vout
, vout_display_state_t
*state
)
1318 if (vout
->p
->spu_blend
)
1319 filter_DeleteBlend(vout
->p
->spu_blend
);
1321 /* Destroy translation tables */
1322 if (vout
->p
->display
.vd
) {
1323 if (vout
->p
->decoder_pool
) {
1324 ThreadFlush(vout
, true, INT64_MAX
);
1325 vout_EndWrapper(vout
);
1327 vout_CloseWrapper(vout
, state
);
1330 /* Destroy the video filters2 */
1331 filter_chain_Delete(vout
->p
->filter
.chain_interactive
);
1332 filter_chain_Delete(vout
->p
->filter
.chain_static
);
1333 video_format_Clean(&vout
->p
->filter
.format
);
1334 free(vout
->p
->filter
.configuration
);
1336 if (vout
->p
->decoder_fifo
)
1337 picture_fifo_Delete(vout
->p
->decoder_fifo
);
1338 assert(!vout
->p
->decoder_pool
);
1341 static void ThreadInit(vout_thread_t
*vout
)
1343 vout
->p
->window
.is_unused
= true;
1344 vout
->p
->window
.object
= NULL
;
1345 vout
->p
->dead
= false;
1346 vout
->p
->is_late_dropped
= var_InheritBool(vout
, "drop-late-frames");
1347 vout
->p
->pause
.is_on
= false;
1348 vout
->p
->pause
.date
= VLC_TS_INVALID
;
1350 vout_chrono_Init(&vout
->p
->render
, 5, 10000); /* Arbitrary initial time */
1353 static void ThreadClean(vout_thread_t
*vout
)
1355 if (vout
->p
->window
.object
) {
1356 assert(vout
->p
->window
.is_unused
);
1357 vout_window_Delete(vout
->p
->window
.object
);
1359 vout_chrono_Clean(&vout
->p
->render
);
1360 vout
->p
->dead
= true;
1361 vout_control_Dead(&vout
->p
->control
);
1364 static int ThreadReinit(vout_thread_t
*vout
,
1365 const vout_configuration_t
*cfg
)
1367 video_format_t original
;
1368 if (VoutValidateFormat(&original
, cfg
->fmt
)) {
1369 ThreadStop(vout
, NULL
);
1371 return VLC_EGENERIC
;
1373 /* We ignore crop/ar changes at this point, they are dynamically supported */
1374 VideoFormatCopyCropAr(&vout
->p
->original
, &original
);
1375 if (video_format_IsSimilar(&original
, &vout
->p
->original
)) {
1376 if (cfg
->dpb_size
<= vout
->p
->dpb_size
)
1378 msg_Warn(vout
, "DPB need to be increased");
1381 vout_display_state_t state
;
1382 memset(&state
, 0, sizeof(state
));
1384 ThreadStop(vout
, &state
);
1386 if (!state
.cfg
.is_fullscreen
) {
1387 state
.cfg
.display
.width
= 0;
1388 state
.cfg
.display
.height
= 0;
1392 /* FIXME current vout "variables" are not in sync here anymore
1393 * and I am not sure what to do */
1395 vout
->p
->original
= original
;
1396 vout
->p
->dpb_size
= cfg
->dpb_size
;
1397 if (ThreadStart(vout
, &state
)) {
1399 return VLC_EGENERIC
;
1404 /*****************************************************************************
1405 * Thread: video output thread
1406 *****************************************************************************
1407 * Video output thread. This function does only returns when the thread is
1408 * terminated. It handles the pictures arriving in the video heap and the
1409 * display device events.
1410 *****************************************************************************/
1411 static void *Thread(void *object
)
1413 vout_thread_t
*vout
= object
;
1415 vout_interlacing_support_t interlacing
= {
1416 .is_interlaced
= false,
1419 vout_postprocessing_support_t postprocessing
= {
1420 .qtype
= QTYPE_NONE
,
1423 mtime_t deadline
= VLC_TS_INVALID
;
1425 vout_control_cmd_t cmd
;
1427 /* FIXME remove thoses ugly timeouts
1429 while (!vout_control_Pop(&vout
->p
->control
, &cmd
, deadline
, 100000)) {
1431 case VOUT_CONTROL_INIT
:
1433 if (ThreadStart(vout
, NULL
)) {
1434 ThreadStop(vout
, NULL
);
1439 case VOUT_CONTROL_CLEAN
:
1440 ThreadStop(vout
, NULL
);
1443 case VOUT_CONTROL_REINIT
:
1444 if (ThreadReinit(vout
, cmd
.u
.cfg
))
1447 case VOUT_CONTROL_SUBPICTURE
:
1448 ThreadDisplaySubpicture(vout
, cmd
.u
.subpicture
);
1449 cmd
.u
.subpicture
= NULL
;
1451 case VOUT_CONTROL_FLUSH_SUBPICTURE
:
1452 ThreadFlushSubpicture(vout
, cmd
.u
.integer
);
1454 case VOUT_CONTROL_OSD_TITLE
:
1455 ThreadDisplayOsdTitle(vout
, cmd
.u
.string
);
1457 case VOUT_CONTROL_CHANGE_FILTERS
:
1458 ThreadChangeFilters(vout
, NULL
, cmd
.u
.string
, false);
1460 case VOUT_CONTROL_CHANGE_SUB_FILTERS
:
1461 ThreadChangeSubFilters(vout
, cmd
.u
.string
);
1463 case VOUT_CONTROL_CHANGE_SUB_MARGIN
:
1464 ThreadChangeSubMargin(vout
, cmd
.u
.integer
);
1466 case VOUT_CONTROL_PAUSE
:
1467 ThreadChangePause(vout
, cmd
.u
.pause
.is_on
, cmd
.u
.pause
.date
);
1469 case VOUT_CONTROL_FLUSH
:
1470 ThreadFlush(vout
, false, cmd
.u
.time
);
1472 case VOUT_CONTROL_RESET
:
1475 case VOUT_CONTROL_STEP
:
1476 ThreadStep(vout
, cmd
.u
.time_ptr
);
1478 case VOUT_CONTROL_FULLSCREEN
:
1479 ThreadChangeFullscreen(vout
, cmd
.u
.boolean
);
1481 case VOUT_CONTROL_ON_TOP
:
1482 ThreadChangeOnTop(vout
, cmd
.u
.boolean
);
1484 case VOUT_CONTROL_DISPLAY_FILLED
:
1485 ThreadChangeDisplayFilled(vout
, cmd
.u
.boolean
);
1487 case VOUT_CONTROL_ZOOM
:
1488 ThreadChangeZoom(vout
, cmd
.u
.pair
.a
, cmd
.u
.pair
.b
);
1490 case VOUT_CONTROL_ASPECT_RATIO
:
1491 ThreadChangeAspectRatio(vout
, cmd
.u
.pair
.a
, cmd
.u
.pair
.b
);
1493 case VOUT_CONTROL_CROP_RATIO
:
1494 ThreadExecuteCropRatio(vout
, cmd
.u
.pair
.a
, cmd
.u
.pair
.b
);
1496 case VOUT_CONTROL_CROP_WINDOW
:
1497 ThreadExecuteCropWindow(vout
,
1498 cmd
.u
.window
.x
, cmd
.u
.window
.y
,
1499 cmd
.u
.window
.width
, cmd
.u
.window
.height
);
1501 case VOUT_CONTROL_CROP_BORDER
:
1502 ThreadExecuteCropBorder(vout
,
1503 cmd
.u
.border
.left
, cmd
.u
.border
.top
,
1504 cmd
.u
.border
.right
, cmd
.u
.border
.bottom
);
1509 vout_control_cmd_Clean(&cmd
);
1512 ThreadManage(vout
, &deadline
, &interlacing
, &postprocessing
);