asf.c 40.3 KB
Newer Older
1
/*****************************************************************************
2
 * asf.c: asf muxer module for vlc
3
 *****************************************************************************
4
 * Copyright (C) 2003-2004, 2006 the VideoLAN team
5
 * $Id$
6
7
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8
 *          Gildas Bazin <gbazin@videolan.org>
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * 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
dionoea's avatar
dionoea committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23
24
25
26
27
28
29
 *****************************************************************************/

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

#include <vlc/vlc.h>
zorglub's avatar
zorglub committed
30
31
32
#include <vlc_sout.h>
#include <vlc_block.h>
#include <vlc_codecs.h>
33

gbazin's avatar
gbazin committed
34
typedef GUID guid_t;
35

36
#define MAX_ASF_TRACKS 128
37
#define ASF_DATA_PACKET_SIZE 4096  // deprecated -- added sout-asf-packet-size
38

39
40
41
42
43
44
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
static int  Open   ( vlc_object_t * );
static void Close  ( vlc_object_t * );

45
46
#define SOUT_CFG_PREFIX "sout-asf-"

zorglub's avatar
zorglub committed
47
#define TITLE_TEXT N_("Title")
zorglub's avatar
zorglub committed
48
#define TITLE_LONGTEXT N_("Title to put in ASF comments." )
zorglub's avatar
zorglub committed
49
#define AUTHOR_TEXT N_("Author")
zorglub's avatar
zorglub committed
50
#define AUTHOR_LONGTEXT N_("Author to put in ASF comments." )
zorglub's avatar
zorglub committed
51
#define COPYRIGHT_TEXT N_("Copyright")
zorglub's avatar
zorglub committed
52
#define COPYRIGHT_LONGTEXT N_("Copyright string to put in ASF comments." )
zorglub's avatar
zorglub committed
53
#define COMMENT_TEXT N_("Comment")
zorglub's avatar
zorglub committed
54
#define COMMENT_LONGTEXT N_("Comment to put in ASF comments." )
zorglub's avatar
zorglub committed
55
#define RATING_TEXT N_("Rating")
zorglub's avatar
zorglub committed
56
#define RATING_LONGTEXT N_("\"Rating\" to put in ASF comments." )
57
#define PACKETSIZE_TEXT N_("Packet Size")
zorglub's avatar
zorglub committed
58
#define PACKETSIZE_LONGTEXT N_("ASF packet size -- default is 4096 bytes")
zorglub's avatar
zorglub committed
59

60
vlc_module_begin();
Sam Hocevar's avatar
Sam Hocevar committed
61
    set_description( _("ASF muxer") );
zorglub's avatar
zorglub committed
62
63
    set_category( CAT_SOUT );
    set_subcategory( SUBCAT_SOUT_MUX );
zorglub's avatar
zorglub committed
64
    set_shortname( "ASF" );
zorglub's avatar
zorglub committed
65

66
67
    set_capability( "sout mux", 5 );
    add_shortcut( "asf" );
68
    add_shortcut( "asfh" );
69
    set_callbacks( Open, Close );
70

zorglub's avatar
zorglub committed
71
72
73
74
75
76
77
78
79
80
    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 );
81
82
    add_integer( "sout-asf-packet-size", 4096, NULL, PACKETSIZE_TEXT, PACKETSIZE_LONGTEXT, VLC_TRUE );

83
84
85
86
87
vlc_module_end();

/*****************************************************************************
 * Locales prototypes
 *****************************************************************************/
88
89
static const char *ppsz_sout_options[] = {
    "title", "author", "copyright", "comment", "rating", NULL
90
};
91

92
93
94
95
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 * );
96
97
98

typedef struct
{
99
100
    int          i_id;
    int          i_cat;
101

102
    /* codec information */
103
104
    uint16_t     i_tag;     /* for audio */
    vlc_fourcc_t i_fourcc;  /* for video */
Laurent Aimar's avatar
Laurent Aimar committed
105
    const char         *psz_name; /* codec name */
gbazin's avatar
gbazin committed
106
    int          i_blockalign; /* for audio only */
107
    vlc_bool_t   b_audio_correction;
108
109
110
111

    int          i_sequence;

    int          i_extra;
112
113
114
    uint8_t      *p_extra;

    es_format_t  fmt;
115

116
117
118
119
120
121
122
123
} 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;
124
    mtime_t         i_dts_last;
125
    mtime_t         i_preroll_time;
126
    int64_t         i_bitrate;
127
128

    int             i_track;
