asf.c 43.5 KB
Newer Older
1
/*****************************************************************************
2
 * asf.c: asf muxer module for vlc
3
 *****************************************************************************
4
 * Copyright (C) 2003-2004, 2006 VLC authors and VideoLAN
5
 * $Id$
6 7
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8
 *          Gildas Bazin <gbazin@videolan.org>
9
 *
10 11 12
 * 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
13 14 15 16
 * (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
17 18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
19
 *
20 21 22
 * 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.
23 24 25 26 27 28
 *****************************************************************************/

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

29 30 31 32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

33 34
#include <assert.h>

35
#include <vlc_common.h>
36
#include <vlc_plugin.h>
37 38 39
#include <vlc_sout.h>
#include <vlc_block.h>
#include <vlc_codecs.h>
40
#include <vlc_arrays.h>
41
#include <vlc_rand.h>
42

43
#include "../demux/asf/libasf_guid.h"
44

45
#define MAX_ASF_TRACKS 128
46
#define ASF_DATA_PACKET_SIZE 4096  // deprecated -- added sout-asf-packet-size
47

48 49 50 51 52 53
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
static int  Open   ( vlc_object_t * );
static void Close  ( vlc_object_t * );

54 55
#define SOUT_CFG_PREFIX "sout-asf-"

56
#define TITLE_TEXT N_("Title")
Clément Stenac's avatar
Clément Stenac committed
57
#define TITLE_LONGTEXT N_("Title to put in ASF comments." )
58
#define AUTHOR_TEXT N_("Author")
Clément Stenac's avatar
Clément Stenac committed
59
#define AUTHOR_LONGTEXT N_("Author to put in ASF comments." )
60
#define COPYRIGHT_TEXT N_("Copyright")
Clément Stenac's avatar
Clément Stenac committed
61
#define COPYRIGHT_LONGTEXT N_("Copyright string to put in ASF comments." )
62
#define COMMENT_TEXT N_("Comment")
Clément Stenac's avatar
Clément Stenac committed
63
#define COMMENT_LONGTEXT N_("Comment to put in ASF comments." )
64
#define RATING_TEXT N_("Rating")
Clément Stenac's avatar
Clément Stenac committed
65
#define RATING_LONGTEXT N_("\"Rating\" to put in ASF comments." )
66
#define PACKETSIZE_TEXT N_("Packet Size")
Clément Stenac's avatar
Clément Stenac committed
67
#define PACKETSIZE_LONGTEXT N_("ASF packet size -- default is 4096 bytes")
68 69 70
#define BITRATE_TEXT N_("Bitrate override")
#define BITRATE_LONGTEXT N_("Do not try to guess ASF bitrate. Setting this, allows you to control how Windows Media Player will cache streamed content. Set to audio+video bitrate in bytes")

71

72 73 74 75 76
vlc_module_begin ()
    set_description( N_("ASF muxer") )
    set_category( CAT_SOUT )
    set_subcategory( SUBCAT_SOUT_MUX )
    set_shortname( "ASF" )
77

78
    set_capability( "sout mux", 5 )
79
    add_shortcut( "asf", "asfh" )
80
    set_callbacks( Open, Close )
81

82
    add_string( SOUT_CFG_PREFIX "title", "", TITLE_TEXT, TITLE_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
83
                                 true )
84
    add_string( SOUT_CFG_PREFIX "author",   "", AUTHOR_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
85
                                 AUTHOR_LONGTEXT, true )
86
    add_string( SOUT_CFG_PREFIX "copyright","", COPYRIGHT_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
87
                                 COPYRIGHT_LONGTEXT, true )
88
    add_string( SOUT_CFG_PREFIX "comment",  "", COMMENT_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
89
                                 COMMENT_LONGTEXT, true )
90
    add_string( SOUT_CFG_PREFIX "rating",  "", RATING_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
91
                                 RATING_LONGTEXT, true )
92
    add_integer( SOUT_CFG_PREFIX "packet-size", 4096, PACKETSIZE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
93
                                 PACKETSIZE_LONGTEXT, true )
94
    add_integer( SOUT_CFG_PREFIX "bitrate-override", 0, BITRATE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
95
                                 BITRATE_LONGTEXT, true )
96

97
vlc_module_end ()
98 99 100 101

/*****************************************************************************
 * Locales prototypes
 *****************************************************************************/
102
static const char *const ppsz_sout_options[] = {
103
    "title", "author", "copyright", "comment", "rating", NULL
104
};
105

106 107
static int Control  ( sout_mux_t *, int, va_list );
static int AddStream( sout_mux_t *, sout_input_t * );
108
static void DelStream( sout_mux_t *, sout_input_t * );
109
static int Mux      ( sout_mux_t * );
110 111 112

typedef struct
{
113 114
    int          i_id;
    int          i_cat;
115

116
    /* codec information */
117 118
    uint16_t     i_tag;     /* for audio */
    vlc_fourcc_t i_fourcc;  /* for video */
Laurent Aimar's avatar
Laurent Aimar committed
119
    const char         *psz_name; /* codec name */
Gildas Bazin's avatar
Gildas Bazin committed
120
    int          i_blockalign; /* for audio only */
121
    bool   b_audio_correction;
122 123 124 125

    int          i_sequence;

    int          i_extra;
126
    uint8_t      *p_extra;
Laurent Aimar's avatar
Laurent Aimar committed
127
    bool         b_extended;
128 129

    es_format_t  fmt;
130

131 132 133 134 135 136 137 138
} 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;
139
    mtime_t         i_dts_last;
140
    mtime_t         i_preroll_time;
