asf.c 46.3 KB
Newer Older
1
/*****************************************************************************
2
 * asf.c: asf muxer module for vlc
3
 *****************************************************************************
4
 * Copyright (C) 2003-2004, 2006 the VideoLAN team
5
 * $Id$
6 7
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8
 *          Gildas Bazin <gbazin@videolan.org>
9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
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

42
typedef GUID guid_t;
43

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

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

53 54
#define SOUT_CFG_PREFIX "sout-asf-"

Clément Stenac's avatar
Clément Stenac committed
55
#define TITLE_TEXT N_("Title")
Clément Stenac's avatar
Clément Stenac committed
56
#define TITLE_LONGTEXT N_("Title to put in ASF comments." )
Clément Stenac's avatar
Clément Stenac committed
57
#define AUTHOR_TEXT N_("Author")
Clément Stenac's avatar
Clément Stenac committed
58
#define AUTHOR_LONGTEXT N_("Author to put in ASF comments." )
Clément Stenac's avatar
Clément Stenac committed
59
#define COPYRIGHT_TEXT N_("Copyright")
Clément Stenac's avatar
Clément Stenac committed
60
#define COPYRIGHT_LONGTEXT N_("Copyright string to put in ASF comments." )
Clément Stenac's avatar
Clément Stenac committed
61
#define COMMENT_TEXT N_("Comment")
Clément Stenac's avatar
Clément Stenac committed
62
#define COMMENT_LONGTEXT N_("Comment to put in ASF comments." )
Clément Stenac's avatar
Clément Stenac committed
63
#define RATING_TEXT N_("Rating")
Clément Stenac's avatar
Clément Stenac committed
64
#define RATING_LONGTEXT N_("\"Rating\" to put in ASF comments." )
65
#define PACKETSIZE_TEXT N_("Packet Size")
Clément Stenac's avatar
Clément Stenac committed
66
#define PACKETSIZE_LONGTEXT N_("ASF packet size -- default is 4096 bytes")
67 68 69
#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
70

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

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

Clément Stenac's avatar
Clément Stenac committed
82
    add_string( SOUT_CFG_PREFIX "title", "", NULL, TITLE_TEXT, TITLE_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
83
                                 true )
Clément Stenac's avatar
Clément Stenac committed
84
    add_string( SOUT_CFG_PREFIX "author",   "", NULL, AUTHOR_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
85
                                 AUTHOR_LONGTEXT, true )
Clément Stenac's avatar
Clément Stenac committed
86
    add_string( SOUT_CFG_PREFIX "copyright","", NULL, COPYRIGHT_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
87
                                 COPYRIGHT_LONGTEXT, true )
Clément Stenac's avatar
Clément Stenac committed
88
    add_string( SOUT_CFG_PREFIX "comment",  "", NULL, COMMENT_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
89
                                 COMMENT_LONGTEXT, true )
Clément Stenac's avatar
Clément Stenac committed
90
    add_string( SOUT_CFG_PREFIX "rating",  "", NULL, 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, NULL, 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, NULL, 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 165
};

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

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

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

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

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

197
    msg_Dbg( p_mux, "asf muxer opened" );
198
    config_ChainParse( p_mux, SOUT_CFG_PREFIX, ppsz_sout_options, p_mux->p_cfg );
199

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

224
    p_sys->b_write_header = true;
225
    p_sys->i_packet_size = config_GetInt( p_mux, "sout-asf-packet-size" );
226
    p_sys->i_bitrate_override = config_GetInt( p_mux, "sout-asf-bitrate-override" );
227
    msg_Dbg( p_mux, "Packet size %d", p_sys->i_packet_size);
228
    if (p_sys->i_bitrate_override)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
229
        msg_Dbg( p_mux, "Bitrate override %"PRId64, p_sys->i_bitrate_override);
230
    p_sys->i_packet_count= 0;
231 232

    /* Generate a random fid */
233
    srand( mdate() & 0xffffffff );
234 235 236
    p_sys->fid.Data1 = 0xbabac001;
    p_sys->fid.Data2 = ( (uint64_t)rand() << 16 ) / RAND_MAX;
    p_sys->fid.Data3 = ( (uint64_t)rand() << 16 ) / RAND_MAX;
237 238
    for( i = 0; i < 8; i++ )
    {
239
        p_sys->fid.Data4[i] = ( (uint64_t)rand() << 8 ) / RAND_MAX;
240
    }