129
    asf_track_t     track[MAX_ASF_TRACKS];
130
131
132

    vlc_bool_t      b_write_header;

133
    block_t         *pk;
134
135
136
    int             i_pk_used;
    int             i_pk_frame;
    mtime_t         i_pk_dts;
137
138
139

    vlc_bool_t      b_asf_http;
    int             i_seq;
140
141
142
143
144
145
146

    /* meta data */
    char            *psz_title;
    char            *psz_author;
    char            *psz_copyright;
    char            *psz_comment;
    char            *psz_rating;
147
148
149
150
};

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

151
static block_t *asf_header_create( sout_mux_t *, vlc_bool_t );
152
static block_t *asf_packet_create( sout_mux_t *, asf_track_t *, block_t * );
153
static block_t *asf_stream_end_create( sout_mux_t *);
154
static block_t *asf_packet_flush( sout_mux_t * );
155
156
157
158
159
160

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

162
163
164
165
166
167
168
169
170
} 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 );

Laurent Aimar's avatar
Laurent Aimar committed
171
static void bo_addle_str16( bo_t *, const char * );
172
173
174
175
176
177
178
179

/*****************************************************************************
 * 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;
180
    vlc_value_t    val;
181
182
    int i;

183
    msg_Dbg( p_mux, "asf muxer opened" );
184
    config_ChainParse( p_mux, SOUT_CFG_PREFIX, ppsz_sout_options, p_mux->p_cfg );
185

186
    p_mux->pf_control   = Control;
187
188
189
190
191
    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 ) );
192
193
194
195
196
    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" );
    }
197
    p_sys->pk = NULL;
198
199
200
201
    p_sys->i_pk_used    = 0;
    p_sys->i_pk_frame   = 0;
    p_sys->i_dts_first  = -1;
    p_sys->i_dts_last   = 0;
202
    p_sys->i_preroll_time = 2000;
203
    p_sys->i_bitrate    = 0;
204
    p_sys->i_seq        = 0;
205
206

    p_sys->b_write_header = VLC_TRUE;
207
    p_sys->i_track = 0;
208
209
    p_sys->i_packet_size = config_GetInt( p_mux, "sout-asf-packet-size" );
    msg_Dbg( p_mux, "Packet size %d", p_sys->i_packet_size);
210
    p_sys->i_packet_count= 0;
211
212

    /* Generate a random fid */
213
    srand( mdate() & 0xffffffff );
gbazin's avatar
gbazin committed
214
215
216
    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;
217
218
    for( i = 0; i < 8; i++ )
    {
gbazin's avatar
gbazin committed
219
        p_sys->fid.Data4[i] = ( (uint64_t)rand() << 8 ) / RAND_MAX;
220
    }
221
222

    /* Meta data */
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
    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;

238
239
    msg_Dbg( p_mux, "meta data: title='%s', author='%s', copyright='%s', "
             "comment='%s', rating='%s'",
240
241
             p_sys->psz_title, p_sys->psz_author, p_sys->psz_copyright,
             p_sys->psz_comment, p_sys->psz_rating );
242

243
244
245
246
247
248
249
250
251
252
    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;
253
    block_t  *out;
254
255
256
257
    int i;

    msg_Dbg( p_mux, "Asf muxer closed" );

258
259
260
261
262
263
    /* Flush last packet if any */
    if( (out = asf_packet_flush( p_mux ) ) )
    {
        sout_AccessOutWrite( p_mux->p_access, out );
    }

264
265
266
267
268
    if( ( out = asf_stream_end_create( p_mux ) ) )
    {
        sout_AccessOutWrite( p_mux->p_access, out );
    }

269
270
271
    /* rewrite header */
    if( !sout_AccessOutSeek( p_mux->p_access, 0 ) )
    {
272
273
        out = asf_header_create( p_mux, VLC_FALSE );
        sout_AccessOutWrite( p_mux->p_access, out );
274
275
    }

276
    for( i = 0; i < p_sys->i_track; i++ )
277
278
    {
        free( p_sys->track[i].p_extra );
279
        es_format_Clean( &p_sys->track[i].fmt );
280
281
282
283
284
285
286
    }
    free( p_sys );
}

/*****************************************************************************
 * Capability:
 *****************************************************************************/
