asf.c 34.9 KB
Newer Older
1
/*****************************************************************************
2
 * asf.c: asf muxer module for vlc
3
 *****************************************************************************
4
 * Copyright (C) 2003-2004 VideoLAN
5
 * $Id$
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (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
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>

#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc/sout.h>

#include "codecs.h"
gbazin's avatar
gbazin committed
34
typedef GUID guid_t;
35
36
37
38
39
40
41

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
static int  Open   ( vlc_object_t * );
static void Close  ( vlc_object_t * );

42
43
#define SOUT_CFG_PREFIX "sout-asf-"

zorglub's avatar
zorglub committed
44
45
46
47
#define TITLE_TEXT N_("Title")
#define TITLE_LONGTEXT N_("Allows you to define the title that will be put " \
                          "in ASF comments.")
#define AUTHOR_TEXT N_("Author")
48
49
#define AUTHOR_LONGTEXT N_("Allows you to define the author that will be put "\
                           "in ASF comments.")
zorglub's avatar
zorglub committed
50
#define COPYRIGHT_TEXT N_("Copyright")
51
52
#define COPYRIGHT_LONGTEXT N_("Allows you to define the copyright string " \
                              "that will be put in ASF comments.")
zorglub's avatar
zorglub committed
53
54
55
56
57
58
59
#define COMMENT_TEXT N_("Comment")
#define COMMENT_LONGTEXT N_("Allows you to define the comment that will be " \
                            "put in ASF comments.")
#define RATING_TEXT N_("Rating")
#define RATING_LONGTEXT N_("Allows you to define the \"rating\" that will " \
                           "be put in ASF comments.")

60
vlc_module_begin();
Sam Hocevar's avatar
Sam Hocevar committed
61
    set_description( _("ASF muxer") );
62
63
    set_capability( "sout mux", 5 );
    add_shortcut( "asf" );
64
    add_shortcut( "asfh" );
65
    set_callbacks( Open, Close );
66

zorglub's avatar
zorglub committed
67
68
69
70
71
72
73
74
75
76
    add_string( SOUT_CFG_PREFIX "title", "", NULL, TITLE_TEXT, TITLE_LONGTEXT,
                                 VLC_TRUE );
    add_string( SOUT_CFG_PREFIX "author",   "", NULL, AUTHOR_TEXT,
                                 AUTHOR_LONGTEXT, VLC_TRUE );
    add_string( SOUT_CFG_PREFIX "copyright","", NULL, COPYRIGHT_TEXT,
                                 COPYRIGHT_LONGTEXT, VLC_TRUE );
    add_string( SOUT_CFG_PREFIX "comment",  "", NULL, COMMENT_TEXT,
                                 COMMENT_LONGTEXT, VLC_TRUE );
    add_string( SOUT_CFG_PREFIX "rating",  "", NULL, RATING_TEXT,
                                 RATING_LONGTEXT, VLC_TRUE );
77
78
79
80
81
vlc_module_end();

/*****************************************************************************
 * Locales prototypes
 *****************************************************************************/
82
83
static const char *ppsz_sout_options[] = {
    "title", "author", "copyright", "comment", "rating", NULL
84
};
85

86
87
88
89
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 * );
90
91
92

typedef struct
{
93
94
    int          i_id;
    int          i_cat;
95

96
    /* codec information */
97
98
99
100
101
102
103
104
    uint16_t     i_tag;     /* for audio */
    vlc_fourcc_t i_fourcc;  /* for video */
    char         *psz_name; /* codec name */

    int          i_sequence;

    int          i_extra;
    uint8_t     *p_extra;
105

106
107
108
109
110
111
112
113
} asf_track_t;

struct sout_mux_sys_t
{
    guid_t          fid;    /* file id */
    int             i_packet_size;
    int64_t         i_packet_count;
    mtime_t         i_dts_first;
114
    mtime_t         i_dts_last;
115
    mtime_t         i_preroll_time;
116
    int64_t         i_bitrate;
117
118
119
120
121
122

    int             i_track;
    asf_track_t     track[128];

    vlc_bool_t      b_write_header;

123
    block_t         *pk;
124
125
126
    int             i_pk_used;
    int             i_pk_frame;
    mtime_t         i_pk_dts;
127
128
129

    vlc_bool_t      b_asf_http;
    int             i_seq;
130
131
132
133
134
135
136

    /* meta data */
    char            *psz_title;
    char            *psz_author;
    char            *psz_copyright;
    char            *psz_comment;
    char            *psz_rating;
137
138
139
140
};

static int MuxGetStream( sout_mux_t *, int *pi_stream, mtime_t *pi_dts );

141
static block_t *asf_header_create( sout_mux_t *, vlc_bool_t b_broadcast );
142
static block_t *asf_packet_create( sout_mux_t *, asf_track_t *, block_t * );
143
static block_t *asf_stream_end_create( sout_mux_t *);
144
145
146
147
148
149

typedef struct
{
    int      i_buffer_size;
    int      i_buffer;
    uint8_t  *p_buffer;
gbazin's avatar
gbazin committed
150

151
152
153
154
155
156
157
158
159
} bo_t;

static void bo_init     ( bo_t *, uint8_t *, int  );
static void bo_add_u8   ( bo_t *, uint8_t  );
static void bo_addle_u16( bo_t *, uint16_t );
static void bo_addle_u32( bo_t *, uint32_t );
static void bo_addle_u64( bo_t *, uint64_t );
static void bo_add_mem  ( bo_t *, uint8_t *, int );

