mp4.c 60.1 KB
Newer Older
1
/*****************************************************************************
2
 * mp4.c: mp4/mov muxer
3
 *****************************************************************************
4
 * Copyright (C) 2001, 2002, 2003, 2006 the VideoLAN team
5
 * $Id$
6 7
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8
 *          Gildas Bazin <gbazin at videolan dot 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

33
#include <vlc_common.h>
34
#include <vlc_plugin.h>
35 36
#include <vlc_sout.h>
#include <vlc_block.h>
37

38 39
#include <time.h>

40
#include <vlc_iso_lang.h>
41
#include <vlc_meta.h>
42

43 44 45
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
46
#define FASTSTART_TEXT N_("Create \"Fast Start\" files")
47
#define FASTSTART_LONGTEXT N_( \
Clément Stenac's avatar
Clément Stenac committed
48 49 50 51
    "Create \"Fast Start\" files. " \
    "\"Fast Start\" files are optimized for downloads and allow the user " \
    "to start previewing the file while it is downloading.")

52 53
static int  Open   ( vlc_object_t * );
static void Close  ( vlc_object_t * );
54

55 56
#define SOUT_CFG_PREFIX "sout-mp4-"

57 58 59 60 61
vlc_module_begin ()
    set_description( N_("MP4/MOV muxer") )
    set_category( CAT_SOUT )
    set_subcategory( SUBCAT_SOUT_MUX )
    set_shortname( "MP4" )
62

63
    add_bool( SOUT_CFG_PREFIX "faststart", true,
Clément Stenac's avatar
Clément Stenac committed
64
              FASTSTART_TEXT, FASTSTART_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
65
              true )
66
    set_capability( "sout mux", 5 )
67
    add_shortcut( "mp4", "mov", "3gp" )
68 69
    set_callbacks( Open, Close )
vlc_module_end ()
70

71 72 73
/*****************************************************************************
 * Exported prototypes
 *****************************************************************************/
74
static const char *const ppsz_sout_options[] = {
75 76 77
    "faststart", NULL
};

78
static int Control( sout_mux_t *, int, va_list );
79 80 81 82
static int AddStream( sout_mux_t *, sout_input_t * );
static int DelStream( sout_mux_t *, sout_input_t * );
static int Mux      ( sout_mux_t * );

83 84 85
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
86 87 88 89 90
typedef struct
{
    uint64_t i_pos;
    int      i_size;

91
    mtime_t  i_pts_dts;
92
    mtime_t  i_length;
93
    unsigned int i_flags;
94 95 96 97 98

} mp4_entry_t;

typedef struct
{
99
    es_format_t   fmt;
100 101 102 103 104 105
    int           i_track_id;

    /* index */
    unsigned int i_entry_count;
    unsigned int i_entry_max;
    mp4_entry_t  *entry;
106
    int64_t      i_length_neg;
107 108

    /* stats */
109 110
    int64_t      i_dts_start;
    int64_t      i_duration;
111 112 113

    /* for later stco fix-up (fast start files) */
    uint64_t i_stco_pos;
114
    bool b_stco64;
115

116 117 118
    /* for spu */
    int64_t i_last_dts;

119 120 121 122
} mp4_stream_t;

struct sout_mux_sys_t
{
123 124 125 126
    bool b_mov;
    bool b_3gp;
    bool b_64_ext;
    bool b_fast_start;
127

128 129 130
    uint64_t i_mdat_pos;
    uint64_t i_pos;

131
    int64_t  i_dts_start;
132

133 134 135 136
    int          i_nb_streams;
    mp4_stream_t **pp_streams;
};

137
typedef struct bo_t
138
{
139
    bool b_grow;
140 141 142 143 144

    int        i_buffer_size;
    int        i_buffer;
    uint8_t    *p_buffer;

145
} bo_t;
146

147
static void bo_init     ( bo_t *, int , uint8_t *, bool  );
148 149 150 151 152
static void bo_add_8    ( bo_t *, uint8_t );
static void bo_add_16be ( bo_t *, uint16_t );
static void bo_add_24be ( bo_t *, uint32_t );
static void bo_add_32be ( bo_t *, uint32_t );
static void bo_add_64be ( bo_t *, uint64_t );
153
static void bo_add_fourcc(bo_t *, const char * );
154 155
static void bo_add_bo   ( bo_t *, bo_t * );
static void bo_add_mem  ( bo_t *, int , uint8_t * );
156
static void bo_add_descr( bo_t *, uint8_t , uint32_t );
157 158 159

static void bo_fix_32be ( bo_t *, int , uint32_t );

160 161
static bo_t *box_new     ( const char *fcc );
static bo_t *box_full_new( const char *fcc, uint8_t v, uint32_t f );
162 163 164
static void  box_fix     ( bo_t *box );
static void  box_free    ( bo_t *box );
static void  box_gather  ( bo_t *box, bo_t *box2 );
165

166 167
static void box_send( sout_mux_t *p_mux,  bo_t *box );

168
static block_t *bo_to_sout( sout_instance_t *p_sout,  bo_t *box );
169

170 171
static bo_t *GetMoovBox( sout_mux_t *p_mux );

172 173
static block_t *ConvertSUBT( block_t *);
static block_t *ConvertAVC1( block_t * );
174