241 242

    /* Meta data */
243 244 245 246 247
    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" );
248

249 250
    msg_Dbg( p_mux, "meta data: title='%s', author='%s', copyright='%s', "
             "comment='%s', rating='%s'",
251 252
             p_sys->psz_title, p_sys->psz_author, p_sys->psz_copyright,
             p_sys->psz_comment, p_sys->psz_rating );
253

254 255 256 257 258 259 260 261 262 263
    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;
264
    block_t  *out;
265 266 267 268
    int i;

    msg_Dbg( p_mux, "Asf muxer closed" );

269 270 271 272 273 274
    /* Flush last packet if any */
    if( (out = asf_packet_flush( p_mux ) ) )
    {
        sout_AccessOutWrite( p_mux->p_access, out );
    }

275 276 277 278 279
    if( ( out = asf_stream_end_create( p_mux ) ) )
    {
        sout_AccessOutWrite( p_mux->p_access, out );
    }

280
    /* rewrite header */
281
    if( sout_AccessOutSeek( p_mux->p_access, 0 ) == VLC_SUCCESS )
282
    {
283
        out = asf_header_create( p_mux, false );
284
        sout_AccessOutWrite( p_mux->p_access, out );
285 286
    }

287 288

    for( i = 0; i < vlc_array_count( p_sys->p_tracks ); i++ )
289
    {
290 291 292
        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 );
293
    }
294

295 296 297 298
    vlc_array_clear( p_sys->p_tracks );

    vlc_array_destroy( p_sys->p_tracks );

299 300 301 302 303
    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 );
304 305 306 307 308 309
    free( p_sys );
}

/*****************************************************************************
 * Capability:
 *****************************************************************************/
310
static int Control( sout_mux_t *p_mux, int i_query, va_list args )
311
{
312
    sout_mux_sys_t *p_sys = p_mux->p_sys;
313
    bool *pb_bool;
314 315
    char **ppsz;

316 317
    switch( i_query )
    {
318
       case MUX_CAN_ADD_STREAM_WHILE_MUXING:
319 320
           pb_bool = (bool*)va_arg( args, bool * );
           if( p_sys->b_asf_http ) *pb_bool = true;
321
           else *pb_bool = false;
322 323 324
           return VLC_SUCCESS;

       case MUX_GET_ADD_STREAM_WAIT:
325 326
           pb_bool = (bool*)va_arg( args, bool * );
           *pb_bool = true;
327 328 329 330 331 332 333 334 335 336
           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;

337
        default:
338
            return VLC_EGENERIC;
339
    }
340 341 342 343 344 345 346 347 348 349 350 351
}

/*****************************************************************************
 * 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" );
352
    if( vlc_array_count( p_sys->p_tracks ) >= MAX_ASF_TRACKS )
353
    {
354
        msg_Dbg( p_mux, "cannot add this track (too much tracks)" );
355 356 357
        return VLC_EGENERIC;
    }

358 359
    tk = p_input->p_sys = malloc( sizeof( asf_track_t ) );
    memset( tk, 0, sizeof( *tk ) );
360 361
    tk->i_cat = p_input->p_fmt->i_cat;
    tk->i_sequence = 0;
362
    tk->b_audio_correction = 0;
Laurent Aimar's avatar
Laurent Aimar committed
363
    tk->b_extended = false;
364 365 366 367 368

    switch( tk->i_cat )
    {
        case AUDIO_ES:
        {
369
            int i_blockalign = p_input->p_fmt->audio.i_blockalign;
Gildas Bazin's avatar
Gildas Bazin committed
370
            int i_bitspersample = p_input->p_fmt->audio.i_bitspersample;
371
            int i_extra = 0;
372

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

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

495 496 497 498 499 500
            if( p_input->p_fmt->i_bitrate > 24000 )
            {
                p_sys->i_bitrate += p_input->p_fmt->i_bitrate;
            }
            else
            {
501
                p_sys->i_bitrate += 128000;
502
            }
503 504
            if (p_sys->i_bitrate_override)
                p_sys->i_bitrate = p_sys->i_bitrate_override;
505 506 507 508
            break;
        }
        case VIDEO_ES:
        {
Laurent Aimar's avatar
Laurent Aimar committed
509 510 511 512
            const es_format_t *p_fmt = p_input->p_fmt;
            uint8_t *p_codec_extra = NULL;
            int     i_codec_extra = 0;

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

            tk->i_extra = 11 + sizeof( BITMAPINFOHEADER ) + i_codec_extra;
            tk->p_extra = malloc( tk->i_extra );
            if( !tk->p_extra )
Rémi Duraffort's avatar
Rémi Duraffort committed
588 589
            {
                free( p_codec_extra );
Laurent Aimar's avatar
Laurent Aimar committed
590
                return VLC_ENOMEM;
Rémi Duraffort's avatar
Rémi Duraffort committed
591
            }
Laurent Aimar's avatar
Laurent Aimar committed
592 593 594 595 596 597 598 599 600 601
            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 */
            bo_addle_u16( &bo, sizeof( BITMAPINFOHEADER ) + i_codec_extra );
            bo_addle_u32( &bo, sizeof( BITMAPINFOHEADER ) + i_codec_extra );
            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 );
