]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
Suppression de vlc.h
[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
18 #ifdef VIDEO_X11
19 #include <X11/Xlib.h>
20 #endif
21
22 #include "common.h"
23 #include "config.h"
24 #include "mtime.h"
25 #include "vlc_thread.h"
26 #include "video.h"
27 #include "video_output.h"
28 #include "video_sys.h"
29 #include "intf_msg.h"
30
31 /*******************************************************************************
32  * Macros
33  *******************************************************************************/
34 #define CLIP_BYTE( i_val ) ( (i_val < 0) ? 0 : ((i_val > 255) ? 255 : i_val) )
35
36 /*******************************************************************************
37  * Constants
38  *******************************************************************************/
39
40 /* RGB/YUV inversion matrix (ISO/IEC 13818-2 section 6.3.6, table 6.9) */
41 int matrix_coefficients[8][4] =
42 {
43   {117504, 138453, 13954, 34903},       /* no sequence_display_extension */
44   {117504, 138453, 13954, 34903},       /* ITU-R Rec. 709 (1990) */
45   {104597, 132201, 25675, 53279},       /* unspecified */
46   {104597, 132201, 25675, 53279},       /* reserved */
47   {104448, 132798, 24759, 53109},       /* FCC */
48   {104597, 132201, 25675, 53279},       /* ITU-R Rec. 624-4 System B, G */
49   {104597, 132201, 25675, 53279},       /* SMPTE 170M */
50   {117579, 136230, 16907, 35559}        /* SMPTE 240M (1987) */
51 };
52
53 /*******************************************************************************
54  * Local prototypes
55  *******************************************************************************/
56 static int      InitThread          ( vout_thread_t *p_vout );
57 static void     RunThread           ( vout_thread_t *p_vout );
58 static void     ErrorThread         ( vout_thread_t *p_vout );
59 static void     EndThread           ( vout_thread_t *p_vout );
60
61 static void     RenderPicture   ( vout_thread_t *p_vout, picture_t *p_pic );
62
63 /*******************************************************************************
64  * vout_CreateThread: creates a new video output thread
65  *******************************************************************************
66  * This function creates a new video output thread, and returns a pointer
67  * to its description. On error, it returns NULL.
68  * Following configuration properties are used:
69  *  VIDEO_CFG_SIZE      video heap maximal size
70  *  VIDEO_CFG_WIDTH     window width
71  *  VIDEO_CFG_HEIGHT    window height 
72  * Using X11 display method (the only one supported yet):
73  *  VIDEO_CFG_DISPLAY   display used
74  *  VIDEO_CFG_TITLE     window title
75  *  VIDEO_CFG_SHM_EXT   try to use XShm extension
76  * If pi_status is NULL, then the function will block until the thread is ready.
77  * If not, pi_error will be updated using one of the THREAD_* constants.
78  *******************************************************************************/
79 vout_thread_t * vout_CreateThread               ( 
80 #if defined(VIDEO_X11)
81                                                   char *psz_display, Window root_window, 
82 #elif defined(VIDEO_FB)
83                                                   //??
84 #endif
85                                                   int i_width, int i_height, int *pi_status 
86                                                 )
87 {
88     vout_thread_t * p_vout;                               /* thread descriptor */
89     int             i_status;                                 /* thread status */
90
91     /* Allocate descriptor and create method */
92     p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
93     if( p_vout == NULL )                                              /* error */
94     {
95         return( NULL );
96     }
97     intf_DbgMsg( "0x%x\n", p_vout );
98     if( vout_SysCreate( p_vout
99 #if defined(VIDEO_X11)
100                         , psz_display, root_window 
101 #elif defined(VIDEO_FB)
102                         //??
103 #endif
104         ) )
105     {
106       free( p_vout );
107       return( NULL );
108     }
109   
110     /* Initialize */
111     p_vout->i_width            = i_width;
112     p_vout->i_height           = i_height;
113     p_vout->pi_status          = (pi_status != NULL) ? pi_status : &i_status;
114     *p_vout->pi_status         = THREAD_CREATE;
115     p_vout->b_die              = 0;
116     p_vout->b_error            = 0;    
117     p_vout->b_active           = 0;
118
119     /* Create thread and set locks */
120     vlc_mutex_init( &p_vout->lock );
121     if( vlc_thread_create( &p_vout->thread_id, "video output", 
122                            (void *) RunThread, (void *) p_vout) )
123     {
124         intf_ErrMsg("vout error: %s\n", strerror(ENOMEM));
125         vout_SysDestroy( p_vout );
126         free( p_vout );
127         return( NULL );
128     }   
129
130     /* If status is NULL, wait until the thread is created */
131     if( pi_status == NULL )
132     {
133         do
134         {            
135             msleep( THREAD_SLEEP );
136         }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR) 
137                 && (i_status != THREAD_FATAL) );
138         if( i_status != THREAD_READY )
139         {
140             return( NULL );            
141         }        
142     }
143
144     return( p_vout );
145 }
146
147 /*******************************************************************************
148  * vout_DestroyThread: destroys a previously created thread
149  *******************************************************************************
150  * Destroy a terminated thread. 
151  * The function will request a destruction of the specified thread. If pi_error
152  * is NULL, it will return once the thread is destroyed. Else, it will be 
153  * update using one of the THREAD_* constants.
154  *******************************************************************************/
155 void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
156 {  
157     int     i_status;                                         /* thread status */
158
159     intf_DbgMsg( "0x%x\n", p_vout );
160
161     /* Set status */
162     p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
163     *p_vout->pi_status = THREAD_DESTROY;    
164      
165     /* Request thread destruction */
166     p_vout->b_die = 1;
167
168     /* If status is NULL, wait until thread has been destroyed */
169     if( pi_status == NULL )
170     {
171         do
172         {
173             msleep( THREAD_SLEEP );
174         }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR) 
175                 && (i_status != THREAD_FATAL) );   
176     }
177 }
178
179 /*******************************************************************************
180  * vout_DisplayPicture: display a picture
181  *******************************************************************************
182  * Remove the reservation flag of a picture, which will cause it to be ready for
183  * display.
184  *******************************************************************************/
185 void  vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
186 {
187     vlc_mutex_lock( &p_vout->lock );
188
189     /* Remove reservation flag */
190     p_pic->i_status = READY_PICTURE;
191
192 #ifdef STATS
193     /* Update stats */
194     p_vout->c_pictures++;
195 #endif
196
197     vlc_mutex_unlock( &p_vout->lock );
198 }
199
200 /*******************************************************************************
201  * vout_CreatePicture: allocate a picture in the video output heap.
202  *******************************************************************************
203  * This function create a reserved image in the video output heap. 
204  * A null pointer is returned if the function fails. This method provides an
205  * already allocated zone of memory in the picture data fields.
206  *******************************************************************************/
207 picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type, 
208                                int i_width, int i_height, int i_bytes_per_line )
209 {
210     int         i_picture;                                    /* picture index */
211     picture_t * p_free_picture = NULL;                   /* first free picture */    
212     picture_t * p_destroyed_picture = NULL;         /* first destroyed picture */    
213
214     /* Get lock */
215     vlc_mutex_lock( &p_vout->lock );
216
217     /* 
218      * Look for an empty place 
219      */
220     for( i_picture = 0; 
221          i_picture < VOUT_MAX_PICTURES; 
222          i_picture++ )
223     {
224         if( p_vout->p_picture[i_picture].i_status == DESTROYED_PICTURE )
225         {
226             /* Picture is marked for destruction, but is still allocated */
227             if( (p_vout->p_picture[i_picture].i_type           == i_type)   &&
228                 (p_vout->p_picture[i_picture].i_height         == i_height) &&
229                 (p_vout->p_picture[i_picture].i_bytes_per_line == i_bytes_per_line) )
230             {
231                 /* Memory size do match : memory will not be reallocated, and function
232                  * can end immediately - this is the best possible case, since no
233                  * memory allocation needs to be done */
234                 p_vout->p_picture[i_picture].i_width  = i_width;
235                 p_vout->p_picture[i_picture].i_status = RESERVED_PICTURE;
236                 vlc_mutex_unlock( &p_vout->lock );
237                 return( &p_vout->p_picture[i_picture] );
238             }
239             else if( p_destroyed_picture == NULL )
240             {
241                 /* Memory size do not match, but picture index will be kept in
242                  * case no other place are left */
243                 p_destroyed_picture = &p_vout->p_picture[i_picture];                
244             }       
245         }
246         else if( (p_free_picture == NULL) && 
247                  (p_vout->p_picture[i_picture].i_status == FREE_PICTURE ))
248         {
249             /* Picture is empty and ready for allocation */
250             p_free_picture = &p_vout->p_picture[i_picture];            
251         }
252     }
253
254     /* If no free picture is available, use a destroyed picture */
255     if( (p_free_picture == NULL) && (p_destroyed_picture != NULL ) )
256     { 
257         /* No free picture or matching destroyed picture has been found, but
258          * a destroyed picture is still avalaible */
259         free( p_destroyed_picture->p_data );        
260         p_free_picture = p_destroyed_picture;        
261     }
262
263     /*
264      * Prepare picture
265      */
266     if( p_free_picture != NULL )
267     {
268         /* Allocate memory */
269         switch( i_type )
270         {
271         case YUV_420_PICTURE:           /* YUV picture: 3*16 ?? bits per pixel */
272         case YUV_422_PICTURE:
273         case YUV_444_PICTURE:
274             p_free_picture->p_data = malloc( 3 * i_height * i_bytes_per_line );                
275             p_free_picture->p_y = (yuv_data_t *) p_free_picture->p_data;
276             p_free_picture->p_u = (yuv_data_t *)(p_free_picture->p_data + i_height * i_bytes_per_line);
277             p_free_picture->p_v = (yuv_data_t *)(p_free_picture->p_data + i_height * i_bytes_per_line * 2);
278             break;                
279 #ifdef DEBUG
280         default:
281             intf_DbgMsg("0x%x error: unknown picture type %d\n", p_vout, i_type );
282             p_free_picture->p_data = NULL;            
283 #endif    
284         }
285
286         if( p_free_picture->p_data != NULL )
287         {        
288             /* Copy picture informations */
289             p_free_picture->i_type           = i_type;
290             p_free_picture->i_status         = RESERVED_PICTURE;
291             p_free_picture->i_width          = i_width;
292             p_free_picture->i_height         = i_height;
293             p_free_picture->i_bytes_per_line = i_bytes_per_line;
294             p_free_picture->i_refcount       = 0;            
295         }
296         else
297         {
298             /* Memory allocation failed : set picture as empty */
299             p_free_picture->i_type   = EMPTY_PICTURE;            
300             p_free_picture->i_status = FREE_PICTURE;            
301             p_free_picture = NULL;            
302             intf_DbgMsg("0x%x malloc for new picture failed\n");            
303         }
304         
305         vlc_mutex_unlock( &p_vout->lock );
306         return( p_free_picture );
307     }
308     
309     // No free or destroyed picture could be found
310     intf_DbgMsg("0x%x no picture available\n");
311     vlc_mutex_unlock( &p_vout->lock );
312     return( NULL );
313 }
314
315 /*******************************************************************************
316  * vout_RemovePicture: remove a permanent or reserved picture from the heap
317  *******************************************************************************
318  * This function frees a previously reserved picture or a permanent
319  * picture. It is meant to be used when the construction of a picture aborted.
320  * Note that the picture will be destroyed even if it is linked !
321  *******************************************************************************/
322 void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
323 {
324     vlc_mutex_lock( &p_vout->lock );
325
326     /* Mark picture for destruction */
327     p_pic->i_status = DESTROYED_PICTURE;
328     intf_DbgMsg("%p -> picture %p destroyed\n", p_vout, p_pic );
329
330     vlc_mutex_unlock( &p_vout->lock );
331 }
332
333 /*******************************************************************************
334  * vout_LinkPicture: increment reference counter of a picture
335  *******************************************************************************
336  * This function increment the reference counter of a picture in the video
337  * heap.
338  *******************************************************************************/
339 void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
340 {
341     vlc_mutex_lock( &p_vout->lock );
342     p_pic->i_refcount++;
343     vlc_mutex_unlock( &p_vout->lock );
344 }
345
346 /*******************************************************************************
347  * vout_UnlinkPicture: decrement reference counter of a picture
348  *******************************************************************************
349  * This function decrement the reference counter of a picture in the video heap.
350  *******************************************************************************/
351 void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
352 {
353     vlc_mutex_lock( &p_vout->lock );
354     p_pic->i_refcount--;
355     if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
356     {
357         p_pic->i_status = DESTROYED_PICTURE;
358     }
359     vlc_mutex_unlock( &p_vout->lock );    
360 }
361
362 /* following functions are local */
363
364 /*******************************************************************************
365  * InitThread: initialize video output thread
366  *******************************************************************************
367  * This function is called from RunThread and performs the second step of the
368  * initialization. It returns 0 on success. Note that the thread's flag are not
369  * modified inside this function.
370  *******************************************************************************/
371 static int InitThread( vout_thread_t *p_vout )
372 {
373     int     i_index;                                          /* generic index */    
374     int     i_pixel_size;     /* pixel size, in bytes, for translations tables */    
375
376     /* Update status */
377     *p_vout->pi_status = THREAD_START;    
378     
379     /* Initialize pictures */    
380     for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
381     {
382         p_vout->p_picture[i_index].i_type  = EMPTY_PICTURE;
383         p_vout->p_picture[i_index].i_status= FREE_PICTURE;
384     }
385
386     /* Initialize other properties */
387     p_vout->i_pictures = 0;
388 #ifdef STATS
389     p_vout->c_loops = 0;
390     p_vout->c_idle_loops = 0;
391     p_vout->c_pictures = 0;
392     p_vout->c_rendered_pictures = 0;
393 #endif
394
395     /* Initialize output method - width, height, screen depth and bytes per 
396      * pixel are initialized by this call. */
397     if( vout_SysInit( p_vout ) )                        /* error */
398     {
399         *p_vout->pi_status = THREAD_ERROR;        
400         return( 1 );
401     } 
402
403     /* Allocate translation tables */
404     switch( p_vout->i_bytes_per_pixel )
405     {
406     case 2:                   /* 15 or 16 bpp, use 16 bits translations tables */        
407         i_pixel_size = sizeof( u16 );        
408         break;                
409     case 3:                   /* 24 or 32 bpp, use 32 bits translations tables */        
410     case 4:
411         i_pixel_size = sizeof( u32 );
412         break;        
413     }
414     p_vout->pi_trans16_red = p_vout->pi_trans32_red =     malloc( 1024 * i_pixel_size );
415     p_vout->pi_trans16_green = p_vout->pi_trans32_green = malloc( 1024 * i_pixel_size );
416     p_vout->pi_trans16_blue = p_vout->pi_trans32_blue =   malloc( 1024 * i_pixel_size );
417     if( (p_vout->pi_trans16_red == NULL) || 
418         (p_vout->pi_trans16_green == NULL ) ||
419         (p_vout->pi_trans16_blue == NULL ) )
420     {
421         if( p_vout->pi_trans16_red != NULL )
422         {
423             free( p_vout->pi_trans16_red );
424         }
425         if( p_vout->pi_trans16_green != NULL )
426         {
427             free( p_vout->pi_trans16_green );
428         }
429         if( p_vout->pi_trans16_blue != NULL )
430         {
431             free( p_vout->pi_trans16_blue );
432         }
433         intf_ErrMsg("vout error: %s\n", strerror(ENOMEM) );
434         *p_vout->pi_status = THREAD_ERROR;        
435         return( 1 );
436     }        
437     
438     /* Translate translation tables */
439     p_vout->pi_trans16_red      += 384;
440     p_vout->pi_trans16_green    += 384;
441     p_vout->pi_trans16_blue     += 384;
442     p_vout->pi_trans32_red      += 384;
443     p_vout->pi_trans32_green    += 384;
444     p_vout->pi_trans32_blue     += 384;
445
446     /* Build translation tables */
447     switch( p_vout->i_screen_depth )
448     {
449     case 15:
450         for( i_index = -384; i_index < 640; i_index++) 
451         {
452             p_vout->pi_trans16_red[i_index]     = (CLIP_BYTE( i_index ) & 0xf8)<<7;
453             p_vout->pi_trans16_green[i_index]   = (CLIP_BYTE( i_index ) & 0xf8)<<2;
454             p_vout->pi_trans16_blue[i_index]    =  CLIP_BYTE( i_index ) >> 3;
455         }
456         break;        
457     case 16:
458         for( i_index = -384; i_index < 640; i_index++) 
459         {
460             p_vout->pi_trans16_red[i_index]     = (CLIP_BYTE( i_index ) & 0xf8)<<8;
461             p_vout->pi_trans16_green[i_index]   = (CLIP_BYTE( i_index ) & 0xf8)<<3;
462             p_vout->pi_trans16_blue[i_index]    =  CLIP_BYTE( i_index ) >> 3;
463         }
464     case 24:
465     case 32:        
466         for( i_index = -384; i_index < 640; i_index++) 
467         {
468             p_vout->pi_trans32_red[i_index]     =  CLIP_BYTE( i_index ) <<16;
469             p_vout->pi_trans32_green[i_index]   =  CLIP_BYTE( i_index ) <<8;
470             p_vout->pi_trans32_blue[i_index]    =  CLIP_BYTE( i_index ) ;
471         }
472         break;        
473     }
474     
475     //????
476
477     /* Mark thread as running and return */
478     *p_vout->pi_status = THREAD_READY;    
479     intf_DbgMsg("%p -> succeeded\n", p_vout);    
480     return(0);    
481 }
482
483 /*******************************************************************************
484  * RunThread: video output thread
485  *******************************************************************************
486  * Video output thread. This function does only returns when the thread is
487  * terminated. It handles the pictures arriving in the video heap and the
488  * display device events.
489  *******************************************************************************/
490 static void RunThread( vout_thread_t *p_vout)
491 {
492     int             i_picture;                                /* picture index */
493     int             i_err;                                       /* error code */
494     mtime_t         current_date;                              /* current date */
495     picture_t *     p_pic = NULL;
496 #ifdef VOUT_DEBUG
497     char            sz_date[MSTRTIME_MAX_SIZE];                 /* date buffer */
498 #endif
499
500     /* 
501      * Initialize thread and free configuration 
502      */
503     intf_DbgMsg( "0x%x begin\n", p_vout );
504     p_vout->b_error = InitThread( p_vout );
505     if( p_vout->b_error )
506     {
507         free( p_vout );                                  /* destroy descriptor */
508         return;        
509     }    
510
511     /*
512      * Main loop - it is not executed if an error occured during
513      * initialization
514      */
515     while( (!p_vout->b_die) && (!p_vout->b_error) )
516     {
517         /* 
518          * Find the picture to display - this is the only operation requiring
519          * the lock on the picture, since once a READY_PICTURE has been found,
520          * it can't be modified by the other threads (except if it is unliked,
521          * but its data remains)
522          */
523         vlc_mutex_lock( &p_vout->lock );
524
525         for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
526         {
527             if( (p_vout->p_picture[i_picture].i_status == READY_PICTURE) &&
528                 ( (p_pic == NULL) || 
529                   (p_vout->p_picture[i_picture].date < p_pic->date) ) )
530             {
531                 p_pic = &p_vout->p_picture[i_picture];
532             }
533         }
534
535         vlc_mutex_unlock( &p_vout->lock );
536
537         /* 
538          * Render picture if any
539          */
540         if( p_pic )
541         {
542             current_date = mdate();
543             if( p_pic->date < current_date )
544             {
545                 /* Picture is late: it will be destroyed and the thread will go
546                  * immediately to next picture */
547                 vlc_mutex_lock( &p_vout->lock );
548                 if( p_pic->i_refcount )
549                 {
550                     p_pic->i_status = DISPLAYED_PICTURE;
551                 }
552                 else
553                 {
554                     p_pic->i_status = DESTROYED_PICTURE;
555                 }
556                 vlc_mutex_unlock( &p_vout->lock );
557                 intf_ErrMsg( "vout error: picture %p was late - skipped\n", p_pic );
558                 p_pic = NULL;
559             }
560             else if( p_pic->date > current_date + VOUT_DISPLAY_DELAY )
561             {
562                 /* A picture is ready to be rendered, but its rendering date is
563                  * far from the current one so the thread will perform an empty loop
564                  * as if no picture were found. The picture state is unchanged */
565                 p_pic = NULL;
566             }
567             else
568             {
569                 /* Picture has not yet been displayed, and has a valid display
570                  * date : render it */
571                 RenderPicture( p_vout, p_pic );
572             }
573         }
574     
575         /*
576          * Check events, sleep and display picture
577          */
578         i_err = vout_SysManage( p_vout );
579         if( i_err < 0 )
580         {
581             /* A fatal error occured, and the thread must terminate immediately,
582              * without displaying anything - setting b_error to 1 cause the
583              * immediate end of the main while() loop. */
584             p_vout->b_error = 1;
585         }
586         else 
587         {
588             if( p_pic )
589             {
590                 /* A picture is ready to be displayed : sleep until its display date */
591                 mwait( p_pic->date );
592
593                 if( !i_err )
594                 {
595                     vout_SysDisplay( p_vout );
596                 }
597
598                 /* Picture has been displayed : destroy it */
599                 vlc_mutex_lock( &p_vout->lock );
600                 if( p_pic->i_refcount )
601                 {
602                     p_pic->i_status = DISPLAYED_PICTURE;
603                 }
604                 else
605                 {
606                     p_pic->i_status = DESTROYED_PICTURE;
607                 }
608                 vlc_mutex_unlock( &p_vout->lock );
609             }
610             else
611             {
612                 /* Sleep to wait for new pictures */
613                 msleep( VOUT_IDLE_SLEEP );
614 #ifdef STATS
615                 /* Update counters */
616                 p_vout->c_idle_loops++;
617 #endif
618             }
619         }
620
621 #ifdef STATS
622         /* Update counters */
623         p_vout->c_loops++;
624 #endif
625     } 
626
627     /*
628      * Error loop
629      */
630     if( p_vout->b_error )
631     {
632         ErrorThread( p_vout );        
633     }
634
635     /* End of thread */
636     EndThread( p_vout );
637     intf_DbgMsg( "0x%x end\n", p_vout );
638 }
639
640 /*******************************************************************************
641  * ErrorThread: RunThread() error loop
642  *******************************************************************************
643  * This function is called when an error occured during thread main's loop. The
644  * thread can still receive feed, but must be ready to terminate as soon as
645  * possible.
646  *******************************************************************************/
647 static void ErrorThread( vout_thread_t *p_vout )
648 {
649     /* Wait until a `die' order */
650     while( !p_vout->b_die )
651     {
652         /* Sleep a while */
653         msleep( VOUT_IDLE_SLEEP );                
654     }
655 }
656
657 /*******************************************************************************
658  * EndThread: thread destruction
659  *******************************************************************************
660  * This function is called when the thread ends after a sucessfull 
661  * initialization.
662  *******************************************************************************/
663 static void EndThread( vout_thread_t *p_vout )
664 {
665     int *   pi_status;                                        /* thread status */
666     int     i_picture;
667         
668     /* Store status */
669     pi_status = p_vout->pi_status;    
670     *pi_status = THREAD_END;    
671
672     /* Destroy all remaining pictures */
673     for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
674     {
675         if( p_vout->p_picture[i_picture].i_status != FREE_PICTURE )
676         {
677             free( p_vout->p_picture[i_picture].p_data );
678         }
679     }
680
681     /* Destroy translation tables - remeber these tables are translated */    
682     free( p_vout->pi_trans16_red - 384 );
683     free( p_vout->pi_trans16_green - 384 );
684     free( p_vout->pi_trans16_blue - 384 );
685     
686     /* Destroy thread structures allocated by InitThread */
687     vout_SysEnd( p_vout );
688     vout_SysDestroy( p_vout );            /* destroy output method */
689     free( p_vout );
690
691     /* Update status */
692     *pi_status = THREAD_OVER;    
693     intf_DbgMsg("%p\n", p_vout);
694 }
695
696 /*******************************************************************************
697  * RenderPicture: render a picture
698  *******************************************************************************
699  * This function convert a picture from a video heap to a pixel-encoded image
700  * and copy it to the current rendering buffer. No lock is required, since the
701  * rendered picture has been determined as existant, and will only be destroyed
702  * by the vout thread later.
703  *******************************************************************************/
704 static void RenderPicture( vout_thread_t *p_vout, picture_t *p_pic )
705 {
706     intf_DbgMsg("0x%x Picture 0x%x type=%d, %dx%d\n", 
707                 p_vout, p_pic, p_pic->i_type, p_pic->i_width, p_pic->i_height );
708     
709     /*???*/
710 }
711