175 176 177 178 179 180 181 182 183
/*****************************************************************************
 * 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;
    bo_t            *box;

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

187
    p_mux->pf_control   = Control;
188 189 190 191
    p_mux->pf_addstream = AddStream;
    p_mux->pf_delstream = DelStream;
    p_mux->pf_mux       = Mux;
    p_mux->p_sys        = p_sys = malloc( sizeof( sout_mux_sys_t ) );
192 193
    if( !p_sys )
        return VLC_ENOMEM;
194 195 196
    p_sys->i_pos        = 0;
    p_sys->i_nb_streams = 0;
    p_sys->pp_streams   = NULL;
197 198
    p_sys->i_mdat_pos   = 0;
    p_sys->b_mov        = p_mux->psz_mux && !strcmp( p_mux->psz_mux, "mov" );
199
    p_sys->b_3gp        = p_mux->psz_mux && !strcmp( p_mux->psz_mux, "3gp" );
200
    p_sys->i_dts_start  = 0;
201 202


203 204 205 206
    if( !p_sys->b_mov )
    {
        /* Now add ftyp header */
        box = box_new( "ftyp" );
207
        if( p_sys->b_3gp ) bo_add_fourcc( box, "3gp6" );
208
        else bo_add_fourcc( box, "isom" );
209
        bo_add_32be  ( box, 0 );
210 211
        if( p_sys->b_3gp ) bo_add_fourcc( box, "3gp4" );
        else bo_add_fourcc( box, "mp41" );
212 213
        bo_add_fourcc( box, "avc1" );
        bo_add_fourcc( box, "qt  " );
214
        box_fix( box );
215

216 217
        p_sys->i_pos += box->i_buffer;
        p_sys->i_mdat_pos = p_sys->i_pos;
218

219 220
        box_send( p_mux, box );
    }
221

222 223
    /* FIXME FIXME
     * Quicktime actually doesn't like the 64 bits extensions !!! */
224
    p_sys->b_64_ext = false;
225

226 227
    /* Now add mdat header */
    box = box_new( "mdat" );
228
    bo_add_64be  ( box, 0 ); // enough to store an extended size
229 230 231 232 233 234 235 236

    p_sys->i_pos += box->i_buffer;

    box_send( p_mux, box );

    return VLC_SUCCESS;
}

237 238 239 240
/*****************************************************************************
 * Close:
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
241
{
242 243
    sout_mux_t      *p_mux = (sout_mux_t*)p_this;
    sout_mux_sys_t  *p_sys = p_mux->p_sys;
244
    block_t   *p_hdr;
245 246
    bo_t            bo, *moov;
    vlc_value_t     val;
247

248 249 250 251
    int             i_trak;
    uint64_t        i_moov_pos;

    msg_Dbg( p_mux, "Close" );
252

253
    /* Update mdat size */
254
    bo_init( &bo, 0, NULL, true );
255
    if( p_sys->i_pos - p_sys->i_mdat_pos >= (((uint64_t)1)<<32) )
256
    {
257 258 259 260
        /* Extended size */
        bo_add_32be  ( &bo, 1 );
        bo_add_fourcc( &bo, "mdat" );
        bo_add_64be  ( &bo, p_sys->i_pos - p_sys->i_mdat_pos );
261 262 263
    }
    else
    {
264 265 266 267
        bo_add_32be  ( &bo, 8 );
        bo_add_fourcc( &bo, "wide" );
        bo_add_32be  ( &bo, p_sys->i_pos - p_sys->i_mdat_pos - 8 );
        bo_add_fourcc( &bo, "mdat" );
268
    }
269 270
    p_hdr = bo_to_sout( p_mux->p_sout, &bo );
    free( bo.p_buffer );
271

272 273
    sout_AccessOutSeek( p_mux->p_access, p_sys->i_mdat_pos );
    sout_AccessOutWrite( p_mux->p_access, p_hdr );
274

275 276 277
    /* Create MOOV header */
    i_moov_pos = p_sys->i_pos;
    moov = GetMoovBox( p_mux );
278

279
    /* Check we need to create "fast start" files */
280
    var_Get( p_this, SOUT_CFG_PREFIX "faststart", &val );
281 282
    p_sys->b_fast_start = val.b_bool;
    while( p_sys->b_fast_start )
283
    {
284 285
        /* Move data to the end of the file so we can fit the moov header
         * at the start */
286
        block_t *p_buf;
287 288
        int64_t i_chunk, i_size = p_sys->i_pos - p_sys->i_mdat_pos;
        int i_moov_size = moov->i_buffer;
289

290
        while( i_size > 0 )
291
        {
292
            i_chunk = __MIN( 32768, i_size );
293
            p_buf = block_New( p_mux, i_chunk );
294 295 296 297
            sout_AccessOutSeek( p_mux->p_access,
                                p_sys->i_mdat_pos + i_size - i_chunk );
            if( sout_AccessOutRead( p_mux->p_access, p_buf ) < i_chunk )
            {
298
                msg_Warn( p_this, "read() not supported by access output, "
299
                          "won't create a fast start file" );
300
                p_sys->b_fast_start = false;
301
                block_Release( p_buf );
302 303 304 305 306 307
                break;
            }
            sout_AccessOutSeek( p_mux->p_access, p_sys->i_mdat_pos + i_size +
                                i_moov_size - i_chunk );
            sout_AccessOutWrite( p_mux->p_access, p_buf );
            i_size -= i_chunk;
308 309
        }

310
        if( !p_sys->b_fast_start ) break;
311

312 313 314 315 316 317
        /* Fix-up samples to chunks table in MOOV header */
        for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ )
        {
            mp4_stream_t *p_stream = p_sys->pp_streams[i_trak];
            unsigned int i;
            int i_chunk;
318

319 320 321 322 323 324 325
            moov->i_buffer = p_stream->i_stco_pos;
            for( i_chunk = 0, i = 0; i < p_stream->i_entry_count; i_chunk++ )
            {
                if( p_stream->b_stco64 )
                    bo_add_64be( moov, p_stream->entry[i].i_pos + i_moov_size);
                else
                    bo_add_32be( moov, p_stream->entry[i].i_pos + i_moov_size);
326

327 328 329 330 331 332 333 334 335
                while( i < p_stream->i_entry_count )
                {
                    if( i + 1 < p_stream->i_entry_count &&
                        p_stream->entry[i].i_pos + p_stream->entry[i].i_size
                        != p_stream->entry[i + 1].i_pos )
                    {
                        i++;
                        break;
                    }
336

337 338 339 340
                    i++;
                }
            }
        }
