ts.c 80.2 KB
Newer Older
1
/*****************************************************************************
2
 * ts.c: MPEG-II TS Muxer
3
 *****************************************************************************
4
 * Copyright (C) 2001-2005 VLC authors and VideoLAN
5
 * $Id$
6
7
8
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *          Eric Petit <titer@videolan.org>
9
 *          Jean-Paul Saman <jpsaman #_at_# m2x.nl>
10
 *          Wallace Wadge <wwadge #_at_# gmail.com>
11
 *
12
13
14
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
15
16
17
18
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
21
 *
22
23
24
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25
26
27
28
29
30
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

31
32
33
34
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

35
36
#include <limits.h>

37
#include <vlc_common.h>
38
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
39
40
#include <vlc_sout.h>
#include <vlc_block.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
41
#include <vlc_rand.h>
42

43
#include <vlc_iso_lang.h>
44

45
46
#include "bits.h"
#include "pes.h"
47
#include "csa.h"
48

49
50
51
52
53
54
55
56
# include <dvbpsi/dvbpsi.h>
# include <dvbpsi/demux.h>
# include <dvbpsi/descriptor.h>
# include <dvbpsi/pat.h>
# include <dvbpsi/pmt.h>
# include <dvbpsi/sdt.h>
# include <dvbpsi/dr.h>
# include <dvbpsi/psi.h>
57

58
59
#include "dvbpsi_compat.h"

60
61
62
63
64
/*
 * TODO:
 *  - check PCR frequency requirement
 *  - check PAT/PMT  "        "
 *  - check PCR/PCR "soft"
65
66
 *  - check if "registration" descriptor : "AC-3" should be a program
 *    descriptor or an es one. (xine want an es one)
67
68
69
70
71
72
73
74
 *
 *  - remove creation of PAT/PMT without dvbpsi
 *  - ?
 * FIXME:
 *  - subtitle support is far from perfect. I expect some subtitles drop
 *    if they arrive a bit late
 *    (We cannot rely on the fact that the fifo should be full)
 */
Kaloyan Kovachev's avatar
Kaloyan Kovachev committed
75
76
77
78
79
80
81

/*****************************************************************************
 * Callback prototypes
 *****************************************************************************/
static int ChangeKeyCallback    ( vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void * );
static int ActiveKeyCallback    ( vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void * );

82
83
84
85
86
87
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
static int     Open   ( vlc_object_t * );
static void    Close  ( vlc_object_t * );

zorglub's avatar
zorglub committed
88
#define VPID_TEXT N_("Video PID")
zorglub's avatar
zorglub committed
89
#define VPID_LONGTEXT N_("Assign a fixed PID to the video stream. The PCR " \
90
  "PID will automatically be the video.")
zorglub's avatar
zorglub committed
91
#define APID_TEXT N_("Audio PID")
zorglub's avatar
zorglub committed
92
#define APID_LONGTEXT N_("Assign a fixed PID to the audio stream.")
93
#define SPUPID_TEXT N_("SPU PID")
zorglub's avatar
zorglub committed
94
#define SPUPID_LONGTEXT N_("Assign a fixed PID to the SPU.")
95
#define PMTPID_TEXT N_("PMT PID")
zorglub's avatar
zorglub committed
96
#define PMTPID_LONGTEXT N_("Assign a fixed PID to the PMT")
97
#define TSID_TEXT N_("TS ID")
zorglub's avatar
zorglub committed
98
#define TSID_LONGTEXT N_("Assign a fixed Transport Stream ID.")
99
#define NETID_TEXT N_("NET ID")
zorglub's avatar
zorglub committed
100
101
102
103
104
105
#define NETID_LONGTEXT N_("Assign a fixed Network ID (for SDT table)")

#define PMTPROG_TEXT N_("PMT Program numbers")
#define PMTPROG_LONGTEXT N_("Assign a program number to each PMT. This " \
                            "requires \"Set PID to ID of ES\" to be enabled." )

106
#define MUXPMT_TEXT N_("Mux PMT (requires --sout-ts-es-id-pid)")
zorglub's avatar
zorglub committed
107
108
#define MUXPMT_LONGTEXT N_("Define the pids to add to each pmt. This " \
                           "requires \"Set PID to ID of ES\" to be enabled." )
109
110

#define SDTDESC_TEXT N_("SDT Descriptors (requires --sout-ts-es-id-pid)")
zorglub's avatar
zorglub committed
111
112
#define SDTDESC_LONGTEXT N_("Defines the descriptors of each SDT. This" \
                        "requires \"Set PID to ID of ES\" to be enabled." )
zorglub's avatar
zorglub committed
113

114
115
#define PID_TEXT N_("Set PID to ID of ES")
#define PID_LONGTEXT N_("Sets PID to the ID if the incoming ES. This is for " \
116
  "use with --ts-es-id-pid, and allows having the same PIDs in the input " \
117
118
119
120
  "and output streams.")

#define ALIGNMENT_TEXT N_("Data alignment")
#define ALIGNMENT_LONGTEXT N_("Enforces alignment of all access units on " \
121
  "PES boundaries. Disabling this might save some bandwidth but introduce incompatibilities.")
122

zorglub's avatar
zorglub committed
123
#define SHAPING_TEXT N_("Shaping delay (ms)")
zorglub's avatar
zorglub committed
124
#define SHAPING_LONGTEXT N_("Cut the " \
125
  "stream in slices of the given duration, and ensure a constant bitrate " \
zorglub's avatar
zorglub committed
126
127
128
  "between the two boundaries. This avoids having huge bitrate peaks, " \
  "especially for reference frames." )

129
#define KEYF_TEXT N_("Use keyframes")
130
131
132
133
134
135
#define KEYF_LONGTEXT N_("If enabled, and shaping is specified, " \
  "the TS muxer will place the boundaries at the end of I pictures. In " \
  "that case, the shaping duration given by the user is a worse case " \
  "used when no reference frame is available. This enhances the efficiency " \
  "of the shaping algorithm, since I frames are usually the biggest " \
  "frames in the stream.")
zorglub's avatar
zorglub committed
136

