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