]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
Moved vout_GetSnapshot() to video_output.c
[vlc] / src / video_output / video_output.c
1 /*****************************************************************************
2  * video_output.c : video output thread
3  *
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$
10  *
11  * Authors: Vincent Seguin <seguin@via.ecp.fr>
12  *          Gildas Bazin <gbazin@videolan.org>
13  *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
14  *
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.
19  *
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.
24  *
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  *****************************************************************************/
29
30 /*****************************************************************************
31  * Preamble
32  *****************************************************************************/
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 #include <vlc_common.h>
38
39 #include <stdlib.h>                                                /* free() */
40 #include <string.h>
41 #include <assert.h>
42
43 #include <vlc_vout.h>
44
45 #include <vlc_filter.h>
46 #include <vlc_vout_osd.h>
47 #include <vlc_image.h>
48
49 #include <libvlc.h>
50 #include "vout_internal.h"
51 #include "interlacing.h"
52 #include "postprocessing.h"
53 #include "display.h"
54
55 /*****************************************************************************
56  * Local prototypes
57  *****************************************************************************/
58 static void *Thread(void *);
59 static void VoutDestructor(vlc_object_t *);
60
61 /* Maximum delay between 2 displayed pictures.
62  * XXX it is needed for now but should be removed in the long term.
63  */
64 #define VOUT_REDISPLAY_DELAY (INT64_C(80000))
65
66 /**
67  * Late pictures having a delay higher than this value are thrashed.
68  */
69 #define VOUT_DISPLAY_LATE_THRESHOLD (INT64_C(20000))
70
71 /* Better be in advance when awakening than late... */
72 #define VOUT_MWAIT_TOLERANCE (INT64_C(4000))
73
74 /* */
75 static int VoutValidateFormat(video_format_t *dst,
76                               const video_format_t *src)
77 {
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;
82
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;
91     }
92     video_format_FixRgb(dst);
93     return VLC_SUCCESS;
94 }
95
96 static vout_thread_t *VoutCreate(vlc_object_t *object,
97                                  const vout_configuration_t *cfg)
98 {
99     video_format_t original;
100     if (VoutValidateFormat(&original, cfg->fmt))
101         return NULL;
102
103     /* Allocate descriptor */
104     vout_thread_t *vout = vlc_custom_create(object,
105                                             sizeof(*vout) + sizeof(*vout->p),
106                                             VLC_OBJECT_VOUT, "video output");
107     if (!vout) {
108         video_format_Clean(&original);
109         return NULL;
110     }
111
112     /* */
113     vout->p = (vout_thread_sys_t*)&vout[1];
114
115     vout->p->original = original;
116     vout->p->dpb_size = cfg->dpb_size;
117
118     vout_control_Init(&vout->p->control);
119     vout_control_PushVoid(&vout->p->control, VOUT_CONTROL_INIT);
120
121     vout_statistic_Init(&vout->p->statistic);
122
123     vout_snapshot_Init(&vout->p->snapshot);
124
125     /* Initialize locks */
126     vlc_mutex_init(&vout->p->picture_lock);
127     vlc_mutex_init(&vout->p->filter.lock);
128     vlc_mutex_init(&vout->p->spu_lock);
129
130     /* Attach the new object now so we can use var inheritance below */
131     vlc_object_attach(vout, object);
132
133     /* Initialize subpicture unit */
134     vout->p->p_spu = spu_Create(vout);
135
136     /* Take care of some "interface/control" related initialisations */
137     vout_IntfInit(vout);
138
139     vout->p->title.show     = var_GetBool(vout, "video-title-show");
140     vout->p->title.timeout  = var_GetInteger(vout, "video-title-timeout");
141     vout->p->title.position = var_GetInteger(vout, "video-title-position");
142
143     /* Get splitter name if present */
144     char *splitter_name = var_GetNonEmptyString(vout, "vout-filter");
145     if (splitter_name) {
146         if (asprintf(&vout->p->splitter_name, "%s,none", splitter_name) < 0)
147             vout->p->splitter_name = NULL;
148         free(splitter_name);
149     } else {
150         vout->p->splitter_name = NULL;
151     }
152
153     /* */
154     vout_InitInterlacingSupport(vout, vout->p->displayed.is_interlaced);
155
156     /* */
157     vlc_object_set_destructor(vout, VoutDestructor);
158
159     /* */
160     if (vlc_clone(&vout->p->thread, Thread, vout,
161                   VLC_THREAD_PRIORITY_OUTPUT)) {
162         spu_Destroy(vout->p->p_spu);
163         vlc_object_release(vout);
164         return NULL;
165     }
166
167     vout_control_WaitEmpty(&vout->p->control);
168
169     if (vout->p->dead) {
170         msg_Err(vout, "video output creation failed");
171         vout_CloseAndRelease(vout);
172         return NULL;
173     }
174
175     vout->p->input = cfg->input;
176     if (vout->p->input)
177         spu_Attach(vout->p->p_spu, vout->p->input, true);
178
179     return vout;
180 }
181
182 vout_thread_t *(vout_Request)(vlc_object_t *object,
183                               const vout_configuration_t *cfg)
184 {
185     vout_thread_t *vout = cfg->vout;
186     if (cfg->change_fmt && !cfg->fmt) {
187         if (vout)
188             vout_CloseAndRelease(vout);
189         return NULL;
190     }
191
192     /* If a vout is provided, try reusing it */
193     if (vout) {
194         if (vout->p->input != cfg->input) {
195             if (vout->p->input)
196                 spu_Attach(vout->p->p_spu, vout->p->input, false);
197             vout->p->input = cfg->input;
198             if (vout->p->input)
199                 spu_Attach(vout->p->p_spu, vout->p->input, true);
200         }
201
202         if (cfg->change_fmt) {
203             vout_control_cmd_t cmd;
204             vout_control_cmd_Init(&cmd, VOUT_CONTROL_REINIT);
205             cmd.u.cfg = cfg;
206
207             vout_control_Push(&vout->p->control, &cmd);
208             vout_control_WaitEmpty(&vout->p->control);
209         }
210
211         if (!vout->p->dead) {
212             msg_Dbg(object, "reusing provided vout");
213             return vout;
214         }
215         vout_CloseAndRelease(vout);
216
217         msg_Warn(object, "cannot reuse provided vout");
218     }
219     return VoutCreate(object, cfg);
220 }
221
222 void vout_Close(vout_thread_t *vout)
223 {
224     assert(vout);
225
226     if (vout->p->input)
227         spu_Attach(vout->p->p_spu, vout->p->input, false);
228
229     vout_snapshot_End(&vout->p->snapshot);
230
231     vout_control_PushVoid(&vout->p->control, VOUT_CONTROL_CLEAN);
232     vlc_join(vout->p->thread, NULL);
233
234     vlc_mutex_lock(&vout->p->spu_lock);
235     spu_Destroy(vout->p->p_spu);
236     vout->p->p_spu = NULL;
237     vlc_mutex_unlock(&vout->p->spu_lock);
238 }
239
240 /* */
241 static void VoutDestructor(vlc_object_t *object)
242 {
243     vout_thread_t *vout = (vout_thread_t *)object;
244
245     /* Make sure the vout was stopped first */
246     //assert(!vout->p_module);
247
248     free(vout->p->splitter_name);
249
250     /* Destroy the locks */
251     vlc_mutex_destroy(&vout->p->spu_lock);
252     vlc_mutex_destroy(&vout->p->picture_lock);
253     vlc_mutex_destroy(&vout->p->filter.lock);
254     vout_control_Clean(&vout->p->control);
255
256     /* */
257     vout_statistic_Clean(&vout->p->statistic);
258
259     /* */
260     vout_snapshot_Clean(&vout->p->snapshot);
261
262     video_format_Clean(&vout->p->original);
263 }
264
265 /* */
266 void vout_ChangePause(vout_thread_t *vout, bool is_paused, mtime_t date)
267 {
268     vout_control_cmd_t cmd;
269     vout_control_cmd_Init(&cmd, VOUT_CONTROL_PAUSE);
270     cmd.u.pause.is_on = is_paused;
271     cmd.u.pause.date  = date;
272     vout_control_Push(&vout->p->control, &cmd);
273
274     vout_control_WaitEmpty(&vout->p->control);
275 }
276
277 void vout_GetResetStatistic(vout_thread_t *vout, int *displayed, int *lost)
278 {
279     vout_statistic_GetReset( &vout->p->statistic, displayed, lost );
280 }
281
282 void vout_Flush(vout_thread_t *vout, mtime_t date)
283 {
284     vout_control_PushTime(&vout->p->control, VOUT_CONTROL_FLUSH, date);
285     vout_control_WaitEmpty(&vout->p->control);
286 }
287
288 void vout_Reset(vout_thread_t *vout)
289 {
290     vout_control_PushVoid(&vout->p->control, VOUT_CONTROL_RESET);
291     vout_control_WaitEmpty(&vout->p->control);
292 }
293
294 bool vout_IsEmpty(vout_thread_t *vout)
295 {
296     vlc_mutex_lock(&vout->p->picture_lock);
297
298     picture_t *picture = picture_fifo_Peek(vout->p->decoder_fifo);
299     if (picture)
300         picture_Release(picture);
301
302     vlc_mutex_unlock(&vout->p->picture_lock);
303
304     return !picture;
305 }
306
307 void vout_FixLeaks( vout_thread_t *vout )
308 {
309     vlc_mutex_lock(&vout->p->picture_lock);
310
311     picture_t *picture = picture_fifo_Peek(vout->p->decoder_fifo);
312     if (!picture) {
313         picture = picture_pool_Get(vout->p->decoder_pool);
314     }
315
316     if (picture) {
317         picture_Release(picture);
318         /* Not all pictures has been displayed yet or some are
319          * free */
320         vlc_mutex_unlock(&vout->p->picture_lock);
321         return;
322     }
323
324     /* There is no reason that no pictures are available, force one
325      * from the pool, becarefull with it though */
326     msg_Err(vout, "pictures leaked, trying to workaround");
327
328     /* */
329     picture_pool_NonEmpty(vout->p->decoder_pool, false);
330
331     vlc_mutex_unlock(&vout->p->picture_lock);
332 }
333 void vout_NextPicture(vout_thread_t *vout, mtime_t *duration)
334 {
335     vout_control_cmd_t cmd;
336     vout_control_cmd_Init(&cmd, VOUT_CONTROL_STEP);
337     cmd.u.time_ptr = duration;
338
339     vout_control_Push(&vout->p->control, &cmd);
340     vout_control_WaitEmpty(&vout->p->control);
341 }
342
343 void vout_DisplayTitle(vout_thread_t *vout, const char *title)
344 {
345     assert(title);
346     vout_control_PushString(&vout->p->control, VOUT_CONTROL_OSD_TITLE, title);
347 }
348
349 void vout_PutSubpicture( vout_thread_t *vout, subpicture_t *subpic )
350 {
351     vout_control_cmd_t cmd;
352     vout_control_cmd_Init(&cmd, VOUT_CONTROL_SUBPICTURE);
353     cmd.u.subpicture = subpic;
354
355     vout_control_Push(&vout->p->control, &cmd);
356 }
357 int vout_RegisterSubpictureChannel( vout_thread_t *vout )
358 {
359     int channel = SPU_DEFAULT_CHANNEL;
360
361     vlc_mutex_lock(&vout->p->spu_lock);
362     if (vout->p->p_spu)
363         channel = spu_RegisterChannel(vout->p->p_spu);
364     vlc_mutex_unlock(&vout->p->spu_lock);
365
366     return channel;
367 }
368 void vout_FlushSubpictureChannel( vout_thread_t *vout, int channel )
369 {
370     vout_control_PushInteger(&vout->p->control, VOUT_CONTROL_FLUSH_SUBPICTURE,
371                              channel);
372 }
373
374 /**
375  * It retreives a picture from the vout or NULL if no pictures are
376  * available yet.
377  *
378  * You MUST call vout_PutPicture or vout_ReleasePicture on it.
379  *
380  * You may use vout_HoldPicture(paired with vout_ReleasePicture) to keep a
381  * read-only reference.
382  */
383 picture_t *vout_GetPicture(vout_thread_t *vout)
384 {
385     /* Get lock */
386     vlc_mutex_lock(&vout->p->picture_lock);
387     picture_t *picture = picture_pool_Get(vout->p->decoder_pool);
388     if (picture) {
389         picture_Reset(picture);
390         picture->p_next = NULL;
391     }
392     vlc_mutex_unlock(&vout->p->picture_lock);
393
394     return picture;
395 }
396
397 /**
398  * It gives to the vout a picture to be displayed.
399  *
400  * The given picture MUST comes from vout_GetPicture.
401  *
402  * Becareful, after vout_PutPicture is called, picture_t::p_next cannot be
403  * read/used.
404  */
405 void vout_PutPicture(vout_thread_t *vout, picture_t *picture)
406 {
407     vlc_mutex_lock(&vout->p->picture_lock);
408
409     picture->p_next = NULL;
410     picture_fifo_Push(vout->p->decoder_fifo, picture);
411
412     vlc_mutex_unlock(&vout->p->picture_lock);
413
414     vout_control_Wake(&vout->p->control);
415 }
416
417 /**
418  * It releases a picture retreived by vout_GetPicture.
419  */
420 void vout_ReleasePicture(vout_thread_t *vout, picture_t *picture)
421 {
422     vlc_mutex_lock(&vout->p->picture_lock);
423
424     picture_Release(picture);
425
426     vlc_mutex_unlock(&vout->p->picture_lock);
427
428     vout_control_Wake(&vout->p->control);
429 }
430
431 /**
432  * It increment the reference counter of a picture retreived by
433  * vout_GetPicture.
434  */
435 void vout_HoldPicture(vout_thread_t *vout, picture_t *picture)
436 {
437     vlc_mutex_lock(&vout->p->picture_lock);
438
439     picture_Hold(picture);
440
441     vlc_mutex_unlock(&vout->p->picture_lock);
442 }
443
444 /* */
445 int vout_GetSnapshot(vout_thread_t *vout,
446                      block_t **image_dst, picture_t **picture_dst,
447                      video_format_t *fmt,
448                      const char *type, mtime_t timeout)
449 {
450     picture_t *picture = vout_snapshot_Get(&vout->p->snapshot, timeout);
451     if (!picture) {
452         msg_Err(vout, "Failed to grab a snapshot");
453         return VLC_EGENERIC;
454     }
455
456     if (image_dst) {
457         vlc_fourcc_t codec = VLC_CODEC_PNG;
458         if (type && image_Type2Fourcc(type))
459             codec = image_Type2Fourcc(type);
460
461         const int override_width  = var_GetInteger(vout, "snapshot-width");
462         const int override_height = var_GetInteger(vout, "snapshot-height");
463
464         if (picture_Export(VLC_OBJECT(vout), image_dst, fmt,
465                            picture, codec, override_width, override_height)) {
466             msg_Err(vout, "Failed to convert image for snapshot");
467             picture_Release(picture);
468             return VLC_EGENERIC;
469         }
470     }
471     if (picture_dst)
472         *picture_dst = picture;
473     else
474         picture_Release(picture);
475     return VLC_SUCCESS;
476 }
477
478 /* vout_Control* are usable by anyone at anytime */
479 void vout_ControlChangeFullscreen(vout_thread_t *vout, bool fullscreen)
480 {
481     vout_control_PushBool(&vout->p->control, VOUT_CONTROL_FULLSCREEN,
482                           fullscreen);
483 }
484 void vout_ControlChangeOnTop(vout_thread_t *vout, bool is_on_top)
485 {
486     vout_control_PushBool(&vout->p->control, VOUT_CONTROL_ON_TOP,
487                           is_on_top);
488 }
489 void vout_ControlChangeDisplayFilled(vout_thread_t *vout, bool is_filled)
490 {
491     vout_control_PushBool(&vout->p->control, VOUT_CONTROL_DISPLAY_FILLED,
492                           is_filled);
493 }
494 void vout_ControlChangeZoom(vout_thread_t *vout, int num, int den)
495 {
496     vout_control_PushPair(&vout->p->control, VOUT_CONTROL_ZOOM,
497                           num, den);
498 }
499 void vout_ControlChangeSampleAspectRatio(vout_thread_t *vout,
500                                          unsigned num, unsigned den)
501 {
502     vout_control_PushPair(&vout->p->control, VOUT_CONTROL_ASPECT_RATIO,
503                           num, den);
504 }
505 void vout_ControlChangeCropRatio(vout_thread_t *vout,
506                                  unsigned num, unsigned den)
507 {
508     vout_control_PushPair(&vout->p->control, VOUT_CONTROL_CROP_RATIO,
509                           num, den);
510 }
511 void vout_ControlChangeCropWindow(vout_thread_t *vout,
512                                   int x, int y, int width, int height)
513 {
514     vout_control_cmd_t cmd;
515     vout_control_cmd_Init(&cmd, VOUT_CONTROL_CROP_WINDOW);
516     cmd.u.window.x      = x;
517     cmd.u.window.y      = y;
518     cmd.u.window.width  = width;
519     cmd.u.window.height = height;
520
521     vout_control_Push(&vout->p->control, &cmd);
522 }
523 void vout_ControlChangeCropBorder(vout_thread_t *vout,
524                                   int left, int top, int right, int bottom)
525 {
526     vout_control_cmd_t cmd;
527     vout_control_cmd_Init(&cmd, VOUT_CONTROL_CROP_BORDER);
528     cmd.u.border.left   = left;
529     cmd.u.border.top    = top;
530     cmd.u.border.right  = right;
531     cmd.u.border.bottom = bottom;
532
533     vout_control_Push(&vout->p->control, &cmd);
534 }
535 void vout_ControlChangeFilters(vout_thread_t *vout, const char *filters)
536 {
537     vout_control_PushString(&vout->p->control, VOUT_CONTROL_CHANGE_FILTERS,
538                             filters);
539 }
540 void vout_ControlChangeSubFilters(vout_thread_t *vout, const char *filters)
541 {
542     vout_control_PushString(&vout->p->control, VOUT_CONTROL_CHANGE_SUB_FILTERS,
543                             filters);
544 }
545 void vout_ControlChangeSubMargin(vout_thread_t *vout, int margin)
546 {
547     vout_control_PushInteger(&vout->p->control, VOUT_CONTROL_CHANGE_SUB_MARGIN,
548                              margin);
549 }
550
551 /* */
552 static void VoutGetDisplayCfg(vout_thread_t *vout, vout_display_cfg_t *cfg, const char *title)
553 {
554     /* Load configuration */
555     cfg->is_fullscreen = var_CreateGetBool(vout, "fullscreen");
556     cfg->display.title = title;
557     const int display_width = var_CreateGetInteger(vout, "width");
558     const int display_height = var_CreateGetInteger(vout, "height");
559     cfg->display.width   = display_width > 0  ? display_width  : 0;
560     cfg->display.height  = display_height > 0 ? display_height : 0;
561     cfg->is_display_filled  = var_CreateGetBool(vout, "autoscale");
562     unsigned msar_num, msar_den;
563     if (var_InheritURational(vout, &msar_num, &msar_den, "monitor-par") ||
564         msar_num <= 0 || msar_den <= 0) {
565         msar_num = 1;
566         msar_den = 1;
567     }
568     cfg->display.sar.num = msar_num;
569     cfg->display.sar.den = msar_den;
570     unsigned zoom_den = 1000;
571     unsigned zoom_num = zoom_den * var_CreateGetFloat(vout, "scale");
572     vlc_ureduce(&zoom_num, &zoom_den, zoom_num, zoom_den, 0);
573     cfg->zoom.num = zoom_num;
574     cfg->zoom.den = zoom_den;
575     cfg->align.vertical = VOUT_DISPLAY_ALIGN_CENTER;
576     cfg->align.horizontal = VOUT_DISPLAY_ALIGN_CENTER;
577     const int align_mask = var_CreateGetInteger(vout, "align");
578     if (align_mask & 0x1)
579         cfg->align.horizontal = VOUT_DISPLAY_ALIGN_LEFT;
580     else if (align_mask & 0x2)
581         cfg->align.horizontal = VOUT_DISPLAY_ALIGN_RIGHT;
582     if (align_mask & 0x4)
583         cfg->align.vertical = VOUT_DISPLAY_ALIGN_TOP;
584     else if (align_mask & 0x8)
585         cfg->align.vertical = VOUT_DISPLAY_ALIGN_BOTTOM;
586 }
587
588 vout_window_t * vout_NewDisplayWindow(vout_thread_t *vout, vout_display_t *vd,
589                                       const vout_window_cfg_t *cfg)
590 {
591     VLC_UNUSED(vd);
592     vout_window_cfg_t cfg_override = *cfg;
593
594     if (!var_InheritBool( vout, "embedded-video"))
595         cfg_override.is_standalone = true;
596
597     if (vout->p->window.is_unused && vout->p->window.object) {
598         assert(!vout->p->splitter_name);
599         if (!cfg_override.is_standalone == !vout->p->window.cfg.is_standalone &&
600             cfg_override.type           == vout->p->window.cfg.type) {
601             /* Reuse the stored window */
602             msg_Dbg(vout, "Reusing previous vout window");
603             vout_window_t *window = vout->p->window.object;
604             if (cfg_override.width  != vout->p->window.cfg.width ||
605                 cfg_override.height != vout->p->window.cfg.height)
606                 vout_window_SetSize(window,
607                                     cfg_override.width, cfg_override.height);
608             vout->p->window.is_unused = false;
609             vout->p->window.cfg       = cfg_override;
610             return window;
611         }
612
613         vout_window_Delete(vout->p->window.object);
614         vout->p->window.is_unused = true;
615         vout->p->window.object    = NULL;
616     }
617
618     vout_window_t *window = vout_window_New(VLC_OBJECT(vout), "$window",
619                                             &cfg_override);
620     if (!window)
621         return NULL;
622     if (!vout->p->splitter_name) {
623         vout->p->window.is_unused = false;
624         vout->p->window.cfg       = cfg_override;
625         vout->p->window.object    = window;
626     }
627     return window;
628 }
629
630 void vout_DeleteDisplayWindow(vout_thread_t *vout, vout_display_t *vd,
631                               vout_window_t *window)
632 {
633     VLC_UNUSED(vd);
634     if (!vout->p->window.is_unused && vout->p->window.object == window) {
635         vout->p->window.is_unused = true;
636     } else if (vout->p->window.is_unused && vout->p->window.object && !window) {
637         vout_window_Delete(vout->p->window.object);
638         vout->p->window.is_unused = true;
639         vout->p->window.object    = NULL;
640     } else if (window) {
641         vout_window_Delete(window);
642     }
643 }
644
645 /* */
646
647 static picture_t *VoutVideoFilterInteractiveNewPicture(filter_t *filter)
648 {
649     vout_thread_t *vout = (vout_thread_t*)filter->p_owner;
650
651     return picture_pool_Get(vout->p->private_pool);
652 }
653 static picture_t *VoutVideoFilterStaticNewPicture(filter_t *filter)
654 {
655     vout_thread_t *vout = (vout_thread_t*)filter->p_owner;
656
657     vlc_assert_locked(&vout->p->filter.lock);
658     if (filter_chain_GetLength(vout->p->filter.chain_interactive) == 0)
659         return VoutVideoFilterInteractiveNewPicture(filter);
660     return picture_NewFromFormat(&filter->fmt_out.video);
661 }
662 static void VoutVideoFilterDelPicture(filter_t *filter, picture_t *picture)
663 {
664     VLC_UNUSED(filter);
665     picture_Release(picture);
666 }
667 static int VoutVideoFilterStaticAllocationSetup(filter_t *filter, void *data)
668 {
669     filter->pf_video_buffer_new = VoutVideoFilterStaticNewPicture;
670     filter->pf_video_buffer_del = VoutVideoFilterDelPicture;
671     filter->p_owner             = data; /* vout */
672     return VLC_SUCCESS;
673 }
674 static int VoutVideoFilterInteractiveAllocationSetup(filter_t *filter, void *data)
675 {
676     filter->pf_video_buffer_new = VoutVideoFilterInteractiveNewPicture;
677     filter->pf_video_buffer_del = VoutVideoFilterDelPicture;
678     filter->p_owner             = data; /* vout */
679     return VLC_SUCCESS;
680 }
681
682 /* */
683 static int ThreadDisplayPreparePicture(vout_thread_t *vout, bool reuse, bool is_late_dropped)
684 {
685     int lost_count = 0;
686
687     vlc_mutex_lock(&vout->p->filter.lock);
688
689     picture_t *picture = filter_chain_VideoFilter(vout->p->filter.chain_static, NULL);
690     assert(!reuse || !picture);
691
692     while (!picture) {
693         picture_t *decoded;
694         if (reuse && vout->p->displayed.decoded) {
695             decoded = picture_Hold(vout->p->displayed.decoded);
696         } else {
697             decoded = picture_fifo_Pop(vout->p->decoder_fifo);
698             if (is_late_dropped && decoded && !decoded->b_force) {
699                 const mtime_t predicted = mdate() + 0; /* TODO improve */
700                 const mtime_t late = predicted - decoded->date;
701                 if (late > VOUT_DISPLAY_LATE_THRESHOLD) {
702                     msg_Warn(vout, "picture is too late to be displayed (missing %d ms)", (int)(late/1000));
703                     picture_Release(decoded);
704                     lost_count++;
705                     continue;
706                 } else if (late > 0) {
707                     msg_Dbg(vout, "picture might be displayed late (missing %d ms)", (int)(late/1000));
708                 }
709             }
710         }
711         if (!decoded)
712             break;
713         reuse = false;
714
715         if (vout->p->displayed.decoded)
716             picture_Release(vout->p->displayed.decoded);
717
718         vout->p->displayed.decoded       = picture_Hold(decoded);
719         vout->p->displayed.timestamp     = decoded->date;
720         vout->p->displayed.is_interlaced = !decoded->b_progressive;
721         vout->p->displayed.qtype         = decoded->i_qtype;
722
723         picture = filter_chain_VideoFilter(vout->p->filter.chain_static, decoded);
724     }
725
726     vlc_mutex_unlock(&vout->p->filter.lock);
727
728     vout_statistic_Update(&vout->p->statistic, 0, lost_count);
729     if (!picture)
730         return VLC_EGENERIC;
731
732     assert(!vout->p->displayed.next);
733     if (!vout->p->displayed.current)
734         vout->p->displayed.current = picture;
735     else
736         vout->p->displayed.next    = picture;
737     return VLC_SUCCESS;
738 }
739
740 static int ThreadDisplayRenderPicture(vout_thread_t *vout, bool is_forced)
741 {
742     vout_display_t *vd = vout->p->display.vd;
743
744     picture_t *torender = picture_Hold(vout->p->displayed.current);
745
746     vout_chrono_Start(&vout->p->render);
747
748     vlc_mutex_lock(&vout->p->filter.lock);
749     picture_t *filtered = filter_chain_VideoFilter(vout->p->filter.chain_interactive, torender);
750     vlc_mutex_unlock(&vout->p->filter.lock);
751
752     if (!filtered)
753         return VLC_EGENERIC;
754
755     if (filtered->date != vout->p->displayed.current->date)
756         msg_Warn(vout, "Unsupported timestamp modifications done by chain_interactive");
757
758     /*
759      * Check for subpictures to display
760      */
761     const bool do_snapshot = vout_snapshot_IsRequested(&vout->p->snapshot);
762     mtime_t spu_render_time = is_forced ? mdate() : filtered->date;
763     if (vout->p->pause.is_on)
764         spu_render_time = vout->p->pause.date;
765     else
766         spu_render_time = filtered->date > 1 ? filtered->date : mdate();
767
768     subpicture_t *subpic = spu_SortSubpictures(vout->p->p_spu,
769                                                spu_render_time,
770                                                do_snapshot);
771     /*
772      * Perform rendering
773      *
774      * We have to:
775      * - be sure to end up with a direct buffer.
776      * - blend subtitles, and in a fast access buffer
777      */
778     picture_t *direct = NULL;
779     if (filtered &&
780         (vout->p->decoder_pool != vout->p->display_pool || subpic)) {
781         picture_t *render;
782         if (vout->p->is_decoder_pool_slow)
783             render = picture_NewFromFormat(&vd->source);
784         else if (vout->p->decoder_pool != vout->p->display_pool)
785             render = picture_pool_Get(vout->p->display_pool);
786         else
787             render = picture_pool_Get(vout->p->private_pool);
788
789         if (render) {
790             picture_Copy(render, filtered);
791
792             spu_RenderSubpictures(vout->p->p_spu,
793                                   render, &vd->source,
794                                   subpic, &vd->source, spu_render_time);
795         }
796         if (vout->p->is_decoder_pool_slow) {
797             direct = picture_pool_Get(vout->p->display_pool);
798             if (direct)
799                 picture_Copy(direct, render);
800             picture_Release(render);
801
802         } else {
803             direct = render;
804         }
805         picture_Release(filtered);
806         filtered = NULL;
807     } else {
808         direct = filtered;
809     }
810
811     if (!direct)
812         return VLC_EGENERIC;
813
814     /*
815      * Take a snapshot if requested
816      */
817     if (do_snapshot)
818         vout_snapshot_Set(&vout->p->snapshot, &vd->source, direct);
819
820     /* Render the direct buffer returned by vout_RenderPicture */
821     vout_RenderWrapper(vout, direct);
822
823     vout_chrono_Stop(&vout->p->render);
824 #if 0
825         {
826         static int i = 0;
827         if (((i++)%10) == 0)
828             msg_Info(vout, "render: avg %d ms var %d ms",
829                      (int)(vout->p->render.avg/1000), (int)(vout->p->render.var/1000));
830         }
831 #endif
832
833     /* Wait the real date (for rendering jitter) */
834 #if 0
835     mtime_t delay = direct->date - mdate();
836     if (delay < 1000)
837         msg_Warn(vout, "picture is late (%lld ms)", delay / 1000);
838 #endif
839     if (!is_forced)
840         mwait(direct->date);
841
842     /* Display the direct buffer returned by vout_RenderPicture */
843     vout->p->displayed.date = mdate();
844
845     vout_DisplayWrapper(vout, direct);
846
847     vout_statistic_Update(&vout->p->statistic, 1, 0);
848
849     return VLC_SUCCESS;
850 }
851
852 static int ThreadDisplayPicture(vout_thread_t *vout,
853                                 bool now, mtime_t *deadline)
854 {
855     bool is_late_dropped = vout->p->is_late_dropped && !vout->p->pause.is_on && !now;
856     bool first = !vout->p->displayed.current;
857     if (first && ThreadDisplayPreparePicture(vout, true, is_late_dropped)) /* FIXME not sure it is ok */
858         return VLC_EGENERIC;
859     if (!vout->p->pause.is_on || now) {
860         while (!vout->p->displayed.next) {
861             if (ThreadDisplayPreparePicture(vout, false, is_late_dropped)) {
862                 break;
863             }
864         }
865     }
866
867     const mtime_t date = mdate();
868     const mtime_t render_delay = vout_chrono_GetHigh(&vout->p->render) + VOUT_MWAIT_TOLERANCE;
869
870     mtime_t date_next = VLC_TS_INVALID;
871     if (!vout->p->pause.is_on && vout->p->displayed.next)
872         date_next = vout->p->displayed.next->date - render_delay;
873
874     /* FIXME/XXX we must redisplay the last decoded picture (because
875      * of potential vout updated, or filters update or SPU update)
876      * For now a high update period is needed but it coulmd be removed
877      * if and only if:
878      * - vout module emits events from theselves.
879      * - *and* SPU is modified to emit an event or a deadline when needed.
880      *
881      * So it will be done latter.
882      */
883     mtime_t date_refresh = VLC_TS_INVALID;
884     if (vout->p->displayed.date > VLC_TS_INVALID)
885         date_refresh = vout->p->displayed.date + VOUT_REDISPLAY_DELAY - render_delay;
886
887     bool drop = now;
888     if (date_next != VLC_TS_INVALID)
889         drop |= date_next + 0 <= date;
890
891     bool refresh = false;
892     if (date_refresh > VLC_TS_INVALID)
893         refresh = date_refresh <= date;
894
895     if (!first && !refresh && !drop) {
896         if (date_next != VLC_TS_INVALID && date_refresh != VLC_TS_INVALID)
897             *deadline = __MIN(date_next, date_refresh);
898         else if (date_next != VLC_TS_INVALID)
899             *deadline = date_next;
900         else if (date_refresh != VLC_TS_INVALID)
901             *deadline = date_refresh;
902         return VLC_EGENERIC;
903     }
904
905     if (drop) {
906         picture_Release(vout->p->displayed.current);
907         vout->p->displayed.current = vout->p->displayed.next;
908         vout->p->displayed.next    = NULL;
909     }
910     if (!vout->p->displayed.current)
911         return VLC_EGENERIC;
912
913     bool is_forced = now || (!drop && refresh) || vout->p->displayed.current->b_force;
914     return ThreadDisplayRenderPicture(vout, is_forced);
915 }
916
917 static void ThreadManage(vout_thread_t *vout,
918                          mtime_t *deadline,
919                          vout_interlacing_support_t *interlacing,
920                          vout_postprocessing_support_t *postprocessing)
921 {
922     vlc_mutex_lock(&vout->p->picture_lock);
923
924     *deadline = VLC_TS_INVALID;
925     for (;;) {
926         if (ThreadDisplayPicture(vout, false, deadline))
927             break;
928     }
929
930     const int  picture_qtype      = vout->p->displayed.qtype;
931     const bool picture_interlaced = vout->p->displayed.is_interlaced;
932
933     vlc_mutex_unlock(&vout->p->picture_lock);
934
935     /* Post processing */
936     vout_SetPostProcessingState(vout, postprocessing, picture_qtype);
937
938     /* Deinterlacing */
939     vout_SetInterlacingState(vout, interlacing, picture_interlaced);
940
941     vout_ManageWrapper(vout);
942 }
943
944 static void ThreadDisplaySubpicture(vout_thread_t *vout,
945                                     subpicture_t *subpicture)
946 {
947     spu_DisplaySubpicture(vout->p->p_spu, subpicture);
948 }
949
950 static void ThreadFlushSubpicture(vout_thread_t *vout, int channel)
951 {
952     spu_ClearChannel(vout->p->p_spu, channel);
953 }
954
955 static void ThreadDisplayOsdTitle(vout_thread_t *vout, const char *string)
956 {
957     if (!vout->p->title.show)
958         return;
959
960     vout_OSDText(vout, SPU_DEFAULT_CHANNEL,
961                  vout->p->title.position, INT64_C(1000) * vout->p->title.timeout,
962                  string);
963 }
964
965 static void ThreadFilterFlush(vout_thread_t *vout)
966 {
967     if (vout->p->displayed.current)
968         picture_Release( vout->p->displayed.current );
969     vout->p->displayed.current = NULL;
970
971     if (vout->p->displayed.next)
972         picture_Release( vout->p->displayed.next );
973     vout->p->displayed.next = NULL;
974
975     vlc_mutex_lock(&vout->p->filter.lock);
976     filter_chain_VideoFlush(vout->p->filter.chain_static);
977     filter_chain_VideoFlush(vout->p->filter.chain_interactive);
978     vlc_mutex_unlock(&vout->p->filter.lock);
979 }
980
981 typedef struct {
982     char           *name;
983     config_chain_t *cfg;
984 } vout_filter_t;
985
986 static void ThreadChangeFilters(vout_thread_t *vout, const char *filters)
987 {
988     ThreadFilterFlush(vout);
989
990     vlc_array_t array_static;
991     vlc_array_t array_interactive;
992
993     vlc_array_init(&array_static);
994     vlc_array_init(&array_interactive);
995     char *current = filters ? strdup(filters) : NULL;
996     while (current) {
997         config_chain_t *cfg;
998         char *name;
999         char *next = config_ChainCreate(&name, &cfg, current);
1000
1001         if (name && *name) {
1002             vout_filter_t *e = xmalloc(sizeof(*e));
1003             e->name = name;
1004             e->cfg  = cfg;
1005             if (!strcmp(e->name, "deinterlace") ||
1006                 !strcmp(e->name, "postproc")) {
1007                 vlc_array_append(&array_static, e);
1008             } else {
1009                 vlc_array_append(&array_interactive, e);
1010             }
1011         } else {
1012             if (cfg)
1013                 config_ChainDestroy(cfg);
1014             free(name);
1015         }
1016         free(current);
1017         current = next;
1018     }
1019
1020     es_format_t fmt;
1021     es_format_Init(&fmt, VIDEO_ES, vout->p->original.i_chroma);
1022     fmt.video = vout->p->original;
1023
1024     vlc_mutex_lock(&vout->p->filter.lock);
1025
1026     filter_chain_Reset(vout->p->filter.chain_static,      &fmt, &fmt);
1027     filter_chain_Reset(vout->p->filter.chain_interactive, &fmt, &fmt);
1028
1029     for (int a = 0; a < 2; a++) {
1030         vlc_array_t    *array = a == 0 ? &array_static :
1031                                          &array_interactive;
1032         filter_chain_t *chain = a == 0 ? vout->p->filter.chain_static :
1033                                          vout->p->filter.chain_interactive;
1034
1035         for (int i = 0; i < vlc_array_count(array); i++) {
1036             vout_filter_t *e = vlc_array_item_at_index(array, i);
1037             msg_Dbg(vout, "Adding '%s' as %s", e->name, a == 0 ? "static" : "interactive");
1038             if (!filter_chain_AppendFilter(chain, e->name, e->cfg, NULL, NULL)) {
1039                 msg_Err(vout, "Failed to add filter '%s'", e->name);
1040                 config_ChainDestroy(e->cfg);
1041             }
1042             free(e->name);
1043             free(e);
1044         }
1045         vlc_array_clear(array);
1046     }
1047
1048     vlc_mutex_unlock(&vout->p->filter.lock);
1049 }
1050
1051 static void ThreadChangeSubFilters(vout_thread_t *vout, const char *filters)
1052 {
1053     spu_ChangeFilters(vout->p->p_spu, filters);
1054 }
1055 static void ThreadChangeSubMargin(vout_thread_t *vout, int margin)
1056 {
1057     spu_ChangeMargin(vout->p->p_spu, margin);
1058 }
1059
1060 static void ThreadChangePause(vout_thread_t *vout, bool is_paused, mtime_t date)
1061 {
1062     assert(!vout->p->pause.is_on || !is_paused);
1063
1064     if (vout->p->pause.is_on) {
1065         const mtime_t duration = date - vout->p->pause.date;
1066
1067         if (vout->p->step.timestamp > VLC_TS_INVALID)
1068             vout->p->step.timestamp += duration;
1069         if (vout->p->step.last > VLC_TS_INVALID)
1070             vout->p->step.last += duration;
1071         picture_fifo_OffsetDate(vout->p->decoder_fifo, duration);
1072         if (vout->p->displayed.decoded)
1073             vout->p->displayed.decoded->date += duration;
1074         spu_OffsetSubtitleDate(vout->p->p_spu, duration);
1075
1076         ThreadFilterFlush(vout);
1077     } else {
1078         vout->p->step.timestamp = VLC_TS_INVALID;
1079         vout->p->step.last      = VLC_TS_INVALID;
1080     }
1081     vout->p->pause.is_on = is_paused;
1082     vout->p->pause.date  = date;
1083 }
1084
1085 static void ThreadFlush(vout_thread_t *vout, bool below, mtime_t date)
1086 {
1087     vout->p->step.timestamp = VLC_TS_INVALID;
1088     vout->p->step.last      = VLC_TS_INVALID;
1089
1090     ThreadFilterFlush(vout); /* FIXME too much */
1091
1092     picture_t *last = vout->p->displayed.decoded;
1093     if (last) {
1094         if (( below && last->date <= date) ||
1095             (!below && last->date >= date)) {
1096             picture_Release(last);
1097
1098             vout->p->displayed.decoded   = NULL;
1099             vout->p->displayed.date      = VLC_TS_INVALID;
1100             vout->p->displayed.timestamp = VLC_TS_INVALID;
1101         }
1102     }
1103
1104     picture_fifo_Flush(vout->p->decoder_fifo, date, below);
1105 }
1106
1107 static void ThreadReset(vout_thread_t *vout)
1108 {
1109     ThreadFlush(vout, true, INT64_MAX);
1110     if (vout->p->decoder_pool)
1111         picture_pool_NonEmpty(vout->p->decoder_pool, true);
1112     vout->p->pause.is_on = false;
1113     vout->p->pause.date  = mdate();
1114 }
1115
1116 static void ThreadStep(vout_thread_t *vout, mtime_t *duration)
1117 {
1118     *duration = 0;
1119
1120     if (vout->p->step.last <= VLC_TS_INVALID)
1121         vout->p->step.last = vout->p->displayed.timestamp;
1122
1123     mtime_t dummy;
1124     if (ThreadDisplayPicture(vout, true, &dummy))
1125         return;
1126
1127     vout->p->step.timestamp = vout->p->displayed.timestamp;
1128
1129     if (vout->p->step.last > VLC_TS_INVALID &&
1130         vout->p->step.timestamp > vout->p->step.last) {
1131         *duration = vout->p->step.timestamp - vout->p->step.last;
1132         vout->p->step.last = vout->p->step.timestamp;
1133         /* TODO advance subpicture by the duration ... */
1134     }
1135 }
1136
1137 static void ThreadChangeFullscreen(vout_thread_t *vout, bool fullscreen)
1138 {
1139     /* FIXME not sure setting "fullscreen" is good ... */
1140     var_SetBool(vout, "fullscreen", fullscreen);
1141     vout_SetDisplayFullscreen(vout->p->display.vd, fullscreen);
1142 }
1143
1144 static void ThreadChangeOnTop(vout_thread_t *vout, bool is_on_top)
1145 {
1146     vout_SetWindowState(vout->p->display.vd,
1147                         is_on_top ? VOUT_WINDOW_STATE_ABOVE :
1148                                     VOUT_WINDOW_STATE_NORMAL);
1149 }
1150
1151 static void ThreadChangeDisplayFilled(vout_thread_t *vout, bool is_filled)
1152 {
1153     vout_SetDisplayFilled(vout->p->display.vd, is_filled);
1154 }
1155
1156 static void ThreadChangeZoom(vout_thread_t *vout, int num, int den)
1157 {
1158     if (num * 10 < den) {
1159         num = den;
1160         den *= 10;
1161     } else if (num > den * 10) {
1162         num = den * 10;
1163     }
1164
1165     vout_SetDisplayZoom(vout->p->display.vd, num, den);
1166 }
1167
1168 static void ThreadChangeAspectRatio(vout_thread_t *vout,
1169                                     unsigned num, unsigned den)
1170 {
1171     const video_format_t *source = &vout->p->original;
1172
1173     if (num > 0 && den > 0) {
1174         num *= source->i_visible_height;
1175         den *= source->i_visible_width;
1176         vlc_ureduce(&num, &den, num, den, 0);
1177     }
1178     vout_SetDisplayAspect(vout->p->display.vd, num, den);
1179 }
1180
1181
1182 static void ThreadExecuteCropWindow(vout_thread_t *vout,
1183                                     unsigned crop_num, unsigned crop_den,
1184                                     unsigned x, unsigned y,
1185                                     unsigned width, unsigned height)
1186 {
1187     const video_format_t *source = &vout->p->original;
1188
1189     vout_SetDisplayCrop(vout->p->display.vd,
1190                         crop_num, crop_den,
1191                         source->i_x_offset + x,
1192                         source->i_y_offset + y,
1193                         width, height);
1194 }
1195 static void ThreadExecuteCropBorder(vout_thread_t *vout,
1196                                     unsigned left, unsigned top,
1197                                     unsigned right, unsigned bottom)
1198 {
1199     const video_format_t *source = &vout->p->original;
1200     ThreadExecuteCropWindow(vout, 0, 0,
1201                             left,
1202                             top,
1203                             /* At worst, it becomes < 0 (but unsigned) and will be rejected */
1204                             source->i_visible_width  - (left + right),
1205                             source->i_visible_height - (top  + bottom));
1206 }
1207
1208 static void ThreadExecuteCropRatio(vout_thread_t *vout,
1209                                    unsigned num, unsigned den)
1210 {
1211     const video_format_t *source = &vout->p->original;
1212     ThreadExecuteCropWindow(vout, num, den,
1213                             0, 0,
1214                             source->i_visible_width,
1215                             source->i_visible_height);
1216 }
1217
1218 static int ThreadStart(vout_thread_t *vout, const vout_display_state_t *state)
1219 {
1220     vlc_mouse_Init(&vout->p->mouse);
1221     vout->p->decoder_fifo = picture_fifo_New();
1222     vout->p->decoder_pool = NULL;
1223     vout->p->display_pool = NULL;
1224     vout->p->private_pool = NULL;
1225
1226     vout->p->filter.chain_static =
1227         filter_chain_New( vout, "video filter2", false,
1228                           VoutVideoFilterStaticAllocationSetup, NULL, vout);
1229     vout->p->filter.chain_interactive =
1230         filter_chain_New( vout, "video filter2", false,
1231                           VoutVideoFilterInteractiveAllocationSetup, NULL, vout);
1232
1233     vout_display_state_t state_default;
1234     if (!state) {
1235         VoutGetDisplayCfg(vout, &state_default.cfg, vout->p->display.title);
1236         state_default.wm_state = var_CreateGetBool(vout, "video-on-top") ? VOUT_WINDOW_STATE_ABOVE :
1237                                                                            VOUT_WINDOW_STATE_NORMAL;
1238         state_default.sar.num = 0;
1239         state_default.sar.den = 0;
1240
1241         state = &state_default;
1242     }
1243
1244     if (vout_OpenWrapper(vout, vout->p->splitter_name, state))
1245         return VLC_EGENERIC;
1246     if (vout_InitWrapper(vout))
1247         return VLC_EGENERIC;
1248     assert(vout->p->decoder_pool);
1249
1250     vout->p->displayed.current       = NULL;
1251     vout->p->displayed.next          = NULL;
1252     vout->p->displayed.decoded       = NULL;
1253     vout->p->displayed.date          = VLC_TS_INVALID;
1254     vout->p->displayed.timestamp     = VLC_TS_INVALID;
1255     vout->p->displayed.qtype         = QTYPE_NONE;
1256     vout->p->displayed.is_interlaced = false;
1257
1258     vout->p->step.last               = VLC_TS_INVALID;
1259     vout->p->step.timestamp          = VLC_TS_INVALID;
1260
1261     video_format_Print(VLC_OBJECT(vout), "original format", &vout->p->original);
1262     return VLC_SUCCESS;
1263 }
1264
1265 static void ThreadStop(vout_thread_t *vout, vout_display_state_t *state)
1266 {
1267     /* Destroy translation tables */
1268     if (vout->p->display.vd) {
1269         if (vout->p->decoder_pool) {
1270             ThreadFlush(vout, true, INT64_MAX);
1271             vout_EndWrapper(vout);
1272         }
1273         vout_CloseWrapper(vout, state);
1274     }
1275
1276     /* Destroy the video filters2 */
1277     filter_chain_Delete(vout->p->filter.chain_interactive);
1278     filter_chain_Delete(vout->p->filter.chain_static);
1279
1280     if (vout->p->decoder_fifo)
1281         picture_fifo_Delete(vout->p->decoder_fifo);
1282     assert(!vout->p->decoder_pool);
1283 }
1284
1285 static void ThreadInit(vout_thread_t *vout)
1286 {
1287     vout->p->window.is_unused = true;
1288     vout->p->window.object    = NULL;
1289     vout->p->dead             = false;
1290     vout->p->is_late_dropped  = var_InheritBool(vout, "drop-late-frames");
1291     vout->p->pause.is_on      = false;
1292     vout->p->pause.date       = VLC_TS_INVALID;
1293
1294     vout_chrono_Init(&vout->p->render, 5, 10000); /* Arbitrary initial time */
1295 }
1296
1297 static void ThreadClean(vout_thread_t *vout)
1298 {
1299     if (vout->p->window.object) {
1300         assert(vout->p->window.is_unused);
1301         vout_window_Delete(vout->p->window.object);
1302     }
1303     vout_chrono_Clean(&vout->p->render);
1304     vout->p->dead = true;
1305     vout_control_Dead(&vout->p->control);
1306 }
1307
1308 static int ThreadReinit(vout_thread_t *vout,
1309                         const vout_configuration_t *cfg)
1310 {
1311     video_format_t original;
1312     if (VoutValidateFormat(&original, cfg->fmt)) {
1313         ThreadStop(vout, NULL);
1314         ThreadClean(vout);
1315         return VLC_EGENERIC;
1316     }
1317     if (video_format_IsSimilar(&original, &vout->p->original)) {
1318         if (cfg->dpb_size <= vout->p->dpb_size)
1319             return VLC_SUCCESS;
1320         msg_Warn(vout, "DPB need to be increased");
1321     }
1322
1323     vout_display_state_t state;
1324     memset(&state, 0, sizeof(state));
1325
1326     ThreadStop(vout, &state);
1327
1328     if (!state.cfg.is_fullscreen) {
1329         state.cfg.display.width  = 0;
1330         state.cfg.display.height = 0;
1331     }
1332     state.sar.num = 0;
1333     state.sar.den = 0;
1334     /* FIXME current vout "variables" are not in sync here anymore
1335      * and I am not sure what to do */
1336
1337     vout->p->original = original;
1338     vout->p->dpb_size = cfg->dpb_size;
1339     if (ThreadStart(vout, &state)) {
1340         ThreadClean(vout);
1341         return VLC_EGENERIC;
1342     }
1343     return VLC_SUCCESS;
1344 }
1345
1346 /*****************************************************************************
1347  * Thread: video output thread
1348  *****************************************************************************
1349  * Video output thread. This function does only returns when the thread is
1350  * terminated. It handles the pictures arriving in the video heap and the
1351  * display device events.
1352  *****************************************************************************/
1353 static void *Thread(void *object)
1354 {
1355     vout_thread_t *vout = object;
1356
1357     vout_interlacing_support_t interlacing = {
1358         .is_interlaced = false,
1359         .date = mdate(),
1360     };
1361     vout_postprocessing_support_t postprocessing = {
1362         .qtype = QTYPE_NONE,
1363     };
1364
1365     mtime_t deadline = VLC_TS_INVALID;
1366     for (;;) {
1367         vout_control_cmd_t cmd;
1368
1369         /* FIXME remove thoses ugly timeouts
1370          */
1371         while (!vout_control_Pop(&vout->p->control, &cmd, deadline, 100000)) {
1372             switch(cmd.type) {
1373             case VOUT_CONTROL_INIT:
1374                 ThreadInit(vout);
1375                 if (ThreadStart(vout, NULL)) {
1376                     ThreadStop(vout, NULL);
1377                     ThreadClean(vout);
1378                     return NULL;
1379                 }
1380                 break;
1381             case VOUT_CONTROL_CLEAN:
1382                 ThreadStop(vout, NULL);
1383                 ThreadClean(vout);
1384                 return NULL;
1385             case VOUT_CONTROL_REINIT:
1386                 if (ThreadReinit(vout, cmd.u.cfg))
1387                     return NULL;
1388                 break;
1389             case VOUT_CONTROL_SUBPICTURE:
1390                 ThreadDisplaySubpicture(vout, cmd.u.subpicture);
1391                 cmd.u.subpicture = NULL;
1392                 break;
1393             case VOUT_CONTROL_FLUSH_SUBPICTURE:
1394                 ThreadFlushSubpicture(vout, cmd.u.integer);
1395                 break;
1396             case VOUT_CONTROL_OSD_TITLE:
1397                 ThreadDisplayOsdTitle(vout, cmd.u.string);
1398                 break;
1399             case VOUT_CONTROL_CHANGE_FILTERS:
1400                 ThreadChangeFilters(vout, cmd.u.string);
1401                 break;
1402             case VOUT_CONTROL_CHANGE_SUB_FILTERS:
1403                 ThreadChangeSubFilters(vout, cmd.u.string);
1404                 break;
1405             case VOUT_CONTROL_CHANGE_SUB_MARGIN:
1406                 ThreadChangeSubMargin(vout, cmd.u.integer);
1407                 break;
1408             case VOUT_CONTROL_PAUSE:
1409                 ThreadChangePause(vout, cmd.u.pause.is_on, cmd.u.pause.date);
1410                 break;
1411             case VOUT_CONTROL_FLUSH:
1412                 ThreadFlush(vout, false, cmd.u.time);
1413                 break;
1414             case VOUT_CONTROL_RESET:
1415                 ThreadReset(vout);
1416                 break;
1417             case VOUT_CONTROL_STEP:
1418                 ThreadStep(vout, cmd.u.time_ptr);
1419                 break;
1420             case VOUT_CONTROL_FULLSCREEN:
1421                 ThreadChangeFullscreen(vout, cmd.u.boolean);
1422                 break;
1423             case VOUT_CONTROL_ON_TOP:
1424                 ThreadChangeOnTop(vout, cmd.u.boolean);
1425                 break;
1426             case VOUT_CONTROL_DISPLAY_FILLED:
1427                 ThreadChangeDisplayFilled(vout, cmd.u.boolean);
1428                 break;
1429             case VOUT_CONTROL_ZOOM:
1430                 ThreadChangeZoom(vout, cmd.u.pair.a, cmd.u.pair.b);
1431                 break;
1432             case VOUT_CONTROL_ASPECT_RATIO:
1433                 ThreadChangeAspectRatio(vout, cmd.u.pair.a, cmd.u.pair.b);
1434                 break;
1435            case VOUT_CONTROL_CROP_RATIO:
1436                 ThreadExecuteCropRatio(vout, cmd.u.pair.a, cmd.u.pair.b);
1437                 break;
1438             case VOUT_CONTROL_CROP_WINDOW:
1439                 ThreadExecuteCropWindow(vout, 0, 0,
1440                                         cmd.u.window.x, cmd.u.window.y,
1441                                         cmd.u.window.width, cmd.u.window.height);
1442                 break;
1443             case VOUT_CONTROL_CROP_BORDER:
1444                 ThreadExecuteCropBorder(vout,
1445                                         cmd.u.border.left,  cmd.u.border.top,
1446                                         cmd.u.border.right, cmd.u.border.bottom);
1447                 break;
1448             default:
1449                 break;
1450             }
1451             vout_control_cmd_Clean(&cmd);
1452         }
1453
1454         ThreadManage(vout, &deadline, &interlacing, &postprocessing);
1455     }
1456 }
1457