137
#define PCR_TEXT N_("PCR interval (ms)")
zorglub's avatar
zorglub committed
138
139
140
#define PCR_LONGTEXT N_("Set at which interval " \
  "PCRs (Program Clock Reference) will be sent (in milliseconds). " \
  "This value should be below 100ms. (default is 70ms).")
zorglub's avatar
zorglub committed
141

142
143
144
145
146
147
#define BMIN_TEXT N_( "Minimum B (deprecated)")
#define BMIN_LONGTEXT N_( "This setting is deprecated and not used anymore" )

#define BMAX_TEXT N_( "Maximum B (deprecated)")
#define BMAX_LONGTEXT N_( "This setting is deprecated and not used anymore")

zorglub's avatar
zorglub committed
148
#define DTS_TEXT N_("DTS delay (ms)")
zorglub's avatar
zorglub committed
149
#define DTS_LONGTEXT N_("Delay the DTS (decoding time " \
150
151
152
  "stamps) and PTS (presentation timestamps) of the data in the " \
  "stream, compared to the PCRs. This allows for some buffering inside " \
  "the client decoder.")
zorglub's avatar
zorglub committed
153
154
155

#define ACRYPT_TEXT N_("Crypt audio")
#define ACRYPT_LONGTEXT N_("Crypt audio using CSA")
156
157
#define VCRYPT_TEXT N_("Crypt video")
#define VCRYPT_LONGTEXT N_("Crypt video using CSA")
zorglub's avatar
zorglub committed
158
159

#define CK_TEXT N_("CSA Key")
zorglub's avatar
zorglub committed
160
#define CK_LONGTEXT N_("CSA encryption key. This must be a " \
161
162
  "16 char string (8 hexadecimal bytes).")

Kaloyan Kovachev's avatar
Kaloyan Kovachev committed
163
164
165
166
167
168
169
170
#define CK2_TEXT N_("Second CSA Key")
#define CK2_LONGTEXT N_("The even CSA encryption key. This must be a " \
  "16 char string (8 hexadecimal bytes).")

#define CU_TEXT N_("CSA Key in use")
#define CU_LONGTEXT N_("CSA encryption key used. It can be the odd/first/1 " \
  "(default) or the even/second/2 one.")

171
#define CPKT_TEXT N_("Packet size in bytes to encrypt")
zorglub's avatar
zorglub committed
172
#define CPKT_LONGTEXT N_("Size of the TS packet to encrypt. " \
173
    "The encryption routines subtract the TS-header from the value before " \
Christophe Mutricy's avatar
Christophe Mutricy committed
174
    "encrypting." )
175

176
#define SOUT_CFG_PREFIX "sout-ts-"
ivoire's avatar
Typos.    
ivoire committed
177
178
#define MAX_PMT 64       /* Maximum number of programs. FIXME: I just chose an arbitrary number. Where is the maximum in the spec? */
#define MAX_PMT_PID 64       /* Maximum pids in each pmt.  FIXME: I just chose an arbitrary number. Where is the maximum in the spec? */
179

180
181
182
183
184
185
186
vlc_module_begin ()
    set_description( N_("TS muxer (libdvbpsi)") )
    set_shortname( "MPEG-TS")
    set_category( CAT_SOUT )
    set_subcategory( SUBCAT_SOUT_MUX )
    set_capability( "sout mux", 120 )
    add_shortcut( "ts" )
zorglub's avatar
zorglub committed
187

Rafaël Carré's avatar
Rafaël Carré committed
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
    add_integer(SOUT_CFG_PREFIX "pid-video", 0, VPID_TEXT, VPID_LONGTEXT, true)
    add_integer(SOUT_CFG_PREFIX "pid-audio", 0, APID_TEXT, APID_LONGTEXT, true)
    add_integer(SOUT_CFG_PREFIX "pid-spu",   0, SPUPID_TEXT, SPUPID_LONGTEXT, true)
    add_integer(SOUT_CFG_PREFIX "pid-pmt", 0, PMTPID_TEXT, PMTPID_LONGTEXT, true)
    add_integer(SOUT_CFG_PREFIX "tsid",  0, TSID_TEXT, TSID_LONGTEXT, true)
    add_integer(SOUT_CFG_PREFIX "netid", 0, NETID_TEXT, NETID_LONGTEXT, true)
    add_string(SOUT_CFG_PREFIX "program-pmt", NULL, PMTPROG_TEXT, PMTPROG_LONGTEXT, true)
    add_bool(SOUT_CFG_PREFIX "es-id-pid", false, PID_TEXT, PID_LONGTEXT, true)
    add_string(SOUT_CFG_PREFIX "muxpmt",  NULL, MUXPMT_TEXT, MUXPMT_LONGTEXT, true)
    add_string(SOUT_CFG_PREFIX "sdtdesc", NULL, SDTDESC_TEXT, SDTDESC_LONGTEXT, true)
    add_bool(SOUT_CFG_PREFIX "alignment", true, ALIGNMENT_TEXT, ALIGNMENT_LONGTEXT, true)

    add_integer(SOUT_CFG_PREFIX "shaping", 200, SHAPING_TEXT, SHAPING_LONGTEXT, true)
    add_bool(SOUT_CFG_PREFIX "use-key-frames", false, KEYF_TEXT, KEYF_LONGTEXT, true)

    add_integer( SOUT_CFG_PREFIX "pcr", 70, PCR_TEXT, PCR_LONGTEXT, true)
    add_integer( SOUT_CFG_PREFIX "bmin", 0, BMIN_TEXT, BMIN_LONGTEXT, true)
    add_integer( SOUT_CFG_PREFIX "bmax", 0, BMAX_TEXT, BMAX_LONGTEXT, true)
    add_integer( SOUT_CFG_PREFIX "dts-delay", 400, DTS_TEXT, DTS_LONGTEXT, true)

    add_bool( SOUT_CFG_PREFIX "crypt-audio", true, ACRYPT_TEXT, ACRYPT_LONGTEXT, true)
    add_bool( SOUT_CFG_PREFIX "crypt-video", true, VCRYPT_TEXT, VCRYPT_LONGTEXT, true)
    add_string( SOUT_CFG_PREFIX "csa-ck",  NULL, CK_TEXT,   CK_LONGTEXT,   true)
    add_string( SOUT_CFG_PREFIX "csa2-ck", NULL, CK2_TEXT,  CK2_LONGTEXT,  true)
    add_string( SOUT_CFG_PREFIX "csa-use", "1",  CU_TEXT,   CU_LONGTEXT,   true)
    add_integer(SOUT_CFG_PREFIX "csa-pkt", 188,  CPKT_TEXT, CPKT_LONGTEXT, true)