341

342 343
        moov->i_buffer = i_moov_size;
        i_moov_pos = p_sys->i_mdat_pos;
344
        p_sys->b_fast_start = false;
345
    }
346

347 348 349
    /* Write MOOV header */
    sout_AccessOutSeek( p_mux->p_access, i_moov_pos );
    box_send( p_mux, moov );
350

351 352 353 354
    /* Clean-up */
    for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ )
    {
        mp4_stream_t *p_stream = p_sys->pp_streams[i_trak];
355

356 357 358 359 360 361 362
        es_format_Clean( &p_stream->fmt );
        free( p_stream->entry );
        free( p_stream );
    }
    if( p_sys->i_nb_streams ) free( p_sys->pp_streams );
    free( p_sys );
}
363

364
/*****************************************************************************
365
 * Control:
366
 *****************************************************************************/
367
static int Control( sout_mux_t *p_mux, int i_query, va_list args )
368
{
369
    VLC_UNUSED(p_mux);
370
    bool *pb_bool;
371

372 373 374
    switch( i_query )
    {
        case MUX_CAN_ADD_STREAM_WHILE_MUXING:
375 376
            pb_bool = (bool*)va_arg( args, bool * );
            *pb_bool = false;
377
            return VLC_SUCCESS;
378

379
        case MUX_GET_ADD_STREAM_WAIT:
380 381
            pb_bool = (bool*)va_arg( args, bool * );
            *pb_bool = true;
382
            return VLC_SUCCESS;
383

384
        case MUX_GET_MIME:   /* Not needed, as not streamable */
385
        default:
386
            return VLC_EGENERIC;
387
    }
388 389
}

390 391 392 393
/*****************************************************************************
 * AddStream:
 *****************************************************************************/
static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
394
{
395 396
    sout_mux_sys_t  *p_sys = p_mux->p_sys;
    mp4_stream_t    *p_stream;
397

398
    switch( p_input->p_fmt->i_codec )
399
    {
400 401 402 403 404 405 406 407 408 409 410 411 412 413
        case VLC_CODEC_MP4A:
        case VLC_CODEC_MP4V:
        case VLC_CODEC_MPGA:
        case VLC_CODEC_MPGV:
        case VLC_CODEC_MJPG:
        case VLC_CODEC_MJPGB:
        case VLC_CODEC_SVQ1:
        case VLC_CODEC_SVQ3:
        case VLC_CODEC_H263:
        case VLC_CODEC_H264:
        case VLC_CODEC_AMR_NB:
        case VLC_CODEC_AMR_WB:
        case VLC_CODEC_YV12:
        case VLC_CODEC_YUYV:
414
            break;
415
        case VLC_CODEC_SUBT:
416 417
            msg_Warn( p_mux, "subtitle track added like in .mov (even when creating .mp4)" );
            break;
418 419 420 421 422
        default:
            msg_Err( p_mux, "unsupported codec %4.4s in mp4",
                     (char*)&p_input->p_fmt->i_codec );
            return VLC_EGENERIC;
    }
423

424
    p_stream                = malloc( sizeof( mp4_stream_t ) );
425 426
    if( !p_stream )
        return VLC_ENOMEM;
427 428 429 430 431 432 433 434 435
    es_format_Copy( &p_stream->fmt, p_input->p_fmt );
    p_stream->i_track_id    = p_sys->i_nb_streams + 1;
    p_stream->i_length_neg  = 0;
    p_stream->i_entry_count = 0;
    p_stream->i_entry_max   = 1000;
    p_stream->entry         =
        calloc( p_stream->i_entry_max, sizeof( mp4_entry_t ) );
    p_stream->i_dts_start   = 0;
    p_stream->i_duration    = 0;
436

437
    p_input->p_sys          = p_stream;
438

439 440 441 442
    msg_Dbg( p_mux, "adding input" );

    TAB_APPEND( p_sys->i_nb_streams, p_sys->pp_streams, p_stream );
    return VLC_SUCCESS;
443 444
}

445 446 447 448
/*****************************************************************************
 * DelStream:
 *****************************************************************************/
static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
449
{
450
    VLC_UNUSED(p_input);
451 452 453
    msg_Dbg( p_mux, "removing input" );
    return VLC_SUCCESS;
}
454

455 456 457 458
/*****************************************************************************
 * Mux:
 *****************************************************************************/
static int Mux( sout_mux_t *p_mux )
459 460 461
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;

462
    for( ;; )
