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 $
10 * Authors: Vincent Seguin <seguin@via.ecp.fr>
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.
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.
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 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
32 #include <errno.h> /* ENOMEM */
33 #include <stdlib.h> /* free() */
34 #include <stdio.h> /* sprintf() */
35 #include <string.h> /* strerror() */
37 #ifdef HAVE_SYS_TIMES_H
38 # include <sys/times.h>
48 #include "video_output.h"
50 /*****************************************************************************
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 );
59 /*****************************************************************************
60 * vout_InitBank: initialize the video output bank.
61 *****************************************************************************/
62 void vout_InitBank ( void )
64 p_vout_bank->i_count = 0;
66 vlc_mutex_init( &p_vout_bank->lock );
69 /*****************************************************************************
70 * vout_EndBank: empty the video output bank.
71 *****************************************************************************
72 * This function ends all unused video outputs and empties the bank in
74 *****************************************************************************/
75 void vout_EndBank ( void )
77 /* Ask all remaining video outputs to die */
78 while( p_vout_bank->i_count )
81 p_vout_bank->pp_vout[ --p_vout_bank->i_count ], NULL );
84 vlc_mutex_destroy( &p_vout_bank->lock );
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 )
99 vout_thread_t * p_vout; /* thread descriptor */
100 int i_status; /* thread status */
101 int i_index; /* loop variable */
103 /* Allocate descriptor */
104 p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
107 intf_ErrMsg( "vout error: vout thread creation returned %s",
112 /* Choose the best module */
113 p_vout->p_module = module_Need( MODULE_CAPABILITY_VOUT, NULL );
115 if( p_vout->p_module == NULL )
117 intf_ErrMsg( "vout error: no suitable vout module" );
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;
132 /* Initialize thread properties - thread id and locks will be initialized
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;
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++)
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;
150 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
152 p_vout->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
153 p_vout->p_subpicture[i_index].i_type = EMPTY_SUBPICTURE;
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;
163 /* Initialize misc stuff */
164 p_vout->i_changes = 0;
166 p_vout->b_grayscale = 0;
168 p_vout->b_interface = 0;
170 p_vout->b_fullscreen = main_GetIntVariable( VOUT_FULLSCREEN_VAR,
171 VOUT_FULLSCREEN_DEFAULT );
172 p_vout->render_time = 10;
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 );
179 if( vlc_thread_create( &p_vout->thread_id, "video output",
180 (void *) RunThread, (void *) p_vout) )
182 intf_ErrMsg("vout error: %s", strerror(ENOMEM));
183 p_vout->pf_destroy( p_vout );
184 module_Unneed( p_vout->p_module );
189 /* If status is NULL, wait until the thread is created */
190 if( pi_status == NULL )
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 )
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 )
216 int i_status; /* thread status */
219 p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
220 *p_vout->pi_status = THREAD_DESTROY;
222 /* Request thread destruction */
225 /* If status is NULL, wait until thread has been destroyed */
226 if( pi_status == NULL )
230 msleep( THREAD_SLEEP );
231 } while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
232 && (i_status != THREAD_FATAL) );
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 )
246 *p_vout->pi_status = THREAD_START;
248 vlc_mutex_lock( &p_vout->change_lock );
254 /* Initialize output method, it issues its own error messages */
255 if( p_vout->pf_init( p_vout ) )
257 vlc_mutex_unlock( &p_vout->change_lock );
261 if( !p_vout->i_directbuffers )
263 intf_ErrMsg( "vout error: plugin was unable to allocate at least "
264 "one direct buffer" );
265 vlc_mutex_unlock( &p_vout->change_lock );
269 intf_WarnMsg( 1, "vout info: got %i direct buffer(s)",
270 p_vout->i_directbuffers );
272 /* Mark thread as running and return */
273 p_vout->b_active = 1;
274 *p_vout->pi_status = THREAD_READY;
276 intf_DbgMsg("thread ready");
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)
289 int i_index; /* index in heap */
290 mtime_t current_date; /* current date */
291 mtime_t display_date; /* display date */
293 picture_t * p_picture; /* picture pointer */
294 picture_t * p_directbuffer; /* direct buffer to display */
296 subpicture_t * p_subpic; /* subpicture pointer */
298 /* Create and initialize system-dependant method - this function issues its
299 * own error messages */
300 if( p_vout->pf_create( p_vout ) )
302 DestroyThread( p_vout, THREAD_ERROR );
309 p_vout->b_error = InitThread( p_vout );
310 if( p_vout->b_error )
312 /* Destroy thread structures allocated by Create and InitThread */
313 p_vout->pf_destroy( p_vout );
315 DestroyThread( p_vout, THREAD_ERROR );
320 * Main loop - it is not executed if an error occured during
323 while( (!p_vout->b_die) && (!p_vout->b_error) )
325 /* Initialize loop variables */
327 p_directbuffer = NULL;
329 current_date = mdate();
333 if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
335 intf_Msg( "vout stats: picture heap: %d/%d",
336 p_vout->i_pictures, VOUT_MAX_PICTURES );
341 * Find the picture to display - this operation does not need lock,
342 * since only READY_PICTUREs are handled
344 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
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) ) )
350 p_picture = &p_vout->p_picture[i_index];
351 display_date = p_picture->date;
355 if( p_picture != NULL )
357 /* Compute FPS rate */
358 p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
361 if( display_date < current_date + p_vout->render_time )
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 )
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;
374 /* Destroy the picture without displaying it */
375 p_picture->i_status = DESTROYED_PICTURE;
376 p_vout->i_pictures--;
378 intf_WarnMsg( 1, "vout warning: late picture skipped (%p)",
380 vlc_mutex_unlock( &p_vout->picture_lock );
384 else if( display_date > current_date + VOUT_DISPLAY_DELAY )
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
396 * Check for subpictures to display
398 p_subpic = vout_SortSubPictures( p_vout, display_date );
403 p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );
408 if( display_date != 0 )
410 /* Store render time using Bresenham algorithm */
411 p_vout->render_time += mdate() - current_date;
412 p_vout->render_time >>= 1;
415 /* Give back change lock */
416 vlc_mutex_unlock( &p_vout->change_lock );
418 /* Sleep a while or until a given date */
419 if( display_date != 0 )
421 mwait( display_date - VOUT_MWAIT_TOLERANCE );
425 msleep( VOUT_IDLE_SLEEP );
428 /* On awakening, take back lock and send immediately picture
430 vlc_mutex_lock( &p_vout->change_lock );
433 intf_DbgMsg( "picture %p, subpicture %p", p_picture, p_subpic );
437 * Display the previously rendered picture
439 if( p_picture != NULL )
441 /* Display the direct buffer returned by vout_RenderPicture */
442 p_vout->pf_display( p_vout, p_directbuffer );
444 /* Remove picture from heap */
445 vlc_mutex_lock( &p_vout->picture_lock );
446 if( p_picture->i_refcount )
448 p_picture->i_status = DISPLAYED_PICTURE;
452 p_picture->i_status = DESTROYED_PICTURE;
453 p_vout->i_pictures--;
455 vlc_mutex_unlock( &p_vout->picture_lock );
459 * Check events and manage thread
461 if( p_vout->pf_manage( p_vout ) )
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. */
471 * Error loop - wait until the thread destruction is requested
473 if( p_vout->b_error )
475 ErrorThread( p_vout );
481 /* Destroy method-dependant resources */
482 p_vout->pf_destroy( p_vout );
484 /* Destroy thread structures allocated by CreateThread */
485 DestroyThread( p_vout, THREAD_OVER );
486 intf_DbgMsg( "thread end" );
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
495 *****************************************************************************/
496 static void ErrorThread( vout_thread_t *p_vout )
498 /* Wait until a `die' order */
499 while( !p_vout->b_die )
502 msleep( VOUT_IDLE_SLEEP );
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 )
514 int i_index; /* index in heap */
517 *p_vout->pi_status = THREAD_END;
521 struct tms cpu_usage;
524 intf_Msg( "vout stats: cpu usage (user: %d, system: %d)",
525 cpu_usage.tms_utime, cpu_usage.tms_stime );
529 /* Destroy all remaining pictures */
530 for( i_index = p_vout->i_directbuffers;
531 i_index < VOUT_MAX_PICTURES;
534 if( p_vout->p_picture[i_index].i_status != FREE_PICTURE )
536 free( p_vout->p_picture[i_index].planes[0].p_data );
540 /* Destroy all remaining subpictures */
541 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
543 if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
545 free( p_vout->p_subpicture[i_index].p_data );
549 /* Destroy translation tables */
550 p_vout->pf_end( p_vout );
552 /* Release the change lock */
553 vlc_mutex_unlock( &p_vout->change_lock );
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 )
564 int *pi_status; /* status adress */
566 /* Store status adress */
567 pi_status = p_vout->pi_status;
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 );
574 /* Release the module */
575 module_Unneed( p_vout->p_module );
579 *pi_status = i_status;