214

215
216
    set_callbacks( Open, Close )
vlc_module_end ()
217
218

/*****************************************************************************
219
 * Local data structures
220
 *****************************************************************************/
221
static const char *const ppsz_sout_options[] = {
222
223
    "pid-video", "pid-audio", "pid-spu", "pid-pmt", "tsid",
    "netid", "sdtdesc",
224
    "es-id-pid", "shaping", "pcr", "bmin", "bmax", "use-key-frames",
Kaloyan Kovachev's avatar
Kaloyan Kovachev committed
225
    "dts-delay", "csa-ck", "csa2-ck", "csa-use", "csa-pkt", "crypt-audio", "crypt-video",
226
    "muxpmt", "program-pmt", "alignment",
227
    NULL
228
229
};

230
231
232
typedef struct pmt_map_t   /* Holds the mapping between the pmt-pid/pmt table */
{
    int i_pid;
Rafaël Carré's avatar
Rafaël Carré committed
233
    unsigned i_prog;
234
235
236
237
} pmt_map_t;

typedef struct sdt_desc_t
{
238
    char *psz_provider;
239
240
241
    char *psz_service_name;  /* name of program */
} sdt_desc_t;

242
243
typedef struct
{
244
    int     i_depth;
245
246
    block_t *p_first;
    block_t **pp_last;
247
248
249
250
251
252
253
254
} sout_buffer_chain_t;

static inline void BufferChainInit  ( sout_buffer_chain_t *c )
{
    c->i_depth = 0;
    c->p_first = NULL;
    c->pp_last = &c->p_first;
}
255

256
static inline void BufferChainAppend( sout_buffer_chain_t *c, block_t *b )
257
258
259
260
261
262
263
264
265
266
267
{
    *c->pp_last = b;
    c->i_depth++;

    while( b->p_next )
    {
        b = b->p_next;
        c->i_depth++;
    }
    c->pp_last = &b->p_next;
}
268

269
static inline block_t *BufferChainGet( sout_buffer_chain_t *c )
270
{
271
    block_t *b = c->p_first;
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286

    if( b )
    {
        c->i_depth--;
        c->p_first = b->p_next;

        if( c->p_first == NULL )
        {
            c->pp_last = &c->p_first;
        }

        b->p_next = NULL;
    }
    return b;
}
287

288
static inline block_t *BufferChainPeek( sout_buffer_chain_t *c )
289
{
290
    block_t *b = c->p_first;
291
292
293

    return b;
}
294

295
static inline void BufferChainClean( sout_buffer_chain_t *c )
296
{
297
    block_t *b;
298
299
300

    while( ( b = BufferChainGet( c ) ) )
    {
301
        block_Release( b );
302
303
304
    }
    BufferChainInit( c );
}
305

306
typedef struct ts_stream_t
307
308
{
    int             i_pid;
309
310
    vlc_fourcc_t    i_codec;

311
312
313
    int             i_stream_type;
    int             i_stream_id;
    int             i_continuity_counter;
314
    bool            b_discontinuity;
315

316
317
318
319
    /* to be used for carriege of DIV3 */
    vlc_fourcc_t    i_bih_codec;
    int             i_bih_width, i_bih_height;

320
321
322
    /* Specific to mpeg4 in mpeg2ts */
    int             i_es_id;

323
324
    int             i_extra;
    uint8_t         *p_extra;
325

326
    /* language is iso639-2T */
327
328
    int             i_langs;
    uint8_t         *lang;
329

330
331
332
333
    sout_buffer_chain_t chain_pes;
    mtime_t             i_pes_dts;
    mtime_t             i_pes_length;
    int                 i_pes_used;
334
    bool                b_key_frame;
335

336
337
} ts_stream_t;

338
struct sout_mux_sys_t
339
340
{
    int             i_pcr_pid;
341
342
    sout_input_t    *p_pcr_input;

Kaloyan Kovachev's avatar
Kaloyan Kovachev committed
343
344
    vlc_mutex_t     csa_lock;

345
346
347
#if (DVBPSI_VERSION_INT >= DVBPSI_VERSION_WANTED(1,0,0))
    dvbpsi_t        *p_dvbpsi;
#endif
348
349
    bool            b_es_id_pid;
    bool            b_sdt;
Christophe Massiot's avatar
Christophe Massiot committed
350
351
    int             i_pid_video;
    int             i_pid_audio;
352
    int             i_pid_spu;
353
    int             i_pid_free; /* first usable pid */
354

355
    int             i_tsid;
356
    int             i_netid;
Rafaël Carré's avatar
Rafaël Carré committed
357
    unsigned        i_num_pmt;
358
    int             i_pmtslots;
359
360
361
362
    int             i_pat_version_number;
    ts_stream_t     pat;

    int             i_pmt_version_number;
363
    ts_stream_t     pmt[MAX_PMT];
364
365
366
    pmt_map_t       pmtmap[MAX_PMT_PID];
    int             i_pmt_program_number[MAX_PMT];
    sdt_desc_t      sdt_descriptors[MAX_PMT];
367
    bool            b_data_alignment;
368

369
370
    int             i_mpeg4_streams;

371
    ts_stream_t     sdt;
372
    dvbpsi_pmt_t    *dvbpmt;
373

374
    /* for TS building */
375
376
    int64_t         i_bitrate_min;
    int64_t         i_bitrate_max;
377

378
379
    int64_t         i_shaping_delay;
    int64_t         i_pcr_delay;
Laurent Aimar's avatar
Laurent Aimar committed
380

381
    int64_t         i_dts_delay;
382
    mtime_t         first_dts;
383

384
    bool            b_use_key_frames;
385

386
    mtime_t         i_pcr;  /* last PCR emited */
387

388
389
390
391
    csa_t           *csa;
    int             i_csa_pkt_size;
    bool            b_crypt_audio;
    bool            b_crypt_video;
392
};
393