463
    {
464 465
        sout_input_t    *p_input;
        mp4_stream_t    *p_stream;
466
        block_t         *p_data;
467
        mtime_t         i_dts;
468

469 470
        int i_stream = sout_MuxGetStream( p_mux, 2, &i_dts);
        if( i_stream < 0 )
471
        {
472
            return( VLC_SUCCESS );
473
        }
474

475 476
        p_input  = p_mux->pp_inputs[i_stream];
        p_stream = (mp4_stream_t*)p_input->p_sys;
477

478
again:
479
        p_data  = block_FifoGet( p_input->p_fifo );
480
        if( p_stream->fmt.i_codec == VLC_CODEC_H264 )
481
        {
482
            p_data = ConvertAVC1( p_data );
483
        }
484
        else if( p_stream->fmt.i_codec == VLC_CODEC_SUBT )
485
        {
486
            p_data = ConvertSUBT( p_data );
487
        }
488
        if( p_data == NULL ) goto again;
489

490 491 492
        if( p_stream->fmt.i_cat != SPU_ES )
        {
            /* Fix length of the sample */
493
            if( block_FifoCount( p_input->p_fifo ) > 0 )
494
            {
495 496 497
                block_t *p_next = block_FifoShow( p_input->p_fifo );
                int64_t       i_diff  = p_next->i_dts - p_data->i_dts;

498
                if( i_diff < INT64_C(1000000 ) )   /* protection */
499 500 501
                {
                    p_data->i_length = i_diff;
                }
502
            }
503 504 505 506 507 508 509 510 511
            if( p_data->i_length <= 0 )
            {
                msg_Warn( p_mux, "i_length <= 0" );
                p_stream->i_length_neg += p_data->i_length - 1;
                p_data->i_length = 1;
            }
            else if( p_stream->i_length_neg < 0 )
            {
                int64_t i_recover = __MIN( p_data->i_length / 4, - p_stream->i_length_neg );
512

513 514 515
                p_data->i_length -= i_recover;
                p_stream->i_length_neg += i_recover;
            }
516
        }
517

518 519
        /* Save starting time */
        if( p_stream->i_entry_count == 0 )
520
        {
521 522 523 524 525 526 527 528
            p_stream->i_dts_start = p_data->i_dts;

            /* Update global dts_start */
            if( p_sys->i_dts_start <= 0 ||
                p_stream->i_dts_start < p_sys->i_dts_start )
            {
                p_sys->i_dts_start = p_stream->i_dts_start;
            }
529
        }
530

531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
        if( p_stream->fmt.i_cat == SPU_ES && p_stream->i_entry_count > 0 )
        {
            int64_t i_length = p_data->i_dts - p_stream->i_last_dts;

            if( i_length <= 0 )
            {
                /* FIXME handle this broken case */
                i_length = 1;
            }

            /* Fix last entry */
            if( p_stream->entry[p_stream->i_entry_count-1].i_length <= 0 )
            {
                p_stream->entry[p_stream->i_entry_count-1].i_length = i_length;
            }
        }


549 550
        /* add index entry */
        p_stream->entry[p_stream->i_entry_count].i_pos    = p_sys->i_pos;
551
        p_stream->entry[p_stream->i_entry_count].i_size   = p_data->i_buffer;
552 553
        p_stream->entry[p_stream->i_entry_count].i_pts_dts=
            __MAX( p_data->i_pts - p_data->i_dts, 0 );
554 555 556 557
        p_stream->entry[p_stream->i_entry_count].i_length = p_data->i_length;
        p_stream->entry[p_stream->i_entry_count].i_flags  = p_data->i_flags;

        p_stream->i_entry_count++;
558 559
        /* XXX: -1 to always have 2 entry for easy adding of empty SPU */
        if( p_stream->i_entry_count >= p_stream->i_entry_max - 1 )
560
        {
561
            p_stream->i_entry_max += 1000;
562
            p_stream->entry = xrealloc( p_stream->entry,
563
                         p_stream->i_entry_max * sizeof( mp4_entry_t ) );
564 565
        }

566
        /* update */
567
        p_stream->i_duration = p_stream->i_last_dts - p_stream->i_dts_start + p_data->i_length;
568
        p_sys->i_pos += p_data->i_buffer;
569

570 571 572
        /* Save the DTS */
        p_stream->i_last_dts = p_data->i_dts;

573 574
        /* write data */
        sout_AccessOutWrite( p_mux->p_access, p_data );
575 576 577 578 579 580 581 582

        if( p_stream->fmt.i_cat == SPU_ES )
        {
            int64_t i_length = p_stream->entry[p_stream->i_entry_count-1].i_length;

            if( i_length != 0 )
            {
                /* TODO */
583
                msg_Dbg( p_mux, "writing an empty sub" ) ;
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611

                /* Append a idx entry */
                p_stream->entry[p_stream->i_entry_count].i_pos    = p_sys->i_pos;
                p_stream->entry[p_stream->i_entry_count].i_size   = 3;
                p_stream->entry[p_stream->i_entry_count].i_pts_dts= 0;
                p_stream->entry[p_stream->i_entry_count].i_length = 0;
                p_stream->entry[p_stream->i_entry_count].i_flags  = 0;

                /* XXX: No need to grow the entry here */
                p_stream->i_entry_count++;

                /* Fix last dts */
                p_stream->i_last_dts += i_length;

                /* Write a " " */
                p_data = block_New( p_mux, 3 );
                p_data->p_buffer[0] = 0;
                p_data->p_buffer[1] = 1;
                p_data->p_buffer[2] = ' ';

                p_sys->i_pos += p_data->i_buffer;

                sout_AccessOutWrite( p_mux->p_access, p_data );
            }

            /* Fix duration */
            p_stream->i_duration = p_stream->i_last_dts - p_stream->i_dts_start;
        }
612 613 614
    }

    return( VLC_SUCCESS );