602
            bo_add_mem( &bo, (uint8_t*)&tk->i_fourcc, 4 );
603 604 605 606 607
            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
608
            if( i_codec_extra > 0 )
Rémi Duraffort's avatar
Rémi Duraffort committed
609
            {
Laurent Aimar's avatar
Laurent Aimar committed
610
                bo_add_mem( &bo, p_codec_extra, i_codec_extra );
Rémi Duraffort's avatar
Rémi Duraffort committed
611 612
                free( p_codec_extra );
            }
613

614 615 616 617 618 619
            if( p_input->p_fmt->i_bitrate > 50000 )
            {
                p_sys->i_bitrate += p_input->p_fmt->i_bitrate;
            }
            else
            {
620
                p_sys->i_bitrate += 512000;
621
            }
Laurent Aimar's avatar
Laurent Aimar committed
622
            if( p_sys->i_bitrate_override )
623
                p_sys->i_bitrate = p_sys->i_bitrate_override;
624 625 626 627 628 629 630
            break;
        }
        default:
            msg_Err(p_mux, "unhandled track type" );
            return VLC_EGENERIC;
    }

631 632
    es_format_Copy( &tk->fmt, p_input->p_fmt );

633 634 635
    vlc_array_append( p_sys->p_tracks, (void *)tk);
    tk->i_id = vlc_array_index_of_item( p_sys->p_tracks, tk ) + 1;

636 637 638

    p_sys->b_write_header = true;

639 640 641 642 643 644 645 646
    return VLC_SUCCESS;
}

/*****************************************************************************
 * DelStream:
 *****************************************************************************/
static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
{
647 648 649 650
    /* if bitrate ain't defined in commanline, reduce it when tracks are deleted
     */
    sout_mux_sys_t   *p_sys = p_mux->p_sys;
    asf_track_t      *tk = p_input->p_sys;
651
    msg_Dbg( p_mux, "removing input" );
652 653 654 655 656 657 658
    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
659
                 p_sys->i_bitrate -= 128000;
660 661 662 663 664 665
        }
        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
666
                 p_sys->i_bitrate -= 512000;
667 668
        }
    }
669 670 671

    vlc_array_remove( p_sys->p_tracks, vlc_array_index_of_item( p_sys->p_tracks, (void *)tk ) );

672 673

    p_sys->b_write_header = true;
674 675 676 677 678 679
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Mux:
 *****************************************************************************/
680
static int Mux( sout_mux_t *p_mux )
681 682 683 684 685
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;

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

688
        out->i_flags |= BLOCK_FLAG_HEADER;
689 690
        sout_AccessOutWrite( p_mux->p_access, out );

691
        p_sys->b_write_header = false;