394
/* Reserve a pid and return it */
Christophe Massiot's avatar
Christophe Massiot committed
395
static int  AllocatePID( sout_mux_sys_t *p_sys, int i_cat )
396
{
Christophe Massiot's avatar
Christophe Massiot committed
397
398
399
400
401
402
403
404
405
406
407
    int i_pid;
    if ( i_cat == VIDEO_ES && p_sys->i_pid_video )
    {
        i_pid = p_sys->i_pid_video;
        p_sys->i_pid_video = 0;
    }
    else if ( i_cat == AUDIO_ES && p_sys->i_pid_audio )
    {
        i_pid = p_sys->i_pid_audio;
        p_sys->i_pid_audio = 0;
    }
408
409
410
411
412
    else if ( i_cat == SPU_ES && p_sys->i_pid_spu )
    {
        i_pid = p_sys->i_pid_spu;
        p_sys->i_pid_spu = 0;
    }
Christophe Massiot's avatar
Christophe Massiot committed
413
414
415
416
417
    else
    {
        i_pid = ++p_sys->i_pid_free;
    }
    return i_pid;
418
419
}

420
421
static int pmtcompare( const void *pa, const void *pb )
{
422
423
424
425
    int id1 = ((pmt_map_t *)pa)->i_pid;
    int id2 = ((pmt_map_t *)pb)->i_pid;

    return id1 - id2;
426
427
428
429
}

static int intcompare( const void *pa, const void *pb )
{
430
    return *(int*)pa - *(int*)pb;
431
432
}

433
434
435
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
436
437
438
439
static int Control  ( sout_mux_t *, int, va_list );
static int AddStream( sout_mux_t *, sout_input_t * );
static int DelStream( sout_mux_t *, sout_input_t * );
static int Mux      ( sout_mux_t * );
440

441
static block_t *FixPES( sout_mux_t *p_mux, block_fifo_t *p_fifo );
Rafaël Carré's avatar
Rafaël Carré committed
442
static block_t *Add_ADTS( block_t *, es_format_t * );
443
444
445
446
static void TSSchedule  ( sout_mux_t *p_mux, sout_buffer_chain_t *p_chain_ts,
                          mtime_t i_pcr_length, mtime_t i_pcr_dts );
static void TSDate      ( sout_mux_t *p_mux, sout_buffer_chain_t *p_chain_ts,
                          mtime_t i_pcr_length, mtime_t i_pcr_dts );
447
448
static void GetPAT( sout_mux_t *p_mux, sout_buffer_chain_t *c );
static void GetPMT( sout_mux_t *p_mux, sout_buffer_chain_t *c );
449

450
static block_t *TSNew( sout_mux_t *p_mux, ts_stream_t *p_stream, bool b_pcr );
451
static void TSSetPCR( block_t *p_ts, mtime_t i_dts );
452

Rafaël Carré's avatar
Rafaël Carré committed
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
static csa_t *csaSetup( vlc_object_t *p_this )
{
    sout_mux_t *p_mux = (sout_mux_t*)p_this;
    sout_mux_sys_t *p_sys = p_mux->p_sys;
    char *csack = var_CreateGetNonEmptyStringCommand( p_mux, SOUT_CFG_PREFIX "csa-ck" );
    if( !csack )
        return NULL;

    csa_t *csa = csa_New();

    if( csa_SetCW( p_this, csa, csack, true ) )
    {
        free(csack);
        csa_Delete( csa );
        return NULL;
    }

    vlc_mutex_init( &p_sys->csa_lock );
    p_sys->b_crypt_audio = var_GetBool( p_mux, SOUT_CFG_PREFIX "crypt-audio" );
    p_sys->b_crypt_video = var_GetBool( p_mux, SOUT_CFG_PREFIX "crypt-video" );

    char *csa2ck = var_CreateGetNonEmptyStringCommand( p_mux, SOUT_CFG_PREFIX "csa2-ck");
475
476
    if (!csa2ck || csa_SetCW( p_this, csa, csa2ck, false ) )
        csa_SetCW( p_this, csa, csack, false );
Rafaël Carré's avatar
Rafaël Carré committed
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
    free(csa2ck);

    var_Create( p_mux, SOUT_CFG_PREFIX "csa-use", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_mux, SOUT_CFG_PREFIX "csa-use", ActiveKeyCallback, NULL );
    var_AddCallback( p_mux, SOUT_CFG_PREFIX "csa-ck", ChangeKeyCallback, (void *)1 );
    var_AddCallback( p_mux, SOUT_CFG_PREFIX "csa2-ck", ChangeKeyCallback, NULL );

    vlc_value_t use_val;
    var_Get( p_mux, SOUT_CFG_PREFIX "csa-use", &use_val );
    if ( var_Set( p_mux, SOUT_CFG_PREFIX "csa-use", use_val ) )
        var_SetString( p_mux, SOUT_CFG_PREFIX "csa-use", "odd" );
    free( use_val.psz_string );

    p_sys->i_csa_pkt_size = var_GetInteger( p_mux, SOUT_CFG_PREFIX "csa-pkt" );
    if( p_sys->i_csa_pkt_size < 12 || p_sys->i_csa_pkt_size > 188 )
    {
        msg_Err( p_mux, "wrong packet size %d specified",
            p_sys->i_csa_pkt_size );
        p_sys->i_csa_pkt_size = 188;
    }

    msg_Dbg( p_mux, "encrypting %d bytes of packet", p_sys->i_csa_pkt_size );

    free(csack);

    return csa;
}