141
    int64_t         i_bitrate;
142
    int64_t         i_bitrate_override;
143

144
    vlc_array_t     *p_tracks;
145

146
    bool            b_write_header;
147

148
    block_t         *pk;
149 150 151
    int             i_pk_used;
    int             i_pk_frame;
    mtime_t         i_pk_dts;
152

153
    bool      b_asf_http;
154
    int             i_seq;
155 156 157 158 159 160 161

    /* meta data */
    char            *psz_title;
    char            *psz_author;
    char            *psz_copyright;
    char            *psz_comment;
    char            *psz_rating;
162 163
};

164
static block_t *asf_header_create( sout_mux_t *, bool );
165
static block_t *asf_packet_create( sout_mux_t *, asf_track_t *, block_t * );
166
static block_t *asf_stream_end_create( sout_mux_t *);
167
static block_t *asf_packet_flush( sout_mux_t * );
168 169 170 171 172 173

typedef struct
{
    int      i_buffer_size;
    int      i_buffer;
    uint8_t  *p_buffer;
174

175 176 177 178 179 180 181 182 183
} 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
184
static void bo_addle_str16( bo_t *, const char * );
185 186 187 188 189 190 191 192 193

/*****************************************************************************
 * 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;

194
    msg_Dbg( p_mux, "asf muxer opened" );
195
    config_ChainParse( p_mux, SOUT_CFG_PREFIX, ppsz_sout_options, p_mux->p_cfg );
196

197
    p_mux->pf_control   = Control;
198 199 200 201 202
    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 ) );
203 204
    if( !p_sys )
        return VLC_ENOMEM;
205 206 207 208 209
    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" );
    }
210
    p_sys->pk = NULL;
211 212
    p_sys->i_pk_used    = 0;
    p_sys->i_pk_frame   = 0;
213 214
    p_sys->i_dts_first  =
    p_sys->i_dts_last   = VLC_TS_INVALID;
215
    p_sys->i_preroll_time = 2000;
216
    p_sys->i_bitrate    = 0;
217
    p_sys->i_bitrate_override = 0;
218
    p_sys->i_seq        = 0;
219
    p_sys->p_tracks     = vlc_array_new();
220

221
    p_sys->b_write_header = true;
222 223
    p_sys->i_packet_size = var_InheritInteger( p_mux, "sout-asf-packet-size" );
    p_sys->i_bitrate_override = var_InheritInteger( p_mux, "sout-asf-bitrate-override" );
224
    msg_Dbg( p_mux, "Packet size %d", p_sys->i_packet_size);
225
    if (p_sys->i_bitrate_override)
226
        msg_Dbg( p_mux, "Bitrate override %"PRId64, p_sys->i_bitrate_override);
227
    p_sys->i_packet_count= 0;
228 229

    /* Generate a random fid */
230
    p_sys->fid.Data1 = 0xbabac001;
231 232 233
    vlc_rand_bytes(&p_sys->fid.Data2, sizeof(p_sys->fid.Data2));
    vlc_rand_bytes(&p_sys->fid.Data3, sizeof(p_sys->fid.Data3));
    vlc_rand_bytes(p_sys->fid.Data4, sizeof(p_sys->fid.Data4));
234 235

    /* Meta data */
236 237 238 239 240
    p_sys->psz_title = var_GetString( p_mux, SOUT_CFG_PREFIX "title" );
    p_sys->psz_author = var_GetString( p_mux, SOUT_CFG_PREFIX "author" );
    p_sys->psz_copyright = var_GetString( p_mux, SOUT_CFG_PREFIX "copyright" );
    p_sys->psz_comment = var_GetString( p_mux, SOUT_CFG_PREFIX "comment" );
    p_sys->psz_rating = var_GetString( p_mux, SOUT_CFG_PREFIX "rating" );
241

242 243
    msg_Dbg( p_mux, "meta data: title='%s', author='%s', copyright='%s', "
             "comment='%s', rating='%s'",
244 245
             p_sys->psz_title, p_sys->psz_author, p_sys->psz_copyright,
             p_sys->psz_comment, p_sys->psz_rating );
246

247 248 249 250 251 252 253 254 255 256
    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;
257
    block_t  *out;
258 259 260

    msg_Dbg( p_mux, "Asf muxer closed" );

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

267 268 269 270 271
    if( ( out = asf_stream_end_create( p_mux ) ) )
    {
        sout_AccessOutWrite( p_mux->p_access, out );
    }

272
    /* rewrite header */
273
    if( sout_AccessOutSeek( p_mux->p_access, 0 ) == VLC_SUCCESS )
274
    {
275
        out = asf_header_create( p_mux, false );
276
        sout_AccessOutWrite( p_mux->p_access, out );
277 278
    }

279

280
    for( int i = 0; i < vlc_array_count( p_sys->p_tracks ); i++ )
281
    {
282 283 284
        asf_track_t *track = (asf_track_t *)vlc_array_item_at_index( p_sys->p_tracks, i );
        free( track->p_extra );
        es_format_Clean( &track->fmt );
285
    }
286

287 288 289 290
    vlc_array_clear( p_sys->p_tracks );

    vlc_array_destroy( p_sys->p_tracks );

291 292 293 294 295
    free( p_sys->psz_title );
    free( p_sys->psz_author );
    free( p_sys->psz_copyright );
    free( p_sys->psz_comment );
    free( p_sys->psz_rating );
296 297 298 299 300 301
    free( p_sys );
}

/*****************************************************************************
 * Capability:
 *****************************************************************************/