160
static void bo_addle_str16( bo_t *, char * );
161
162
163
164
165
166
167
168

/*****************************************************************************
 * Open:
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    sout_mux_t     *p_mux = (sout_mux_t*)p_this;
    sout_mux_sys_t *p_sys;
169
    vlc_value_t    val;
170
171
172
    int i;

    msg_Dbg( p_mux, "Asf muxer opened" );
173
    sout_CfgParse( p_mux, SOUT_CFG_PREFIX, ppsz_sout_options, p_mux->p_cfg );
174

175
    p_mux->pf_control   = Control;
176
177
178
179
180
    p_mux->pf_addstream = AddStream;
    p_mux->pf_delstream = DelStream;
    p_mux->pf_mux       = Mux;

    p_mux->p_sys = p_sys = malloc( sizeof( sout_mux_sys_t ) );
181
182
183
184
185
    p_sys->b_asf_http = p_mux->psz_mux && !strcmp( p_mux->psz_mux, "asfh" );
    if( p_sys->b_asf_http )
    {
        msg_Dbg( p_mux, "creating asf stream to be used with mmsh" );
    }
186
    p_sys->pk = NULL;
187
188
189
190
    p_sys->i_pk_used    = 0;
    p_sys->i_pk_frame   = 0;
    p_sys->i_dts_first  = -1;
    p_sys->i_dts_last   = 0;
191
    p_sys->i_preroll_time = 2000;
192
    p_sys->i_bitrate    = 0;
193
    p_sys->i_seq        = 0;
194
195
196
197
198

    p_sys->b_write_header = VLC_TRUE;
    p_sys->i_track = 1;
    p_sys->i_packet_size = 4096;
    p_sys->i_packet_count= 0;
199
200

    /* Generate a random fid */
201
    srand( mdate() & 0xffffffff );
gbazin's avatar
gbazin committed
202
203
204
    p_sys->fid.Data1 = 0xbabac001;
    p_sys->fid.Data2 = ( (uint64_t)rand() << 16 ) / RAND_MAX;
    p_sys->fid.Data3 = ( (uint64_t)rand() << 16 ) / RAND_MAX;
205
206
    for( i = 0; i < 8; i++ )
    {
gbazin's avatar
gbazin committed
207
        p_sys->fid.Data4[i] = ( (uint64_t)rand() << 8 ) / RAND_MAX;
208
    }
209
210

    /* Meta data */
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
    var_Get( p_mux, SOUT_CFG_PREFIX "title", &val );
    p_sys->psz_title = val.psz_string;

    var_Get( p_mux, SOUT_CFG_PREFIX "author", &val );
    p_sys->psz_author = val.psz_string;

    var_Get( p_mux, SOUT_CFG_PREFIX "copyright", &val );
    p_sys->psz_copyright = val.psz_string;

    var_Get( p_mux, SOUT_CFG_PREFIX "comment", &val );
    p_sys->psz_comment = val.psz_string;

    var_Get( p_mux, SOUT_CFG_PREFIX "rating", &val );
    p_sys->psz_rating = val.psz_string;

226
227
    msg_Dbg( p_mux, "meta data: title='%s' author='%s' copyright='%s' "
             "comment='%s' rating='%s'",
228
229
             p_sys->psz_title, p_sys->psz_author, p_sys->psz_copyright,
             p_sys->psz_comment, p_sys->psz_rating );
230

231
232
233
234
235
236
237
238
239
240
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Close:
 *****************************************************************************/
static void Close( 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;
241
    block_t  *out;
242
243
244
245
    int i;

    msg_Dbg( p_mux, "Asf muxer closed" );

246
247
248
249
250
    if( ( out = asf_stream_end_create( p_mux ) ) )
    {
        sout_AccessOutWrite( p_mux->p_access, out );
    }

251
252
253
    /* rewrite header */
    if( !sout_AccessOutSeek( p_mux->p_access, 0 ) )
    {
254
255
        out = asf_header_create( p_mux, VLC_FALSE );
        sout_AccessOutWrite( p_mux->p_access, out );
256
257
258
259
260
261
262
263
264
265
266
267
    }

    for( i = 1; i < p_sys->i_track; i++ )
    {
        free( p_sys->track[i].p_extra );
    }
    free( p_sys );
}

/*****************************************************************************
 * Capability:
 *****************************************************************************/
268
static int Control( sout_mux_t *p_mux, int i_query, va_list args )
269
{
270
271
272
273
    sout_mux_sys_t *p_sys = p_mux->p_sys;
    vlc_bool_t *pb_bool;
    char **ppsz;

274
275
    switch( i_query )
    {
276
277
       case MUX_CAN_ADD_STREAM_WHILE_MUXING:
           pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
278
279
           if( p_sys->b_asf_http ) *pb_bool = VLC_TRUE;
           else *pb_bool = VLC_FALSE;
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
           return VLC_SUCCESS;

       case MUX_GET_ADD_STREAM_WAIT:
           pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
           *pb_bool = VLC_FALSE;
           return VLC_SUCCESS;

       case MUX_GET_MIME:
           ppsz = (char**)va_arg( args, char ** );
           if( p_sys->b_asf_http )
               *ppsz = strdup( "video/x-ms-asf-stream" );
           else
               *ppsz = strdup( "video/x-ms-asf" );
           return VLC_SUCCESS;

295
        default:
296
            return VLC_EGENERIC;
297
    }
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
}