287
static int Control( sout_mux_t *p_mux, int i_query, va_list args )
288
{
289
290
291
292
    sout_mux_sys_t *p_sys = p_mux->p_sys;
    vlc_bool_t *pb_bool;
    char **ppsz;

293
294
    switch( i_query )
    {
295
296
       case MUX_CAN_ADD_STREAM_WHILE_MUXING:
           pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
297
298
           if( p_sys->b_asf_http ) *pb_bool = VLC_TRUE;
           else *pb_bool = VLC_FALSE;
299
300
301
302
           return VLC_SUCCESS;

       case MUX_GET_ADD_STREAM_WAIT:
           pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
303
           *pb_bool = VLC_TRUE;
304
305
306
307
308
309
310
311
312
313
           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;

314
        default:
315
            return VLC_EGENERIC;
316
    }
317
318
319
320
321
322
323
324
325
326
327
328
}

/*****************************************************************************
 * 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" );
329
    if( p_sys->i_track >= MAX_ASF_TRACKS )
330
    {
331
        msg_Dbg( p_mux, "cannot add this track (too much tracks)" );
332
333
334
335
        return VLC_EGENERIC;
    }

    tk = p_input->p_sys = &p_sys->track[p_sys->i_track];
336
    tk->i_id  = p_sys->i_track + 1;
337
338
    tk->i_cat = p_input->p_fmt->i_cat;
    tk->i_sequence = 0;
339
    tk->b_audio_correction = 0;
340
341
342
343
344

    switch( tk->i_cat )
    {
        case AUDIO_ES:
        {
345
            int i_blockalign = p_input->p_fmt->audio.i_blockalign;
gbazin's avatar
gbazin committed
346
            int i_bitspersample = p_input->p_fmt->audio.i_bitspersample;
347
            int i_extra = 0;
348

349
            switch( p_input->p_fmt->i_codec )
350
351
            {
                case VLC_FOURCC( 'a', '5', '2', ' ' ):
352
353
                    tk->i_tag = WAVE_FORMAT_A52;
                    tk->psz_name = "A/52";
354
                    i_bitspersample = 0;
355
                    break;
356
357
358
359
360
                case VLC_FOURCC( 'm', 'p', '4', 'a' ):
                    tk->i_tag = WAVE_FORMAT_AAC;
                    tk->psz_name = "MPEG-4 Audio";
                    i_bitspersample = 0;
                    break;
361
                case VLC_FOURCC( 'm', 'p', 'g', 'a' ):
362
#if 1
363
364
                    tk->psz_name = "MPEG Audio Layer 3";
                    tk->i_tag = WAVE_FORMAT_MPEGLAYER3;
365
                    i_bitspersample = 0;
366
                    i_blockalign = 1;
367
                    i_extra = 12;
368
                    break;
369
#else
370
371
                    tk->psz_name = "MPEG Audio Layer 1/2";
                    tk->i_tag = WAVE_FORMAT_MPEG;
372
                    i_bitspersample = 0;
373
374
375
376
                    i_blockalign = 1;
                    i_extra = 22;
                    break;
#endif
377
                case VLC_FOURCC( 'w', 'm', 'a', '1' ):
378
                    tk->psz_name = "Windows Media Audio v1";
379
                    tk->i_tag = WAVE_FORMAT_WMA1;
380
                    tk->b_audio_correction = VLC_TRUE;
381
                    break;
382
                case VLC_FOURCC( 'w', 'm', 'a', ' ' ):
383
                case VLC_FOURCC( 'w', 'm', 'a', '2' ):
384
                    tk->psz_name= "Windows Media Audio (v2) 7, 8 and 9 Series";
385
                    tk->i_tag = WAVE_FORMAT_WMA2;
386
                    tk->b_audio_correction = VLC_TRUE;
387
                    break;
388
389
390
391
392
393
394
395
396
                case VLC_FOURCC( 'w', 'm', 'a', 'p' ):
                    tk->psz_name = "Windows Media Audio 9 Professional";
                    tk->i_tag = WAVE_FORMAT_WMAP;
                    tk->b_audio_correction = VLC_TRUE;
                    break;
                case VLC_FOURCC( 'w', 'm', 'a', 'l' ):
                    tk->psz_name = "Windows Media Audio 9 Lossless";
                    tk->i_tag = WAVE_FORMAT_WMAL;
                    tk->b_audio_correction = VLC_TRUE;
397
398
399
                    break;
                    /* raw codec */
                case VLC_FOURCC( 'u', '8', ' ', ' ' ):
400
401
                    tk->psz_name = "Raw audio 8bits";
                    tk->i_tag = WAVE_FORMAT_PCM;
402
                    i_blockalign= p_input->p_fmt->audio.i_channels;
403
404
405
                    i_bitspersample = 8;
                    break;
                case VLC_FOURCC( 's', '1', '6', 'l' ):
