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-2019 VLC authors, VideoLAN and Videolabs SAS
10 * Authors: Vincent Seguin <seguin@via.ecp.fr>
11 * Gildas Bazin <gbazin@videolan.org>
12 * Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
14 * This program is free software; you can redistribute it and/or modify it
15 * under the terms of the GNU Lesser General Public License as published by
16 * the Free Software Foundation; either version 2.1 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public License
25 * along with this program; if not, write to the Free Software Foundation,
26 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27 *****************************************************************************/
29 /*****************************************************************************
31 *****************************************************************************/
36 #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>
50 #include <vlc_codec.h>
51 #include <vlc_atomic.h>
54 #include "vout_private.h"
55 #include "vout_internal.h"
59 #include "../misc/variables.h"
60 #include "../clock/clock.h"
61 #include "statistic.h"
65 typedef struct vout_thread_sys_t
67 struct vout_thread_t obj
;
69 vout_thread_private_t
private;
73 /* Splitter module if used */
80 video_format_t original
; /* Original format ie coming from the decoder */
85 struct vout_crop crop
;
88 /* Snapshot interface */
89 struct vout_snapshot
*snapshot
;
92 vout_statistic_t statistic
;
96 vlc_fourcc_t spu_blend_chroma
;
97 vlc_blender_t
*spu_blend
;
99 /* Thread & synchronization */
100 vout_control_t control
;
101 atomic_bool control_is_terminated
; // shutdown the vout thread
106 vlc_tick_t timestamp
;
108 picture_t
*decoded
; // decoded picture before passed through chain_static
114 vlc_tick_t timestamp
;
122 /* OSD title configuration */
130 bool is_late_dropped
;
134 vlc_mouse_event mouse_event
;
137 /* Video output window */
139 unsigned window_width
; /* protected by display_lock */
140 unsigned window_height
; /* protected by display_lock */
141 vlc_mutex_t window_lock
;
142 vlc_decoder_device
*dec_device
;
144 /* Video output display */
145 vout_display_cfg_t display_cfg
;
146 vout_display_t
*display
;
147 vlc_mutex_t display_lock
;
149 /* Video filter2 chain */
155 video_format_t src_fmt
;
156 vlc_video_context
*src_vctx
;
157 struct filter_chain_t
*chain_static
;
158 struct filter_chain_t
*chain_interactive
;
161 picture_fifo_t
*decoder_fifo
;
162 vout_chrono_t render
; /**< picture render time estimator */
163 vout_chrono_t static_filter
;
169 #define VOUT_THREAD_TO_SYS(vout) \
170 container_of(vout, vout_thread_sys_t, obj.obj)
173 /* Maximum delay between 2 displayed pictures.
174 * XXX it is needed for now but should be removed in the long term.
176 #define VOUT_REDISPLAY_DELAY VLC_TICK_FROM_MS(80)
179 * Late pictures having a delay higher than this value are thrashed.
181 #define VOUT_DISPLAY_LATE_THRESHOLD VLC_TICK_FROM_MS(20)
183 /* Better be in advance when awakening than late... */
184 #define VOUT_MWAIT_TOLERANCE VLC_TICK_FROM_MS(4)
187 static bool VoutCheckFormat(const video_format_t
*src
)
189 if (src
->i_width
== 0 || src
->i_width
> 8192 ||
190 src
->i_height
== 0 || src
->i_height
> 8192)
192 if (src
->i_sar_num
<= 0 || src
->i_sar_den
<= 0)
197 static void VoutFixFormat(video_format_t
*dst
, const video_format_t
*src
)
199 video_format_Copy(dst
, src
);
200 dst
->i_chroma
= vlc_fourcc_GetCodec(VIDEO_ES
, src
->i_chroma
);
201 VoutFixFormatAR( dst
);
202 video_format_FixRgb(dst
);
205 static bool VideoFormatIsCropArEqual(video_format_t
*dst
,
206 const video_format_t
*src
)
208 return dst
->i_sar_num
* src
->i_sar_den
== dst
->i_sar_den
* src
->i_sar_num
&&
209 dst
->i_x_offset
== src
->i_x_offset
&&
210 dst
->i_y_offset
== src
->i_y_offset
&&
211 dst
->i_visible_width
== src
->i_visible_width
&&
212 dst
->i_visible_height
== src
->i_visible_height
;
215 static void vout_display_SizeWindow(unsigned *restrict width
,
216 unsigned *restrict height
,
217 unsigned w
, unsigned h
,
218 unsigned sar_num
, unsigned sar_den
,
219 video_orientation_t orientation
,
220 const vout_display_cfg_t
*restrict cfg
)
222 *width
= cfg
->display
.width
;
223 *height
= cfg
->display
.height
;
225 /* If both width and height are forced, keep them as is. */
226 if (*width
!= 0 && *height
!= 0)
229 /* Compute intended video resolution from source. */
230 assert(sar_num
> 0 && sar_den
> 0);
231 w
= (w
* sar_num
) / sar_den
;
233 /* Adjust video size for orientation and pixel A/R. */
234 if (ORIENT_IS_SWAP(orientation
)) {
241 if (cfg
->display
.sar
.num
> 0 && cfg
->display
.sar
.den
> 0)
242 w
= (w
* cfg
->display
.sar
.den
) / cfg
->display
.sar
.num
;
244 /* If width is forced, adjust height according to the aspect ratio */
246 *height
= (*width
* h
) / w
;
250 /* If height is forced, adjust width according to the aspect ratio */
252 *width
= (*height
* w
) / h
;
256 /* If neither width nor height are forced, use the requested zoom. */
257 *width
= (w
* cfg
->zoom
.num
) / cfg
->zoom
.den
;
258 *height
= (h
* cfg
->zoom
.num
) / cfg
->zoom
.den
;
261 static void vout_SizeWindow(vout_thread_sys_t
*vout
,
262 const video_format_t
*original
,
263 unsigned *restrict width
,
264 unsigned *restrict height
)
266 vout_thread_sys_t
*sys
= vout
;
267 unsigned w
= original
->i_visible_width
;
268 unsigned h
= original
->i_visible_height
;
269 unsigned sar_num
= original
->i_sar_num
;
270 unsigned sar_den
= original
->i_sar_den
;
272 if (sys
->source
.dar
.num
> 0 && sys
->source
.dar
.den
> 0) {
273 unsigned num
= sys
->source
.dar
.num
* h
;
274 unsigned den
= sys
->source
.dar
.den
* w
;
276 vlc_ureduce(&sar_num
, &sar_den
, num
, den
, 0);
279 switch (sys
->source
.crop
.mode
) {
283 case VOUT_CROP_RATIO
: {
284 unsigned num
= sys
->source
.crop
.ratio
.num
;
285 unsigned den
= sys
->source
.crop
.ratio
.den
;
287 if (w
* den
> h
* num
)
294 case VOUT_CROP_WINDOW
:
295 w
= sys
->source
.crop
.window
.width
;
296 h
= sys
->source
.crop
.window
.height
;
299 case VOUT_CROP_BORDER
:
300 w
= sys
->source
.crop
.border
.right
- sys
->source
.crop
.border
.left
;
301 h
= sys
->source
.crop
.border
.bottom
- sys
->source
.crop
.border
.top
;
305 /* If the vout thread is running, the window lock must be held here. */
306 vout_display_SizeWindow(width
, height
, w
, h
, sar_num
, sar_den
,
307 original
->orientation
,
311 static void vout_UpdateWindowSizeLocked(vout_thread_sys_t
*vout
)
313 vout_thread_sys_t
*sys
= vout
;
314 unsigned width
, height
;
316 vlc_mutex_assert(&sys
->window_lock
);
318 vlc_mutex_lock(&sys
->display_lock
);
319 if (sys
->display
!= NULL
) {
320 vout_SizeWindow(vout
, &sys
->original
, &width
, &height
);
321 vlc_mutex_unlock(&sys
->display_lock
);
323 msg_Dbg(&vout
->obj
, "requested window size: %ux%u", width
, height
);
324 vout_window_SetSize(sys
->display_cfg
.window
, width
, height
);
326 vlc_mutex_unlock(&sys
->display_lock
);
330 void vout_GetResetStatistic(vout_thread_t
*vout
, unsigned *restrict displayed
,
331 unsigned *restrict lost
, unsigned *restrict late
)
333 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
335 vout_statistic_GetReset( &sys
->statistic
, displayed
, lost
, late
);
338 bool vout_IsEmpty(vout_thread_t
*vout
)
340 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
342 if (!sys
->decoder_fifo
)
345 return picture_fifo_IsEmpty(sys
->decoder_fifo
);
348 void vout_DisplayTitle(vout_thread_t
*vout
, const char *title
)
350 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
354 if (!sys
->title
.show
)
357 vout_OSDText(vout
, VOUT_SPU_CHANNEL_OSD
, sys
->title
.position
,
358 VLC_TICK_FROM_MS(sys
->title
.timeout
), title
);
361 void vout_MouseState(vout_thread_t
*vout
, const vlc_mouse_t
*mouse
)
363 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
367 /* Translate window coordinates to video coordinates */
368 vlc_mutex_lock(&sys
->display_lock
);
369 vlc_mouse_t video_mouse
;
371 vout_display_TranslateMouseState(sys
->display
, &video_mouse
, mouse
);
373 video_mouse
= *mouse
;
374 vlc_mutex_unlock(&sys
->display_lock
);
376 vout_control_PushMouse(&sys
->control
, &video_mouse
);
379 void vout_PutSubpicture( vout_thread_t
*vout
, subpicture_t
*subpic
)
381 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
384 if (sys
->spu
!= NULL
)
385 spu_PutSubpicture(sys
->spu
, subpic
);
387 subpicture_Delete(subpic
);
390 ssize_t
vout_RegisterSubpictureChannel( vout_thread_t
*vout
)
392 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
394 ssize_t channel
= VOUT_SPU_CHANNEL_INVALID
;
397 channel
= spu_RegisterChannel(sys
->spu
);
402 ssize_t
vout_RegisterSubpictureChannelInternal(vout_thread_t
*vout
,
404 enum vlc_vout_order
*out_order
)
406 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
408 ssize_t channel
= VOUT_SPU_CHANNEL_INVALID
;
411 channel
= spu_RegisterChannelInternal(sys
->spu
, clock
, out_order
);
416 void vout_UnregisterSubpictureChannel( vout_thread_t
*vout
, size_t channel
)
418 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
421 spu_UnregisterChannel(sys
->spu
, channel
);
424 void vout_FlushSubpictureChannel( vout_thread_t
*vout
, size_t channel
)
426 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
429 spu_ClearChannel(sys
->spu
, channel
);
432 void vout_SetSpuHighlight( vout_thread_t
*vout
,
433 const vlc_spu_highlight_t
*spu_hl
)
435 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
438 spu_SetHighlight(sys
->spu
, spu_hl
);
442 * Allocates a video output picture buffer.
444 * Either vout_PutPicture() or picture_Release() must be used to return the
445 * buffer to the video output free buffer pool.
447 * You may use picture_Hold() (paired with picture_Release()) to keep a
448 * read-only reference.
450 picture_t
*vout_GetPicture(vout_thread_t
*vout
)
452 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
454 picture_t
*picture
= picture_pool_Wait(sys
->private.display_pool
);
455 if (likely(picture
!= NULL
)) {
456 picture_Reset(picture
);
457 video_format_CopyCropAr(&picture
->format
, &sys
->original
);
463 * It gives to the vout a picture to be displayed.
465 * The given picture MUST comes from vout_GetPicture.
467 * Becareful, after vout_PutPicture is called, picture_t::p_next cannot be
470 void vout_PutPicture(vout_thread_t
*vout
, picture_t
*picture
)
472 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
474 assert( !picture_HasChainedPics( picture
) );
475 picture_fifo_Push(sys
->decoder_fifo
, picture
);
476 vout_control_Wake(&sys
->control
);
480 int vout_GetSnapshot(vout_thread_t
*vout
,
481 block_t
**image_dst
, picture_t
**picture_dst
,
483 const char *type
, vlc_tick_t timeout
)
485 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
487 picture_t
*picture
= vout_snapshot_Get(sys
->snapshot
, timeout
);
489 msg_Err(vout
, "Failed to grab a snapshot");
494 vlc_fourcc_t codec
= VLC_CODEC_PNG
;
495 if (type
&& image_Type2Fourcc(type
))
496 codec
= image_Type2Fourcc(type
);
498 const int override_width
= var_InheritInteger(vout
, "snapshot-width");
499 const int override_height
= var_InheritInteger(vout
, "snapshot-height");
501 if (picture_Export(VLC_OBJECT(vout
), image_dst
, fmt
,
502 picture
, codec
, override_width
, override_height
, false)) {
503 msg_Err(vout
, "Failed to convert image for snapshot");
504 picture_Release(picture
);
509 *picture_dst
= picture
;
511 picture_Release(picture
);
515 /* vout_Control* are usable by anyone at anytime */
516 void vout_ChangeFullscreen(vout_thread_t
*vout
, const char *id
)
518 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
520 vlc_mutex_lock(&sys
->window_lock
);
521 vout_window_SetFullScreen(sys
->display_cfg
.window
, id
);
522 vlc_mutex_unlock(&sys
->window_lock
);
525 void vout_ChangeWindowed(vout_thread_t
*vout
)
527 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
529 vlc_mutex_lock(&sys
->window_lock
);
530 vout_window_UnsetFullScreen(sys
->display_cfg
.window
);
531 /* Attempt to reset the intended window size */
532 vout_UpdateWindowSizeLocked(sys
);
533 vlc_mutex_unlock(&sys
->window_lock
);
536 void vout_ChangeWindowState(vout_thread_t
*vout
, unsigned st
)
538 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
540 vlc_mutex_lock(&sys
->window_lock
);
541 vout_window_SetState(sys
->display_cfg
.window
, st
);
542 vlc_mutex_unlock(&sys
->window_lock
);
545 void vout_ChangeDisplaySize(vout_thread_t
*vout
,
546 unsigned width
, unsigned height
,
547 void (*cb
)(void *), void *opaque
)
549 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
553 /* DO NOT call this outside the vout window callbacks */
554 vlc_mutex_lock(&sys
->display_lock
);
556 sys
->window_width
= width
;
557 sys
->window_height
= height
;
559 if (sys
->display
!= NULL
)
560 vout_display_SetSize(sys
->display
, width
, height
);
564 vlc_mutex_unlock(&sys
->display_lock
);
567 void vout_ChangeDisplayFilled(vout_thread_t
*vout
, bool is_filled
)
569 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
572 vlc_mutex_lock(&sys
->window_lock
);
573 sys
->display_cfg
.is_display_filled
= is_filled
;
574 /* no window size update here */
576 vlc_mutex_lock(&sys
->display_lock
);
577 vlc_mutex_unlock(&sys
->window_lock
);
579 if (sys
->display
!= NULL
)
580 vout_SetDisplayFilled(sys
->display
, is_filled
);
581 vlc_mutex_unlock(&sys
->display_lock
);
584 void vout_ChangeZoom(vout_thread_t
*vout
, unsigned num
, unsigned den
)
586 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
589 if (num
!= 0 && den
!= 0) {
590 vlc_ureduce(&num
, &den
, num
, den
, 0);
596 if (num
* 10 < den
) {
599 } else if (num
> den
* 10) {
604 vlc_mutex_lock(&sys
->window_lock
);
605 sys
->display_cfg
.zoom
.num
= num
;
606 sys
->display_cfg
.zoom
.den
= den
;
608 vout_UpdateWindowSizeLocked(sys
);
610 vlc_mutex_lock(&sys
->display_lock
);
611 vlc_mutex_unlock(&sys
->window_lock
);
613 if (sys
->display
!= NULL
)
614 vout_SetDisplayZoom(sys
->display
, num
, den
);
615 vlc_mutex_unlock(&sys
->display_lock
);
618 static void vout_SetAspectRatio(vout_thread_sys_t
*sys
,
619 unsigned dar_num
, unsigned dar_den
)
621 sys
->source
.dar
.num
= dar_num
;
622 sys
->source
.dar
.den
= dar_den
;
625 void vout_ChangeDisplayAspectRatio(vout_thread_t
*vout
,
626 unsigned dar_num
, unsigned dar_den
)
628 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
631 vlc_mutex_lock(&sys
->window_lock
);
632 vout_SetAspectRatio(sys
, dar_num
, dar_den
);
634 vout_UpdateWindowSizeLocked(sys
);
636 vlc_mutex_lock(&sys
->display_lock
);
637 vlc_mutex_unlock(&sys
->window_lock
);
639 if (sys
->display
!= NULL
)
640 vout_SetDisplayAspect(sys
->display
, dar_num
, dar_den
);
641 vlc_mutex_unlock(&sys
->display_lock
);
644 void vout_ChangeCrop(vout_thread_t
*vout
,
645 const struct vout_crop
*restrict crop
)
647 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
650 vlc_mutex_lock(&sys
->window_lock
);
651 sys
->source
.crop
= *crop
;
652 vout_UpdateWindowSizeLocked(sys
);
654 vlc_mutex_lock(&sys
->display_lock
);
655 vlc_mutex_unlock(&sys
->window_lock
);
657 if (sys
->display
!= NULL
)
658 vout_SetDisplayCrop(sys
->display
, crop
);
659 vlc_mutex_unlock(&sys
->display_lock
);
662 void vout_ControlChangeFilters(vout_thread_t
*vout
, const char *filters
)
664 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
666 vlc_mutex_lock(&sys
->filter
.lock
);
667 if (sys
->filter
.configuration
)
669 if (filters
== NULL
|| strcmp(sys
->filter
.configuration
, filters
))
671 free(sys
->filter
.configuration
);
672 sys
->filter
.configuration
= filters
? strdup(filters
) : NULL
;
673 sys
->filter
.changed
= true;
676 else if (filters
!= NULL
)
678 sys
->filter
.configuration
= strdup(filters
);
679 sys
->filter
.changed
= true;
681 vlc_mutex_unlock(&sys
->filter
.lock
);
684 void vout_ControlChangeInterlacing(vout_thread_t
*vout
, bool set
)
686 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
688 vlc_mutex_lock(&sys
->filter
.lock
);
689 sys
->filter
.new_interlaced
= set
;
690 vlc_mutex_unlock(&sys
->filter
.lock
);
693 void vout_ControlChangeSubSources(vout_thread_t
*vout
, const char *filters
)
695 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
697 if (likely(sys
->spu
!= NULL
))
698 spu_ChangeSources(sys
->spu
, filters
);
701 void vout_ControlChangeSubFilters(vout_thread_t
*vout
, const char *filters
)
703 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
705 if (likely(sys
->spu
!= NULL
))
706 spu_ChangeFilters(sys
->spu
, filters
);
709 void vout_ChangeSpuChannelMargin(vout_thread_t
*vout
,
710 enum vlc_vout_order order
, int margin
)
712 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
714 if (likely(sys
->spu
!= NULL
))
715 spu_ChangeChannelOrderMargin(sys
->spu
, order
, margin
);
718 void vout_ChangeViewpoint(vout_thread_t
*vout
,
719 const vlc_viewpoint_t
*p_viewpoint
)
721 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
724 vlc_mutex_lock(&sys
->window_lock
);
725 sys
->display_cfg
.viewpoint
= *p_viewpoint
;
726 /* no window size update here */
727 vlc_mutex_unlock(&sys
->window_lock
);
729 vlc_mutex_lock(&sys
->display_lock
);
730 if (sys
->display
!= NULL
)
731 vout_SetDisplayViewpoint(sys
->display
, p_viewpoint
);
732 vlc_mutex_unlock(&sys
->display_lock
);
736 static void VoutGetDisplayCfg(vout_thread_sys_t
*p_vout
, const video_format_t
*fmt
, vout_display_cfg_t
*cfg
)
738 vout_thread_t
*vout
= &p_vout
->obj
;
739 /* Load configuration */
740 cfg
->viewpoint
= fmt
->pose
;
742 const int display_width
= var_GetInteger(vout
, "width");
743 const int display_height
= var_GetInteger(vout
, "height");
744 cfg
->display
.width
= display_width
> 0 ? display_width
: 0;
745 cfg
->display
.height
= display_height
> 0 ? display_height
: 0;
746 cfg
->is_display_filled
= var_GetBool(vout
, "autoscale");
747 unsigned msar_num
, msar_den
;
748 if (var_InheritURational(vout
, &msar_num
, &msar_den
, "monitor-par") ||
749 msar_num
<= 0 || msar_den
<= 0) {
753 cfg
->display
.sar
.num
= msar_num
;
754 cfg
->display
.sar
.den
= msar_den
;
755 unsigned zoom_den
= 1000;
756 unsigned zoom_num
= zoom_den
* var_GetFloat(vout
, "zoom");
757 vlc_ureduce(&zoom_num
, &zoom_den
, zoom_num
, zoom_den
, 0);
758 cfg
->zoom
.num
= zoom_num
;
759 cfg
->zoom
.den
= zoom_den
;
760 cfg
->align
.vertical
= VLC_VIDEO_ALIGN_CENTER
;
761 cfg
->align
.horizontal
= VLC_VIDEO_ALIGN_CENTER
;
762 const int align_mask
= var_GetInteger(vout
, "align");
763 if (align_mask
& VOUT_ALIGN_LEFT
)
764 cfg
->align
.horizontal
= VLC_VIDEO_ALIGN_LEFT
;
765 else if (align_mask
& VOUT_ALIGN_RIGHT
)
766 cfg
->align
.horizontal
= VLC_VIDEO_ALIGN_RIGHT
;
767 if (align_mask
& VOUT_ALIGN_TOP
)
768 cfg
->align
.vertical
= VLC_VIDEO_ALIGN_TOP
;
769 else if (align_mask
& VOUT_ALIGN_BOTTOM
)
770 cfg
->align
.vertical
= VLC_VIDEO_ALIGN_BOTTOM
;
774 static int FilterRestartCallback(vlc_object_t
*p_this
, char const *psz_var
,
775 vlc_value_t oldval
, vlc_value_t newval
,
778 (void) p_this
; (void) psz_var
; (void) oldval
; (void) newval
;
779 vout_ControlChangeFilters((vout_thread_t
*)p_data
, NULL
);
783 static int ThreadDelFilterCallbacks(filter_t
*filter
, void *opaque
)
785 filter_DelProxyCallbacks((vlc_object_t
*)opaque
, filter
,
786 FilterRestartCallback
);
790 static void ThreadDelAllFilterCallbacks(vout_thread_sys_t
*vout
)
792 vout_thread_sys_t
*sys
= vout
;
793 assert(sys
->filter
.chain_interactive
!= NULL
);
794 filter_chain_ForEach(sys
->filter
.chain_interactive
,
795 ThreadDelFilterCallbacks
, vout
);
798 static picture_t
*VoutVideoFilterInteractiveNewPicture(filter_t
*filter
)
800 vout_thread_sys_t
*sys
= filter
->owner
.sys
;
802 picture_t
*picture
= picture_pool_Get(sys
->private.private_pool
);
804 picture_Reset(picture
);
805 video_format_CopyCropAr(&picture
->format
, &filter
->fmt_out
.video
);
810 static picture_t
*VoutVideoFilterStaticNewPicture(filter_t
*filter
)
812 vout_thread_sys_t
*sys
= filter
->owner
.sys
;
814 vlc_mutex_assert(&sys
->filter
.lock
);
815 if (filter_chain_IsEmpty(sys
->filter
.chain_interactive
))
816 // we may be using the last filter of both chains, so we get the picture
817 // from the display module pool, just like for the last interactive filter.
818 return VoutVideoFilterInteractiveNewPicture(filter
);
820 return picture_NewFromFormat(&filter
->fmt_out
.video
);
823 static void ThreadFilterFlush(vout_thread_sys_t
*sys
, bool is_locked
)
825 if (sys
->displayed
.current
)
827 picture_Release( sys
->displayed
.current
);
828 sys
->displayed
.current
= NULL
;
832 vlc_mutex_lock(&sys
->filter
.lock
);
833 filter_chain_VideoFlush(sys
->filter
.chain_static
);
834 filter_chain_VideoFlush(sys
->filter
.chain_interactive
);
836 vlc_mutex_unlock(&sys
->filter
.lock
);
844 static void ThreadChangeFilters(vout_thread_sys_t
*vout
)
846 vout_thread_sys_t
*sys
= vout
;
847 ThreadFilterFlush(vout
, true);
848 ThreadDelAllFilterCallbacks(vout
);
850 vlc_array_t array_static
;
851 vlc_array_t array_interactive
;
853 vlc_array_init(&array_static
);
854 vlc_array_init(&array_interactive
);
856 if (sys
->private.interlacing
.has_deint
)
858 vout_filter_t
*e
= malloc(sizeof(*e
));
862 free(config_ChainCreate(&e
->name
, &e
->cfg
, "deinterlace"));
863 vlc_array_append_or_abort(&array_static
, e
);
867 char *current
= sys
->filter
.configuration
? strdup(sys
->filter
.configuration
) : NULL
;
871 char *next
= config_ChainCreate(&name
, &cfg
, current
);
874 vout_filter_t
*e
= malloc(sizeof(*e
));
879 if (!strcmp(e
->name
, "postproc"))
880 vlc_array_append_or_abort(&array_static
, e
);
882 vlc_array_append_or_abort(&array_interactive
, e
);
886 config_ChainDestroy(cfg
);
891 config_ChainDestroy(cfg
);
898 es_format_t fmt_target
;
899 es_format_InitFromVideo(&fmt_target
, &sys
->filter
.src_fmt
);
900 vlc_video_context
*vctx_target
= sys
->filter
.src_vctx
;
902 const es_format_t
*p_fmt_current
= &fmt_target
;
903 vlc_video_context
*vctx_current
= vctx_target
;
905 for (int a
= 0; a
< 2; a
++) {
906 vlc_array_t
*array
= a
== 0 ? &array_static
:
908 filter_chain_t
*chain
= a
== 0 ? sys
->filter
.chain_static
:
909 sys
->filter
.chain_interactive
;
911 filter_chain_Reset(chain
, p_fmt_current
, vctx_current
, p_fmt_current
);
912 for (size_t i
= 0; i
< vlc_array_count(array
); i
++) {
913 vout_filter_t
*e
= vlc_array_item_at_index(array
, i
);
914 msg_Dbg(&vout
->obj
, "Adding '%s' as %s", e
->name
, a
== 0 ? "static" : "interactive");
915 filter_t
*filter
= filter_chain_AppendFilter(chain
, e
->name
, e
->cfg
,
918 msg_Err(&vout
->obj
, "Failed to add filter '%s'", e
->name
);
919 else if (a
== 1) /* Add callbacks for interactive filters */
920 filter_AddProxyCallbacks(&vout
->obj
, filter
, FilterRestartCallback
);
922 config_ChainDestroy(e
->cfg
);
926 if (!filter_chain_IsEmpty(chain
))
928 p_fmt_current
= filter_chain_GetFmtOut(chain
);
929 vctx_current
= filter_chain_GetVideoCtxOut(chain
);
931 vlc_array_clear(array
);
934 if (!es_format_IsSimilar(p_fmt_current
, &fmt_target
)) {
935 msg_Dbg(&vout
->obj
, "Adding a filter to compensate for format changes");
936 if (filter_chain_AppendConverter(sys
->filter
.chain_interactive
,
938 msg_Err(&vout
->obj
, "Failed to compensate for the format changes, removing all filters");
939 ThreadDelAllFilterCallbacks(vout
);
940 filter_chain_Reset(sys
->filter
.chain_static
, &fmt_target
, vctx_target
, &fmt_target
);
941 filter_chain_Reset(sys
->filter
.chain_interactive
, &fmt_target
, vctx_target
, &fmt_target
);
945 es_format_Clean(&fmt_target
);
947 sys
->filter
.changed
= false;
953 static picture_t
*ThreadDisplayPreparePicture(vout_thread_sys_t
*vout
, bool reuse_decoded
,
954 bool frame_by_frame
, bool *paused
)
956 vout_thread_sys_t
*sys
= vout
;
957 bool is_late_dropped
= sys
->is_late_dropped
&& !frame_by_frame
;
959 vlc_mutex_lock(&sys
->filter
.lock
);
961 picture_t
*picture
= filter_chain_VideoFilter(sys
->filter
.chain_static
, NULL
);
962 assert(!reuse_decoded
|| !picture
);
966 if (unlikely(reuse_decoded
&& sys
->displayed
.decoded
)) {
967 decoded
= picture_Hold(sys
->displayed
.decoded
);
969 decoded
= picture_fifo_Pop(sys
->decoder_fifo
);
972 const vlc_tick_t system_now
= vlc_tick_now();
973 const vlc_tick_t system_pts
=
974 vlc_clock_ConvertToSystem(sys
->clock
, system_now
,
975 decoded
->date
, sys
->rate
);
977 if (system_pts
== INT64_MAX
)
979 /* The clock is paused, notify it (so that the current
980 * picture is displayed but not the next one), this
981 * current picture can't be be late. */
984 else if (is_late_dropped
&& !decoded
->b_force
)
986 const vlc_tick_t prepare_decoded_duration
= vout_chrono_GetHigh(&sys
->render
) +
987 VOUT_MWAIT_TOLERANCE
+
988 vout_chrono_GetHigh(&sys
->static_filter
);
989 vlc_tick_t late
= system_now
+ prepare_decoded_duration
- system_pts
;
991 vlc_tick_t late_threshold
;
992 if (decoded
->format
.i_frame_rate
&& decoded
->format
.i_frame_rate_base
) {
993 late_threshold
= vlc_tick_from_samples(decoded
->format
.i_frame_rate_base
, decoded
->format
.i_frame_rate
);
996 late_threshold
= VOUT_DISPLAY_LATE_THRESHOLD
;
997 if (late
> late_threshold
) {
998 msg_Warn(&vout
->obj
, "picture is too late to be displayed (missing %"PRId64
" ms)", MS_FROM_VLC_TICK(late
));
999 picture_Release(decoded
);
1000 vout_statistic_AddLost(&sys
->statistic
, 1);
1004 vlc_video_context
*pic_vctx
= picture_GetVideoContext(decoded
);
1005 if (!VideoFormatIsCropArEqual(&decoded
->format
, &sys
->filter
.src_fmt
))
1007 // we received an aspect ratio change
1008 // Update the filters with the filter source format with the new aspect ratio
1009 video_format_Clean(&sys
->filter
.src_fmt
);
1010 video_format_Copy(&sys
->filter
.src_fmt
, &decoded
->format
);
1011 if (sys
->filter
.src_vctx
)
1012 vlc_video_context_Release(sys
->filter
.src_vctx
);
1013 sys
->filter
.src_vctx
= pic_vctx
? vlc_video_context_Hold(pic_vctx
) : NULL
;
1015 ThreadChangeFilters(vout
);
1022 reuse_decoded
= false;
1024 if (sys
->displayed
.decoded
)
1025 picture_Release(sys
->displayed
.decoded
);
1027 sys
->displayed
.decoded
= picture_Hold(decoded
);
1028 sys
->displayed
.timestamp
= decoded
->date
;
1029 sys
->displayed
.is_interlaced
= !decoded
->b_progressive
;
1031 vout_chrono_Start(&sys
->static_filter
);
1032 picture
= filter_chain_VideoFilter(sys
->filter
.chain_static
, sys
->displayed
.decoded
);
1033 vout_chrono_Stop(&sys
->static_filter
);
1036 vlc_mutex_unlock(&sys
->filter
.lock
);
1041 static vlc_decoder_device
* VoutHoldDecoderDevice(vlc_object_t
*o
, void *opaque
)
1044 vout_thread_sys_t
*sys
= opaque
;
1045 return sys
->dec_device
? vlc_decoder_device_Hold( sys
->dec_device
) : NULL
;
1048 static const struct filter_video_callbacks vout_video_cbs
= {
1049 NULL
, VoutHoldDecoderDevice
,
1052 static picture_t
*ConvertRGB32AndBlend(vout_thread_sys_t
*vout
, picture_t
*pic
,
1053 subpicture_t
*subpic
)
1055 vout_thread_sys_t
*sys
= vout
;
1056 /* This function will convert the pic to RGB32 and blend the subpic to it.
1057 * The returned pic can't be used to display since the chroma will be
1058 * different than the "vout display" one, but it can be used for snapshots.
1061 assert(sys
->spu_blend
);
1063 filter_owner_t owner
= {
1064 .video
= &vout_video_cbs
,
1067 filter_chain_t
*filterc
= filter_chain_NewVideo(&vout
->obj
, false, &owner
);
1071 es_format_t src
= sys
->spu_blend
->fmt_out
;
1072 es_format_t dst
= src
;
1073 dst
.video
.i_chroma
= VLC_CODEC_RGB32
;
1074 video_format_FixRgb(&dst
.video
);
1076 filter_chain_Reset(filterc
, &src
,
1077 NULL
/* TODO output video context of blender */,
1080 if (filter_chain_AppendConverter(filterc
, &dst
) != 0)
1082 filter_chain_Delete(filterc
);
1087 pic
= filter_chain_VideoFilter(filterc
, pic
);
1088 filter_chain_Delete(filterc
);
1092 vlc_blender_t
*swblend
= filter_NewBlend(VLC_OBJECT(&vout
->obj
), &dst
.video
);
1095 bool success
= picture_BlendSubpicture(pic
, swblend
, subpic
) > 0;
1096 filter_DeleteBlend(swblend
);
1100 picture_Release(pic
);
1105 static int ThreadDisplayRenderPicture(vout_thread_sys_t
*vout
, bool render_now
)
1107 vout_thread_sys_t
*sys
= vout
;
1109 // hold it as the filter chain will release it or return it and we release it
1110 picture_Hold(sys
->displayed
.current
);
1112 vout_chrono_Start(&sys
->render
);
1114 vlc_mutex_lock(&sys
->filter
.lock
);
1115 picture_t
*filtered
= filter_chain_VideoFilter(sys
->filter
.chain_interactive
, sys
->displayed
.current
);
1116 vlc_mutex_unlock(&sys
->filter
.lock
);
1119 return VLC_EGENERIC
;
1121 if (filtered
->date
!= sys
->displayed
.current
->date
)
1122 msg_Warn(&vout
->obj
, "Unsupported timestamp modifications done by chain_interactive");
1124 vout_display_t
*vd
= sys
->display
;
1126 vlc_mutex_lock(&sys
->display_lock
);
1129 * Get the rendering date for the current subpicture to be displayed.
1131 vlc_tick_t system_now
= vlc_tick_now();
1132 vlc_tick_t render_subtitle_date
;
1133 if (sys
->pause
.is_on
)
1134 render_subtitle_date
= sys
->pause
.date
;
1137 render_subtitle_date
= filtered
->date
<= VLC_TICK_0
? system_now
:
1138 vlc_clock_ConvertToSystem(sys
->clock
, system_now
, filtered
->date
,
1141 /* The clock is paused, it's too late to fallback to the previous
1142 * picture, display the current picture anyway and force the rendering
1144 if (unlikely(render_subtitle_date
== INT64_MAX
))
1146 render_subtitle_date
= system_now
;
1152 * Check whether we let the display draw the subpicture itself (when
1153 * do_dr_spu=true), and if we can fallback to blending the subpicture
1154 * ourselves (do_early_spu=true).
1156 const bool do_snapshot
= vout_snapshot_IsRequested(sys
->snapshot
);
1157 const bool do_dr_spu
= !do_snapshot
&&
1158 vd
->info
.subpicture_chromas
&&
1159 *vd
->info
.subpicture_chromas
!= 0;
1161 //FIXME: Denying do_early_spu if vd->source->orientation != ORIENT_NORMAL
1162 //will have the effect that snapshots miss the subpictures. We do this
1163 //because there is currently no way to transform subpictures to match
1164 //the source format.
1165 const bool do_early_spu
= !do_dr_spu
&&
1166 vd
->source
->orientation
== ORIENT_NORMAL
;
1168 const vlc_fourcc_t
*subpicture_chromas
;
1169 video_format_t fmt_spu
;
1171 vout_display_place_t place
;
1172 vout_display_PlacePicture(&place
, vd
->source
, vd
->cfg
);
1174 fmt_spu
= *vd
->source
;
1175 if (fmt_spu
.i_width
* fmt_spu
.i_height
< place
.width
* place
.height
) {
1176 fmt_spu
.i_sar_num
= vd
->cfg
->display
.sar
.num
;
1177 fmt_spu
.i_sar_den
= vd
->cfg
->display
.sar
.den
;
1179 fmt_spu
.i_visible_width
= place
.width
;
1181 fmt_spu
.i_visible_height
= place
.height
;
1183 subpicture_chromas
= vd
->info
.subpicture_chromas
;
1186 fmt_spu
= *vd
->source
;
1189 fmt_spu
.i_sar_num
= vd
->cfg
->display
.sar
.num
;
1190 fmt_spu
.i_sar_den
= vd
->cfg
->display
.sar
.den
;
1192 subpicture_chromas
= NULL
;
1194 if (sys
->spu_blend
&&
1195 sys
->spu_blend
->fmt_out
.video
.i_chroma
!= fmt_spu
.i_chroma
) {
1196 filter_DeleteBlend(sys
->spu_blend
);
1197 sys
->spu_blend
= NULL
;
1198 sys
->spu_blend_chroma
= 0;
1200 if (!sys
->spu_blend
&& sys
->spu_blend_chroma
!= fmt_spu
.i_chroma
) {
1201 sys
->spu_blend_chroma
= fmt_spu
.i_chroma
;
1202 sys
->spu_blend
= filter_NewBlend(VLC_OBJECT(&vout
->obj
), &fmt_spu
);
1203 if (!sys
->spu_blend
)
1204 msg_Err(&vout
->obj
, "Failed to create blending filter, OSD/Subtitles will not work");
1208 /* Get the subpicture to be displayed. */
1209 video_format_t fmt_spu_rot
;
1210 video_format_ApplyRotation(&fmt_spu_rot
, &fmt_spu
);
1211 subpicture_t
*subpic
= !sys
->spu
? NULL
:
1212 spu_Render(sys
->spu
,
1213 subpicture_chromas
, &fmt_spu_rot
,
1214 vd
->source
, system_now
,
1215 render_subtitle_date
,
1216 do_snapshot
, vd
->info
.can_scale_spu
);
1221 * - be sure to end up with a direct buffer.
1222 * - blend subtitles, and in a fast access buffer
1224 picture_t
*todisplay
= filtered
;
1225 picture_t
*snap_pic
= todisplay
;
1226 if (do_early_spu
&& subpic
) {
1227 if (sys
->spu_blend
) {
1228 picture_t
*blent
= picture_pool_Get(sys
->private.private_pool
);
1230 video_format_CopyCropAr(&blent
->format
, &filtered
->format
);
1231 picture_Copy(blent
, filtered
);
1232 if (picture_BlendSubpicture(blent
, sys
->spu_blend
, subpic
)) {
1233 picture_Release(todisplay
);
1234 snap_pic
= todisplay
= blent
;
1237 /* Blending failed, likely because the picture is opaque or
1238 * read-only. Try to convert the opaque picture to a
1239 * software RGB32 one before blending it. */
1242 picture_t
*copy
= ConvertRGB32AndBlend(vout
, blent
, subpic
);
1246 picture_Release(blent
);
1250 subpicture_Delete(subpic
);
1255 * Take a snapshot if requested
1260 vout_snapshot_Set(sys
->snapshot
, vd
->source
, snap_pic
);
1261 if (snap_pic
!= todisplay
)
1262 picture_Release(snap_pic
);
1265 /* Render the direct buffer */
1266 vout_UpdateDisplaySourceProperties(vd
, &todisplay
->format
, &sys
->source
.dar
);
1268 todisplay
= vout_ConvertForDisplay(vd
, todisplay
);
1269 if (todisplay
== NULL
) {
1270 vlc_mutex_unlock(&sys
->display_lock
);
1273 subpicture_Delete(subpic
);
1274 return VLC_EGENERIC
;
1277 if (!do_dr_spu
&& sys
->spu_blend
!= NULL
&& subpic
!= NULL
)
1278 picture_BlendSubpicture(todisplay
, sys
->spu_blend
, subpic
);
1280 system_now
= vlc_tick_now();
1281 const vlc_tick_t pts
= todisplay
->date
;
1282 vlc_tick_t system_pts
= render_now
? system_now
:
1283 vlc_clock_ConvertToSystem(sys
->clock
, system_now
, pts
, sys
->rate
);
1284 if (unlikely(system_pts
== INT64_MAX
))
1286 /* The clock is paused, it's too late to fallback to the previous
1287 * picture, display the current picture anyway and force the rendering
1289 system_pts
= system_now
;
1293 const unsigned frame_rate
= todisplay
->format
.i_frame_rate
;
1294 const unsigned frame_rate_base
= todisplay
->format
.i_frame_rate_base
;
1296 if (vd
->ops
->prepare
!= NULL
)
1297 vd
->ops
->prepare(vd
, todisplay
, do_dr_spu
? subpic
: NULL
, system_pts
);
1299 vout_chrono_Stop(&sys
->render
);
1303 if (((i
++)%10) == 0)
1304 msg_Info(&vout
->obj
, "render: avg %d ms var %d ms",
1305 (int)(sys
->render
.avg
/1000), (int)(sys
->render
.var
/1000));
1309 system_now
= vlc_tick_now();
1312 const vlc_tick_t late
= system_now
- system_pts
;
1313 if (unlikely(late
> 0))
1315 msg_Dbg(vd
, "picture displayed late (missing %"PRId64
" ms)", MS_FROM_VLC_TICK(late
));
1316 vout_statistic_AddLate(&sys
->statistic
, 1);
1318 /* vd->prepare took too much time. Tell the clock that the pts was
1320 system_pts
= system_now
;
1322 else if (vd
->ops
->display
!= NULL
)
1324 /* Wait to reach system_pts if the plugin doesn't handle
1325 * asynchronous display */
1326 vlc_clock_Wait(sys
->clock
, system_now
, pts
, sys
->rate
,
1327 VOUT_REDISPLAY_DELAY
);
1329 /* Don't touch system_pts. Tell the clock that the pts was rendered
1330 * at the expected date */
1332 sys
->displayed
.date
= system_pts
;
1336 sys
->displayed
.date
= system_now
;
1337 /* Tell the clock that the pts was forced */
1338 system_pts
= INT64_MAX
;
1340 vlc_clock_UpdateVideo(sys
->clock
, system_pts
, pts
, sys
->rate
,
1341 frame_rate
, frame_rate_base
);
1343 /* Display the direct buffer returned by vout_RenderPicture */
1344 vout_display_Display(vd
, todisplay
);
1345 vlc_mutex_unlock(&sys
->display_lock
);
1348 subpicture_Delete(subpic
);
1350 vout_statistic_AddDisplayed(&sys
->statistic
, 1);
1355 static int ThreadDisplayPicture(vout_thread_sys_t
*vout
, vlc_tick_t
*deadline
)
1357 vout_thread_sys_t
*sys
= vout
;
1358 bool frame_by_frame
= !deadline
;
1359 bool paused
= sys
->pause
.is_on
;
1363 vlc_mutex_lock(&sys
->filter
.lock
);
1364 if (sys
->filter
.changed
||
1365 sys
->private.interlacing
.has_deint
!= sys
->filter
.new_interlaced
)
1367 sys
->private.interlacing
.has_deint
= sys
->filter
.new_interlaced
;
1368 ThreadChangeFilters(vout
);
1370 vlc_mutex_unlock(&sys
->filter
.lock
);
1372 bool render_now
= true;
1377 ThreadDisplayPreparePicture(vout
, !sys
->displayed
.current
, true, &paused
);
1381 if (likely(sys
->displayed
.current
!= NULL
))
1382 picture_Release(sys
->displayed
.current
);
1383 sys
->displayed
.current
= next
;
1386 if (!sys
->displayed
.current
)
1387 return VLC_EGENERIC
;
1391 const vlc_tick_t system_now
= vlc_tick_now();
1392 const vlc_tick_t render_delay
= vout_chrono_GetHigh(&sys
->render
) + VOUT_MWAIT_TOLERANCE
;
1393 const bool first
= !sys
->displayed
.current
;
1395 bool dropped_current_frame
= false;
1397 /* FIXME/XXX we must redisplay the last decoded picture (because
1398 * of potential vout updated, or filters update or SPU update)
1399 * For now a high update period is needed but it could be removed
1401 * - vout module emits events from themselves.
1402 * - *and* SPU is modified to emit an event or a deadline when needed.
1404 * So it will be done later.
1406 bool refresh
= false;
1408 vlc_tick_t date_refresh
= VLC_TICK_INVALID
;
1410 picture_t
*next
= NULL
;
1414 ThreadDisplayPreparePicture(vout
, true, false, &paused
);
1417 *deadline
= VLC_TICK_INVALID
;
1418 return VLC_EGENERIC
; // wait with no known deadline
1423 const vlc_tick_t next_system_pts
=
1424 vlc_clock_ConvertToSystem(sys
->clock
, system_now
,
1425 sys
->displayed
.current
->date
, sys
->rate
);
1426 if (likely(next_system_pts
!= INT64_MAX
))
1428 vlc_tick_t date_next
= next_system_pts
- render_delay
;
1429 if (date_next
<= system_now
)
1431 // the current frame will be late, look for the next not late one
1433 ThreadDisplayPreparePicture(vout
, false, false, &paused
);
1440 const vlc_tick_t swap_next_pts
=
1441 vlc_clock_ConvertToSystem(sys
->clock
, vlc_tick_now(),
1442 next
->date
, sys
->rate
);
1443 if (likely(swap_next_pts
!= INT64_MAX
))
1444 date_refresh
= swap_next_pts
- render_delay
;
1446 // next frame will still need some waiting before display
1447 dropped_current_frame
= sys
->displayed
.current
!= NULL
;
1450 if (likely(dropped_current_frame
))
1451 picture_Release(sys
->displayed
.current
);
1452 sys
->displayed
.current
= next
;
1454 else if (likely(sys
->displayed
.date
!= VLC_TICK_INVALID
))
1456 // next date we need to display again the current picture
1457 date_refresh
= sys
->displayed
.date
+ VOUT_REDISPLAY_DELAY
- render_delay
;
1458 refresh
= date_refresh
<= system_now
;
1459 render_now
= refresh
;
1462 if (date_refresh
!= VLC_TICK_INVALID
)
1463 *deadline
= date_refresh
;
1465 if (!first
&& !refresh
&& !dropped_current_frame
) {
1466 // nothing changed, wait until the next deadline or a control
1467 return VLC_EGENERIC
;
1470 /* display the picture immediately */
1471 render_now
|= sys
->displayed
.current
->b_force
;
1474 int ret
= ThreadDisplayRenderPicture(vout
, render_now
);
1475 return render_now
? VLC_EGENERIC
: ret
;
1478 void vout_ChangePause(vout_thread_t
*vout
, bool is_paused
, vlc_tick_t date
)
1480 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
1481 assert(!sys
->dummy
);
1483 vout_control_Hold(&sys
->control
);
1484 assert(!sys
->pause
.is_on
|| !is_paused
);
1486 if (sys
->pause
.is_on
)
1487 ThreadFilterFlush(sys
, false);
1489 sys
->step
.timestamp
= VLC_TICK_INVALID
;
1490 sys
->step
.last
= VLC_TICK_INVALID
;
1492 sys
->pause
.is_on
= is_paused
;
1493 sys
->pause
.date
= date
;
1494 vout_control_Release(&sys
->control
);
1496 vlc_mutex_lock(&sys
->window_lock
);
1497 vout_window_SetInhibition(sys
->display_cfg
.window
, !is_paused
);
1498 vlc_mutex_unlock(&sys
->window_lock
);
1501 static void vout_FlushUnlocked(vout_thread_sys_t
*vout
, bool below
,
1504 vout_thread_sys_t
*sys
= vout
;
1506 sys
->step
.timestamp
= VLC_TICK_INVALID
;
1507 sys
->step
.last
= VLC_TICK_INVALID
;
1509 ThreadFilterFlush(vout
, false); /* FIXME too much */
1511 picture_t
*last
= sys
->displayed
.decoded
;
1513 if ((date
== VLC_TICK_INVALID
) ||
1514 ( below
&& last
->date
<= date
) ||
1515 (!below
&& last
->date
>= date
)) {
1516 picture_Release(last
);
1518 sys
->displayed
.decoded
= NULL
;
1519 sys
->displayed
.date
= VLC_TICK_INVALID
;
1520 sys
->displayed
.timestamp
= VLC_TICK_INVALID
;
1524 picture_fifo_Flush(sys
->decoder_fifo
, date
, below
);
1526 vlc_mutex_lock(&sys
->display_lock
);
1527 if (sys
->display
!= NULL
)
1528 vout_FilterFlush(sys
->display
);
1529 vlc_mutex_unlock(&sys
->display_lock
);
1531 if (sys
->clock
!= NULL
)
1533 vlc_clock_Reset(sys
->clock
);
1534 vlc_clock_SetDelay(sys
->clock
, sys
->delay
);
1538 void vout_Flush(vout_thread_t
*vout
, vlc_tick_t date
)
1540 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
1541 assert(!sys
->dummy
);
1543 vout_control_Hold(&sys
->control
);
1544 vout_FlushUnlocked(sys
, false, date
);
1545 vout_control_Release(&sys
->control
);
1548 void vout_NextPicture(vout_thread_t
*vout
, vlc_tick_t
*duration
)
1550 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
1551 assert(!sys
->dummy
);
1554 vout_control_Hold(&sys
->control
);
1555 if (sys
->step
.last
== VLC_TICK_INVALID
)
1556 sys
->step
.last
= sys
->displayed
.timestamp
;
1558 if (ThreadDisplayPicture(sys
, NULL
) == 0) {
1559 sys
->step
.timestamp
= sys
->displayed
.timestamp
;
1561 if (sys
->step
.last
!= VLC_TICK_INVALID
&&
1562 sys
->step
.timestamp
> sys
->step
.last
) {
1563 *duration
= sys
->step
.timestamp
- sys
->step
.last
;
1564 sys
->step
.last
= sys
->step
.timestamp
;
1565 /* TODO advance subpicture by the duration ... */
1568 vout_control_Release(&sys
->control
);
1571 void vout_ChangeDelay(vout_thread_t
*vout
, vlc_tick_t delay
)
1573 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
1574 assert(!sys
->dummy
);
1575 assert(sys
->display
);
1577 vout_control_Hold(&sys
->control
);
1578 vlc_clock_SetDelay(sys
->clock
, delay
);
1580 vout_control_Release(&sys
->control
);
1583 void vout_ChangeRate(vout_thread_t
*vout
, float rate
)
1585 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
1586 assert(!sys
->dummy
);
1588 vout_control_Hold(&sys
->control
);
1590 vout_control_Release(&sys
->control
);
1593 void vout_ChangeSpuDelay(vout_thread_t
*vout
, size_t channel_id
,
1596 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
1597 assert(!sys
->dummy
);
1599 spu_SetClockDelay(sys
->spu
, channel_id
, delay
);
1602 void vout_ChangeSpuRate(vout_thread_t
*vout
, size_t channel_id
, float rate
)
1604 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
1605 assert(!sys
->dummy
);
1607 spu_SetClockRate(sys
->spu
, channel_id
, rate
);
1610 static void ThreadProcessMouseState(vout_thread_sys_t
*p_vout
,
1611 const vlc_mouse_t
*win_mouse
)
1613 vlc_mouse_t tmp1
, tmp2
;
1614 const vlc_mouse_t
*m
;
1615 vout_thread_t
*vout
= &p_vout
->obj
;
1616 vout_thread_sys_t
*sys
= p_vout
;
1618 /* pass mouse coordinates in the filter chains. */
1620 vlc_mutex_lock(&sys
->filter
.lock
);
1621 if (sys
->filter
.chain_static
&& sys
->filter
.chain_interactive
) {
1622 if (!filter_chain_MouseFilter(sys
->filter
.chain_interactive
,
1625 if (!filter_chain_MouseFilter(sys
->filter
.chain_static
,
1629 vlc_mutex_unlock(&sys
->filter
.lock
);
1631 if (vlc_mouse_HasMoved(&sys
->mouse
, m
))
1632 var_SetCoords(vout
, "mouse-moved", m
->i_x
, m
->i_y
);
1634 if (vlc_mouse_HasButton(&sys
->mouse
, m
))
1635 var_SetInteger(vout
, "mouse-button-down", m
->i_pressed
);
1637 if (m
->b_double_click
)
1638 var_ToggleBool(vout
, "fullscreen");
1641 if (sys
->mouse_event
)
1642 sys
->mouse_event(m
, sys
->mouse_opaque
);
1645 static int vout_Start(vout_thread_sys_t
*vout
, vlc_video_context
*vctx
, const vout_configuration_t
*cfg
)
1647 vout_thread_sys_t
*sys
= vout
;
1648 assert(!sys
->dummy
);
1650 sys
->mouse_event
= cfg
->mouse_event
;
1651 sys
->mouse_opaque
= cfg
->mouse_opaque
;
1652 vlc_mouse_Init(&sys
->mouse
);
1654 sys
->decoder_fifo
= picture_fifo_New();
1655 sys
->private.display_pool
= NULL
;
1656 sys
->private.private_pool
= NULL
;
1658 sys
->filter
.configuration
= NULL
;
1659 video_format_Copy(&sys
->filter
.src_fmt
, &sys
->original
);
1660 sys
->filter
.src_vctx
= vctx
? vlc_video_context_Hold(vctx
) : NULL
;
1662 static const struct filter_video_callbacks static_cbs
= {
1663 VoutVideoFilterStaticNewPicture
, VoutHoldDecoderDevice
,
1665 static const struct filter_video_callbacks interactive_cbs
= {
1666 VoutVideoFilterInteractiveNewPicture
, VoutHoldDecoderDevice
,
1668 filter_owner_t owner
= {
1669 .video
= &static_cbs
,
1672 sys
->filter
.chain_static
= filter_chain_NewVideo(&vout
->obj
, true, &owner
);
1674 owner
.video
= &interactive_cbs
;
1675 sys
->filter
.chain_interactive
= filter_chain_NewVideo(&vout
->obj
, true, &owner
);
1677 vout_display_cfg_t dcfg
;
1678 struct vout_crop crop
;
1681 vlc_mutex_lock(&sys
->window_lock
);
1685 // make sure the decoder device we receive matches the one we have cached
1686 vlc_decoder_device
*dec_device
= vlc_video_context_HoldDevice(vctx
);
1687 assert(dec_device
&& dec_device
== sys
->dec_device
);
1688 vlc_decoder_device_Release(dec_device
);
1692 dcfg
= sys
->display_cfg
;
1693 crop
= sys
->source
.crop
;
1694 num
= sys
->source
.dar
.num
;
1695 den
= sys
->source
.dar
.den
;
1696 vlc_mutex_lock(&sys
->display_lock
);
1697 vlc_mutex_unlock(&sys
->window_lock
);
1699 /* Setup the window size, protected by the display_lock */
1700 dcfg
.window_props
.width
= sys
->window_width
;
1701 dcfg
.window_props
.height
= sys
->window_height
;
1703 sys
->display
= vout_OpenWrapper(&vout
->obj
, &sys
->private, sys
->splitter_name
, &dcfg
,
1704 &sys
->original
, vctx
);
1705 if (sys
->display
== NULL
) {
1706 vlc_mutex_unlock(&sys
->display_lock
);
1710 vout_SetDisplayCrop(sys
->display
, &crop
);
1712 if (num
!= 0 && den
!= 0)
1713 vout_SetDisplayAspect(sys
->display
, num
, den
);
1714 vlc_mutex_unlock(&sys
->display_lock
);
1716 assert(sys
->private.display_pool
!= NULL
&& sys
->private.private_pool
!= NULL
);
1718 sys
->displayed
.current
= NULL
;
1719 sys
->displayed
.decoded
= NULL
;
1720 sys
->displayed
.date
= VLC_TICK_INVALID
;
1721 sys
->displayed
.timestamp
= VLC_TICK_INVALID
;
1722 sys
->displayed
.is_interlaced
= false;
1724 sys
->step
.last
= VLC_TICK_INVALID
;
1725 sys
->step
.timestamp
= VLC_TICK_INVALID
;
1727 sys
->pause
.is_on
= false;
1728 sys
->pause
.date
= VLC_TICK_INVALID
;
1730 sys
->spu_blend_chroma
= 0;
1731 sys
->spu_blend
= NULL
;
1733 video_format_Print(VLC_OBJECT(&vout
->obj
), "original format", &sys
->original
);
1736 if (sys
->filter
.chain_interactive
!= NULL
)
1738 ThreadDelAllFilterCallbacks(vout
);
1739 filter_chain_Delete(sys
->filter
.chain_interactive
);
1741 if (sys
->filter
.chain_static
!= NULL
)
1742 filter_chain_Delete(sys
->filter
.chain_static
);
1743 video_format_Clean(&sys
->filter
.src_fmt
);
1744 if (sys
->filter
.src_vctx
)
1746 vlc_video_context_Release(sys
->filter
.src_vctx
);
1747 sys
->filter
.src_vctx
= NULL
;
1749 if (sys
->decoder_fifo
!= NULL
)
1751 picture_fifo_Delete(sys
->decoder_fifo
);
1752 sys
->decoder_fifo
= NULL
;
1754 return VLC_EGENERIC
;
1757 /*****************************************************************************
1758 * Thread: video output thread
1759 *****************************************************************************
1760 * Video output thread. This function does only returns when the thread is
1761 * terminated. It handles the pictures arriving in the video heap and the
1762 * display device events.
1763 *****************************************************************************/
1764 static void *Thread(void *object
)
1766 vout_thread_sys_t
*vout
= object
;
1767 vout_thread_sys_t
*sys
= vout
;
1769 vlc_tick_t deadline
= VLC_TICK_INVALID
;
1775 const vlc_tick_t max_deadline
= vlc_tick_now() + VLC_TICK_FROM_MS(100);
1776 deadline
= deadline
== VLC_TICK_INVALID
? max_deadline
: __MIN(deadline
, max_deadline
);
1778 deadline
= VLC_TICK_INVALID
;
1781 vlc_mouse_t video_mouse
;
1782 while (vout_control_Pop(&sys
->control
, &video_mouse
, deadline
) == VLC_SUCCESS
) {
1783 if (atomic_load(&sys
->control_is_terminated
))
1785 ThreadProcessMouseState(vout
, &video_mouse
);
1788 if (atomic_load(&sys
->control_is_terminated
))
1791 wait
= ThreadDisplayPicture(vout
, &deadline
) != VLC_SUCCESS
;
1793 if (atomic_load(&sys
->control_is_terminated
))
1796 const bool picture_interlaced
= sys
->displayed
.is_interlaced
;
1798 vout_SetInterlacingState(&vout
->obj
, &sys
->private, picture_interlaced
);
1803 static void vout_ReleaseDisplay(vout_thread_sys_t
*vout
)
1805 vout_thread_sys_t
*sys
= vout
;
1807 assert(sys
->display
!= NULL
);
1809 if (sys
->spu_blend
!= NULL
)
1810 filter_DeleteBlend(sys
->spu_blend
);
1812 /* Destroy the rendering display */
1813 if (sys
->private.display_pool
!= NULL
)
1814 vout_FlushUnlocked(vout
, true, INT64_MAX
);
1816 vlc_mutex_lock(&sys
->display_lock
);
1817 vout_CloseWrapper(&vout
->obj
, &sys
->private, sys
->display
);
1818 sys
->display
= NULL
;
1819 vlc_mutex_unlock(&sys
->display_lock
);
1821 /* Destroy the video filters */
1822 ThreadDelAllFilterCallbacks(vout
);
1823 filter_chain_Delete(sys
->filter
.chain_interactive
);
1824 filter_chain_Delete(sys
->filter
.chain_static
);
1825 video_format_Clean(&sys
->filter
.src_fmt
);
1826 if (sys
->filter
.src_vctx
)
1828 vlc_video_context_Release(sys
->filter
.src_vctx
);
1829 sys
->filter
.src_vctx
= NULL
;
1831 free(sys
->filter
.configuration
);
1833 if (sys
->decoder_fifo
!= NULL
)
1835 picture_fifo_Delete(sys
->decoder_fifo
);
1836 sys
->decoder_fifo
= NULL
;
1838 assert(sys
->private.display_pool
== NULL
);
1840 if (sys
->mouse_event
)
1842 sys
->mouse_event(NULL
, sys
->mouse_opaque
);
1843 sys
->mouse_event
= NULL
;
1847 spu_Detach(sys
->spu
);
1849 video_format_Clean(&sys
->original
);
1852 void vout_StopDisplay(vout_thread_t
*vout
)
1854 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
1856 atomic_store(&sys
->control_is_terminated
, true);
1857 // wake up so it goes back to the loop that will detect the terminated state
1858 vout_control_Wake(&sys
->control
);
1859 vlc_join(sys
->thread
, NULL
);
1861 vout_ReleaseDisplay(sys
);
1864 static void vout_DisableWindow(vout_thread_sys_t
*sys
)
1866 vlc_mutex_lock(&sys
->window_lock
);
1867 if (sys
->window_enabled
) {
1868 vout_window_Disable(sys
->display_cfg
.window
);
1869 sys
->window_enabled
= false;
1871 vlc_mutex_unlock(&sys
->window_lock
);
1874 void vout_Stop(vout_thread_t
*vout
)
1876 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
1877 assert(!sys
->dummy
);
1879 if (sys
->display
!= NULL
)
1880 vout_StopDisplay(vout
);
1882 vout_DisableWindow(sys
);
1885 void vout_Close(vout_thread_t
*vout
)
1889 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
1890 assert(!sys
->dummy
);
1892 if (sys
->display
!= NULL
)
1895 vout_IntfDeinit(VLC_OBJECT(vout
));
1896 vout_snapshot_End(sys
->snapshot
);
1897 vout_chrono_Clean(&sys
->render
);
1898 vout_chrono_Clean(&sys
->static_filter
);
1901 spu_Destroy(sys
->spu
);
1906 void vout_Release(vout_thread_t
*vout
)
1908 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
1910 if (!vlc_atomic_rc_dec(&sys
->rc
))
1915 vlc_object_delete(VLC_OBJECT(vout
));
1919 free(sys
->splitter_name
);
1921 if (sys
->dec_device
)
1922 vlc_decoder_device_Release(sys
->dec_device
);
1924 assert(!sys
->window_enabled
);
1925 vout_display_window_Delete(sys
->display_cfg
.window
);
1927 vout_control_Clean(&sys
->control
);
1930 vout_statistic_Clean(&sys
->statistic
);
1933 vout_snapshot_Destroy(sys
->snapshot
);
1934 video_format_Clean(&sys
->original
);
1935 vlc_object_delete(VLC_OBJECT(vout
));
1938 static vout_thread_sys_t
*vout_CreateCommon(vlc_object_t
*object
)
1940 /* Allocate descriptor */
1941 vout_thread_sys_t
*vout
= vlc_custom_create(object
,
1947 vout_CreateVars(&vout
->obj
);
1949 vout_thread_sys_t
*sys
= vout
;
1950 vlc_atomic_rc_init(&sys
->rc
);
1955 vout_thread_t
*vout_CreateDummy(vlc_object_t
*object
)
1957 vout_thread_sys_t
*vout
= vout_CreateCommon(object
);
1961 vout_thread_sys_t
*sys
= vout
;
1966 vout_thread_t
*vout_Create(vlc_object_t
*object
)
1968 vout_thread_sys_t
*p_vout
= vout_CreateCommon(object
);
1971 vout_thread_t
*vout
= &p_vout
->obj
;
1972 vout_thread_sys_t
*sys
= p_vout
;
1975 /* Register the VLC variable and callbacks. On the one hand, the variables
1976 * must be ready early on because further initializations below depend on
1977 * some of them. On the other hand, the callbacks depend on said
1978 * initializations. In practice, this works because the object is not
1979 * visible and callbacks not triggerable before this function returns the
1980 * fully initialized object to its caller.
1982 vout_IntfInit(vout
);
1984 /* Get splitter name if present */
1985 sys
->splitter_name
= config_GetType("video-splitter") ?
1986 var_InheritString(vout
, "video-splitter") : NULL
;
1987 if (sys
->splitter_name
!= NULL
) {
1988 var_Create(vout
, "window", VLC_VAR_STRING
);
1989 var_SetString(vout
, "window", "wdummy");
1992 sys
->original
.i_chroma
= 0;
1993 sys
->source
.dar
.num
= 0;
1994 sys
->source
.dar
.den
= 0;
1995 sys
->source
.crop
.mode
= VOUT_CROP_NONE
;
1996 sys
->snapshot
= vout_snapshot_New();
1997 vout_statistic_Init(&sys
->statistic
);
1999 /* Initialize subpicture unit */
2000 sys
->spu
= var_InheritBool(vout
, "spu") || var_InheritBool(vout
, "osd") ?
2001 spu_Create(vout
, vout
) : NULL
;
2003 vout_control_Init(&sys
->control
);
2004 atomic_init(&sys
->control_is_terminated
, false);
2006 sys
->title
.show
= var_InheritBool(vout
, "video-title-show");
2007 sys
->title
.timeout
= var_InheritInteger(vout
, "video-title-timeout");
2008 sys
->title
.position
= var_InheritInteger(vout
, "video-title-position");
2010 vout_InitInterlacingSupport(vout
, &sys
->private);
2012 sys
->is_late_dropped
= var_InheritBool(vout
, "drop-late-frames");
2014 vlc_mutex_init(&sys
->filter
.lock
);
2017 sys
->display
= NULL
;
2018 vlc_mutex_init(&sys
->display_lock
);
2021 sys
->window_width
= sys
->window_height
= 0;
2022 sys
->display_cfg
.window
= vout_display_window_New(vout
);
2023 if (sys
->display_cfg
.window
== NULL
) {
2025 spu_Destroy(sys
->spu
);
2026 vlc_object_delete(vout
);
2030 if (sys
->splitter_name
!= NULL
)
2031 var_Destroy(vout
, "window");
2032 sys
->window_enabled
= false;
2033 vlc_mutex_init(&sys
->window_lock
);
2035 /* Arbitrary initial time */
2036 vout_chrono_Init(&sys
->render
, 5, VLC_TICK_FROM_MS(10));
2037 vout_chrono_Init(&sys
->static_filter
, 4, VLC_TICK_FROM_MS(0));
2039 if (var_InheritBool(vout
, "video-wallpaper"))
2040 vout_window_SetState(sys
->display_cfg
.window
, VOUT_WINDOW_STATE_BELOW
);
2041 else if (var_InheritBool(vout
, "video-on-top"))
2042 vout_window_SetState(sys
->display_cfg
.window
, VOUT_WINDOW_STATE_ABOVE
);
2047 vout_thread_t
*vout_Hold( vout_thread_t
*vout
)
2049 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
2051 vlc_atomic_rc_inc(&sys
->rc
);
2055 int vout_ChangeSource( vout_thread_t
*vout
, const video_format_t
*original
)
2057 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
2059 /* TODO: If dimensions are equal or slightly smaller, update the aspect
2060 * ratio and crop settings, instead of recreating a display.
2062 if (video_format_IsSimilar(original
, &sys
->original
)) {
2063 /* It is assumed that the SPU input matches input already. */
2070 static int EnableWindowLocked(vout_thread_sys_t
*vout
, const video_format_t
*original
)
2072 assert(vout
!= NULL
);
2073 vout_thread_sys_t
*sys
= vout
;
2075 assert(!sys
->dummy
);
2076 vlc_mutex_assert(&sys
->window_lock
);
2078 if (!sys
->window_enabled
) {
2079 vout_window_cfg_t wcfg
= {
2080 .is_fullscreen
= var_GetBool(&vout
->obj
, "fullscreen"),
2081 .is_decorated
= var_InheritBool(&vout
->obj
, "video-deco"),
2082 // TODO: take pixel A/R, crop and zoom into account
2083 #if defined(__APPLE__) || defined(_WIN32)
2084 .x
= var_InheritInteger(&vout
->obj
, "video-x"),
2085 .y
= var_InheritInteger(&vout
->obj
, "video-y"),
2089 VoutGetDisplayCfg(vout
, original
, &sys
->display_cfg
);
2090 vout_SizeWindow(vout
, original
, &wcfg
.width
, &wcfg
.height
);
2092 if (vout_window_Enable(sys
->display_cfg
.window
, &wcfg
)) {
2093 msg_Err(&vout
->obj
, "failed to enable window");
2096 sys
->window_enabled
= true;
2098 vout_UpdateWindowSizeLocked(vout
);
2102 static void vout_InitSource(vout_thread_sys_t
*vout
)
2104 char *psz_ar
= var_InheritString(&vout
->obj
, "aspect-ratio");
2107 if (!GetAspectRatio(psz_ar
, &num
, &den
))
2108 vout_SetAspectRatio(vout
, num
, den
);
2112 char *psz_crop
= var_InheritString(&vout
->obj
, "crop");
2114 if (!vout_ParseCrop(&vout
->source
.crop
, psz_crop
))
2115 vout
->source
.crop
.mode
= VOUT_CROP_NONE
;
2120 int vout_Request(const vout_configuration_t
*cfg
, vlc_video_context
*vctx
, input_thread_t
*input
)
2122 vout_thread_sys_t
*vout
= VOUT_THREAD_TO_SYS(cfg
->vout
);
2123 vout_thread_sys_t
*sys
= vout
;
2125 assert(cfg
->fmt
!= NULL
);
2126 assert(cfg
->clock
!= NULL
);
2128 if (!VoutCheckFormat(cfg
->fmt
))
2129 /* don't stop the display and keep sys->original */
2132 video_format_t original
;
2133 VoutFixFormat(&original
, cfg
->fmt
);
2135 if (vout_ChangeSource(cfg
->vout
, &original
) == 0)
2137 video_format_Clean(&original
);
2141 vlc_mutex_lock(&sys
->window_lock
);
2142 vout_InitSource(vout
);
2144 if (EnableWindowLocked(vout
, &original
) != 0)
2146 /* the window was not enabled, nor the display started */
2147 msg_Err(cfg
->vout
, "failed to enable window");
2148 video_format_Clean(&original
);
2149 vlc_mutex_unlock(&sys
->window_lock
);
2152 vlc_mutex_unlock(&sys
->window_lock
);
2154 if (sys
->display
!= NULL
)
2155 vout_StopDisplay(cfg
->vout
);
2157 vout_ReinitInterlacingSupport(cfg
->vout
, &sys
->private);
2159 sys
->original
= original
;
2163 sys
->clock
= cfg
->clock
;
2166 if (vout_Start(vout
, vctx
, cfg
))
2168 msg_Err(cfg
->vout
, "video output display creation failed");
2169 video_format_Clean(&sys
->original
);
2170 vout_DisableWindow(vout
);
2173 atomic_store(&sys
->control_is_terminated
, false);
2174 if (vlc_clone(&sys
->thread
, Thread
, vout
, VLC_THREAD_PRIORITY_OUTPUT
)) {
2175 vout_ReleaseDisplay(vout
);
2176 vout_DisableWindow(vout
);
2180 if (input
!= NULL
&& sys
->spu
)
2181 spu_Attach(sys
->spu
, input
);
2182 vout_IntfReinit(cfg
->vout
);
2186 vlc_decoder_device
*vout_GetDevice(vout_thread_t
*vout
)
2188 vlc_decoder_device
*dec_device
= NULL
;
2190 vout_thread_sys_t
*sys
= VOUT_THREAD_TO_SYS(vout
);
2192 vlc_mutex_lock(&sys
->window_lock
);
2193 if (sys
->dec_device
== NULL
)
2194 sys
->dec_device
= vlc_decoder_device_Create(&vout
->obj
, sys
->display_cfg
.window
);
2195 dec_device
= sys
->dec_device
? vlc_decoder_device_Hold( sys
->dec_device
) : NULL
;
2196 vlc_mutex_unlock(&sys
->window_lock
);