spu_decoder.c 13.1 KB
Newer Older
1
/*****************************************************************************
2
 * spu_decoder.c : spu decoder thread
3 4 5 6 7 8 9 10 11
 *****************************************************************************
 * Copyright (C) 2000 VideoLAN
 *
 * Authors:
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
12
 * 
13 14
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
17
 *
18 19 20
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
21
 *****************************************************************************/
22

23
/*****************************************************************************
24
 * Preamble
25
 *****************************************************************************/
26 27
#include "defs.h"

28 29
#include <stdlib.h>                                      /* malloc(), free() */
#include <unistd.h>                                              /* getpid() */
30 31 32

#include "config.h"
#include "common.h"
33
#include "threads.h"
34 35 36
#include "mtime.h"

#include "intf_msg.h"
37
#include "debug.h"                                                 /* ASSERT */
38

39 40
#include "stream_control.h"
#include "input_ext-dec.h"
41

42 43 44
#include "video.h"
#include "video_output.h"

45 46 47 48 49 50 51 52 53 54
#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 );

55
/*****************************************************************************
56
 * spudec_CreateThread: create a spu decoder thread
57
 *****************************************************************************/
58
vlc_thread_t spudec_CreateThread( vdec_config_t * p_config )
59 60 61
{
    spudec_thread_t *     p_spudec;

Sam Hocevar's avatar
 
Sam Hocevar committed
62
    intf_DbgMsg("spudec debug: creating spu decoder thread");
63 64

    /* Allocate the memory needed to store the thread's structure */
Sam Hocevar's avatar
 
Sam Hocevar committed
65 66 67
    p_spudec = (spudec_thread_t *)malloc( sizeof(spudec_thread_t) );

    if ( p_spudec == NULL )
68
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
69 70
        intf_ErrMsg( "spudec error: not enough memory "
                     "for spudec_CreateThread() to create the new thread" );
71
        return( 0 );
72 73 74 75 76
    }

    /*
     * Initialize the thread properties
     */
77 78
    p_spudec->p_config = p_config;
    p_spudec->p_fifo = p_config->decoder_config.p_decoder_fifo;
79

80
    /* Get the video output informations */
81
    p_spudec->p_vout = p_config->p_vout;
82

83 84 85 86
    /* Spawn the spu decoder thread */
    if ( vlc_thread_create(&p_spudec->thread_id, "spu decoder",
         (vlc_thread_func_t)RunThread, (void *)p_spudec) )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
87
        intf_ErrMsg("spudec error: can't spawn spu decoder thread");
88
        free( p_spudec );
89
        return( 0 );
90 91
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
92
    intf_DbgMsg("spudec debug: spu decoder thread (%p) created", p_spudec);
93
    return( p_spudec->thread_id );
94 95 96 97
}

/* following functions are local */

98
/*****************************************************************************
99
 * InitThread: initialize spu decoder thread
100
 *****************************************************************************
101 102 103
 * 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.
104
 *****************************************************************************/
105 106
static int InitThread( spudec_thread_t *p_spudec )
{
Sam Hocevar's avatar
 
Sam Hocevar committed
107
    intf_DbgMsg("spudec debug: initializing spu decoder thread %p", p_spudec);
108

Sam Hocevar's avatar
 
Sam Hocevar committed
109 110
    p_spudec->p_config->decoder_config.pf_init_bit_stream(
            &p_spudec->bit_stream,
111
            p_spudec->p_config->decoder_config.p_decoder_fifo );
112

113
    /* Mark thread as running and return */
Sam Hocevar's avatar
 
Sam Hocevar committed
114
    intf_DbgMsg( "spudec debug: InitThread(%p) succeeded", p_spudec );
115
    return( 0 );
116 117
}

118
/*****************************************************************************
119
 * RunThread: spu decoder thread
120
 *****************************************************************************
121
 * spu decoder thread. This function only returns when the thread is
122 123
 * terminated.
 *****************************************************************************/