302
static int Control( sout_mux_t *p_mux, int i_query, va_list args )
303
{
304
    sout_mux_sys_t *p_sys = p_mux->p_sys;
305
    bool *pb_bool;
306 307
    char **ppsz;

308 309
    switch( i_query )
    {
310
       case MUX_CAN_ADD_STREAM_WHILE_MUXING:
311 312
           pb_bool = (bool*)va_arg( args, bool * );
           if( p_sys->b_asf_http ) *pb_bool = true;
313
           else *pb_bool = false;
314 315 316
           return VLC_SUCCESS;

       case MUX_GET_ADD_STREAM_WAIT:
317 318
           pb_bool = (bool*)va_arg( args, bool * );
           *pb_bool = true;
319 320 321 322 323 324 325 326 327 328
           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;

329
        default:
330
            return VLC_EGENERIC;
331
    }
332 333 334 335 336 337 338 339 340 341 342 343
}

/*****************************************************************************
 * 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" );
344
    if( vlc_array_count( p_sys->p_tracks ) >= MAX_ASF_TRACKS )
345
    {
346
        msg_Dbg( p_mux, "cannot add this track (too much tracks)" );
347 348 349
        return VLC_EGENERIC;
    }

350 351
    tk = p_input->p_sys = malloc( sizeof( asf_track_t ) );
    memset( tk, 0, sizeof( *tk ) );
352 353
    tk->i_cat = p_input->p_fmt->i_cat;
    tk->i_sequence = 0;
354
    tk->b_audio_correction = 0;
Laurent Aimar's avatar
Laurent Aimar committed
355
    tk->b_extended = false;
356 357 358 359 360

    switch( tk->i_cat )
    {
        case AUDIO_ES:
        {
361
            int i_blockalign = p_input->p_fmt->audio.i_blockalign;
Gildas Bazin's avatar
Gildas Bazin committed
362
            int i_bitspersample = p_input->p_fmt->audio.i_bitspersample;
363
            int i_extra = 0;
364

365
            switch( p_input->p_fmt->i_codec )
366
            {
367
                case VLC_CODEC_A52:
368 369
                    tk->i_tag = WAVE_FORMAT_A52;
                    tk->psz_name = "A/52";
370
                    i_bitspersample = 0;
371
                    break;
372
                case VLC_CODEC_MP4A:
373 374 375 376
                    tk->i_tag = WAVE_FORMAT_AAC;
                    tk->psz_name = "MPEG-4 Audio";
                    i_bitspersample = 0;
                    break;
377
                case VLC_CODEC_MP3:
378 379
                    tk->psz_name = "MPEG Audio Layer 3";
                    tk->i_tag = WAVE_FORMAT_MPEGLAYER3;
380
                    i_bitspersample = 0;
381
                    i_blockalign = 1;
382
                    i_extra = 12;
383
                    break;
384
                case VLC_CODEC_MPGA:
385 386
                    tk->psz_name = "MPEG Audio Layer 1/2";
                    tk->i_tag = WAVE_FORMAT_MPEG;
387
                    i_bitspersample = 0;
388 389 390
                    i_blockalign = 1;
                    i_extra = 22;
                    break;
391
                case VLC_CODEC_WMA1:
392
                    tk->psz_name = "Windows Media Audio v1";
393
                    tk->i_tag = WAVE_FORMAT_WMA1;
394
                    tk->b_audio_correction = true;
395
                    break;
396
                case VLC_CODEC_WMA2:
397
                    tk->psz_name= "Windows Media Audio (v2) 7, 8 and 9 Series";
398
                    tk->i_tag = WAVE_FORMAT_WMA2;
399
                    tk->b_audio_correction = true;
400
                    break;
401
                case VLC_CODEC_WMAP:
402 403
                    tk->psz_name = "Windows Media Audio 9 Professional";
                    tk->i_tag = WAVE_FORMAT_WMAP;
404
                    tk->b_audio_correction = true;
405
                    break;
406
                case VLC_CODEC_WMAL:
407 408
                    tk->psz_name = "Windows Media Audio 9 Lossless";
                    tk->i_tag = WAVE_FORMAT_WMAL;
409
                    tk->b_audio_correction = true;
410 411
                    break;
                    /* raw codec */
412
                case VLC_CODEC_U8:
413 414
                    tk->psz_name = "Raw audio 8bits";
                    tk->i_tag = WAVE_FORMAT_PCM;
415
                    i_blockalign= p_input->p_fmt->audio.i_channels;
416 417
                    i_bitspersample = 8;
                    break;
418
                case VLC_CODEC_S16L:
419 420
                    tk->psz_name = "Raw audio 16bits";
                    tk->i_tag = WAVE_FORMAT_PCM;
421
                    i_blockalign= 2 * p_input->p_fmt->audio.i_channels;
422 423
                    i_bitspersample = 16;
                    break;
424
                case VLC_CODEC_S24L:
425 426
                    tk->psz_name = "Raw audio 24bits";
                    tk->i_tag = WAVE_FORMAT_PCM;
427
                    i_blockalign= 3 * p_input->p_fmt->audio.i_channels;
428 429
                    i_bitspersample = 24;
                    break;
430
                case VLC_CODEC_S32L:
431 432
                    tk->psz_name = "Raw audio 32bits";
                    tk->i_tag = WAVE_FORMAT_PCM;
433
                    i_blockalign= 4 * p_input->p_fmt->audio.i_channels;
434 435 436 437 438 439
                    i_bitspersample = 32;
                    break;
                default:
                    return VLC_EGENERIC;
            }

440
            tk->i_extra = sizeof( WAVEFORMATEX ) +
441
                          p_input->p_fmt->i_extra + i_extra;
