1 /*******************************************************************************
2 * generic_decoder.c : generic decoder thread
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
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 *******************************************************************************/
15 /*******************************************************************************
17 *******************************************************************************/
23 #include <netinet/in.h>
26 #include <X11/extensions/XShm.h>
34 #include "debug.h" /* ?? temporaire, requis par netlist.h */
37 #include "input_netlist.h"
38 #include "decoder_fifo.h"
40 #include "generic_decoder.h"
43 #include "video_output.h"
44 #include "video_decoder.h"
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 );
55 static void IdentifyPES ( gdec_thread_t *p_gdec, pes_packet_t *p_pes,
57 static void PrintPES ( pes_packet_t *p_pes, int i_stream_id );
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)
67 *******************************************************************************/
68 gdec_thread_t * gdec_CreateThread( gdec_cfg_t *p_cfg, input_thread_t *p_input,
71 gdec_thread_t * p_gdec; /* thread descriptor */
72 int i_status; /* thread status */
77 if( CheckConfiguration( p_cfg ) )
82 /* Allocate descriptor and initialize flags */
83 p_gdec = (gdec_thread_t *) malloc( sizeof(gdec_thread_t) );
84 if( p_gdec == NULL ) /* error */
89 /* Copy configuration */
90 p_gdec->i_actions = p_cfg->i_actions;
94 p_gdec->pi_status = (pi_status != NULL) ? pi_status : &i_status;
95 *p_gdec->pi_status = THREAD_CREATE;
97 /* Initialize flags */
100 p_gdec->b_active = 1;
103 if( pthread_create( &p_gdec->thread_id, NULL, (void *) RunThread, (void *) p_gdec) )
105 intf_ErrMsg("gdec error: %s\n", strerror(ENOMEM));
106 intf_DbgMsg("failed\n");
111 /* If status is NULL, wait until the thread is created */
112 if( pi_status == NULL )
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 )
121 intf_DbgMsg("failed\n");
126 intf_DbgMsg("succeeded -> %p\n", p_gdec);
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 )
139 int i_status; /* thread status */
142 p_gdec->pi_status = (pi_status != NULL) ? pi_status : &i_status;
143 *p_gdec->pi_status = THREAD_DESTROY;
145 /* Request thread destruction */
148 /* If status is NULL, wait until thread has been destroyed */
153 msleep( THREAD_SLEEP );
154 }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
155 && (i_status != THREAD_FATAL) );
158 intf_DbgMsg("%p -> succeeded\n", p_gdec);
161 /* following functions are local */
163 /*******************************************************************************
164 * CheckConfiguration: check gdec_CreateThread() configuration
165 *******************************************************************************
166 * Set default parameters where required. In DEBUG mode, check if configuration
168 *******************************************************************************/
169 static int CheckConfiguration( gdec_cfg_t *p_cfg )
172 /* Actions (required) */
173 if( !(p_cfg->i_properties & GDEC_CFG_ACTIONS) )
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 )
194 *p_gdec->pi_status = THREAD_START;
196 /* Initialize other properties */
199 p_gdec->c_idle_loops = 0;
203 /* Mark thread as running and return */
204 *p_gdec->pi_status = THREAD_READY;
205 intf_DbgMsg("%p -> succeeded\n", p_gdec);
209 /*******************************************************************************
210 * RunThread: generic decoder thread
211 *******************************************************************************
212 * Generic decoder thread. This function does only returns when the thread is
214 *******************************************************************************/
215 static void RunThread( gdec_thread_t *p_gdec )
217 pes_packet_t * p_pes; /* current packet */
218 int i_stream_id; /* PES stream id */
221 * Initialize thread and free configuration
223 p_gdec->b_error = InitThread( p_gdec );
224 if( p_gdec->b_error )
226 free( p_gdec ); /* destroy descriptor */
231 * Main loop - it is not executed if an error occured during
234 while( (!p_gdec->b_die) && (!p_gdec->b_error) )
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 ) )
240 p_pes = DECODER_FIFO_START( p_gdec->fifo );
241 DECODER_FIFO_INCSTART( p_gdec->fifo );
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) )
247 i_stream_id = p_pes->p_pes_header[3];
250 /* PES identification */
251 if( p_gdec->i_actions & GDEC_IDENTIFY )
253 IdentifyPES( p_gdec, p_pes, i_stream_id );
256 /* PES multiplexed stream saving */
257 if( p_gdec->i_actions & GDEC_SAVE )
262 /* PES demultiplexed stream saving */
263 if( p_gdec->i_actions & GDEC_SAVE_DEMUX )
268 /* PES information printing */
269 if( p_gdec->i_actions & GDEC_PRINT )
271 PrintPES( p_pes, i_stream_id );
274 /* Trash PES packet (give it back to fifo) */
275 input_NetlistFreePES( p_gdec->p_input, p_pes );
289 if( p_gdec->b_error )
291 ErrorThread( p_gdec );
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
304 *******************************************************************************/
305 static void ErrorThread( gdec_thread_t *p_gdec )
307 pes_packet_t * p_pes; /* pes packet */
309 /* Wait until a `die' order */
310 while( !p_gdec->b_die )
312 /* Trash all received PES packets */
313 while( !DECODER_FIFO_ISEMPTY( p_gdec->fifo ) )
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 );
321 msleep( GDEC_IDLE_SLEEP );
325 /*******************************************************************************
326 * EndThread: thread destruction
327 *******************************************************************************
328 * This function is called when the thread ends after a sucessfull
330 *******************************************************************************/
331 static void EndThread( gdec_thread_t *p_gdec )
333 int * pi_status; /* thread status */
336 pi_status = p_gdec->pi_status;
337 *pi_status = THREAD_END;
340 /* Check for remaining PES packets */
344 /* Destroy thread structures allocated by InitThread */
345 free( p_gdec ); /* destroy descriptor */
347 *pi_status = THREAD_OVER;
348 intf_DbgMsg("%p\n", p_gdec);
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 )
359 int i_id; /* stream id in es table */
360 int i_type; /* stream type according ISO/IEC 13818-1 table 2-29 */
362 /* Search where the elementary stream id does come from */
363 switch( p_gdec->p_input->i_method )
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;
376 default: /* unknown id origin */
377 intf_DbgMsg("unable to identify PES using input method %d\n",
378 p_gdec->p_input->i_method );
383 /* Try to identify PES stream_id - see ISO 13818-1 table 2-18 */
384 if( (i_stream_id & 0xe0) == 0xc0 )
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);
393 else if( (i_stream_id & 0xf0) == 0xe0 )
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);
404 /* The stream could not be identified - just return */
405 intf_DbgMsg("PES %p could not be identified\n", p_pes);
409 /* Update ES table */
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
420 *******************************************************************************/
421 static void PrintPES( pes_packet_t *p_pes, int i_stream_id )
423 char psz_pes[128]; /* descriptor buffer */
426 /* PES informations, long (DEBUG) format - this string is maximum 70 bytes
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,
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' : '-' );
434 /* PES informations, short format */
435 sprintf(psz_pes, "id 0x%x, %d bytes",
436 i_stream_id, p_pes->i_pes_size );
438 intf_Msg("gdec: PES %s\n", psz_pes );