/*****************************************************************************
 * AddStream:
 *****************************************************************************/
static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
{
    sout_mux_sys_t   *p_sys = p_mux->p_sys;
    asf_track_t      *tk;
    bo_t             bo;

    msg_Dbg( p_mux, "adding input" );
    if( p_sys->i_track > 127 )
    {
        msg_Dbg( p_mux, "cannot add this track (too much track)" );
        return VLC_EGENERIC;
    }

    tk = p_input->p_sys = &p_sys->track[p_sys->i_track];
    tk->i_id  = p_sys->i_track;
    tk->i_cat = p_input->p_fmt->i_cat;
    tk->i_sequence = 0;

    switch( tk->i_cat )
    {
        case AUDIO_ES:
        {
325
326
327
            int i_blockalign = p_input->p_fmt->audio.i_blockalign;
            int i_bitspersample = 0;
            int i_extra = 0;
328

329
            switch( p_input->p_fmt->i_codec )
330
331
            {
                case VLC_FOURCC( 'a', '5', '2', ' ' ):
332
333
                    tk->i_tag = WAVE_FORMAT_A52;
                    tk->psz_name = "A/52";
334
335
                    break;
                case VLC_FOURCC( 'm', 'p', 'g', 'a' ):
336
#if 1
337
338
                    tk->psz_name = "MPEG Audio Layer 3";
                    tk->i_tag = WAVE_FORMAT_MPEGLAYER3;
339
                    i_blockalign = 1;
340
                    i_extra = 12;
341
                    break;
342
#else
343
344
                    tk->psz_name = "MPEG Audio Layer 1/2";
                    tk->i_tag = WAVE_FORMAT_MPEG;
345
346
347
348
                    i_blockalign = 1;
                    i_extra = 22;
                    break;
#endif
349
                case VLC_FOURCC( 'w', 'm', 'a', '1' ):
350
351
                    tk->psz_name = "Windows Media Audio 1";
                    tk->i_tag = WAVE_FORMAT_WMA1;
352
353
                    break;
                case VLC_FOURCC( 'w', 'm', 'a', '2' ):
354
355
                    tk->psz_name = "Windows Media Audio 2";
                    tk->i_tag = WAVE_FORMAT_WMA2;
356
357
                    break;
                case VLC_FOURCC( 'w', 'm', 'a', '3' ):
358
359
                    tk->psz_name = "Windows Media Audio 3";
                    tk->i_tag = WAVE_FORMAT_WMA3;
360
361
362
                    break;
                    /* raw codec */
                case VLC_FOURCC( 'u', '8', ' ', ' ' ):
363
364
                    tk->psz_name = "Raw audio 8bits";
                    tk->i_tag = WAVE_FORMAT_PCM;
365
                    i_blockalign= p_input->p_fmt->audio.i_channels;
366
367
368
                    i_bitspersample = 8;
                    break;
                case VLC_FOURCC( 's', '1', '6', 'l' ):
369
370
                    tk->psz_name = "Raw audio 16bits";
                    tk->i_tag = WAVE_FORMAT_PCM;
371
                    i_blockalign= 2 * p_input->p_fmt->audio.i_channels;
372
373
374
                    i_bitspersample = 16;
                    break;
                case VLC_FOURCC( 's', '2', '4', 'l' ):
375
376
                    tk->psz_name = "Raw audio 24bits";
                    tk->i_tag = WAVE_FORMAT_PCM;
377
                    i_blockalign= 3 * p_input->p_fmt->audio.i_channels;
378
379
380
                    i_bitspersample = 24;
                    break;
                case VLC_FOURCC( 's', '3', '2', 'l' ):
381
382
                    tk->psz_name = "Raw audio 32bits";
                    tk->i_tag = WAVE_FORMAT_PCM;
383
                    i_blockalign= 4 * p_input->p_fmt->audio.i_channels;
384
385
386
387
388
389
                    i_bitspersample = 32;
                    break;
                default:
                    return VLC_EGENERIC;
            }

390

391
            tk->i_extra = sizeof( WAVEFORMATEX ) +
392
                          p_input->p_fmt->i_extra + i_extra;
393
394
            tk->p_extra = malloc( tk->i_extra );
            bo_init( &bo, tk->p_extra, tk->i_extra );
395
            bo_addle_u16( &bo, tk->i_tag );
396
397
            bo_addle_u16( &bo, p_input->p_fmt->audio.i_channels );
            bo_addle_u32( &bo, p_input->p_fmt->audio.i_rate );
398
399
400
            bo_addle_u32( &bo, p_input->p_fmt->i_bitrate / 8 );
            bo_addle_u16( &bo, i_blockalign );
            bo_addle_u16( &bo, i_bitspersample );
401
            if( p_input->p_fmt->i_extra > 0 )
402
            {
403
404
405
                bo_addle_u16( &bo, p_input->p_fmt->i_extra );
                bo_add_mem  ( &bo, p_input->p_fmt->p_extra,
                              p_input->p_fmt->i_extra );
406
            }
407
408
409
            else
            {
                bo_addle_u16( &bo, i_extra );
410
                if( tk->i_tag == WAVE_FORMAT_MPEGLAYER3 )
411
                {
412
                    msg_Dbg( p_mux, "adding mp3 header" );
413
414
415
416
417
418
                    bo_addle_u16( &bo, 1 );     /* wId */
                    bo_addle_u32( &bo, 2 );     /* fdwFlags */
                    bo_addle_u16( &bo, 1152 );  /* nBlockSize */
                    bo_addle_u16( &bo, 1 );     /* nFramesPerBlock */
                    bo_addle_u16( &bo, 1393 );  /* nCodecDelay */
                }
419
                else if( tk->i_tag == WAVE_FORMAT_MPEG )
420
421
422
423
                {
                    msg_Dbg( p_mux, "adding mp2 header" );
                    bo_addle_u16( &bo, 2 );     /* fwHeadLayer */
                    bo_addle_u32( &bo, p_input->p_fmt->i_bitrate );
424
                    bo_addle_u16( &bo, p_input->p_fmt->audio.i_channels == 2 ?1:8 );
425
426
427
428
429
430
                    bo_addle_u16( &bo, 0 );     /* fwHeadModeExt */
                    bo_addle_u16( &bo, 1 );     /* wHeadEmphasis */
                    bo_addle_u16( &bo, 16 );    /* fwHeadFlags */
                    bo_addle_u32( &bo, 0 );     /* dwPTSLow */
                    bo_addle_u32( &bo, 0 );     /* dwPTSHigh */
                }
431
432
            }

433
434
435
436
437
438
439
440
            if( p_input->p_fmt->i_bitrate > 24000 )
            {
                p_sys->i_bitrate += p_input->p_fmt->i_bitrate;
            }
            else
            {
                p_sys->i_bitrate += 512000;
            }
441
442
443
444
445
            break;
        }
        case VIDEO_ES:
        {
            tk->i_extra = 11 + sizeof( BITMAPINFOHEADER ) +
446
                          p_input->p_fmt->i_extra;
447
448
            tk->p_extra = malloc( tk->i_extra );
            bo_init( &bo, tk->p_extra, tk->i_extra );
449
450
            bo_addle_u32( &bo, p_input->p_fmt->video.i_width );
            bo_addle_u32( &bo, p_input->p_fmt->video.i_height );
451
452
            bo_add_u8   ( &bo, 0x02 );  /* flags */
            bo_addle_u16( &bo, sizeof( BITMAPINFOHEADER ) +
453
                               p_input->p_fmt->i_extra );
454
            bo_addle_u32( &bo, sizeof( BITMAPINFOHEADER ) +
455
456
457
                               p_input->p_fmt->i_extra );
            bo_addle_u32( &bo, p_input->p_fmt->video.i_width );
            bo_addle_u32( &bo, p_input->p_fmt->video.i_height );
458
459
            bo_addle_u16( &bo, 1 );
            bo_addle_u16( &bo, 24 );
460
            if( p_input->p_fmt->i_codec == VLC_FOURCC('m','p','4','v') )
461
            {
462
463
                tk->psz_name = "MPEG-4 Video";
                tk->i_fourcc = VLC_FOURCC( 'M', 'P', '4', 'S' );
464
            }
465
            else if( p_input->p_fmt->i_codec == VLC_FOURCC('D','I','V','3') )
466
            {
467
468
                tk->psz_name = "MSMPEG-4 V3 Video";
                tk->i_fourcc = VLC_FOURCC( 'M', 'P', '4', '3' );
469
            }
470
            else if( p_input->p_fmt->i_codec == VLC_FOURCC('D','I','V','2') )
471
            {
472
473
                tk->psz_name = "MSMPEG-4 V2 Video";
                tk->i_fourcc = VLC_FOURCC( 'M', 'P', '4', '2' );
474
            }
475
            else if( p_input->p_fmt->i_codec == VLC_FOURCC('D','I','V','1') )
476
            {
477
478
479
                tk->psz_name = "MSMPEG-4 V1 Video";
                tk->i_fourcc = VLC_FOURCC( 'M', 'P', 'G', '4' );
            }
480
            else if( p_input->p_fmt->i_codec == VLC_FOURCC('W','M','V','1') )
481
482
483
484
            {
                tk->psz_name = "Windows Media Video 1";
                tk->i_fourcc = VLC_FOURCC( 'W', 'M', 'V', '1' );
            }
485
            else if( p_input->p_fmt->i_codec == VLC_FOURCC('W','M','V','2') )
486
487
488
            {
                tk->psz_name = "Windows Media Video 2";
                tk->i_fourcc = VLC_FOURCC( 'W', 'M', 'V', '2' );
489
            }
490
491
492
493
494
            else if( p_input->p_fmt->i_codec == VLC_FOURCC('W','M','V','3') )
            {
                tk->psz_name = "Windows Media Video 3";
                tk->i_fourcc = VLC_FOURCC( 'W', 'M', 'V', '3' );
            }
495
496
            else
            {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
497
                tk->psz_name = _("Unknown Video");
498
                tk->i_fourcc = p_input->p_fmt->i_codec;
499
            }
500
            bo_add_mem( &bo, (uint8_t*)&tk->i_fourcc, 4 );
501
502
503
504
505
            bo_addle_u32( &bo, 0 );
            bo_addle_u32( &bo, 0 );
            bo_addle_u32( &bo, 0 );
            bo_addle_u32( &bo, 0 );
            bo_addle_u32( &bo, 0 );
506
            if( p_input->p_fmt->i_extra > 0 )
507
            {
508
509
                bo_add_mem  ( &bo, p_input->p_fmt->p_extra,
                              p_input->p_fmt->i_extra );
510
511
            }

512
513
514
515
516
517
518
519
            if( p_input->p_fmt->i_bitrate > 50000 )
            {
                p_sys->i_bitrate += p_input->p_fmt->i_bitrate;
            }
            else
            {
                p_sys->i_bitrate += 1000000;
            }
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
            break;
        }
        default:
            msg_Err(p_mux, "unhandled track type" );
            return VLC_EGENERIC;
    }

    p_sys->i_track++;
    return VLC_SUCCESS;
}