505
506
507
508
509
/*****************************************************************************
 * Open:
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
510
    sout_mux_t          *p_mux =(sout_mux_t*)p_this;
511
    sout_mux_sys_t      *p_sys = NULL;
512

513
    config_ChainParse( p_mux, SOUT_CFG_PREFIX, ppsz_sout_options, p_mux->p_cfg );
514

Rafaël Carré's avatar
Rafaël Carré committed
515
    p_sys = calloc( 1, sizeof( sout_mux_sys_t ) );
516
517
    if( !p_sys )
        return VLC_ENOMEM;
518
    p_sys->i_num_pmt = 1;
519
520


521
#if (DVBPSI_VERSION_INT >= DVBPSI_VERSION_WANTED(1,0,0))
Jean-Paul Saman's avatar
Jean-Paul Saman committed
522
    p_sys->p_dvbpsi = dvbpsi_new( &dvbpsi_messages, DVBPSI_MSG_DEBUG );
523
524
525
526
527
528
529
530
    if( !p_sys->p_dvbpsi )
    {
        free( p_sys );
        return VLC_ENOMEM;
    }
    p_sys->p_dvbpsi->p_sys = (void *) p_mux;
#endif

ivoire's avatar
ivoire committed
531
    p_sys->b_es_id_pid = var_GetBool( p_mux, SOUT_CFG_PREFIX "es-id-pid" );
532

533
    /*
534
535
536
       fetch string of pmts. Here's a sample: --sout-ts-muxpmt="0x451,0x200,0x28a,0x240,,0x450,0x201,0x28b,0x241,,0x452,0x202,0x28c,0x242"
       This would mean 0x451, 0x200, 0x28a, 0x240 would fall under one pmt (program), 0x450,0x201,0x28b,0x241 would fall under another
    */
Rafaël Carré's avatar
Rafaël Carré committed
537
    char *muxpmt = var_GetNonEmptyString(p_mux, SOUT_CFG_PREFIX "muxpmt");
538
    for (char *psz = muxpmt; psz; )
539
540
    {
        char *psz_next;
541
542
        uint16_t i_pid = strtoul( psz, &psz_next, 0 );
        psz = *psz_next ? &psz_next[1] : NULL;
543

544
        if ( i_pid == 0 )
545
        {
546
            if ( ++p_sys->i_num_pmt > MAX_PMT )
547
            {
548
549
                msg_Err( p_mux, "Number of PMTs > %d)", MAX_PMT );
                p_sys->i_num_pmt = MAX_PMT;
550
            }
551
552
553
554
555
        }
        else
        {
            p_sys->pmtmap[p_sys->i_pmtslots].i_pid = i_pid;
            p_sys->pmtmap[p_sys->i_pmtslots].i_prog = p_sys->i_num_pmt - 1;
556
            if ( ++p_sys->i_pmtslots >= MAX_PMT_PID )
557
            {
558
                msg_Err( p_mux, "Number of pids in PMT > %d", MAX_PMT_PID );
559
                p_sys->i_pmtslots = MAX_PMT_PID - 1;
560
561
562
            }
        }
    }
563
564
565
566
    /* Now sort according to pids for fast search later on */
    qsort( (void *)p_sys->pmtmap, p_sys->i_pmtslots,
            sizeof(pmt_map_t), pmtcompare );
    free(muxpmt);
567

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
568
569
570
    unsigned short subi[3];
    vlc_rand_bytes(subi, sizeof(subi));
    p_sys->i_pat_version_number = nrand48(subi) & 0x1f;
571

Rafaël Carré's avatar
Rafaël Carré committed
572
    vlc_value_t val;
573
574
575
576
    var_Get( p_mux, SOUT_CFG_PREFIX "tsid", &val );
    if ( val.i_int )
        p_sys->i_tsid = val.i_int;
    else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
577
        p_sys->i_tsid = nrand48(subi) & 0xffff;
578

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
579
    p_sys->i_netid = nrand48(subi) & 0xffff;
580

581
582
583
584
    var_Get( p_mux, SOUT_CFG_PREFIX "netid", &val );
    if ( val.i_int )
        p_sys->i_netid = val.i_int;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
585
    p_sys->i_pmt_version_number = nrand48(subi) & 0x1f;
586
    p_sys->sdt.i_pid = 0x11;
587

Rafaël Carré's avatar
Rafaël Carré committed
588
    char *sdtdesc = var_GetNonEmptyString( p_mux, SOUT_CFG_PREFIX "sdtdesc" );
589

590
    /* Syntax is provider_sdt1,service_name_sdt1,provider_sdt2,service_name_sdt2... */
Rafaël Carré's avatar
Rafaël Carré committed
591
    if( sdtdesc )
592
    {
Rafaël Carré's avatar
Rafaël Carré committed
593
        p_sys->b_sdt = true;
594

Rafaël Carré's avatar
Rafaël Carré committed
595
        char *psz_sdttoken = sdtdesc;
596

Rafaël Carré's avatar
Rafaël Carré committed
597
        for (int i = 0; i < MAX_PMT * 2 && psz_sdttoken; i++)
598
        {
Rafaël Carré's avatar
Rafaël Carré committed
599
            sdt_desc_t *sdt = &p_sys->sdt_descriptors[i/2];
600
            char *psz_end = strchr( psz_sdttoken, ',' );
Rafaël Carré's avatar
Rafaël Carré committed
601
            if ( psz_end )
602
                *psz_end++ = '\0';
Rafaël Carré's avatar
Rafaël Carré committed
603
604
605

            if (i % 2)
                sdt->psz_service_name = strdup(psz_sdttoken);
606
            else
Rafaël Carré's avatar
Rafaël Carré committed
607
                sdt->psz_provider = strdup(psz_sdttoken);
608
609
610

            psz_sdttoken = psz_end;
        }
Rafaël Carré's avatar
Rafaël Carré committed
611
        free(sdtdesc);
612
    }
613

ivoire's avatar
ivoire committed
614
    p_sys->b_data_alignment = var_GetBool( p_mux, SOUT_CFG_PREFIX "alignment" );
615

Rafaël Carré's avatar
Rafaël Carré committed
616
    char *pgrpmt = var_GetNonEmptyString(p_mux, SOUT_CFG_PREFIX "program-pmt");
Rafaël Carré's avatar
Rafaël Carré committed
617
    if( pgrpmt )
