]> git.sesse.net Git - vlc/blob - plugins/mpeg_system/input_ps.c
388c951f0133bc87e381ced12bc6f40a11f6cc5c
[vlc] / plugins / mpeg_system / input_ps.c
1 /*****************************************************************************
2  * input_ps.c: PS demux and packet management
3  *****************************************************************************
4  * Copyright (C) 1998-2001 VideoLAN
5  * $Id: input_ps.c,v 1.4 2001/12/12 13:48:09 massiot Exp $
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *          Cyril Deguet <asmax@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 #define MODULE_NAME mpeg_ps
26 #include "modules_inner.h"
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #include "defs.h"
32
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <errno.h>
37
38 #ifdef STRNCASECMP_IN_STRINGS_H
39 #   include <strings.h>
40 #endif
41
42 #include <sys/types.h>
43 #include <sys/stat.h>
44
45 #ifdef HAVE_UNISTD_H
46 #   include <unistd.h>
47 #elif defined( _MSC_VER ) && defined( _WIN32 )
48 #   include <io.h>
49 #endif
50
51 #include <fcntl.h>
52
53 #include "common.h"
54 #include "intf_msg.h"
55 #include "threads.h"
56 #include "mtime.h"
57 #include "tests.h"
58
59 #include "stream_control.h"
60 #include "input_ext-intf.h"
61 #include "input_ext-dec.h"
62 #include "input_ext-plugins.h"
63
64 #include "input_ps.h"
65
66 #include "debug.h"
67
68 #include "modules.h"
69 #include "modules_export.h"
70
71 /*****************************************************************************
72  * fseeko: fseeko replacement for BSDI.
73  *****************************************************************************/
74 #ifdef __bsdi__
75 int    __sfseek __P(( FILE *, fpos_t, int ));
76 fpos_t __sftell __P(( FILE * ));
77
78 static __inline__ off_t fseeko( FILE *p_file, off_t i_offset, int i_pos )
79 {
80     return __sfseek( p_file, i_offset, i_pos );
81 }
82 #endif
83
84 /*****************************************************************************
85  * Local prototypes
86  *****************************************************************************/
87 static int  PSProbe         ( probedata_t * );
88 static int  PSRead          ( struct input_thread_s *,
89                              data_packet_t * p_packets[INPUT_READ_ONCE] );
90 static void PSInit          ( struct input_thread_s * );
91 static void PSEnd           ( struct input_thread_s * );
92 static int  PSSetProgram    ( struct input_thread_s * , pgrm_descriptor_t * );
93 static void PSSeek          ( struct input_thread_s *, off_t );
94
95 /*****************************************************************************
96  * Declare a buffer manager
97  *****************************************************************************/
98 #define FLAGS           BUFFERS_NOFLAGS
99 #define NB_LIFO         2
100 DECLARE_BUFFERS_EMBEDDED( FLAGS, NB_LIFO );
101 DECLARE_BUFFERS_INIT( FLAGS, NB_LIFO );
102 DECLARE_BUFFERS_END( FLAGS, NB_LIFO );
103 DECLARE_BUFFERS_NEWPACKET( FLAGS, NB_LIFO );
104 DECLARE_BUFFERS_DELETEPACKET( FLAGS, NB_LIFO, 150 );
105 DECLARE_BUFFERS_NEWPES( FLAGS, NB_LIFO );
106 DECLARE_BUFFERS_DELETEPES( FLAGS, NB_LIFO, 150, 150 );
107
108
109 /*****************************************************************************
110  * Functions exported as capabilities. They are declared as static so that
111  * we don't pollute the namespace too much.
112  *****************************************************************************/
113 void _M( input_getfunctions )( function_list_t * p_function_list )
114 {
115 #define input p_function_list->functions.input
116     p_function_list->pf_probe = PSProbe;
117     input.pf_init             = PSInit;
118     input.pf_open             = NULL;
119     input.pf_close            = NULL;
120     input.pf_end              = PSEnd;
121     input.pf_init_bit_stream  = InitBitstream;
122     input.pf_set_area         = NULL;
123     input.pf_set_program      = PSSetProgram;
124     input.pf_read             = PSRead;
125     input.pf_demux            = input_DemuxPS;
126     input.pf_new_packet       = input_NewPacket;
127     input.pf_new_pes          = input_NewPES;
128     input.pf_delete_packet    = input_DeletePacket;
129     input.pf_delete_pes       = input_DeletePES;
130     input.pf_rewind           = NULL;
131     input.pf_seek             = PSSeek;
132 #undef input
133 }
134
135 /*
136  * Data reading functions
137  */
138
139 /*****************************************************************************
140  * PSProbe: verifies that the stream is a PS stream
141  *****************************************************************************/
142 static int PSProbe( probedata_t *p_data )
143 {
144     input_thread_t * p_input = (input_thread_t *)p_data;
145
146     char * psz_name = p_input->p_source;
147     int i_score = 10;
148
149     if( TestMethod( INPUT_METHOD_VAR, "ps" ) )
150     {
151         return( 999 );
152     }
153
154     if( ( strlen(psz_name) > 5 ) && (!strncasecmp( psz_name, "file:", 5 )
155                                       || !strncasecmp( psz_name, "http:", 5 )) )
156     {
157         /* If the user specified "file:" or "http:" then it's probably a
158          * PS file */
159         i_score = 100;
160         psz_name += 5;
161     }
162
163     return( i_score );
164 }
165
166 /*****************************************************************************
167  * PSInit: initializes PS structures
168  *****************************************************************************/
169 static void PSInit( input_thread_t * p_input )
170 {
171     if( (p_input->p_method_data = input_BuffersInit()) == NULL )
172     {
173         p_input->b_error = 1;
174         return;
175     }
176
177     if( p_input->p_stream == NULL )
178     {
179         /* Re-open the socket as a buffered FILE stream */
180         p_input->p_stream = fdopen( p_input->i_handle, "r" );
181
182         if( p_input->p_stream == NULL )
183         {
184             intf_ErrMsg( "Cannot open file (%s)", strerror(errno) );
185             p_input->b_error = 1;
186             return;
187         }
188     }
189
190     /* FIXME : detect if InitStream failed */
191     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
192     input_AddProgram( p_input, 0, sizeof( stream_ps_data_t ) );
193     
194     p_input->stream.p_selected_program = 
195             p_input->stream.pp_programs[0] ;
196     p_input->stream.p_new_program = 
197             p_input->stream.pp_programs[0] ;
198     
199     if( p_input->stream.b_seekable )
200     {
201         stream_ps_data_t * p_demux_data =
202              (stream_ps_data_t *)p_input->stream.pp_programs[0]->p_demux_data;
203
204         rewind( p_input->p_stream );
205
206         /* Pre-parse the stream to gather stream_descriptor_t. */
207         p_input->stream.pp_programs[0]->b_is_ok = 0;
208         p_demux_data->i_PSM_version = EMPTY_PSM_VERSION;
209
210         while( !p_input->b_die && !p_input->b_error
211                 && !p_demux_data->b_has_PSM )
212         {
213             int                 i_result, i;
214             data_packet_t *     pp_packets[INPUT_READ_ONCE];
215
216             i_result = PSRead( p_input, pp_packets );
217             if( i_result == 1 )
218             {
219                 /* EOF */
220                 vlc_mutex_lock( &p_input->stream.stream_lock );
221                 p_input->stream.pp_programs[0]->b_is_ok = 1;
222                 vlc_mutex_unlock( &p_input->stream.stream_lock );
223                 break;
224             }
225             if( i_result == -1 )
226             {
227                 p_input->b_error = 1;
228                 break;
229             }
230
231             for( i = 0; i < INPUT_READ_ONCE && pp_packets[i] != NULL; i++ )
232             {
233                 /* FIXME: use i_p_config_t */
234                 input_ParsePS( p_input, pp_packets[i] );
235                 p_input->pf_delete_packet( p_input->p_method_data, pp_packets[i] );
236             }
237
238             /* File too big. */
239             if( p_input->stream.p_selected_area->i_tell >
240                                                     INPUT_PREPARSE_LENGTH )
241             {
242                 break;
243             }
244         }
245         rewind( p_input->p_stream );
246         vlc_mutex_lock( &p_input->stream.stream_lock );
247
248         p_input->stream.p_selected_area->i_tell = 0;
249
250         if( p_demux_data->b_has_PSM )
251         {
252             /* (The PSM decoder will care about spawning the decoders) */
253             p_input->stream.pp_programs[0]->b_is_ok = 1;
254         }
255 #ifdef AUTO_SPAWN
256         else
257         {
258             /* (We have to do it ourselves) */
259             int                 i_es;
260
261             /* FIXME: we should do multiple passes in case an audio type
262              * is not present */
263             for( i_es = 0;
264                  i_es < p_input->stream.pp_programs[0]->i_es_number;
265                  i_es++ )
266             {
267 #define p_es p_input->stream.pp_programs[0]->pp_es[i_es]
268                 switch( p_es->i_type )
269                 {
270                     case MPEG1_VIDEO_ES:
271                     case MPEG2_VIDEO_ES:
272                         input_SelectES( p_input, p_es );
273                         break;
274
275                     case MPEG1_AUDIO_ES:
276                     case MPEG2_AUDIO_ES:
277                         if( main_GetIntVariable( INPUT_CHANNEL_VAR, 0 )
278                                 == (p_es->i_id & 0x1F) )
279                         switch( main_GetIntVariable( INPUT_AUDIO_VAR, 0 ) )
280                         {
281                         case 0:
282                             main_PutIntVariable( INPUT_AUDIO_VAR,
283                                                  REQUESTED_MPEG );
284                         case REQUESTED_MPEG:
285                             input_SelectES( p_input, p_es );
286                         }
287                         break;
288
289                     case AC3_AUDIO_ES:
290                         if( main_GetIntVariable( INPUT_CHANNEL_VAR, 0 )
291                                 == ((p_es->i_id & 0xF00) >> 8) )
292                         switch( main_GetIntVariable( INPUT_AUDIO_VAR, 0 ) )
293                         {
294                         case 0:
295                             main_PutIntVariable( INPUT_AUDIO_VAR,
296                                                  REQUESTED_AC3 );
297                         case REQUESTED_AC3:
298                             input_SelectES( p_input, p_es );
299                         }
300                         break;
301
302                     case DVD_SPU_ES:
303                         if( main_GetIntVariable( INPUT_SUBTITLE_VAR, -1 )
304                                 == ((p_es->i_id & 0x1F00) >> 8) )
305                         {
306                             input_SelectES( p_input, p_es );
307                         }
308                         break;
309
310                     case LPCM_AUDIO_ES:
311                         if( main_GetIntVariable( INPUT_CHANNEL_VAR, 0 )
312                                 == ((p_es->i_id & 0x1F00) >> 8) )
313                         switch( main_GetIntVariable( INPUT_AUDIO_VAR, 0 ) )
314                         {
315                         case 0:
316                             main_PutIntVariable( INPUT_AUDIO_VAR,
317                                                  REQUESTED_LPCM );
318                         case REQUESTED_LPCM:
319                             input_SelectES( p_input, p_es );
320                         }
321                         break;
322                 }
323             }
324                     
325         }
326 #endif
327         if( p_main->b_stats )
328         {
329             input_DumpStream( p_input );
330         }
331         vlc_mutex_unlock( &p_input->stream.stream_lock );
332     }
333     else
334     {
335         /* The programs will be added when we read them. */
336         vlc_mutex_lock( &p_input->stream.stream_lock );
337         p_input->stream.i_method = INPUT_METHOD_FILE;
338         p_input->stream.pp_programs[0]->b_is_ok = 0;
339         vlc_mutex_unlock( &p_input->stream.stream_lock );
340     }
341 }
342
343 /*****************************************************************************
344  * PSEnd: frees unused data
345  *****************************************************************************/
346 static void PSEnd( input_thread_t * p_input )
347 {
348     input_BuffersEnd( p_input->p_method_data );
349 }
350
351 /*****************************************************************************
352  * SafeRead: reads a chunk of stream and correctly detects errors
353  *****************************************************************************/
354 static __inline__ int SafeRead( input_thread_t * p_input, byte_t * p_buffer,
355                                 size_t i_len )
356 {
357     int                 i_error;
358
359     while( fread( p_buffer, i_len, 1, p_input->p_stream ) != 1 )
360     {
361         if( feof( p_input->p_stream ) )
362         {
363             return( 1 );
364         }
365
366         if( (i_error = ferror( p_input->p_stream )) )
367         {
368             intf_ErrMsg( "Read failed (%s)", strerror(i_error) );
369             return( -1 );
370         }
371     }
372     vlc_mutex_lock( &p_input->stream.stream_lock );
373     p_input->stream.p_selected_area->i_tell += i_len;
374     vlc_mutex_unlock( &p_input->stream.stream_lock );
375     return( 0 );
376 }
377
378 /*****************************************************************************
379  * PSRead: reads data packets
380  *****************************************************************************
381  * Returns -1 in case of error, 0 if everything went well, and 1 in case of
382  * EOF.
383  *****************************************************************************/
384 static int PSRead( input_thread_t * p_input,
385                    data_packet_t * pp_packets[INPUT_READ_ONCE] )
386 {
387     byte_t              p_header[6];
388     data_packet_t *     p_data;
389     size_t              i_packet_size;
390     int                 i_packet, i_error;
391
392     memset( pp_packets, 0, INPUT_READ_ONCE * sizeof(data_packet_t *) );
393     for( i_packet = 0; i_packet < INPUT_READ_ONCE; i_packet++ )
394     {
395         /* Read what we believe to be a packet header. */
396         if( (i_error = SafeRead( p_input, p_header, 4 )) )
397         {
398             return( i_error );
399         }
400
401         if( (U32_AT(p_header) & 0xFFFFFF00) != 0x100L )
402         {
403             /* This is not the startcode of a packet. Read the stream
404              * until we find one. */
405             u32         i_startcode = U32_AT(p_header);
406             int         i_dummy;
407
408             if( i_startcode )
409             {
410                 /* It is common for MPEG-1 streams to pad with zeros
411                  * (although it is forbidden by the recommendation), so
412                  * don't bother everybody in this case. */
413                 intf_WarnMsg( 3, "Garbage at input (%.8x)", i_startcode );
414             }
415
416             while( (i_startcode & 0xFFFFFF00) != 0x100L )
417             {
418                 i_startcode <<= 8;
419                 if( (i_dummy = getc( p_input->p_stream )) != EOF )
420                 {
421                     i_startcode |= i_dummy;
422                 }
423                 else
424                 {
425                     return( 1 );
426                 }
427             }
428             /* Packet found. */
429             *(u32 *)p_header = U32_AT(&i_startcode);
430         }
431
432         /* 0x1B9 == SYSTEM_END_CODE, it is only 4 bytes long. */
433         if( U32_AT(p_header) != 0x1B9 )
434         {
435             /* The packet is at least 6 bytes long. */
436             if( (i_error = SafeRead( p_input, p_header + 4, 2 )) )
437             {
438                 return( i_error );
439             }
440
441             if( U32_AT(p_header) != 0x1BA )
442             {
443                 /* That's the case for all packets, except pack header. */
444                 i_packet_size = U16_AT(&p_header[4]);
445             }
446             else
447             {
448                 /* Pack header. */
449                 if( (p_header[4] & 0xC0) == 0x40 )
450                 {
451                     /* MPEG-2 */
452                     i_packet_size = 8;
453                 }
454                 else if( (p_header[4] & 0xF0) == 0x20 )
455                 {
456                     /* MPEG-1 */
457                     i_packet_size = 6;
458                 }
459                 else
460                 {
461                     intf_ErrMsg( "Unable to determine stream type" );
462                     return( -1 );
463                 }
464             }
465         }
466         else
467         {
468             /* System End Code */
469             i_packet_size = -2;
470         }
471
472         /* Fetch a packet of the appropriate size. */
473         p_data = p_input->pf_new_packet( p_input->p_method_data,
474                                          i_packet_size + 6 );
475         if( p_data == NULL )
476         {
477             intf_ErrMsg( "Out of memory" );
478             return( -1 );
479         }
480
481         if( U32_AT(p_header) != 0x1B9 )
482         {
483             /* Copy the header we already read. */
484             memcpy( p_data->p_buffer, p_header, 6 );
485
486             /* Read the remaining of the packet. */
487             if( i_packet_size && (i_error =
488                     SafeRead( p_input, p_data->p_buffer + 6, i_packet_size )) )
489             {
490                 return( i_error );
491             }
492
493             /* In MPEG-2 pack headers we still have to read stuffing bytes. */
494             if( U32_AT(p_header) == 0x1BA )
495             {
496                 if( i_packet_size == 8 && (p_data->p_buffer[13] & 0x7) != 0 )
497                 {
498                     /* MPEG-2 stuffing bytes */
499                     byte_t      p_garbage[8];
500                     if( (i_error = SafeRead( p_input, p_garbage,
501                                              p_data->p_buffer[13] & 0x7)) )
502                     {
503                         return( i_error );
504                     }
505                 }
506             }
507         }
508         else
509         {
510             /* Copy the small header. */
511             memcpy( p_data->p_buffer, p_header, 4 );
512         }
513
514         /* Give the packet to the other input stages. */
515         pp_packets[i_packet] = p_data;
516     }
517
518     return( 0 );
519 }
520
521 /*****************************************************************************
522  * PSSetProgram: Does nothing since a PS Stream is mono-program
523  *****************************************************************************/
524 static int PSSetProgram( input_thread_t * p_input, 
525             pgrm_descriptor_t * p_program)
526 {
527     return( 0 );
528 }
529 /*****************************************************************************
530  * PSSeek: changes the stream position indicator
531  *****************************************************************************/
532 static void PSSeek( input_thread_t * p_input, off_t i_position )
533 {
534     /* A little bourrin but should work for a while --Meuuh */
535 #if defined( WIN32 ) || defined( SYS_GNU0_2 )
536     fseek( p_input->p_stream, (long)i_position, SEEK_SET );
537 #else
538     fseeko( p_input->p_stream, i_position, SEEK_SET );
539 #endif
540
541     p_input->stream.p_selected_area->i_tell = i_position;
542 }
543