asf.c 43.5 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
Antoine Cellerier's avatar
Antoine Cellerier committed
22
 * 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

Ilkka Ollakka's avatar
Ilkka Ollakka committed
33 34
#include <assert.h>

35
#include <vlc_common.h>
36
#include <vlc_plugin.h>
Clément Stenac's avatar
Clément Stenac committed
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-"

Clément Stenac's avatar
Clément Stenac committed
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." )
Clément Stenac's avatar
Clément Stenac committed
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." )
Clément Stenac's avatar
Clément Stenac committed
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." )
Clément Stenac's avatar
Clément Stenac committed
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." )
Clément Stenac's avatar
Clément Stenac committed
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")

Clément Stenac's avatar
Clément Stenac committed
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 108 109
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 * );
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 ) );
Rémi Duraffort's avatar
Rémi Duraffort committed
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;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
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)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
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_MPGA:
378
#if 1
379 380
                    tk->psz_name = "MPEG Audio Layer 3";
                    tk->i_tag = WAVE_FORMAT_MPEGLAYER3;
381
                    i_bitspersample = 0;
382
                    i_blockalign = 1;
383
                    i_extra = 12;
384
                    break;
385
#else
386 387
                    tk->psz_name = "MPEG Audio Layer 1/2";
                    tk->i_tag = WAVE_FORMAT_MPEG;
388
                    i_bitspersample = 0;
389 390 391 392
                    i_blockalign = 1;
                    i_extra = 22;
                    break;
#endif
393
                case VLC_CODEC_WMA1:
394
                    tk->psz_name = "Windows Media Audio v1";
395
                    tk->i_tag = WAVE_FORMAT_WMA1;
396
                    tk->b_audio_correction = true;
397
                    break;
398
                case VLC_CODEC_WMA2:
399
                    tk->psz_name= "Windows Media Audio (v2) 7, 8 and 9 Series";
400
                    tk->i_tag = WAVE_FORMAT_WMA2;
401
                    tk->b_audio_correction = true;
402
                    break;
403
                case VLC_CODEC_WMAP:
404 405
                    tk->psz_name = "Windows Media Audio 9 Professional";
                    tk->i_tag = WAVE_FORMAT_WMAP;
406
                    tk->b_audio_correction = true;
407
                    break;
408
                case VLC_CODEC_WMAL:
409 410
                    tk->psz_name = "Windows Media Audio 9 Lossless";
                    tk->i_tag = WAVE_FORMAT_WMAL;
411
                    tk->b_audio_correction = true;
412 413
                    break;
                    /* raw codec */
414
                case VLC_CODEC_U8:
415 416
                    tk->psz_name = "Raw audio 8bits";
                    tk->i_tag = WAVE_FORMAT_PCM;
417
                    i_blockalign= p_input->p_fmt->audio.i_channels;
418 419
                    i_bitspersample = 8;
                    break;
420
                case VLC_CODEC_S16L:
421 422
                    tk->psz_name = "Raw audio 16bits";
                    tk->i_tag = WAVE_FORMAT_PCM;
423
                    i_blockalign= 2 * p_input->p_fmt->audio.i_channels;
424 425
                    i_bitspersample = 16;
                    break;
426
                case VLC_CODEC_S24L:
427 428
                    tk->psz_name = "Raw audio 24bits";
                    tk->i_tag = WAVE_FORMAT_PCM;
429
                    i_blockalign= 3 * p_input->p_fmt->audio.i_channels;
430 431
                    i_bitspersample = 24;
                    break;
432
                case VLC_CODEC_S32L:
433 434
                    tk->psz_name = "Raw audio 32bits";
                    tk->i_tag = WAVE_FORMAT_PCM;
435
                    i_blockalign= 4 * p_input->p_fmt->audio.i_channels;
436 437 438 439 440 441
                    i_bitspersample = 32;
                    break;
                default:
                    return VLC_EGENERIC;
            }

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

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

505
            if( p_input->p_fmt->i_codec == VLC_CODEC_MP4V )
506
            {
507 508
                tk->psz_name = "MPEG-4 Video";
                tk->i_fourcc = VLC_FOURCC( 'M', 'P', '4', 'S' );
509
            }
