]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
Fin du remplacement des pthread + ajout du frame rate dans display.c.
[vlc] / src / video_output / video_output.c
1 /*******************************************************************************
2  * video_output.c : video output thread
3  * (c)1999 VideoLAN
4  *******************************************************************************
5  * This module describes the programming interface for video output threads.
6  * It includes functions allowing to open a new thread, send pictures to a
7  * thread, and destroy a previously oppenned video output thread.
8  *******************************************************************************/
9
10 /*******************************************************************************
11  * Preamble
12  *******************************************************************************/
13 #include <errno.h> 
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <X11/Xlib.h>
18 #include <X11/Xutil.h>
19 #include <X11/extensions/XShm.h>
20
21 #include "common.h"
22 #include "config.h"
23 #include "mtime.h"
24 #include "vlc_thread.h"
25 #include "thread.h"
26
27 #include "video.h"
28 #include "video_graphics.h"
29 #include "video_output.h"
30 #include "video_x11.h"
31
32 #include "intf_msg.h"
33
34 /*
35  * Local prototypes
36  */
37 static int      CheckConfiguration  ( video_cfg_t *p_cfg );
38 static int      InitThread          ( vout_thread_t *p_vout );
39 static void     RunThread           ( vout_thread_t *p_vout );
40 static void     ErrorThread         ( vout_thread_t *p_vout );
41 static void     EndThread           ( vout_thread_t *p_vout );
42
43 static picture_t *  FindPicture     ( vout_thread_t *p_vout );
44 static void         EmptyPicture    ( vout_thread_t *p_vout, picture_t *p_pic );
45 static mtime_t      FirstPass       ( vout_thread_t *p_vout, mtime_t current_date );
46 static void         SecondPass      ( vout_thread_t *p_vout, mtime_t display_date );
47 static void         SortPictures    ( vout_thread_t *p_vout, picture_t **pp_picture );
48 static void         RenderPicture   ( vout_thread_t *p_vout, picture_t *p_pic );
49 static void         ClipPicture     ( int *pi_ofs, int i_size, int *pi_pic_ofs, int *pi_pic_size,
50                                       int i_ratio, int i_placement );
51
52 /*******************************************************************************
53  * vout_CreateThread: creates a new video output thread
54  *******************************************************************************
55  * This function creates a new video output thread, and returns a pointer
56  * to its description. On error, it returns NULL.
57  * Following configuration properties are used:
58  *  VIDEO_CFG_SIZE      video heap maximal size
59  *  VIDEO_CFG_WIDTH     window width
60  *  VIDEO_CFG_HEIGHT    window height 
61  * Using X11 display method (the only one supported yet):
62  *  VIDEO_CFG_DISPLAY   display used
63  *  VIDEO_CFG_TITLE     window title
64  *  VIDEO_CFG_SHM_EXT   try to use XShm extension
65  * If pi_status is NULL, then the function will block until the thread is ready.
66  * If not, pi_error will be updated using one of the THREAD_* constants.
67  *******************************************************************************/
68 vout_thread_t *vout_CreateThread( video_cfg_t *p_cfg, int *pi_status )
69 {
70     vout_thread_t * p_vout;                               /* thread descriptor */
71     int             i_status;                                 /* thread status */
72
73     /*
74      * Check configuration 
75      */
76     if( CheckConfiguration( p_cfg ) )
77     {
78         return( NULL );
79     }
80
81     /* Allocate descriptor and initialize flags */
82     p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
83     if( p_vout == NULL )                                              /* error */
84     {
85         return( NULL );
86     }
87     if( vout_X11AllocOutputMethod( p_vout, p_cfg ) )
88     {
89         free( p_vout );
90         return( NULL );        
91     }
92
93     /* Copy configuration */
94     p_vout->i_max_pictures = p_cfg->i_size;    
95     p_vout->i_width = p_cfg->i_width;
96     p_vout->i_height = p_cfg->i_height;
97    
98     /* Set status */
99     p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
100     *p_vout->pi_status = THREAD_CREATE;
101     
102     /* Initialize flags */
103     p_vout->b_die = 0;
104     p_vout->b_error = 0;    
105     p_vout->b_active = 0;
106
107     /* Create thread and set locks */
108     vlc_mutex_init( &p_vout->streams_lock );
109     vlc_mutex_init( &p_vout->pictures_lock );
110     if( vlc_thread_create( &p_vout->thread_id, "video output", (vlc_thread_func)RunThread, (void *) p_vout) )
111     {
112         intf_ErrMsg("vout error: %s\n", strerror(ENOMEM));
113         intf_DbgMsg("failed\n");        
114         vout_X11FreeOutputMethod( p_vout );        
115         free( p_vout );
116         return( NULL );
117     }   
118
119     /* If status is NULL, wait until the thread is created */
120     if( pi_status == NULL )
121     {
122         do
123         {            
124             msleep( THREAD_SLEEP );
125         }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR) 
126                 && (i_status != THREAD_FATAL) );
127         if( i_status != THREAD_READY )
128         {
129             intf_DbgMsg("failed\n");            
130             return( NULL );            
131         }        
132     }
133
134     intf_DbgMsg("succeeded -> %p\n", p_vout);    
135     return( p_vout );
136 }
137
138 /*******************************************************************************
139  * vout_DestroyThread: destroys a previously created thread
140  *******************************************************************************
141  * Destroy a terminated thread. 
142  * The function will request a destruction of the specified thread. If pi_error
143  * is NULL, it will return once the thread is destroyed. Else, it will be 
144  * update using one of the THREAD_* constants.
145  *******************************************************************************/
146 void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
147 {  
148     int     i_status;                                         /* thread status */
149
150     /* Set status */
151     p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
152     *p_vout->pi_status = THREAD_DESTROY;
153      
154     /* Request thread destruction */
155     p_vout->b_die = 1;
156
157     /* If status is NULL, wait until thread has been destroyed */
158     if( pi_status )
159     {
160         do
161         {
162             msleep( THREAD_SLEEP );
163         }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR) 
164                 && (i_status != THREAD_FATAL) );   
165     }
166     
167     intf_DbgMsg("%p -> succeeded\n", p_vout);
168 }
169
170 /*******************************************************************************
171  * vout_DisplayPicture: add a picture to a thread
172  *******************************************************************************
173  * The picture will be placed in an empty place of the video heap of the thread.
174  * If this is impossible, NULL will be returned. Else, the original picture
175  * is destroyed.
176  *******************************************************************************/
177 picture_t * vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
178 {
179     picture_t * p_newpic;                                 /* new picture index */
180
181     p_newpic = FindPicture( p_vout );
182     if( p_newpic != NULL )
183     {        
184         /* Copy picture information */
185         video_CopyPictureDescriptor( p_newpic, p_pic );
186         p_newpic->p_data = p_pic->p_data;
187         free( p_pic );
188
189         /* Update heap size and stats */
190         p_vout->i_pictures++;
191 #ifdef STATS
192         p_vout->c_pictures++;
193         p_vout->p_stream[ p_newpic->i_stream ].c_pictures++;
194 #endif
195     }
196
197     /* Release lock and return */
198     vlc_mutex_unlock( &p_vout->pictures_lock );
199     return( p_newpic );
200 }
201
202 /*******************************************************************************
203  * vout_DisplayPictureCopy: copy a picture to a thread
204  *******************************************************************************
205  * The picture will be copied in an empty place of the video heap of the thread.
206  * If this is impossible, NULL will be returned. The picture used is the copy
207  * of the picture passed as argument, which remains usable.
208  * The picture data are copied only if the original picture owns its own data.
209  * The link reference counter is set to 0.
210  *******************************************************************************/
211 picture_t * vout_DisplayPictureCopy( vout_thread_t *p_vout, picture_t *p_pic )
212 {
213     picture_t * p_newpic;                                 /* new picture index */
214
215     p_newpic = FindPicture( p_vout );
216     if( p_newpic != NULL )
217     {        
218         /* Copy picture information */
219         video_CopyPictureDescriptor( p_newpic, p_pic );
220
221         /* If source picture owns its data, make a copy */
222         if( p_pic->i_flags & OWNER_PICTURE )
223         {      
224             p_newpic->p_data = malloc( p_pic->i_height * p_pic->i_bytes_per_line );
225             if( p_newpic->p_data == NULL )                            /* error */
226             {
227                 intf_ErrMsg("vout error: %s\n", strerror(ENOMEM) );
228
229                 /* Restore type and flags */
230                 p_newpic->i_type = EMPTY_PICTURE;
231                 p_newpic->i_flags = 0;
232                 vlc_mutex_unlock( &p_vout->pictures_lock );            
233                 return( NULL );
234             }
235             memcpy( p_newpic->p_data, p_pic->p_data,
236                     p_pic->i_height * p_pic->i_bytes_per_line );
237         }
238         /* If source picture does not owns its data, copy the reference */
239         else              
240         {
241             p_newpic->p_data = p_pic->p_data;
242         }
243         p_newpic->i_refcount = 0;
244             
245         /* Update heap size and stats */
246         p_vout->i_pictures++;
247 #ifdef STATS
248         p_vout->c_pictures++;
249         p_vout->p_stream[ p_newpic->i_stream ].c_pictures++;
250 #endif
251     }
252
253     /* Release lock and return */
254     vlc_mutex_unlock( &p_vout->pictures_lock );
255     return( p_newpic );
256 }
257
258 /*******************************************************************************
259  * vout_DisplayPictureReplicate: copy a picture to a thread
260  *******************************************************************************
261  * The picture will be copied in an empty place of the video heap of the thread.
262  * If this is impossible, NULL will be returned. The picture used is the copy
263  * of the picture passed as argument, which remains usable.
264  * The picture data are a reference to original picture (the picture in the heap
265  * does not owns its data). Link reference counter is set to 0.
266  *******************************************************************************/
267 picture_t * vout_DisplayPictureReplicate( vout_thread_t *p_vout, picture_t *p_pic )
268 {
269     picture_t * p_newpic;                            /* new picture descrpitor */
270
271     p_newpic = FindPicture( p_vout );
272     if( p_newpic != NULL )
273     {        
274         /* Copy picture information */
275         video_CopyPictureDescriptor( p_newpic, p_pic );   
276         p_newpic->i_flags &= ~OWNER_PICTURE;
277         p_newpic->p_data = p_pic->p_data;
278         p_newpic->i_refcount = 0;
279             
280         /* Update heap size and stats */
281         p_vout->i_pictures++;
282 #ifdef STATS
283         p_vout->c_pictures++;
284         p_vout->p_stream[ p_newpic->i_stream ].c_pictures++;
285 #endif
286     }
287
288    /* Release lock and return */
289     vlc_mutex_unlock( &p_vout->pictures_lock );
290     return( p_newpic );
291 }
292
293 /*******************************************************************************
294  * vout_DisplayReservedPicture: add a reserved picture to a thread
295  *******************************************************************************
296  * The picture will be placed in an empty place of the video heap of the thread.
297  * If the picture has been declared on reservation as permanent, it remains
298  * usable. Else, it will be destroyed by this call.
299  * This function can't fail, since it is the purpose of picture reservation to
300  * provide such a robust mechanism.
301  *******************************************************************************/
302 picture_t * vout_DisplayReservedPicture( vout_thread_t *p_vout, picture_t *p_pic )
303 {
304     vlc_mutex_lock( &p_vout->pictures_lock );
305
306     /* Remove reservation flag */
307     p_pic->i_flags &= ~RESERVED_PICTURE;
308
309 #ifdef STATS
310     /* Update stats */
311     p_vout->c_pictures++;
312     p_vout->p_stream[ p_pic->i_stream ].c_pictures++;
313 #endif
314
315     vlc_mutex_unlock( &p_vout->pictures_lock );
316     return( p_pic );
317 }
318
319 /*******************************************************************************
320  * vout_CreateReservedPicture: reserve a place for a new picture in heap
321  *******************************************************************************
322  * This function create a reserved image in the video output heap. 
323  * A null pointer is returned if the function fails. 
324  * Following configuration properties are used:
325  *  VIDEO_CFG_WIDTH     picture width (required)
326  *  VIDEO_CFG_HEIGHT    picture height (required)
327  *  VIDEO_CFG_TYPE      picture type (required)
328  *  VIDEO_CFG_FLAGS     flags
329  *  VIDEO_CFG_BPP       padded bpp (required for pixel pictures)
330  *  VIDEO_CFG_POSITION  position in output window
331  *  VIDEO_CFG_ALIGN     base position in output window
332  *  VIDEO_CFG_RATIO     display ratio
333  *  VIDEO_CFG_LEVEL     overlay hierarchical level  
334  *  VIDEO_CFG_REFCOUNT  links reference counter
335  *  VIDEO_CFG_STREAM    video stream id
336  *  VIDEO_CFG_DATE      display date
337  *  VIDEO_CFG_DURATION  duration for overlay pictures
338  *  VIDEO_CFG_PIXEL     pixel value for mask pictures
339  *  VIDEO_CFG_DATA      picture data (required if not owner and non blank)
340  * Pictures created using this function are always reserved, even if they did
341  * not had the RESERVED_PICTURE flag set.
342  * This function is very similar to the video_CreatePicture function.
343  *******************************************************************************/
344 picture_t *vout_CreateReservedPicture( vout_thread_t *p_vout, video_cfg_t *p_cfg )
345 {
346     picture_t * p_newpic;                                 /* new picture index */
347
348     p_newpic = FindPicture( p_vout );
349     if( p_newpic != NULL )
350     {        
351         /* Create new picture */
352         if( video_CreatePictureBody( p_newpic, p_cfg ) )
353         {   
354             intf_ErrMsg("vout error: %s\n", strerror(ENOMEM));             
355             /* Error: restore type and flags */
356             p_newpic->i_type = EMPTY_PICTURE;
357             p_newpic->i_flags = 0;
358             vlc_mutex_unlock( &p_vout->pictures_lock );
359             return( NULL );
360         }
361         p_newpic->i_flags |= RESERVED_PICTURE;
362
363         /* Update heap size, release lock and return */
364         p_vout->i_pictures++;
365     }
366
367     /* Release lock and return */
368     vlc_mutex_unlock( &p_vout->pictures_lock );
369     return( p_newpic );
370 }
371
372 /*******************************************************************************
373  * vout_ReservePicture: reserve a place for a picture in heap
374  *******************************************************************************
375  * This function transforms an existing picture in a reserved picture in a
376  * video heap. The picture will be placed in an empty place of the video heap 
377  * of the thread. If this is impossible, NULL will be returned. Else, the 
378  * original picture is destroyed.
379  *******************************************************************************/
380 picture_t *vout_ReservePicture( vout_thread_t *p_vout, picture_t *p_pic )
381 {
382     picture_t * p_newpic;                                 /* new picture index */
383
384     p_newpic = FindPicture( p_vout );
385     if( p_newpic != NULL )
386     {        
387         /* Copy picture information */
388         video_CopyPictureDescriptor( p_newpic, p_pic );
389         p_newpic->p_data = p_pic->p_data;
390         p_newpic->i_flags |= RESERVED_PICTURE;
391         free( p_pic );           
392
393         /* Update heap size */
394         p_vout->i_pictures++;
395     }
396
397     /* Release lock and return */
398     vlc_mutex_unlock( &p_vout->pictures_lock );
399     return( p_newpic );
400 }
401
402 /*******************************************************************************
403  * vout_ReservePictureCopy: reserve a place for a picture in heap
404  *******************************************************************************
405  * This function returns a pointer to a picture which can be processed. The
406  * returned picture is a copy of the one passed as parameter.
407  * This function returns NULL if the reservation fails.
408  *******************************************************************************/
409 picture_t *vout_ReservePictureCopy( vout_thread_t *p_vout, picture_t *p_pic )
410 {
411     picture_t * p_newpic;                                 /* new picture index */
412
413     p_newpic = FindPicture( p_vout );
414     if( p_newpic != NULL )
415     {        
416         /* Copy picture information */
417         video_CopyPictureDescriptor( p_newpic, p_pic );
418
419         /* If source picture owns its data, make a copy */
420         if( p_pic->i_flags & OWNER_PICTURE )
421         {              
422             p_newpic->p_data = malloc( p_pic->i_height * p_pic->i_bytes_per_line );
423             if( p_newpic->p_data == NULL )                            /* error */
424             {
425                 intf_ErrMsg("vout error: %s\n", strerror(ENOMEM));                    
426                 /* Restore type and flags */
427                 p_newpic->i_type = EMPTY_PICTURE;
428                 p_newpic->i_flags = 0;
429                 vlc_mutex_unlock( &p_vout->pictures_lock );   
430                 return( NULL );
431             }
432             memcpy( p_newpic->p_data, p_pic->p_data,
433                     p_pic->i_height * p_pic->i_bytes_per_line );
434         }   
435         /* If source picture does not owns its data, copy the reference */
436         else              
437         {
438             p_newpic->p_data = p_pic->p_data;
439         }
440         p_newpic->i_refcount = 0;
441         p_newpic->i_flags |= RESERVED_PICTURE;
442
443         /* Update heap size */
444         p_vout->i_pictures++;
445     }
446
447     /* Release lock and return */
448     vlc_mutex_unlock( &p_vout->pictures_lock );
449     return( p_newpic );
450 }
451
452 /*******************************************************************************
453  * vout_ReservePictureReplicate: reserve a place for a picture in heap
454  *******************************************************************************
455  * This function returns a pointer to a picture which can be processed. The
456  * returned picture is a copy of the one passed as parameter.
457  * This function returns NULL if the reservation fails.
458  *******************************************************************************/
459 picture_t *vout_ReservePictureReplicate( vout_thread_t *p_vout, picture_t *p_pic )
460 {
461     picture_t * p_newpic;                                 /* new picture index */
462
463     p_newpic = FindPicture( p_vout );
464     if( p_newpic != NULL )
465     {        
466         /* Copy picture information */
467         video_CopyPictureDescriptor( p_newpic, p_pic );
468         p_newpic->p_data = p_pic->p_data;
469         p_newpic->i_refcount = 0;
470         p_newpic->i_flags &= ~OWNER_PICTURE;
471         p_newpic->i_flags |= RESERVED_PICTURE;
472
473         /* Update heap size */
474         p_vout->i_pictures++;
475     }
476
477     /* Release lock and return */
478     vlc_mutex_unlock( &p_vout->pictures_lock );
479     return( p_newpic );
480 }
481
482 /*******************************************************************************
483  * vout_RemovePicture: remove a permanent or reserved picture from the heap
484  *******************************************************************************
485  * This function frees a previously reserved picture or a permanent
486  * picture. The picture data is destroyed if required. 
487  *******************************************************************************/
488 void vout_RemovePicture( vout_thread_t *p_vout, picture_t *p_pic )
489 {
490     vlc_mutex_lock( &p_vout->pictures_lock );
491
492     /* Mark picture for destruction */
493     p_pic->i_flags |= DESTROY_PICTURE;
494     /* Since permanent pictures can normally not be destroyed by the vout thread,   
495      * the permanent flag needs to be removed */
496     p_pic->i_flags &= ~PERMANENT_PICTURE;
497     intf_DbgMsg("%p -> picture %p removing requested\n", p_vout, p_pic );
498
499     vlc_mutex_unlock( &p_vout->pictures_lock );
500 }
501
502 /*******************************************************************************
503  * vout_RefreshPermanentPicture: 
504  *******************************************************************************
505  * Set the DISPLAYED_PICTURE flag of a permanent picture to 0, allowing to
506  * display it again if it not overlaid. The display date is also updated.
507  *******************************************************************************/
508 void vout_RefreshPermanentPicture( vout_thread_t *p_vout, picture_t *p_pic, 
509                                    mtime_t display_date )
510 {
511     vlc_mutex_lock( &p_vout->pictures_lock );
512     p_pic->i_flags &= ~DISPLAYED_PICTURE;
513     p_pic->date = display_date;
514     vlc_mutex_lock( &p_vout->pictures_lock );
515 }
516
517 /*******************************************************************************
518  * vout_LinkPicture: increment reference counter of a picture
519  *******************************************************************************
520  * This function increment the reference counter of a picture in the video
521  * heap.
522  *******************************************************************************/
523 void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
524 {
525     vlc_mutex_lock( &p_vout->pictures_lock );
526     p_pic->i_refcount++;
527     vlc_mutex_unlock( &p_vout->pictures_lock );
528 }
529
530 /*******************************************************************************
531  * vout_UnlinkPicture: decrement reference counter of a picture
532  *******************************************************************************
533  * This function decrement the reference counter of a picture in the video heap.
534  *******************************************************************************/
535 void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
536 {
537     vlc_mutex_lock( &p_vout->pictures_lock );
538     p_pic->i_refcount--;
539 #ifdef DEBUG
540     if( p_pic->i_refcount < 0 )
541     {
542         intf_DbgMsg("%p -> picture %p i_refcount < 0\n", p_vout, p_pic);
543     }
544 #endif
545     vlc_mutex_unlock( &p_vout->pictures_lock );    
546 }
547
548 /*******************************************************************************
549  * vout_CreateStream: get a new stream id
550  *******************************************************************************
551  * Stream ids are used to create list of pictures in a video output thread.
552  * The function returns an unused stream id, or a negative number on error.
553  * Stream id 0 is never returned, since it is not a stream descriptor but a set 
554  * of independant images.
555  *******************************************************************************/
556 int vout_CreateStream( vout_thread_t *p_vout )
557 {
558     int     i_index;                                           /* stream index */
559
560     /* Find free (inactive) stream id */
561     vlc_mutex_lock( &p_vout->streams_lock );                   /* get lock */
562     for( i_index = 1; i_index < VOUT_MAX_STREAMS; i_index++ )  /* find free id */
563     {
564         if( p_vout->p_stream[i_index].i_status == VOUT_INACTIVE_STREAM )
565         {
566             /* Initialize stream */
567             p_vout->p_stream[i_index].i_status = VOUT_ACTIVE_STREAM; 
568 #ifdef STATS
569             p_vout->p_stream[i_index].c_pictures = 0;
570             p_vout->p_stream[i_index].c_rendered_pictures = 0;
571 #endif
572             
573             /* Return stream id */
574             intf_DbgMsg("%p -> stream %i created\n", p_vout, i_index);
575             vlc_mutex_unlock( &p_vout->streams_lock );     /* release lock */
576             return( i_index );                                    /* return id */
577         }
578     }
579    
580     /* Failure: all streams id are already active */
581     intf_DbgMsg("%p -> failed\n", p_vout);    
582     vlc_mutex_unlock( &p_vout->streams_lock );    
583     return( -1 );
584 }
585
586 /*******************************************************************************
587  * vout_EndStream: free a previously allocated stream
588  *******************************************************************************
589  * This function must be called once a stream is no more used. It can be called
590  * even if there are remaining pictures in the video heap, since the stream will
591  * only be destroyed once all pictures of the stream have been displayed.
592  *******************************************************************************/
593 void vout_EndStream( vout_thread_t *p_vout, int i_stream )
594 {
595     vlc_mutex_lock( &p_vout->streams_lock );                   /* get lock */
596     p_vout->p_stream[i_stream].i_status = VOUT_ENDING_STREAM;   /* mark stream */
597     vlc_mutex_unlock( &p_vout->streams_lock );             /* release lock */
598     intf_DbgMsg("%p -> stream %d\n", p_vout, i_stream);
599 }
600
601 /*******************************************************************************
602  * vout_DestroyStream: free a previously allocated stream
603  *******************************************************************************
604  * This function must be called once a stream is no more used. It can be called
605  * even if there are remaining pictures in the video heap, since all pictures
606  * will be removed before the stream is destroyed.
607  *******************************************************************************/
608 void vout_DestroyStream( vout_thread_t *p_vout, int i_stream )
609 {
610     vlc_mutex_lock( &p_vout->streams_lock );                   /* get lock */
611     p_vout->p_stream[i_stream].i_status = VOUT_DESTROYED_STREAM;/* mark stream */
612     vlc_mutex_unlock( &p_vout->streams_lock );             /* release lock */
613     intf_DbgMsg("%p -> stream %d\n", p_vout, i_stream);
614 }
615
616 /* following functions are debugging functions */
617
618 /*******************************************************************************
619  * vout_PrintHeap: display heap state (debugging function)
620  *******************************************************************************
621  * This functions, which is only defined if DEBUG is defined, can be used 
622  * for debugging purposes. It prints on debug stream the current state of the   
623  * heap. The second parameter is used to identify the output.
624  *******************************************************************************/
625 #ifdef DEBUG
626 void vout_PrintHeap( vout_thread_t *p_vout, char *psz_str )
627 {
628     int         i_picture;                                    /* picture index */
629
630     intf_Msg("vout: --- thread %p heap status (%s) ---\n", p_vout, psz_str );
631
632     /* Browse all pictures in heap */
633     for( i_picture = 0; i_picture < p_vout->i_max_pictures; i_picture++ )
634     {
635         if( p_vout->p_picture[i_picture].i_type != EMPTY_PICTURE )
636         {
637             video_PrintPicture( &p_vout->p_picture[i_picture], "vout: ..." );
638         }
639     }   
640
641     intf_Msg("vout: --- end ---\n");
642 }
643 #endif
644
645 /* following functions are local */
646
647 /*******************************************************************************
648  * CheckConfiguration: check vout_CreateThread() configuration
649  *******************************************************************************
650  * Set default parameters where required. In DEBUG mode, check if configuration
651  * is valid.
652  *******************************************************************************/
653 static int CheckConfiguration( video_cfg_t *p_cfg )
654 {
655     /* Heap size */
656     if( !( p_cfg->i_properties & VIDEO_CFG_SIZE ) )
657     {
658         p_cfg->i_size = VOUT_HEAP_SIZE;
659     }
660
661     return( 0 );
662 }
663
664 /*******************************************************************************
665  * InitThread: initialize video output thread
666  *******************************************************************************
667  * This function is called from RunThread and performs the second step of the
668  * initialization. It returns 0 on success. Note that the thread's flag are not
669  * modified inside this function.
670  *******************************************************************************/
671 static int InitThread( vout_thread_t *p_vout )
672 {
673     int     i_index;                                          /* generic index */    
674
675     /* Update status */
676     *p_vout->pi_status = THREAD_START;    
677     
678     /* Allocate video heap */
679     p_vout->p_picture = 
680         (picture_t *) malloc( sizeof(picture_t) * p_vout->i_max_pictures );
681     if( !p_vout->p_picture )                                          /* error */
682     {        
683         intf_ErrMsg("vout error: %s\n", strerror(ENOMEM));
684         intf_DbgMsg("%p -> failed\n", p_vout);
685         *p_vout->pi_status = THREAD_ERROR;        
686         return( 1 );
687     }
688
689     /* Initialize pictures */    
690     for( i_index = 0; i_index < p_vout->i_max_pictures; i_index++)
691     {
692         p_vout->p_picture[i_index].i_type = EMPTY_PICTURE;
693         p_vout->p_picture[i_index].i_flags = 0;
694     }
695
696     /* Initialize video streams - note that stream 0 is always active */
697     p_vout->p_stream[0].i_status = VOUT_ACTIVE_STREAM; 
698 #ifdef STATS
699     p_vout->p_stream[0].c_pictures = 0;
700     p_vout->p_stream[0].c_rendered_pictures = 0;
701 #endif
702     for( i_index = 1; i_index < VOUT_MAX_STREAMS; i_index++ )
703     {
704         p_vout->p_stream[i_index].i_status = VOUT_INACTIVE_STREAM;
705     }
706  
707     /* Initialize other properties */
708     p_vout->i_pictures = 0;
709 #ifdef STATS
710     p_vout->c_loops = 0;
711     p_vout->c_idle_loops = 0;
712     p_vout->c_pictures = 0;
713     p_vout->c_rendered_pictures = 0;
714 #endif
715
716     /* Initialize output method - width, height, screen depth and bytes per 
717      * pixel are initialized by this call. */
718     if( vout_X11CreateOutputMethod( p_vout ) )                        /* error */
719     {
720         free( p_vout->p_picture );
721         *p_vout->pi_status = THREAD_ERROR;        
722         return( 1 );
723     } 
724
725     /* Mark thread as running and return */
726     *p_vout->pi_status = THREAD_READY;    
727     intf_DbgMsg("%p -> succeeded\n", p_vout);    
728     return(0);    
729 }
730
731 /*******************************************************************************
732  * RunThread: video output thread
733  *******************************************************************************
734  * Video output thread. This function does only returns when the thread is
735  * terminated. It handles the pictures arriving in the video heap and the
736  * display device events.
737  *******************************************************************************/
738 static void RunThread( vout_thread_t *p_vout)
739 {
740     int             i_stream;                                  /* stream index */
741     int             i_picture;                                /* picture index */
742     int             i_active_streams;              /* number of active streams */
743     int             i_err;                                       /* error code */
744     mtime_t         display_date;                              /* display date */    
745     mtime_t         current_date;                              /* current date */
746     picture_t *     pp_sorted_picture[VOUT_MAX_PICTURES];   /* sorted pictures */
747 #ifdef VOUT_DEBUG
748     char            sz_date[MSTRTIME_MAX_SIZE];                 /* date buffer */
749 #endif
750
751     /* 
752      * Initialize thread and free configuration 
753      */
754     p_vout->b_error = InitThread( p_vout );
755     if( p_vout->b_error )
756     {
757         free( p_vout );                                  /* destroy descriptor */
758         return;        
759     }    
760
761     /*
762      * Main loop - it is not executed if an error occured during
763      * initialization
764      */
765     while( (!p_vout->b_die) && (!p_vout->b_error) )
766     {
767         /* Get locks on pictures and streams */
768         vlc_mutex_lock( &p_vout->streams_lock );
769         vlc_mutex_lock( &p_vout->pictures_lock );
770         
771         /* Initialise streams: clear images from all streams */
772         for( i_stream = 0; i_stream < VOUT_MAX_STREAMS; i_stream++ )
773         {
774             p_vout->p_stream[i_stream].p_next_picture = NULL;
775         }
776
777         /* First pass: a set of pictures is selected - all unselected pictures
778          * which should be removed are removed, and a display date is computed.
779          * If a stream has no next_picture after this step, then the heap does
780          * not include any picture from that stream. */
781         current_date = mdate();
782         display_date = FirstPass( p_vout, current_date );
783
784         /* Stream management: streams which are in ENDING or DESTROYED state and
785          * which have no more pictures are destroyed */
786         for( i_active_streams = i_stream = 1; 
787              i_stream < VOUT_MAX_STREAMS; 
788              i_stream++ )
789         {    
790             switch( p_vout->p_stream[i_stream].i_status )
791             {
792             case VOUT_ACTIVE_STREAM:                          /* active stream */
793                 i_active_streams++;
794                 break;
795
796             case VOUT_ENDING_STREAM:            /* ending or destroyed streams */
797             case VOUT_DESTROYED_STREAM:
798                 /* Those streams can be destroyed if there are no more picture
799                  * remaining. This should always be the case for destroyed
800                  * streams, except if it includes permanent pictures */
801                 if( p_vout->p_stream[i_stream].p_next_picture == NULL )
802                 {
803                     p_vout->p_stream[i_stream].i_status = VOUT_INACTIVE_STREAM;
804 #ifdef STATS                   
805                     intf_DbgMsg("%p -> stream %d destroyed %d pictures, %d rendered pictures\n", 
806                                 p_vout, i_stream, p_vout->p_stream[i_stream].c_pictures, 
807                                 p_vout->p_stream[i_stream].c_rendered_pictures );
808 #else
809                     intf_DbgMsg("%p -> stream %d destroyed\n", p_vout, i_stream );
810 #endif
811                 }
812                 /* If the stream can't be destroyed, it means it is still 
813                  * active - in that case, the next image pointer needs to be
814                  * cleard */
815                 else
816                 {
817                     i_active_streams++;
818                     p_vout->p_stream[i_stream].p_next_picture = NULL;
819                 }
820                 break;
821             }
822         }
823       
824         /* From now until next loop, only next_picture field in streams
825          * will be used, and selected pictures structures won't modified.
826          * Therefore, locks can be released */
827         vlc_mutex_unlock( &p_vout->pictures_lock );
828         vlc_mutex_unlock( &p_vout->streams_lock );
829
830         /* If there are some pictures to display, then continue */
831         if( display_date != LAST_MDATE )
832         {
833             /* Increase display_date if it is too low */
834             if( display_date < current_date + VOUT_DISPLAY_DELAY )
835             {
836                 intf_Msg("vout: late picture(s) detected\n");
837                 display_date = current_date + VOUT_DISPLAY_DELAY;
838             }
839 #ifdef VOUT_DEBUG
840             intf_DbgMsg("%p -> display_date is %s\n", p_vout, 
841                         mstrtime( sz_date, display_date ));
842 #endif
843
844             /* Second pass: a maximum of one picture per stream is selected
845              * (except for stream 0), and some previously selected pictures may
846              * be removed */
847             SecondPass( p_vout, display_date );
848 #ifdef VOUT_DEBUG
849             vout_PrintHeap( p_vout, "ready for rendering" );
850 #endif
851  
852             /* Rendering: sort pictures and render them */
853             SortPictures( p_vout, pp_sorted_picture );
854             for( i_picture = 0; pp_sorted_picture[i_picture] != NULL; i_picture++ )
855             {
856                 /* Render picture */
857                 RenderPicture( p_vout, pp_sorted_picture[i_picture] );             
858             }
859             
860             /* Handle output method events - this function can return a negative
861              * value, which means a fatal error and the end of the display loop 
862              * (i.e. the X11 window has been destroyed), a positive one, meaning
863              * a modification occured in the pictures and they do have need to 
864              * be displayed, or 0 */
865             i_err = vout_X11ManageOutputMethod( p_vout );
866             if( !i_err )
867             {
868                 /* Sleep and display */
869                 mwait( display_date );
870                 vout_X11DisplayOutput( p_vout );
871             }
872             else if( i_err < 0 )
873             {                
874                 /* An error occured, and the thread must terminate immediately,
875                  * without displaying anything - setting b_error to 1 cause the
876                  * immediate end of the main while() loop. */
877                 p_vout->b_error = 1;
878             }
879         }        
880         /* If there is nothing to display, just handle events and sleep a while, 
881          * hopping there will be something to do later */
882         else    
883         {
884             if( vout_X11ManageOutputMethod( p_vout ) < 0)
885             {
886                 p_vout->b_error = 1;
887                 
888             }
889             else
890             {   
891                 msleep( VOUT_IDLE_SLEEP );
892             }            
893 #ifdef STATS
894             /* Update counters */
895             p_vout->c_idle_loops++;
896 #endif
897         }        
898
899 #ifdef STATS
900         /* Update counters */
901         p_vout->c_loops++;
902 #endif
903     } 
904
905     /*
906      * Error loop
907      */
908     if( p_vout->b_error )
909     {
910         ErrorThread( p_vout );        
911     }
912
913     /* End of thread */
914     EndThread( p_vout );
915 }
916
917 /*******************************************************************************
918  * ErrorThread: RunThread() error loop
919  *******************************************************************************
920  * This function is called when an error occured during thread main's loop. The
921  * thread can still receive feed, but must be ready to terminate as soon as
922  * possible.
923  *******************************************************************************/
924 static void ErrorThread( vout_thread_t *p_vout )
925 {
926     int i_picture;                                            /* picture index */    
927     
928     /* Wait until a `die' order */
929     while( !p_vout->b_die )
930     {
931         /* Get lock on pictures */
932         vlc_mutex_lock( &p_vout->pictures_lock );     
933
934         /* Try to remove all pictures - only removable pictures will be
935          * removed */
936         for( i_picture = 0; i_picture < p_vout->i_max_pictures; i_picture++ )
937         {   
938             if( p_vout->p_picture[i_picture].i_type != EMPTY_PICTURE )
939             {
940                 EmptyPicture( p_vout, &p_vout->p_picture[i_picture] );
941             }
942         }
943         
944         /* Release locks on pictures */
945         vlc_mutex_unlock( &p_vout->pictures_lock );
946
947         /* Sleep a while */
948         msleep( VOUT_IDLE_SLEEP );                
949     }
950 }
951
952 /*******************************************************************************
953  * EndThread: thread destruction
954  *******************************************************************************
955  * This function is called when the thread ends after a sucessfull 
956  * initialization.
957  *******************************************************************************/
958 static void EndThread( vout_thread_t *p_vout )
959 {
960     int *   pi_status;                                        /* thread status */
961 #ifdef DEBUG
962     int     i_stream;                                    /* video stream index */
963 #endif
964         
965     /* Store status */
966     pi_status = p_vout->pi_status;    
967     *pi_status = THREAD_END;    
968     
969 #ifdef DEBUG
970     /* Check for remaining pictures or video streams */
971     if( p_vout->i_pictures )
972     {
973         intf_DbgMsg("%p -> remaining pictures\n", p_vout);        
974     }   
975     for( i_stream = 1; 
976          (i_stream < VOUT_MAX_STREAMS) && (p_vout->p_stream[i_stream].i_status == VOUT_INACTIVE_STREAM);
977          i_stream++ )
978     {
979         ;        
980     }    
981     if( i_stream != VOUT_MAX_STREAMS )
982     {
983         intf_DbgMsg("%p -> remaining video streams\n", p_vout);        
984     }    
985 #endif
986
987     /* Destroy thread structures allocated by InitThread */
988     vout_X11DestroyOutputMethod( p_vout );            /* destroy output method */
989     free( p_vout->p_picture );                                    /* free heap */
990     free( p_vout );
991
992     /* Update status */
993     *pi_status = THREAD_OVER;    
994     intf_DbgMsg("%p\n", p_vout);
995 }
996
997 /*******************************************************************************
998  * FindPicture: find an empty picture in a video heap
999  *******************************************************************************
1000  * This function is used by most of the vout_*Picture*() functions. It locks the
1001  * video heap, look for an empty picture in it and return a pointer to this
1002  * picture, or NULL if the heap is full.
1003  * Not that the heap is not unlocked when this function returns, and it needs 
1004  * to be donne by the calling function.
1005  *******************************************************************************/
1006 static picture_t *FindPicture( vout_thread_t *p_vout )
1007 {
1008     int         i_picture;                                    /* picture index */
1009
1010     /* Get lock */
1011     vlc_mutex_lock( &p_vout->pictures_lock );
1012
1013     /* Look for an empty place */
1014     for( i_picture = 0; i_picture < p_vout->i_max_pictures; i_picture++ )
1015     {
1016         /* An empty place has been found: return */
1017         if( p_vout->p_picture[i_picture].i_type == EMPTY_PICTURE )
1018         {
1019             return( &p_vout->p_picture[i_picture] );
1020         }
1021     }
1022
1023     /* Nothing has been found */
1024     return( NULL );    
1025 }
1026
1027 /*******************************************************************************
1028  * EmptyPicture: empty a picture without destroying its descriptor
1029  *******************************************************************************
1030  * When a picture is no more used in video heap or must be destroyed, this
1031  * function is called to destroy associated data and set picture type to empty.
1032  * Only non permanent and unlinked pictures can be destroyed. 
1033  * Only non-empty pictures should be sent to this function.
1034  * All picture flags are cleared, and the heap size is decreased.
1035  *******************************************************************************
1036  * Messages type: vout, major code 15
1037  *******************************************************************************/
1038 static void EmptyPicture( vout_thread_t *p_vout, picture_t *p_pic )
1039 {    
1040 #ifdef DEBUG
1041     /* Check if picture is non-empty */
1042     if( p_pic->i_type == EMPTY_PICTURE )
1043     {
1044         intf_DbgMsg("%p -> trying to remove empty picture %p\n",
1045                     p_vout, p_pic);
1046         return;
1047     }
1048 #endif
1049
1050     /* Mark as ready for destruction */
1051     p_pic->i_flags |= DESTROY_PICTURE;
1052     p_pic->i_flags &= ~DISPLAY_PICTURE;
1053
1054     /* If picture is no more referenced and is not permanent, destroy */
1055     if( (p_pic->i_refcount == 0) && !(p_pic->i_flags & PERMANENT_PICTURE) )
1056     {
1057         /* If picture owns its data, free them */
1058         if( p_pic->i_flags & OWNER_PICTURE )
1059         {
1060             free( p_pic->p_data );
1061         }           
1062         p_pic->i_type = EMPTY_PICTURE;
1063         p_pic->i_flags = 0;  
1064         p_vout->i_pictures--;
1065         intf_DbgMsg("%p -> picture %p removed\n", p_vout, p_pic);
1066     }
1067 }
1068
1069 /*******************************************************************************
1070  * FirstPass: first pass of the vout thread on pictures
1071  *******************************************************************************
1072  * A set of pictures is selected according to the current date and their display
1073  * date. Some unselected and already displayed pictures are removed, and the
1074  * next display date is computed. 
1075  * The next_picture fields of all the streams are updated. Note that streams
1076  * are initialized before this function is called.
1077  *******************************************************************************
1078  * Messages type: vout, major code 18
1079  *******************************************************************************/
1080 static mtime_t FirstPass( vout_thread_t *p_vout, mtime_t current_date )
1081 {
1082     mtime_t         display_date;                              /* display date */
1083     int             i_picture;                                /* picture index */
1084     picture_t *     p_picture;                   /* picture pointer (shortcut) */
1085  
1086     /* Set up date */
1087     display_date = LAST_MDATE;
1088         
1089     /* Browse all pictures */
1090     for( i_picture = 0; i_picture < p_vout->i_max_pictures; i_picture++ )
1091     {
1092         p_picture = &p_vout->p_picture[i_picture];
1093
1094         /* Empty pictures are never selected */
1095         if( p_picture->i_type == EMPTY_PICTURE )
1096         {
1097             ;
1098         }               
1099         /* Destroyed pictures are never selected, they can be emptied if needed */
1100         else if( p_picture->i_flags & DESTROY_PICTURE )
1101         {
1102             EmptyPicture( p_vout, p_picture );
1103         }  
1104 #ifdef DEBUG
1105         /* Pictures in an inactive stream are submitted to a deletion attempt,
1106          * and nether selected - this case should never happen */
1107         else if( p_vout->p_stream[ p_picture->i_stream ].i_status == VOUT_INACTIVE_STREAM )
1108         {   
1109             intf_DbgMsg("%p -> picture %p belongs to an inactive stream\n", 
1110                         p_vout, p_picture);
1111             EmptyPicture( p_vout, p_picture );
1112         }  
1113 #endif
1114         /* Pictures in a destroyed stream are submitted to a deletion attempt,
1115          * and nether selected */
1116         else if( p_vout->p_stream[ p_picture->i_stream ].i_status == VOUT_DESTROYED_STREAM )
1117         {   
1118             EmptyPicture( p_vout, p_picture );
1119         }
1120         /* Reserved pictures are never selected */
1121         else if( p_picture->i_flags & RESERVED_PICTURE )
1122         {
1123             p_picture->i_flags &= ~DISPLAY_PICTURE;
1124         }
1125         /* Overlay pictures */
1126         else if( p_picture->i_flags & OVERLAY_PICTURE )
1127         {
1128             /* A picture is outdated if it is not permanent and if it's maximal
1129              * date is passed */
1130             if( !(p_picture->i_flags & PERMANENT_PICTURE ) 
1131                 && (p_picture->date + p_picture->duration < current_date) )
1132             {
1133                 p_picture->i_flags &= ~DISPLAY_PICTURE;      
1134                 EmptyPicture( p_vout, p_picture );
1135             }
1136             /* Else if picture is in stream 0, it will always be selected */
1137             else if( p_picture->i_stream == 0 )
1138             {
1139                 p_picture->i_flags |= DISPLAY_PICTURE;   
1140                 /* Update display_date if picture has never been displayed */
1141                 if( !(p_picture->i_flags & DISPLAYED_PICTURE) && (p_picture->date < display_date) )
1142                 {
1143                     display_date = p_picture->date;
1144                 }    
1145             }
1146             /* The picture can be considered as a regular picture, because it
1147              * has never been displayed */
1148             else if( !(p_picture->i_flags & DISPLAYED_PICTURE) )
1149             {                
1150                 /* Select picture if can be updated */
1151                 if( (p_vout->p_stream[ p_picture->i_stream].p_next_picture == NULL)
1152                     || (p_vout->p_stream[p_picture->i_stream].p_next_picture->date > p_picture->date))
1153                 {
1154                     p_picture->i_flags |= DISPLAY_PICTURE;   
1155                     p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;
1156                     /* Update display_date */
1157                     if( p_picture->date < display_date )
1158                     {
1159                         display_date = p_picture->date;
1160                     }
1161                 }
1162             }
1163             /* In other cases (overlay pictures which have already been displayed), 
1164              * the picture is always selected */
1165             else
1166             {
1167                 p_picture->i_flags |= DISPLAY_PICTURE;
1168                 /* The stream is only updated if there is no picture in that stream */
1169                 if( p_vout->p_stream[ p_picture->i_stream].p_next_picture == NULL )
1170                 {
1171                     p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;
1172                 }
1173             }
1174         }
1175         /* Non overlay pictures are deleted if they have been displayed, and
1176          * selected if there date is valid */
1177         else
1178         {
1179             /* Remove already displayed pictures */
1180             if( p_picture->i_flags & DISPLAYED_PICTURE )
1181             {
1182                 EmptyPicture( p_vout, p_picture );
1183             }
1184             /* Remove passed pictures, The picture is marked as displayed in case it 
1185              * could not be removed */
1186             else if( p_picture->date < current_date )
1187             {
1188                 intf_DbgMsg("%p -> picture %p detected late\n", 
1189                             p_vout, p_picture );
1190                 p_picture->i_flags |= DISPLAYED_PICTURE;
1191                 EmptyPicture( p_vout, p_picture );
1192             }
1193             /* Update streams for other pictures */
1194             else
1195             {
1196                 p_picture->i_flags |= DISPLAY_PICTURE;
1197                 /* Update 'next picture' field in stream descriptor */
1198                 if( (p_vout->p_stream[ p_picture->i_stream].p_next_picture == NULL)
1199                     || (p_vout->p_stream[p_picture->i_stream].p_next_picture->date > p_picture->date))
1200                 {
1201                    p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;
1202                    /* Update display_date */
1203                    if( p_picture->date < display_date )
1204                    {
1205                        display_date = p_picture->date;
1206                    }
1207                 }
1208             }
1209         }
1210     }
1211
1212     return( display_date );
1213 }
1214
1215 /*******************************************************************************
1216  * SecondPass: second pass of the vout thread on pictures
1217  *******************************************************************************
1218  * Select only one picture per stream other than stream 0, and remove pictures
1219  * which should have been displayed.... but arrived too late. Note that nothing
1220  * is locked when this function is called, and therefore pictures should not
1221  * be removed here.
1222  * Only previously selected pictures are processed.
1223  *******************************************************************************
1224  * Messages type: vout, major code 19
1225  *******************************************************************************/
1226 static void SecondPass( vout_thread_t *p_vout, mtime_t display_date )
1227 {
1228     int         i_picture;                                    /* picture index */
1229     picture_t * p_picture;                       /* picture pointer (shortcut) */
1230
1231     for( i_picture = 0; i_picture < p_vout->i_max_pictures; i_picture++ )
1232     {
1233         p_picture = &p_vout->p_picture[i_picture];
1234         
1235         /* Process only previously selected pictures */
1236         if( p_picture->i_flags & DISPLAY_PICTURE )
1237         {
1238             /* Deselect picture if it is too youg */
1239             if( p_picture->date > display_date + VOUT_DISPLAY_TOLERANCE )
1240             {
1241                 p_picture->i_flags &= ~DISPLAY_PICTURE;
1242             }
1243             /* Pictures in stream 0 are selected depending only of their date */
1244             else if( p_picture->i_stream == 0 )
1245             {
1246                 /* Overlay pictures are not selected if they are 
1247                  * outdated */
1248                 if( p_picture->i_flags & OVERLAY_PICTURE )
1249                 {
1250                     if( !(p_picture->i_flags & PERMANENT_PICTURE)
1251                     && (display_date > p_picture->date + p_picture->duration) )
1252                     {
1253                         p_picture->i_flags |= DESTROY_PICTURE;
1254                         p_picture->i_flags &= ~DISPLAY_PICTURE;
1255                     }
1256                 }
1257                 /* Regular pictures are selected if they are not too late */
1258                 else if( p_picture->date < display_date - VOUT_DISPLAY_TOLERANCE )
1259                 {
1260                     intf_DbgMsg("%p -> picture %p detected late\n",
1261                                 p_vout, p_picture );
1262                     p_picture->i_flags |= DISPLAYED_PICTURE | DESTROY_PICTURE;
1263                     p_picture->i_flags &= ~DISPLAY_PICTURE;
1264                 }
1265             }
1266             /* Overlay pictures which have been displayed have special 
1267              * processing */
1268             else if( (p_picture->i_flags & OVERLAY_PICTURE) 
1269                      && (p_picture->i_flags & DISPLAYED_PICTURE) )
1270             {
1271                 /* If the stream is not empty, or the picture has been 
1272                  * outdated, de select */
1273                 if( (p_vout->p_stream[p_picture->i_stream].p_next_picture != NULL)
1274                     || (!(p_picture->i_flags & PERMANENT_PICTURE)
1275                         && (display_date > p_picture->date + p_picture->duration) ) )
1276                 {
1277                     p_picture->i_flags |= DESTROY_PICTURE;
1278                     p_picture->i_flags &= ~DISPLAY_PICTURE;
1279                 }
1280             }
1281             /* Other pictures are 'regular' pictures */
1282             else 
1283             {
1284                 /* If the stream is empty, always select */
1285                 if( p_vout->p_stream[p_picture->i_stream].p_next_picture == NULL)
1286                 {
1287                     p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;
1288                 }
1289                 /* Else, remove and mark as displayed (skip) if the picture is too old */
1290                 else if( p_picture->date < display_date - VOUT_DISPLAY_TOLERANCE )
1291                 {
1292                     intf_DbgMsg("%p -> picture %p detected late\n",
1293                                 p_vout, p_picture );
1294                     p_picture->i_flags |= DISPLAYED_PICTURE | DESTROY_PICTURE;
1295                     p_picture->i_flags &= ~DISPLAY_PICTURE;
1296                 }
1297                 /* Else, select if the picture is younger than the current selected one */
1298                 else if( p_picture->date 
1299                           < p_vout->p_stream[p_picture->i_stream].p_next_picture->date )
1300                 {
1301                     /* Deselect the previous picture */
1302                     p_vout->p_stream[p_picture->i_stream].p_next_picture->i_flags 
1303                         &= ~DISPLAY_PICTURE; 
1304                     /* Select the current one */
1305                     p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;     
1306                 }
1307                 /* Else, select if the current selected one is an already displayed overlay */
1308                 else if( (p_vout->p_stream[p_picture->i_stream].p_next_picture->i_flags 
1309                           & ( OVERLAY_PICTURE | DISPLAYED_PICTURE )) 
1310                          == (OVERLAY_PICTURE | DISPLAYED_PICTURE) ) 
1311
1312                 {
1313                     /* Deselect and remove the previous picture */
1314                     p_picture->i_flags |= DESTROY_PICTURE;
1315                     p_picture->i_flags &= ~DISPLAY_PICTURE;
1316                     /* Select the current one */
1317                     p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;     
1318                 }
1319                 /* Else, deselect the picture */
1320                 else
1321                 {
1322                     p_picture->i_flags &= ~DISPLAY_PICTURE;
1323                 }
1324             }
1325         }
1326     }
1327 }
1328
1329 /*******************************************************************************
1330  * SortPictures: sort pictures ready for display by hierarchical level
1331  *******************************************************************************
1332  * Build an array of pictures in rendering order, starting from minimal to
1333  * level to maximal level. A NULL pointer always ends the array, which size is
1334  * limited to VOUT_MAX_PICTURES.
1335  *******************************************************************************
1336  * Messages type: vout, major code 26
1337  *******************************************************************************/
1338 static void SortPictures( vout_thread_t *p_vout, picture_t **pp_picture )
1339 {
1340     int             i_picture;                /* picture index in sorted array */
1341     int             i_heap_picture;                   /* picture index in heap */
1342     picture_t *     p_previous_picture;                /* first shift register */
1343     picture_t *     p_current_picture;                /* second shift register */
1344
1345     /* Initialize array */
1346     pp_picture[0] = NULL;
1347
1348     /* Copy and sort pictures */
1349     for( i_heap_picture = 0; i_heap_picture < p_vout->i_max_pictures ; i_heap_picture++ )
1350     {
1351         /* Sort only selected pictures */
1352         if( p_vout->p_picture[ i_heap_picture ].i_flags & DISPLAY_PICTURE )
1353         {            
1354             /* Find picture position */
1355             for( i_picture = 0; (pp_picture[i_picture] != NULL) 
1356                      && (pp_picture[i_picture]->i_level 
1357                          <= p_vout->p_picture[ i_heap_picture ].i_level);
1358                  i_picture++ )
1359             {
1360                 ;
1361             }
1362             p_current_picture = &p_vout->p_picture[ i_heap_picture ];
1363
1364             /* Insert picture and shift end of array */
1365             for( ; p_previous_picture != NULL; i_picture++ )
1366             {
1367                 p_previous_picture = p_current_picture;
1368                 p_current_picture = pp_picture[i_picture];
1369                 if( i_picture == VOUT_MAX_PICTURES - 1 )
1370                 {
1371                     p_previous_picture = NULL;
1372                 }
1373                 pp_picture[i_picture] = p_previous_picture; 
1374             }
1375         }
1376     }
1377
1378 }
1379
1380 /*******************************************************************************
1381  * RenderPicture: render a picture
1382  *******************************************************************************
1383  * This function convert a picture from a video heap to a pixel-encoded image
1384  * and copy it to the current rendering buffer. No lock is required, since the
1385  * rendered picture has been determined as existant, and will only be destroyed
1386  * by the vout_Thread() later.
1387  *******************************************************************************
1388  * Messages types: vout, major code 27
1389  *******************************************************************************/
1390 static void RenderPicture( vout_thread_t *p_vout, picture_t *p_pic )
1391 {
1392     int     i_x, i_y;                                   /* position in display */
1393     int     i_pic_x;                            /* x offset in clipped picture */
1394     int     i_pic_y;                            /* y offset in clipped picture */
1395     int     i_height, i_width;                   /* clipped picture dimensions */
1396     int     i_line_width;                                        /* line width */
1397     void    (*RenderLine)(vout_thread_t *p_vout, picture_t *p_pic, /* renderer */
1398                           int i_x, int i_y, int i_pic_x, int i_pic_y, int i_width,
1399                           int i_line_width, int i_ratio );
1400
1401     /* Update counters - this is done now since for blank pictures, the function 
1402      * may never reach it's regular end */
1403     p_pic->i_flags |= DISPLAYED_PICTURE;
1404 #ifdef STATS
1405     p_vout->c_rendered_pictures++;
1406     p_vout->p_stream[ p_pic->i_stream ].c_rendered_pictures++;
1407 #endif                
1408     
1409     /* Computes position and size of the picture zone to render */
1410     i_x = p_pic->i_x;
1411     i_y = p_pic->i_y;
1412     i_width = p_pic->i_width;
1413     i_height = p_pic->i_height;
1414     ClipPicture( &i_x, p_vout->i_width, &i_pic_x, &i_width, p_pic->i_h_ratio, p_pic->i_h_align );
1415     ClipPicture( &i_y, p_vout->i_height, &i_pic_y, &i_height, p_pic->i_v_ratio, p_pic->i_v_align );
1416 #ifdef VOUT_DEBUG
1417    intf_DbgMsg("%p -> picture %p, (%d-%d:%d-%d, %dx%d)\n", 
1418                p_vout, p_pic, i_x, i_pic_x, i_y, i_pic_y, i_width, i_height );
1419
1420 #endif
1421
1422     /* If there is noting to display, returns immediately */
1423     if( (i_pic_x >= i_width) || (i_pic_y >= i_height) )
1424     {
1425         return;
1426     }
1427     
1428     /* Determine method used to render line. This function is choosed here to
1429      * avoid multiple conditions tests later */
1430     switch( p_pic->i_type )
1431     {
1432     case RGB_BLANK_PICTURE:         /* picture is blank, RGB encoded (no data) */
1433     case PIXEL_BLANK_PICTURE:     /* picture is blank, pixel encoded (no data) */
1434         /* Blank pictures are displayed immediately - dimensions are real ones,
1435          * and should be recalculated */
1436         switch( p_pic->i_h_ratio )                   
1437         {
1438         case DISPLAY_RATIO_HALF:                              /* 1:2 half size */
1439             i_width = (i_width - i_pic_x) / 2;
1440             break;     
1441         case DISPLAY_RATIO_NORMAL:                          /* 1:1 normal size */
1442             i_width -= i_pic_x;
1443             break;
1444         case DISPLAY_RATIO_DOUBLE:                          /* 2:1 double size */   
1445             i_width = (i_width - i_pic_x) * 2;
1446             break;
1447             /* ?? add others display ratio */
1448         }
1449         switch( p_pic->i_v_ratio )                   
1450         {
1451         case DISPLAY_RATIO_HALF:                              /* 1:2 half size */
1452             i_height = (i_height - i_pic_y) / 2;
1453             break;     
1454         case DISPLAY_RATIO_NORMAL:                          /* 1:1 normal size */
1455             i_height -= i_pic_y;
1456             break;
1457         case DISPLAY_RATIO_DOUBLE:                          /* 2:1 double size */   
1458             i_height = (i_height - i_pic_y) * 2;
1459             break;
1460             /* ?? add others display ratio */
1461         }
1462         if( p_pic->i_type == RGB_BLANK_PICTURE )          /* RGB blank picture */
1463         {            
1464             p_vout->RenderRGBBlank( p_vout, p_pic->pixel, i_x, i_y, i_width, i_height );
1465         }
1466         else                                            /* pixel blank picture */
1467         {
1468             p_vout->RenderPixelBlank( p_vout, p_pic->pixel, i_x, i_y, i_width, i_height );
1469         }        
1470         return;
1471         break;
1472     case RGB_PICTURE:                        /* picture is 24 bits rgb encoded */
1473         RenderLine = p_vout->RenderRGBLine;
1474         break;
1475     case PIXEL_PICTURE:                            /* picture is pixel encoded */
1476         RenderLine = p_vout->RenderPixelLine;
1477         break;
1478     case RGB_MASK_PICTURE:                      /* picture is a 1 rgb bpp mask */
1479         RenderLine = p_vout->RenderRGBMaskLine;
1480         break;
1481     case PIXEL_MASK_PICTURE:                  /* picture is a 1 pixel bpp mask */
1482         RenderLine = p_vout->RenderPixelMaskLine;
1483         break;
1484         /* ?? add YUV types */
1485 #ifdef DEBUG
1486     default:                      /* internal error, which should never happen */
1487         intf_DbgMsg("%p -> unknown type for picture %p\n", p_vout, p_pic);
1488         break;  
1489 #endif  
1490     }
1491
1492     /* For non blank pictures, loop on lines */
1493     for( ; i_pic_y < i_height; i_pic_y++ )
1494     {
1495         /* First step: check if line has to be rendered. This is not obvious since
1496          * if display ratio is less than 1:1, some of the lines don't need to
1497          * be displayed, and therefore do not need to be rendered. */
1498         switch( p_pic->i_v_ratio )
1499         {
1500         case DISPLAY_RATIO_HALF:                              /* 1:2 half size */
1501             /* Only even lines are rendered, and copied once */
1502             i_line_width = i_pic_y % 2;
1503             break;
1504
1505         case DISPLAY_RATIO_NORMAL:                          /* 1:1 normal size */
1506             /* All lines are rendered and copied once */
1507             i_line_width = 1;
1508             break;
1509
1510         case DISPLAY_RATIO_DOUBLE:                          /* 2:1 double size */
1511             /* All lines are rendered and copied twice */
1512             i_line_width = 2;
1513             break;
1514         }
1515
1516         if( i_line_width )
1517         {
1518             /* Computed line width can be reduced if it would cause to render 
1519              * outside the display */
1520             if( i_y + i_line_width > p_vout->i_height )
1521             {
1522                 i_line_width = p_vout->i_height - i_y;
1523             }
1524
1525             /* Second step: render line. Since direct access to the rendering 
1526              * buffer is required, functions in charge of rendering the line
1527              * are part of the display driver. Because this step is critical
1528              * and require high optimization, different methods are used 
1529              * depending of the horizontal display ratio, the image type and 
1530              * the screen depth. */
1531             RenderLine( p_vout, p_pic, i_x, i_y, i_pic_x, i_pic_y,
1532                         i_width, i_line_width, p_pic->i_h_ratio );
1533             
1534             /* Increment display line index */
1535             i_y += i_line_width;
1536         }
1537     }
1538 }
1539
1540 /*******************************************************************************
1541  * ClipPicture: clip a picture in display window
1542  *******************************************************************************
1543  * This function computes picture placement in display window and rendering
1544  * zone, according to wished picture placement and display ratio. It must be
1545  * called twice, once and with the x coordinates and once with the y
1546  * coordinates. 
1547  * The pi_pic_ofs parameter is only written, but pi_ofs and pi_pic_size must
1548  * be valid arguments. Note that *pi_pic_size is still the rendering zone size,
1549  * starting from picture offset 0 and not the size starting from *pi_pic_ofs
1550  * (same thing for i_size).
1551  *******************************************************************************
1552  * Messages types: vout, major code 28
1553  *******************************************************************************/
1554 static void ClipPicture( int *pi_ofs, int i_size, int *pi_pic_ofs, int *pi_pic_size,
1555                          int i_ratio, int i_placement )
1556 {
1557     int i_ofs;                                   /* temporary picture position */
1558
1559     /* Computes base picture position */
1560     switch( i_placement )
1561     {
1562     case -1:                                               /* left/top aligned */
1563         i_ofs = *pi_ofs;
1564         break;
1565     case 0:                                                        /* centered */
1566         i_ofs = *pi_ofs + (i_size - *pi_pic_size) / 2;
1567         break;
1568     case 1:                                            /* right/bottom aligned */
1569         i_ofs = *pi_ofs + i_size - *pi_pic_size;
1570         break;
1571 #ifdef DEBUG
1572     default:                      /* internal error, which should never happen */
1573         intf_DbgMsg("invalid placement\n");
1574         break;
1575 #endif
1576     }
1577
1578     /* Computes base rendering position and update picture position - i_ofs is
1579      * the picture position, and can be negative */
1580     if( i_ofs < 0 )                       /* picture starts outside the screen */
1581     {
1582         switch( i_ratio )                   
1583         {
1584         case DISPLAY_RATIO_HALF:                              /* 1:2 half size */
1585             *pi_pic_ofs = - *pi_ofs * 2;
1586             break;     
1587         case DISPLAY_RATIO_NORMAL:                          /* 1:1 normal size */
1588             *pi_pic_ofs = -*pi_ofs;
1589             break;
1590         case DISPLAY_RATIO_DOUBLE:                          /* 2:1 double size */
1591             *pi_pic_ofs = -CEIL(*pi_ofs, 2);
1592             break;
1593 #ifdef DEBUG
1594         default:                  /* internal error, which should never happen */
1595             intf_DbgMsg("unsupported ratio\n");
1596             break;  
1597 #endif  
1598         }
1599         *pi_ofs = 0;
1600     }
1601     else                                   /* picture starts inside the screen */
1602     {
1603         *pi_pic_ofs = 0;
1604         *pi_ofs = i_ofs;
1605     }
1606
1607     /* Computes rendering size - i_ofs is the picture position, and can be 
1608      * negative, *pi_ofs is the real picture position, and is always positive */
1609     switch( i_ratio )                   
1610     {
1611     case DISPLAY_RATIO_HALF:                                  /* 1:2 half size */
1612         if( i_ofs + CEIL(*pi_pic_size, 2) > i_size )
1613         {
1614             *pi_pic_size = ( i_size - i_ofs ) * 2;
1615         }
1616         break;     
1617     case DISPLAY_RATIO_NORMAL:                              /* 1:1 normal size */
1618         if( i_ofs + *pi_pic_size > i_size )
1619         {
1620             *pi_pic_size = i_size - i_ofs;
1621         }
1622         break;
1623     case DISPLAY_RATIO_DOUBLE:                              /* 2:1 double size */
1624         if( *pi_ofs + *pi_pic_size * 2 > i_size )
1625         {
1626             *pi_pic_size = ( i_size - i_ofs ) / 2;
1627         }
1628         break;
1629 #ifdef DEBUG
1630     default:                  /* internal error, which should never happen */
1631         intf_DbgMsg("unsupported ratio\n");
1632         break;  
1633 #endif  
1634     }     
1635 }
1636