442
            tk->p_extra = malloc( tk->i_extra );
443 444
            if( !tk->p_extra )
                return VLC_ENOMEM;
445
            bo_init( &bo, tk->p_extra, tk->i_extra );
446
            bo_addle_u16( &bo, tk->i_tag );
447 448
            bo_addle_u16( &bo, p_input->p_fmt->audio.i_channels );
            bo_addle_u32( &bo, p_input->p_fmt->audio.i_rate );
449 450
            bo_addle_u32( &bo, p_input->p_fmt->i_bitrate / 8 );
            bo_addle_u16( &bo, i_blockalign );
Gildas Bazin's avatar
Gildas Bazin committed
451
            tk->i_blockalign = i_blockalign;
452
            bo_addle_u16( &bo, i_bitspersample );
453
            if( p_input->p_fmt->i_extra > 0 )
454
            {
455 456 457
                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 );
458
            }
459 460 461
            else
            {
                bo_addle_u16( &bo, i_extra );
462
                if( tk->i_tag == WAVE_FORMAT_MPEGLAYER3 )
463
                {
464
                    msg_Dbg( p_mux, "adding mp3 header" );
465 466 467 468 469 470
                    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 */
                }
471
                else if( tk->i_tag == WAVE_FORMAT_MPEG )
472 473 474 475
                {
                    msg_Dbg( p_mux, "adding mp2 header" );
                    bo_addle_u16( &bo, 2 );     /* fwHeadLayer */
                    bo_addle_u32( &bo, p_input->p_fmt->i_bitrate );
476
                    bo_addle_u16( &bo, p_input->p_fmt->audio.i_channels == 2 ?1:8 );
477 478 479 480 481 482
                    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 */
                }
483 484
            }

485 486 487 488 489 490
            if( p_input->p_fmt->i_bitrate > 24000 )
            {
                p_sys->i_bitrate += p_input->p_fmt->i_bitrate;
            }
            else
            {
491
                p_sys->i_bitrate += 128000;
492
            }
493 494
            if (p_sys->i_bitrate_override)
                p_sys->i_bitrate = p_sys->i_bitrate_override;
495 496 497 498
            break;
        }
        case VIDEO_ES:
        {
Laurent Aimar's avatar
Laurent Aimar committed
499 500 501 502
            const es_format_t *p_fmt = p_input->p_fmt;
            uint8_t *p_codec_extra = NULL;
            int     i_codec_extra = 0;

503
            if( p_input->p_fmt->i_codec == VLC_CODEC_MP4V )
504
            {
505 506
                tk->psz_name = "MPEG-4 Video";
                tk->i_fourcc = VLC_FOURCC( 'M', 'P', '4', 'S' );
507
            }
508
            else if( p_input->p_fmt->i_codec == VLC_CODEC_DIV3 )
509
            {
510 511
                tk->psz_name = "MSMPEG-4 V3 Video";
                tk->i_fourcc = VLC_FOURCC( 'M', 'P', '4', '3' );
512
            }
513
            else if( p_input->p_fmt->i_codec == VLC_CODEC_DIV2 )
514
            {
515 516
                tk->psz_name = "MSMPEG-4 V2 Video";
                tk->i_fourcc = VLC_FOURCC( 'M', 'P', '4', '2' );
517
            }
518
            else if( p_input->p_fmt->i_codec == VLC_CODEC_DIV1 )
519
            {
520 521 522
                tk->psz_name = "MSMPEG-4 V1 Video";
                tk->i_fourcc = VLC_FOURCC( 'M', 'P', 'G', '4' );
            }
523
            else if( p_input->p_fmt->i_codec == VLC_CODEC_WMV1 )
524
            {
525
                tk->psz_name = "Windows Media Video 7";
526 527
                tk->i_fourcc = VLC_FOURCC( 'W', 'M', 'V', '1' );
            }
528
            else if( p_input->p_fmt->i_codec == VLC_CODEC_WMV2 )
529
            {
530
                tk->psz_name = "Windows Media Video 8";
531
                tk->i_fourcc = VLC_FOURCC( 'W', 'M', 'V', '2' );
532
            }
533
            else if( p_input->p_fmt->i_codec == VLC_CODEC_WMV3 )
534
            {
535
                tk->psz_name = "Windows Media Video 9";
536
                tk->i_fourcc = VLC_FOURCC( 'W', 'M', 'V', '3' );
537
                tk->b_extended = true;
538
            }
539
            else if( p_input->p_fmt->i_codec == VLC_CODEC_VC1 )
Laurent Aimar's avatar
Laurent Aimar committed
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
            {
                tk->psz_name = "Windows Media Video 9 Advanced Profile";
                tk->i_fourcc = VLC_FOURCC( 'W', 'V', 'C', '1' );
                tk->b_extended = true;

                if( p_fmt->i_extra > 0 )
                {
                    p_codec_extra = malloc( 1 + p_fmt->i_extra );
                    if( p_codec_extra )
                    {
                        i_codec_extra = 1 + p_fmt->i_extra;
                        p_codec_extra[0] = 0x01;
                        memcpy( &p_codec_extra[1], p_fmt->p_extra, p_fmt->i_extra );
                    }
                }
            }
556
            else if( p_input->p_fmt->i_codec == VLC_CODEC_H264 )
557 558 559 560
            {
                tk->psz_name = "H.264/MPEG-4 AVC";
                tk->i_fourcc = VLC_FOURCC('h','2','6','4');
            }
561 562
            else
            {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
563
                tk->psz_name = _("Unknown Video");
564
                tk->i_fourcc = p_input->p_fmt->i_original_fourcc ?: p_input->p_fmt->i_codec;
565
            }
Laurent Aimar's avatar
Laurent Aimar committed
566 567 568 569 570 571 572 573 574 575
            if( !i_codec_extra && p_fmt->i_extra > 0 )
            {
                p_codec_extra = malloc( p_fmt->i_extra );
                if( p_codec_extra )
                {
                    i_codec_extra = p_fmt->i_extra;
                    memcpy( p_codec_extra, p_fmt->p_extra, p_fmt->i_extra );
                }
            }

576
            tk->i_extra = 11 + sizeof( VLC_BITMAPINFOHEADER ) + i_codec_extra;
Laurent Aimar's avatar
Laurent Aimar committed
577 578
            tk->p_extra = malloc( tk->i_extra );
            if( !tk->p_extra )
Rémi Duraffort's avatar
Rémi Duraffort committed
579 580
            {
                free( p_codec_extra );
Laurent Aimar's avatar
Laurent Aimar committed
581
                return VLC_ENOMEM;
Rémi Duraffort's avatar
Rémi Duraffort committed
582
            }
Laurent Aimar's avatar
Laurent Aimar committed
583 584 585 586
            bo_init( &bo, tk->p_extra, tk->i_extra );
            bo_addle_u32( &bo, p_input->p_fmt->video.i_width );
            bo_addle_u32( &bo, p_input->p_fmt->video.i_height );
            bo_add_u8   ( &bo, 0x02 );  /* flags */
587 588
            bo_addle_u16( &bo, sizeof( VLC_BITMAPINFOHEADER ) + i_codec_extra );
            bo_addle_u32( &bo, sizeof( VLC_BITMAPINFOHEADER ) + i_codec_extra );
Laurent Aimar's avatar
Laurent Aimar committed
589 590 591 592
            bo_addle_u32( &bo, p_input->p_fmt->video.i_width );
            bo_addle_u32( &bo, p_input->p_fmt->video.i_height );
            bo_addle_u16( &bo, 1 );
            bo_addle_u16( &bo, 24 );
593
            bo_add_mem( &bo, (uint8_t*)&tk->i_fourcc, 4 );
594 595 596 597 598
            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 );