406
407
                    tk->psz_name = "Raw audio 16bits";
                    tk->i_tag = WAVE_FORMAT_PCM;
408
                    i_blockalign= 2 * p_input->p_fmt->audio.i_channels;
409
410
411
                    i_bitspersample = 16;
                    break;
                case VLC_FOURCC( 's', '2', '4', 'l' ):
412
413
                    tk->psz_name = "Raw audio 24bits";
                    tk->i_tag = WAVE_FORMAT_PCM;
414
                    i_blockalign= 3 * p_input->p_fmt->audio.i_channels;
415
416
417
                    i_bitspersample = 24;
                    break;
                case VLC_FOURCC( 's', '3', '2', 'l' ):
418
419
                    tk->psz_name = "Raw audio 32bits";
                    tk->i_tag = WAVE_FORMAT_PCM;
420
                    i_blockalign= 4 * p_input->p_fmt->audio.i_channels;
421
422
423
424
425
426
                    i_bitspersample = 32;
                    break;
                default:
                    return VLC_EGENERIC;
            }

427
            tk->i_extra = sizeof( WAVEFORMATEX ) +
428
                          p_input->p_fmt->i_extra + i_extra;
429
430
            tk->p_extra = malloc( tk->i_extra );
            bo_init( &bo, tk->p_extra, tk->i_extra );
431
            bo_addle_u16( &bo, tk->i_tag );
432
433
            bo_addle_u16( &bo, p_input->p_fmt->audio.i_channels );
            bo_addle_u32( &bo, p_input->p_fmt->audio.i_rate );
434
435
            bo_addle_u32( &bo, p_input->p_fmt->i_bitrate / 8 );
            bo_addle_u16( &bo, i_blockalign );
gbazin's avatar
gbazin committed
436
            tk->i_blockalign = i_blockalign;
437
            bo_addle_u16( &bo, i_bitspersample );
438
            if( p_input->p_fmt->i_extra > 0 )
439
            {
440
441
442
                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 );
443
            }
444
445
446
            else
            {
                bo_addle_u16( &bo, i_extra );
447
                if( tk->i_tag == WAVE_FORMAT_MPEGLAYER3 )
448
                {
449
                    msg_Dbg( p_mux, "adding mp3 header" );
450
451
452
453
454
455
                    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 */
                }
456
                else if( tk->i_tag == WAVE_FORMAT_MPEG )
457
458
459
460
                {
                    msg_Dbg( p_mux, "adding mp2 header" );
                    bo_addle_u16( &bo, 2 );     /* fwHeadLayer */
                    bo_addle_u32( &bo, p_input->p_fmt->i_bitrate );
461
                    bo_addle_u16( &bo, p_input->p_fmt->audio.i_channels == 2 ?1:8 );
462
463
464
465
466
467
                    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 */
                }
468
469
            }

470
471
472
473
474
475
476
477
            if( p_input->p_fmt->i_bitrate > 24000 )
            {
                p_sys->i_bitrate += p_input->p_fmt->i_bitrate;
            }
            else
            {
                p_sys->i_bitrate += 512000;
            }