618
    {
Rafaël Carré's avatar
Rafaël Carré committed
619
        char *psz = pgrpmt;
Rafaël Carré's avatar
Rafaël Carré committed
620
        char *psz_next = psz;
621

Rafaël Carré's avatar
Rafaël Carré committed
622
        for (int i = 0; psz; )
623
        {
Rafaël Carré's avatar
Rafaël Carré committed
624
            uint16_t i_pid = strtoul( psz, &psz_next, 0 );
625
            if( psz_next[0] != '\0' )
626
627
628
                psz = &psz_next[1];
            else
                psz = NULL;
629

630
            if( i_pid == 0 )
631
            {
632
                if( i >= MAX_PMT )
Rafaël Carré's avatar
Rafaël Carré committed
633
                    msg_Err( p_mux, "Number of PMTs > maximum (%d)", MAX_PMT );
634
635
636
637
638
639
640
            }
            else
            {
                p_sys->i_pmt_program_number[i] = i_pid;
                i++;
            }
        }
Rafaël Carré's avatar
Rafaël Carré committed
641
        free(pgrpmt);
642
643
644
    }
    else
    {
645
        /* Option not specified, use 1, 2, 3... */
Rafaël Carré's avatar
Rafaël Carré committed
646
        for (unsigned i = 0; i < p_sys->i_num_pmt; i++ )
647
            p_sys->i_pmt_program_number[i] = i + 1;
648
649
    }

650
    var_Get( p_mux, SOUT_CFG_PREFIX "pid-pmt", &val );
Rafaël Carré's avatar
Rafaël Carré committed
651
652
    if( !val.i_int ) /* Does this make any sense? */
        val.i_int = 0x42;
Rafaël Carré's avatar
Rafaël Carré committed
653
    for (unsigned i = 0; i < p_sys->i_num_pmt; i++ )
654
        p_sys->pmt[i].i_pid = val.i_int + i;
655

656
    p_sys->i_pid_free = p_sys->pmt[p_sys->i_num_pmt - 1].i_pid + 1;
657

ivoire's avatar
ivoire committed
658
    p_sys->i_pid_video = var_GetInteger( p_mux, SOUT_CFG_PREFIX "pid-video" );
659
    if ( p_sys->i_pid_video > p_sys->i_pid_free )
Christophe Massiot's avatar
Christophe Massiot committed
660
    {
661
        p_sys->i_pid_free = p_sys->i_pid_video + 1;
Christophe Massiot's avatar
Christophe Massiot committed
662
    }
663

ivoire's avatar
ivoire committed
664
    p_sys->i_pid_audio = var_GetInteger( p_mux, SOUT_CFG_PREFIX "pid-audio" );
665
    if ( p_sys->i_pid_audio > p_sys->i_pid_free )
Christophe Massiot's avatar
Christophe Massiot committed
666
    {
667
        p_sys->i_pid_free = p_sys->i_pid_audio + 1;
Christophe Massiot's avatar
Christophe Massiot committed
668
669
    }

ivoire's avatar
ivoire committed
670
    p_sys->i_pid_spu = var_GetInteger( p_mux, SOUT_CFG_PREFIX "pid-spu" );
671
672
673
674
675
    if ( p_sys->i_pid_spu > p_sys->i_pid_free )
    {
        p_sys->i_pid_free = p_sys->i_pid_spu + 1;
    }

676
    p_sys->i_pcr_pid = 0x1fff;
677

678
    /* Allow to create constrained stream */
ivoire's avatar
ivoire committed
679
    p_sys->i_bitrate_min = var_GetInteger( p_mux, SOUT_CFG_PREFIX "bmin" );
680

ivoire's avatar
ivoire committed
681
    p_sys->i_bitrate_max = var_GetInteger( p_mux, SOUT_CFG_PREFIX "bmax" );
682

683
684
685
    if( p_sys->i_bitrate_min > 0 && p_sys->i_bitrate_max > 0 &&
        p_sys->i_bitrate_min > p_sys->i_bitrate_max )
    {
gbazin's avatar
   
gbazin committed
686
687
        msg_Err( p_mux, "incompatible minimum and maximum bitrate, "
                 "disabling bitrate control" );
688
689
690
        p_sys->i_bitrate_min = 0;
        p_sys->i_bitrate_max = 0;
    }
691
    if( p_sys->i_bitrate_min > 0 || p_sys->i_bitrate_max > 0 )
692
    {
693
694
        msg_Err( p_mux, "bmin and bmax no more supported "
                 "(if you need them report it)" );
695
696
    }

697
    var_Get( p_mux, SOUT_CFG_PREFIX "shaping", &val );
Rafaël Carré's avatar
Rafaël Carré committed
698
    p_sys->i_shaping_delay = val.i_int * 1000;
699
    if( p_sys->i_shaping_delay <= 0 )
700
    {
701
        msg_Err( p_mux,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
702
                 "invalid shaping (%"PRId64"ms) resetting to 200ms",
703
704
                 p_sys->i_shaping_delay / 1000 );
        p_sys->i_shaping_delay = 200000;
705
    }
gbazin's avatar
   
gbazin committed
706

707
    var_Get( p_mux, SOUT_CFG_PREFIX "pcr", &val );
Rafaël Carré's avatar
Rafaël Carré committed
708
    p_sys->i_pcr_delay = val.i_int * 1000;
709
710
    if( p_sys->i_pcr_delay <= 0 ||
        p_sys->i_pcr_delay >= p_sys->i_shaping_delay )
711
    {
712
        msg_Err( p_mux,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
713
                 "invalid pcr delay (%"PRId64"ms) resetting to 70ms",
714
                 p_sys->i_pcr_delay / 1000 );
715
        p_sys->i_pcr_delay = 70000;
716
717
    }

718
    var_Get( p_mux, SOUT_CFG_PREFIX "dts-delay", &val );
Rafaël Carré's avatar
Rafaël Carré committed
719
    p_sys->i_dts_delay = val.i_int * 1000;
720

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
721
    msg_Dbg( p_mux, "shaping=%"PRId64" pcr=%"PRId64" dts_delay=%"PRId64,
722
723
             p_sys->i_shaping_delay, p_sys->i_pcr_delay, p_sys->i_dts_delay );

ivoire's avatar
ivoire committed
724
    p_sys->b_use_key_frames = var_GetBool( p_mux, SOUT_CFG_PREFIX "use-key-frames" );
725

726
727
    p_mux->p_sys        = p_sys;

Rafaël Carré's avatar
Rafaël Carré committed
728
    p_sys->csa = csaSetup(p_this);
