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

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_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 );
Rémi Duraffort's avatar
Rémi Duraffort committed
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
            }
Rémi Duraffort's avatar
Rémi Duraffort committed
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 637 638
    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
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 672
    return VLC_SUCCESS;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

902 903
    }

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

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

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

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

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

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