478
479
480
481
482
            break;
        }
        case VIDEO_ES:
        {
            tk->i_extra = 11 + sizeof( BITMAPINFOHEADER ) +
483
                          p_input->p_fmt->i_extra;
484
485
            tk->p_extra = malloc( tk->i_extra );
            bo_init( &bo, tk->p_extra, tk->i_extra );
486
487
            bo_addle_u32( &bo, p_input->p_fmt->video.i_width );
            bo_addle_u32( &bo, p_input->p_fmt->video.i_height );
488
489
            bo_add_u8   ( &bo, 0x02 );  /* flags */
            bo_addle_u16( &bo, sizeof( BITMAPINFOHEADER ) +
490
                               p_input->p_fmt->i_extra );
491
            bo_addle_u32( &bo, sizeof( BITMAPINFOHEADER ) +
492
493
494
                               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 );
495
496
            bo_addle_u16( &bo, 1 );
            bo_addle_u16( &bo, 24 );
497
            if( p_input->p_fmt->i_codec == VLC_FOURCC('m','p','4','v') )
498
            {
499
500
                tk->psz_name = "MPEG-4 Video";
                tk->i_fourcc = VLC_FOURCC( 'M', 'P', '4', 'S' );
501
            }
502
            else if( p_input->p_fmt->i_codec == VLC_FOURCC('D','I','V','3') )
503
            {
504
505
                tk->psz_name = "MSMPEG-4 V3 Video";
                tk->i_fourcc = VLC_FOURCC( 'M', 'P', '4', '3' );
506
            }
507
            else if( p_input->p_fmt->i_codec == VLC_FOURCC('D','I','V','2') )
508
            {
509
510
                tk->psz_name = "MSMPEG-4 V2 Video";
                tk->i_fourcc = VLC_FOURCC( 'M', 'P', '4', '2' );
511
            }
512
            else if( p_input->p_fmt->i_codec == VLC_FOURCC('D','I','V','1') )
513
            {
514
515
516
                tk->psz_name = "MSMPEG-4 V1 Video";
                tk->i_fourcc = VLC_FOURCC( 'M', 'P', 'G', '4' );
            }
517
            else if( p_input->p_fmt->i_codec == VLC_FOURCC('W','M','V','1') )
518
            {
519
                tk->psz_name = "Windows Media Video 7";
520
521
                tk->i_fourcc = VLC_FOURCC( 'W', 'M', 'V', '1' );
            }
522
            else if( p_input->p_fmt->i_codec == VLC_FOURCC('W','M','V','2') )
523
            {
524
                tk->psz_name = "Windows Media Video 8";
525
                tk->i_fourcc = VLC_FOURCC( 'W', 'M', 'V', '2' );
526
            }
527
528
            else if( p_input->p_fmt->i_codec == VLC_FOURCC('W','M','V','3') )
            {
529
                tk->psz_name = "Windows Media Video 9";
530
531
                tk->i_fourcc = VLC_FOURCC( 'W', 'M', 'V', '3' );
            }
532
533
534
535
536
            else if( p_input->p_fmt->i_codec == VLC_FOURCC('h','2','6','4') )
            {
                tk->psz_name = "H.264/MPEG-4 AVC";
                tk->i_fourcc = VLC_FOURCC('h','2','6','4');
            }
537
538
            else
            {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
539
                tk->psz_name = _("Unknown Video");
540
                tk->i_fourcc = p_input->p_fmt->i_codec;
541
            }
542
            bo_add_mem( &bo, (uint8_t*)&tk->i_fourcc, 4 );
543
544
545
546
547
            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 );
548
            if( p_input->p_fmt->i_extra > 0 )
549
            {
550
551
                bo_add_mem  ( &bo, p_input->p_fmt->p_extra,
                              p_input->p_fmt->i_extra );
552
553
            }

554
555
556
557
558
559
560
561
            if( p_input->p_fmt->i_bitrate > 50000 )
            {
                p_sys->i_bitrate += p_input->p_fmt->i_bitrate;
            }
            else
            {
                p_sys->i_bitrate += 1000000;
            }
562
563
564
565
566
567
568
            break;
        }
        default:
            msg_Err(p_mux, "unhandled track type" );
            return VLC_EGENERIC;
    }

569
570
    es_format_Copy( &tk->fmt, p_input->p_fmt );

571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
    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:
 *****************************************************************************/
587
static int Mux( sout_mux_t *p_mux )
588
589
590
591
592
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;

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

595
        out->i_flags |= BLOCK_FLAG_HEADER;
596
597
598
599
600
601
602
603
604
605
606
        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;
607
608
        block_t *data;
        block_t *pk;
609
610
611
612
613
614
615
616
617
618
619

        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;
        }
620
621
622
623
        if( p_sys->i_dts_last < i_dts )
        {
            p_sys->i_dts_last = i_dts;
        }
624
625
626
627

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

628
        data = block_FifoGet( p_input->p_fifo );
629
630
631
632
633
634
635
636
637
638

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

    return VLC_SUCCESS;
}

639
static int MuxGetStream( sout_mux_t *p_mux, int *pi_stream, mtime_t *pi_dts )
640
641
642
643
644
645
646
647
{
    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];
648
        block_t *p_data;
649

650
        if( block_FifoCount( p_input->p_fifo ) <= 0 )
651
652
653
654
655
656
657
658
659
660
661
        {
            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;
        }

662
        p_data = block_FifoShow( p_input->p_fifo );
663
        if( i_stream == -1 || p_data->i_dts < i_dts )
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
        {
            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 )
    {
719
        memcpy( &p_bo->p_buffer[p_bo->i_buffer], p_mem, i_copy );
720
721
722
723
    }
    p_bo->i_buffer += i_size;
}

