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 *******************************************************************************/
18 #include <X11/Xutil.h>
19 #include <X11/extensions/XShm.h>
24 #include "vlc_thread.h"
28 #include "video_graphics.h"
29 #include "video_output.h"
30 #include "video_x11.h"
37 static int CheckConfiguration ( video_cfg_t *p_cfg );
38 static int InitThread ( vout_thread_t *p_vout );
39 static void RunThread ( vout_thread_t *p_vout );
40 static void ErrorThread ( vout_thread_t *p_vout );
41 static void EndThread ( vout_thread_t *p_vout );
43 static picture_t * FindPicture ( vout_thread_t *p_vout );
44 static void EmptyPicture ( vout_thread_t *p_vout, picture_t *p_pic );
45 static mtime_t FirstPass ( vout_thread_t *p_vout, mtime_t current_date );
46 static void SecondPass ( vout_thread_t *p_vout, mtime_t display_date );
47 static void SortPictures ( vout_thread_t *p_vout, picture_t **pp_picture );
48 static void RenderPicture ( vout_thread_t *p_vout, picture_t *p_pic );
49 static void ClipPicture ( int *pi_ofs, int i_size, int *pi_pic_ofs, int *pi_pic_size,
50 int i_ratio, int i_placement );
52 /*******************************************************************************
53 * vout_CreateThread: creates a new video output thread
54 *******************************************************************************
55 * This function creates a new video output thread, and returns a pointer
56 * to its description. On error, it returns NULL.
57 * Following configuration properties are used:
58 * VIDEO_CFG_SIZE video heap maximal size
59 * VIDEO_CFG_WIDTH window width
60 * VIDEO_CFG_HEIGHT window height
61 * Using X11 display method (the only one supported yet):
62 * VIDEO_CFG_DISPLAY display used
63 * VIDEO_CFG_TITLE window title
64 * VIDEO_CFG_SHM_EXT try to use XShm extension
65 * If pi_status is NULL, then the function will block until the thread is ready.
66 * If not, pi_error will be updated using one of the THREAD_* constants.
67 *******************************************************************************/
68 vout_thread_t *vout_CreateThread( video_cfg_t *p_cfg, int *pi_status )
70 vout_thread_t * p_vout; /* thread descriptor */
71 int i_status; /* thread status */
76 if( CheckConfiguration( p_cfg ) )
81 /* Allocate descriptor and initialize flags */
82 p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
83 if( p_vout == NULL ) /* error */
87 if( vout_X11AllocOutputMethod( p_vout, p_cfg ) )
93 /* Copy configuration */
94 p_vout->i_max_pictures = p_cfg->i_size;
95 p_vout->i_width = p_cfg->i_width;
96 p_vout->i_height = p_cfg->i_height;
99 p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
100 *p_vout->pi_status = THREAD_CREATE;
102 /* Initialize flags */
105 p_vout->b_active = 0;
107 /* Create thread and set locks */
108 vlc_mutex_init( &p_vout->streams_lock );
109 vlc_mutex_init( &p_vout->pictures_lock );
110 if( vlc_thread_create( &p_vout->thread_id, "video output", (vlc_thread_func)RunThread, (void *) p_vout) )
112 intf_ErrMsg("vout error: %s\n", strerror(ENOMEM));
113 intf_DbgMsg("failed\n");
114 vout_X11FreeOutputMethod( p_vout );
119 /* If status is NULL, wait until the thread is created */
120 if( pi_status == NULL )
124 msleep( THREAD_SLEEP );
125 }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR)
126 && (i_status != THREAD_FATAL) );
127 if( i_status != THREAD_READY )
129 intf_DbgMsg("failed\n");
134 intf_DbgMsg("succeeded -> %p\n", p_vout);
138 /*******************************************************************************
139 * vout_DestroyThread: destroys a previously created thread
140 *******************************************************************************
141 * Destroy a terminated thread.
142 * The function will request a destruction of the specified thread. If pi_error
143 * is NULL, it will return once the thread is destroyed. Else, it will be
144 * update using one of the THREAD_* constants.
145 *******************************************************************************/
146 void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
148 int i_status; /* thread status */
151 p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
152 *p_vout->pi_status = THREAD_DESTROY;
154 /* Request thread destruction */
157 /* If status is NULL, wait until thread has been destroyed */
162 msleep( THREAD_SLEEP );
163 }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
164 && (i_status != THREAD_FATAL) );
167 intf_DbgMsg("%p -> succeeded\n", p_vout);
170 /*******************************************************************************
171 * vout_DisplayPicture: add a picture to a thread
172 *******************************************************************************
173 * The picture will be placed in an empty place of the video heap of the thread.
174 * If this is impossible, NULL will be returned. Else, the original picture
176 *******************************************************************************/
177 picture_t * vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
179 picture_t * p_newpic; /* new picture index */
181 p_newpic = FindPicture( p_vout );
182 if( p_newpic != NULL )
184 /* Copy picture information */
185 video_CopyPictureDescriptor( p_newpic, p_pic );
186 p_newpic->p_data = p_pic->p_data;
189 /* Update heap size and stats */
190 p_vout->i_pictures++;
192 p_vout->c_pictures++;
193 p_vout->p_stream[ p_newpic->i_stream ].c_pictures++;
197 /* Release lock and return */
198 vlc_mutex_unlock( &p_vout->pictures_lock );
202 /*******************************************************************************
203 * vout_DisplayPictureCopy: copy a picture to a thread
204 *******************************************************************************
205 * The picture will be copied in an empty place of the video heap of the thread.
206 * If this is impossible, NULL will be returned. The picture used is the copy
207 * of the picture passed as argument, which remains usable.
208 * The picture data are copied only if the original picture owns its own data.
209 * The link reference counter is set to 0.
210 *******************************************************************************/
211 picture_t * vout_DisplayPictureCopy( vout_thread_t *p_vout, picture_t *p_pic )
213 picture_t * p_newpic; /* new picture index */
215 p_newpic = FindPicture( p_vout );
216 if( p_newpic != NULL )
218 /* Copy picture information */
219 video_CopyPictureDescriptor( p_newpic, p_pic );
221 /* If source picture owns its data, make a copy */
222 if( p_pic->i_flags & OWNER_PICTURE )
224 p_newpic->p_data = malloc( p_pic->i_height * p_pic->i_bytes_per_line );
225 if( p_newpic->p_data == NULL ) /* error */
227 intf_ErrMsg("vout error: %s\n", strerror(ENOMEM) );
229 /* Restore type and flags */
230 p_newpic->i_type = EMPTY_PICTURE;
231 p_newpic->i_flags = 0;
232 vlc_mutex_unlock( &p_vout->pictures_lock );
235 memcpy( p_newpic->p_data, p_pic->p_data,
236 p_pic->i_height * p_pic->i_bytes_per_line );
238 /* If source picture does not owns its data, copy the reference */
241 p_newpic->p_data = p_pic->p_data;
243 p_newpic->i_refcount = 0;
245 /* Update heap size and stats */
246 p_vout->i_pictures++;
248 p_vout->c_pictures++;
249 p_vout->p_stream[ p_newpic->i_stream ].c_pictures++;
253 /* Release lock and return */
254 vlc_mutex_unlock( &p_vout->pictures_lock );
258 /*******************************************************************************
259 * vout_DisplayPictureReplicate: copy a picture to a thread
260 *******************************************************************************
261 * The picture will be copied in an empty place of the video heap of the thread.
262 * If this is impossible, NULL will be returned. The picture used is the copy
263 * of the picture passed as argument, which remains usable.
264 * The picture data are a reference to original picture (the picture in the heap
265 * does not owns its data). Link reference counter is set to 0.
266 *******************************************************************************/
267 picture_t * vout_DisplayPictureReplicate( vout_thread_t *p_vout, picture_t *p_pic )
269 picture_t * p_newpic; /* new picture descrpitor */
271 p_newpic = FindPicture( p_vout );
272 if( p_newpic != NULL )
274 /* Copy picture information */
275 video_CopyPictureDescriptor( p_newpic, p_pic );
276 p_newpic->i_flags &= ~OWNER_PICTURE;
277 p_newpic->p_data = p_pic->p_data;
278 p_newpic->i_refcount = 0;
280 /* Update heap size and stats */
281 p_vout->i_pictures++;
283 p_vout->c_pictures++;
284 p_vout->p_stream[ p_newpic->i_stream ].c_pictures++;
288 /* Release lock and return */
289 vlc_mutex_unlock( &p_vout->pictures_lock );
293 /*******************************************************************************
294 * vout_DisplayReservedPicture: add a reserved picture to a thread
295 *******************************************************************************
296 * The picture will be placed in an empty place of the video heap of the thread.
297 * If the picture has been declared on reservation as permanent, it remains
298 * usable. Else, it will be destroyed by this call.
299 * This function can't fail, since it is the purpose of picture reservation to
300 * provide such a robust mechanism.
301 *******************************************************************************/
302 picture_t * vout_DisplayReservedPicture( vout_thread_t *p_vout, picture_t *p_pic )
304 vlc_mutex_lock( &p_vout->pictures_lock );
306 /* Remove reservation flag */
307 p_pic->i_flags &= ~RESERVED_PICTURE;
311 p_vout->c_pictures++;
312 p_vout->p_stream[ p_pic->i_stream ].c_pictures++;
315 vlc_mutex_unlock( &p_vout->pictures_lock );
319 /*******************************************************************************
320 * vout_CreateReservedPicture: reserve a place for a new picture in heap
321 *******************************************************************************
322 * This function create a reserved image in the video output heap.
323 * A null pointer is returned if the function fails.
324 * Following configuration properties are used:
325 * VIDEO_CFG_WIDTH picture width (required)
326 * VIDEO_CFG_HEIGHT picture height (required)
327 * VIDEO_CFG_TYPE picture type (required)
328 * VIDEO_CFG_FLAGS flags
329 * VIDEO_CFG_BPP padded bpp (required for pixel pictures)
330 * VIDEO_CFG_POSITION position in output window
331 * VIDEO_CFG_ALIGN base position in output window
332 * VIDEO_CFG_RATIO display ratio
333 * VIDEO_CFG_LEVEL overlay hierarchical level
334 * VIDEO_CFG_REFCOUNT links reference counter
335 * VIDEO_CFG_STREAM video stream id
336 * VIDEO_CFG_DATE display date
337 * VIDEO_CFG_DURATION duration for overlay pictures
338 * VIDEO_CFG_PIXEL pixel value for mask pictures
339 * VIDEO_CFG_DATA picture data (required if not owner and non blank)
340 * Pictures created using this function are always reserved, even if they did
341 * not had the RESERVED_PICTURE flag set.
342 * This function is very similar to the video_CreatePicture function.
343 *******************************************************************************/
344 picture_t *vout_CreateReservedPicture( vout_thread_t *p_vout, video_cfg_t *p_cfg )
346 picture_t * p_newpic; /* new picture index */
348 p_newpic = FindPicture( p_vout );
349 if( p_newpic != NULL )
351 /* Create new picture */
352 if( video_CreatePictureBody( p_newpic, p_cfg ) )
354 intf_ErrMsg("vout error: %s\n", strerror(ENOMEM));
355 /* Error: restore type and flags */
356 p_newpic->i_type = EMPTY_PICTURE;
357 p_newpic->i_flags = 0;
358 vlc_mutex_unlock( &p_vout->pictures_lock );
361 p_newpic->i_flags |= RESERVED_PICTURE;
363 /* Update heap size, release lock and return */
364 p_vout->i_pictures++;
367 /* Release lock and return */
368 vlc_mutex_unlock( &p_vout->pictures_lock );
372 /*******************************************************************************
373 * vout_ReservePicture: reserve a place for a picture in heap
374 *******************************************************************************
375 * This function transforms an existing picture in a reserved picture in a
376 * video heap. The picture will be placed in an empty place of the video heap
377 * of the thread. If this is impossible, NULL will be returned. Else, the
378 * original picture is destroyed.
379 *******************************************************************************/
380 picture_t *vout_ReservePicture( vout_thread_t *p_vout, picture_t *p_pic )
382 picture_t * p_newpic; /* new picture index */
384 p_newpic = FindPicture( p_vout );
385 if( p_newpic != NULL )
387 /* Copy picture information */
388 video_CopyPictureDescriptor( p_newpic, p_pic );
389 p_newpic->p_data = p_pic->p_data;
390 p_newpic->i_flags |= RESERVED_PICTURE;
393 /* Update heap size */
394 p_vout->i_pictures++;
397 /* Release lock and return */
398 vlc_mutex_unlock( &p_vout->pictures_lock );
402 /*******************************************************************************
403 * vout_ReservePictureCopy: reserve a place for a picture in heap
404 *******************************************************************************
405 * This function returns a pointer to a picture which can be processed. The
406 * returned picture is a copy of the one passed as parameter.
407 * This function returns NULL if the reservation fails.
408 *******************************************************************************/
409 picture_t *vout_ReservePictureCopy( vout_thread_t *p_vout, picture_t *p_pic )
411 picture_t * p_newpic; /* new picture index */
413 p_newpic = FindPicture( p_vout );
414 if( p_newpic != NULL )
416 /* Copy picture information */
417 video_CopyPictureDescriptor( p_newpic, p_pic );
419 /* If source picture owns its data, make a copy */
420 if( p_pic->i_flags & OWNER_PICTURE )
422 p_newpic->p_data = malloc( p_pic->i_height * p_pic->i_bytes_per_line );
423 if( p_newpic->p_data == NULL ) /* error */
425 intf_ErrMsg("vout error: %s\n", strerror(ENOMEM));
426 /* Restore type and flags */
427 p_newpic->i_type = EMPTY_PICTURE;
428 p_newpic->i_flags = 0;
429 vlc_mutex_unlock( &p_vout->pictures_lock );
432 memcpy( p_newpic->p_data, p_pic->p_data,
433 p_pic->i_height * p_pic->i_bytes_per_line );
435 /* If source picture does not owns its data, copy the reference */
438 p_newpic->p_data = p_pic->p_data;
440 p_newpic->i_refcount = 0;
441 p_newpic->i_flags |= RESERVED_PICTURE;
443 /* Update heap size */
444 p_vout->i_pictures++;
447 /* Release lock and return */
448 vlc_mutex_unlock( &p_vout->pictures_lock );
452 /*******************************************************************************
453 * vout_ReservePictureReplicate: reserve a place for a picture in heap
454 *******************************************************************************
455 * This function returns a pointer to a picture which can be processed. The
456 * returned picture is a copy of the one passed as parameter.
457 * This function returns NULL if the reservation fails.
458 *******************************************************************************/
459 picture_t *vout_ReservePictureReplicate( vout_thread_t *p_vout, picture_t *p_pic )
461 picture_t * p_newpic; /* new picture index */
463 p_newpic = FindPicture( p_vout );
464 if( p_newpic != NULL )
466 /* Copy picture information */
467 video_CopyPictureDescriptor( p_newpic, p_pic );
468 p_newpic->p_data = p_pic->p_data;
469 p_newpic->i_refcount = 0;
470 p_newpic->i_flags &= ~OWNER_PICTURE;
471 p_newpic->i_flags |= RESERVED_PICTURE;
473 /* Update heap size */
474 p_vout->i_pictures++;
477 /* Release lock and return */
478 vlc_mutex_unlock( &p_vout->pictures_lock );
482 /*******************************************************************************
483 * vout_RemovePicture: remove a permanent or reserved picture from the heap
484 *******************************************************************************
485 * This function frees a previously reserved picture or a permanent
486 * picture. The picture data is destroyed if required.
487 *******************************************************************************/
488 void vout_RemovePicture( vout_thread_t *p_vout, picture_t *p_pic )
490 vlc_mutex_lock( &p_vout->pictures_lock );
492 /* Mark picture for destruction */
493 p_pic->i_flags |= DESTROY_PICTURE;
494 /* Since permanent pictures can normally not be destroyed by the vout thread,
495 * the permanent flag needs to be removed */
496 p_pic->i_flags &= ~PERMANENT_PICTURE;
497 intf_DbgMsg("%p -> picture %p removing requested\n", p_vout, p_pic );
499 vlc_mutex_unlock( &p_vout->pictures_lock );
502 /*******************************************************************************
503 * vout_RefreshPermanentPicture:
504 *******************************************************************************
505 * Set the DISPLAYED_PICTURE flag of a permanent picture to 0, allowing to
506 * display it again if it not overlaid. The display date is also updated.
507 *******************************************************************************/
508 void vout_RefreshPermanentPicture( vout_thread_t *p_vout, picture_t *p_pic,
509 mtime_t display_date )
511 vlc_mutex_lock( &p_vout->pictures_lock );
512 p_pic->i_flags &= ~DISPLAYED_PICTURE;
513 p_pic->date = display_date;
514 vlc_mutex_lock( &p_vout->pictures_lock );
517 /*******************************************************************************
518 * vout_LinkPicture: increment reference counter of a picture
519 *******************************************************************************
520 * This function increment the reference counter of a picture in the video
522 *******************************************************************************/
523 void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
525 vlc_mutex_lock( &p_vout->pictures_lock );
527 vlc_mutex_unlock( &p_vout->pictures_lock );
530 /*******************************************************************************
531 * vout_UnlinkPicture: decrement reference counter of a picture
532 *******************************************************************************
533 * This function decrement the reference counter of a picture in the video heap.
534 *******************************************************************************/
535 void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
537 vlc_mutex_lock( &p_vout->pictures_lock );
540 if( p_pic->i_refcount < 0 )
542 intf_DbgMsg("%p -> picture %p i_refcount < 0\n", p_vout, p_pic);
545 vlc_mutex_unlock( &p_vout->pictures_lock );
548 /*******************************************************************************
549 * vout_CreateStream: get a new stream id
550 *******************************************************************************
551 * Stream ids are used to create list of pictures in a video output thread.
552 * The function returns an unused stream id, or a negative number on error.
553 * Stream id 0 is never returned, since it is not a stream descriptor but a set
554 * of independant images.
555 *******************************************************************************/
556 int vout_CreateStream( vout_thread_t *p_vout )
558 int i_index; /* stream index */
560 /* Find free (inactive) stream id */
561 vlc_mutex_lock( &p_vout->streams_lock ); /* get lock */
562 for( i_index = 1; i_index < VOUT_MAX_STREAMS; i_index++ ) /* find free id */
564 if( p_vout->p_stream[i_index].i_status == VOUT_INACTIVE_STREAM )
566 /* Initialize stream */
567 p_vout->p_stream[i_index].i_status = VOUT_ACTIVE_STREAM;
569 p_vout->p_stream[i_index].c_pictures = 0;
570 p_vout->p_stream[i_index].c_rendered_pictures = 0;
573 /* Return stream id */
574 intf_DbgMsg("%p -> stream %i created\n", p_vout, i_index);
575 vlc_mutex_unlock( &p_vout->streams_lock ); /* release lock */
576 return( i_index ); /* return id */
580 /* Failure: all streams id are already active */
581 intf_DbgMsg("%p -> failed\n", p_vout);
582 vlc_mutex_unlock( &p_vout->streams_lock );
586 /*******************************************************************************
587 * vout_EndStream: free a previously allocated stream
588 *******************************************************************************
589 * This function must be called once a stream is no more used. It can be called
590 * even if there are remaining pictures in the video heap, since the stream will
591 * only be destroyed once all pictures of the stream have been displayed.
592 *******************************************************************************/
593 void vout_EndStream( vout_thread_t *p_vout, int i_stream )
595 vlc_mutex_lock( &p_vout->streams_lock ); /* get lock */
596 p_vout->p_stream[i_stream].i_status = VOUT_ENDING_STREAM; /* mark stream */
597 vlc_mutex_unlock( &p_vout->streams_lock ); /* release lock */
598 intf_DbgMsg("%p -> stream %d\n", p_vout, i_stream);
601 /*******************************************************************************
602 * vout_DestroyStream: free a previously allocated stream
603 *******************************************************************************
604 * This function must be called once a stream is no more used. It can be called
605 * even if there are remaining pictures in the video heap, since all pictures
606 * will be removed before the stream is destroyed.
607 *******************************************************************************/
608 void vout_DestroyStream( vout_thread_t *p_vout, int i_stream )
610 vlc_mutex_lock( &p_vout->streams_lock ); /* get lock */
611 p_vout->p_stream[i_stream].i_status = VOUT_DESTROYED_STREAM;/* mark stream */
612 vlc_mutex_unlock( &p_vout->streams_lock ); /* release lock */
613 intf_DbgMsg("%p -> stream %d\n", p_vout, i_stream);
616 /* following functions are debugging functions */
618 /*******************************************************************************
619 * vout_PrintHeap: display heap state (debugging function)
620 *******************************************************************************
621 * This functions, which is only defined if DEBUG is defined, can be used
622 * for debugging purposes. It prints on debug stream the current state of the
623 * heap. The second parameter is used to identify the output.
624 *******************************************************************************/
626 void vout_PrintHeap( vout_thread_t *p_vout, char *psz_str )
628 int i_picture; /* picture index */
630 intf_Msg("vout: --- thread %p heap status (%s) ---\n", p_vout, psz_str );
632 /* Browse all pictures in heap */
633 for( i_picture = 0; i_picture < p_vout->i_max_pictures; i_picture++ )
635 if( p_vout->p_picture[i_picture].i_type != EMPTY_PICTURE )
637 video_PrintPicture( &p_vout->p_picture[i_picture], "vout: ..." );
641 intf_Msg("vout: --- end ---\n");
645 /* following functions are local */
647 /*******************************************************************************
648 * CheckConfiguration: check vout_CreateThread() configuration
649 *******************************************************************************
650 * Set default parameters where required. In DEBUG mode, check if configuration
652 *******************************************************************************/
653 static int CheckConfiguration( video_cfg_t *p_cfg )
656 if( !( p_cfg->i_properties & VIDEO_CFG_SIZE ) )
658 p_cfg->i_size = VOUT_HEAP_SIZE;
664 /*******************************************************************************
665 * InitThread: initialize video output thread
666 *******************************************************************************
667 * This function is called from RunThread and performs the second step of the
668 * initialization. It returns 0 on success. Note that the thread's flag are not
669 * modified inside this function.
670 *******************************************************************************/
671 static int InitThread( vout_thread_t *p_vout )
673 int i_index; /* generic index */
676 *p_vout->pi_status = THREAD_START;
678 /* Allocate video heap */
680 (picture_t *) malloc( sizeof(picture_t) * p_vout->i_max_pictures );
681 if( !p_vout->p_picture ) /* error */
683 intf_ErrMsg("vout error: %s\n", strerror(ENOMEM));
684 intf_DbgMsg("%p -> failed\n", p_vout);
685 *p_vout->pi_status = THREAD_ERROR;
689 /* Initialize pictures */
690 for( i_index = 0; i_index < p_vout->i_max_pictures; i_index++)
692 p_vout->p_picture[i_index].i_type = EMPTY_PICTURE;
693 p_vout->p_picture[i_index].i_flags = 0;
696 /* Initialize video streams - note that stream 0 is always active */
697 p_vout->p_stream[0].i_status = VOUT_ACTIVE_STREAM;
699 p_vout->p_stream[0].c_pictures = 0;
700 p_vout->p_stream[0].c_rendered_pictures = 0;
702 for( i_index = 1; i_index < VOUT_MAX_STREAMS; i_index++ )
704 p_vout->p_stream[i_index].i_status = VOUT_INACTIVE_STREAM;
707 /* Initialize other properties */
708 p_vout->i_pictures = 0;
711 p_vout->c_idle_loops = 0;
712 p_vout->c_pictures = 0;
713 p_vout->c_rendered_pictures = 0;
716 /* Initialize output method - width, height, screen depth and bytes per
717 * pixel are initialized by this call. */
718 if( vout_X11CreateOutputMethod( p_vout ) ) /* error */
720 free( p_vout->p_picture );
721 *p_vout->pi_status = THREAD_ERROR;
725 /* Mark thread as running and return */
726 *p_vout->pi_status = THREAD_READY;
727 intf_DbgMsg("%p -> succeeded\n", p_vout);
731 /*******************************************************************************
732 * RunThread: video output thread
733 *******************************************************************************
734 * Video output thread. This function does only returns when the thread is
735 * terminated. It handles the pictures arriving in the video heap and the
736 * display device events.
737 *******************************************************************************/
738 static void RunThread( vout_thread_t *p_vout)
740 int i_stream; /* stream index */
741 int i_picture; /* picture index */
742 int i_active_streams; /* number of active streams */
743 int i_err; /* error code */
744 mtime_t display_date; /* display date */
745 mtime_t current_date; /* current date */
746 picture_t * pp_sorted_picture[VOUT_MAX_PICTURES]; /* sorted pictures */
748 char sz_date[MSTRTIME_MAX_SIZE]; /* date buffer */
752 * Initialize thread and free configuration
754 p_vout->b_error = InitThread( p_vout );
755 if( p_vout->b_error )
757 free( p_vout ); /* destroy descriptor */
762 * Main loop - it is not executed if an error occured during
765 while( (!p_vout->b_die) && (!p_vout->b_error) )
767 /* Get locks on pictures and streams */
768 vlc_mutex_lock( &p_vout->streams_lock );
769 vlc_mutex_lock( &p_vout->pictures_lock );
771 /* Initialise streams: clear images from all streams */
772 for( i_stream = 0; i_stream < VOUT_MAX_STREAMS; i_stream++ )
774 p_vout->p_stream[i_stream].p_next_picture = NULL;
777 /* First pass: a set of pictures is selected - all unselected pictures
778 * which should be removed are removed, and a display date is computed.
779 * If a stream has no next_picture after this step, then the heap does
780 * not include any picture from that stream. */
781 current_date = mdate();
782 display_date = FirstPass( p_vout, current_date );
784 /* Stream management: streams which are in ENDING or DESTROYED state and
785 * which have no more pictures are destroyed */
786 for( i_active_streams = i_stream = 1;
787 i_stream < VOUT_MAX_STREAMS;
790 switch( p_vout->p_stream[i_stream].i_status )
792 case VOUT_ACTIVE_STREAM: /* active stream */
796 case VOUT_ENDING_STREAM: /* ending or destroyed streams */
797 case VOUT_DESTROYED_STREAM:
798 /* Those streams can be destroyed if there are no more picture
799 * remaining. This should always be the case for destroyed
800 * streams, except if it includes permanent pictures */
801 if( p_vout->p_stream[i_stream].p_next_picture == NULL )
803 p_vout->p_stream[i_stream].i_status = VOUT_INACTIVE_STREAM;
805 intf_DbgMsg("%p -> stream %d destroyed %d pictures, %d rendered pictures\n",
806 p_vout, i_stream, p_vout->p_stream[i_stream].c_pictures,
807 p_vout->p_stream[i_stream].c_rendered_pictures );
809 intf_DbgMsg("%p -> stream %d destroyed\n", p_vout, i_stream );
812 /* If the stream can't be destroyed, it means it is still
813 * active - in that case, the next image pointer needs to be
818 p_vout->p_stream[i_stream].p_next_picture = NULL;
824 /* From now until next loop, only next_picture field in streams
825 * will be used, and selected pictures structures won't modified.
826 * Therefore, locks can be released */
827 vlc_mutex_unlock( &p_vout->pictures_lock );
828 vlc_mutex_unlock( &p_vout->streams_lock );
830 /* If there are some pictures to display, then continue */
831 if( display_date != LAST_MDATE )
833 /* Increase display_date if it is too low */
834 if( display_date < current_date + VOUT_DISPLAY_DELAY )
836 intf_Msg("vout: late picture(s) detected\n");
837 display_date = current_date + VOUT_DISPLAY_DELAY;
840 intf_DbgMsg("%p -> display_date is %s\n", p_vout,
841 mstrtime( sz_date, display_date ));
844 /* Second pass: a maximum of one picture per stream is selected
845 * (except for stream 0), and some previously selected pictures may
847 SecondPass( p_vout, display_date );
849 vout_PrintHeap( p_vout, "ready for rendering" );
852 /* Rendering: sort pictures and render them */
853 SortPictures( p_vout, pp_sorted_picture );
854 for( i_picture = 0; pp_sorted_picture[i_picture] != NULL; i_picture++ )
857 RenderPicture( p_vout, pp_sorted_picture[i_picture] );
860 /* Handle output method events - this function can return a negative
861 * value, which means a fatal error and the end of the display loop
862 * (i.e. the X11 window has been destroyed), a positive one, meaning
863 * a modification occured in the pictures and they do have need to
864 * be displayed, or 0 */
865 i_err = vout_X11ManageOutputMethod( p_vout );
868 /* Sleep and display */
869 mwait( display_date );
870 vout_X11DisplayOutput( p_vout );
874 /* An error occured, and the thread must terminate immediately,
875 * without displaying anything - setting b_error to 1 cause the
876 * immediate end of the main while() loop. */
880 /* If there is nothing to display, just handle events and sleep a while,
881 * hopping there will be something to do later */
884 if( vout_X11ManageOutputMethod( p_vout ) < 0)
891 msleep( VOUT_IDLE_SLEEP );
894 /* Update counters */
895 p_vout->c_idle_loops++;
900 /* Update counters */
908 if( p_vout->b_error )
910 ErrorThread( p_vout );
917 /*******************************************************************************
918 * ErrorThread: RunThread() error loop
919 *******************************************************************************
920 * This function is called when an error occured during thread main's loop. The
921 * thread can still receive feed, but must be ready to terminate as soon as
923 *******************************************************************************/
924 static void ErrorThread( vout_thread_t *p_vout )
926 int i_picture; /* picture index */
928 /* Wait until a `die' order */
929 while( !p_vout->b_die )
931 /* Get lock on pictures */
932 vlc_mutex_lock( &p_vout->pictures_lock );
934 /* Try to remove all pictures - only removable pictures will be
936 for( i_picture = 0; i_picture < p_vout->i_max_pictures; i_picture++ )
938 if( p_vout->p_picture[i_picture].i_type != EMPTY_PICTURE )
940 EmptyPicture( p_vout, &p_vout->p_picture[i_picture] );
944 /* Release locks on pictures */
945 vlc_mutex_unlock( &p_vout->pictures_lock );
948 msleep( VOUT_IDLE_SLEEP );
952 /*******************************************************************************
953 * EndThread: thread destruction
954 *******************************************************************************
955 * This function is called when the thread ends after a sucessfull
957 *******************************************************************************/
958 static void EndThread( vout_thread_t *p_vout )
960 int * pi_status; /* thread status */
962 int i_stream; /* video stream index */
966 pi_status = p_vout->pi_status;
967 *pi_status = THREAD_END;
970 /* Check for remaining pictures or video streams */
971 if( p_vout->i_pictures )
973 intf_DbgMsg("%p -> remaining pictures\n", p_vout);
976 (i_stream < VOUT_MAX_STREAMS) && (p_vout->p_stream[i_stream].i_status == VOUT_INACTIVE_STREAM);
981 if( i_stream != VOUT_MAX_STREAMS )
983 intf_DbgMsg("%p -> remaining video streams\n", p_vout);
987 /* Destroy thread structures allocated by InitThread */
988 vout_X11DestroyOutputMethod( p_vout ); /* destroy output method */
989 free( p_vout->p_picture ); /* free heap */
993 *pi_status = THREAD_OVER;
994 intf_DbgMsg("%p\n", p_vout);
997 /*******************************************************************************
998 * FindPicture: find an empty picture in a video heap
999 *******************************************************************************
1000 * This function is used by most of the vout_*Picture*() functions. It locks the
1001 * video heap, look for an empty picture in it and return a pointer to this
1002 * picture, or NULL if the heap is full.
1003 * Not that the heap is not unlocked when this function returns, and it needs
1004 * to be donne by the calling function.
1005 *******************************************************************************/
1006 static picture_t *FindPicture( vout_thread_t *p_vout )
1008 int i_picture; /* picture index */
1011 vlc_mutex_lock( &p_vout->pictures_lock );
1013 /* Look for an empty place */
1014 for( i_picture = 0; i_picture < p_vout->i_max_pictures; i_picture++ )
1016 /* An empty place has been found: return */
1017 if( p_vout->p_picture[i_picture].i_type == EMPTY_PICTURE )
1019 return( &p_vout->p_picture[i_picture] );
1023 /* Nothing has been found */
1027 /*******************************************************************************
1028 * EmptyPicture: empty a picture without destroying its descriptor
1029 *******************************************************************************
1030 * When a picture is no more used in video heap or must be destroyed, this
1031 * function is called to destroy associated data and set picture type to empty.
1032 * Only non permanent and unlinked pictures can be destroyed.
1033 * Only non-empty pictures should be sent to this function.
1034 * All picture flags are cleared, and the heap size is decreased.
1035 *******************************************************************************
1036 * Messages type: vout, major code 15
1037 *******************************************************************************/
1038 static void EmptyPicture( vout_thread_t *p_vout, picture_t *p_pic )
1041 /* Check if picture is non-empty */
1042 if( p_pic->i_type == EMPTY_PICTURE )
1044 intf_DbgMsg("%p -> trying to remove empty picture %p\n",
1050 /* Mark as ready for destruction */
1051 p_pic->i_flags |= DESTROY_PICTURE;
1052 p_pic->i_flags &= ~DISPLAY_PICTURE;
1054 /* If picture is no more referenced and is not permanent, destroy */
1055 if( (p_pic->i_refcount == 0) && !(p_pic->i_flags & PERMANENT_PICTURE) )
1057 /* If picture owns its data, free them */
1058 if( p_pic->i_flags & OWNER_PICTURE )
1060 free( p_pic->p_data );
1062 p_pic->i_type = EMPTY_PICTURE;
1064 p_vout->i_pictures--;
1065 intf_DbgMsg("%p -> picture %p removed\n", p_vout, p_pic);
1069 /*******************************************************************************
1070 * FirstPass: first pass of the vout thread on pictures
1071 *******************************************************************************
1072 * A set of pictures is selected according to the current date and their display
1073 * date. Some unselected and already displayed pictures are removed, and the
1074 * next display date is computed.
1075 * The next_picture fields of all the streams are updated. Note that streams
1076 * are initialized before this function is called.
1077 *******************************************************************************
1078 * Messages type: vout, major code 18
1079 *******************************************************************************/
1080 static mtime_t FirstPass( vout_thread_t *p_vout, mtime_t current_date )
1082 mtime_t display_date; /* display date */
1083 int i_picture; /* picture index */
1084 picture_t * p_picture; /* picture pointer (shortcut) */
1087 display_date = LAST_MDATE;
1089 /* Browse all pictures */
1090 for( i_picture = 0; i_picture < p_vout->i_max_pictures; i_picture++ )
1092 p_picture = &p_vout->p_picture[i_picture];
1094 /* Empty pictures are never selected */
1095 if( p_picture->i_type == EMPTY_PICTURE )
1099 /* Destroyed pictures are never selected, they can be emptied if needed */
1100 else if( p_picture->i_flags & DESTROY_PICTURE )
1102 EmptyPicture( p_vout, p_picture );
1105 /* Pictures in an inactive stream are submitted to a deletion attempt,
1106 * and nether selected - this case should never happen */
1107 else if( p_vout->p_stream[ p_picture->i_stream ].i_status == VOUT_INACTIVE_STREAM )
1109 intf_DbgMsg("%p -> picture %p belongs to an inactive stream\n",
1111 EmptyPicture( p_vout, p_picture );
1114 /* Pictures in a destroyed stream are submitted to a deletion attempt,
1115 * and nether selected */
1116 else if( p_vout->p_stream[ p_picture->i_stream ].i_status == VOUT_DESTROYED_STREAM )
1118 EmptyPicture( p_vout, p_picture );
1120 /* Reserved pictures are never selected */
1121 else if( p_picture->i_flags & RESERVED_PICTURE )
1123 p_picture->i_flags &= ~DISPLAY_PICTURE;
1125 /* Overlay pictures */
1126 else if( p_picture->i_flags & OVERLAY_PICTURE )
1128 /* A picture is outdated if it is not permanent and if it's maximal
1130 if( !(p_picture->i_flags & PERMANENT_PICTURE )
1131 && (p_picture->date + p_picture->duration < current_date) )
1133 p_picture->i_flags &= ~DISPLAY_PICTURE;
1134 EmptyPicture( p_vout, p_picture );
1136 /* Else if picture is in stream 0, it will always be selected */
1137 else if( p_picture->i_stream == 0 )
1139 p_picture->i_flags |= DISPLAY_PICTURE;
1140 /* Update display_date if picture has never been displayed */
1141 if( !(p_picture->i_flags & DISPLAYED_PICTURE) && (p_picture->date < display_date) )
1143 display_date = p_picture->date;
1146 /* The picture can be considered as a regular picture, because it
1147 * has never been displayed */
1148 else if( !(p_picture->i_flags & DISPLAYED_PICTURE) )
1150 /* Select picture if can be updated */
1151 if( (p_vout->p_stream[ p_picture->i_stream].p_next_picture == NULL)
1152 || (p_vout->p_stream[p_picture->i_stream].p_next_picture->date > p_picture->date))
1154 p_picture->i_flags |= DISPLAY_PICTURE;
1155 p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;
1156 /* Update display_date */
1157 if( p_picture->date < display_date )
1159 display_date = p_picture->date;
1163 /* In other cases (overlay pictures which have already been displayed),
1164 * the picture is always selected */
1167 p_picture->i_flags |= DISPLAY_PICTURE;
1168 /* The stream is only updated if there is no picture in that stream */
1169 if( p_vout->p_stream[ p_picture->i_stream].p_next_picture == NULL )
1171 p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;
1175 /* Non overlay pictures are deleted if they have been displayed, and
1176 * selected if there date is valid */
1179 /* Remove already displayed pictures */
1180 if( p_picture->i_flags & DISPLAYED_PICTURE )
1182 EmptyPicture( p_vout, p_picture );
1184 /* Remove passed pictures, The picture is marked as displayed in case it
1185 * could not be removed */
1186 else if( p_picture->date < current_date )
1188 intf_DbgMsg("%p -> picture %p detected late\n",
1189 p_vout, p_picture );
1190 p_picture->i_flags |= DISPLAYED_PICTURE;
1191 EmptyPicture( p_vout, p_picture );
1193 /* Update streams for other pictures */
1196 p_picture->i_flags |= DISPLAY_PICTURE;
1197 /* Update 'next picture' field in stream descriptor */
1198 if( (p_vout->p_stream[ p_picture->i_stream].p_next_picture == NULL)
1199 || (p_vout->p_stream[p_picture->i_stream].p_next_picture->date > p_picture->date))
1201 p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;
1202 /* Update display_date */
1203 if( p_picture->date < display_date )
1205 display_date = p_picture->date;
1212 return( display_date );
1215 /*******************************************************************************
1216 * SecondPass: second pass of the vout thread on pictures
1217 *******************************************************************************
1218 * Select only one picture per stream other than stream 0, and remove pictures
1219 * which should have been displayed.... but arrived too late. Note that nothing
1220 * is locked when this function is called, and therefore pictures should not
1222 * Only previously selected pictures are processed.
1223 *******************************************************************************
1224 * Messages type: vout, major code 19
1225 *******************************************************************************/
1226 static void SecondPass( vout_thread_t *p_vout, mtime_t display_date )
1228 int i_picture; /* picture index */
1229 picture_t * p_picture; /* picture pointer (shortcut) */
1231 for( i_picture = 0; i_picture < p_vout->i_max_pictures; i_picture++ )
1233 p_picture = &p_vout->p_picture[i_picture];
1235 /* Process only previously selected pictures */
1236 if( p_picture->i_flags & DISPLAY_PICTURE )
1238 /* Deselect picture if it is too youg */
1239 if( p_picture->date > display_date + VOUT_DISPLAY_TOLERANCE )
1241 p_picture->i_flags &= ~DISPLAY_PICTURE;
1243 /* Pictures in stream 0 are selected depending only of their date */
1244 else if( p_picture->i_stream == 0 )
1246 /* Overlay pictures are not selected if they are
1248 if( p_picture->i_flags & OVERLAY_PICTURE )
1250 if( !(p_picture->i_flags & PERMANENT_PICTURE)
1251 && (display_date > p_picture->date + p_picture->duration) )
1253 p_picture->i_flags |= DESTROY_PICTURE;
1254 p_picture->i_flags &= ~DISPLAY_PICTURE;
1257 /* Regular pictures are selected if they are not too late */
1258 else if( p_picture->date < display_date - VOUT_DISPLAY_TOLERANCE )
1260 intf_DbgMsg("%p -> picture %p detected late\n",
1261 p_vout, p_picture );
1262 p_picture->i_flags |= DISPLAYED_PICTURE | DESTROY_PICTURE;
1263 p_picture->i_flags &= ~DISPLAY_PICTURE;
1266 /* Overlay pictures which have been displayed have special
1268 else if( (p_picture->i_flags & OVERLAY_PICTURE)
1269 && (p_picture->i_flags & DISPLAYED_PICTURE) )
1271 /* If the stream is not empty, or the picture has been
1272 * outdated, de select */
1273 if( (p_vout->p_stream[p_picture->i_stream].p_next_picture != NULL)
1274 || (!(p_picture->i_flags & PERMANENT_PICTURE)
1275 && (display_date > p_picture->date + p_picture->duration) ) )
1277 p_picture->i_flags |= DESTROY_PICTURE;
1278 p_picture->i_flags &= ~DISPLAY_PICTURE;
1281 /* Other pictures are 'regular' pictures */
1284 /* If the stream is empty, always select */
1285 if( p_vout->p_stream[p_picture->i_stream].p_next_picture == NULL)
1287 p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;
1289 /* Else, remove and mark as displayed (skip) if the picture is too old */
1290 else if( p_picture->date < display_date - VOUT_DISPLAY_TOLERANCE )
1292 intf_DbgMsg("%p -> picture %p detected late\n",
1293 p_vout, p_picture );
1294 p_picture->i_flags |= DISPLAYED_PICTURE | DESTROY_PICTURE;
1295 p_picture->i_flags &= ~DISPLAY_PICTURE;
1297 /* Else, select if the picture is younger than the current selected one */
1298 else if( p_picture->date
1299 < p_vout->p_stream[p_picture->i_stream].p_next_picture->date )
1301 /* Deselect the previous picture */
1302 p_vout->p_stream[p_picture->i_stream].p_next_picture->i_flags
1303 &= ~DISPLAY_PICTURE;
1304 /* Select the current one */
1305 p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;
1307 /* Else, select if the current selected one is an already displayed overlay */
1308 else if( (p_vout->p_stream[p_picture->i_stream].p_next_picture->i_flags
1309 & ( OVERLAY_PICTURE | DISPLAYED_PICTURE ))
1310 == (OVERLAY_PICTURE | DISPLAYED_PICTURE) )
1313 /* Deselect and remove the previous picture */
1314 p_picture->i_flags |= DESTROY_PICTURE;
1315 p_picture->i_flags &= ~DISPLAY_PICTURE;
1316 /* Select the current one */
1317 p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;
1319 /* Else, deselect the picture */
1322 p_picture->i_flags &= ~DISPLAY_PICTURE;
1329 /*******************************************************************************
1330 * SortPictures: sort pictures ready for display by hierarchical level
1331 *******************************************************************************
1332 * Build an array of pictures in rendering order, starting from minimal to
1333 * level to maximal level. A NULL pointer always ends the array, which size is
1334 * limited to VOUT_MAX_PICTURES.
1335 *******************************************************************************
1336 * Messages type: vout, major code 26
1337 *******************************************************************************/
1338 static void SortPictures( vout_thread_t *p_vout, picture_t **pp_picture )
1340 int i_picture; /* picture index in sorted array */
1341 int i_heap_picture; /* picture index in heap */
1342 picture_t * p_previous_picture; /* first shift register */
1343 picture_t * p_current_picture; /* second shift register */
1345 /* Initialize array */
1346 pp_picture[0] = NULL;
1348 /* Copy and sort pictures */
1349 for( i_heap_picture = 0; i_heap_picture < p_vout->i_max_pictures ; i_heap_picture++ )
1351 /* Sort only selected pictures */
1352 if( p_vout->p_picture[ i_heap_picture ].i_flags & DISPLAY_PICTURE )
1354 /* Find picture position */
1355 for( i_picture = 0; (pp_picture[i_picture] != NULL)
1356 && (pp_picture[i_picture]->i_level
1357 <= p_vout->p_picture[ i_heap_picture ].i_level);
1362 p_current_picture = &p_vout->p_picture[ i_heap_picture ];
1364 /* Insert picture and shift end of array */
1365 for( ; p_previous_picture != NULL; i_picture++ )
1367 p_previous_picture = p_current_picture;
1368 p_current_picture = pp_picture[i_picture];
1369 if( i_picture == VOUT_MAX_PICTURES - 1 )
1371 p_previous_picture = NULL;
1373 pp_picture[i_picture] = p_previous_picture;
1380 /*******************************************************************************
1381 * RenderPicture: render a picture
1382 *******************************************************************************
1383 * This function convert a picture from a video heap to a pixel-encoded image
1384 * and copy it to the current rendering buffer. No lock is required, since the
1385 * rendered picture has been determined as existant, and will only be destroyed
1386 * by the vout_Thread() later.
1387 *******************************************************************************
1388 * Messages types: vout, major code 27
1389 *******************************************************************************/
1390 static void RenderPicture( vout_thread_t *p_vout, picture_t *p_pic )
1392 int i_x, i_y; /* position in display */
1393 int i_pic_x; /* x offset in clipped picture */
1394 int i_pic_y; /* y offset in clipped picture */
1395 int i_height, i_width; /* clipped picture dimensions */
1396 int i_line_width; /* line width */
1397 void (*RenderLine)(vout_thread_t *p_vout, picture_t *p_pic, /* renderer */
1398 int i_x, int i_y, int i_pic_x, int i_pic_y, int i_width,
1399 int i_line_width, int i_ratio );
1401 /* Update counters - this is done now since for blank pictures, the function
1402 * may never reach it's regular end */
1403 p_pic->i_flags |= DISPLAYED_PICTURE;
1405 p_vout->c_rendered_pictures++;
1406 p_vout->p_stream[ p_pic->i_stream ].c_rendered_pictures++;
1409 /* Computes position and size of the picture zone to render */
1412 i_width = p_pic->i_width;
1413 i_height = p_pic->i_height;
1414 ClipPicture( &i_x, p_vout->i_width, &i_pic_x, &i_width, p_pic->i_h_ratio, p_pic->i_h_align );
1415 ClipPicture( &i_y, p_vout->i_height, &i_pic_y, &i_height, p_pic->i_v_ratio, p_pic->i_v_align );
1417 intf_DbgMsg("%p -> picture %p, (%d-%d:%d-%d, %dx%d)\n",
1418 p_vout, p_pic, i_x, i_pic_x, i_y, i_pic_y, i_width, i_height );
1422 /* If there is noting to display, returns immediately */
1423 if( (i_pic_x >= i_width) || (i_pic_y >= i_height) )
1428 /* Determine method used to render line. This function is choosed here to
1429 * avoid multiple conditions tests later */
1430 switch( p_pic->i_type )
1432 case RGB_BLANK_PICTURE: /* picture is blank, RGB encoded (no data) */
1433 case PIXEL_BLANK_PICTURE: /* picture is blank, pixel encoded (no data) */
1434 /* Blank pictures are displayed immediately - dimensions are real ones,
1435 * and should be recalculated */
1436 switch( p_pic->i_h_ratio )
1438 case DISPLAY_RATIO_HALF: /* 1:2 half size */
1439 i_width = (i_width - i_pic_x) / 2;
1441 case DISPLAY_RATIO_NORMAL: /* 1:1 normal size */
1444 case DISPLAY_RATIO_DOUBLE: /* 2:1 double size */
1445 i_width = (i_width - i_pic_x) * 2;
1447 /* ?? add others display ratio */
1449 switch( p_pic->i_v_ratio )
1451 case DISPLAY_RATIO_HALF: /* 1:2 half size */
1452 i_height = (i_height - i_pic_y) / 2;
1454 case DISPLAY_RATIO_NORMAL: /* 1:1 normal size */
1455 i_height -= i_pic_y;
1457 case DISPLAY_RATIO_DOUBLE: /* 2:1 double size */
1458 i_height = (i_height - i_pic_y) * 2;
1460 /* ?? add others display ratio */
1462 if( p_pic->i_type == RGB_BLANK_PICTURE ) /* RGB blank picture */
1464 p_vout->RenderRGBBlank( p_vout, p_pic->pixel, i_x, i_y, i_width, i_height );
1466 else /* pixel blank picture */
1468 p_vout->RenderPixelBlank( p_vout, p_pic->pixel, i_x, i_y, i_width, i_height );
1472 case RGB_PICTURE: /* picture is 24 bits rgb encoded */
1473 RenderLine = p_vout->RenderRGBLine;
1475 case PIXEL_PICTURE: /* picture is pixel encoded */
1476 RenderLine = p_vout->RenderPixelLine;
1478 case RGB_MASK_PICTURE: /* picture is a 1 rgb bpp mask */
1479 RenderLine = p_vout->RenderRGBMaskLine;
1481 case PIXEL_MASK_PICTURE: /* picture is a 1 pixel bpp mask */
1482 RenderLine = p_vout->RenderPixelMaskLine;
1484 /* ?? add YUV types */
1486 default: /* internal error, which should never happen */
1487 intf_DbgMsg("%p -> unknown type for picture %p\n", p_vout, p_pic);
1492 /* For non blank pictures, loop on lines */
1493 for( ; i_pic_y < i_height; i_pic_y++ )
1495 /* First step: check if line has to be rendered. This is not obvious since
1496 * if display ratio is less than 1:1, some of the lines don't need to
1497 * be displayed, and therefore do not need to be rendered. */
1498 switch( p_pic->i_v_ratio )
1500 case DISPLAY_RATIO_HALF: /* 1:2 half size */
1501 /* Only even lines are rendered, and copied once */
1502 i_line_width = i_pic_y % 2;
1505 case DISPLAY_RATIO_NORMAL: /* 1:1 normal size */
1506 /* All lines are rendered and copied once */
1510 case DISPLAY_RATIO_DOUBLE: /* 2:1 double size */
1511 /* All lines are rendered and copied twice */
1518 /* Computed line width can be reduced if it would cause to render
1519 * outside the display */
1520 if( i_y + i_line_width > p_vout->i_height )
1522 i_line_width = p_vout->i_height - i_y;
1525 /* Second step: render line. Since direct access to the rendering
1526 * buffer is required, functions in charge of rendering the line
1527 * are part of the display driver. Because this step is critical
1528 * and require high optimization, different methods are used
1529 * depending of the horizontal display ratio, the image type and
1530 * the screen depth. */
1531 RenderLine( p_vout, p_pic, i_x, i_y, i_pic_x, i_pic_y,
1532 i_width, i_line_width, p_pic->i_h_ratio );
1534 /* Increment display line index */
1535 i_y += i_line_width;
1540 /*******************************************************************************
1541 * ClipPicture: clip a picture in display window
1542 *******************************************************************************
1543 * This function computes picture placement in display window and rendering
1544 * zone, according to wished picture placement and display ratio. It must be
1545 * called twice, once and with the x coordinates and once with the y
1547 * The pi_pic_ofs parameter is only written, but pi_ofs and pi_pic_size must
1548 * be valid arguments. Note that *pi_pic_size is still the rendering zone size,
1549 * starting from picture offset 0 and not the size starting from *pi_pic_ofs
1550 * (same thing for i_size).
1551 *******************************************************************************
1552 * Messages types: vout, major code 28
1553 *******************************************************************************/
1554 static void ClipPicture( int *pi_ofs, int i_size, int *pi_pic_ofs, int *pi_pic_size,
1555 int i_ratio, int i_placement )
1557 int i_ofs; /* temporary picture position */
1559 /* Computes base picture position */
1560 switch( i_placement )
1562 case -1: /* left/top aligned */
1565 case 0: /* centered */
1566 i_ofs = *pi_ofs + (i_size - *pi_pic_size) / 2;
1568 case 1: /* right/bottom aligned */
1569 i_ofs = *pi_ofs + i_size - *pi_pic_size;
1572 default: /* internal error, which should never happen */
1573 intf_DbgMsg("invalid placement\n");
1578 /* Computes base rendering position and update picture position - i_ofs is
1579 * the picture position, and can be negative */
1580 if( i_ofs < 0 ) /* picture starts outside the screen */
1584 case DISPLAY_RATIO_HALF: /* 1:2 half size */
1585 *pi_pic_ofs = - *pi_ofs * 2;
1587 case DISPLAY_RATIO_NORMAL: /* 1:1 normal size */
1588 *pi_pic_ofs = -*pi_ofs;
1590 case DISPLAY_RATIO_DOUBLE: /* 2:1 double size */
1591 *pi_pic_ofs = -CEIL(*pi_ofs, 2);
1594 default: /* internal error, which should never happen */
1595 intf_DbgMsg("unsupported ratio\n");
1601 else /* picture starts inside the screen */
1607 /* Computes rendering size - i_ofs is the picture position, and can be
1608 * negative, *pi_ofs is the real picture position, and is always positive */
1611 case DISPLAY_RATIO_HALF: /* 1:2 half size */
1612 if( i_ofs + CEIL(*pi_pic_size, 2) > i_size )
1614 *pi_pic_size = ( i_size - i_ofs ) * 2;
1617 case DISPLAY_RATIO_NORMAL: /* 1:1 normal size */
1618 if( i_ofs + *pi_pic_size > i_size )
1620 *pi_pic_size = i_size - i_ofs;
1623 case DISPLAY_RATIO_DOUBLE: /* 2:1 double size */
1624 if( *pi_ofs + *pi_pic_size * 2 > i_size )
1626 *pi_pic_size = ( i_size - i_ofs ) / 2;
1630 default: /* internal error, which should never happen */
1631 intf_DbgMsg("unsupported ratio\n");