615 616
}

617 618 619
/*****************************************************************************
 *
 *****************************************************************************/
620
static block_t *ConvertSUBT( block_t *p_block )
621 622 623 624 625 626 627 628 629 630 631 632 633
{
    p_block = block_Realloc( p_block, 2, p_block->i_buffer );

    /* No trailling '\0' */
    if( p_block->i_buffer > 2 && p_block->p_buffer[p_block->i_buffer-1] == '\0' )
        p_block->i_buffer--;

    p_block->p_buffer[0] = ( (p_block->i_buffer - 2) >> 8 )&0xff;
    p_block->p_buffer[1] = ( (p_block->i_buffer - 2)      )&0xff;

    return p_block;
}

634
static block_t *ConvertAVC1( block_t *p_block )
635
{
636 637 638
    uint8_t *last = p_block->p_buffer;  /* Assume it starts with 0x00000001 */
    uint8_t *dat  = &p_block->p_buffer[4];
    uint8_t *end = &p_block->p_buffer[p_block->i_buffer];
639 640


641
    /* Replace the 4 bytes start code with 4 bytes size,
642
     * FIXME are all startcodes 4 bytes ? (I don't think :( */
643
    while( dat < end )
644 645 646
    {
        int i_size;

647
        while( dat < end - 4 )
648
        {
649 650
            if( dat[0] == 0x00 && dat[1] == 0x00  &&
                dat[2] == 0x00 && dat[3] == 0x01 )
651 652 653
            {
                break;
            }
654 655 656 657 658
            dat++;
        }
        if( dat >= end - 4 )
        {
            dat = end;
659 660
        }

661 662 663 664 665 666 667
        /* Fix size */
        i_size = dat - &last[4];
        last[0] = ( i_size >> 24 )&0xff;
        last[1] = ( i_size >> 16 )&0xff;
        last[2] = ( i_size >>  8 )&0xff;
        last[3] = ( i_size       )&0xff;

668
        /* Skip blocks with SPS/PPS */
669
        if( (last[4]&0x1f) == 7 || (last[4]&0x1f) == 8 )
670
        {
671
            ; // FIXME Find a way to skip dat without frelling everything
672
        }
673 674 675
        last = dat;
        dat += 4;
    }
676
    return p_block;
677 678
}

679
static int GetDescrLength( int i_size )
680
{
681 682 683 684 685 686 687 688 689
    if( i_size < 0x00000080 )
        return 2 + i_size;
    else if( i_size < 0x00004000 )
        return 3 + i_size;
    else if( i_size < 0x00200000 )
        return 4 + i_size;
    else
        return 5 + i_size;
}
690

691 692 693 694 695 696
static bo_t *GetESDS( mp4_stream_t *p_stream )
{
    bo_t *esds;
    int  i_stream_type;
    int  i_object_type_indication;
    int  i_decoder_specific_info_size;
697
    unsigned int i;
698 699
    int64_t i_bitrate_avg = 0;
    int64_t i_bitrate_max = 0;
700

701 702 703 704 705 706
    /* Compute avg/max bitrate */
    for( i = 0; i < p_stream->i_entry_count; i++ )
    {
        i_bitrate_avg += p_stream->entry[i].i_size;
        if( p_stream->entry[i].i_length > 0)
        {
707
            int64_t i_bitrate = INT64_C(8000000) * p_stream->entry[i].i_size / p_stream->entry[i].i_length;
708 709 710 711 712 713
            if( i_bitrate > i_bitrate_max )
                i_bitrate_max = i_bitrate;
        }
    }

    if( p_stream->i_duration > 0 )
714
        i_bitrate_avg = INT64_C(8000000) * i_bitrate_avg / p_stream->i_duration;
715 716 717 718 719 720
    else
        i_bitrate_avg = 0;
    if( i_bitrate_max <= 1 )
        i_bitrate_max = 0x7fffffff;

    /* */
721
    if( p_stream->fmt.i_extra > 0 )
722
    {
723 724
        i_decoder_specific_info_size =
            GetDescrLength( p_stream->fmt.i_extra );
725
    }
726
    else
727
    {
728
        i_decoder_specific_info_size = 0;
729 730
    }

731
    esds = box_full_new( "esds", 0, 0 );
732

733 734 735 736 737 738
    /* ES_Descr */
    bo_add_descr( esds, 0x03, 3 +
                  GetDescrLength( 13 + i_decoder_specific_info_size ) +
                  GetDescrLength( 1 ) );
    bo_add_16be( esds, p_stream->i_track_id );
    bo_add_8   ( esds, 0x1f );      // flags=0|streamPriority=0x1f
739

740 741
    /* DecoderConfigDescr */
    bo_add_descr( esds, 0x04, 13 + i_decoder_specific_info_size );
742

743
    switch( p_stream->fmt.i_codec )
744
    {
745
        case VLC_CODEC_MP4V:
746 747
            i_object_type_indication = 0x20;
            break;
748
        case VLC_CODEC_MPGV:
749 750 751
            /* FIXME MPEG-I=0x6b, MPEG-II = 0x60 -> 0x65 */
            i_object_type_indication = 0x60;
            break;
752
        case VLC_CODEC_MP4A:
753 754 755
            /* FIXME for mpeg2-aac == 0x66->0x68 */
            i_object_type_indication = 0x40;
            break;
756
        case VLC_CODEC_MPGA:
757 758 759 760 761 762
            i_object_type_indication =
                p_stream->fmt.audio.i_rate < 32000 ? 0x69 : 0x6b;
            break;
        default:
            i_object_type_indication = 0x00;
            break;
763
    }
764
    i_stream_type = p_stream->fmt.i_cat == VIDEO_ES ? 0x04 : 0x05;
765

766 767 768
    bo_add_8   ( esds, i_object_type_indication );
    bo_add_8   ( esds, ( i_stream_type << 2 ) | 1 );
    bo_add_24be( esds, 1024 * 1024 );       // bufferSizeDB
769 770
    bo_add_32be( esds, i_bitrate_max );     // maxBitrate
    bo_add_32be( esds, i_bitrate_avg );     // avgBitrate
771

772
    if( p_stream->fmt.i_extra > 0 )
773
    {
774
        int i;
775

776 777
        /* DecoderSpecificInfo */
        bo_add_descr( esds, 0x05, p_stream->fmt.i_extra );
778

779
        for( i = 0; i < p_stream->fmt.i_extra; i++ )
780
        {
781
            bo_add_8( esds, ((uint8_t*)p_stream->fmt.p_extra)[i] );
782 783 784
        }
    }

785 786 787
    /* SL_Descr mandatory */
    bo_add_descr( esds, 0x06, 1 );
    bo_add_8    ( esds, 0x02 );  // sl_predefined
788

789 790 791
    box_fix( esds );

    return esds;
792 793
}