510
            else if( p_input->p_fmt->i_codec == VLC_CODEC_DIV3 )
511
            {
512 513
                tk->psz_name = "MSMPEG-4 V3 Video";
                tk->i_fourcc = VLC_FOURCC( 'M', 'P', '4', '3' );
514
            }
515
            else if( p_input->p_fmt->i_codec == VLC_CODEC_DIV2 )
516
            {
517 518
                tk->psz_name = "MSMPEG-4 V2 Video";
                tk->i_fourcc = VLC_FOURCC( 'M', 'P', '4', '2' );
519
            }
Rémi Duraffort's avatar
Rémi Duraffort committed
520
            else if( p_input->p_fmt->i_codec == VLC_CODEC_DIV1 )
521
            {
522 523 524
                tk->psz_name = "MSMPEG-4 V1 Video";
                tk->i_fourcc = VLC_FOURCC( 'M', 'P', 'G', '4' );
            }
525
            else if( p_input->p_fmt->i_codec == VLC_CODEC_WMV1 )
526
            {
527
                tk->psz_name = "Windows Media Video 7";
528 529
                tk->i_fourcc = VLC_FOURCC( 'W', 'M', 'V', '1' );
            }
530
            else if( p_input->p_fmt->i_codec == VLC_CODEC_WMV2 )
531
            {
532
                tk->psz_name = "Windows Media Video 8";
533
                tk->i_fourcc = VLC_FOURCC( 'W', 'M', 'V', '2' );
534
            }
535
            else if( p_input->p_fmt->i_codec == VLC_CODEC_WMV3 )
536
            {
537
                tk->psz_name = "Windows Media Video 9";
538
                tk->i_fourcc = VLC_FOURCC( 'W', 'M', 'V', '3' );
539
                tk->b_extended = true;
540
            }
541
            else if( p_input->p_fmt->i_codec == VLC_CODEC_VC1 )
Laurent Aimar's avatar
Laurent Aimar committed
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
            {
                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 );
                    }
                }
            }
558
            else if( p_input->p_fmt->i_codec == VLC_CODEC_H264 )
559 560 561 562
            {
                tk->psz_name = "H.264/MPEG-4 AVC";
                tk->i_fourcc = VLC_FOURCC('h','2','6','4');
            }
563 564
            else
            {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
565
                tk->psz_name = _("Unknown Video");
566
                tk->i_fourcc = p_input->p_fmt->i_original_fourcc ?: p_input->p_fmt->i_codec;
567
            }
Laurent Aimar's avatar
Laurent Aimar committed
568 569 570 571 572 573 574 575 576 577
            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 );
                }
            }

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

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

624 625
    es_format_Copy( &tk->fmt, p_input->p_fmt );

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

629

630 631
    if( p_sys->b_asf_http )
        p_sys->b_write_header = true;
632

633 634 635 636 637 638 639 640
    return VLC_SUCCESS;
}

/*****************************************************************************
 * DelStream:
 *****************************************************************************/
static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
641
    /* if bitrate ain't defined in commandline, reduce it when tracks are deleted
642 643 644
     */
    sout_mux_sys_t   *p_sys = p_mux->p_sys;
    asf_track_t      *tk = p_input->p_sys;
645
    msg_Dbg( p_mux, "removing input" );
646 647 648 649 650 651 652
    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
653
                 p_sys->i_bitrate -= 128000;
654 655 656 657 658 659
        }
        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
660
                 p_sys->i_bitrate -= 512000;
661 662
        }
    }
663

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

/*****************************************************************************
 * Mux:
 *****************************************************************************/
675
static int Mux( sout_mux_t *p_mux )
676 677 678 679 680
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;

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

683
        out->i_flags |= BLOCK_FLAG_HEADER;
684 685
        sout_AccessOutWrite( p_mux->p_access, out );

686
        p_sys->b_write_header = false;
