qt: playlist: use item title if available
[vlc.git] / src / video_output / video_output.c
blob8db67467255a834924a866ae5a482d45484d831a
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 /*****************************************************************************
30 * Preamble
31 *****************************************************************************/
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
36 #include <vlc_common.h>
38 #include <math.h>
39 #include <stdlib.h> /* free() */
40 #include <string.h>
41 #include <assert.h>
43 #include <vlc_vout.h>
45 #include <vlc_filter.h>
46 #include <vlc_spu.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>
53 #include <libvlc.h>
54 #include "vout_private.h"
55 #include "vout_internal.h"
56 #include "display.h"
57 #include "snapshot.h"
58 #include "window.h"
59 #include "../misc/variables.h"
60 #include "../clock/clock.h"
61 #include "statistic.h"
62 #include "chrono.h"
63 #include "control.h"
65 typedef struct vout_thread_sys_t
67 struct vout_thread_t obj;
69 vout_thread_private_t private;
71 bool dummy;
73 /* Splitter module if used */
74 char *splitter_name;
76 vlc_clock_t *clock;
77 float rate;
78 vlc_tick_t delay;
80 video_format_t original; /* Original format ie coming from the decoder */
82 /* */
83 struct {
84 vlc_rational_t dar;
85 struct vout_crop crop;
86 } source;
88 /* Snapshot interface */
89 struct vout_snapshot *snapshot;
91 /* Statistics */
92 vout_statistic_t statistic;
94 /* Subpicture unit */
95 spu_t *spu;
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
102 vlc_thread_t thread;
104 struct {
105 vlc_tick_t date;
106 vlc_tick_t timestamp;
107 bool is_interlaced;
108 picture_t *decoded; // decoded picture before passed through chain_static
109 picture_t *current;
110 } displayed;
112 struct {
113 vlc_tick_t last;
114 vlc_tick_t timestamp;
115 } step;
117 struct {
118 bool is_on;
119 vlc_tick_t date;
120 } pause;
122 /* OSD title configuration */
123 struct {
124 bool show;
125 int timeout;
126 int position;
127 } title;
129 /* */
130 bool is_late_dropped;
132 /* */
133 vlc_mouse_t mouse;
134 vlc_mouse_event mouse_event;
135 void *mouse_opaque;
137 /* Video output window */
138 bool window_enabled;
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 */
150 struct {
151 vlc_mutex_t lock;
152 bool changed;
153 bool new_interlaced;
154 char *configuration;
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;
159 } filter;
161 picture_fifo_t *decoder_fifo;
162 vout_chrono_t render; /**< picture render time estimator */
163 vout_chrono_t static_filter;
165 vlc_atomic_rc_t rc;
167 } vout_thread_sys_t;
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)
186 /* */
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)
191 return false;
192 if (src->i_sar_num <= 0 || src->i_sar_den <= 0)
193 return false;
194 return true;
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)
227 return;
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)) {
235 unsigned x = w;
237 w = h;
238 h = x;
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 */
245 if (*width != 0) {
246 *height = (*width * h) / w;
247 return;
250 /* If height is forced, adjust width according to the aspect ratio */
251 if (*height != 0) {
252 *width = (*height * w) / h;
253 return;
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) {
280 case VOUT_CROP_NONE:
281 break;
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)
288 w = h * num / den;
289 else
290 h = w * den / num;
291 break;
294 case VOUT_CROP_WINDOW:
295 w = sys->source.crop.window.width;
296 h = sys->source.crop.window.height;
297 break;
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;
302 break;
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,
308 &sys->display_cfg);
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);
325 } else
326 vlc_mutex_unlock(&sys->display_lock);
329 /* */
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);
334 assert(!sys->dummy);
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);
341 assert(!sys->dummy);
342 if (!sys->decoder_fifo)
343 return true;
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);
351 assert(!sys->dummy);
352 assert(title);
354 if (!sys->title.show)
355 return;
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);
364 assert(!sys->dummy);
365 assert(mouse);
367 /* Translate window coordinates to video coordinates */
368 vlc_mutex_lock(&sys->display_lock);
369 vlc_mouse_t video_mouse;
370 if (sys->display)
371 vout_display_TranslateMouseState(sys->display, &video_mouse, mouse);
372 else
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);
382 assert(!sys->dummy);
384 if (sys->spu != NULL)
385 spu_PutSubpicture(sys->spu, subpic);
386 else
387 subpicture_Delete(subpic);
390 ssize_t vout_RegisterSubpictureChannel( vout_thread_t *vout )
392 vout_thread_sys_t *sys = VOUT_THREAD_TO_SYS(vout);
393 assert(!sys->dummy);
394 ssize_t channel = VOUT_SPU_CHANNEL_INVALID;
396 if (sys->spu)
397 channel = spu_RegisterChannel(sys->spu);
399 return channel;
402 ssize_t vout_RegisterSubpictureChannelInternal(vout_thread_t *vout,
403 vlc_clock_t *clock,
404 enum vlc_vout_order *out_order)
406 vout_thread_sys_t *sys = VOUT_THREAD_TO_SYS(vout);
407 assert(!sys->dummy);
408 ssize_t channel = VOUT_SPU_CHANNEL_INVALID;
410 if (sys->spu)
411 channel = spu_RegisterChannelInternal(sys->spu, clock, out_order);
413 return channel;
416 void vout_UnregisterSubpictureChannel( vout_thread_t *vout, size_t channel )
418 vout_thread_sys_t *sys = VOUT_THREAD_TO_SYS(vout);
419 assert(!sys->dummy);
420 assert(sys->spu);
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);
427 assert(!sys->dummy);
428 if (sys->spu)
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);
436 assert(!sys->dummy);
437 if (sys->spu)
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);
453 assert(!sys->dummy);
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);
459 return picture;
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
468 * read/used.
470 void vout_PutPicture(vout_thread_t *vout, picture_t *picture)
472 vout_thread_sys_t *sys = VOUT_THREAD_TO_SYS(vout);
473 assert(!sys->dummy);
474 assert( !picture_HasChainedPics( picture ) );
475 picture_fifo_Push(sys->decoder_fifo, picture);
476 vout_control_Wake(&sys->control);
479 /* */
480 int vout_GetSnapshot(vout_thread_t *vout,
481 block_t **image_dst, picture_t **picture_dst,
482 video_format_t *fmt,
483 const char *type, vlc_tick_t timeout)
485 vout_thread_sys_t *sys = VOUT_THREAD_TO_SYS(vout);
486 assert(!sys->dummy);
487 picture_t *picture = vout_snapshot_Get(sys->snapshot, timeout);
488 if (!picture) {
489 msg_Err(vout, "Failed to grab a snapshot");
490 return VLC_EGENERIC;
493 if (image_dst) {
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);
505 return VLC_EGENERIC;
508 if (picture_dst)
509 *picture_dst = picture;
510 else
511 picture_Release(picture);
512 return VLC_SUCCESS;
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);
519 assert(!sys->dummy);
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);
528 assert(!sys->dummy);
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);
539 assert(!sys->dummy);
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);
551 assert(!sys->dummy);
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);
562 if (cb != NULL)
563 cb(opaque);
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);
570 assert(!sys->dummy);
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);
587 assert(!sys->dummy);
589 if (num != 0 && den != 0) {
590 vlc_ureduce(&num, &den, num, den, 0);
591 } else {
592 num = 1;
593 den = 1;
596 if (num * 10 < den) {
597 num = 1;
598 den = 10;
599 } else if (num > den * 10) {
600 num = 10;
601 den = 1;
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);
629 assert(!sys->dummy);
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);
648 assert(!sys->dummy);
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);
665 assert(!sys->dummy);
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);
687 assert(!sys->dummy);
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);
696 assert(!sys->dummy);
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);
704 assert(!sys->dummy);
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);
713 assert(!sys->dummy);
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);
722 assert(!sys->dummy);
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);
735 /* */
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) {
750 msar_num = 1;
751 msar_den = 1;
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;
773 /* */
774 static int FilterRestartCallback(vlc_object_t *p_this, char const *psz_var,
775 vlc_value_t oldval, vlc_value_t newval,
776 void *p_data)
778 (void) p_this; (void) psz_var; (void) oldval; (void) newval;
779 vout_ControlChangeFilters((vout_thread_t *)p_data, NULL);
780 return 0;
783 static int ThreadDelFilterCallbacks(filter_t *filter, void *opaque)
785 filter_DelProxyCallbacks((vlc_object_t*)opaque, filter,
786 FilterRestartCallback);
787 return VLC_SUCCESS;
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);
803 if (picture) {
804 picture_Reset(picture);
805 video_format_CopyCropAr(&picture->format, &filter->fmt_out.video);
807 return picture;
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;
831 if (!is_locked)
832 vlc_mutex_lock(&sys->filter.lock);
833 filter_chain_VideoFlush(sys->filter.chain_static);
834 filter_chain_VideoFlush(sys->filter.chain_interactive);
835 if (!is_locked)
836 vlc_mutex_unlock(&sys->filter.lock);
839 typedef struct {
840 char *name;
841 config_chain_t *cfg;
842 } vout_filter_t;
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));
860 if (likely(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;
868 while (current) {
869 config_chain_t *cfg;
870 char *name;
871 char *next = config_ChainCreate(&name, &cfg, current);
873 if (name && *name) {
874 vout_filter_t *e = malloc(sizeof(*e));
876 if (likely(e)) {
877 e->name = name;
878 e->cfg = cfg;
879 if (!strcmp(e->name, "postproc"))
880 vlc_array_append_or_abort(&array_static, e);
881 else
882 vlc_array_append_or_abort(&array_interactive, e);
884 else {
885 if (cfg)
886 config_ChainDestroy(cfg);
887 free(name);
889 } else {
890 if (cfg)
891 config_ChainDestroy(cfg);
892 free(name);
894 free(current);
895 current = next;
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 :
907 &array_interactive;
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,
916 NULL);
917 if (!filter)
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);
923 free(e->name);
924 free(e);
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,
937 &fmt_target) != 0) {
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;
951 /* */
952 VLC_USED
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);
964 while (!picture) {
965 picture_t *decoded;
966 if (unlikely(reuse_decoded && sys->displayed.decoded)) {
967 decoded = picture_Hold(sys->displayed.decoded);
968 } else {
969 decoded = picture_fifo_Pop(sys->decoder_fifo);
971 if (decoded) {
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. */
982 *paused = true;
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);
995 else
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);
1001 continue;
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);
1020 if (!decoded)
1021 break;
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);
1038 return picture;
1041 static vlc_decoder_device * VoutHoldDecoderDevice(vlc_object_t *o, void *opaque)
1043 VLC_UNUSED(o);
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.
1059 * */
1061 assert(sys->spu_blend);
1063 filter_owner_t owner = {
1064 .video = &vout_video_cbs,
1065 .sys = vout,
1067 filter_chain_t *filterc = filter_chain_NewVideo(&vout->obj, false, &owner);
1068 if (!filterc)
1069 return NULL;
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 */,
1078 &dst);
1080 if (filter_chain_AppendConverter(filterc, &dst) != 0)
1082 filter_chain_Delete(filterc);
1083 return NULL;
1086 picture_Hold(pic);
1087 pic = filter_chain_VideoFilter(filterc, pic);
1088 filter_chain_Delete(filterc);
1090 if (pic)
1092 vlc_blender_t *swblend = filter_NewBlend(VLC_OBJECT(&vout->obj), &dst.video);
1093 if (swblend)
1095 bool success = picture_BlendSubpicture(pic, swblend, subpic) > 0;
1096 filter_DeleteBlend(swblend);
1097 if (success)
1098 return pic;
1100 picture_Release(pic);
1102 return NULL;
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);
1118 if (!filtered)
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;
1135 else
1137 render_subtitle_date = filtered->date <= VLC_TICK_0 ? system_now :
1138 vlc_clock_ConvertToSystem(sys->clock, system_now, filtered->date,
1139 sys->rate);
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
1143 * to now. */
1144 if (unlikely(render_subtitle_date == INT64_MAX))
1146 render_subtitle_date = system_now;
1147 render_now = true;
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;
1170 if (do_dr_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;
1178 fmt_spu.i_width =
1179 fmt_spu.i_visible_width = place.width;
1180 fmt_spu.i_height =
1181 fmt_spu.i_visible_height = place.height;
1183 subpicture_chromas = vd->info.subpicture_chromas;
1184 } else {
1185 if (do_early_spu) {
1186 fmt_spu = *vd->source;
1187 } else {
1188 fmt_spu = *vd->fmt;
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);
1218 * Perform rendering
1220 * We have to:
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);
1229 if (blent) {
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;
1235 } else
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. */
1240 if (do_snapshot)
1242 picture_t *copy = ConvertRGB32AndBlend(vout, blent, subpic);
1243 if (copy)
1244 snap_pic = copy;
1246 picture_Release(blent);
1250 subpicture_Delete(subpic);
1251 subpic = NULL;
1255 * Take a snapshot if requested
1257 if (do_snapshot)
1259 assert(snap_pic);
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);
1272 if (subpic != NULL)
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
1288 * to now. */
1289 system_pts = system_now;
1290 render_now = true;
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);
1300 #if 0
1302 static int i = 0;
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));
1307 #endif
1309 system_now = vlc_tick_now();
1310 if (!render_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
1319 * rendered late. */
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;
1334 else
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);
1347 if (subpic)
1348 subpicture_Delete(subpic);
1350 vout_statistic_AddDisplayed(&sys->statistic, 1);
1352 return VLC_SUCCESS;
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;
1361 assert(sys->clock);
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;
1373 if (frame_by_frame)
1375 picture_t *next;
1376 next =
1377 ThreadDisplayPreparePicture(vout, !sys->displayed.current, true, &paused);
1379 if (next)
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;
1389 else
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
1400 * if and only if:
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;
1411 if (first)
1413 next =
1414 ThreadDisplayPreparePicture(vout, true, false, &paused);
1415 if (!next)
1417 *deadline = VLC_TICK_INVALID;
1418 return VLC_EGENERIC; // wait with no known deadline
1421 else if (!paused)
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
1432 next =
1433 ThreadDisplayPreparePicture(vout, false, false, &paused);
1438 if (next != NULL)
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;
1448 render_now = false;
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);
1488 else {
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,
1502 vlc_tick_t date)
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;
1512 if (last) {
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);
1552 *duration = 0;
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);
1579 sys->delay = 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);
1589 sys->rate = rate;
1590 vout_control_Release(&sys->control);
1593 void vout_ChangeSpuDelay(vout_thread_t *vout, size_t channel_id,
1594 vlc_tick_t delay)
1596 vout_thread_sys_t *sys = VOUT_THREAD_TO_SYS(vout);
1597 assert(!sys->dummy);
1598 assert(sys->spu);
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);
1606 assert(sys->spu);
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. */
1619 m = win_mouse;
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,
1623 &tmp1, m))
1624 m = &tmp1;
1625 if (!filter_chain_MouseFilter(sys->filter.chain_static,
1626 &tmp2, m))
1627 m = &tmp2;
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");
1639 sys->mouse = *m;
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,
1670 .sys = vout,
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;
1679 unsigned num, den;
1681 vlc_mutex_lock(&sys->window_lock);
1682 #ifndef NDEBUG
1683 if (vctx)
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);
1690 #endif
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);
1707 goto error;
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);
1734 return VLC_SUCCESS;
1735 error:
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;
1770 bool wait = false;
1772 for (;;) {
1773 if (wait)
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);
1777 } else {
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))
1784 break;
1785 ThreadProcessMouseState(vout, &video_mouse);
1788 if (atomic_load(&sys->control_is_terminated))
1789 break;
1791 wait = ThreadDisplayPicture(vout, &deadline) != VLC_SUCCESS;
1793 if (atomic_load(&sys->control_is_terminated))
1794 break;
1796 const bool picture_interlaced = sys->displayed.is_interlaced;
1798 vout_SetInterlacingState(&vout->obj, &sys->private, picture_interlaced);
1800 return NULL;
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;
1846 if (sys->spu)
1847 spu_Detach(sys->spu);
1848 sys->clock = NULL;
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)
1887 assert(vout);
1889 vout_thread_sys_t *sys = VOUT_THREAD_TO_SYS(vout);
1890 assert(!sys->dummy);
1892 if (sys->display != NULL)
1893 vout_Stop(vout);
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);
1900 if (sys->spu)
1901 spu_Destroy(sys->spu);
1903 vout_Release(vout);
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))
1911 return;
1913 if (sys->dummy)
1915 vlc_object_delete(VLC_OBJECT(vout));
1916 return;
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);
1929 /* */
1930 vout_statistic_Clean(&sys->statistic);
1932 /* */
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,
1942 sizeof(*vout),
1943 "video output");
1944 if (!vout)
1945 return NULL;
1947 vout_CreateVars(&vout->obj);
1949 vout_thread_sys_t *sys = vout;
1950 vlc_atomic_rc_init(&sys->rc);
1952 return vout;
1955 vout_thread_t *vout_CreateDummy(vlc_object_t *object)
1957 vout_thread_sys_t *vout = vout_CreateCommon(object);
1958 if (!vout)
1959 return NULL;
1961 vout_thread_sys_t *sys = vout;
1962 sys->dummy = true;
1963 return &vout->obj;
1966 vout_thread_t *vout_Create(vlc_object_t *object)
1968 vout_thread_sys_t *p_vout = vout_CreateCommon(object);
1969 if (!p_vout)
1970 return NULL;
1971 vout_thread_t *vout = &p_vout->obj;
1972 vout_thread_sys_t *sys = p_vout;
1973 sys->dummy = false;
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);
2016 /* Display */
2017 sys->display = NULL;
2018 vlc_mutex_init(&sys->display_lock);
2020 /* Window */
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) {
2024 if (sys->spu)
2025 spu_Destroy(sys->spu);
2026 vlc_object_delete(vout);
2027 return NULL;
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);
2044 return vout;
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);
2052 return vout;
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. */
2064 return 0;
2067 return -1;
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"),
2086 #endif
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");
2094 return -1;
2096 sys->window_enabled = true;
2097 } else
2098 vout_UpdateWindowSizeLocked(vout);
2099 return 0;
2102 static void vout_InitSource(vout_thread_sys_t *vout)
2104 char *psz_ar = var_InheritString(&vout->obj, "aspect-ratio");
2105 if (psz_ar) {
2106 unsigned num, den;
2107 if (!GetAspectRatio(psz_ar, &num, &den))
2108 vout_SetAspectRatio(vout, num, den);
2109 free(psz_ar);
2112 char *psz_crop = var_InheritString(&vout->obj, "crop");
2113 if (psz_crop) {
2114 if (!vout_ParseCrop(&vout->source.crop, psz_crop))
2115 vout->source.crop.mode = VOUT_CROP_NONE;
2116 free(psz_crop);
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 */
2130 return -1;
2132 video_format_t original;
2133 VoutFixFormat(&original, cfg->fmt);
2135 if (vout_ChangeSource(cfg->vout, &original) == 0)
2137 video_format_Clean(&original);
2138 return 0;
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);
2150 return -1;
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;
2161 sys->delay = 0;
2162 sys->rate = 1.f;
2163 sys->clock = cfg->clock;
2164 sys->delay = 0;
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);
2171 return -1;
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);
2177 return -1;
2180 if (input != NULL && sys->spu)
2181 spu_Attach(sys->spu, input);
2182 vout_IntfReinit(cfg->vout);
2183 return 0;
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);
2197 return dec_device;