]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
* COMPLETE CVS BREAKAGE !! The MAIN branch is going to be a playground
[vlc] / src / video_output / video_output.c
1 /*****************************************************************************
2  * video_output.c : video output thread
3  * This module describes the programming interface for video output threads.
4  * It includes functions allowing to open a new thread, send pictures to a
5  * thread, and destroy a previously oppened video output thread.
6  *****************************************************************************
7  * Copyright (C) 2000-2001 VideoLAN
8  * $Id: video_output.c,v 1.146 2001/12/09 17:01:37 sam Exp $
9  *
10  * Authors: Vincent Seguin <seguin@via.ecp.fr>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include "defs.h"
31
32 #include <errno.h>                                                 /* ENOMEM */
33 #include <stdlib.h>                                                /* free() */
34 #include <stdio.h>                                              /* sprintf() */
35 #include <string.h>                                            /* strerror() */
36
37 #ifdef HAVE_SYS_TIMES_H
38 #   include <sys/times.h>
39 #endif
40
41 #include "common.h"
42 #include "intf_msg.h"
43 #include "threads.h"
44 #include "mtime.h"
45 #include "modules.h"
46
47 #include "video.h"
48 #include "video_output.h"
49
50 /*****************************************************************************
51  * Local prototypes
52  *****************************************************************************/
53 static int      InitThread        ( vout_thread_t *p_vout );
54 static void     RunThread         ( vout_thread_t *p_vout );
55 static void     ErrorThread       ( vout_thread_t *p_vout );
56 static void     EndThread         ( vout_thread_t *p_vout );
57 static void     DestroyThread     ( vout_thread_t *p_vout, int i_status );
58
59 /*****************************************************************************
60  * vout_InitBank: initialize the video output bank.
61  *****************************************************************************/
62 void vout_InitBank ( void )
63 {
64     p_vout_bank->i_count = 0;
65
66     vlc_mutex_init( &p_vout_bank->lock );
67 }
68
69 /*****************************************************************************
70  * vout_EndBank: empty the video output bank.
71  *****************************************************************************
72  * This function ends all unused video outputs and empties the bank in
73  * case of success.
74  *****************************************************************************/
75 void vout_EndBank ( void )
76 {
77     /* Ask all remaining video outputs to die */
78     while( p_vout_bank->i_count )
79     {
80         vout_DestroyThread(
81                 p_vout_bank->pp_vout[ --p_vout_bank->i_count ], NULL );
82     }
83
84     vlc_mutex_destroy( &p_vout_bank->lock );
85 }
86
87 /*****************************************************************************
88  * vout_CreateThread: creates a new video output thread
89  *****************************************************************************
90  * This function creates a new video output thread, and returns a pointer
91  * to its description. On error, it returns NULL.
92  * If pi_status is NULL, then the function will block until the thread is ready.
93  * If not, it will be updated using one of the THREAD_* constants.
94  *****************************************************************************/
95 vout_thread_t * vout_CreateThread   ( int *pi_status,
96                                       int i_width, int i_height,
97                                       int i_chroma, int i_aspect_ratio )
98 {
99     vout_thread_t * p_vout;                             /* thread descriptor */
100     int             i_status;                               /* thread status */
101     int             i_index;                                /* loop variable */
102
103     /* Allocate descriptor */
104     p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
105     if( p_vout == NULL )
106     {
107         intf_ErrMsg( "vout error: vout thread creation returned %s",
108                      strerror(ENOMEM) );
109         return( NULL );
110     }
111
112     /* Choose the best module */
113     p_vout->p_module = module_Need( MODULE_CAPABILITY_VOUT, NULL );
114
115     if( p_vout->p_module == NULL )
116     {
117         intf_ErrMsg( "vout error: no suitable vout module" );
118         free( p_vout );
119         return( NULL );
120     }
121
122 #define f p_vout->p_module->p_functions->vout.functions.vout
123     p_vout->pf_create     = f.pf_create;
124     p_vout->pf_init       = f.pf_init;
125     p_vout->pf_end        = f.pf_end;
126     p_vout->pf_destroy    = f.pf_destroy;
127     p_vout->pf_manage     = f.pf_manage;
128     p_vout->pf_display    = f.pf_display;
129     p_vout->pf_setpalette = f.pf_setpalette;
130 #undef f
131
132     /* Initialize thread properties - thread id and locks will be initialized
133      * later */
134     p_vout->b_die               = 0;
135     p_vout->b_error             = 0;
136     p_vout->b_active            = 0;
137     p_vout->pi_status           = (pi_status != NULL) ? pi_status : &i_status;
138     *p_vout->pi_status          = THREAD_CREATE;
139
140     /* Initialize pictures and subpictures - translation tables and functions
141      * will be initialized later in InitThread */
142     for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
143     {
144         p_vout->p_picture[i_index].i_status = FREE_PICTURE;
145         p_vout->p_picture[i_index].i_chroma = EMPTY_PICTURE;
146         p_vout->p_picture[i_index].i_aspect_ratio = i_aspect_ratio;
147         p_vout->p_picture[i_index].b_directbuffer = 0;
148     }
149
150     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
151     {
152         p_vout->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
153         p_vout->p_subpicture[i_index].i_type   = EMPTY_SUBPICTURE;
154     }
155
156     p_vout->i_width         = i_width;
157     p_vout->i_height        = i_height;
158     p_vout->i_chroma        = i_chroma;
159     p_vout->i_aspect_ratio  = i_aspect_ratio;
160     p_vout->i_pictures = 0;
161     p_vout->i_directbuffers = 0;
162
163     /* Initialize misc stuff */
164     p_vout->i_changes = 0;
165     p_vout->f_gamma = 0;
166     p_vout->b_grayscale = 0;
167     p_vout->b_info = 0;
168     p_vout->b_interface = 0;
169     p_vout->b_scale = 1;
170     p_vout->b_fullscreen = main_GetIntVariable( VOUT_FULLSCREEN_VAR,
171                                                 VOUT_FULLSCREEN_DEFAULT );
172     p_vout->render_time = 10;
173
174     /* Create thread and set locks */
175     vlc_mutex_init( &p_vout->picture_lock );
176     vlc_mutex_init( &p_vout->subpicture_lock );
177     vlc_mutex_init( &p_vout->change_lock );
178
179     if( vlc_thread_create( &p_vout->thread_id, "video output",
180                            (void *) RunThread, (void *) p_vout) )
181     {
182         intf_ErrMsg("vout error: %s", strerror(ENOMEM));
183         p_vout->pf_destroy( p_vout );
184         module_Unneed( p_vout->p_module );
185         free( p_vout );
186         return( NULL );
187     }
188
189     /* If status is NULL, wait until the thread is created */
190     if( pi_status == NULL )
191     {
192         do
193         {
194             msleep( THREAD_SLEEP );
195         }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR)
196                 && (i_status != THREAD_FATAL) );
197         if( i_status != THREAD_READY )
198         {
199             return( NULL );
200         }
201     }
202
203     return( p_vout );
204 }
205
206 /*****************************************************************************
207  * vout_DestroyThread: destroys a previously created thread
208  *****************************************************************************
209  * Destroy a terminated thread.
210  * The function will request a destruction of the specified thread. If pi_error
211  * is NULL, it will return once the thread is destroyed. Else, it will be
212  * update using one of the THREAD_* constants.
213  *****************************************************************************/
214 void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
215 {
216     int     i_status;                                       /* thread status */
217
218     /* Set status */
219     p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
220     *p_vout->pi_status = THREAD_DESTROY;
221
222     /* Request thread destruction */
223     p_vout->b_die = 1;
224
225     /* If status is NULL, wait until thread has been destroyed */
226     if( pi_status == NULL )
227     {
228         do
229         {
230             msleep( THREAD_SLEEP );
231         } while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
232                  && (i_status != THREAD_FATAL) );
233     }
234 }
235
236 /*****************************************************************************
237  * InitThread: initialize video output thread
238  *****************************************************************************
239  * This function is called from RunThread and performs the second step of the
240  * initialization. It returns 0 on success. Note that the thread's flag are not
241  * modified inside this function.
242  *****************************************************************************/
243 static int InitThread( vout_thread_t *p_vout )
244 {
245     /* Update status */
246     *p_vout->pi_status = THREAD_START;
247
248     vlc_mutex_lock( &p_vout->change_lock );
249
250 #ifdef STATS
251     p_vout->c_loops = 0;
252 #endif
253
254     /* Initialize output method, it issues its own error messages */
255     if( p_vout->pf_init( p_vout ) )
256     {
257         vlc_mutex_unlock( &p_vout->change_lock );
258         return( 1 );
259     }
260
261     if( !p_vout->i_directbuffers )
262     {
263         intf_ErrMsg( "vout error: plugin was unable to allocate at least "
264                      "one direct buffer" );
265         vlc_mutex_unlock( &p_vout->change_lock );
266         return( 1 );
267     }
268
269     intf_WarnMsg( 1, "vout info: got %i direct buffer(s)",
270                   p_vout->i_directbuffers );
271
272     /* Mark thread as running and return */
273     p_vout->b_active =          1;
274     *p_vout->pi_status =        THREAD_READY;
275
276     intf_DbgMsg("thread ready");
277     return( 0 );
278 }
279
280 /*****************************************************************************
281  * RunThread: video output thread
282  *****************************************************************************
283  * Video output thread. This function does only returns when the thread is
284  * terminated. It handles the pictures arriving in the video heap and the
285  * display device events.
286  *****************************************************************************/
287 static void RunThread( vout_thread_t *p_vout)
288 {
289     int             i_index;                                /* index in heap */
290     mtime_t         current_date;                            /* current date */
291     mtime_t         display_date;                            /* display date */
292
293     picture_t *     p_picture;                            /* picture pointer */
294     picture_t *     p_directbuffer;              /* direct buffer to display */
295
296     subpicture_t *  p_subpic;                          /* subpicture pointer */
297
298     /* Create and initialize system-dependant method - this function issues its
299      * own error messages */
300     if( p_vout->pf_create( p_vout ) )
301     {
302         DestroyThread( p_vout, THREAD_ERROR );
303         return;
304     }
305
306     /*
307      * Initialize thread
308      */
309     p_vout->b_error = InitThread( p_vout );
310     if( p_vout->b_error )
311     {
312         /* Destroy thread structures allocated by Create and InitThread */
313         p_vout->pf_destroy( p_vout );
314
315         DestroyThread( p_vout, THREAD_ERROR );
316         return;
317     }
318
319     /*
320      * Main loop - it is not executed if an error occured during
321      * initialization
322      */
323     while( (!p_vout->b_die) && (!p_vout->b_error) )
324     {
325         /* Initialize loop variables */
326         p_picture =      NULL;
327         p_directbuffer = NULL;
328         display_date =   0;
329         current_date =   mdate();
330
331 #ifdef STATS
332         p_vout->c_loops++;
333         if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
334         {
335             intf_Msg( "vout stats: picture heap: %d/%d",
336                       p_vout->i_pictures, VOUT_MAX_PICTURES );
337         }
338 #endif
339
340         /*
341          * Find the picture to display - this operation does not need lock,
342          * since only READY_PICTUREs are handled
343          */
344         for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
345         {
346             if( (p_vout->p_picture[i_index].i_status == READY_PICTURE) &&
347             ( (p_picture == NULL) ||
348               (p_vout->p_picture[i_index].date < display_date) ) )
349             {
350                 p_picture = &p_vout->p_picture[i_index];
351                 display_date = p_picture->date;
352             }
353         }
354
355         if( p_picture != NULL )
356         {
357             /* Compute FPS rate */
358             p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
359                 = display_date;
360
361             if( display_date < current_date + p_vout->render_time )
362             {
363                 /* Picture is late: it will be destroyed and the thread
364                  * will directly choose the next picture */
365                 vlc_mutex_lock( &p_vout->picture_lock );
366                 if( p_picture->i_refcount )
367                 {
368                     /* Pretend we displayed the picture, but don't destroy
369                      * it since the decoder might still need it. */
370                     p_picture->i_status = DISPLAYED_PICTURE;
371                 }
372                 else
373                 {
374                     /* Destroy the picture without displaying it */
375                     p_picture->i_status = DESTROYED_PICTURE;
376                     p_vout->i_pictures--;
377                 }
378                 intf_WarnMsg( 1, "vout warning: late picture skipped (%p)",
379                               p_picture );
380                 vlc_mutex_unlock( &p_vout->picture_lock );
381
382                 continue;
383             }
384             else if( display_date > current_date + VOUT_DISPLAY_DELAY )
385             {
386                 /* A picture is ready to be rendered, but its rendering date
387                  * is far from the current one so the thread will perform an
388                  * empty loop as if no picture were found. The picture state
389                  * is unchanged */
390                 p_picture    = NULL;
391                 display_date = 0;
392             }
393         }
394
395         /*
396          * Check for subpictures to display
397          */
398         p_subpic = vout_SortSubPictures( p_vout, display_date );
399
400         /*
401          * Perform rendering
402          */
403         p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );
404
405         /*
406          * Sleep, wake up
407          */
408         if( display_date != 0 )
409         {
410             /* Store render time using Bresenham algorithm */
411             p_vout->render_time += mdate() - current_date;
412             p_vout->render_time >>= 1;
413         }
414
415         /* Give back change lock */
416         vlc_mutex_unlock( &p_vout->change_lock );
417
418         /* Sleep a while or until a given date */
419         if( display_date != 0 )
420         {
421             mwait( display_date - VOUT_MWAIT_TOLERANCE );
422         }
423         else
424         {
425             msleep( VOUT_IDLE_SLEEP );
426         }
427
428         /* On awakening, take back lock and send immediately picture
429          * to display. */
430         vlc_mutex_lock( &p_vout->change_lock );
431
432 #ifdef TRACE_VOUT
433         intf_DbgMsg( "picture %p, subpicture %p", p_picture, p_subpic );
434 #endif
435
436         /*
437          * Display the previously rendered picture
438          */
439         if( p_picture != NULL )
440         {
441             /* Display the direct buffer returned by vout_RenderPicture */
442             p_vout->pf_display( p_vout, p_directbuffer );
443
444             /* Remove picture from heap */
445             vlc_mutex_lock( &p_vout->picture_lock );
446             if( p_picture->i_refcount )
447             {
448                 p_picture->i_status = DISPLAYED_PICTURE;
449             }
450             else
451             {
452                 p_picture->i_status = DESTROYED_PICTURE;
453                 p_vout->i_pictures--;
454             }
455             vlc_mutex_unlock( &p_vout->picture_lock );
456         }
457
458         /*
459          * Check events and manage thread
460          */
461         if( p_vout->pf_manage( p_vout ) )
462         {
463             /* A fatal error occured, and the thread must terminate immediately,
464              * without displaying anything - setting b_error to 1 causes the
465              * immediate end of the main while() loop. */
466             p_vout->b_error = 1;
467         }
468     }
469
470     /*
471      * Error loop - wait until the thread destruction is requested
472      */
473     if( p_vout->b_error )
474     {
475         ErrorThread( p_vout );
476     }
477
478     /* End of thread */
479     EndThread( p_vout );
480
481     /* Destroy method-dependant resources */
482     p_vout->pf_destroy( p_vout );
483
484     /* Destroy thread structures allocated by CreateThread */
485     DestroyThread( p_vout, THREAD_OVER );
486     intf_DbgMsg( "thread end" );
487 }
488
489 /*****************************************************************************
490  * ErrorThread: RunThread() error loop
491  *****************************************************************************
492  * This function is called when an error occured during thread main's loop. The
493  * thread can still receive feed, but must be ready to terminate as soon as
494  * possible.
495  *****************************************************************************/
496 static void ErrorThread( vout_thread_t *p_vout )
497 {
498     /* Wait until a `die' order */
499     while( !p_vout->b_die )
500     {
501         /* Sleep a while */
502         msleep( VOUT_IDLE_SLEEP );
503     }
504 }
505
506 /*****************************************************************************
507  * EndThread: thread destruction
508  *****************************************************************************
509  * This function is called when the thread ends after a sucessful
510  * initialization. It frees all ressources allocated by InitThread.
511  *****************************************************************************/
512 static void EndThread( vout_thread_t *p_vout )
513 {
514     int     i_index;                                        /* index in heap */
515
516     /* Store status */
517     *p_vout->pi_status = THREAD_END;
518
519 #ifdef STATS
520     {
521         struct tms cpu_usage;
522         times( &cpu_usage );
523
524         intf_Msg( "vout stats: cpu usage (user: %d, system: %d)",
525                   cpu_usage.tms_utime, cpu_usage.tms_stime );
526     }
527 #endif
528
529     /* Destroy all remaining pictures */
530     for( i_index = p_vout->i_directbuffers;
531          i_index < VOUT_MAX_PICTURES;
532          i_index++ )
533     {
534         if( p_vout->p_picture[i_index].i_status != FREE_PICTURE )
535         {
536             free( p_vout->p_picture[i_index].planes[0].p_data );
537         }
538     }
539
540     /* Destroy all remaining subpictures */
541     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
542     {
543         if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
544         {
545             free( p_vout->p_subpicture[i_index].p_data );
546         }
547     }
548
549     /* Destroy translation tables */
550     p_vout->pf_end( p_vout );
551
552     /* Release the change lock */
553     vlc_mutex_unlock( &p_vout->change_lock );
554 }
555
556 /*****************************************************************************
557  * DestroyThread: thread destruction
558  *****************************************************************************
559  * This function is called when the thread ends. It frees all ressources
560  * allocated by CreateThread. Status is available at this stage.
561  *****************************************************************************/
562 static void DestroyThread( vout_thread_t *p_vout, int i_status )
563 {
564     int *pi_status;                                         /* status adress */
565
566     /* Store status adress */
567     pi_status = p_vout->pi_status;
568
569     /* Destroy the locks */
570     vlc_mutex_destroy( &p_vout->picture_lock );
571     vlc_mutex_destroy( &p_vout->subpicture_lock );
572     vlc_mutex_destroy( &p_vout->change_lock );
573
574     /* Release the module */
575     module_Unneed( p_vout->p_module );
576
577     /* Free structure */
578     free( p_vout );
579     *pi_status = i_status;
580 }
581