729

730
731
732
733
734
    p_mux->pf_control   = Control;
    p_mux->pf_addstream = AddStream;
    p_mux->pf_delstream = DelStream;
    p_mux->pf_mux       = Mux;

735
736
737
738
739
740
741
742
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Close:
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
743
744
    sout_mux_t          *p_mux = (sout_mux_t*)p_this;
    sout_mux_sys_t      *p_sys = p_mux->p_sys;
745

746
747
748
749
750
#if (DVBPSI_VERSION_INT >= DVBPSI_VERSION_WANTED(1,0,0))
    if( p_sys->p_dvbpsi )
        dvbpsi_delete( p_sys->p_dvbpsi );
#endif

751
752
    if( p_sys->csa )
    {
Kaloyan Kovachev's avatar
Kaloyan Kovachev committed
753
754
755
        var_DelCallback( p_mux, SOUT_CFG_PREFIX "csa-ck", ChangeKeyCallback, NULL );
        var_DelCallback( p_mux, SOUT_CFG_PREFIX "csa2-ck", ChangeKeyCallback, NULL );
        var_DelCallback( p_mux, SOUT_CFG_PREFIX "csa-use", ActiveKeyCallback, NULL );
756
        csa_Delete( p_sys->csa );
Rafaël Carré's avatar
Rafaël Carré committed
757
        vlc_mutex_destroy( &p_sys->csa_lock );
758
    }
Kaloyan Kovachev's avatar
Kaloyan Kovachev committed
759

Rafaël Carré's avatar
Rafaël Carré committed
760
    for (int i = 0; i < MAX_PMT; i++ )
761
    {
762
763
        free( p_sys->sdt_descriptors[i].psz_service_name );
        free( p_sys->sdt_descriptors[i].psz_provider );
764
765
    }

766
    free( p_sys->dvbpmt );
767
    free( p_sys );
768
769
}

Kaloyan Kovachev's avatar
Kaloyan Kovachev committed
770
771
772
773
774
775
776
777
778
779
/*****************************************************************************
 * ChangeKeyCallback: called when changing the odd encryption key on the fly.
 *****************************************************************************/
static int ChangeKeyCallback( vlc_object_t *p_this, char const *psz_cmd,
                           vlc_value_t oldval, vlc_value_t newval,
                           void *p_data )
{
    VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
    sout_mux_t      *p_mux = (sout_mux_t*)p_this;
    sout_mux_sys_t  *p_sys = p_mux->p_sys;
780
    int ret;
Kaloyan Kovachev's avatar
Kaloyan Kovachev committed
781

Rafaël Carré's avatar
Rafaël Carré committed
782
783
784
    vlc_mutex_lock(&p_sys->csa_lock);
    ret = csa_SetCW(p_this, p_sys->csa, newval.psz_string, !!(intptr_t)p_data);
    vlc_mutex_unlock(&p_sys->csa_lock);
Kaloyan Kovachev's avatar
Kaloyan Kovachev committed
785

786
    return ret;
Kaloyan Kovachev's avatar
Kaloyan Kovachev committed
787
788
789
790
791
792
793
794
795
796
797
798
}

/*****************************************************************************
 * ActiveKeyCallback: called when changing the active (in use) encryption key on the fly.
 *****************************************************************************/
static int ActiveKeyCallback( vlc_object_t *p_this, char const *psz_cmd,
                           vlc_value_t oldval, vlc_value_t newval,
                           void *p_data )
{
    VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
    sout_mux_t      *p_mux = (sout_mux_t*)p_this;
    sout_mux_sys_t  *p_sys = p_mux->p_sys;
799
    int             i_res, use_odd = -1;
Kaloyan Kovachev's avatar
Kaloyan Kovachev committed
800

801
802
803
    if( !strcmp(newval.psz_string, "odd" ) ||
        !strcmp(newval.psz_string, "first" ) ||
        !strcmp(newval.psz_string, "1" ) )
Kaloyan Kovachev's avatar
Kaloyan Kovachev committed
804
    {
805
        use_odd = 1;
Kaloyan Kovachev's avatar
Kaloyan Kovachev committed
806
    }
807
808
809
    else if( !strcmp(newval.psz_string, "even" ) ||
             !strcmp(newval.psz_string, "second" ) ||
             !strcmp(newval.psz_string, "2" ) )
Kaloyan Kovachev's avatar
Kaloyan Kovachev committed
810
    {
811
        use_odd = 0;
Kaloyan Kovachev's avatar
Kaloyan Kovachev committed
812
    }
813
814
815
816
817
818

    if (use_odd < 0)
        return VLC_EBADVAR;

    vlc_mutex_lock( &p_sys->csa_lock );
    i_res = csa_UseKey( p_this, p_sys->csa, use_odd );
Kaloyan Kovachev's avatar
Kaloyan Kovachev committed
819
820
821
822
823
    vlc_mutex_unlock( &p_sys->csa_lock );

    return i_res;
}

824
/*****************************************************************************
825
 * Control:
826
 *****************************************************************************/
827
static int Control( sout_mux_t *p_mux, int i_query, va_list args )
828
{
829
    VLC_UNUSED(p_mux);
830
    bool *pb_bool;
831
832
    char **ppsz;

833
834
    switch( i_query )
    {
835
836
837
838
839
840
841
842
843
844
845
846
    case MUX_CAN_ADD_STREAM_WHILE_MUXING:
        pb_bool = (bool*)va_arg( args, bool * );
        *pb_bool = true;
        return VLC_SUCCESS;

    case MUX_GET_ADD_STREAM_WAIT:
        pb_bool = (bool*)va_arg( args, bool * );
        *pb_bool = false;
        return VLC_SUCCESS;

    case MUX_GET_MIME:
        ppsz = (char**)va_arg( args, char ** );
Rafaël Carré's avatar
Rafaël Carré committed
847
        *ppsz = strdup( "video/mp2t" );
848
849
850
851
        return VLC_SUCCESS;

    default:
        return VLC_EGENERIC;
852
    }
853
}
854

