]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
* Fixed aspect ratio handling.
[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.147 2001/12/13 12:47:17 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 )
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_type   = EMPTY_PICTURE;
146     }
147
148     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
149     {
150         p_vout->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
151         p_vout->p_subpicture[i_index].i_type   = EMPTY_SUBPICTURE;
152     }
153
154     /* Initialize the rendering heap */
155     p_vout->i_heap_size = 0;
156
157     I_RENDERPICTURES = 0;
158     p_vout->render.i_width    = i_width;
159     p_vout->render.i_height   = i_height;
160     p_vout->render.i_chroma   = i_chroma;
161     p_vout->render.i_aspect   = i_aspect;
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     int i_index;
246
247     /* Update status */
248     *p_vout->pi_status = THREAD_START;
249
250     vlc_mutex_lock( &p_vout->change_lock );
251
252 #ifdef STATS
253     p_vout->c_loops = 0;
254 #endif
255
256     /* Initialize output method, it issues its own error messages */
257     if( p_vout->pf_init( p_vout ) )
258     {
259         vlc_mutex_unlock( &p_vout->change_lock );
260         return( 1 );
261     }
262
263     if( !I_OUTPUTPICTURES )
264     {
265         intf_ErrMsg( "vout error: plugin was unable to allocate at least "
266                      "one direct buffer" );
267         vlc_mutex_unlock( &p_vout->change_lock );
268         return( 1 );
269     }
270
271     /* Check whether we managed to create direct buffers similar to
272      * the render buffers, ie same size, chroma and aspect ratio */
273     if( ( p_vout->output.i_width == p_vout->render.i_width )
274      && ( p_vout->output.i_height == p_vout->render.i_height )
275      && ( p_vout->output.i_chroma == p_vout->render.i_chroma )
276      && ( p_vout->output.i_aspect == p_vout->render.i_aspect ) )
277     {
278         p_vout->b_direct = 1;
279
280         /* Map the first render buffers to the first direct buffers, but
281          * leave the first direct buffer for memcpy operations */
282         i_index = 1;
283     }
284     else
285     {
286         p_vout->b_direct = 0;
287
288         /* Append render buffers after the direct buffers */
289         i_index = I_RENDERPICTURES;
290     }
291
292     for( ; i_index < VOUT_MAX_PICTURES; i_index++ )
293     {
294         PP_RENDERPICTURE[ I_RENDERPICTURES ]
295             = &p_vout->p_picture[ i_index ];
296         I_RENDERPICTURES++;
297     }
298
299     intf_WarnMsg( 1, "vout info: got %i direct buffer(s)", I_OUTPUTPICTURES );
300
301     /* Mark thread as running and return */
302     p_vout->b_active = 1;
303     *p_vout->pi_status = THREAD_READY;
304
305     intf_DbgMsg("thread ready");
306     return( 0 );
307 }
308
309 /*****************************************************************************
310  * RunThread: video output thread
311  *****************************************************************************
312  * Video output thread. This function does only returns when the thread is
313  * terminated. It handles the pictures arriving in the video heap and the
314  * display device events.
315  *****************************************************************************/
316 static void RunThread( vout_thread_t *p_vout)
317 {
318     int             i_index;                                /* index in heap */
319     mtime_t         current_date;                            /* current date */
320     mtime_t         display_date;                            /* display date */
321
322     picture_t *     p_picture;                            /* picture pointer */
323     picture_t *     p_directbuffer;              /* direct buffer to display */
324
325     subpicture_t *  p_subpic;                          /* subpicture pointer */
326
327     /* Create and initialize system-dependant method - this function issues its
328      * own error messages */
329     if( p_vout->pf_create( p_vout ) )
330     {
331         DestroyThread( p_vout, THREAD_ERROR );
332         return;
333     }
334
335     /*
336      * Initialize thread
337      */
338     p_vout->b_error = InitThread( p_vout );
339     if( p_vout->b_error )
340     {
341         /* Destroy thread structures allocated by Create and InitThread */
342         p_vout->pf_destroy( p_vout );
343
344         DestroyThread( p_vout, THREAD_ERROR );
345         return;
346     }
347
348     /*
349      * Main loop - it is not executed if an error occured during
350      * initialization
351      */
352     while( (!p_vout->b_die) && (!p_vout->b_error) )
353     {
354         /* Initialize loop variables */
355         display_date = 0;
356         current_date = mdate();
357
358 #ifdef STATS
359         p_vout->c_loops++;
360         if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
361         {
362             intf_Msg( "vout stats: picture heap: %d/%d",
363                       I_RENDERPICTURES, p_vout->i_heap_size );
364         }
365 #endif
366
367         /*
368          * Find the picture to display - this operation does not need lock,
369          * since only READY_PICTUREs are handled
370          */
371         p_picture = NULL;
372
373         for( i_index = 0;
374              i_index < I_RENDERPICTURES;
375              i_index++ )
376         {
377             if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
378                 && ( (p_picture == NULL) ||
379                      (PP_RENDERPICTURE[i_index]->date < display_date) ) )
380             {
381                 p_picture = PP_RENDERPICTURE[i_index];
382                 display_date = p_picture->date;
383             }
384         }
385
386         if( p_picture != NULL )
387         {
388             /* Compute FPS rate */
389             p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
390                 = display_date;
391
392             if( display_date < current_date + p_vout->render_time )
393             {
394                 /* Picture is late: it will be destroyed and the thread
395                  * will directly choose the next picture */
396                 vlc_mutex_lock( &p_vout->picture_lock );
397                 if( p_picture->i_refcount )
398                 {
399                     /* Pretend we displayed the picture, but don't destroy
400                      * it since the decoder might still need it. */
401                     p_picture->i_status = DISPLAYED_PICTURE;
402                 }
403                 else
404                 {
405                     /* Destroy the picture without displaying it */
406                     p_picture->i_status = DESTROYED_PICTURE;
407                     p_vout->i_heap_size--;
408                 }
409                 intf_WarnMsg( 1, "vout warning: late picture skipped (%p)",
410                               p_picture );
411                 vlc_mutex_unlock( &p_vout->picture_lock );
412
413                 continue;
414             }
415             else if( display_date > current_date + VOUT_DISPLAY_DELAY )
416             {
417                 /* A picture is ready to be rendered, but its rendering date
418                  * is far from the current one so the thread will perform an
419                  * empty loop as if no picture were found. The picture state
420                  * is unchanged */
421                 p_picture    = NULL;
422                 display_date = 0;
423             }
424         }
425
426         /*
427          * Check for subpictures to display
428          */
429         p_subpic = vout_SortSubPictures( p_vout, display_date );
430
431         /*
432          * Perform rendering
433          */
434         p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );
435
436         /*
437          * Sleep, wake up
438          */
439         if( display_date != 0 )
440         {
441             /* Store render time using Bresenham algorithm */
442             p_vout->render_time += mdate() - current_date;
443             p_vout->render_time >>= 1;
444         }
445
446         /* Give back change lock */
447         vlc_mutex_unlock( &p_vout->change_lock );
448
449         /* Sleep a while or until a given date */
450         if( display_date != 0 )
451         {
452             mwait( display_date - VOUT_MWAIT_TOLERANCE );
453         }
454         else
455         {
456             msleep( VOUT_IDLE_SLEEP );
457         }
458
459         /* On awakening, take back lock and send immediately picture
460          * to display. */
461         vlc_mutex_lock( &p_vout->change_lock );
462
463 #ifdef TRACE_VOUT
464         intf_DbgMsg( "picture %p, subpicture %p", p_picture, p_subpic );
465 #endif
466
467         /*
468          * Display the previously rendered picture
469          */
470         if( p_picture != NULL )
471         {
472             /* Display the direct buffer returned by vout_RenderPicture */
473             p_vout->pf_display( p_vout, p_directbuffer );
474
475             /* Remove picture from heap */
476             vlc_mutex_lock( &p_vout->picture_lock );
477             if( p_picture->i_refcount )
478             {
479                 p_picture->i_status = DISPLAYED_PICTURE;
480             }
481             else
482             {
483                 p_picture->i_status = DESTROYED_PICTURE;
484                 p_vout->i_heap_size--;
485             }
486             vlc_mutex_unlock( &p_vout->picture_lock );
487         }
488
489         /*
490          * Check events and manage thread
491          */
492         if( p_vout->pf_manage( p_vout ) )
493         {
494             /* A fatal error occured, and the thread must terminate immediately,
495              * without displaying anything - setting b_error to 1 causes the
496              * immediate end of the main while() loop. */
497             p_vout->b_error = 1;
498         }
499     }
500
501     /*
502      * Error loop - wait until the thread destruction is requested
503      */
504     if( p_vout->b_error )
505     {
506         ErrorThread( p_vout );
507     }
508
509     /* End of thread */
510     EndThread( p_vout );
511
512     /* Destroy method-dependant resources */
513     p_vout->pf_destroy( p_vout );
514
515     /* Destroy thread structures allocated by CreateThread */
516     DestroyThread( p_vout, THREAD_OVER );
517     intf_DbgMsg( "thread end" );
518 }
519
520 /*****************************************************************************
521  * ErrorThread: RunThread() error loop
522  *****************************************************************************
523  * This function is called when an error occured during thread main's loop. The
524  * thread can still receive feed, but must be ready to terminate as soon as
525  * possible.
526  *****************************************************************************/
527 static void ErrorThread( vout_thread_t *p_vout )
528 {
529     /* Wait until a `die' order */
530     while( !p_vout->b_die )
531     {
532         /* Sleep a while */
533         msleep( VOUT_IDLE_SLEEP );
534     }
535 }
536
537 /*****************************************************************************
538  * EndThread: thread destruction
539  *****************************************************************************
540  * This function is called when the thread ends after a sucessful
541  * initialization. It frees all ressources allocated by InitThread.
542  *****************************************************************************/
543 static void EndThread( vout_thread_t *p_vout )
544 {
545     int     i_index;                                        /* index in heap */
546
547     /* Store status */
548     *p_vout->pi_status = THREAD_END;
549
550 #ifdef STATS
551     {
552         struct tms cpu_usage;
553         times( &cpu_usage );
554
555         intf_Msg( "vout stats: cpu usage (user: %d, system: %d)",
556                   cpu_usage.tms_utime, cpu_usage.tms_stime );
557     }
558 #endif
559
560     /* Destroy all remaining pictures */
561     for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
562     {
563         if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
564         {
565             free( p_vout->p_picture[i_index].planes[0].p_data );
566         }
567     }
568
569     /* Destroy all remaining subpictures */
570     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
571     {
572         if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
573         {
574             free( p_vout->p_subpicture[i_index].p_data );
575         }
576     }
577
578     /* Destroy translation tables */
579     p_vout->pf_end( p_vout );
580
581     /* Release the change lock */
582     vlc_mutex_unlock( &p_vout->change_lock );
583 }
584
585 /*****************************************************************************
586  * DestroyThread: thread destruction
587  *****************************************************************************
588  * This function is called when the thread ends. It frees all ressources
589  * allocated by CreateThread. Status is available at this stage.
590  *****************************************************************************/
591 static void DestroyThread( vout_thread_t *p_vout, int i_status )
592 {
593     int *pi_status;                                         /* status adress */
594
595     /* Store status adress */
596     pi_status = p_vout->pi_status;
597
598     /* Destroy the locks */
599     vlc_mutex_destroy( &p_vout->picture_lock );
600     vlc_mutex_destroy( &p_vout->subpicture_lock );
601     vlc_mutex_destroy( &p_vout->change_lock );
602
603     /* Release the module */
604     module_Unneed( p_vout->p_module );
605
606     /* Free structure */
607     free( p_vout );
608     *pi_status = i_status;
609 }
610