Laurent Aimar's avatar
Laurent Aimar committed
599
            if( i_codec_extra > 0 )
Rémi Duraffort's avatar
Rémi Duraffort committed
600
            {
Laurent Aimar's avatar
Laurent Aimar committed
601
                bo_add_mem( &bo, p_codec_extra, i_codec_extra );
Rémi Duraffort's avatar
Rémi Duraffort committed
602 603
                free( p_codec_extra );
            }
604

605 606 607 608 609 610
            if( p_input->p_fmt->i_bitrate > 50000 )
            {
                p_sys->i_bitrate += p_input->p_fmt->i_bitrate;
            }
            else
            {
611
                p_sys->i_bitrate += 512000;
612
            }
Laurent Aimar's avatar
Laurent Aimar committed
613
            if( p_sys->i_bitrate_override )
614
                p_sys->i_bitrate = p_sys->i_bitrate_override;
615 616 617 618 619 620 621
            break;
        }
        default:
            msg_Err(p_mux, "unhandled track type" );
            return VLC_EGENERIC;
    }

622 623
    es_format_Copy( &tk->fmt, p_input->p_fmt );

624 625 626
    vlc_array_append( p_sys->p_tracks, (void *)tk);
    tk->i_id = vlc_array_index_of_item( p_sys->p_tracks, tk ) + 1;

627

628 629
    if( p_sys->b_asf_http )
        p_sys->b_write_header = true;
630

631 632 633 634 635 636
    return VLC_SUCCESS;
}

/*****************************************************************************
 * DelStream:
 *****************************************************************************/
637
static void DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
638
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
639
    /* if bitrate ain't defined in commandline, reduce it when tracks are deleted
640 641 642
     */
    sout_mux_sys_t   *p_sys = p_mux->p_sys;
    asf_track_t      *tk = p_input->p_sys;
643
    msg_Dbg( p_mux, "removing input" );
644 645 646 647 648 649 650
    if(!p_sys->i_bitrate_override)
    {
        if( tk->i_cat == AUDIO_ES )
        {
             if( p_input->p_fmt->i_bitrate > 24000 )
                 p_sys->i_bitrate -= p_input->p_fmt->i_bitrate;
             else
651
                 p_sys->i_bitrate -= 128000;
652 653 654 655 656 657
        }
        else if(tk->i_cat == VIDEO_ES )
        {
             if( p_input->p_fmt->i_bitrate > 50000 )
                 p_sys->i_bitrate -= p_input->p_fmt->i_bitrate;
             else
658
                 p_sys->i_bitrate -= 512000;
659 660
        }
    }
661

662
    if( p_sys->b_asf_http )
663
    {
664
        vlc_array_remove( p_sys->p_tracks, vlc_array_index_of_item( p_sys->p_tracks, (void *)tk ) );
665 666
        p_sys->b_write_header = true;
    }
667 668 669 670 671
}

/*****************************************************************************
 * Mux:
 *****************************************************************************/