855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
/* returns a pointer to a valid string, with length 0 or 3 */
static const char *GetIso639_2LangCode(const char *lang)
{
    const iso639_lang_t *pl;

    if (strlen(lang) == 2)
    {
        pl = GetLang_1(lang);
    }
    else
    {
        pl = GetLang_2B(lang);      /* try native code first */
        if (!*pl->psz_iso639_2T)
            pl = GetLang_2T(lang);  /* else fallback to english code */

    }

    return pl->psz_iso639_2T;   /* returns the english code */
}

875
876
877
/*****************************************************************************
 * AddStream: called for each stream addition
 *****************************************************************************/
878
static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
879
{
880
    sout_mux_sys_t      *p_sys = p_mux->p_sys;
881
882
    ts_stream_t         *p_stream;

883
    p_input->p_sys = p_stream = calloc( 1, sizeof( ts_stream_t ) );
Rafaël Carré's avatar
Rafaël Carré committed
884
885
    if( !p_stream )
        goto oom;
886

887
888
889
890
    if ( p_sys->b_es_id_pid )
        p_stream->i_pid = p_input->p_fmt->i_id & 0x1fff;
    else
        p_stream->i_pid = AllocatePID( p_sys, p_input->p_fmt->i_cat );
891

892
    p_stream->i_codec = p_input->p_fmt->i_codec;
893

894
895
    p_stream->i_stream_type = -1;
    switch( p_input->p_fmt->i_codec )
896
    {
897
898
899
    /* VIDEO */

    case VLC_CODEC_MPGV:
Ilkka Ollakka's avatar
Ilkka Ollakka committed
900
901
    case VLC_CODEC_MP2V:
    case VLC_CODEC_MP1V:
902
903
904
905
906
907
908
909
910
        /* TODO: do we need to check MPEG-I/II ? */
        p_stream->i_stream_type = 0x02;
        p_stream->i_stream_id = 0xe0;
        break;
    case VLC_CODEC_MP4V:
        p_stream->i_stream_type = 0x10;
        p_stream->i_stream_id = 0xe0;
        p_stream->i_es_id = p_stream->i_pid;
        break;
Rafaël Carré's avatar
Rafaël Carré committed
911
912
913
914
    case VLC_CODEC_HEVC:
        p_stream->i_stream_type = 0x24;
        p_stream->i_stream_id = 0xe0;
        break;
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
    case VLC_CODEC_H264:
        p_stream->i_stream_type = 0x1b;
        p_stream->i_stream_id = 0xe0;
        break;
    /* XXX dirty dirty but somebody want crapy MS-codec XXX */
    case VLC_CODEC_H263I:
    case VLC_CODEC_H263:
    case VLC_CODEC_WMV3:
    case VLC_CODEC_WMV2:
    case VLC_CODEC_WMV1:
    case VLC_CODEC_DIV3:
    case VLC_CODEC_DIV2:
    case VLC_CODEC_DIV1:
    case VLC_CODEC_MJPG:
        p_stream->i_stream_type = 0xa0; /* private */
        p_stream->i_stream_id = 0xa0;   /* beurk */
        p_stream->i_bih_codec  = p_input->p_fmt->i_codec;
        p_stream->i_bih_width  = p_input->p_fmt->video.i_width;
        p_stream->i_bih_height = p_input->p_fmt->video.i_height;
        break;
    case VLC_CODEC_DIRAC:
        /* stream_id makes use of stream_id_extension */
        p_stream->i_stream_id = (PES_EXTENDED_STREAM_ID << 8) | 0x60;
        p_stream->i_stream_type = 0xd1;
939
        break;
940

941
942
943
    /* AUDIO */

    case VLC_CODEC_MPGA:
944
    case VLC_CODEC_MP3:
945
946
947
948
949
950
951
952
953
954
955
956
        p_stream->i_stream_type =
            p_input->p_fmt->audio.i_rate >= 32000 ? 0x03 : 0x04;
        p_stream->i_stream_id = 0xc0;
        break;
    case VLC_CODEC_A52:
        p_stream->i_stream_type = 0x81;
        p_stream->i_stream_id = 0xbd;
        break;
    case VLC_CODEC_DVD_LPCM:
        p_stream->i_stream_type = 0x83;
        p_stream->i_stream_id = 0xbd;
        break;
957
    case VLC_CODEC_EAC3:
958
959
960
961
962
963
964
965
966
967
968
    case VLC_CODEC_DTS:
        p_stream->i_stream_type = 0x06;
        p_stream->i_stream_id = 0xbd;
        break;
    case VLC_CODEC_MP4A:
        /* XXX: make that configurable in some way when LOAS
         * is implemented for AAC in TS */
        //p_stream->i_stream_type = 0x11; /* LOAS/LATM */
        p_stream->i_stream_type = 0x0f; /* ADTS */
        p_stream->i_stream_id = 0xc0;
        p_stream->i_es_id = p_stream->i_pid;
969
        break;
970

971
972
973
974
975
    /* TEXT */

    case VLC_CODEC_SPU:
        p_stream->i_stream_type = 0x82;
        p_stream->i_stream_id = 0xbd;
976
        break;
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
    case VLC_CODEC_SUBT:
        p_stream->i_stream_type = 0x12;
        p_stream->i_stream_id = 0xfa;
        p_sys->i_mpeg4_streams++;
        p_stream->i_es_id = p_stream->i_pid;
        break;
    case VLC_CODEC_DVBS:
        p_stream->i_stream_type = 0x06;
        p_stream->i_es_id = p_input->p_fmt->subs.dvb.i_id;
        p_stream->i_stream_id = 0xbd;
        break;
    case VLC_CODEC_TELETEXT:
        p_stream->i_stream_type = 0x06;
        p_stream->i_stream_id = 0xbd; /* FIXME */
        break;
    }
993

994
995
    if (p_stream->i_stream_type == -1)
    {
996
997
        msg_Warn( p_mux, "rejecting stream with unsupported codec %4.4s",
                  (char*)&p_stream->i_codec );
998
999
        free( p_stream );
        return VLC_EGENERIC;
1000
1001
    }

1002
1003
    p_stream->i_langs = 1 + p_input->p_fmt->i_extra_languages;
    p_stream->lang = calloc(1, p_stream->i_langs * 4);