687 688 689 690 691 692 693
    }

    for( ;; )
    {
        sout_input_t  *p_input;
        asf_track_t   *tk;
        mtime_t       i_dts;
694 695
        block_t *data;
        block_t *pk;
696

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

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

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

716
        data = block_FifoGet( p_input->p_fifo );
717

Laurent Aimar's avatar
Laurent Aimar committed
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
        /* 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;
            }
        }

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

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

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

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

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

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

852 853
    msg_Dbg( p_mux, "Asf muxer creating header" );

854
    if( p_sys->i_dts_first > VLC_TS_INVALID )
855 856
    {
        i_duration = p_sys->i_dts_last - p_sys->i_dts_first;
857
        if( i_duration < 0 ) i_duration = 0;
858 859
    }

860
    /* calculate header size */
Gildas Bazin's avatar
Gildas Bazin committed
861
    i_size = 30 + 104;
862
    i_ci_size = 44;
863
    for( i = 0; i < vlc_array_count( p_sys->p_tracks ); i++ )
864
    {
865 866 867 868 869 870 871 872
        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;
873 874

        /* Error correction data field */
875
        if( tk->b_audio_correction ) i_size += 8;
876
    }
877

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

Laurent Aimar's avatar
Laurent Aimar committed
889 890
    i_header_ext_size = 46;

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

904 905
    }

Laurent Aimar's avatar
Laurent Aimar committed
906 907
    i_header_ext_size += i_cm_size;

Gildas Bazin's avatar
Gildas Bazin committed
908
    i_size += i_ci_size + i_cd_size + i_header_ext_size ;
909

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

922 923 924
    /* header object */
    bo_add_guid ( &bo, &asf_object_header_guid );
    bo_addle_u64( &bo, i_size );
925
    bo_addle_u32( &bo, 2 + vlc_array_count( p_sys->p_tracks ) + 1 +
926
                  (i_cd_size ? 1 : 0) + (i_cm_size ? 1 : 0) );
927 928 929 930 931 932 933 934 935
    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 );
936
    bo_addle_u64( &bo, i_size + 50 + p_sys->i_packet_count *
937
                                p_sys->i_packet_size ); /* file size */
938
    bo_addle_u64( &bo, 0 );                 /* creation date */
939
    bo_addle_u64( &bo, b_broadcast ? 0xffffffffLL : p_sys->i_packet_count );
940 941
    bo_addle_u64( &bo, i_duration * 10 );   /* play duration (100ns) */
    bo_addle_u64( &bo, i_duration * 10 );   /* send duration (100ns) */
942
    bo_addle_u64( &bo, p_sys->i_preroll_time ); /* preroll duration (ms) */
Gildas Bazin's avatar
Gildas Bazin committed
943
    bo_addle_u32( &bo, b_broadcast ? 0x01 : 0x02 /* seekable */ ); /* flags */
944 945
    bo_addle_u32( &bo, p_sys->i_packet_size );  /* packet size min */
    bo_addle_u32( &bo, p_sys->i_packet_size );  /* packet size max */
946 947 948 949
    /* 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 */
950

951
    /* header extension */
952 953 954 955 956
    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 );
957

Laurent Aimar's avatar
Laurent Aimar committed
958
    /* extended stream properties */
959
    for( i = 0; i < vlc_array_count( p_sys->p_tracks ); i++ )
Laurent Aimar's avatar
Laurent Aimar committed
960
    {
961
        const asf_track_t *p_track = vlc_array_item_at_index( p_sys->p_tracks, i );
Laurent Aimar's avatar
Laurent Aimar committed
962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982
        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 */
        bo_addle_u32( &bo, 0 );                 /* Buffer size */
        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 */
Benjamin Drung's avatar
Benjamin Drung committed
983
        bo_addle_u32( &bo, 0 );                 /* Maximum object size (0 = unknown) */
Laurent Aimar's avatar
Laurent Aimar committed
984
        bo_addle_u32( &bo, 0x02 );              /* Flags (seekable) */
Ilkka Ollakka's avatar
Ilkka Ollakka committed
985
        bo_addle_u16( &bo, p_track->i_id ); /* Stream number */
Laurent Aimar's avatar
Laurent Aimar committed
986 987 988 989 990 991
        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 */
    }

992 993 994
    /* metadata object (part of header extension) */
    if( i_cm_size )
    {
995
        unsigned int i_dst_num, i_dst_den;
996