124 125
static void RunThread( spudec_thread_t *p_spudec )
{
Sam Hocevar's avatar
 
Sam Hocevar committed
126
    intf_DbgMsg("spudec debug: running spu decoder thread (%p) (pid == %i)",
127
        p_spudec, getpid());
128

129 130
    /*
     * Initialize thread and free configuration
131
     */
132
    p_spudec->p_fifo->b_error = InitThread( p_spudec );
133 134 135 136 137

    /*
     * Main loop - it is not executed if an error occured during
     * initialization
     */
138
    while( (!p_spudec->p_fifo->b_die) && (!p_spudec->p_fifo->b_error) )
139
    {
140 141
        int i_packet_size;
        int i_rle_size;
Sam Hocevar's avatar
 
Sam Hocevar committed
142 143 144
        int i_index, i_next;
        boolean_t b_valid;
        subpicture_t * p_spu = NULL;
145

146 147 148 149
        /* wait for the next SPU ID.
         * XXX: We trash 0xff bytes since they probably come from
         * an incomplete previous packet */
        do
150
        {
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
            i_packet_size = GetBits( &p_spudec->bit_stream, 8 );
        }
        while( i_packet_size == 0xff );

        if( p_spudec->p_fifo->b_die )
        {
            break;
        }

        /* the total size - should equal the sum of the
         * PES packet size that form the SPU packet */
        i_packet_size = i_packet_size << 8
                         | GetBits( &p_spudec->bit_stream, 8 );

        /* the RLE stuff size */
        i_rle_size = GetBits( &p_spudec->bit_stream, 16 );

        /* if the values we got aren't too strange, decode the data */
        if( i_rle_size < i_packet_size )
        {
            /* allocate the subpicture.
             * FIXME: we should check if the allocation failed */
            p_spu = vout_CreateSubPicture( p_spudec->p_vout,
                                           DVD_SUBPICTURE, i_rle_size );
            /* get display time */
            p_spu->begin_date = p_spu->end_date
                            = DECODER_FIFO_START(*p_spudec->p_fifo)->i_pts;

            /* get RLE data, skip 4 bytes for the first two read offsets */
            GetChunk( &p_spudec->bit_stream, p_spu->p_data,
                      i_rle_size - 4 );
182

183
            if( p_spudec->p_fifo->b_die )
Sam Hocevar's avatar
 
Sam Hocevar committed
184
            {
185
                break;
Sam Hocevar's avatar
 
Sam Hocevar committed
186
            }
Sam Hocevar's avatar
Sam Hocevar committed
187

188 189
            /* continue parsing after the RLE part */
            i_index = i_rle_size;
190

191 192
            /* assume packet is valid */
            b_valid = 1;
193

194 195
            /* getting the control part */
            do
Sam Hocevar's avatar
NEWS:  
Sam Hocevar committed
196
            {
197 198 199 200 201
                unsigned char   i_cmd;
                u16             i_date;

                /* Get the sequence date */
                i_date = GetBits( &p_spudec->bit_stream, 16 );
202

203 204
                /* Next offset */
                i_next = GetBits( &p_spudec->bit_stream, 16 );
Sam Hocevar's avatar
 
Sam Hocevar committed
205

206
                i_index += 4;
Sam Hocevar's avatar
 
Sam Hocevar committed
207

Sam Hocevar's avatar
NEWS:  
Sam Hocevar committed
208
                do
209
                {
210 211
                    i_cmd = GetBits( &p_spudec->bit_stream, 8 );
                    i_index++;
212

213
                    switch( i_cmd )
Sam Hocevar's avatar
NEWS:  
Sam Hocevar committed
214
                    {
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
                        case SPU_CMD_FORCE_DISPLAY:
                            /* 00 (force displaying) */
                            break;
                        /* FIXME: here we have to calculate dates. It's
                         * around i_date * 12000 but I don't know
                         * how much exactly.
                         */
                        case SPU_CMD_START_DISPLAY:
                            /* 01 (start displaying) */
                            p_spu->begin_date += ( i_date * 12000 );
                            break;
                        case SPU_CMD_STOP_DISPLAY:
                            /* 02 (stop displaying) */
                            p_spu->end_date += ( i_date * 12000 );
                            break;
                        case SPU_CMD_SET_PALETTE:
                            /* 03xxxx (palette) - trashed */
                            RemoveBits( &p_spudec->bit_stream, 16 );
                            i_index += 2;
                            break;
                        case SPU_CMD_SET_ALPHACHANNEL:
                            /* 04xxxx (alpha channel) - trashed */
                            RemoveBits( &p_spudec->bit_stream, 16 );
                            i_index += 2;
                            break;
                        case SPU_CMD_SET_COORDINATES:
                            /* 05xxxyyyxxxyyy (coordinates) */
                            p_spu->i_x =
                                GetBits( &p_spudec->bit_stream, 12 );

                            p_spu->i_width = p_spu->i_x -
                                GetBits( &p_spudec->bit_stream, 12 ) + 1;

                            p_spu->i_y =
                                GetBits( &p_spudec->bit_stream, 12 );

                            p_spu->i_height = p_spu->i_y -
                                GetBits( &p_spudec->bit_stream, 12 ) + 1;

                            i_index += 6;
                            break;
                        case SPU_CMD_SET_OFFSETS:
                            /* 06xxxxyyyy (byte offsets) */
                            p_spu->type.spu.i_offset[0] =
                                GetBits( &p_spudec->bit_stream, 16 ) - 4;
                            p_spu->type.spu.i_offset[1] =
                                GetBits( &p_spudec->bit_stream, 16 ) - 4;
                            i_index += 4;
                            break;
                        case SPU_CMD_END:
                            /* ff (end) */
                            break;
                        default:
                            /* ?? (unknown command) */
                            intf_ErrMsg( "spudec: unknown command 0x%.2x",
                                         i_cmd );
                            b_valid = 0;
                            break;
Sam Hocevar's avatar
NEWS:  
Sam Hocevar committed
273 274
                    }
                }
275 276 277
                while( b_valid && ( i_cmd != SPU_CMD_END ) );
            }
            while( b_valid && ( i_index == i_next ) );
278

279 280 281 282 283
            if( b_valid )
            {
                /* SPU is finished - we can tell the video output
                 * to display it */
                vout_DisplaySubPicture( p_spudec->p_vout, p_spu );
284
            }
285
            else
286
            {
287
                vout_DestroySubPicture( p_spudec->p_vout, p_spu );
288
            }
289 290 291 292 293 294 295 296 297 298
        }
        else 
        {
            /* Unexpected PES packet - trash it */
            intf_ErrMsg( "spudec: trying to recover from bad packet" );
            vlc_mutex_lock( &p_spudec->p_fifo->data_lock );
            p_spudec->p_fifo->pf_delete_pes( p_spudec->p_fifo->p_packets_mgt,
                                  DECODER_FIFO_START(*p_spudec->p_fifo) );
            DECODER_FIFO_INCSTART( *p_spudec->p_fifo );
            vlc_mutex_unlock( &p_spudec->p_fifo->data_lock );
299 300
        }
    }
301 302 303 304

    /*
     * Error loop
     */
305
    if( p_spudec->p_fifo->b_error )
306
    {
307
        ErrorThread( p_spudec );
308 309 310 311 312 313
    }

    /* End of thread */
    EndThread( p_spudec );
}