672
static int Mux( sout_mux_t *p_mux )
673 674 675 676 677
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;

    if( p_sys->b_write_header )
    {
678
        block_t *out = asf_header_create( p_mux, true );
679

680
        out->i_flags |= BLOCK_FLAG_HEADER;
681 682
        sout_AccessOutWrite( p_mux->p_access, out );

683
        p_sys->b_write_header = false;
684 685 686 687 688 689 690
    }

    for( ;; )
    {
        sout_input_t  *p_input;
        asf_track_t   *tk;
        mtime_t       i_dts;
691 692
        block_t *data;
        block_t *pk;
693

694 695
        int i_stream = sout_MuxGetStream( p_mux, 1, &i_dts );
        if( i_stream < 0 )
696 697 698 699 700
        {
            /* not enough data */
            return VLC_SUCCESS;
        }

701
        if( p_sys->i_dts_first <= VLC_TS_INVALID )
702 703 704
        {
            p_sys->i_dts_first = i_dts;
        }
705 706 707 708
        if( p_sys->i_dts_last < i_dts )
        {
            p_sys->i_dts_last = i_dts;
        }
709 710 711 712

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

713
        data = block_FifoGet( p_input->p_fifo );
714

Laurent Aimar's avatar
Laurent Aimar committed
715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
        /* Convert VC1 to ASF special format */
        if( tk->i_fourcc == VLC_FOURCC( 'W', 'V', 'C', '1' ) )
        {
            while( data->i_buffer >= 4 &&
                   ( data->p_buffer[0] != 0x00 || data->p_buffer[1] != 0x00 ||
                     data->p_buffer[2] != 0x01 ||
                     ( data->p_buffer[3] != 0x0D && data->p_buffer[3] != 0x0C ) ) )
            {
                data->i_buffer--;
                data->p_buffer++;
            }
            if( data->i_buffer >= 4 )
            {
                data->i_buffer -= 4;
                data->p_buffer += 4;
            }
        }

733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
        if( ( pk = asf_packet_create( p_mux, tk, data ) ) )
        {
            sout_AccessOutWrite( p_mux->p_access, pk );
        }
    }

    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 )
    {
785
        memcpy( &p_bo->p_buffer[p_bo->i_buffer], p_mem, i_copy );
786 787 788 789
    }
    p_bo->i_buffer += i_size;
}

Laurent Aimar's avatar
Laurent Aimar committed
790
static void bo_addle_str16( bo_t *bo, const char *str )
791 792 793 794
{
    bo_addle_u16( bo, strlen( str ) + 1 );
    for( ;; )
    {
795
        uint16_t c = (uint8_t)*str++;
796
        bo_addle_u16( bo, c );
797
        if( c == '\0' ) break;
798 799 800
    }
}

Laurent Aimar's avatar
Laurent Aimar committed
801
static void bo_addle_str16_nosize( bo_t *bo, const char *str )
802 803 804
{
    for( ;; )
    {
805
        uint16_t c = (uint8_t)*str++;
806
        bo_addle_u16( bo, c );
807
        if( c == '\0' ) break;
808 809 810
    }
}

811
/****************************************************************************
812
 * GUID definitions
813 814 815
 ****************************************************************************/
static void bo_add_guid( bo_t *p_bo, const guid_t *id )
{
816 817 818
    bo_addle_u32( p_bo, id->Data1 );
    bo_addle_u16( p_bo, id->Data2 );
    bo_addle_u16( p_bo, id->Data3 );
819
    for( int i = 0; i < 8; i++ )
820
    {
821
        bo_add_u8( p_bo, id->Data4[i] );
822 823 824
    }
}

825 826 827
/****************************************************************************
 * Misc
 ****************************************************************************/
828 829 830 831 832 833 834 835 836 837
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 );
}

838
static block_t *asf_header_create( sout_mux_t *p_mux, bool b_broadcast )
839 840 841
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;
    asf_track_t    *tk;
842
    mtime_t i_duration = 0;
Gildas Bazin's avatar
Gildas Bazin committed
843
    int i_size, i_header_ext_size, i;
844
    int i_ci_size, i_cm_size = 0, i_cd_size = 0;
845
    block_t *out;
846
    bo_t bo;
847
    tk=NULL;
848

849 850
    msg_Dbg( p_mux, "Asf muxer creating header" );

851
    if( p_sys->i_dts_first > VLC_TS_INVALID )
852 853
    {
        i_duration = p_sys->i_dts_last - p_sys->i_dts_first;
854
        if( i_duration < 0 ) i_duration = 0;
855 856
    }

857
    /* calculate header size */
Gildas Bazin's avatar
Gildas Bazin committed
858
    i_size = 30 + 104;
859
    i_ci_size = 44;
860
    for( i = 0; i < vlc_array_count( p_sys->p_tracks ); i++ )
861
    {
862 863 864 865 866 867 868 869
        tk = vlc_array_item_at_index( p_sys->p_tracks, i );
        /* update also track-id */
        tk->i_id = i + 1;

        i_size += 78 + tk->i_extra;
        i_ci_size += 8 + 2 * strlen( tk->psz_name );
        if( tk->i_cat == AUDIO_ES ) i_ci_size += 4;
        else if( tk->i_cat == VIDEO_ES ) i_ci_size += 6;
870 871

        /* Error correction data field */
872
        if( tk->b_audio_correction ) i_size += 8;
873
    }
874

875
    /* size of the content description object */
876 877 878 879 880 881 882 883 884
    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 );
    }
885

Laurent Aimar's avatar
Laurent Aimar committed
886 887
    i_header_ext_size = 46;

888
    /* size of the metadata object */
889
    for( i = 0; i < vlc_array_count( p_sys->p_tracks ); i++ )
890
    {
891
        const asf_track_t *p_track = vlc_array_item_at_index( p_sys->p_tracks, i );
892 893 894
        if( p_track->i_cat == VIDEO_ES &&
            p_track->fmt.video.i_sar_num != 0 &&
            p_track->fmt.video.i_sar_den != 0 )
895 896 897
        {
            i_cm_size = 26 + 2 * (16 + 2 * sizeof("AspectRatio?"));
        }
Laurent Aimar's avatar
Laurent Aimar committed
898 899 900
        if( p_track->b_extended )
            i_header_ext_size += 88;

901 902
    }

