]> git.sesse.net Git - vlc/blob - src/generic_decoder/generic_decoder.c
ee8d050a5d03adc63269b2b6a0924bced85c1f89
[vlc] / src / generic_decoder / generic_decoder.c
1 /*****************************************************************************
2  * generic_decoder.c : generic decoder thread
3  * (c)1999 VideoLAN
4  *****************************************************************************
5  * This decoder provides a way to parse packets which do not belong to any
6  * known stream type, or to redirect packets to files. It can extract PES files
7  * from a multiplexed stream, identify automatically ES in a stream missing
8  * stream informations (i.e. a TS stream without PSI) and update ES tables in
9  * its input thread, or just print informations about what it receives in DEBUG
10  * mode.
11  * A single generic decoder is able to handle several ES, therefore it can be
12  * used as a 'default' decoder by the input thread.
13  *****************************************************************************/
14
15 /*****************************************************************************
16  * Preamble
17  *****************************************************************************/
18 #include "vlc.h"
19
20 /*#include <errno.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <sys/uio.h>
28
29 #include "config.h"
30 #include "common.h"
31 #include "mtime.h"
32 #include "vlc_thread.h"
33 #include "thread.h"
34
35 #include "intf_msg.h"
36 #include "debug.h"   */
37 /*
38 #include "input.h"
39 #include "input_netlist.h"
40 #include "decoder_fifo.h"
41
42 #include "generic_decoder.h"
43
44 #include "video.h"
45 #include "video_output.h"
46 #include "video_decoder.h"*/
47
48 /*
49  * Local prototypes
50  */
51 static int      CheckConfiguration  ( gdec_cfg_t *p_cfg );
52 static int      InitThread          ( gdec_thread_t *p_gdec );
53 static void     RunThread           ( gdec_thread_t *p_gdec );
54 static void     ErrorThread         ( gdec_thread_t *p_gdec );
55 static void     EndThread           ( gdec_thread_t *p_gdec );
56
57 static void     IdentifyPES         ( gdec_thread_t *p_gdec, pes_packet_t *p_pes,
58                                       int i_stream_id );
59 static void     PrintPES            ( pes_packet_t *p_pes, int i_stream_id );
60
61 /*****************************************************************************
62  * gdec_CreateThread: create a generic decoder thread
63  *****************************************************************************
64  * This function creates a new generic decoder thread, and returns a pointer
65  * to its description. On error, it returns NULL.
66  * Following configuration properties are used:
67  *  GDEC_CFG_ACTIONS    (required)
68  * XXX??
69  *****************************************************************************/
70 gdec_thread_t * gdec_CreateThread( gdec_cfg_t *p_cfg, input_thread_t *p_input,
71                                    int *pi_status )
72 {
73     gdec_thread_t * p_gdec;                             /* thread descriptor */
74     int             i_status;                               /* thread status */
75
76     /*
77      * Check configuration
78      */
79     if( CheckConfiguration( p_cfg ) )
80     {
81         return( NULL );
82     }
83
84     /* Allocate descriptor and initialize flags */
85     p_gdec = (gdec_thread_t *) malloc( sizeof(gdec_thread_t) );
86     if( p_gdec == NULL )                                            /* error */
87     {
88         return( NULL );
89     }
90
91     /* Copy configuration */
92     p_gdec->i_actions = p_cfg->i_actions;
93     /* XXX?? */
94
95     /* Set status */
96     p_gdec->pi_status = (pi_status != NULL) ? pi_status : &i_status;
97     *p_gdec->pi_status = THREAD_CREATE;
98
99     /* Initialize flags */
100     p_gdec->b_die = 0;
101     p_gdec->b_error = 0;
102     p_gdec->b_active = 1;
103
104     /* Create thread */
105     if( vlc_thread_create( &p_gdec->thread_id, "generic decoder", (vlc_thread_func)RunThread, (void *) p_gdec) )
106     {
107         intf_ErrMsg("gdec error: %s\n", strerror(ENOMEM));
108         intf_DbgMsg("failed\n");
109         free( p_gdec );
110         return( NULL );
111     }
112
113     /* If status is NULL, wait until the thread is created */
114     if( pi_status == NULL )
115     {
116         do
117         {
118             msleep( THREAD_SLEEP );
119         }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR)
120                 && (i_status != THREAD_FATAL) );
121         if( i_status != THREAD_READY )
122         {
123             intf_DbgMsg("failed\n");
124             return( NULL );
125         }
126     }
127
128     intf_DbgMsg("succeeded -> %p\n", p_gdec);
129     return( p_gdec );
130 }
131
132 /*****************************************************************************
133  * gdec_DestroyThread: destroy a generic decoder thread
134  *****************************************************************************
135  * Destroy a terminated thread. This function will return 0 if the thread could
136  * be destroyed, and non 0 else. The last case probably means that the thread
137  * was still active, and another try may succeed.
138  *****************************************************************************/
139 void gdec_DestroyThread( gdec_thread_t *p_gdec, int *pi_status )
140 {
141     int     i_status;                                       /* thread status */
142
143     /* Set status */
144     p_gdec->pi_status = (pi_status != NULL) ? pi_status : &i_status;
145     *p_gdec->pi_status = THREAD_DESTROY;
146
147     /* Request thread destruction */
148     p_gdec->b_die = 1;
149     /* Make sure the decoder thread leaves the GetByte() function */
150     vlc_mutex_lock( &(p_gdec->fifo.data_lock) );
151     vlc_cond_signal( &(p_gdec->fifo.data_wait) );
152     vlc_mutex_unlock( &(p_gdec->fifo.data_lock) );
153
154     /* If status is NULL, wait until thread has been destroyed */
155     if( pi_status )
156     {
157         do
158         {
159             msleep( THREAD_SLEEP );
160         }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
161                 && (i_status != THREAD_FATAL) );
162     }
163
164     intf_DbgMsg("%p -> succeeded\n", p_gdec);
165 }
166
167 /* following functions are local */
168
169 /*****************************************************************************
170  * CheckConfiguration: check gdec_CreateThread() configuration
171  *****************************************************************************
172  * Set default parameters where required. In DEBUG mode, check if configuration
173  * is valid.
174  *****************************************************************************/
175 static int CheckConfiguration( gdec_cfg_t *p_cfg )
176 {
177 #ifdef DEBUG
178     /* Actions (required) */
179     if( !(p_cfg->i_properties & GDEC_CFG_ACTIONS) )
180     {
181         return( 1 );
182     }
183 #endif
184
185     return( 0 );
186 }
187
188 /*****************************************************************************
189  * InitThread: initialize gdec thread
190  *****************************************************************************
191  * This function is called from RunThread and performs the second step of the
192  * initialization. It returns 0 on success. Note that the thread's flag are not
193  * modified inside this function.
194  *****************************************************************************/
195 static int InitThread( gdec_thread_t *p_gdec )
196 {
197     /* XXX?? */
198
199     /* Update status */
200     *p_gdec->pi_status = THREAD_START;
201
202     /* Initialize other properties */
203 #ifdef STATS
204     p_gdec->c_loops = 0;
205     p_gdec->c_idle_loops = 0;
206     p_gdec->c_pes = 0;
207 #endif
208
209     /* Mark thread as running and return */
210     *p_gdec->pi_status = THREAD_READY;
211     intf_DbgMsg("%p -> succeeded\n", p_gdec);
212     return(0);
213 }
214
215 /*****************************************************************************
216  * RunThread: generic decoder thread
217  *****************************************************************************
218  * Generic decoder thread. This function does only returns when the thread is
219  * terminated.
220  *****************************************************************************/
221 static void RunThread( gdec_thread_t *p_gdec )
222 {
223     pes_packet_t *  p_pes;                                 /* current packet */
224     int             i_stream_id;                            /* PES stream id */
225
226     /*
227      * Initialize thread and free configuration
228      */
229     p_gdec->b_error = InitThread( p_gdec );
230     if( p_gdec->b_error )
231     {
232         free( p_gdec );                                /* destroy descriptor */
233         return;
234     }
235
236     /*
237      * Main loop - it is not executed if an error occured during
238      * initialization
239      */
240     while( (!p_gdec->b_die) && (!p_gdec->b_error) )
241     {
242         /* FIXME: locks à rajouter ?? - vérifier les macros (transformer en inline ?) */
243         /* on idle loop, increment c_idle_loops */
244         while( !DECODER_FIFO_ISEMPTY( p_gdec->fifo ) )
245         {
246             p_pes = DECODER_FIFO_START( p_gdec->fifo );
247             DECODER_FIFO_INCSTART( p_gdec->fifo );
248
249             /* Extract stream id from packet if required - stream id is used
250              * by GDEC_IDENTIFY, GDEC_SAVE_DEMUX and GDEC_PRINT */
251             if( p_gdec->i_actions & (GDEC_IDENTIFY | GDEC_SAVE_DEMUX | GDEC_PRINT) )
252             {
253                 i_stream_id = p_pes->p_pes_header[3];
254             }
255
256             /* PES identification */
257             if( p_gdec->i_actions & GDEC_IDENTIFY )
258             {
259                 IdentifyPES( p_gdec, p_pes, i_stream_id );
260             }
261
262             /* PES multiplexed stream saving */
263             if( p_gdec->i_actions & GDEC_SAVE )
264             {
265                 /* XXX?? */
266             }
267
268             /* PES demultiplexed stream saving */
269             if( p_gdec->i_actions & GDEC_SAVE_DEMUX )
270             {
271                 /* XXX?? */
272             }
273
274             /* PES information printing */
275             if( p_gdec->i_actions & GDEC_PRINT )
276             {
277                 PrintPES( p_pes, i_stream_id );
278             }
279
280             /* Trash PES packet (give it back to fifo) */
281             input_NetlistFreePES( p_gdec->p_input, p_pes );
282
283 #ifdef STATS
284             p_gdec->c_pes++;
285 #endif
286         }
287 #ifdef STATS
288         p_gdec->c_loops++;
289 #endif
290     }
291
292     /*
293      * Error loop
294      */
295     if( p_gdec->b_error )
296     {
297         ErrorThread( p_gdec );
298     }
299
300     /* End of thread */
301     EndThread( p_gdec );
302 }
303
304 /*****************************************************************************
305  * ErrorThread: RunThread() error loop
306  *****************************************************************************
307  * This function is called when an error occured during thread main's loop. The
308  * thread can still receive feed, but must be ready to terminate as soon as
309  * possible.
310  *****************************************************************************/
311 static void ErrorThread( gdec_thread_t *p_gdec )
312 {
313     pes_packet_t *  p_pes;                                     /* pes packet */
314
315     /* Wait until a `die' order */
316     while( !p_gdec->b_die )
317     {
318         /* Trash all received PES packets */
319         while( !DECODER_FIFO_ISEMPTY( p_gdec->fifo ) )
320         {
321             p_pes = DECODER_FIFO_START( p_gdec->fifo );
322             DECODER_FIFO_INCSTART( p_gdec->fifo );
323             input_NetlistFreePES( p_gdec->p_input, p_pes );
324         }
325
326         /* Sleep a while */
327         msleep( GDEC_IDLE_SLEEP );
328     }
329 }
330
331 /*****************************************************************************
332  * EndThread: thread destruction
333  *****************************************************************************
334  * This function is called when the thread ends after a sucessfull
335  * initialization.
336  *****************************************************************************/
337 static void EndThread( gdec_thread_t *p_gdec )
338 {
339     int *   pi_status;                                      /* thread status */
340
341     /* Store status */
342     pi_status = p_gdec->pi_status;
343     *pi_status = THREAD_END;
344
345 #ifdef DEBUG
346     /* Check for remaining PES packets */
347     /* XXX?? */
348 #endif
349
350     /* Destroy thread structures allocated by InitThread */
351     free( p_gdec );                                    /* destroy descriptor */
352
353     *pi_status = THREAD_OVER;
354     intf_DbgMsg("%p\n", p_gdec);
355 }
356
357 /*****************************************************************************
358  * IdentifyPES: identify a PES packet
359  *****************************************************************************
360  * Update ES tables in the input thread according to the stream_id value. See
361  * ISO 13818-1, table 2-18.
362  *****************************************************************************/
363 static void IdentifyPES( gdec_thread_t *p_gdec, pes_packet_t *p_pes, int i_stream_id )
364 {
365     int i_id;                                       /* stream id in es table */
366     int i_type;          /* stream type according ISO/IEC 13818-1 table 2-29 */
367
368     /* Search where the elementary stream id does come from */
369     switch( p_gdec->p_input->i_method )
370     {
371     case INPUT_METHOD_TS_FILE:                   /* TS methods: id is TS PID */
372     case INPUT_METHOD_TS_UCAST:
373     case INPUT_METHOD_TS_BCAST:
374     case INPUT_METHOD_TS_VLAN_BCAST:
375         /* XXX?? since PID is extracted by demux, it could be usefull to store it
376          * in a readable place, i.e. the TS packet descriptor, rather than to
377          * re-extract it now */
378         i_id = U16_AT(&p_pes->p_first_ts->buffer[1]) & 0x1fff;
379         break;
380
381 #ifdef DEBUG
382     default:                                            /* unknown id origin */
383         intf_DbgMsg("unable to identify PES using input method %d\n",
384                     p_gdec->p_input->i_method );
385         break;
386 #endif
387     }
388
389     /* Try to identify PES stream_id - see ISO 13818-1 table 2-18 */
390     if( i_stream_id == 0xbd )
391     {
392         /* Dolby AC-3 stream - might be specific to DVD PS streams */
393         i_type = MPEG2_AUDIO_ES;
394         intf_DbgMsg("PES %p identified as AUDIO AC3\n", p_pes);
395     }
396     else if( (i_stream_id & 0xe0) == 0xc0 )
397     {
398         /* ISO/IEC 13818-3 or ISO/IEC 11172-3 audio stream - since there is no
399          * way to make the difference between the two possibilities, and since
400          * an ISO/IEC 13818-3 is capable of decoding an ISO/IEC 11172-3 stream,
401          * the first one is used */
402         i_type = MPEG2_AUDIO_ES;
403         intf_DbgMsg("PES %p identified as AUDIO MPEG\n", p_pes);
404     }
405     else if( (i_stream_id & 0xf0) == 0xe0 )
406     {
407         /* ISO/IEC 13818-2 or ISO/IEC 11172-2 video stream - since there is no
408          * way to make the difference between the two possibilities, and since
409          * an ISO/IEC 13818-2 is capable of decoding an ISO/IEC 11172-2 stream,
410          * the first one is used */
411         i_type = MPEG2_VIDEO_ES;
412         intf_DbgMsg("PES %p identified as VIDEO\n", p_pes);
413     }
414     else
415     {
416         /* The stream could not be identified - just return */
417         intf_DbgMsg("PES %p could not be identified\n", p_pes);
418         return;
419     }
420
421     /* Update ES table */
422     /* XXX?? */
423 }
424
425 /*****************************************************************************
426  * PrintPES: print informations about a PES packet
427  *****************************************************************************
428  * This function will print information about a received PES packet. It is
429  * probably usefull only for debugging purposes, or before demultiplexing a
430  * stream. It has two different formats, depending of the presence of the DEBUG
431  * symbol.
432  *****************************************************************************/
433 static void PrintPES( pes_packet_t *p_pes, int i_stream_id )
434 {
435     char psz_pes[128];                                  /* descriptor buffer */
436
437 #ifdef DEBUG
438     /* PES informations, long (DEBUG) format - this string is maximum 70 bytes
439      * long */
440     sprintf(psz_pes, "id 0x%x, %d bytes (%d TS): %p %c%c%c%c",
441             i_stream_id, p_pes->i_pes_size, p_pes->i_ts_packets,
442             p_pes,
443             p_pes->b_data_loss ? 'l' : '-', p_pes->b_data_alignment ? 'a' : '-',
444             p_pes->b_random_access ? 'r' : '-', p_pes->b_discard_payload ? 'd' : '-' );
445 #else
446     /* PES informations, short format */
447     sprintf(psz_pes, "id 0x%x, %d bytes",
448             i_stream_id, p_pes->i_pes_size );
449 #endif
450     intf_Msg("gdec: PES %s\n", psz_pes );
451 }