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 $
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 )
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_type = EMPTY_PICTURE;
148 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
150 p_vout->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
151 p_vout->p_subpicture[i_index].i_type = EMPTY_SUBPICTURE;
154 /* Initialize the rendering heap */
155 p_vout->i_heap_size = 0;
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;
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 )
248 *p_vout->pi_status = THREAD_START;
250 vlc_mutex_lock( &p_vout->change_lock );
256 /* Initialize output method, it issues its own error messages */
257 if( p_vout->pf_init( p_vout ) )
259 vlc_mutex_unlock( &p_vout->change_lock );
263 if( !I_OUTPUTPICTURES )
265 intf_ErrMsg( "vout error: plugin was unable to allocate at least "
266 "one direct buffer" );
267 vlc_mutex_unlock( &p_vout->change_lock );
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 ) )
278 p_vout->b_direct = 1;
280 /* Map the first render buffers to the first direct buffers, but
281 * leave the first direct buffer for memcpy operations */
286 p_vout->b_direct = 0;
288 /* Append render buffers after the direct buffers */
289 i_index = I_RENDERPICTURES;
292 for( ; i_index < VOUT_MAX_PICTURES; i_index++ )
294 PP_RENDERPICTURE[ I_RENDERPICTURES ]
295 = &p_vout->p_picture[ i_index ];
299 intf_WarnMsg( 1, "vout info: got %i direct buffer(s)", I_OUTPUTPICTURES );
301 /* Mark thread as running and return */
302 p_vout->b_active = 1;
303 *p_vout->pi_status = THREAD_READY;
305 intf_DbgMsg("thread ready");
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)
318 int i_index; /* index in heap */
319 mtime_t current_date; /* current date */
320 mtime_t display_date; /* display date */
322 picture_t * p_picture; /* picture pointer */
323 picture_t * p_directbuffer; /* direct buffer to display */
325 subpicture_t * p_subpic; /* subpicture pointer */
327 /* Create and initialize system-dependant method - this function issues its
328 * own error messages */
329 if( p_vout->pf_create( p_vout ) )
331 DestroyThread( p_vout, THREAD_ERROR );
338 p_vout->b_error = InitThread( p_vout );
339 if( p_vout->b_error )
341 /* Destroy thread structures allocated by Create and InitThread */
342 p_vout->pf_destroy( p_vout );
344 DestroyThread( p_vout, THREAD_ERROR );
349 * Main loop - it is not executed if an error occured during
352 while( (!p_vout->b_die) && (!p_vout->b_error) )
354 /* Initialize loop variables */
356 current_date = mdate();
360 if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
362 intf_Msg( "vout stats: picture heap: %d/%d",
363 I_RENDERPICTURES, p_vout->i_heap_size );
368 * Find the picture to display - this operation does not need lock,
369 * since only READY_PICTUREs are handled
374 i_index < I_RENDERPICTURES;
377 if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
378 && ( (p_picture == NULL) ||
379 (PP_RENDERPICTURE[i_index]->date < display_date) ) )
381 p_picture = PP_RENDERPICTURE[i_index];
382 display_date = p_picture->date;
386 if( p_picture != NULL )
388 /* Compute FPS rate */
389 p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
392 if( display_date < current_date + p_vout->render_time )
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 )
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;
405 /* Destroy the picture without displaying it */
406 p_picture->i_status = DESTROYED_PICTURE;
407 p_vout->i_heap_size--;
409 intf_WarnMsg( 1, "vout warning: late picture skipped (%p)",
411 vlc_mutex_unlock( &p_vout->picture_lock );
415 else if( display_date > current_date + VOUT_DISPLAY_DELAY )
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
427 * Check for subpictures to display
429 p_subpic = vout_SortSubPictures( p_vout, display_date );
434 p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );
439 if( display_date != 0 )
441 /* Store render time using Bresenham algorithm */
442 p_vout->render_time += mdate() - current_date;
443 p_vout->render_time >>= 1;
446 /* Give back change lock */
447 vlc_mutex_unlock( &p_vout->change_lock );
449 /* Sleep a while or until a given date */
450 if( display_date != 0 )
452 mwait( display_date - VOUT_MWAIT_TOLERANCE );
456 msleep( VOUT_IDLE_SLEEP );
459 /* On awakening, take back lock and send immediately picture
461 vlc_mutex_lock( &p_vout->change_lock );
464 intf_DbgMsg( "picture %p, subpicture %p", p_picture, p_subpic );
468 * Display the previously rendered picture
470 if( p_picture != NULL )
472 /* Display the direct buffer returned by vout_RenderPicture */
473 p_vout->pf_display( p_vout, p_directbuffer );
475 /* Remove picture from heap */
476 vlc_mutex_lock( &p_vout->picture_lock );
477 if( p_picture->i_refcount )
479 p_picture->i_status = DISPLAYED_PICTURE;
483 p_picture->i_status = DESTROYED_PICTURE;
484 p_vout->i_heap_size--;
486 vlc_mutex_unlock( &p_vout->picture_lock );
490 * Check events and manage thread
492 if( p_vout->pf_manage( p_vout ) )
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. */
502 * Error loop - wait until the thread destruction is requested
504 if( p_vout->b_error )
506 ErrorThread( p_vout );
512 /* Destroy method-dependant resources */
513 p_vout->pf_destroy( p_vout );
515 /* Destroy thread structures allocated by CreateThread */
516 DestroyThread( p_vout, THREAD_OVER );
517 intf_DbgMsg( "thread end" );
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
526 *****************************************************************************/
527 static void ErrorThread( vout_thread_t *p_vout )
529 /* Wait until a `die' order */
530 while( !p_vout->b_die )
533 msleep( VOUT_IDLE_SLEEP );
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 )
545 int i_index; /* index in heap */
548 *p_vout->pi_status = THREAD_END;
552 struct tms cpu_usage;
555 intf_Msg( "vout stats: cpu usage (user: %d, system: %d)",
556 cpu_usage.tms_utime, cpu_usage.tms_stime );
560 /* Destroy all remaining pictures */
561 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
563 if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
565 free( p_vout->p_picture[i_index].planes[0].p_data );
569 /* Destroy all remaining subpictures */
570 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
572 if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
574 free( p_vout->p_subpicture[i_index].p_data );
578 /* Destroy translation tables */
579 p_vout->pf_end( p_vout );
581 /* Release the change lock */
582 vlc_mutex_unlock( &p_vout->change_lock );
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 )
593 int *pi_status; /* status adress */
595 /* Store status adress */
596 pi_status = p_vout->pi_status;
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 );
603 /* Release the module */
604 module_Unneed( p_vout->p_module );
608 *pi_status = i_status;