/*****************************************************************************
 * DelStream:
 *****************************************************************************/
static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
{
    msg_Dbg( p_mux, "removing input" );
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Mux:
 *****************************************************************************/
543
static int Mux( sout_mux_t *p_mux )
544
545
546
547
548
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;

    if( p_sys->b_write_header )
    {
549
        block_t *out = asf_header_create( p_mux, VLC_TRUE );
550

551
        out->i_flags |= BLOCK_FLAG_HEADER;
552
553
554
555
556
557
558
559
560
561
562
        sout_AccessOutWrite( p_mux->p_access, out );

        p_sys->b_write_header = VLC_FALSE;
    }

    for( ;; )
    {
        sout_input_t  *p_input;
        asf_track_t   *tk;
        int           i_stream;
        mtime_t       i_dts;
563
564
        block_t *data;
        block_t *pk;
565
566
567
568
569
570
571
572
573
574
575

        if( MuxGetStream( p_mux, &i_stream, &i_dts ) )
        {
            /* not enough data */
            return VLC_SUCCESS;
        }

        if( p_sys->i_dts_first < 0 )
        {
            p_sys->i_dts_first = i_dts;
        }
576
577
578
579
        if( p_sys->i_dts_last < i_dts )
        {
            p_sys->i_dts_last = i_dts;
        }
580
581
582
583

        p_input = p_mux->pp_inputs[i_stream];
        tk      = (asf_track_t*)p_input->p_sys;

584
        data = block_FifoGet( p_input->p_fifo );
585
586
587
588
589
590
591
592
593
594

        if( ( pk = asf_packet_create( p_mux, tk, data ) ) )
        {
            sout_AccessOutWrite( p_mux->p_access, pk );
        }
    }

    return VLC_SUCCESS;
}

