]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
be0095a6050be72c62b8a0b9af65277a329040c8
[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->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_InheritString(vout, "video-splitter");
145     if (splitter_name && *splitter_name) {
146         if (asprintf(&vout->p->splitter_name, "%s,none", splitter_name) < 0)
147             vout->p->splitter_name = NULL;
148     } else {
149         vout->p->splitter_name = NULL;
150     }
151     free(splitter_name);
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->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->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->spu, vout->p->input, false);
197             vout->p->input = cfg->input;
198             if (vout->p->input)
199                 spu_Attach(vout->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->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->spu);
236     vout->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->spu)
363         channel = spu_RegisterChannel(vout->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->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->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->spu, subpicture);
948 }
949
950 static void ThreadFlushSubpicture(vout_thread_t *vout, int channel)
951 {
952     spu_ClearChannel(vout->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     for (int a = 0; a < 2; a++) {
1027         vlc_array_t    *array = a == 0 ? &array_static :
1028                                          &array_interactive;
1029         filter_chain_t *chain = a == 0 ? vout->p->filter.chain_static :
1030                                          vout->p->filter.chain_interactive;
1031
1032         filter_chain_Reset(chain, &fmt, &fmt);
1033         for (int i = 0; i < vlc_array_count(array); i++) {
1034             vout_filter_t *e = vlc_array_item_at_index(array, i);
1035             msg_Dbg(vout, "Adding '%s' as %s", e->name, a == 0 ? "static" : "interactive");
1036             if (!filter_chain_AppendFilter(chain, e->name, e->cfg, NULL, NULL)) {
1037                 msg_Err(vout, "Failed to add filter '%s'", e->name);
1038                 config_ChainDestroy(e->cfg);
1039             }
1040             free(e->name);
1041             free(e);
1042         }
1043         vlc_array_clear(array);
1044     }
1045
1046     vlc_mutex_unlock(&vout->p->filter.lock);
1047 }
1048
1049 static void ThreadChangeSubFilters(vout_thread_t *vout, const char *filters)
1050 {
1051     spu_ChangeFilters(vout->p->spu, filters);
1052 }
1053 static void ThreadChangeSubMargin(vout_thread_t *vout, int margin)
1054 {
1055     spu_ChangeMargin(vout->p->spu, margin);
1056 }
1057
1058 static void ThreadChangePause(vout_thread_t *vout, bool is_paused, mtime_t date)
1059 {
1060     assert(!vout->p->pause.is_on || !is_paused);
1061
1062     if (vout->p->pause.is_on) {
1063         const mtime_t duration = date - vout->p->pause.date;
1064
1065         if (vout->p->step.timestamp > VLC_TS_INVALID)
1066             vout->p->step.timestamp += duration;
1067         if (vout->p->step.last > VLC_TS_INVALID)
1068             vout->p->step.last += duration;
1069         picture_fifo_OffsetDate(vout->p->decoder_fifo, duration);
1070         if (vout->p->displayed.decoded)
1071             vout->p->displayed.decoded->date += duration;
1072         spu_OffsetSubtitleDate(vout->p->spu, duration);
1073
1074         ThreadFilterFlush(vout);
1075     } else {
1076         vout->p->step.timestamp = VLC_TS_INVALID;
1077         vout->p->step.last      = VLC_TS_INVALID;
1078     }
1079     vout->p->pause.is_on = is_paused;
1080     vout->p->pause.date  = date;
1081 }
1082
1083 static void ThreadFlush(vout_thread_t *vout, bool below, mtime_t date)
1084 {
1085     vout->p->step.timestamp = VLC_TS_INVALID;
1086     vout->p->step.last      = VLC_TS_INVALID;
1087
1088     ThreadFilterFlush(vout); /* FIXME too much */
1089
1090     picture_t *last = vout->p->displayed.decoded;
1091     if (last) {
1092         if (( below && last->date <= date) ||
1093             (!below && last->date >= date)) {
1094             picture_Release(last);
1095
1096             vout->p->displayed.decoded   = NULL;
1097             vout->p->displayed.date      = VLC_TS_INVALID;
1098             vout->p->displayed.timestamp = VLC_TS_INVALID;
1099         }
1100     }
1101
1102     picture_fifo_Flush(vout->p->decoder_fifo, date, below);
1103 }
1104
1105 static void ThreadReset(vout_thread_t *vout)
1106 {
1107     ThreadFlush(vout, true, INT64_MAX);
1108     if (vout->p->decoder_pool)
1109         picture_pool_NonEmpty(vout->p->decoder_pool, true);
1110     vout->p->pause.is_on = false;
1111     vout->p->pause.date  = mdate();
1112 }
1113
1114 static void ThreadStep(vout_thread_t *vout, mtime_t *duration)
1115 {
1116     *duration = 0;
1117
1118     if (vout->p->step.last <= VLC_TS_INVALID)
1119         vout->p->step.last = vout->p->displayed.timestamp;
1120
1121     mtime_t dummy;
1122     if (ThreadDisplayPicture(vout, true, &dummy))
1123         return;
1124
1125     vout->p->step.timestamp = vout->p->displayed.timestamp;
1126
1127     if (vout->p->step.last > VLC_TS_INVALID &&
1128         vout->p->step.timestamp > vout->p->step.last) {
1129         *duration = vout->p->step.timestamp - vout->p->step.last;
1130         vout->p->step.last = vout->p->step.timestamp;
1131         /* TODO advance subpicture by the duration ... */
1132     }
1133 }
1134
1135 static void ThreadChangeFullscreen(vout_thread_t *vout, bool fullscreen)
1136 {
1137     /* FIXME not sure setting "fullscreen" is good ... */
1138     var_SetBool(vout, "fullscreen", fullscreen);
1139     vout_SetDisplayFullscreen(vout->p->display.vd, fullscreen);
1140 }
1141
1142 static void ThreadChangeOnTop(vout_thread_t *vout, bool is_on_top)
1143 {
1144     vout_SetWindowState(vout->p->display.vd,
1145                         is_on_top ? VOUT_WINDOW_STATE_ABOVE :
1146                                     VOUT_WINDOW_STATE_NORMAL);
1147 }
1148
1149 static void ThreadChangeDisplayFilled(vout_thread_t *vout, bool is_filled)
1150 {
1151     vout_SetDisplayFilled(vout->p->display.vd, is_filled);
1152 }
1153
1154 static void ThreadChangeZoom(vout_thread_t *vout, int num, int den)
1155 {
1156     if (num * 10 < den) {
1157         num = den;
1158         den *= 10;
1159     } else if (num > den * 10) {
1160         num = den * 10;
1161     }
1162
1163     vout_SetDisplayZoom(vout->p->display.vd, num, den);
1164 }
1165
1166 static void ThreadChangeAspectRatio(vout_thread_t *vout,
1167                                     unsigned num, unsigned den)
1168 {
1169     const video_format_t *source = &vout->p->original;
1170
1171     if (num > 0 && den > 0) {
1172         num *= source->i_visible_height;
1173         den *= source->i_visible_width;
1174         vlc_ureduce(&num, &den, num, den, 0);
1175     }
1176     vout_SetDisplayAspect(vout->p->display.vd, num, den);
1177 }
1178
1179
1180 static void ThreadExecuteCropWindow(vout_thread_t *vout,
1181                                     unsigned crop_num, unsigned crop_den,
1182                                     unsigned x, unsigned y,
1183                                     unsigned width, unsigned height)
1184 {
1185     const video_format_t *source = &vout->p->original;
1186
1187     vout_SetDisplayCrop(vout->p->display.vd,
1188                         crop_num, crop_den,
1189                         source->i_x_offset + x,
1190                         source->i_y_offset + y,
1191                         width, height);
1192 }
1193 static void ThreadExecuteCropBorder(vout_thread_t *vout,
1194                                     unsigned left, unsigned top,
1195                                     unsigned right, unsigned bottom)
1196 {
1197     const video_format_t *source = &vout->p->original;
1198     ThreadExecuteCropWindow(vout, 0, 0,
1199                             left,
1200                             top,
1201                             /* At worst, it becomes < 0 (but unsigned) and will be rejected */
1202                             source->i_visible_width  - (left + right),
1203                             source->i_visible_height - (top  + bottom));
1204 }
1205
1206 static void ThreadExecuteCropRatio(vout_thread_t *vout,
1207                                    unsigned num, unsigned den)
1208 {
1209     const video_format_t *source = &vout->p->original;
1210     ThreadExecuteCropWindow(vout, num, den,
1211                             0, 0,
1212                             source->i_visible_width,
1213                             source->i_visible_height);
1214 }
1215
1216 static int ThreadStart(vout_thread_t *vout, const vout_display_state_t *state)
1217 {
1218     vlc_mouse_Init(&vout->p->mouse);
1219     vout->p->decoder_fifo = picture_fifo_New();
1220     vout->p->decoder_pool = NULL;
1221     vout->p->display_pool = NULL;
1222     vout->p->private_pool = NULL;
1223
1224     vout->p->filter.chain_static =
1225         filter_chain_New( vout, "video filter2", false,
1226                           VoutVideoFilterStaticAllocationSetup, NULL, vout);
1227     vout->p->filter.chain_interactive =
1228         filter_chain_New( vout, "video filter2", false,
1229                           VoutVideoFilterInteractiveAllocationSetup, NULL, vout);
1230
1231     vout_display_state_t state_default;
1232     if (!state) {
1233         VoutGetDisplayCfg(vout, &state_default.cfg, vout->p->display.title);
1234         state_default.wm_state = var_CreateGetBool(vout, "video-on-top") ? VOUT_WINDOW_STATE_ABOVE :
1235                                                                            VOUT_WINDOW_STATE_NORMAL;
1236         state_default.sar.num = 0;
1237         state_default.sar.den = 0;
1238
1239         state = &state_default;
1240     }
1241
1242     if (vout_OpenWrapper(vout, vout->p->splitter_name, state))
1243         return VLC_EGENERIC;
1244     if (vout_InitWrapper(vout))
1245         return VLC_EGENERIC;
1246     assert(vout->p->decoder_pool);
1247
1248     vout->p->displayed.current       = NULL;
1249     vout->p->displayed.next          = NULL;
1250     vout->p->displayed.decoded       = NULL;
1251     vout->p->displayed.date          = VLC_TS_INVALID;
1252     vout->p->displayed.timestamp     = VLC_TS_INVALID;
1253     vout->p->displayed.qtype         = QTYPE_NONE;
1254     vout->p->displayed.is_interlaced = false;
1255
1256     vout->p->step.last               = VLC_TS_INVALID;
1257     vout->p->step.timestamp          = VLC_TS_INVALID;
1258
1259     video_format_Print(VLC_OBJECT(vout), "original format", &vout->p->original);
1260     return VLC_SUCCESS;
1261 }
1262
1263 static void ThreadStop(vout_thread_t *vout, vout_display_state_t *state)
1264 {
1265     /* Destroy translation tables */
1266     if (vout->p->display.vd) {
1267         if (vout->p->decoder_pool) {
1268             ThreadFlush(vout, true, INT64_MAX);
1269             vout_EndWrapper(vout);
1270         }
1271         vout_CloseWrapper(vout, state);
1272     }
1273
1274     /* Destroy the video filters2 */
1275     filter_chain_Delete(vout->p->filter.chain_interactive);
1276     filter_chain_Delete(vout->p->filter.chain_static);
1277
1278     if (vout->p->decoder_fifo)
1279         picture_fifo_Delete(vout->p->decoder_fifo);
1280     assert(!vout->p->decoder_pool);
1281 }
1282
1283 static void ThreadInit(vout_thread_t *vout)
1284 {
1285     vout->p->window.is_unused = true;
1286     vout->p->window.object    = NULL;
1287     vout->p->dead             = false;
1288     vout->p->is_late_dropped  = var_InheritBool(vout, "drop-late-frames");
1289     vout->p->pause.is_on      = false;
1290     vout->p->pause.date       = VLC_TS_INVALID;
1291
1292     vout_chrono_Init(&vout->p->render, 5, 10000); /* Arbitrary initial time */
1293 }
1294
1295 static void ThreadClean(vout_thread_t *vout)
1296 {
1297     if (vout->p->window.object) {
1298         assert(vout->p->window.is_unused);
1299         vout_window_Delete(vout->p->window.object);
1300     }
1301     vout_chrono_Clean(&vout->p->render);
1302     vout->p->dead = true;
1303     vout_control_Dead(&vout->p->control);
1304 }
1305
1306 static int ThreadReinit(vout_thread_t *vout,
1307                         const vout_configuration_t *cfg)
1308 {
1309     video_format_t original;
1310     if (VoutValidateFormat(&original, cfg->fmt)) {
1311         ThreadStop(vout, NULL);
1312         ThreadClean(vout);
1313         return VLC_EGENERIC;
1314     }
1315     if (video_format_IsSimilar(&original, &vout->p->original)) {
1316         if (cfg->dpb_size <= vout->p->dpb_size)
1317             return VLC_SUCCESS;
1318         msg_Warn(vout, "DPB need to be increased");
1319     }
1320
1321     vout_display_state_t state;
1322     memset(&state, 0, sizeof(state));
1323
1324     ThreadStop(vout, &state);
1325
1326     if (!state.cfg.is_fullscreen) {
1327         state.cfg.display.width  = 0;
1328         state.cfg.display.height = 0;
1329     }
1330     state.sar.num = 0;
1331     state.sar.den = 0;
1332     /* FIXME current vout "variables" are not in sync here anymore
1333      * and I am not sure what to do */
1334
1335     vout->p->original = original;
1336     vout->p->dpb_size = cfg->dpb_size;
1337     if (ThreadStart(vout, &state)) {
1338         ThreadClean(vout);
1339         return VLC_EGENERIC;
1340     }
1341     return VLC_SUCCESS;
1342 }
1343
1344 /*****************************************************************************
1345  * Thread: video output thread
1346  *****************************************************************************
1347  * Video output thread. This function does only returns when the thread is
1348  * terminated. It handles the pictures arriving in the video heap and the
1349  * display device events.
1350  *****************************************************************************/
1351 static void *Thread(void *object)
1352 {
1353     vout_thread_t *vout = object;
1354
1355     vout_interlacing_support_t interlacing = {
1356         .is_interlaced = false,
1357         .date = mdate(),
1358     };
1359     vout_postprocessing_support_t postprocessing = {
1360         .qtype = QTYPE_NONE,
1361     };
1362
1363     mtime_t deadline = VLC_TS_INVALID;
1364     for (;;) {
1365         vout_control_cmd_t cmd;
1366
1367         /* FIXME remove thoses ugly timeouts
1368          */
1369         while (!vout_control_Pop(&vout->p->control, &cmd, deadline, 100000)) {
1370             switch(cmd.type) {
1371             case VOUT_CONTROL_INIT:
1372                 ThreadInit(vout);
1373                 if (ThreadStart(vout, NULL)) {
1374                     ThreadStop(vout, NULL);
1375                     ThreadClean(vout);
1376                     return NULL;
1377                 }
1378                 break;
1379             case VOUT_CONTROL_CLEAN:
1380                 ThreadStop(vout, NULL);
1381                 ThreadClean(vout);
1382                 return NULL;
1383             case VOUT_CONTROL_REINIT:
1384                 if (ThreadReinit(vout, cmd.u.cfg))
1385                     return NULL;
1386                 break;
1387             case VOUT_CONTROL_SUBPICTURE:
1388                 ThreadDisplaySubpicture(vout, cmd.u.subpicture);
1389                 cmd.u.subpicture = NULL;
1390                 break;
1391             case VOUT_CONTROL_FLUSH_SUBPICTURE:
1392                 ThreadFlushSubpicture(vout, cmd.u.integer);
1393                 break;
1394             case VOUT_CONTROL_OSD_TITLE:
1395                 ThreadDisplayOsdTitle(vout, cmd.u.string);
1396                 break;
1397             case VOUT_CONTROL_CHANGE_FILTERS:
1398                 ThreadChangeFilters(vout, cmd.u.string);
1399                 break;
1400             case VOUT_CONTROL_CHANGE_SUB_FILTERS:
1401                 ThreadChangeSubFilters(vout, cmd.u.string);
1402                 break;
1403             case VOUT_CONTROL_CHANGE_SUB_MARGIN:
1404                 ThreadChangeSubMargin(vout, cmd.u.integer);
1405                 break;
1406             case VOUT_CONTROL_PAUSE:
1407                 ThreadChangePause(vout, cmd.u.pause.is_on, cmd.u.pause.date);
1408                 break;
1409             case VOUT_CONTROL_FLUSH:
1410                 ThreadFlush(vout, false, cmd.u.time);
1411                 break;
1412             case VOUT_CONTROL_RESET:
1413                 ThreadReset(vout);
1414                 break;
1415             case VOUT_CONTROL_STEP:
1416                 ThreadStep(vout, cmd.u.time_ptr);
1417                 break;
1418             case VOUT_CONTROL_FULLSCREEN:
1419                 ThreadChangeFullscreen(vout, cmd.u.boolean);
1420                 break;
1421             case VOUT_CONTROL_ON_TOP:
1422                 ThreadChangeOnTop(vout, cmd.u.boolean);
1423                 break;
1424             case VOUT_CONTROL_DISPLAY_FILLED:
1425                 ThreadChangeDisplayFilled(vout, cmd.u.boolean);
1426                 break;
1427             case VOUT_CONTROL_ZOOM:
1428                 ThreadChangeZoom(vout, cmd.u.pair.a, cmd.u.pair.b);
1429                 break;
1430             case VOUT_CONTROL_ASPECT_RATIO:
1431                 ThreadChangeAspectRatio(vout, cmd.u.pair.a, cmd.u.pair.b);
1432                 break;
1433            case VOUT_CONTROL_CROP_RATIO:
1434                 ThreadExecuteCropRatio(vout, cmd.u.pair.a, cmd.u.pair.b);
1435                 break;
1436             case VOUT_CONTROL_CROP_WINDOW:
1437                 ThreadExecuteCropWindow(vout, 0, 0,
1438                                         cmd.u.window.x, cmd.u.window.y,
1439                                         cmd.u.window.width, cmd.u.window.height);
1440                 break;
1441             case VOUT_CONTROL_CROP_BORDER:
1442                 ThreadExecuteCropBorder(vout,
1443                                         cmd.u.border.left,  cmd.u.border.top,
1444                                         cmd.u.border.right, cmd.u.border.bottom);
1445                 break;
1446             default:
1447                 break;
1448             }
1449             vout_control_cmd_Clean(&cmd);
1450         }
1451
1452         ThreadManage(vout, &deadline, &interlacing, &postprocessing);
1453     }
1454 }
1455