Laurent Aimar's avatar
Laurent Aimar committed
903 904
    i_header_ext_size += i_cm_size;

Gildas Bazin's avatar
Gildas Bazin committed
905
    i_size += i_ci_size + i_cd_size + i_header_ext_size ;
906

907 908
    if( p_sys->b_asf_http )
    {
909
        out = block_Alloc( i_size + 50 + 12 );
910 911 912 913 914
        bo_init( &bo, out->p_buffer, i_size + 50 + 12 );
        asf_chunk_add( &bo, 0x4824, i_size + 50, 0xc00, p_sys->i_seq++ );
    }
    else
    {
915
        out = block_Alloc( i_size + 50 );
916 917
        bo_init( &bo, out->p_buffer, i_size + 50 );
    }
918

919 920 921
    /* header object */
    bo_add_guid ( &bo, &asf_object_header_guid );
    bo_addle_u64( &bo, i_size );
922
    bo_addle_u32( &bo, 2 + vlc_array_count( p_sys->p_tracks ) + 1 +
923
                  (i_cd_size ? 1 : 0) + (i_cm_size ? 1 : 0) );
924 925 926 927 928 929 930 931 932
    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 );
933
    bo_addle_u64( &bo, i_size + 50 + p_sys->i_packet_count *
934
                                p_sys->i_packet_size ); /* file size */
935
    bo_addle_u64( &bo, 0 );                 /* creation date */
936
    bo_addle_u64( &bo, b_broadcast ? 0xffffffffLL : p_sys->i_packet_count );
937 938
    bo_addle_u64( &bo, i_duration * 10 );   /* play duration (100ns) */
    bo_addle_u64( &bo, i_duration * 10 );   /* send duration (100ns) */
939
    bo_addle_u64( &bo, p_sys->i_preroll_time ); /* preroll duration (ms) */
Gildas Bazin's avatar
Gildas Bazin committed
940
    bo_addle_u32( &bo, b_broadcast ? 0x01 : 0x02 /* seekable */ ); /* flags */
941 942
    bo_addle_u32( &bo, p_sys->i_packet_size );  /* packet size min */
    bo_addle_u32( &bo, p_sys->i_packet_size );  /* packet size max */
943 944 945 946
    /* NOTE: According to p6-9 of the ASF specification the bitrate cannot be 0,
     * therefor apply this workaround to make sure it is not 0. If the bitrate is
     * 0 the file will play in WMP11, but not in Sliverlight and WMP12 */
    bo_addle_u32( &bo, p_sys->i_bitrate > 0 ? p_sys->i_bitrate : 1 ); /* maxbitrate */
947

948
    /* header extension */
949 950 951 952 953
    bo_add_guid ( &bo, &asf_object_header_extension_guid );
    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 );
954

Laurent Aimar's avatar
Laurent Aimar committed
955
    /* extended stream properties */
956
    for( i = 0; i < vlc_array_count( p_sys->p_tracks ); i++ )
Laurent Aimar's avatar
Laurent Aimar committed
957
    {
958
        const asf_track_t *p_track = vlc_array_item_at_index( p_sys->p_tracks, i );
Laurent Aimar's avatar
Laurent Aimar committed
959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974
        const es_format_t *p_fmt = &p_track->fmt;

        if( !p_track->b_extended )
            continue;

        uint64_t i_avg_duration = 0;
        if( p_fmt->i_cat == VIDEO_ES &&
            p_fmt->video.i_frame_rate > 0 && p_fmt->video.i_frame_rate_base > 0 )
            i_avg_duration = ( INT64_C(10000000) * p_fmt->video.i_frame_rate_base +
                               p_fmt->video.i_frame_rate/2 ) / p_fmt->video.i_frame_rate;

        bo_add_guid ( &bo, &asf_object_extended_stream_properties_guid );
        bo_addle_u64( &bo, 88 );
        bo_addle_u64( &bo, 0 );
        bo_addle_u64( &bo, 0 );
        bo_addle_u32( &bo, p_fmt->i_bitrate );  /* Bitrate */
975
        bo_addle_u32( &bo, p_sys->i_preroll_time ); /* Buffer size */
Laurent Aimar's avatar
Laurent Aimar committed
976 977 978 979
        bo_addle_u32( &bo, 0 );                 /* Initial buffer fullness */
        bo_addle_u32( &bo, p_fmt->i_bitrate );  /* Alternate Bitrate */
        bo_addle_u32( &bo, 0 );                 /* Alternate Buffer size */
        bo_addle_u32( &bo, 0 );                 /* Alternate Initial buffer fullness */
980
        bo_addle_u32( &bo, 0 );                 /* Maximum object size (0 = unknown) */
Laurent Aimar's avatar
Laurent Aimar committed
981
        bo_addle_u32( &bo, 0x02 );              /* Flags (seekable) */
982
        bo_addle_u16( &bo, p_track->i_id ); /* Stream number */
Laurent Aimar's avatar
Laurent Aimar committed
983 984 985 986 987 988
        bo_addle_u16( &bo, 0 ); /* Stream language index */
        bo_addle_u64( &bo, i_avg_duration );    /* Average time per frame */
        bo_addle_u16( &bo, 0 ); /* Stream name count */
        bo_addle_u16( &bo, 0 ); /* Payload extension system count */
    }