794
static bo_t *GetWaveTag( mp4_stream_t *p_stream )
795
{
796 797
    bo_t *wave;
    bo_t *box;
798

799
    wave = box_new( "wave" );
800

801 802 803 804
    box = box_new( "frma" );
    bo_add_fourcc( box, "mp4a" );
    box_fix( box );
    box_gather( wave, box );
805

806 807 808 809
    box = box_new( "mp4a" );
    bo_add_32be( box, 0 );
    box_fix( box );
    box_gather( wave, box );
810

811 812 813
    box = GetESDS( p_stream );
    box_fix( box );
    box_gather( wave, box );
814

815 816 817 818
    box = box_new( "srcq" );
    bo_add_32be( box, 0x40 );
    box_fix( box );
    box_gather( wave, box );
819

820 821 822
    /* wazza ? */
    bo_add_32be( wave, 8 ); /* new empty box */
    bo_add_32be( wave, 0 ); /* box label */
823

824 825 826 827 828
    box_fix( wave );

    return wave;
}

829 830 831 832 833 834 835 836 837
static bo_t *GetDamrTag( mp4_stream_t *p_stream )
{
    bo_t *damr;

    damr = box_new( "damr" );

    bo_add_fourcc( damr, "REFC" );
    bo_add_8( damr, 0 );

838
    if( p_stream->fmt.i_codec == VLC_CODEC_AMR_NB )
839 840 841 842 843 844 845 846 847 848
        bo_add_16be( damr, 0x81ff ); /* Mode set (all modes for AMR_NB) */
    else
        bo_add_16be( damr, 0x83ff ); /* Mode set (all modes for AMR_WB) */
    bo_add_16be( damr, 0x1 ); /* Mode change period (no restriction) */

    box_fix( damr );

    return damr;
}

849
static bo_t *GetD263Tag( void )
850 851 852 853 854 855 856 857 858 859 860 861 862 863
{
    bo_t *d263;

    d263 = box_new( "d263" );

    bo_add_fourcc( d263, "VLC " );
    bo_add_16be( d263, 0xa );
    bo_add_8( d263, 0 );

    box_fix( d263 );

    return d263;
}

864 865
static bo_t *GetAvcCTag( mp4_stream_t *p_stream )
{
866 867 868 869 870
    bo_t    *avcC = NULL;
    uint8_t *p_sps = NULL;
    uint8_t *p_pps = NULL;
    int     i_sps_size = 0;
    int     i_pps_size = 0;
871

872 873 874 875 876 877
    if( p_stream->fmt.i_extra > 0 )
    {
        /* FIXME: take into account multiple sps/pps */
        uint8_t *p_buffer = p_stream->fmt.p_extra;
        int     i_buffer = p_stream->fmt.i_extra;

878
        while( i_buffer > 3 )
879
        {
880 881 882 883 884 885 886 887
            /* seek startcode */
            while( p_buffer[0] != 0 || p_buffer[1] != 0 ||
                   p_buffer[2] != 1 )
            {
                 i_buffer--;
                 p_buffer++;
            }
            const int i_nal_type = p_buffer[3]&0x1f;
888 889 890
            int i_offset    = 1;
            int i_size      = 0;
            int i_startcode = 0;
891 892
 
 
893
            for( i_offset = 1; i_offset+2 < i_buffer ; i_offset++)
894
            {
895
                if( p_buffer[i_offset] == 0 && p_buffer[i_offset+1] == 0 &&
896
                    p_buffer[i_offset+2] == 1 )
897 898 899
                {
                    /* we found another startcode */
                    i_startcode = i_offset;
900 901
                    while( p_buffer[i_startcode-1] == 0 && i_startcode > 0 )
                        i_startcode--;
902
                    break;
903
                }
904 905
            }
            i_size = i_startcode ? i_startcode : i_buffer;
906

907 908
            if( i_nal_type == 7 )
            {
909 910
                p_sps = &p_buffer[3];
                i_sps_size = i_size - 3;
911 912 913
            }
            if( i_nal_type == 8 )
            {
914 915
                p_pps = &p_buffer[3];
                i_pps_size = i_size - 3;
916 917 918 919 920
            }
            i_buffer -= i_size;
            p_buffer += i_size;
        }
    }
921
 
922 923 924
    /* FIXME use better value */
    avcC = box_new( "avcC" );
    bo_add_8( avcC, 1 );      /* configuration version */
925 926 927
    bo_add_8( avcC, i_sps_size ? p_sps[1] : 77 );
    bo_add_8( avcC, i_sps_size ? p_sps[2] : 64 );
    bo_add_8( avcC, i_sps_size ? p_sps[3] : 30 );       /* level, 5.1 */
928 929
    bo_add_8( avcC, 0xff );   /* 0b11111100 | lengthsize = 0x11 */

930 931
    bo_add_8( avcC, 0xe0 | (i_sps_size > 0 ? 1 : 0) );   /* 0b11100000 | sps_count */
    if( i_sps_size > 0 )
932
    {
933 934
        bo_add_16be( avcC, i_sps_size );
        bo_add_mem( avcC, i_sps_size, p_sps );
935 936
    }

937 938
    bo_add_8( avcC, (i_pps_size > 0 ? 1 : 0) );   /* pps_count */
    if( i_pps_size > 0 )
939
    {
940 941
        bo_add_16be( avcC, i_pps_size );
        bo_add_mem( avcC, i_pps_size, p_pps );
942 943 944 945 946 947
    }
    box_fix( avcC );

    return avcC;
}