595
static int MuxGetStream( sout_mux_t *p_mux, int *pi_stream, mtime_t *pi_dts )
596
597
598
599
600
601
602
603
{
    mtime_t i_dts;
    int     i_stream;
    int     i;

    for( i = 0, i_dts = 0, i_stream = -1; i < p_mux->i_nb_inputs; i++ )
    {
        sout_input_t  *p_input = p_mux->pp_inputs[i];
604
        block_t *p_data;
605
606
607
608
609
610
611
612
613
614
615
616
617

        if( p_input->p_fifo->i_depth <= 0 )
        {
            if( p_input->p_fmt->i_cat == AUDIO_ES ||
                p_input->p_fmt->i_cat == VIDEO_ES )
            {
                /* We need that audio+video fifo contain at least 1 packet */
                return VLC_EGENERIC;
            }
            /* SPU */
            continue;
        }

618
        p_data = block_FifoShow( p_input->p_fifo );
619
        if( i_stream == -1 || p_data->i_dts < i_dts )
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
        {
            i_stream = i;
            i_dts    = p_data->i_dts;
        }
    }

    *pi_stream = i_stream;
    *pi_dts = i_dts;

    return VLC_SUCCESS;
}

/****************************************************************************
 * Asf header construction
 ****************************************************************************/

/****************************************************************************
 * Buffer out
 ****************************************************************************/
static void bo_init( bo_t *p_bo, uint8_t *p_buffer, int i_size )
{
    p_bo->i_buffer_size = i_size;
    p_bo->i_buffer = 0;
    p_bo->p_buffer = p_buffer;
}
static void bo_add_u8( bo_t *p_bo, uint8_t i )
{
    if( p_bo->i_buffer < p_bo->i_buffer_size )
    {
        p_bo->p_buffer[p_bo->i_buffer] = i;
    }
    p_bo->i_buffer++;
}
static void bo_addle_u16( bo_t *p_bo, uint16_t i )
{
    bo_add_u8( p_bo, i &0xff );
    bo_add_u8( p_bo, ( ( i >> 8) &0xff ) );
}
static void bo_addle_u32( bo_t *p_bo, uint32_t i )
{
    bo_addle_u16( p_bo, i &0xffff );
    bo_addle_u16( p_bo, ( ( i >> 16) &0xffff ) );
}
static void bo_addle_u64( bo_t *p_bo, uint64_t i )
{
    bo_addle_u32( p_bo, i &0xffffffff );
    bo_addle_u32( p_bo, ( ( i >> 32) &0xffffffff ) );
}

static void bo_add_mem( bo_t *p_bo, uint8_t *p_mem, int i_size )
{
    int i_copy = __MIN( i_size, p_bo->i_buffer_size - p_bo->i_buffer );

    if( i_copy > 0 )
    {
675
        memcpy( &p_bo->p_buffer[p_bo->i_buffer], p_mem, i_copy );
676
677
678
679
    }
    p_bo->i_buffer += i_size;
}

680
681
682
683
684
static void bo_addle_str16( bo_t *bo, char *str )
{
    bo_addle_u16( bo, strlen( str ) + 1 );
    for( ;; )
    {
685
        uint16_t c = (uint8_t)*str++;
686
        bo_addle_u16( bo, c );
687
        if( c == '\0' ) break;
688
689
690
    }
}