989 990 991
    /* metadata object (part of header extension) */
    if( i_cm_size )
    {
992
        unsigned int i_dst_num, i_dst_den;
993

994
        asf_track_t *tk = NULL;
995 996 997
        for( i = 0; i < vlc_array_count( p_sys->p_tracks ); i++ )
        {
            tk = vlc_array_item_at_index( p_sys->p_tracks, i );
998 999 1000 1001
            if( tk->i_cat == VIDEO_ES &&
                tk->fmt.video.i_sar_num != 0 &&
                tk->fmt.video.i_sar_den != 0 )
                break;
1002
        }
1003
        assert( tk != NULL );
1004

1005 1006 1007
        vlc_ureduce( &i_dst_num, &i_dst_den,
                     tk->fmt.video.i_sar_num,
                     tk->fmt.video.i_sar_den, 0 );
1008

Clément Stenac's avatar
Clément Stenac committed
1009
        msg_Dbg( p_mux, "pixel aspect-ratio: %i/%i", i_dst_num, i_dst_den );
1010 1011 1012 1013 1014 1015

        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 */
1016
        bo_addle_u16( &bo, tk->i_id ); /* stream number (0 for the whole file) */
1017 1018 1019 1020 1021 1022 1023
        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 */
1024
        bo_addle_u16( &bo, tk->i_id ); /* stream number (0 for the whole file) */
1025 1026 1027 1028 1029 1030
        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 */
    }
1031

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

1050
    /* stream properties */
1051
    for( i = 0; i < vlc_array_count( p_sys->p_tracks ); i++ )
1052
    {
1053
        tk = vlc_array_item_at_index( p_sys->p_tracks, i);
1054 1055

        bo_add_guid ( &bo, &asf_object_stream_properties_guid );
1056
        bo_addle_u64( &bo, 78 + tk->i_extra + (tk->b_audio_correction ? 8:0) );
Gildas Bazin's avatar
Gildas Bazin committed
1057

1058 1059 1060
        if( tk->i_cat == AUDIO_ES )
        {
            bo_add_guid( &bo, &asf_object_stream_type_audio );
1061 1062 1063
            if( tk->b_audio_correction )
                bo_add_guid( &bo, &asf_guid_audio_conceal_spread );
            else
1064
                bo_add_guid( &bo, &asf_no_error_correction_guid );
1065 1066 1067 1068
        }
        else if( tk->i_cat == VIDEO_ES )
        {
            bo_add_guid( &bo, &asf_object_stream_type_video );
1069
            bo_add_guid( &bo, &asf_no_error_correction_guid );
1070 1071 1072
        }
        bo_addle_u64( &bo, 0 );         /* time offset */
        bo_addle_u32( &bo, tk->i_extra );
1073 1074
        /* correction data length */
        bo_addle_u32( &bo, tk->b_audio_correction ? 8 : 0 );
1075 1076 1077
        bo_addle_u16( &bo, tk->i_id );  /* stream number */
        bo_addle_u32( &bo, 0 );
        bo_add_mem  ( &bo, tk->p_extra, tk->i_extra );
Gildas Bazin's avatar
Gildas Bazin committed
1078 1079

        /* Error correction data field */
1080
        if( tk->b_audio_correction )
Gildas Bazin's avatar
Gildas Bazin committed
1081 1082 1083 1084 1085 1086 1087
        {
            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 */
        }
1088 1089
    }

1090
    /* Codec Infos */
1091
    bo_add_guid ( &bo, &asf_object_codec_list_guid );
1092
    bo_addle_u64( &bo, i_ci_size );
1093
    bo_add_guid ( &bo, &asf_guid_reserved_2 );
1094 1095
    bo_addle_u32( &bo, vlc_array_count( p_sys->p_tracks ) );
    for( i = 0; i < vlc_array_count( p_sys->p_tracks ); i++ )
1096
    {
1097
        tk = vlc_array_item_at_index( p_sys->p_tracks ,i);
1098

1099 1100 1101 1102
        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 */ );

1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116
        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 );
        }
    }

1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129
    /* 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;
}

/****************************************************************************
 *
 ****************************************************************************/
1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164
static block_t *asf_packet_flush( sout_mux_t *p_mux )
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;
    int i_pad, i_preheader = p_sys->b_asf_http ? 12 : 0;
    block_t *pk;
    bo_t bo;

    if( !p_sys->pk ) return 0;

    i_pad = p_sys->i_packet_size - p_sys->i_pk_used;
    memset( p_sys->pk->p_buffer + p_sys->i_pk_used, 0, i_pad );

    bo_init( &bo, p_sys->pk->p_buffer, 14 + i_preheader );

    if( p_sys->b_asf_http )
        asf_chunk_add( &bo, 0x4424, p_sys->i_packet_size, 0x0, p_sys->i_seq++);

    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 );
    bo_addle_u32( &bo, (p_sys->i_pk_dts - p_sys->i_dts_first) / 1000 +
                  p_sys->i_preroll_time );
    bo_addle_u16( &bo, 0 /* data->i_length */ );
    bo_add_u8( &bo, 0x80 | p_sys->i_pk_frame );

    pk = p_sys->pk;
    p_sys->pk = NULL;

    p_sys->i_packet_count++;

    return pk;
}

1165
static block_t *asf_packet_create( sout_mux_t *p_mux,
1166
                                   asf_track_t *tk, block_t *data )
1167 1168 1169
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;

1170
    int     i_data = data->i_buffer;
1171 1172
    int     i_pos  = 0;
    uint8_t *p_data= data->p_buffer;
1173
    block_t *first = NULL, **last = &first;
Laurent Aimar's avatar