314
/*****************************************************************************
315
 * ErrorThread: RunThread() error loop
316
 *****************************************************************************
317 318 319
 * 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.
320
 *****************************************************************************/
321 322
static void ErrorThread( spudec_thread_t *p_spudec )
{
323 324
    /* We take the lock, because we are going to read/write the start/end
     * indexes of the decoder fifo */
325
    vlc_mutex_lock( &p_spudec->p_fifo->data_lock );
326 327

    /* Wait until a `die' order is sent */
328
    while( !p_spudec->p_fifo->b_die )
329
    {
330
        /* Trash all received PES packets */
331
        while( !DECODER_FIFO_ISEMPTY(*p_spudec->p_fifo) )
332
        {
333 334 335
            p_spudec->p_fifo->pf_delete_pes( p_spudec->p_fifo->p_packets_mgt,
                    DECODER_FIFO_START(*p_spudec->p_fifo) );
            DECODER_FIFO_INCSTART( *p_spudec->p_fifo );
336 337 338
        }

        /* Waiting for the input thread to put new PES packets in the fifo */
339
        vlc_cond_wait( &p_spudec->p_fifo->data_wait, &p_spudec->p_fifo->data_lock );
340
    }
341 342

    /* We can release the lock before leaving */
343
    vlc_mutex_unlock( &p_spudec->p_fifo->data_lock );
344 345
}

346
/*****************************************************************************
347
 * EndThread: thread destruction
348
 *****************************************************************************
349
 * This function is called when the thread ends after a sucessful
350
 * initialization.
351
 *****************************************************************************/
352 353
static void EndThread( spudec_thread_t *p_spudec )
{
Sam Hocevar's avatar
 
Sam Hocevar committed
354
    intf_DbgMsg( "spudec debug: destroying spu decoder thread %p", p_spudec );
355
    free( p_spudec->p_config );
356
    free( p_spudec );
Sam Hocevar's avatar
 
Sam Hocevar committed
357
    intf_DbgMsg( "spudec debug: spu decoder thread %p destroyed", p_spudec);
358
}
Sam Hocevar's avatar
Sam Hocevar committed
359