Laurent Aimar's avatar
Laurent Aimar committed
724
static void bo_addle_str16( bo_t *bo, const char *str )
725
726
727
728
{
    bo_addle_u16( bo, strlen( str ) + 1 );
    for( ;; )
    {
729
        uint16_t c = (uint8_t)*str++;
730
        bo_addle_u16( bo, c );
731
        if( c == '\0' ) break;
732
733
734
    }
}

Laurent Aimar's avatar
Laurent Aimar committed
735
static void bo_addle_str16_nosize( bo_t *bo, const char *str )
736
737
738
{
    for( ;; )
    {
739
        uint16_t c = (uint8_t)*str++;
740
        bo_addle_u16( bo, c );
741
        if( c == '\0' ) break;
742
743
744
    }
}

745
/****************************************************************************
gbazin's avatar
gbazin committed
746
 * GUID definitions
747
748
749
750
 ****************************************************************************/
static void bo_add_guid( bo_t *p_bo, const guid_t *id )
{
    int i;
gbazin's avatar
gbazin committed
751
752
753
    bo_addle_u32( p_bo, id->Data1 );
    bo_addle_u16( p_bo, id->Data2 );
    bo_addle_u16( p_bo, id->Data3 );
754
755
    for( i = 0; i < 8; i++ )
    {
gbazin's avatar
gbazin committed
756
        bo_add_u8( p_bo, id->Data4[i] );
757
758
759
760
    }
}

