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