692 693 694 695 696 697 698 699
    }

    for( ;; )
    {
        sout_input_t  *p_input;
        asf_track_t   *tk;
        int           i_stream;
        mtime_t       i_dts;
700 701
        block_t *data;
        block_t *pk;
702 703 704 705 706 707 708 709 710 711 712

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

        if( p_sys->i_dts_first < 0 )
        {
            p_sys->i_dts_first = i_dts;
        }
713 714 715 716
        if( p_sys->i_dts_last < i_dts )
        {
            p_sys->i_dts_last = i_dts;
        }
717 718 719 720

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

721
        data = block_FifoGet( p_input->p_fifo );
722

Laurent Aimar's avatar
Laurent Aimar committed
723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740
        /* 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;
            }
        }

741 742 743 744 745 746 747 748 749
        if( ( pk = asf_packet_create( p_mux, tk, data ) ) )
        {
            sout_AccessOutWrite( p_mux->p_access, pk );
        }
    }

    return VLC_SUCCESS;
}

750
static int MuxGetStream( sout_mux_t *p_mux, int *pi_stream, mtime_t *pi_dts )
751 752 753 754 755 756 757 758
{
    mtime_t i_dts;
    int     i_stream;
    int     i;

    for( i = 0, i_dts = 0, i_stream = -1; i < p_mux->i_nb_inputs; i++ )
    {
        sout_input_t  *p_input = p_mux->pp_inputs[i];
759
        block_t *p_data;
760

761
        if( block_FifoCount( p_input->p_fifo ) <= 0 )
762 763 764 765 766 767 768 769 770 771 772
        {
            if( p_input->p_fmt->i_cat == AUDIO_ES ||
                p_input->p_fmt->i_cat == VIDEO_ES )
            {
                /* We need that audio+video fifo contain at least 1 packet */
                return VLC_EGENERIC;
            }
            /* SPU */
            continue;
        }

773
        p_data = block_FifoShow( p_input->p_fifo );
774
        if( i_stream == -1 || p_data->i_dts < i_dts )
775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829
        {
            i_stream = i;
            i_dts    = p_data->i_dts;
        }
    }

    *pi_stream = i_stream;
    *pi_dts = i_dts;

    return VLC_SUCCESS;
}

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

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

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

    if( i_copy > 0 )
    {
830
        memcpy( &p_bo->p_buffer[p_bo->i_buffer], p_mem, i_copy );
831 832 833 834
    }
    p_bo->i_buffer += i_size;
}

Laurent Aimar's avatar
Laurent Aimar committed
835
static void bo_addle_str16( bo_t *bo, const char *str )
836 837 838 839
{
    bo_addle_u16( bo, strlen( str ) + 1 );
    for( ;; )
    {
840
        uint16_t c = (uint8_t)*str++;
841
        bo_addle_u16( bo, c );
842
        if( c == '\0' ) break;
843 844 845
    }
}

Laurent Aimar's avatar
Laurent Aimar committed
846
static void bo_addle_str16_nosize( bo_t *bo, const char *str )
847 848 849
{
    for( ;; )
    {
850
        uint16_t c = (uint8_t)*str++;
851
        bo_addle_u16( bo, c );
852
        if( c == '\0' ) break;
853 854 855
    }
}

856
/****************************************************************************
857
 * GUID definitions
858 859 860 861
 ****************************************************************************/
static void bo_add_guid( bo_t *p_bo, const guid_t *id )
{
    int i;
862 863 864
    bo_addle_u32( p_bo, id->Data1 );
    bo_addle_u16( p_bo, id->Data2 );
    bo_addle_u16( p_bo, id->Data3 );
865 866
    for( i = 0; i < 8; i++ )
    {
867
        bo_add_u8( p_bo, id->Data4[i] );
868 869 870 871
    }
}