691
692
693
694
static void bo_addle_str16_nosize( bo_t *bo, char *str )
{
    for( ;; )
    {
695
        uint16_t c = (uint8_t)*str++;
696
        bo_addle_u16( bo, c );
697
        if( c == '\0' ) break;
698
699
700
    }
}

701
/****************************************************************************
gbazin's avatar
gbazin committed
702
 * GUID definitions
703
704
705
706
 ****************************************************************************/
static void bo_add_guid( bo_t *p_bo, const guid_t *id )
{
    int i;
gbazin's avatar
gbazin committed
707
708
709
    bo_addle_u32( p_bo, id->Data1 );
    bo_addle_u16( p_bo, id->Data2 );
    bo_addle_u16( p_bo, id->Data3 );
710
711
    for( i = 0; i < 8; i++ )
    {
gbazin's avatar
gbazin committed
712
        bo_add_u8( p_bo, id->Data4[i] );
713
714
715
716
    }
}

static const guid_t asf_object_header_guid =
gbazin's avatar
gbazin committed
717
{0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
718
static const guid_t asf_object_data_guid =
gbazin's avatar
gbazin committed
719
{0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
720
static const guid_t asf_object_file_properties_guid =
gbazin's avatar
gbazin committed
721
{0x8cabdca1, 0xa947, 0x11cf, {0x8e, 0xe4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
722
static const guid_t asf_object_stream_properties_guid =
gbazin's avatar
gbazin committed
723
{0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
724
static const guid_t asf_object_header_extention_guid =
gbazin's avatar
gbazin committed
725
{0x5FBF03B5, 0xA92E, 0x11CF, {0x8E, 0xE3, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
726
static const guid_t asf_object_stream_type_audio =
gbazin's avatar
gbazin committed
727
{0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
728
static const guid_t asf_object_stream_type_video =
gbazin's avatar
gbazin committed
729
{0xbc19efc0, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
730
static const guid_t asf_guid_audio_conceal_none =
gbazin's avatar
gbazin committed
731
{0x20FB5700, 0x5B55, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
732
static const guid_t asf_guid_video_conceal_none =
gbazin's avatar
gbazin committed
733
{0x20FB5700, 0x5B55, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
734
static const guid_t asf_guid_reserved_1 =
gbazin's avatar
gbazin committed
735
{0xABD3D211, 0xA9BA, 0x11cf, {0x8E, 0xE6, 0x00, 0xC0, 0x0C ,0x20, 0x53, 0x65}};
736
static const guid_t asf_object_codec_comment_guid =
gbazin's avatar
gbazin committed
737
{0x86D15240, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}};
738
static const guid_t asf_object_codec_comment_reserved_guid =
gbazin's avatar
gbazin committed
739
{0x86D15241, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}};
740
static const guid_t asf_object_content_description_guid =
gbazin's avatar
gbazin committed
741
{0x75B22633, 0x668E, 0x11CF, {0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c}};
742
743
static const guid_t asf_object_index_guid =
{0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}};
744

gbazin's avatar
gbazin committed
745
746
747
/****************************************************************************
 * Misc
 ****************************************************************************/
748
749
750
751
752
753
754
755
756
757
static void asf_chunk_add( bo_t *bo,
                           int i_type, int i_len, int i_flags, int i_seq )
{
    bo_addle_u16( bo, i_type );
    bo_addle_u16( bo, i_len + 8 );
    bo_addle_u32( bo, i_seq );
    bo_addle_u16( bo, i_flags );
    bo_addle_u16( bo, i_len + 8 );
}

758
static block_t *asf_header_create( sout_mux_t *p_mux, vlc_bool_t b_broadcast )
759
760
761
762
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;
    asf_track_t    *tk;

763
    mtime_t i_duration = 0;
764
    int i_size;
765
    int i_ci_size;
766
    int i_cd_size = 0;
767
    block_t *out;
768
769
    bo_t bo;
    int i;
770

771
772
    msg_Dbg( p_mux, "Asf muxer creating header" );

773
774
775
    if( p_sys->i_dts_first > 0 )
    {
        i_duration = p_sys->i_dts_last - p_sys->i_dts_first;
776
        if( i_duration < 0 ) i_duration = 0;
777
778
    }

779
780
    /* calculate header size */
    i_size = 30 + 104 + 46;
781
    i_ci_size = 44;
782
783
784
    for( i = 1; i < p_sys->i_track; i++ )
    {
        i_size += 78 + p_sys->track[i].i_extra;
785
786
787
788
789
790
791
792
793
        i_ci_size += 8 + 2 * strlen( p_sys->track[i].psz_name );
        if( p_sys->track[i].i_cat == AUDIO_ES )
        {
            i_ci_size += 4;
        }
        else if( p_sys->track[i].i_cat == VIDEO_ES )
        {
            i_ci_size += 6;
        }
794
    }
795

796
797
798
799
800
801
802
803
804
    if( *p_sys->psz_title || *p_sys->psz_author || *p_sys->psz_copyright ||
        *p_sys->psz_comment || *p_sys->psz_rating )
    {
        i_cd_size = 34 + 2 * ( strlen( p_sys->psz_title ) + 1 +
                             strlen( p_sys->psz_author ) + 1 +
                             strlen( p_sys->psz_copyright ) + 1 +
                             strlen( p_sys->psz_comment ) + 1 +
                             strlen( p_sys->psz_rating ) + 1 );
    }
805

806
    i_size += i_ci_size + i_cd_size;
807

808
809
    if( p_sys->b_asf_http )
    {
810
        out = block_New( p_mux, i_size + 50 + 12 );
811
812
813
814
815
        bo_init( &bo, out->p_buffer, i_size + 50 + 12 );
        asf_chunk_add( &bo, 0x4824, i_size + 50, 0xc00, p_sys->i_seq++ );
    }
    else
    {
816
        out = block_New( p_mux, i_size + 50 );
817
818
        bo_init( &bo, out->p_buffer, i_size + 50 );
    }
819

820
821
822
    /* header object */
    bo_add_guid ( &bo, &asf_object_header_guid );
    bo_addle_u64( &bo, i_size );
823
    bo_addle_u32( &bo, 2 + p_sys->i_track - 1 );
824
825
826
827
828
829
830
831
832
    bo_add_u8   ( &bo, 1 );
    bo_add_u8   ( &bo, 2 );

    /* sub object */

    /* file properties */
    bo_add_guid ( &bo, &asf_object_file_properties_guid );
    bo_addle_u64( &bo, 104 );
    bo_add_guid ( &bo, &p_sys->fid );
833
    bo_addle_u64( &bo, i_size + 50 + p_sys->i_packet_count *
834
                                p_sys->i_packet_size ); /* file size */
835
    bo_addle_u64( &bo, 0 );                 /* creation date */
836
    bo_addle_u64( &bo, b_broadcast ? 0xffffffffLL : p_sys->i_packet_count );
837
838
    bo_addle_u64( &bo, i_duration * 10 );   /* play duration (100ns) */
    bo_addle_u64( &bo, i_duration * 10 );   /* send duration (100ns) */
839
    bo_addle_u64( &bo, p_sys->i_preroll_time ); /* preroll duration (ms) */
840
841
842
    bo_addle_u32( &bo, b_broadcast ? 0x01 : 0x00);      /* flags */
    bo_addle_u32( &bo, p_sys->i_packet_size );  /* packet size min */
    bo_addle_u32( &bo, p_sys->i_packet_size );  /* packet size max */
843
    bo_addle_u32( &bo, p_sys->i_bitrate );      /* maxbitrate */
844
845
846
847
848
849
850
851

    /* header extention */
    bo_add_guid ( &bo, &asf_object_header_extention_guid );
    bo_addle_u64( &bo, 46 );
    bo_add_guid ( &bo, &asf_guid_reserved_1 );
    bo_addle_u16( &bo, 6 );
    bo_addle_u32( &bo, 0 );

852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
    /* content description header */
    if( i_cd_size > 0 )
    {
        bo_add_guid ( &bo, &asf_object_content_description_guid );
        bo_addle_u64( &bo, i_cd_size );
        bo_addle_u16( &bo, 2 * strlen( p_sys->psz_title ) + 2 );
        bo_addle_u16( &bo, 2 * strlen( p_sys->psz_author ) + 2 );
        bo_addle_u16( &bo, 2 * strlen( p_sys->psz_copyright ) + 2 );
        bo_addle_u16( &bo, 2 * strlen( p_sys->psz_comment ) + 2 );
        bo_addle_u16( &bo, 2 * strlen( p_sys->psz_rating ) + 2 );

        bo_addle_str16_nosize( &bo, p_sys->psz_title );
        bo_addle_str16_nosize( &bo, p_sys->psz_author );
        bo_addle_str16_nosize( &bo, p_sys->psz_copyright );
        bo_addle_str16_nosize( &bo, p_sys->psz_comment );
        bo_addle_str16_nosize( &bo, p_sys->psz_rating );
    }

870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
    /* stream properties */
    for( i = 1; i < p_sys->i_track; i++ )
    {
        tk = &p_sys->track[i];

        bo_add_guid ( &bo, &asf_object_stream_properties_guid );
        bo_addle_u64( &bo, 78 + tk->i_extra );
        if( tk->i_cat == AUDIO_ES )
        {
            bo_add_guid( &bo, &asf_object_stream_type_audio );
            bo_add_guid( &bo, &asf_guid_audio_conceal_none );
        }
        else if( tk->i_cat == VIDEO_ES )
        {
            bo_add_guid( &bo, &asf_object_stream_type_video );
            bo_add_guid( &bo, &asf_guid_video_conceal_none );
        }
        bo_addle_u64( &bo, 0 );         /* time offset */
        bo_addle_u32( &bo, tk->i_extra );
        bo_addle_u32( &bo, 0 );         /* 0 */
        bo_addle_u16( &bo, tk->i_id );  /* stream number */
        bo_addle_u32( &bo, 0 );
        bo_add_mem  ( &bo, tk->p_extra, tk->i_extra );
    }

895
    /* Codec Infos */
896
    bo_add_guid ( &bo, &asf_object_codec_comment_guid );
897
    bo_addle_u64( &bo, i_ci_size );
898
    bo_add_guid ( &bo, &asf_object_codec_comment_reserved_guid );
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
    bo_addle_u32( &bo, p_sys->i_track - 1 );
    for( i = 1; i < p_sys->i_track; i++ )
    {
        tk = &p_sys->track[i];

        bo_addle_u16( &bo, tk->i_id );
        bo_addle_str16( &bo, tk->psz_name );
        bo_addle_u16( &bo, 0 );
        if( tk->i_cat == AUDIO_ES )
        {
            bo_addle_u16( &bo, 2 );
            bo_addle_u16( &bo, tk->i_tag );
        }
        else if( tk->i_cat == VIDEO_ES )
        {
            bo_addle_u16( &bo, 4 );
            bo_add_mem  ( &bo, (uint8_t*)&tk->i_fourcc, 4 );

        }
    }

920
921
922
923
924
925
926
927
928
929
930
931
932
    /* data object */
    bo_add_guid ( &bo, &asf_object_data_guid );
    bo_addle_u64( &bo, 50 + p_sys->i_packet_count * p_sys->i_packet_size );
    bo_add_guid ( &bo, &p_sys->fid );
    bo_addle_u64( &bo, p_sys->i_packet_count );
    bo_addle_u16( &bo, 0x101 );

    return out;
}

/****************************************************************************
 *
 ****************************************************************************/
933
static block_t *asf_packet_create( sout_mux_t *p_mux,
934
                                   asf_track_t *tk, block_t *data )
935
936
937
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;

938
    int     i_data = data->i_buffer;
939
940
    int     i_pos  = 0;
    uint8_t *p_data= data->p_buffer;
941
    block_t *first = NULL, **last = &first;
942
    int     i_preheader = p_sys->b_asf_http ? 12 : 0;
943
944
945

    while( i_pos < i_data )
    {
946
947
        bo_t bo;
        int i_payload;
948
949
950

        if( p_sys->pk == NULL )
        {
951
            p_sys->pk = block_New( p_mux, p_sys->i_packet_size + i_preheader );
952
            /* reserve 14 bytes for the packet header */
953
            p_sys->i_pk_used = 14 + i_preheader;
954
955
956
957
958
            p_sys->i_pk_frame = 0;
            p_sys->i_pk_dts = data->i_dts;
        }

        bo_init( &bo, &p_sys->pk->p_buffer[p_sys->i_pk_used],
959
                 p_sys->i_packet_size - p_sys->i_pk_used );
960
961
962
963

        /* add payload (header size = 17) */
        i_payload = __MIN( i_data - i_pos,
                           p_sys->i_packet_size - p_sys->i_pk_used - 17 );
964
965
966
        bo_add_u8   ( &bo, !(data->i_flags & BLOCK_FLAG_TYPE_P ||
                      data->i_flags & BLOCK_FLAG_TYPE_B) ?
                      0x80 | tk->i_id : tk->i_id );
967
968
969
970
        bo_add_u8   ( &bo, tk->i_sequence );
        bo_addle_u32( &bo, i_pos );
        bo_add_u8   ( &bo, 0x08 );  /* flags */
        bo_addle_u32( &bo, i_data );
971
        bo_addle_u32( &bo, (data->i_dts - p_sys->i_dts_first) / 1000 +
972
                      p_sys->i_preroll_time );
973
974
975
976
977
978
979
        bo_addle_u16( &bo, i_payload );
        bo_add_mem  ( &bo, &p_data[i_pos], i_payload );
        i_pos += i_payload;
        p_sys->i_pk_used += 17 + i_payload;

        p_sys->i_pk_frame++;

980
        if( p_sys->i_pk_used + 17 >= p_sys->i_packet_size )
981
982
983
984
        {
            /* not enough data for another payload, flush the packet */
            int i_pad = p_sys->i_packet_size - p_sys->i_pk_used;

985
986
987
988
            bo_init( &bo, p_sys->pk->p_buffer, 14 + i_preheader );

            if( p_sys->b_asf_http )
            {
989
990
                asf_chunk_add( &bo, 0x4424, p_sys->i_packet_size,
                               0x00, p_sys->i_seq++);
991
            }
992
993
994
995
996
            bo_add_u8   ( &bo, 0x82 );
            bo_addle_u16( &bo, 0 );
            bo_add_u8( &bo, 0x11 );
            bo_add_u8( &bo, 0x5d );
            bo_addle_u16( &bo, i_pad );
997
            bo_addle_u32( &bo, (p_sys->i_pk_dts - p_sys->i_dts_first) / 1000 +
998
                          p_sys->i_preroll_time );
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
            bo_addle_u16( &bo, 0 * data->i_length / 1000 );
            bo_add_u8( &bo, 0x80 | p_sys->i_pk_frame );

            /* append the packet */
            *last = p_sys->pk;
            last  = &p_sys->pk->p_next;

            p_sys->pk = NULL;

            p_sys->i_packet_count++;
        }
    }

    tk->i_sequence++;
1013
    block_Release( data );
1014
1015
1016
1017

    return first;
}

1018
static block_t *asf_stream_end_create( sout_mux_t *p_mux )
1019
1020
1021
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;

1022
    block_t *out = NULL;
1023
    bo_t bo;
1024

1025
1026
    if( p_sys->b_asf_http )
    {
1027
        out = block_New( p_mux, 12 );
1028
1029
1030
        bo_init( &bo, out->p_buffer, 12 );
        asf_chunk_add( &bo, 0x4524, 0, 0x00, p_sys->i_seq++ );
    }
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
    else
    {
        /* Create index */
        out = block_New( p_mux, 56 );
        bo_init( &bo, out->p_buffer, 56 );
        bo_add_guid ( &bo, &asf_object_index_guid );
        bo_addle_u64( &bo, 56 );
        bo_add_guid ( &bo, &p_sys->fid );
        bo_addle_u64( &bo, 10000000 );
        bo_addle_u32( &bo, 5 );
        bo_addle_u32( &bo, 0 );
    }
1043

1044
1045
    return out;
}