static const guid_t asf_object_header_guid =
gbazin's avatar
gbazin committed
761
{0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
762
static const guid_t asf_object_data_guid =
gbazin's avatar
gbazin committed
763
{0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
764
static const guid_t asf_object_file_properties_guid =
gbazin's avatar
gbazin committed
765
{0x8cabdca1, 0xa947, 0x11cf, {0x8e, 0xe4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
766
static const guid_t asf_object_stream_properties_guid =
gbazin's avatar
gbazin committed
767
{0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
768
static const guid_t asf_object_header_extension_guid =
gbazin's avatar
gbazin committed
769
{0x5FBF03B5, 0xA92E, 0x11CF, {0x8E, 0xE3, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
770
static const guid_t asf_object_stream_type_audio =
gbazin's avatar
gbazin committed
771
{0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
772
static const guid_t asf_object_stream_type_video =
gbazin's avatar
gbazin committed
773
{0xbc19efc0, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
774
static const guid_t asf_guid_audio_conceal_none =
gbazin's avatar
gbazin committed
775
{0x20FB5700, 0x5B55, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
gbazin's avatar
gbazin committed
776
777
static const guid_t asf_guid_audio_conceal_spread =
{0xBFC3CD50, 0x618F, 0x11CF, {0x8B, 0xB2, 0x00, 0xAA, 0x00, 0xB4, 0xE2, 0x20}};
778
static const guid_t asf_guid_video_conceal_none =
gbazin's avatar
gbazin committed
779
{0x20FB5700, 0x5B55, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
780
static const guid_t asf_guid_reserved_1 =
gbazin's avatar
gbazin committed
781
{0xABD3D211, 0xA9BA, 0x11cf, {0x8E, 0xE6, 0x00, 0xC0, 0x0C ,0x20, 0x53, 0x65}};
782
static const guid_t asf_object_codec_list_guid =
gbazin's avatar
gbazin committed
783
{0x86D15240, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}};
784
static const guid_t asf_object_codec_list_reserved_guid =
gbazin's avatar
gbazin committed
785
{0x86D15241, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}};
786
static const guid_t asf_object_content_description_guid =
gbazin's avatar
gbazin committed
787
{0x75B22633, 0x668E, 0x11CF, {0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c}};
788
789
static const guid_t asf_object_index_guid =
{0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}};
790
791
static const guid_t asf_object_metadata_guid =
{0xC5F8CBEA, 0x5BAF, 0x4877, {0x84, 0x67, 0xAA, 0x8C, 0x44, 0xFA, 0x4C, 0xCA}};
792

gbazin's avatar
gbazin committed
793
794
795
/****************************************************************************
 * Misc
 ****************************************************************************/
796
797
798
799
800
801
802
803
804
805
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 );
}

806
static block_t *asf_header_create( sout_mux_t *p_mux, vlc_bool_t b_broadcast )
807
808
809
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;
    asf_track_t    *tk;
810
    mtime_t i_duration = 0;
gbazin's avatar
gbazin committed
811
    int i_size, i_header_ext_size, i;
812
    int i_ci_size, i_cm_size = 0, i_cd_size = 0;
813
    block_t *out;
814
    bo_t bo;
815

816
817
    msg_Dbg( p_mux, "Asf muxer creating header" );

818
819
820
    if( p_sys->i_dts_first > 0 )
    {
        i_duration = p_sys->i_dts_last - p_sys->i_dts_first;
821
        if( i_duration < 0 ) i_duration = 0;
822
823
    }

824
    /* calculate header size */
gbazin's avatar
gbazin committed
825
    i_size = 30 + 104;
826
    i_ci_size = 44;
827
    for( i = 0; i < p_sys->i_track; i++ )
828
829
    {
        i_size += 78 + p_sys->track[i].i_extra;
830
        i_ci_size += 8 + 2 * strlen( p_sys->track[i].psz_name );
831
832
        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;
833
834
835

        /* Error correction data field */
        if( p_sys->track[i].b_audio_correction ) i_size += 8;
836
    }
837

838
    /* size of the content description object */
839
840
841
842
843
844
845
846
847
    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 );
    }
848

849
850
851
852
853
854
855
856
857
858
    /* size of the metadata object */
    for( i = 0; i < p_sys->i_track; i++ )
    {
        if( p_sys->track[i].i_cat == VIDEO_ES )
        {
            i_cm_size = 26 + 2 * (16 + 2 * sizeof("AspectRatio?"));
            break;
        }
    }

gbazin's avatar
gbazin committed
859
860
    i_header_ext_size = i_cm_size ? i_cm_size + 46 : 0;
    i_size += i_ci_size + i_cd_size + i_header_ext_size ;
861

862
863
    if( p_sys->b_asf_http )
    {
864
        out = block_New( p_mux, i_size + 50 + 12 );
865
866
867
868
869
        bo_init( &bo, out->p_buffer, i_size + 50 + 12 );
        asf_chunk_add( &bo, 0x4824, i_size + 50, 0xc00, p_sys->i_seq++ );
    }
    else
    {
870
        out = block_New( p_mux, i_size + 50 );
871
872
        bo_init( &bo, out->p_buffer, i_size + 50 );
    }
873

874
875
876
    /* header object */
    bo_add_guid ( &bo, &asf_object_header_guid );
    bo_addle_u64( &bo, i_size );
877
878
    bo_addle_u32( &bo, 2 + p_sys->i_track +
                  (i_cd_size ? 1 : 0) + (i_cm_size ? 1 : 0) );
879
880
881
882
883
884
885
886
887
    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 );
888
    bo_addle_u64( &bo, i_size + 50 + p_sys->i_packet_count *
889
                                p_sys->i_packet_size ); /* file size */
890
    bo_addle_u64( &bo, 0 );                 /* creation date */
891
    bo_addle_u64( &bo, b_broadcast ? 0xffffffffLL : p_sys->i_packet_count );
892
893
    bo_addle_u64( &bo, i_duration * 10 );   /* play duration (100ns) */
    bo_addle_u64( &bo, i_duration * 10 );   /* send duration (100ns) */
894
    bo_addle_u64( &bo, p_sys->i_preroll_time ); /* preroll duration (ms) */
gbazin's avatar
gbazin committed
895
    bo_addle_u32( &bo, b_broadcast ? 0x01 : 0x02 /* seekable */ ); /* flags */
896
897
    bo_addle_u32( &bo, p_sys->i_packet_size );  /* packet size min */
    bo_addle_u32( &bo, p_sys->i_packet_size );  /* packet size max */
898
    bo_addle_u32( &bo, p_sys->i_bitrate );      /* maxbitrate */
899

900
    /* header extension */
gbazin's avatar
gbazin committed
901
902
    if( i_header_ext_size )
    {
903
        bo_add_guid ( &bo, &asf_object_header_extension_guid );
gbazin's avatar
gbazin committed
904
905
906
907
908
        bo_addle_u64( &bo, i_header_ext_size );
        bo_add_guid ( &bo, &asf_guid_reserved_1 );
        bo_addle_u16( &bo, 6 );
        bo_addle_u32( &bo, i_header_ext_size - 46 );
    }
909
910
911
912
913

    /* metadata object (part of header extension) */
    if( i_cm_size )
    {
        int64_t i_num, i_den;
dionoea's avatar
dionoea committed
914
        unsigned int i_dst_num, i_dst_den;
915

916
917
918
        for( i = 0; i < p_sys->i_track; i++ )
            if( p_sys->track[i].i_cat == VIDEO_ES ) break;

919
920
921
        i_num = p_sys->track[i].fmt.video.i_aspect *
            (int64_t)p_sys->track[i].fmt.video.i_height;
        i_den = VOUT_ASPECT_FACTOR * p_sys->track[i].fmt.video.i_width;
922
        vlc_ureduce( &i_dst_num, &i_dst_den, i_num, i_den, 0 );
923

zorglub's avatar
zorglub committed
924
        msg_Dbg( p_mux, "pixel aspect-ratio: %i/%i", i_dst_num, i_dst_den );
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945

        bo_add_guid ( &bo, &asf_object_metadata_guid );
        bo_addle_u64( &bo, i_cm_size );
        bo_addle_u16( &bo, 2 ); /* description records count */
        /* 1st description record */
        bo_addle_u16( &bo, 0 ); /* reserved */
        bo_addle_u16( &bo, i + 1 ); /* stream number (0 for the whole file) */
        bo_addle_u16( &bo, 2 * sizeof("AspectRatioX") ); /* name length */
        bo_addle_u16( &bo, 0x3 /* DWORD */ ); /* data type */
        bo_addle_u32( &bo, 4 ); /* data length */
        bo_addle_str16_nosize( &bo, "AspectRatioX" );
        bo_addle_u32( &bo, i_dst_num ); /* data */
        /* 2nd description record */
        bo_addle_u16( &bo, 0 ); /* reserved */
        bo_addle_u16( &bo, i + 1 ); /* stream number (0 for the whole file) */
        bo_addle_u16( &bo, 2 * sizeof("AspectRatioY") ); /* name length */
        bo_addle_u16( &bo, 0x3 /* DWORD */ ); /* data type */
        bo_addle_u32( &bo, 4 ); /* data length */
        bo_addle_str16_nosize( &bo, "AspectRatioY" );
        bo_addle_u32( &bo, i_dst_den ); /* data */
    }
946

947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
    /* 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 );
    }

965
    /* stream properties */
966
    for( i = 0; i < p_sys->i_track; i++ )
967
968
969
970
    {
        tk = &p_sys->track[i];

        bo_add_guid ( &bo, &asf_object_stream_properties_guid );
971
        bo_addle_u64( &bo, 78 + tk->i_extra + (tk->b_audio_correction ? 8:0) );
gbazin's avatar
gbazin committed
972

973
974
975
        if( tk->i_cat == AUDIO_ES )
        {
            bo_add_guid( &bo, &asf_object_stream_type_audio );
976
977
978
979
            if( tk->b_audio_correction )
                bo_add_guid( &bo, &asf_guid_audio_conceal_spread );
            else
                bo_add_guid( &bo, &asf_guid_audio_conceal_none );
980
981
982
983
984
985
986
987
        }
        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 );
988
989
        /* correction data length */
        bo_addle_u32( &bo, tk->b_audio_correction ? 8 : 0 );
990
991
992
        bo_addle_u16( &bo, tk->i_id );  /* stream number */
        bo_addle_u32( &bo, 0 );
        bo_add_mem  ( &bo, tk->p_extra, tk->i_extra );
gbazin's avatar
gbazin committed
993
994

        /* Error correction data field */
995
        if( tk->b_audio_correction )
gbazin's avatar
gbazin committed
996
997
998
999
1000
1001
1002
        {
            bo_add_u8( &bo, 0x1 ); /* span */
            bo_addle_u16( &bo, tk->i_blockalign );  /* virtual packet length */
            bo_addle_u16( &bo, tk->i_blockalign );  /* virtual chunck length */
            bo_addle_u16( &bo, 1 );  /* silence length */
            bo_add_u8( &bo, 0x0 ); /* data */
        }
1003
1004
    }

1005
    /* Codec Infos */
1006
    bo_add_guid ( &bo, &asf_object_codec_list_guid );
1007
    bo_addle_u64( &bo, i_ci_size );
1008
    bo_add_guid ( &bo, &asf_object_codec_list_reserved_guid );
1009
1010
    bo_addle_u32( &bo, p_sys->i_track );
    for( i = 0; i < p_sys->i_track; i++ )
1011
1012
1013
    {
        tk = &p_sys->track[i];

1014
1015
1016
1017
        if( tk->i_cat == VIDEO_ES ) bo_addle_u16( &bo, 1 /* video */ );
        else if( tk->i_cat == AUDIO_ES ) bo_addle_u16( &bo, 2 /* audio */ );
        else bo_addle_u16( &bo, 0xFFFF /* unknown */ );

1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
        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 );
        }
    }

1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
    /* 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;
}

/****************************************************************************
 *
 ****************************************************************************/
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
static block_t *asf_packet_flush( sout_mux_t *p_mux )
{