948 949 950 951 952 953 954 955 956 957 958
/* TODO: No idea about these values */
static bo_t *GetSVQ3Tag( mp4_stream_t *p_stream )
{
    bo_t *smi = box_new( "SMI " );

    if( p_stream->fmt.i_extra > 0x4e )
    {
        uint8_t *p_end = &((uint8_t*)p_stream->fmt.p_extra)[p_stream->fmt.i_extra];
        uint8_t *p     = &((uint8_t*)p_stream->fmt.p_extra)[0x46];

        while( p + 8 < p_end )
959
        {
960 961
            int i_size = GetDWBE( p );
            if( i_size <= 1 )
962
            {
963
                /* FIXME handle 1 as long size */
964 965
                break;
            }
966
            if( !strncmp( (const char *)&p[4], "SMI ", 4 ) )
967 968 969 970 971
            {
                bo_add_mem( smi, p_end - p - 8, &p[8] );
                return smi;
            }
            p += i_size;
972 973 974
        }
    }

975 976 977 978 979 980
    /* Create a dummy one in fallback */
    bo_add_fourcc( smi, "SEQH" );
    bo_add_32be( smi, 0x5 );
    bo_add_32be( smi, 0xe2c0211d );
    bo_add_8( smi, 0xc0 );
    box_fix( smi );
981

982 983
    return smi;
}
984

985 986 987 988
static bo_t *GetUdtaTag( sout_mux_t *p_mux )
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;
    bo_t *udta = box_new( "udta" );
989
    vlc_meta_t *p_meta = p_mux->p_sout->p_meta;
990
    int i_track;
991

992 993
    /* Requirements */
    for( i_track = 0; i_track < p_sys->i_nb_streams; i_track++ )
994
    {
995
        mp4_stream_t *p_stream = p_sys->pp_streams[i_track];
996

997 998
        if( p_stream->fmt.i_codec == VLC_CODEC_MP4V ||
            p_stream->fmt.i_codec == VLC_CODEC_MP4A )
999
        {
1000 1001 1002 1003 1004
            bo_t *box = box_new( "\251req" );
            /* String length */
            bo_add_16be( box, sizeof("QuickTime 6.0 or greater") - 1);
            bo_add_16be( box, 0 );
            bo_add_mem( box, sizeof("QuickTime 6.0 or greater") - 1,
1005
                        (uint8_t *)"QuickTime 6.0 or greater" );
1006 1007 1008
            box_fix( box );
            box_gather( udta, box );
            break;
1009 1010 1011
        }
    }

1012
    /* Encoder */
1013
    {
1014 1015 1016 1017 1018
        bo_t *box = box_new( "\251enc" );
        /* String length */
        bo_add_16be( box, sizeof(PACKAGE_STRING " stream output") - 1);
        bo_add_16be( box, 0 );
        bo_add_mem( box, sizeof(PACKAGE_STRING " stream output") - 1,
1019
                    (uint8_t*)PACKAGE_STRING " stream output" );
1020 1021
        box_fix( box );
        box_gather( udta, box );
1022
    }
1023 1024

    /* Misc atoms */
