Commit a57bfee7 authored by Sam Hocevar's avatar Sam Hocevar

. le d�codeur de sous-titres s'appelle maintenant spu_decoder

 . auto spawn du spu_decoder (pour le moment �a chie)
parent b25b4229
......@@ -213,7 +213,7 @@ ac3_decoder_obj = ac3_decoder/ac3_decoder.o \
audio_decoder_obj = audio_decoder/audio_decoder.o \
audio_decoder/audio_math.o
subtitle_decoder_obj = subtitle_decoder/subtitle_decoder.o
spu_decoder_obj = spu_decoder/spu_decoder.o
#??generic_decoder_obj = generic_decoder/generic_decoder.o
# remeber to add it to OBJ
......@@ -253,7 +253,7 @@ C_OBJ = $(interface_obj) \
$(video_output_obj) \
$(ac3_decoder_obj) \
$(audio_decoder_obj) \
$(subtitle_decoder_obj) \
$(spu_decoder_obj) \
$(generic_decoder_obj) \
$(video_parser_obj) \
$(video_decoder_obj) \
......
......@@ -148,6 +148,7 @@ typedef struct es_descriptor_t
#define MPEG1_AUDIO_ES 0x03
#define MPEG2_AUDIO_ES 0x04
#define AC3_AUDIO_ES 0x81
#define DVD_SPU_ES 0x82 /* 0x82 might violate the norm */
/******************************************************************************
* program_descriptor_t
......
/******************************************************************************
* subtitle_decoder.h : subtitle decoder thread interface
* spu_decoder.h : sub picture unit decoder thread interface
* (c)1999 VideoLAN
******************************************************************************/
/******************************************************************************
* subtdec_thread_t : subtitle decoder thread descriptor
* spudec_thread_t : sub picture unit decoder thread descriptor
******************************************************************************/
typedef struct subtdec_thread_s
typedef struct spudec_thread_s
{
/*
* Thread properties and locks
......@@ -30,10 +30,10 @@ typedef struct subtdec_thread_s
unsigned int total_bits_read;
/* ... */
} subtdec_thread_t;
} spudec_thread_t;
/******************************************************************************
* Prototypes
******************************************************************************/
subtdec_thread_t * subtdec_CreateThread( input_thread_t * p_input );
void subtdec_DestroyThread( subtdec_thread_t * p_subtdec );
spudec_thread_t * spudec_CreateThread( input_thread_t * p_input );
void spudec_DestroyThread( spudec_thread_t * p_spudec );
......@@ -87,35 +87,35 @@ typedef struct picture_s
#define AR_221_1_PICTURE 4 /* 2.21:1 picture (movie) */
/*******************************************************************************
* subtitle_t: video subtitle
* spu_t: video sub picture unit
*******************************************************************************
* Any subtitle destined to be displayed by a video output thread should be
* stored in this structure from it's creation to it's effective display.
* Any sub picture unit destined to be displayed by a video output thread should
* be stored in this structure from it's creation to it's effective display.
* Subtitle type and flags should only be modified by the output thread. Note
* that an empty subtitle MUST have its flags set to 0.
*******************************************************************************/
typedef struct subtitle_s
typedef struct spu_s
{
/* Type and flags - should NOT be modified except by the vout thread */
int i_type; /* subtitle type */
int i_status; /* subtitle flags */
int i_type; /* spu type */
int i_status; /* spu flags */
/* Other properties */
mtime_t begin_date; /* beginning of display date */
mtime_t end_date; /* end of display date */
/* Subtitle data - data can always be freely modified. p_data itself
/* Sub picture unit data - data can always be freely modified. p_data itself
* (the pointer) should NEVER be modified. */
void * p_data; /* subtitle data */
} subtitle_t;
void * p_data; /* spu data */
} spu_t;
/* Subtitle types */
#define EMPTY_SUBTITLE 0 /* subtitle slot is empty and available */
#define RLE_SUBTITLE 100 /* RLE encoded subtitle */
/* SPU types */
#define EMPTY_SPU 0 /* subtitle slot is empty and available */
#define RLE_SPU 100 /* RLE encoded subtitle */
/* Subtitle status */
#define FREE_SUBTITLE 0 /* subtitle is free and not allocated */
#define RESERVED_SUBTITLE 1 /* subtitle is allocated and reserved */
#define READY_SUBTITLE 2 /* subtitle is ready for display */
#define DESTROYED_SUBTITLE 3 /* subtitle is allocated but no more used */
/* Subpicture status */
#define FREE_SPU 0 /* subtitle is free and not allocated */
#define RESERVED_SPU 1 /* subtitle is allocated and reserved */
#define READY_SPU 2 /* subtitle is ready for display */
#define DESTROYED_SPU 3 /* subtitle is allocated but no more used */
......@@ -85,7 +85,7 @@ typedef struct vout_thread_s
boolean_t b_active; /* `active' flag */
vlc_thread_t thread_id; /* id for pthread functions */
vlc_mutex_t picture_lock; /* picture heap lock */
vlc_mutex_t subtitle_lock; /* subtitle heap lock */
vlc_mutex_t spu_lock; /* subtitle heap lock */
vlc_mutex_t change_lock; /* thread change lock */
int * pi_status; /* temporary status flag */
p_vout_sys_t p_sys; /* system output method */
......@@ -123,7 +123,7 @@ typedef struct vout_thread_s
/* Videos heap and translation tables */
picture_t p_picture[VOUT_MAX_PICTURES]; /* pictures */
subtitle_t p_subtitle[VOUT_MAX_PICTURES]; /* subtitles */
spu_t p_spu[VOUT_MAX_PICTURES]; /* subtitles */
vout_tables_t tables; /* translation tables */
vout_convert_t * p_ConvertYUV420; /* YUV 4:2:0 converter */
vout_convert_t * p_ConvertYUV422; /* YUV 4:2:2 converter */
......@@ -158,9 +158,9 @@ void vout_DisplayPicture ( vout_thread_t *p_vout, picture_t *p_pi
void vout_DatePicture ( vout_thread_t *p_vout, picture_t *p_pic, mtime_t date );
void vout_LinkPicture ( vout_thread_t *p_vout, picture_t *p_pic );
void vout_UnlinkPicture ( vout_thread_t *p_vout, picture_t *p_pic );
subtitle_t * vout_CreateSubtitle ( vout_thread_t *p_vout, int i_type, int i_size );
void vout_DestroySubtitle ( vout_thread_t *p_vout, subtitle_t *p_sub );
void vout_DisplaySubtitle ( vout_thread_t *p_vout, subtitle_t *p_sub );
spu_t * vout_CreateSubPictureUnit ( vout_thread_t *p_vout, int i_type, int i_size );
void vout_DestroySubPictureUnit ( vout_thread_t *p_vout, spu_t *p_spu );
void vout_DisplaySubPictureUnit ( vout_thread_t *p_vout, spu_t *p_spu );
void vout_ClearBuffer ( vout_thread_t *p_vout, vout_buffer_t *p_buffer );
......
......@@ -68,6 +68,9 @@
#include "audio_decoder.h"
#include "ac3_decoder.h"
/* Subtitles */
#include "spu_decoder.h"
/* Video */
#include "video.h"
#include "video_output.h"
......
......@@ -412,6 +412,10 @@ static void EndThread( input_thread_t * p_input )
case AC3_AUDIO_ES:
ac3dec_DestroyThread( (ac3dec_thread_t *)(p_input->pp_selected_es[i_es_loop]->p_dec) );
break;
case DVD_SPU_ES:
fprintf(stderr, "input.h : destroying spudec\n");
spudec_DestroyThread( (spudec_thread_t *)(p_input->pp_selected_es[i_es_loop]->p_dec) );
break;
case 0:
/* Special streams for the PSI decoder, PID 0 and 1 */
break;
......@@ -1064,6 +1068,13 @@ static __inline__ void input_DemuxPES( input_thread_t *p_input,
p_fifo = &(((ac3dec_thread_t *)(p_es_descriptor->p_dec))->fifo);
break;
case DVD_SPU_ES:
/* we skip 4 bytes at the beginning of the subpicture payload */
p_ts->i_payload_start += 4;
fprintf(stderr, "input.h : launching spudec\n");
p_fifo = &(((spudec_thread_t *)(p_es_descriptor->p_dec))->fifo);
break;
default:
/* This should never happen */
intf_DbgMsg("Unknown stream type (%d, %d): PES trashed\n",
......
......@@ -112,6 +112,17 @@ int input_AddPgrmElem( input_thread_t *p_input, int i_current_id )
}
break;
case DVD_SPU_ES:
/* Spawn spu thread */
if ( ((spudec_thread_t *)(p_input->p_es[i_es_loop].p_dec) =
spudec_CreateThread(p_input)) == NULL )
{
intf_ErrMsg( "Could not start subtitle decoder\n" );
vlc_mutex_unlock( &p_input->es_lock );
return( -1 );
}
break;
case MPEG1_AUDIO_ES:
case MPEG2_AUDIO_ES:
/* Spawn audio thread. */
......@@ -156,7 +167,7 @@ int input_AddPgrmElem( input_thread_t *p_input, int i_current_id )
/* Initialise the demux */
p_input->p_es[i_es_loop].p_pes_packet = NULL;
p_input->p_es[i_es_loop].i_continuity_counter = 0xFF;
p_input->p_es[i_es_loop].i_continuity_counter = 0xff;
p_input->p_es[i_es_loop].b_random = 0;
/* Mark stream to be demultiplexed. */
......@@ -217,6 +228,10 @@ int input_DelPgrmElem( input_thread_t *p_input, int i_current_id )
ac3dec_DestroyThread( (ac3dec_thread_t *)(p_input->pp_selected_es[i_selected_es_loop]->p_dec) );
break;
case DVD_SPU_ES:
spudec_DestroyThread( (spudec_thread_t *)(p_input->pp_selected_es[i_selected_es_loop]->p_dec) );
break;
case MPEG1_AUDIO_ES:
case MPEG2_AUDIO_ES:
adec_DestroyThread( (adec_thread_t*)(p_input->pp_selected_es[i_selected_es_loop]->p_dec) );
......
......@@ -649,6 +649,15 @@ static void DecodePgrmMapSection( u8* p_pms, input_thread_t* p_input )
}
break;
case DVD_SPU_ES:
if ( p_main->b_audio )
{
/* Spawn a spu decoder thread */
input_AddPgrmElem( p_input,
p_input->p_es[i_es_loop].i_id );
}
break;
case MPEG1_AUDIO_ES:
case MPEG2_AUDIO_ES:
if( p_main->b_audio )
......
/*******************************************************************************
* spu_decoder.c : spu decoder thread
* (c)2000 VideoLAN
*******************************************************************************/
/* repomp sur video_decoder.c
* ?? passer en terminate/destroy avec les signaux supplmentaires */
/*******************************************************************************
* Preamble
*******************************************************************************/
//#include "vlc.h"
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/uio.h>
#include "config.h"
#include "common.h"
#include "mtime.h"
#include "vlc_thread.h"
#include "intf_msg.h"
#include "debug.h" /* ?? temporaire, requis par netlist.h */
#include "input.h"
#include "input_netlist.h"
#include "decoder_fifo.h"
#include "spu_decoder.h"
/*
* Local prototypes
*/
static int InitThread ( spudec_thread_t *p_spudec );
static void RunThread ( spudec_thread_t *p_spudec );
static void ErrorThread ( spudec_thread_t *p_spudec );
static void EndThread ( spudec_thread_t *p_spudec );
/******************************************************************************
* spudec_CreateThread: create a spu decoder thread
******************************************************************************/
spudec_thread_t * spudec_CreateThread( input_thread_t * p_input )
{
spudec_thread_t * p_spudec;
intf_DbgMsg("spudec debug: creating spu decoder thread\n");
fprintf(stderr, "spudec debug: creating spu decoder thread\n");
/* Allocate the memory needed to store the thread's structure */
if ( (p_spudec = (spudec_thread_t *)malloc( sizeof(spudec_thread_t) )) == NULL )
{
intf_ErrMsg("spudec error: not enough memory for spudec_CreateThread() to create the new thread\n");
return( NULL );
}
/*
* Initialize the thread properties
*/
p_spudec->b_die = 0;
p_spudec->b_error = 0;
/* Spawn the spu decoder thread */
if ( vlc_thread_create(&p_spudec->thread_id, "spu decoder",
(vlc_thread_func_t)RunThread, (void *)p_spudec) )
{
intf_ErrMsg("spudec error: can't spawn spu decoder thread\n");
free( p_spudec );
return( NULL );
}
intf_DbgMsg("spudec debug: spu decoder thread (%p) created\n", p_spudec);
return( p_spudec );
}
/*******************************************************************************
* spudec_DestroyThread: destroy a spu decoder thread
*******************************************************************************
* Destroy and terminate thread. This function will return 0 if the thread could
* be destroyed, and non 0 else. The last case probably means that the thread
* was still active, and another try may succeed.
*******************************************************************************/
void spudec_DestroyThread( spudec_thread_t *p_spudec )
{
intf_DbgMsg("spudec debug: requesting termination of spu decoder thread %p\n", p_spudec);
fprintf(stderr, "spudec debug: requesting termination of spu decoder thread %p\n", p_spudec);
/* Ask thread to kill itself */
p_spudec->b_die = 1;
/* Waiting for the decoder thread to exit */
/* Remove this as soon as the "status" flag is implemented */
vlc_thread_join( p_spudec->thread_id );
}
/* following functions are local */
/*******************************************************************************
* InitThread: initialize spu decoder thread
*******************************************************************************
* This function is called from RunThread and performs the second step of the
* initialization. It returns 0 on success. Note that the thread's flag are not
* modified inside this function.
*******************************************************************************/
static int InitThread( spudec_thread_t *p_spudec )
{
intf_DbgMsg("spudec debug: initializing spu decoder thread %p\n", p_spudec);
/* Mark thread as running and return */
intf_DbgMsg("spudec debug: InitThread(%p) succeeded\n", p_spudec);
return( 0 );
}
/*******************************************************************************
* RunThread: spu decoder thread
*******************************************************************************
* spu decoder thread. This function does only return when the thread is
* terminated.
*******************************************************************************/
static void RunThread( spudec_thread_t *p_spudec )
{
intf_DbgMsg("spudec debug: running spu decoder thread (%p) (pid == %i)\n",
p_spudec, getpid());
/*
* Initialize thread and free configuration
*/
p_spudec->b_error = InitThread( p_spudec );
if( p_spudec->b_error )
{
return;
}
p_spudec->b_run = 1;
/*
* Main loop - it is not executed if an error occured during
* initialization
*/
while( (!p_spudec->b_die) && (!p_spudec->b_error) )
{
fprintf(stderr, "I'm a spu decoder !\n");
sleep(1);
}
/*
* Error loop
*/
if( p_spudec->b_error )
{
ErrorThread( p_spudec );
}
/* End of thread */
EndThread( p_spudec );
p_spudec->b_run = 0;
}
/*******************************************************************************
* ErrorThread: RunThread() error loop
*******************************************************************************
* This function is called when an error occured during thread main's loop. The
* thread can still receive feed, but must be ready to terminate as soon as
* possible.
*******************************************************************************/
static void ErrorThread( spudec_thread_t *p_spudec )
{
/* Wait until a `die' order */
while( !p_spudec->b_die )
{
// foo();
}
}
/*******************************************************************************
* EndThread: thread destruction
*******************************************************************************
* This function is called when the thread ends after a sucessfull
* initialization.
*******************************************************************************/
static void EndThread( spudec_thread_t *p_spudec )
{
intf_DbgMsg("spudec debug: EndThread(%p)\n", p_spudec);
}
......@@ -41,7 +41,7 @@ static void SetBufferArea ( vout_thread_t *p_vout, int i_x, int i_
static void SetBufferPicture ( vout_thread_t *p_vout, picture_t *p_pic );
static void RenderPicture ( vout_thread_t *p_vout, picture_t *p_pic );
static void RenderPictureInfo ( vout_thread_t *p_vout, picture_t *p_pic );
static void RenderSubtitle ( vout_thread_t *p_vout, subtitle_t *p_sub );
static void RenderSubPictureUnit ( vout_thread_t *p_vout, spu_t *p_spu );
static void RenderInterface ( vout_thread_t *p_vout );
static void RenderIdle ( vout_thread_t *p_vout );
static void RenderInfo ( vout_thread_t *p_vout );
......@@ -112,14 +112,14 @@ vout_thread_t * vout_CreateThread ( char *psz_display, int i_root_
/* Initialize buffer index */
p_vout->i_buffer_index = 0;
/* Initialize pictures and subtitles - translation tables and functions
/* Initialize pictures and spus - translation tables and functions
* will be initialized later in InitThread */
for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
{
p_vout->p_picture[i_index].i_type = EMPTY_PICTURE;
p_vout->p_picture[i_index].i_status = FREE_PICTURE;
p_vout->p_subtitle[i_index].i_type = EMPTY_SUBTITLE;
p_vout->p_subtitle[i_index].i_status= FREE_SUBTITLE;
p_vout->p_spu[i_index].i_type = EMPTY_SPU;
p_vout->p_spu[i_index].i_status= FREE_SPU;
}
/* Create and initialize system-dependant method - this function issues its
......@@ -153,7 +153,7 @@ vout_thread_t * vout_CreateThread ( char *psz_display, int i_root_
/* Create thread and set locks */
vlc_mutex_init( &p_vout->picture_lock );
vlc_mutex_init( &p_vout->subtitle_lock );
vlc_mutex_init( &p_vout->spu_lock );
vlc_mutex_init( &p_vout->change_lock );
vlc_mutex_lock( &p_vout->change_lock );
if( vlc_thread_create( &p_vout->thread_id, "video output", (void *) RunThread, (void *) p_vout) )
......@@ -217,13 +217,13 @@ void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
}
/******************************************************************************
* vout_DisplaySubtitle: display a subtitle
* vout_DisplaySubPictureUnit: display a sub picture unit
******************************************************************************
* Remove the reservation flag of a subtitle, which will cause it to be ready for
* Remove the reservation flag of an spu, which will cause it to be ready for
* display. The picture does not need to be locked, since it is ignored by
* the output thread if is reserved.
******************************************************************************/
void vout_DisplaySubtitle( vout_thread_t *p_vout, subtitle_t *p_sub )
void vout_DisplaySubPictureUnit( vout_thread_t *p_vout, spu_t *p_spu )
{
#ifdef DEBUG_VIDEO
char psz_begin_date[MSTRTIME_MAX_SIZE]; /* buffer for date string */
......@@ -232,59 +232,59 @@ void vout_DisplaySubtitle( vout_thread_t *p_vout, subtitle_t *p_sub )
#ifdef DEBUG
/* Check if status is valid */
if( p_sub->i_status != RESERVED_SUBTITLE )
if( p_spu->i_status != RESERVED_SPU )
{
intf_DbgMsg("error: subtitle %p has invalid status %d\n", p_sub, p_sub->i_status );
intf_DbgMsg("error: spu %p has invalid status %d\n", p_spu, p_spu->i_status );
}
#endif
/* Remove reservation flag */
p_sub->i_status = READY_SUBTITLE;
p_spu->i_status = READY_SPU;
#ifdef DEBUG_VIDEO
/* Send subtitle informations */
intf_DbgMsg("subtitle %p: type=%d, begin date=%s, end date=%s\n", p_sub, p_sub->i_type,
mstrtime( psz_begin_date, p_sub->begin_date ),
mstrtime( psz_end_date, p_sub->end_date ) );
/* Send subpicture informations */
intf_DbgMsg("spu %p: type=%d, begin date=%s, end date=%s\n", p_spu, p_spu->i_type,
mstrtime( psz_begin_date, p_spu->begin_date ),
mstrtime( psz_end_date, p_spu->end_date ) );
#endif
}
/******************************************************************************
* vout_CreateSubtitle: allocate a subtitle in the video output heap.
* vout_CreateSubPictureUnit: allocate an spu in the video output heap.
******************************************************************************
* This function create a reserved subtitle in the video output heap.
* This function create a reserved spu in the video output heap.
* A null pointer is returned if the function fails. This method provides an
* already allocated zone of memory in the subtitle data fields. It needs locking
* already allocated zone of memory in the spu data fields. It needs locking
* since several pictures can be created by several producers threads.
******************************************************************************/
subtitle_t *vout_CreateSubtitle( vout_thread_t *p_vout, int i_type,
spu_t *vout_CreateSubPictureUnit( vout_thread_t *p_vout, int i_type,
int i_size )
{
//??
}
/******************************************************************************
* vout_DestroySubtitle: remove a permanent or reserved subtitle from the heap
* vout_DestroySubPictureUnit: remove a permanent or reserved spu from the heap
******************************************************************************
* This function frees a previously reserved subtitle.
* This function frees a previously reserved spu.
* It is meant to be used when the construction of a picture aborted.
* This function does not need locking since reserved subtitles are ignored by
* This function does not need locking since reserved spus are ignored by
* the output thread.
******************************************************************************/
void vout_DestroySubtitle( vout_thread_t *p_vout, subtitle_t *p_sub )
void vout_DestroySubPictureUnit( vout_thread_t *p_vout, spu_t *p_spu )
{
#ifdef DEBUG
/* Check if subtitle status is valid */
if( p_sub->i_status != RESERVED_SUBTITLE )
/* Check if spu status is valid */
if( p_spu->i_status != RESERVED_SPU )
{
intf_DbgMsg("error: subtitle %p has invalid status %d\n", p_sub, p_sub->i_status );
intf_DbgMsg("error: spu %p has invalid status %d\n", p_spu, p_spu->i_status );
}
#endif
p_sub->i_status = DESTROYED_SUBTITLE;
p_spu->i_status = DESTROYED_SPU;
#ifdef DEBUG_VIDEO
intf_DbgMsg("subtitle %p\n", p_sub);
intf_DbgMsg("spu %p\n", p_spu);
#endif
}
......@@ -643,7 +643,7 @@ static void RunThread( vout_thread_t *p_vout)
mtime_t display_date; /* display date */
boolean_t b_display; /* display flag */
picture_t * p_pic; /* picture pointer */
subtitle_t * p_sub; /* subtitle pointer */
spu_t * p_spu; /* subpicture pointer */
/*
* Initialize thread
......@@ -664,7 +664,7 @@ static void RunThread( vout_thread_t *p_vout)
{
/* Initialize loop variables */
p_pic = NULL;
p_sub = NULL;
p_spu = NULL;
display_date = 0;
current_date = mdate();
......@@ -711,16 +711,16 @@ static void RunThread( vout_thread_t *p_vout)
}
/*
* Find the subtitle to display - this operation does not need lock, since
* only READY_SUBTITLEs are handled. If no picture has been selected,
* display_date will depend on the subtitle
* Find the subpicture to display - this operation does not need lock, since
* only READY_SPUs are handled. If no picture has been selected,
* display_date will depend on the spu
*/
//??
/*
* Perform rendering, sleep and display rendered picture
*/
if( p_pic ) /* picture and perhaps subtitle */
if( p_pic ) /* picture and perhaps spu */
{
b_display = p_vout->b_active;
......@@ -743,26 +743,26 @@ static void RunThread( vout_thread_t *p_vout)
p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
vlc_mutex_unlock( &p_vout->picture_lock );
/* Render interface and subtitles */
/* Render interface and spus */
if( b_display && p_vout->b_interface )
{
RenderInterface( p_vout );
}
if( p_sub )
if( p_spu )
{
if( b_display )
{
RenderSubtitle( p_vout, p_sub );
RenderSubPictureUnit( p_vout, p_spu );
}
/* Remove subtitle from heap */
vlc_mutex_lock( &p_vout->subtitle_lock );
p_sub->i_status = DESTROYED_SUBTITLE;
vlc_mutex_unlock( &p_vout->subtitle_lock );
/* Remove spu from heap */
vlc_mutex_lock( &p_vout->spu_lock );
p_spu->i_status = DESTROYED_SPU;
vlc_mutex_unlock( &p_vout->spu_lock );
}
}
else if( p_sub ) /* subtitle alone */
else if( p_spu ) /* spu alone */
{
b_display = p_vout->b_active;
......@@ -771,7 +771,7 @@ static void RunThread( vout_thread_t *p_vout)
/* Clear buffer */
SetBufferPicture( p_vout, NULL );
/* Render informations, interface and subtitle */
/* Render informations, interface and spu */
if( p_vout->b_info )
{
RenderInfo( p_vout );
......@@ -780,13 +780,13 @@ static void RunThread( vout_thread_t *p_vout)
{
RenderInterface( p_vout );
}
RenderSubtitle( p_vout, p_sub );
RenderSubPictureUnit( p_vout, p_spu );
}
/* Remove subtitle from heap */
vlc_mutex_lock( &p_vout->subtitle_lock );
p_sub->i_status = DESTROYED_SUBTITLE;
vlc_mutex_unlock( &p_vout->subtitle_lock );
/* Remove spu from heap */
vlc_mutex_lock( &p_vout->spu_lock );