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