]> git.sesse.net Git - vlc/blob - modules/mux/mpeg/ps.c
Improvements to preferences
[vlc] / modules / mux / mpeg / ps.c
1 /*****************************************************************************
2  * ps.c: MPEG PS (ISO/IEC 13818-1) / MPEG SYSTEM (ISO/IEC 1172-1)
3  *       multiplexer module for vlc
4  *****************************************************************************
5  * Copyright (C) 2001, 2002 VideoLAN
6  * $Id$
7  *
8  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
9  *          Eric Petit <titer@videolan.org>
10  *          Gildas Bazin <gbazin@videolan.org>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include <stdlib.h>
31
32 #include <vlc/vlc.h>
33 #include <vlc/input.h>
34 #include <vlc/sout.h>
35
36 #include "codecs.h"
37 #include "bits.h"
38 #include "pes.h"
39
40 /*****************************************************************************
41  * Module descriptor
42  *****************************************************************************/
43 #define DTS_TEXT N_("DTS delay (ms)")
44 #define DTS_LONGTEXT N_("This option will delay the DTS (decoding time " \
45   "stamps) and PTS (presentation timestamps) of the data in the " \
46   "stream, compared to the SCRs. This allows for some buffering inside " \
47   "the client decoder.")
48
49 static int     Open   ( vlc_object_t * );
50 static void    Close  ( vlc_object_t * );
51
52 #define SOUT_CFG_PREFIX "sout-ps-"
53
54 vlc_module_begin();
55     set_description( _("PS muxer") );
56     set_name( "MPEG-PS" );
57     set_category( CAT_SOUT );
58     set_subcategory( SUBCAT_SOUT_MUX );
59     set_capability( "sout mux", 50 );
60     add_shortcut( "ps" );
61     add_shortcut( "mpeg1" );
62     add_shortcut( "dvd" );
63     set_callbacks( Open, Close );
64
65     add_integer( SOUT_CFG_PREFIX "dts-delay", 200, NULL, DTS_TEXT,
66                  DTS_LONGTEXT, VLC_TRUE );
67 vlc_module_end();
68
69 /*****************************************************************************
70  * Exported prototypes
71  *****************************************************************************/
72 static int Control  ( sout_mux_t *, int, va_list );
73 static int AddStream( sout_mux_t *, sout_input_t * );
74 static int DelStream( sout_mux_t *, sout_input_t * );
75 static int Mux      ( sout_mux_t * );
76
77 /*****************************************************************************
78  * Local prototypes
79  *****************************************************************************/
80 static int  MuxGetStream        ( sout_mux_t *, int *, mtime_t * );
81
82 static void MuxWritePackHeader  ( sout_mux_t *, block_t **, mtime_t );
83 static void MuxWriteSystemHeader( sout_mux_t *, block_t **, mtime_t );
84
85 static void StreamIdInit        ( vlc_bool_t *id, int i_range );
86 static int  StreamIdGet         ( vlc_bool_t *id, int i_id_min, int i_id_max );
87 static void StreamIdRelease     ( vlc_bool_t *id, int i_id_min, int i_id );
88
89 typedef struct ps_stream_s
90 {
91     int             i_stream_id;
92
93 } ps_stream_t;
94
95 struct sout_mux_sys_t
96 {
97     /* Which id are unused */
98     vlc_bool_t  stream_id_mpga[16]; /* 0xc0 -> 0xcf */
99     vlc_bool_t  stream_id_mpgv[16]; /* 0xe0 -> 0xef */
100     vlc_bool_t  stream_id_a52[8];   /* 0x80 -> 0x87 <- FIXME I'm not sure */
101     vlc_bool_t  stream_id_spu[32];  /* 0x20 -> 0x3f */
102     vlc_bool_t  stream_id_dts[8];   /* 0x88 -> 0x8f */
103     vlc_bool_t  stream_id_lpcm[16]; /* 0xa0 -> 0xaf */
104
105     int i_audio_bound;
106     int i_video_bound;
107     int i_pes_count;
108     int i_system_header;
109     int i_dts_delay;
110
111     vlc_bool_t b_mpeg2;
112 };
113
114 static const char *ppsz_sout_options[] = {
115     "dts-delay", NULL
116 };
117
118 /*****************************************************************************
119  * Open:
120  *****************************************************************************/
121 static int Open( vlc_object_t *p_this )
122 {
123     sout_mux_t *p_mux = (sout_mux_t*)p_this;
124     sout_mux_sys_t *p_sys;
125     vlc_value_t val;
126
127     msg_Info( p_mux, "Open" );
128     sout_CfgParse( p_mux, SOUT_CFG_PREFIX, ppsz_sout_options, p_mux->p_cfg );
129
130     p_mux->pf_control   = Control;
131     p_mux->pf_addstream = AddStream;
132     p_mux->pf_delstream = DelStream;
133     p_mux->pf_mux       = Mux;
134     p_mux->p_sys        = p_sys = malloc( sizeof( sout_mux_sys_t ) );
135
136     /* Init free stream id */
137     StreamIdInit( p_sys->stream_id_a52,  8  );
138     StreamIdInit( p_sys->stream_id_dts,  8  );
139     StreamIdInit( p_sys->stream_id_mpga, 16 );
140     StreamIdInit( p_sys->stream_id_mpgv, 16 );
141     StreamIdInit( p_sys->stream_id_lpcm, 16 );
142     StreamIdInit( p_sys->stream_id_spu,  32 );
143
144     p_sys->i_audio_bound   = 0;
145     p_sys->i_video_bound   = 0;
146     p_sys->i_system_header = 0;
147     p_sys->i_pes_count     = 0;
148
149     p_sys->b_mpeg2 = !(p_mux->psz_mux && !strcmp( p_mux->psz_mux, "mpeg1" ));
150
151     var_Get( p_mux, SOUT_CFG_PREFIX "dts-delay", &val );
152     p_sys->i_dts_delay = (int64_t)val.i_int * 1000;
153
154     return VLC_SUCCESS;
155 }
156
157 /*****************************************************************************
158  * Close:
159  *****************************************************************************/
160 static void Close( vlc_object_t * p_this )
161 {
162     sout_mux_t      *p_mux = (sout_mux_t*)p_this;
163     sout_mux_sys_t  *p_sys = p_mux->p_sys;
164
165     block_t   *p_end;
166
167     msg_Info( p_mux, "Close" );
168
169     p_end = block_New( p_mux, 4 );
170     p_end->p_buffer[0] = 0x00;
171     p_end->p_buffer[1] = 0x00;
172     p_end->p_buffer[2] = 0x01;
173     p_end->p_buffer[3] = 0xb9;
174
175     sout_AccessOutWrite( p_mux->p_access, p_end );
176
177     free( p_sys );
178 }
179
180 /*****************************************************************************
181  * Control:
182  *****************************************************************************/
183 static int Control( sout_mux_t *p_mux, int i_query, va_list args )
184 {
185     vlc_bool_t *pb_bool;
186     char **ppsz;
187
188    switch( i_query )
189    {
190        case MUX_CAN_ADD_STREAM_WHILE_MUXING:
191            pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
192            *pb_bool = VLC_TRUE;
193            return VLC_SUCCESS;
194
195        case MUX_GET_ADD_STREAM_WAIT:
196            pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
197            *pb_bool = VLC_FALSE;
198            return VLC_SUCCESS;
199
200        case MUX_GET_MIME:
201            ppsz = (char**)va_arg( args, char ** );
202            *ppsz = strdup( "video/mpeg" );
203            return VLC_SUCCESS;
204
205         default:
206             return VLC_EGENERIC;
207    }
208 }
209
210 /*****************************************************************************
211  * AddStream:
212  *****************************************************************************/
213 static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
214 {
215     sout_mux_sys_t  *p_sys = p_mux->p_sys;
216     ps_stream_t *p_stream;
217
218     msg_Dbg( p_mux, "adding input codec=%4.4s",
219              (char*)&p_input->p_fmt->i_codec );
220
221     p_input->p_sys = p_stream = malloc( sizeof( ps_stream_t ) );
222
223     /* Init this new stream */
224     switch( p_input->p_fmt->i_codec )
225     {
226         case VLC_FOURCC( 'm', 'p', 'g', 'v' ):
227             p_stream->i_stream_id =
228                 StreamIdGet( p_sys->stream_id_mpgv, 0xe0, 0xef );
229             break;
230         case VLC_FOURCC( 'l', 'p', 'c', 'm' ):
231             p_stream->i_stream_id =
232                 0xbd00 | StreamIdGet( p_sys->stream_id_lpcm, 0xa0, 0xaf );
233             break;
234         case VLC_FOURCC( 'd', 't', 's', ' ' ):
235             p_stream->i_stream_id =
236                 0xbd00 | StreamIdGet( p_sys->stream_id_dts, 0x88, 0x8f );
237             break;
238         case VLC_FOURCC( 'a', '5', '2', ' ' ):
239             p_stream->i_stream_id =
240                 0xbd00 | StreamIdGet( p_sys->stream_id_a52, 0x80, 0x87 );
241             break;
242         case VLC_FOURCC( 'm', 'p', 'g', 'a' ):
243             p_stream->i_stream_id =
244                 StreamIdGet( p_sys->stream_id_mpga, 0xc0, 0xcf );
245             break;
246         case VLC_FOURCC( 's', 'p', 'u', ' ' ):
247             p_stream->i_stream_id =
248                 0xbd00 | StreamIdGet( p_sys->stream_id_spu, 0x20, 0x3f );
249             break;
250         default:
251             goto error;
252     }
253
254     if( p_stream->i_stream_id < 0 )
255     {
256         goto error;
257     }
258
259     if( p_input->p_fmt->i_cat == AUDIO_ES )
260     {
261         p_sys->i_audio_bound++;
262     }
263     else if( p_input->p_fmt->i_cat == VIDEO_ES )
264     {
265         p_sys->i_video_bound++;
266     }
267
268     return VLC_SUCCESS;
269
270 error:
271     free( p_stream );
272     return VLC_EGENERIC;
273 }
274
275 /*****************************************************************************
276  * DelStream:
277  *****************************************************************************/
278 static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
279 {
280     sout_mux_sys_t *p_sys = p_mux->p_sys;
281     ps_stream_t *p_stream =(ps_stream_t*)p_input->p_sys;
282
283     msg_Dbg( p_mux, "removing input" );
284     switch( p_input->p_fmt->i_codec )
285     {
286         case VLC_FOURCC( 'm', 'p', 'g', 'v' ):
287             StreamIdRelease( p_sys->stream_id_mpgv, 0xe0,
288                              p_stream->i_stream_id );
289             break;
290         case VLC_FOURCC( 'l', 'p', 'c', 'm' ):
291             StreamIdRelease( p_sys->stream_id_lpcm, 0xa0,
292                              p_stream->i_stream_id&0xff );
293             break;
294         case VLC_FOURCC( 'd', 't', 's', ' ' ):
295             StreamIdRelease( p_sys->stream_id_dts, 0x88,
296                              p_stream->i_stream_id&0xff );
297             break;
298         case VLC_FOURCC( 'a', '5', '2', ' ' ):
299             StreamIdRelease( p_sys->stream_id_a52, 0x80,
300                              p_stream->i_stream_id&0xff );
301             break;
302         case VLC_FOURCC( 'm', 'p', 'g', 'a' ):
303             StreamIdRelease( p_sys->stream_id_mpga, 0xc0,
304                              p_stream->i_stream_id  );
305             break;
306         case VLC_FOURCC( 's', 'p', 'u', ' ' ):
307             StreamIdRelease( p_sys->stream_id_spu, 0x20,
308                              p_stream->i_stream_id&0xff );
309             break;
310         default:
311             /* Never reached */
312             break;
313     }
314
315     if( p_input->p_fmt->i_cat == AUDIO_ES )
316     {
317         p_sys->i_audio_bound--;
318     }
319     else if( p_input->p_fmt->i_cat == VIDEO_ES )
320     {
321         p_sys->i_video_bound--;
322     }
323
324     free( p_stream );
325     return VLC_SUCCESS;
326 }
327
328 /*****************************************************************************
329  * Mux: Call each time there is new data for at least one stream
330  *****************************************************************************/
331 static int Mux( sout_mux_t *p_mux )
332 {
333     sout_mux_sys_t *p_sys = p_mux->p_sys;
334
335     for( ;; )
336     {
337         sout_input_t *p_input;
338         ps_stream_t *p_stream;
339
340         block_t *p_ps, *p_data;
341
342         mtime_t        i_dts;
343         int            i_stream;
344
345         /* Choose which stream to mux */
346         if( MuxGetStream( p_mux, &i_stream, &i_dts ) )
347         {
348             return VLC_SUCCESS;
349         }
350
351         p_input  = p_mux->pp_inputs[i_stream];
352         p_stream = (ps_stream_t*)p_input->p_sys;
353         p_ps     = NULL;
354
355         /* Write regulary PackHeader */
356         if( p_sys->i_pes_count % 30 == 0)
357         {
358             MuxWritePackHeader( p_mux, &p_ps, i_dts );
359         }
360
361         /* Write regulary SystemHeader */
362         if( p_sys->i_pes_count % 300 == 0 )
363         {
364             block_t *p_pk;
365
366             MuxWriteSystemHeader( p_mux, &p_ps, i_dts );
367
368             /* For MPEG1 streaming, set HEADER flag */
369             for( p_pk = p_ps; p_pk != NULL; p_pk = p_pk->p_next )
370             {
371                 p_pk->i_flags |= BLOCK_FLAG_HEADER;
372             }
373         }
374
375         /* Get and mux a packet */
376         p_data = block_FifoGet( p_input->p_fifo );
377         E_( EStoPES )( p_mux->p_sout, &p_data, p_data,
378                        p_input->p_fmt, p_stream->i_stream_id,
379                        p_mux->p_sys->b_mpeg2, 0, 0 );
380
381         block_ChainAppend( &p_ps, p_data );
382
383         sout_AccessOutWrite( p_mux->p_access, p_ps );
384
385         /* Increase counter */
386         p_sys->i_pes_count++;
387     }
388
389     return VLC_SUCCESS;
390 }
391
392 /*****************************************************************************
393  *
394  *****************************************************************************/
395 static void StreamIdInit( vlc_bool_t *id, int i_range )
396 {
397     int i;
398
399     for( i = 0; i < i_range; i++ )
400     {
401         id[i] = VLC_TRUE;
402     }
403 }
404 static int StreamIdGet( vlc_bool_t *id, int i_id_min, int i_id_max )
405 {
406     int i;
407
408     for( i = 0; i <= i_id_max - i_id_min; i++ )
409     {
410         if( id[i] )
411         {
412             id[i] = VLC_FALSE;
413
414             return i_id_min + i;
415         }
416     }
417     return -1;
418 }
419 static void StreamIdRelease( vlc_bool_t *id, int i_id_min, int i_id )
420 {
421     id[i_id - i_id_min] = VLC_TRUE;
422 }
423
424 static void MuxWritePackHeader( sout_mux_t *p_mux, block_t **p_buf,
425                                 mtime_t i_dts )
426 {
427     sout_mux_sys_t *p_sys = p_mux->p_sys;
428     bits_buffer_t bits;
429     block_t *p_hdr;
430     mtime_t i_scr;
431
432     i_scr = (i_dts - p_sys->i_dts_delay) * 9 / 100;
433
434     p_hdr = block_New( p_mux, 18 );
435     p_hdr->i_pts = p_hdr->i_dts = i_dts;
436     bits_initwrite( &bits, 14, p_hdr->p_buffer );
437     bits_write( &bits, 32, 0x01ba );
438
439     if( p_sys->b_mpeg2 )
440     {
441         bits_write( &bits, 2, 0x01 );
442     }
443     else
444     {
445         bits_write( &bits, 4, 0x02 );
446     }
447
448     bits_write( &bits, 3, ( i_scr >> 30 )&0x07 );
449     bits_write( &bits, 1,  1 );
450     bits_write( &bits, 15, ( i_scr >> 15 )&0x7fff );
451     bits_write( &bits, 1,  1 );
452     bits_write( &bits, 15, i_scr&0x7fff );
453     bits_write( &bits, 1,  1 );
454
455     if( p_sys->b_mpeg2 )
456     {
457         bits_write( &bits, 9,  0 ); // src extention
458     }
459     bits_write( &bits, 1,  1 );
460
461     bits_write( &bits, 22,  1000/8/50); // FIXME mux rate
462     bits_write( &bits, 1,  1 );
463
464     if( p_sys->b_mpeg2 )
465     {
466         bits_write( &bits, 1,  1 );
467         bits_write( &bits, 5,  0x1f );  // FIXME reserved
468         bits_write( &bits, 3,  0 );     // stuffing bytes
469     }
470
471     p_hdr->i_buffer = p_sys->b_mpeg2 ? 14: 12;
472
473     block_ChainAppend( p_buf, p_hdr );
474 }
475
476 static void MuxWriteSystemHeader( sout_mux_t *p_mux, block_t **p_buf,
477                                   mtime_t i_dts )
478 {
479     sout_mux_sys_t  *p_sys = p_mux->p_sys;
480     block_t   *p_hdr;
481     bits_buffer_t   bits;
482     vlc_bool_t      b_private;
483     int             i_nb_private, i_nb_stream;
484     int i;
485
486     /* Count the number of private stream */
487     for( i = 0, i_nb_private = 0; i < p_mux->i_nb_inputs; i++ )
488     {
489         ps_stream_t *p_stream;
490
491         p_stream = (ps_stream_t*)p_mux->pp_inputs[i]->p_sys;
492
493         if( ( p_stream->i_stream_id&0xff00 ) == 0xbd00 )
494         {
495             i_nb_private++;
496         }
497     }
498
499     /* Private stream are declared only one time */
500     i_nb_stream = p_mux->i_nb_inputs -
501         ( i_nb_private > 0 ? i_nb_private - 1 : 0 );
502
503     p_hdr = block_New( p_mux, 12 + i_nb_stream * 3 );
504     p_hdr->i_dts = p_hdr->i_pts = i_dts;
505
506     bits_initwrite( &bits, 12 + i_nb_stream * 3, p_hdr->p_buffer );
507     bits_write( &bits, 32, 0x01bb );
508     bits_write( &bits, 16, 12 - 6 + i_nb_stream * 3 );
509     bits_write( &bits, 1,  1 );
510     bits_write( &bits, 22, 0 ); // FIXME rate bound
511     bits_write( &bits, 1,  1 );
512
513     bits_write( &bits, 6,  p_sys->i_audio_bound );
514     bits_write( &bits, 1,  0 ); // fixed flag
515     bits_write( &bits, 1,  0 ); // CSPS flag
516     bits_write( &bits, 1,  0 ); // system audio lock flag
517     bits_write( &bits, 1,  0 ); // system video lock flag
518
519     bits_write( &bits, 1,  1 ); // marker bit
520
521     bits_write( &bits, 5,  p_sys->i_video_bound );
522     bits_write( &bits, 1,  1 ); // packet rate restriction flag (1 for mpeg1)
523     bits_write( &bits, 7,  0xff ); // reserved bits
524
525     /* stream_id table */
526     for( i = 0, b_private = VLC_FALSE; i < p_mux->i_nb_inputs; i++ )
527     {
528         sout_input_t *p_input;
529         ps_stream_t *p_stream;
530
531         p_input = p_mux->pp_inputs[i];
532         p_stream = (ps_stream_t *)p_input->p_sys;
533
534         if( ( p_stream->i_stream_id&0xff00 ) == 0xbd00 )
535         {
536             if( b_private )
537             {
538                 continue;
539             }
540             b_private = VLC_TRUE;
541             /* Write stream id */
542             bits_write( &bits, 8, 0xbd );
543         }
544         else
545         {
546             /* Write stream id */
547             bits_write( &bits, 8, p_stream->i_stream_id&0xff );
548         }
549         bits_write( &bits, 2, 0x03 );
550         if( p_input->p_fmt->i_cat == AUDIO_ES )
551         {
552             bits_write( &bits, 1, 0 );
553             bits_write( &bits, 13, /* stream->max_buffer_size */ 0 / 128 );
554         }
555         else if( p_input->p_fmt->i_cat == VIDEO_ES )
556         {
557             bits_write( &bits, 1, 1 );
558             bits_write( &bits, 13, /* stream->max_buffer_size */ 0 / 1024);
559         }
560         else
561         {
562             /* FIXME */
563             bits_write( &bits, 1, 0 );
564             bits_write( &bits, 13, /* stream->max_buffer_size */ 0 );
565         }
566     }
567
568     block_ChainAppend( p_buf, p_hdr );
569 }
570
571 /*
572  * Find stream to be muxed.
573  */
574 static int MuxGetStream( sout_mux_t *p_mux, int *pi_stream, mtime_t *pi_dts )
575 {
576     mtime_t i_dts;
577     int     i_stream;
578     int     i;
579
580     for( i = 0, i_dts = 0, i_stream = -1; i < p_mux->i_nb_inputs; i++ )
581     {
582         sout_input_t *p_input = p_mux->pp_inputs[i];
583         block_t *p_data;
584
585         if( p_input->p_fifo->i_depth <= 0 )
586         {
587             if( p_input->p_fmt->i_cat == AUDIO_ES ||
588                 p_input->p_fmt->i_cat == VIDEO_ES )
589             {
590                 /* We need that audio+video fifo contain at least 1 packet */
591                 return VLC_EGENERIC;
592             }
593
594             /* SPU */
595             continue;
596         }
597
598         p_data = block_FifoShow( p_input->p_fifo );
599         if( i_stream == -1 || p_data->i_dts < i_dts )
600         {
601             i_stream = i;
602             i_dts    = p_data->i_dts;
603         }
604     }
605
606     *pi_stream = i_stream;
607     *pi_dts = i_dts;
608
609     return VLC_SUCCESS;
610 }