1 /*******************************************************************************
2 * video_output.c : video output thread
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 *******************************************************************************/
10 /*******************************************************************************
12 *******************************************************************************/
24 #include "vlc_thread.h"
28 #include "video_graphics.h"
29 #include "video_output.h"
30 #include "video_x11.h"
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 );
41 static void RenderPicture ( vout_thread_t *p_vout, picture_t *p_pic );
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)
65 int i_width, int i_height, int *pi_status
68 vout_thread_t * p_vout; /* thread descriptor */
69 int i_status; /* thread status */
71 /* Allocate descriptor and create method */
72 p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
73 if( p_vout == NULL ) /* error */
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)
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;
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) )
104 intf_ErrMsg("vout error: %s\n", strerror(ENOMEM));
105 vout_SysDestroy( p_vout );
110 /* If status is NULL, wait until the thread is created */
111 if( pi_status == NULL )
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 )
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 )
137 int i_status; /* thread status */
139 intf_DbgMsg( "0x%x\n", p_vout );
142 p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
143 *p_vout->pi_status = THREAD_DESTROY;
145 /* Request thread destruction */
148 /* If status is NULL, wait until thread has been destroyed */
149 if( pi_status == NULL )
153 msleep( THREAD_SLEEP );
154 }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
155 && (i_status != THREAD_FATAL) );
159 /*******************************************************************************
160 * vout_DisplayPicture: display a picture
161 *******************************************************************************
162 * Remove the reservation flag of a picture, which will cause it to be ready for
164 *******************************************************************************/
165 void vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
167 vlc_mutex_lock( &p_vout->lock );
169 /* Remove reservation flag */
170 p_pic->i_status = READY_PICTURE;
174 p_vout->c_pictures++;
177 vlc_mutex_unlock( &p_vout->lock );
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 )
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 */
195 vlc_mutex_lock( &p_vout->lock );
198 * Look for an empty place
201 i_picture < VOUT_MAX_PICTURES;
204 if( p_vout->p_picture[i_picture].i_status == DESTROYED_PICTURE )
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) )
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] );
219 else if( p_destroyed_picture == NULL )
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];
226 else if( (p_free_picture == NULL) &&
227 (p_vout->p_picture[i_picture].i_status == FREE_PICTURE ))
229 /* Picture is empty and ready for allocation */
230 p_free_picture = &p_vout->p_picture[i_picture];
234 /* If no free picture is available, use a destroyed picture */
235 if( (p_free_picture == NULL) && (p_destroyed_picture != NULL ) )
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;
246 if( p_free_picture != NULL )
248 /* Allocate memory */
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 = p_free_picture->p_data;
256 p_free_picture->p_u = p_free_picture->p_data + i_height * i_bytes_per_line;
257 p_free_picture->p_v = p_free_picture->p_data + i_height * i_bytes_per_line * 2;
261 intf_DbgMsg("0x%x error: unknown picture type %d\n", p_vout, i_type );
262 p_free_picture->p_data = NULL;
266 if( p_free_picture->p_data != NULL )
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;
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");
285 vlc_mutex_unlock( &p_vout->lock );
286 return( p_free_picture );
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 );
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 )
304 vlc_mutex_lock( &p_vout->lock );
306 /* Mark picture for destruction */
307 p_pic->i_status = DESTROYED_PICTURE;
308 intf_DbgMsg("%p -> picture %p destroyed\n", p_vout, p_pic );
310 vlc_mutex_unlock( &p_vout->lock );
313 /*******************************************************************************
314 * vout_LinkPicture: increment reference counter of a picture
315 *******************************************************************************
316 * This function increment the reference counter of a picture in the video
318 *******************************************************************************/
319 void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
321 vlc_mutex_lock( &p_vout->lock );
323 vlc_mutex_unlock( &p_vout->lock );
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 )
333 vlc_mutex_lock( &p_vout->lock );
335 if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
337 p_pic->i_status = DESTROYED_PICTURE;
339 vlc_mutex_unlock( &p_vout->lock );
342 /* following functions are local */
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 )
353 int i_index; /* generic index */
356 *p_vout->pi_status = THREAD_START;
358 /* Initialize pictures */
359 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
361 p_vout->p_picture[i_index].i_type = EMPTY_PICTURE;
362 p_vout->p_picture[i_index].i_status= FREE_PICTURE;
365 /* Initialize other properties */
366 p_vout->i_pictures = 0;
369 p_vout->c_idle_loops = 0;
370 p_vout->c_pictures = 0;
371 p_vout->c_rendered_pictures = 0;
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 */
378 *p_vout->pi_status = THREAD_ERROR;
382 /* Mark thread as running and return */
383 *p_vout->pi_status = THREAD_READY;
384 intf_DbgMsg("%p -> succeeded\n", p_vout);
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)
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;
402 char sz_date[MSTRTIME_MAX_SIZE]; /* date buffer */
406 * Initialize thread and free configuration
408 intf_DbgMsg( "0x%x begin\n", p_vout );
409 p_vout->b_error = InitThread( p_vout );
410 if( p_vout->b_error )
412 free( p_vout ); /* destroy descriptor */
417 * Main loop - it is not executed if an error occured during
420 while( (!p_vout->b_die) && (!p_vout->b_error) )
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)
428 vlc_mutex_lock( &p_vout->lock );
430 for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
432 if( (p_vout->p_picture[i_picture].i_status == READY_PICTURE) &&
434 (p_vout->p_picture[i_picture].date < p_pic->date) ) )
436 p_pic = &p_vout->p_picture[i_picture];
440 vlc_mutex_unlock( &p_vout->lock );
443 * Render picture if any
447 current_date = mdate();
448 if( p_pic->date < current_date )
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 )
455 p_pic->i_status = DISPLAYED_PICTURE;
459 p_pic->i_status = DESTROYED_PICTURE;
461 vlc_mutex_unlock( &p_vout->lock );
462 intf_ErrMsg( "vout error: picture %p was late - skipped\n", p_pic );
465 else if( p_pic->date > current_date + VOUT_DISPLAY_DELAY )
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 */
474 /* Picture has not yet been displayed, and has a valid display
475 * date : render it */
476 RenderPicture( p_vout, p_pic );
481 * Check events, sleep and display picture
483 i_err = vout_SysManage( p_vout );
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. */
495 /* A picture is ready to be displayed : sleep until its display date */
496 mwait( p_pic->date );
500 vout_SysDisplay( p_vout );
503 /* Picture has been displayed : destroy it */
504 vlc_mutex_lock( &p_vout->lock );
505 if( p_pic->i_refcount )
507 p_pic->i_status = DISPLAYED_PICTURE;
511 p_pic->i_status = DESTROYED_PICTURE;
513 vlc_mutex_unlock( &p_vout->lock );
517 /* Sleep to wait for new pictures */
518 msleep( VOUT_IDLE_SLEEP );
520 /* Update counters */
521 p_vout->c_idle_loops++;
527 /* Update counters */
535 if( p_vout->b_error )
537 ErrorThread( p_vout );
542 intf_DbgMsg( "0x%x end\n", p_vout );
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
551 *******************************************************************************/
552 static void ErrorThread( vout_thread_t *p_vout )
554 /* Wait until a `die' order */
555 while( !p_vout->b_die )
558 msleep( VOUT_IDLE_SLEEP );
562 /*******************************************************************************
563 * EndThread: thread destruction
564 *******************************************************************************
565 * This function is called when the thread ends after a sucessfull
567 *******************************************************************************/
568 static void EndThread( vout_thread_t *p_vout )
570 int * pi_status; /* thread status */
574 pi_status = p_vout->pi_status;
575 *pi_status = THREAD_END;
577 /* Destroy all remaining pictures */
578 for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
580 if( p_vout->p_picture[i_picture].i_status != FREE_PICTURE )
582 free( p_vout->p_picture[i_picture].p_data );
586 /* Destroy thread structures allocated by InitThread */
587 vout_SysEnd( p_vout );
588 vout_SysDestroy( p_vout ); /* destroy output method */
592 *pi_status = THREAD_OVER;
593 intf_DbgMsg("%p\n", p_vout);
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 )