1025 1026
    if( p_meta )
    {
1027 1028
#define ADD_META_BOX( type, box_string ) { \
        bo_t *box = NULL;  \
1029
        if( vlc_meta_Get( p_meta, vlc_meta_##type ) ) box = box_new( "\251" box_string ); \
1030 1031
        if( box ) \
        { \
1032
            bo_add_16be( box, strlen( vlc_meta_Get( p_meta, vlc_meta_##type ) )); \
1033
            bo_add_16be( box, 0 ); \
1034 1035
            bo_add_mem( box, strlen( vlc_meta_Get( p_meta, vlc_meta_##type ) ), \
                        (uint8_t*)(vlc_meta_Get( p_meta, vlc_meta_##type ) ) ); \
1036 1037 1038 1039
            box_fix( box ); \
            box_gather( udta, box ); \
        } }

1040 1041 1042 1043 1044 1045 1046
        ADD_META_BOX( Title, "nam" );
        ADD_META_BOX( Artist, "ART" );
        ADD_META_BOX( Genre, "gen" );
        ADD_META_BOX( Copyright, "cpy" );
        ADD_META_BOX( Description, "des" );
        ADD_META_BOX( Date, "day" );
        ADD_META_BOX( URL, "url" );
1047
#undef ADD_META_BOX
1048
    }
1049

1050 1051
    box_fix( udta );
    return udta;
1052 1053
}

1054
static bo_t *GetSounBox( sout_mux_t *p_mux, mp4_stream_t *p_stream )
1055 1056
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;
1057
    bool b_descr = false;
1058 1059 1060
    bo_t *soun;
    char fcc[4] = "    ";
    int  i;
1061

1062 1063
    switch( p_stream->fmt.i_codec )
    {
1064
    case VLC_CODEC_MP4A:
1065
        memcpy( fcc, "mp4a", 4 );
1066
        b_descr = true;
1067
        break;
1068

1069 1070
    case VLC_CODEC_AMR_NB:
    case VLC_CODEC_AMR_WB:
1071
        memcpy( fcc, (char*)&p_stream->fmt.i_codec, 4 );
1072
        b_descr = true;
1073 1074
        break;

1075
    case VLC_CODEC_MPGA:
1076 1077 1078 1079 1080
        if( p_sys->b_mov )
            memcpy( fcc, ".mp3", 4 );
        else
        {
            memcpy( fcc, "mp4a", 4 );
1081
            b_descr = true;
1082 1083
        }
        break;
1084

1085 1086 1087 1088
    default:
        memcpy( fcc, (char*)&p_stream->fmt.i_codec, 4 );
        break;
    }
1089

1090 1091
    soun = box_new( fcc );
    for( i = 0; i < 6; i++ )
1092
    {
1093
        bo_add_8( soun, 0 );        // reserved;
1094
    }
1095
    bo_add_16be( soun, 1 );         // data-reference-index
1096

1097 1098
    /* SoundDescription */
    if( p_sys->b_mov &&
1099
        p_stream->fmt.i_codec == VLC_CODEC_MP4A )
1100
    {
1101
        bo_add_16be( soun, 1 );     // version 1;
1102 1103 1104
    }
    else
    {
1105
        bo_add_16be( soun, 0 );     // version 0;
1106
    }
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120
    bo_add_16be( soun, 0 );         // revision level (0)
    bo_add_32be( soun, 0 );         // vendor
    // channel-count
    bo_add_16be( soun, p_stream->fmt.audio.i_channels );
    // sample size
    bo_add_16be( soun, p_stream->fmt.audio.i_bitspersample ?
                 p_stream->fmt.audio.i_bitspersample : 16 );
    bo_add_16be( soun, -2 );        // compression id
    bo_add_16be( soun, 0 );         // packet size (0)
    bo_add_16be( soun, p_stream->fmt.audio.i_rate ); // sampleratehi
    bo_add_16be( soun, 0 );                             // sampleratelo

    /* Extended data for SoundDescription V1 */
    if( p_sys->b_mov &&
1121
        p_stream->fmt.i_codec == VLC_CODEC_MP4A )
1122
    {
1123 1124 1125 1126 1127 1128
        /* samples per packet */
        bo_add_32be( soun, p_stream->fmt.audio.i_frame_length );
        bo_add_32be( soun, 1536 ); /* bytes per packet */
        bo_add_32be( soun, 2 );    /* bytes per frame */
        /* bytes per sample */
        bo_add_32be( soun, 2 /*p_stream->fmt.audio.i_bitspersample/8 */);
1129
    }
1130 1131 1132

    /* Add an ES Descriptor */
    if( b_descr )
1133
    {
1134 1135 1136
        bo_t *box;

        if( p_sys->b_mov &&
1137
            p_stream->fmt.i_codec == VLC_CODEC_MP4A )
1138 1139 1140
        {
            box = GetWaveTag( p_stream );
        }
1141
        else if( p_stream->fmt.i_codec == VLC_CODEC_AMR_NB )
1142 1143 1144
        {
            box = GetDamrTag( p_stream );
        }
1145 1146 1147 1148 1149 1150
        else
        {
            box = GetESDS( p_stream );
        }
        box_fix( box );
        box_gather( soun, box );
1151
    }
1152 1153 1154 1155 1156 1157

    box_fix( soun );

    return soun;
}

1158
static bo_t *GetVideBox( mp4_stream_t *p_stream )
1159 1160 1161 1162 1163 1164 1165
{

    bo_t *vide;
    char fcc[4] = "    ";
    int  i;

    switch( p_stream->fmt.i_codec )
1166
    {
1167 1168
    case VLC_CODEC_MP4V:
    case VLC_CODEC_MPGV:
1169 1170
        memcpy( fcc, "mp4v", 4 );
        break;
1171

1172
    case VLC_CODEC_MJPG:
1173 1174
        memcpy( fcc, "mjpa", 4 );
        break;
1175

1176
    case VLC_CODEC_SVQ1:
1177 1178 1179
        memcpy( fcc, "SVQ1", 4 );
        break;

1180
    case VLC_CODEC_SVQ3:
1181 1182
        memcpy( fcc, "SVQ3", 4 );
        break;
1183

1184
    case VLC_CODEC_H263:
1185 1186 1187
        memcpy( fcc, "s263", 4 );
        break;

1188
    case VLC_CODEC_H264:
1189 1190 1191
        memcpy( fcc, "avc1", 4 );
        break;

1192
    case VLC_CODEC_YV12:
1193 1194 1195
        memcpy( fcc, "yv12", 4 );
        break;

1196
    case VLC_CODEC_YUYV:
1197 1198 1199
        memcpy( fcc, "yuy2", 4 );
        break;

1200 1201 1202