]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
Clean up vout object creation/destruction.
[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
48 #include <libvlc.h>
49 #include "vout_internal.h"
50 #include "interlacing.h"
51 #include "postprocessing.h"
52 #include "display.h"
53
54 /*****************************************************************************
55  * Local prototypes
56  *****************************************************************************/
57 static void *Thread(void *);
58 static void VoutDestructor(vlc_object_t *);
59
60 /* Maximum delay between 2 displayed pictures.
61  * XXX it is needed for now but should be removed in the long term.
62  */
63 #define VOUT_REDISPLAY_DELAY (INT64_C(80000))
64
65 /**
66  * Late pictures having a delay higher than this value are thrashed.
67  */
68 #define VOUT_DISPLAY_LATE_THRESHOLD (INT64_C(20000))
69
70 /* Better be in advance when awakening than late... */
71 #define VOUT_MWAIT_TOLERANCE (INT64_C(1000))
72
73 /* */
74 static int VoutValidateFormat(video_format_t *dst,
75                               const video_format_t *src)
76 {
77     if (src->i_width <= 0 || src->i_height <= 0)
78         return VLC_EGENERIC;
79     if (src->i_sar_num <= 0 || src->i_sar_den <= 0)
80         return VLC_EGENERIC;
81
82     /* */
83     video_format_Copy(dst, src);
84     dst->i_chroma = vlc_fourcc_GetCodec(VIDEO_ES, src->i_chroma);
85     vlc_ureduce( &dst->i_sar_num, &dst->i_sar_den,
86                  src->i_sar_num,  src->i_sar_den, 50000 );
87     if (dst->i_sar_num <= 0 || dst->i_sar_den <= 0) {
88         dst->i_sar_num = 1;
89         dst->i_sar_den = 1;
90     }
91     video_format_FixRgb(dst);
92     return VLC_SUCCESS;
93 }
94
95 #undef vout_Request
96 /*****************************************************************************
97  * vout_Request: find a video output thread, create one, or destroy one.
98  *****************************************************************************
99  * This function looks for a video output thread matching the current
100  * properties. If not found, it spawns a new one.
101  *****************************************************************************/
102 vout_thread_t *vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
103                              video_format_t *p_fmt )
104 {
105     if( !p_fmt )
106     {
107         /* Video output is no longer used.
108          * TODO: support for reusing video outputs with proper _thread-safe_
109          * reference handling. */
110         if( p_vout )
111             vout_CloseAndRelease( p_vout );
112         return NULL;
113     }
114
115     /* If a video output was provided, lock it, otherwise look for one. */
116     if( p_vout )
117     {
118         vlc_object_hold( p_vout );
119     }
120
121     /* TODO: find a suitable unused video output */
122
123     /* If we now have a video output, check it has the right properties */
124     if( p_vout )
125     {
126         if( !video_format_IsSimilar( &p_vout->p->original, p_fmt ) )
127         {
128             /* We are not interested in this format, close this vout */
129             vout_CloseAndRelease( p_vout );
130             vlc_object_release( p_vout );
131             p_vout = NULL;
132         }
133         else
134         {
135             /* This video output is cool! Hijack it. */
136             vlc_object_release( p_vout );
137         }
138
139         if( p_vout )
140         {
141             msg_Dbg( p_this, "reusing provided vout" );
142
143             spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), false );
144             vlc_object_detach( p_vout );
145
146             vlc_object_attach( p_vout, p_this );
147             spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), true );
148         }
149     }
150
151     if( !p_vout )
152     {
153         msg_Dbg( p_this, "no usable vout present, spawning one" );
154
155         p_vout = vout_Create( p_this, p_fmt );
156     }
157
158     return p_vout;
159 }
160
161 /*****************************************************************************
162  * vout_Create: creates a new video output thread
163  *****************************************************************************
164  * This function creates a new video output thread, and returns a pointer
165  * to its description. On error, it returns NULL.
166  *****************************************************************************/
167 vout_thread_t * (vout_Create)( vlc_object_t *p_parent, video_format_t *p_fmt )
168 {
169     video_format_t original;
170     if (VoutValidateFormat(&original, p_fmt))
171         return NULL;
172
173     /* Allocate descriptor */
174     vout_thread_t *p_vout = vlc_custom_create(p_parent,
175                                               sizeof(*p_vout) + sizeof(*p_vout->p),
176                                               VLC_OBJECT_VOUT, "video output");
177     if (!p_vout) {
178         video_format_Clean(&original);
179         return NULL;
180     }
181
182     /* */
183     p_vout->p = (vout_thread_sys_t*)&p_vout[1];
184
185     p_vout->p->original = original;
186
187     vout_control_Init( &p_vout->p->control );
188     vout_control_PushVoid( &p_vout->p->control, VOUT_CONTROL_INIT );
189
190     vout_statistic_Init( &p_vout->p->statistic );
191     p_vout->p->i_par_num =
192     p_vout->p->i_par_den = 1;
193
194     vout_snapshot_Init( &p_vout->p->snapshot );
195
196     /* Initialize locks */
197     vlc_mutex_init( &p_vout->p->picture_lock );
198     vlc_mutex_init( &p_vout->p->vfilter_lock );
199
200     /* Attach the new object now so we can use var inheritance below */
201     vlc_object_attach( p_vout, p_parent );
202
203     /* Initialize subpicture unit */
204     p_vout->p->p_spu = spu_Create( p_vout );
205
206     /* */
207     spu_Init( p_vout->p->p_spu );
208
209     /* Take care of some "interface/control" related initialisations */
210     vout_IntfInit( p_vout );
211
212     /* Get splitter name if present */
213     char *splitter_name = var_GetNonEmptyString(p_vout, "vout-filter");
214     if (splitter_name) {
215         if (asprintf(&p_vout->p->splitter_name, "%s,none", splitter_name) < 0)
216             p_vout->p->splitter_name = NULL;
217         free(splitter_name);
218     } else {
219         p_vout->p->splitter_name = NULL;
220     }
221
222     /* */
223     vout_InitInterlacingSupport( p_vout, p_vout->p->displayed.is_interlaced );
224
225     /* */
226     vlc_object_set_destructor( p_vout, VoutDestructor );
227
228     /* */
229     if( vlc_clone( &p_vout->p->thread, Thread, p_vout,
230                    VLC_THREAD_PRIORITY_OUTPUT ) )
231     {
232         vlc_object_release( p_vout );
233         return NULL;
234     }
235     spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), true );
236
237     vout_control_WaitEmpty( &p_vout->p->control );
238
239     if (p_vout->p->dead )
240     {
241         msg_Err( p_vout, "video output creation failed" );
242         vout_CloseAndRelease( p_vout );
243         return NULL;
244     }
245
246     return p_vout;
247 }
248
249 /*****************************************************************************
250  * vout_Close: Close a vout created by vout_Create.
251  *****************************************************************************
252  * You HAVE to call it on vout created by vout_Create before vlc_object_release.
253  * You should NEVER call it on vout not obtained through vout_Create
254  * (like with vout_Request or vlc_object_find.)
255  * You can use vout_CloseAndRelease() as a convenience method.
256  *****************************************************************************/
257 void vout_Close( vout_thread_t *p_vout )
258 {
259     assert( p_vout );
260
261     spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), false );
262
263     vout_snapshot_End( &p_vout->p->snapshot );
264
265     vout_control_PushVoid( &p_vout->p->control, VOUT_CONTROL_CLEAN );
266     vlc_join( p_vout->p->thread, NULL );
267 }
268
269 /* */
270 static void VoutDestructor( vlc_object_t * p_this )
271 {
272     vout_thread_t *p_vout = (vout_thread_t *)p_this;
273
274     /* Make sure the vout was stopped first */
275     //assert( !p_vout->p_module );
276
277     free( p_vout->p->splitter_name );
278
279     /* */
280     spu_Destroy( p_vout->p->p_spu );
281
282     /* Destroy the locks */
283     vlc_mutex_destroy( &p_vout->p->picture_lock );
284     vlc_mutex_destroy( &p_vout->p->vfilter_lock );
285     vout_control_Clean( &p_vout->p->control );
286
287     /* */
288     vout_statistic_Clean( &p_vout->p->statistic );
289
290     /* */
291     vout_snapshot_Clean( &p_vout->p->snapshot );
292
293     video_format_Clean( &p_vout->p->original );
294 }
295
296 /* */
297 void vout_ChangePause(vout_thread_t *vout, bool is_paused, mtime_t date)
298 {
299     vout_control_cmd_t cmd;
300     vout_control_cmd_Init(&cmd, VOUT_CONTROL_PAUSE);
301     cmd.u.pause.is_on = is_paused;
302     cmd.u.pause.date  = date;
303     vout_control_Push(&vout->p->control, &cmd);
304
305     vout_control_WaitEmpty(&vout->p->control);
306 }
307
308 void vout_GetResetStatistic( vout_thread_t *p_vout, int *pi_displayed, int *pi_lost )
309 {
310     vout_statistic_GetReset( &p_vout->p->statistic,
311                              pi_displayed, pi_lost );
312 }
313
314 void vout_Flush(vout_thread_t *vout, mtime_t date)
315 {
316     vout_control_PushTime(&vout->p->control, VOUT_CONTROL_FLUSH, date);
317     vout_control_WaitEmpty(&vout->p->control);
318 }
319
320 void vout_Reset(vout_thread_t *vout)
321 {
322     vout_control_PushVoid(&vout->p->control, VOUT_CONTROL_RESET);
323     vout_control_WaitEmpty(&vout->p->control);
324 }
325
326 bool vout_IsEmpty(vout_thread_t *vout)
327 {
328     vlc_mutex_lock(&vout->p->picture_lock);
329
330     picture_t *picture = picture_fifo_Peek(vout->p->decoder_fifo);
331     if (picture)
332         picture_Release(picture);
333
334     vlc_mutex_unlock(&vout->p->picture_lock);
335
336     return !picture;
337 }
338
339 void vout_FixLeaks( vout_thread_t *vout )
340 {
341     vlc_mutex_lock(&vout->p->picture_lock);
342
343     picture_t *picture = picture_fifo_Peek(vout->p->decoder_fifo);
344     if (!picture) {
345         picture = picture_pool_Get(vout->p->decoder_pool);
346     }
347
348     if (picture) {
349         picture_Release(picture);
350         /* Not all pictures has been displayed yet or some are
351          * free */
352         vlc_mutex_unlock(&vout->p->picture_lock);
353         return;
354     }
355
356     /* There is no reason that no pictures are available, force one
357      * from the pool, becarefull with it though */
358     msg_Err(vout, "pictures leaked, trying to workaround");
359
360     /* */
361     picture_pool_NonEmpty(vout->p->decoder_pool, false);
362
363     vlc_mutex_unlock(&vout->p->picture_lock);
364 }
365 void vout_NextPicture(vout_thread_t *vout, mtime_t *duration)
366 {
367     vout_control_cmd_t cmd;
368     vout_control_cmd_Init(&cmd, VOUT_CONTROL_STEP);
369     cmd.u.time_ptr = duration;
370
371     vout_control_Push(&vout->p->control, &cmd);
372     vout_control_WaitEmpty(&vout->p->control);
373 }
374
375 void vout_DisplayTitle(vout_thread_t *vout, const char *title)
376 {
377     assert(title);
378     vout_control_PushString(&vout->p->control, VOUT_CONTROL_OSD_TITLE, title);
379 }
380
381 void vout_PutSubpicture( vout_thread_t *vout, subpicture_t *subpic )
382 {
383     spu_DisplaySubpicture(vout->p->p_spu, subpic);
384 }
385 int vout_RegisterSubpictureChannel( vout_thread_t *vout )
386 {
387     return spu_RegisterChannel(vout->p->p_spu);
388 }
389 void vout_FlushSubpictureChannel( vout_thread_t *vout, int channel )
390 {
391     spu_ClearChannel(vout->p->p_spu, channel);
392 }
393
394 spu_t *vout_GetSpu( vout_thread_t *p_vout )
395 {
396     return p_vout->p->p_spu;
397 }
398
399 /* vout_Control* are usable by anyone at anytime */
400 void vout_ControlChangeFullscreen(vout_thread_t *vout, bool fullscreen)
401 {
402     vout_control_PushBool(&vout->p->control, VOUT_CONTROL_FULLSCREEN,
403                           fullscreen);
404 }
405 void vout_ControlChangeOnTop(vout_thread_t *vout, bool is_on_top)
406 {
407     vout_control_PushBool(&vout->p->control, VOUT_CONTROL_ON_TOP,
408                           is_on_top);
409 }
410 void vout_ControlChangeDisplayFilled(vout_thread_t *vout, bool is_filled)
411 {
412     vout_control_PushBool(&vout->p->control, VOUT_CONTROL_DISPLAY_FILLED,
413                           is_filled);
414 }
415 void vout_ControlChangeZoom(vout_thread_t *vout, int num, int den)
416 {
417     vout_control_PushPair(&vout->p->control, VOUT_CONTROL_ZOOM,
418                           num, den);
419 }
420 void vout_ControlChangeSampleAspectRatio(vout_thread_t *vout,
421                                          unsigned num, unsigned den)
422 {
423     vout_control_PushPair(&vout->p->control, VOUT_CONTROL_ASPECT_RATIO,
424                           num, den);
425 }
426 void vout_ControlChangeCropRatio(vout_thread_t *vout,
427                                  unsigned num, unsigned den)
428 {
429     vout_control_PushPair(&vout->p->control, VOUT_CONTROL_CROP_RATIO,
430                           num, den);
431 }
432 void vout_ControlChangeCropWindow(vout_thread_t *vout,
433                                   int x, int y, int width, int height)
434 {
435     vout_control_cmd_t cmd;
436     vout_control_cmd_Init(&cmd, VOUT_CONTROL_CROP_WINDOW);
437     cmd.u.window.x      = x;
438     cmd.u.window.y      = y;
439     cmd.u.window.width  = width;
440     cmd.u.window.height = height;
441
442     vout_control_Push(&vout->p->control, &cmd);
443 }
444 void vout_ControlChangeCropBorder(vout_thread_t *vout,
445                                   int left, int top, int right, int bottom)
446 {
447     vout_control_cmd_t cmd;
448     vout_control_cmd_Init(&cmd, VOUT_CONTROL_CROP_BORDER);
449     cmd.u.border.left   = left;
450     cmd.u.border.top    = top;
451     cmd.u.border.right  = right;
452     cmd.u.border.bottom = bottom;
453
454     vout_control_Push(&vout->p->control, &cmd);
455 }
456 void vout_ControlChangeFilters(vout_thread_t *vout, const char *filters)
457 {
458     vout_control_PushString(&vout->p->control, VOUT_CONTROL_CHANGE_FILTERS,
459                             filters);
460 }
461
462 /* */
463 static picture_t *VoutVideoFilterNewPicture(filter_t *filter)
464 {
465     vout_thread_t *vout = (vout_thread_t*)filter->p_owner;
466     return picture_pool_Get(vout->p->private_pool);
467 }
468 static void VoutVideoFilterDelPicture(filter_t *filter, picture_t *picture)
469 {
470     VLC_UNUSED(filter);
471     picture_Release(picture);
472 }
473 static int VoutVideoFilterAllocationSetup(filter_t *filter, void *data)
474 {
475     filter->pf_video_buffer_new = VoutVideoFilterNewPicture;
476     filter->pf_video_buffer_del = VoutVideoFilterDelPicture;
477     filter->p_owner             = data; /* vout */
478     return VLC_SUCCESS;
479 }
480
481 /* */
482 static int ThreadDisplayPicture(vout_thread_t *vout,
483                                 bool now, mtime_t *deadline)
484 {
485     vout_display_t *vd = vout->p->display.vd;
486     int displayed_count = 0;
487     int lost_count = 0;
488
489     for (;;) {
490         const mtime_t date = mdate();
491         const bool is_paused = vout->p->pause.is_on;
492         bool redisplay = is_paused && !now && vout->p->displayed.decoded;
493         bool is_forced;
494
495         /* FIXME/XXX we must redisplay the last decoded picture (because
496          * of potential vout updated, or filters update or SPU update)
497          * For now a high update period is needed but it coulmd be removed
498          * if and only if:
499          * - vout module emits events from theselves.
500          * - *and* SPU is modified to emit an event or a deadline when needed.
501          *
502          * So it will be done latter.
503          */
504         if (!redisplay) {
505             picture_t *peek = picture_fifo_Peek(vout->p->decoder_fifo);
506             if (peek) {
507                 is_forced = peek->b_force || is_paused || now;
508                 *deadline = (is_forced ? date : peek->date) - vout_chrono_GetHigh(&vout->p->render);
509                 picture_Release(peek);
510             } else {
511                 redisplay = true;
512             }
513         }
514         if (redisplay) {
515              /* FIXME a better way for this delay is needed */
516             const mtime_t date_update = vout->p->displayed.date + VOUT_REDISPLAY_DELAY;
517             if (date_update > date || !vout->p->displayed.decoded) {
518                 *deadline = vout->p->displayed.decoded ? date_update : VLC_TS_INVALID;
519                 break;
520             }
521             /* */
522             is_forced = true;
523             *deadline = date - vout_chrono_GetHigh(&vout->p->render);
524         }
525         if (*deadline > VOUT_MWAIT_TOLERANCE)
526             *deadline -= VOUT_MWAIT_TOLERANCE;
527
528         /* If we are too early and can wait, do it */
529         if (date < *deadline && !now)
530             break;
531
532         picture_t *decoded;
533         if (redisplay) {
534             decoded = vout->p->displayed.decoded;
535             vout->p->displayed.decoded = NULL;
536         } else {
537             decoded = picture_fifo_Pop(vout->p->decoder_fifo);
538             assert(decoded);
539             if (!is_forced && !vout->p->is_late_dropped) {
540                 const mtime_t predicted = date + vout_chrono_GetLow(&vout->p->render);
541                 const mtime_t late = predicted - decoded->date;
542                 if (late > 0) {
543                     msg_Dbg(vout, "picture might be displayed late (missing %d ms)", (int)(late/1000));
544                     if (late > VOUT_DISPLAY_LATE_THRESHOLD) {
545                         msg_Warn(vout, "rejected picture because of render time");
546                         /* TODO */
547                         picture_Release(decoded);
548                         lost_count++;
549                         break;
550                     }
551                 }
552             }
553
554             vout->p->displayed.is_interlaced = !decoded->b_progressive;
555             vout->p->displayed.qtype         = decoded->i_qtype;
556         }
557         vout->p->displayed.timestamp = decoded->date;
558
559         /* */
560         if (vout->p->displayed.decoded)
561             picture_Release(vout->p->displayed.decoded);
562         picture_Hold(decoded);
563         vout->p->displayed.decoded = decoded;
564
565         /* */
566         vout_chrono_Start(&vout->p->render);
567
568         picture_t *filtered = NULL;
569         if (decoded) {
570             vlc_mutex_lock(&vout->p->vfilter_lock);
571             filtered = filter_chain_VideoFilter(vout->p->vfilter_chain, decoded);
572             //assert(filtered == decoded); // TODO implement
573             vlc_mutex_unlock(&vout->p->vfilter_lock);
574             if (!filtered)
575                 continue;
576         }
577
578         /*
579          * Check for subpictures to display
580          */
581         const bool do_snapshot = vout_snapshot_IsRequested(&vout->p->snapshot);
582         mtime_t spu_render_time = is_forced ? mdate() : filtered->date;
583         if (vout->p->pause.is_on)
584             spu_render_time = vout->p->pause.date;
585         else
586             spu_render_time = filtered->date > 1 ? filtered->date : mdate();
587
588         subpicture_t *subpic = spu_SortSubpictures(vout->p->p_spu,
589                                                    spu_render_time,
590                                                    do_snapshot);
591         /*
592          * Perform rendering
593          *
594          * We have to:
595          * - be sure to end up with a direct buffer.
596          * - blend subtitles, and in a fast access buffer
597          */
598         picture_t *direct = NULL;
599         if (filtered &&
600             (vout->p->decoder_pool != vout->p->display_pool || subpic)) {
601             picture_t *render;
602             if (vout->p->is_decoder_pool_slow)
603                 render = picture_NewFromFormat(&vd->source);
604             else if (vout->p->decoder_pool != vout->p->display_pool)
605                 render = picture_pool_Get(vout->p->display_pool);
606             else
607                 render = picture_pool_Get(vout->p->private_pool);
608
609             if (render) {
610                 picture_Copy(render, filtered);
611
612                 spu_RenderSubpictures(vout->p->p_spu,
613                                       render, &vd->source,
614                                       subpic, &vd->source, spu_render_time);
615             }
616             if (vout->p->is_decoder_pool_slow) {
617                 direct = picture_pool_Get(vout->p->display_pool);
618                 if (direct)
619                     picture_Copy(direct, render);
620                 picture_Release(render);
621
622             } else {
623                 direct = render;
624             }
625             picture_Release(filtered);
626             filtered = NULL;
627         } else {
628             direct = filtered;
629         }
630
631         /*
632          * Take a snapshot if requested
633          */
634         if (direct && do_snapshot)
635             vout_snapshot_Set(&vout->p->snapshot, &vd->source, direct);
636
637         /* Render the direct buffer returned by vout_RenderPicture */
638         if (direct) {
639             vout_RenderWrapper(vout, direct);
640
641             vout_chrono_Stop(&vout->p->render);
642 #if 0
643             {
644             static int i = 0;
645             if (((i++)%10) == 0)
646                 msg_Info(vout, "render: avg %d ms var %d ms",
647                          (int)(vout->p->render.avg/1000), (int)(vout->p->render.var/1000));
648             }
649 #endif
650         }
651
652         /* Wait the real date (for rendering jitter) */
653         if (!is_forced)
654             mwait(decoded->date);
655
656         /* Display the direct buffer returned by vout_RenderPicture */
657         vout->p->displayed.date = mdate();
658         if (direct)
659             vout_DisplayWrapper(vout, direct);
660
661         displayed_count++;
662         break;
663     }
664
665     vout_statistic_Update(&vout->p->statistic, displayed_count, lost_count);
666     if (displayed_count <= 0)
667         return VLC_EGENERIC;
668     return VLC_SUCCESS;
669 }
670
671 static void ThreadManage(vout_thread_t *vout,
672                          mtime_t *deadline,
673                          vout_interlacing_support_t *interlacing,
674                          vout_postprocessing_support_t *postprocessing)
675 {
676     vlc_mutex_lock(&vout->p->picture_lock);
677
678     *deadline = VLC_TS_INVALID;
679     ThreadDisplayPicture(vout, false, deadline);
680
681     const int  picture_qtype      = vout->p->displayed.qtype;
682     const bool picture_interlaced = vout->p->displayed.is_interlaced;
683
684     vlc_mutex_unlock(&vout->p->picture_lock);
685
686     /* Post processing */
687     vout_SetPostProcessingState(vout, postprocessing, picture_qtype);
688
689     /* Deinterlacing */
690     vout_SetInterlacingState(vout, interlacing, picture_interlaced);
691
692     vout_ManageWrapper(vout);
693 }
694
695 static void ThreadDisplayOsdTitle(vout_thread_t *vout, const char *string)
696 {
697     if (!vout->p->title.show)
698         return;
699
700     vout_OSDText(vout, SPU_DEFAULT_CHANNEL,
701                  vout->p->title.position, INT64_C(1000) * vout->p->title.timeout,
702                  string);
703 }
704
705 static void ThreadChangeFilters(vout_thread_t *vout, const char *filters)
706 {
707     es_format_t fmt;
708     es_format_Init(&fmt, VIDEO_ES, vout->p->original.i_chroma);
709     fmt.video = vout->p->original;
710
711     vlc_mutex_lock(&vout->p->vfilter_lock);
712
713     filter_chain_Reset(vout->p->vfilter_chain, &fmt, &fmt);
714     if (filter_chain_AppendFromString(vout->p->vfilter_chain,
715                                       filters) < 0)
716         msg_Err(vout, "Video filter chain creation failed");
717
718     vlc_mutex_unlock(&vout->p->vfilter_lock);
719 }
720
721 static void ThreadChangePause(vout_thread_t *vout, bool is_paused, mtime_t date)
722 {
723     assert(!vout->p->pause.is_on || !is_paused);
724
725     if (vout->p->pause.is_on) {
726         const mtime_t duration = date - vout->p->pause.date;
727
728         if (vout->p->step.timestamp > VLC_TS_INVALID)
729             vout->p->step.timestamp += duration;
730         if (vout->p->step.last > VLC_TS_INVALID)
731             vout->p->step.last += duration;
732         picture_fifo_OffsetDate(vout->p->decoder_fifo, duration);
733         if (vout->p->displayed.decoded)
734             vout->p->displayed.decoded->date += duration;
735
736         spu_OffsetSubtitleDate(vout->p->p_spu, duration);
737     } else {
738         vout->p->step.timestamp = VLC_TS_INVALID;
739         vout->p->step.last      = VLC_TS_INVALID;
740     }
741     vout->p->pause.is_on = is_paused;
742     vout->p->pause.date  = date;
743 }
744
745 static void ThreadFlush(vout_thread_t *vout, bool below, mtime_t date)
746 {
747     vout->p->step.timestamp = VLC_TS_INVALID;
748     vout->p->step.last      = VLC_TS_INVALID;
749
750     picture_t *last = vout->p->displayed.decoded;
751     if (last) {
752         if (( below && last->date <= date) ||
753             (!below && last->date >= date)) {
754             picture_Release(last);
755
756             vout->p->displayed.decoded   = NULL;
757             vout->p->displayed.date      = VLC_TS_INVALID;
758             vout->p->displayed.timestamp = VLC_TS_INVALID;
759         }
760     }
761     picture_fifo_Flush(vout->p->decoder_fifo, date, below);
762 }
763
764 static void ThreadReset(vout_thread_t *vout)
765 {
766     ThreadFlush(vout, true, INT64_MAX);
767     if (vout->p->decoder_pool)
768         picture_pool_NonEmpty(vout->p->decoder_pool, true);
769     vout->p->pause.is_on = false;
770     vout->p->pause.date  = mdate();
771 }
772
773 static void ThreadStep(vout_thread_t *vout, mtime_t *duration)
774 {
775     *duration = 0;
776
777     if (vout->p->step.last <= VLC_TS_INVALID)
778         vout->p->step.last = vout->p->displayed.timestamp;
779
780     mtime_t dummy;
781     if (ThreadDisplayPicture(vout, true, &dummy))
782         return;
783
784     vout->p->step.timestamp = vout->p->displayed.timestamp;
785
786     if (vout->p->step.last > VLC_TS_INVALID &&
787         vout->p->step.timestamp > vout->p->step.last) {
788         *duration = vout->p->step.timestamp - vout->p->step.last;
789         vout->p->step.last = vout->p->step.timestamp;
790         /* TODO advance subpicture by the duration ... */
791     }
792 }
793
794 static void ThreadChangeFullscreen(vout_thread_t *vout, bool fullscreen)
795 {
796     /* FIXME not sure setting "fullscreen" is good ... */
797     var_SetBool(vout, "fullscreen", fullscreen);
798     vout_SetDisplayFullscreen(vout->p->display.vd, fullscreen);
799 }
800
801 static void ThreadChangeOnTop(vout_thread_t *vout, bool is_on_top)
802 {
803     vout_SetWindowState(vout->p->display.vd,
804                         is_on_top ? VOUT_WINDOW_STATE_ABOVE :
805                                     VOUT_WINDOW_STATE_NORMAL);
806 }
807
808 static void ThreadChangeDisplayFilled(vout_thread_t *vout, bool is_filled)
809 {
810     vout_SetDisplayFilled(vout->p->display.vd, is_filled);
811 }
812
813 static void ThreadChangeZoom(vout_thread_t *vout, int num, int den)
814 {
815     if (num * 10 < den) {
816         num = den;
817         den *= 10;
818     } else if (num > den * 10) {
819         num = den * 10;
820     }
821
822     vout_SetDisplayZoom(vout->p->display.vd, num, den);
823 }
824
825 static void ThreadChangeAspectRatio(vout_thread_t *vout,
826                                     unsigned num, unsigned den)
827 {
828     const video_format_t *source = &vout->p->original;
829
830     if (num > 0 && den > 0) {
831         num *= source->i_visible_height;
832         den *= source->i_visible_width;
833         vlc_ureduce(&num, &den, num, den, 0);
834     }
835     vout_SetDisplayAspect(vout->p->display.vd, num, den);
836 }
837
838
839 static void ThreadExecuteCropWindow(vout_thread_t *vout,
840                                     unsigned crop_num, unsigned crop_den,
841                                     unsigned x, unsigned y,
842                                     unsigned width, unsigned height)
843 {
844     const video_format_t *source = &vout->p->original;
845
846     vout_SetDisplayCrop(vout->p->display.vd,
847                         crop_num, crop_den,
848                         source->i_x_offset + x,
849                         source->i_y_offset + y,
850                         width, height);
851 }
852 static void ThreadExecuteCropBorder(vout_thread_t *vout,
853                                     unsigned left, unsigned top,
854                                     unsigned right, unsigned bottom)
855 {
856     const video_format_t *source = &vout->p->original;
857     ThreadExecuteCropWindow(vout, 0, 0,
858                             left,
859                             top,
860                             /* At worst, it becomes < 0 (but unsigned) and will be rejected */
861                             source->i_visible_width  - (left + right),
862                             source->i_visible_height - (top  + bottom));
863 }
864
865 static void ThreadExecuteCropRatio(vout_thread_t *vout,
866                                    unsigned num, unsigned den)
867 {
868     const video_format_t *source = &vout->p->original;
869
870     int x, y;
871     int width, height;
872     if (num <= 0 || den <= 0) {
873         num = 0;
874         den = 0;
875         x   = 0;
876         y   = 0;
877         width  = source->i_visible_width;
878         height = source->i_visible_height;
879     } else {
880         unsigned scaled_width  = (uint64_t)source->i_visible_height * num * source->i_sar_den / den / source->i_sar_num;
881         unsigned scaled_height = (uint64_t)source->i_visible_width  * den * source->i_sar_num / num / source->i_sar_den;
882
883         if (scaled_width < source->i_visible_width) {
884             x      = (source->i_visible_width - scaled_width) / 2;
885             y      = 0;
886             width  = scaled_width;
887             height = source->i_visible_height;
888         } else {
889             x      = 0;
890             y      = (source->i_visible_height - scaled_height) / 2;
891             width  = source->i_visible_width;
892             height = scaled_height;
893         }
894     }
895     ThreadExecuteCropWindow(vout, num, den, x, y, width, height);
896 }
897
898 static int ThreadInit(vout_thread_t *vout)
899 {
900     vout->p->dead = false;
901     vout->p->is_late_dropped = var_InheritBool(vout, "drop-late-frames");
902     vlc_mouse_Init(&vout->p->mouse);
903     vout->p->decoder_fifo = picture_fifo_New();
904     vout->p->decoder_pool = NULL;
905     vout->p->display_pool = NULL;
906     vout->p->private_pool = NULL;
907
908     vout->p->vfilter_chain =
909         filter_chain_New( vout, "video filter2", false,
910                           VoutVideoFilterAllocationSetup, NULL, vout);
911
912     if (vout_OpenWrapper(vout, vout->p->splitter_name))
913         return VLC_EGENERIC;
914     if (vout_InitWrapper(vout))
915         return VLC_EGENERIC;
916     assert(vout->p->decoder_pool);
917
918     vout_chrono_Init(&vout->p->render, 5, 10000); /* Arbitrary initial time */
919
920     vout->p->displayed.decoded       = NULL;
921     vout->p->displayed.date          = VLC_TS_INVALID;
922     vout->p->displayed.decoded       = NULL;
923     vout->p->displayed.timestamp     = VLC_TS_INVALID;
924     vout->p->displayed.qtype         = QTYPE_NONE;
925     vout->p->displayed.is_interlaced = false;
926
927     vout->p->step.last               = VLC_TS_INVALID;
928     vout->p->step.timestamp          = VLC_TS_INVALID;
929
930     vout->p->pause.is_on             = false;
931     vout->p->pause.date              = VLC_TS_INVALID;
932
933     video_format_Print(VLC_OBJECT(vout), "original format", &vout->p->original);
934     return VLC_SUCCESS;
935 }
936
937 static void ThreadClean(vout_thread_t *vout)
938 {
939     /* Destroy the video filters2 */
940     filter_chain_Delete(vout->p->vfilter_chain);
941
942     /* Destroy translation tables */
943     if (vout->p->display.vd) {
944         if (vout->p->decoder_pool) {
945             ThreadFlush(vout, true, INT64_MAX);
946             vout_EndWrapper(vout);
947         }
948         vout_CloseWrapper(vout);
949     }
950
951     /* Detach subpicture unit from vout */
952     vlc_object_detach(vout->p->p_spu);
953
954     if (vout->p->decoder_fifo)
955         picture_fifo_Delete(vout->p->decoder_fifo);
956     assert(!vout->p->decoder_pool);
957     vout_chrono_Clean(&vout->p->render);
958
959     vout->p->dead = true;
960     vout_control_Dead(&vout->p->control);
961 }
962
963 /*****************************************************************************
964  * Thread: video output thread
965  *****************************************************************************
966  * Video output thread. This function does only returns when the thread is
967  * terminated. It handles the pictures arriving in the video heap and the
968  * display device events.
969  *****************************************************************************/
970 static void *Thread(void *object)
971 {
972     vout_thread_t *vout = object;
973
974     vout_interlacing_support_t interlacing = {
975         .is_interlaced = false,
976         .date = mdate(),
977     };
978     vout_postprocessing_support_t postprocessing = {
979         .qtype = QTYPE_NONE,
980     };
981
982     mtime_t deadline = VLC_TS_INVALID;
983     for (;;) {
984         vout_control_cmd_t cmd;
985
986         /* FIXME remove thoses ugly timeouts
987          */
988         while (!vout_control_Pop(&vout->p->control, &cmd, deadline, 100000)) {
989             switch(cmd.type) {
990             case VOUT_CONTROL_INIT:
991                 if (ThreadInit(vout)) {
992                     ThreadClean(vout);
993                     return NULL;
994                 }
995                 break;
996             case VOUT_CONTROL_CLEAN:
997                 ThreadClean(vout);
998                 return NULL;
999             case VOUT_CONTROL_OSD_TITLE:
1000                 ThreadDisplayOsdTitle(vout, cmd.u.string);
1001                 break;
1002             case VOUT_CONTROL_CHANGE_FILTERS:
1003                 ThreadChangeFilters(vout, cmd.u.string);
1004                 break;
1005             case VOUT_CONTROL_PAUSE:
1006                 ThreadChangePause(vout, cmd.u.pause.is_on, cmd.u.pause.date);
1007                 break;
1008             case VOUT_CONTROL_FLUSH:
1009                 ThreadFlush(vout, false, cmd.u.time);
1010                 break;
1011             case VOUT_CONTROL_RESET:
1012                 ThreadReset(vout);
1013                 break;
1014             case VOUT_CONTROL_STEP:
1015                 ThreadStep(vout, cmd.u.time_ptr);
1016                 break;
1017             case VOUT_CONTROL_FULLSCREEN:
1018                 ThreadChangeFullscreen(vout, cmd.u.boolean);
1019                 break;
1020             case VOUT_CONTROL_ON_TOP:
1021                 ThreadChangeOnTop(vout, cmd.u.boolean);
1022                 break;
1023             case VOUT_CONTROL_DISPLAY_FILLED:
1024                 ThreadChangeDisplayFilled(vout, cmd.u.boolean);
1025                 break;
1026             case VOUT_CONTROL_ZOOM:
1027                 ThreadChangeZoom(vout, cmd.u.pair.a, cmd.u.pair.b);
1028                 break;
1029             case VOUT_CONTROL_ASPECT_RATIO:
1030                 ThreadChangeAspectRatio(vout, cmd.u.pair.a, cmd.u.pair.b);
1031                 break;
1032            case VOUT_CONTROL_CROP_RATIO:
1033                 ThreadExecuteCropRatio(vout, cmd.u.pair.a, cmd.u.pair.b);
1034                 break;
1035             case VOUT_CONTROL_CROP_WINDOW:
1036                 ThreadExecuteCropWindow(vout, 0, 0,
1037                                         cmd.u.window.x, cmd.u.window.y,
1038                                         cmd.u.window.width, cmd.u.window.height);
1039                 break;
1040             case VOUT_CONTROL_CROP_BORDER:
1041                 ThreadExecuteCropBorder(vout,
1042                                         cmd.u.border.left,  cmd.u.border.top,
1043                                         cmd.u.border.right, cmd.u.border.bottom);
1044                 break;
1045             default:
1046                 break;
1047             }
1048             vout_control_cmd_Clean(&cmd);
1049         }
1050
1051         ThreadManage(vout, &deadline, &interlacing, &postprocessing);
1052     }
1053 }
1054