static const guid_t asf_object_header_guid =
872
{0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
873
static const guid_t asf_object_data_guid =
874
{0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
875
static const guid_t asf_object_file_properties_guid =
876
{0x8cabdca1, 0xa947, 0x11cf, {0x8e, 0xe4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
877
static const guid_t asf_object_stream_properties_guid =
878
{0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
879
static const guid_t asf_object_header_extension_guid =
880
{0x5FBF03B5, 0xA92E, 0x11CF, {0x8E, 0xE3, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
881
static const guid_t asf_object_stream_type_audio =
882
{0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
883
static const guid_t asf_object_stream_type_video =
884
{0xbc19efc0, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
885
static const guid_t asf_guid_audio_conceal_none =
886
{0x20FB5700, 0x5B55, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
Gildas Bazin's avatar
Gildas Bazin committed
887 888
static const guid_t asf_guid_audio_conceal_spread =
{0xBFC3CD50, 0x618F, 0x11CF, {0x8B, 0xB2, 0x00, 0xAA, 0x00, 0xB4, 0xE2, 0x20}};
889
static const guid_t asf_guid_video_conceal_none =
890
{0x20FB5700, 0x5B55, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
891
static const guid_t asf_guid_reserved_1 =
892
{0xABD3D211, 0xA9BA, 0x11cf, {0x8E, 0xE6, 0x00, 0xC0, 0x0C ,0x20, 0x53, 0x65}};
893
static const guid_t asf_object_codec_list_guid =
894
{0x86D15240, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}};
895
static const guid_t asf_object_codec_list_reserved_guid =
896
{0x86D15241, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}};
897
static const guid_t asf_object_content_description_guid =
898
{0x75B22633, 0x668E, 0x11CF, {0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c}};
899 900
static const guid_t asf_object_index_guid =
{0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}};
901 902
static const guid_t asf_object_metadata_guid =
{0xC5F8CBEA, 0x5BAF, 0x4877, {0x84, 0x67, 0xAA, 0x8C, 0x44, 0xFA, 0x4C, 0xCA}};
Laurent Aimar's avatar
Laurent Aimar committed
903 904
static const guid_t asf_object_extended_stream_properties_guid =
{0x14E6A5CB, 0xC672, 0x4332, {0x83, 0x99, 0xA9, 0x69, 0x52, 0x06, 0x5B, 0x5A}};
905

906 907 908
/****************************************************************************
 * Misc
 ****************************************************************************/
909 910 911 912 913 914 915 916 917 918
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 );
}

919
static block_t *asf_header_create( sout_mux_t *p_mux, bool b_broadcast )
920 921 922
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;
    asf_track_t    *tk;
923
    mtime_t i_duration = 0;
Gildas Bazin's avatar
Gildas Bazin committed
924
    int i_size, i_header_ext_size, i;
925
    int i_ci_size, i_cm_size = 0, i_cd_size = 0;
926
    block_t *out;
927
    bo_t bo;
Ilkka Ollakka's avatar
Ilkka Ollakka committed
928
    tk=NULL;
929

930 931
    msg_Dbg( p_mux, "Asf muxer creating header" );

932 933 934
    if( p_sys->i_dts_first > 0 )
    {
        i_duration = p_sys->i_dts_last - p_sys->i_dts_first;
935
        if( i_duration < 0 ) i_duration = 0;
936 937
    }

938
    /* calculate header size */
Gildas Bazin's avatar
Gildas Bazin committed
939
    i_size = 30 + 104;
940
    i_ci_size = 44;
941
    for( i = 0; i < vlc_array_count( p_sys->p_tracks ); i++ )
942
    {
943 944 945 946 947 948 949 950
        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;
951 952

        /* Error correction data field */
953
        if( tk->b_audio_correction ) i_size += 8;
954
    }
955

956
    /* size of the content description object */
957 958 959 960 961 962 963 964 965
    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 );
    }
966

Laurent Aimar's avatar
Laurent Aimar committed
967 968
    i_header_ext_size = 46;

969
    /* size of the metadata object */
970
    for( i = 0; i < vlc_array_count( p_sys->p_tracks ); i++ )
971
    {
972
        const asf_track_t *p_track = vlc_array_item_at_index( p_sys->p_tracks, i );
973
        if( p_track->i_cat == VIDEO_ES && p_track->fmt.video.i_aspect != 0 )
974 975 976 977
        {
            i_cm_size = 26 + 2 * (16 + 2 * sizeof("AspectRatio?"));
            break;
        }
Laurent Aimar's avatar
Laurent Aimar committed
978 979 980
        if( p_track->b_extended )
            i_header_ext_size += 88;

981 982
    }

Laurent Aimar's avatar
Laurent Aimar committed
983 984
    i_header_ext_size += i_cm_size;

Gildas Bazin's avatar
Gildas Bazin committed
985
    i_size += i_ci_size + i_cd_size + i_header_ext_size ;
986

987 988
    if( p_sys->b_asf_http )
    {
989
        out = block_New( p_mux, i_size + 50 + 12 );
990 991 992 993 994
        bo_init( &bo, out->p_buffer, i_size + 50 + 12 );
        asf_chunk_add( &bo, 0x4824, i_size + 50, 0xc00, p_sys->i_seq++ );
    }
    else
    {
995
        out = block_New( p_mux, i_size + 50 );
996 997
        bo_init( &bo, out->p_buffer, i_size + 50 );
    }
998

999 1000 1001
    /* header object */
    bo_add_guid ( &bo, &asf_object_header_guid );
    bo_addle_u64( &bo, i_size );
1002
    bo_addle_u32( &bo, 2 + vlc_array_count( p_sys->p_tracks ) + 1 +
1003
                  (i_cd_size ? 1 : 0) + (i_cm_size ? 1 : 0) );
1004 1005 1006 1007 1008 1009 1010 1011 1012
    bo_add_u8   ( &bo, 1 );
    bo_add_u8   ( &bo, 2 );

    /* sub object */