2003-08-26 22:23:13 +02:00
/*
2006-10-23 10:57:54 +02:00
* MOV , 3 GP , MP4 muxer
2009-01-19 16:46:40 +01:00
* Copyright ( c ) 2003 Thomas Raivio
* Copyright ( c ) 2004 Gildas Bazin < gbazin at videolan dot org >
2009-03-15 11:53:12 +01:00
* Copyright ( c ) 2009 Baptiste Coudurier < baptiste dot coudurier at gmail dot com >
2003-08-26 22:23:13 +02:00
*
2006-10-07 17:30:46 +02:00
* This file is part of FFmpeg .
*
* FFmpeg is free software ; you can redistribute it and / or
2003-08-26 22:23:13 +02:00
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
2006-10-07 17:30:46 +02:00
* version 2.1 of the License , or ( at your option ) any later version .
2003-08-26 22:23:13 +02:00
*
2006-10-07 17:30:46 +02:00
* FFmpeg is distributed in the hope that it will be useful ,
2003-08-26 22:23:13 +02:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
2006-10-07 17:30:46 +02:00
* License along with FFmpeg ; if not , write to the Free Software
2006-01-12 23:43:26 +01:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2003-08-26 22:23:13 +02:00
*/
2009-03-15 11:49:52 +01:00
2010-05-18 21:38:37 +02:00
# include "movenc.h"
2003-08-26 22:23:13 +02:00
# include "avformat.h"
2011-02-24 07:36:02 +01:00
# include "avio_internal.h"
2006-07-12 02:09:34 +02:00
# include "riff.h"
2003-08-26 22:23:13 +02:00
# include "avio.h"
2006-08-01 16:58:15 +02:00
# include "isom.h"
2008-01-11 02:24:55 +01:00
# include "avc.h"
2009-04-13 18:20:26 +02:00
# include "libavcodec/get_bits.h"
2009-04-12 10:35:26 +02:00
# include "libavcodec/put_bits.h"
2010-05-18 21:47:24 +02:00
# include "internal.h"
# include "libavutil/avstring.h"
2011-11-27 15:04:16 +01:00
# include "libavutil/intfloat.h"
2011-06-04 13:58:23 +02:00
# include "libavutil/mathematics.h"
2011-05-21 13:57:04 +02:00
# include "libavutil/opt.h"
2011-05-22 12:46:29 +02:00
# include "libavutil/dict.h"
2011-05-21 13:58:43 +02:00
# include "rtpenc.h"
2011-12-03 19:32:45 +01:00
# include "mov_chan.h"
2003-08-26 22:23:13 +02:00
2003-09-10 01:03:04 +02:00
# undef NDEBUG
# include <assert.h>
2011-05-21 13:57:04 +02:00
static const AVOption options [ ] = {
2011-10-04 07:38:01 +02:00
{ " movflags " , " MOV muxer flags " , offsetof ( MOVMuxContext , flags ) , AV_OPT_TYPE_FLAGS , { . dbl = 0 } , INT_MIN , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM , " movflags " } ,
{ " rtphint " , " Add RTP hint tracks " , 0 , AV_OPT_TYPE_CONST , { . dbl = FF_MOV_FLAG_RTP_HINT } , INT_MIN , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM , " movflags " } ,
2011-10-26 22:09:26 +02:00
{ " moov_size " , " maximum moov size so it can be placed at the begin " , offsetof ( MOVMuxContext , reserved_moov_size ) , AV_OPT_TYPE_INT , { . dbl = 0 } , 0 , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM , 0 } ,
2012-01-05 12:57:05 +01:00
{ " empty_moov " , " Make the initial moov atom empty (not supported by QuickTime) " , 0 , AV_OPT_TYPE_CONST , { . dbl = FF_MOV_FLAG_EMPTY_MOOV } , INT_MIN , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM , " movflags " } ,
{ " frag_keyframe " , " Fragment at video keyframes " , 0 , AV_OPT_TYPE_CONST , { . dbl = FF_MOV_FLAG_FRAG_KEYFRAME } , INT_MIN , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM , " movflags " } ,
{ " separate_moof " , " Write separate moof/mdat atoms for each track " , 0 , AV_OPT_TYPE_CONST , { . dbl = FF_MOV_FLAG_SEPARATE_MOOF } , INT_MIN , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM , " movflags " } ,
2012-01-20 18:29:33 +01:00
{ " frag_custom " , " Flush fragments on caller requests " , 0 , AV_OPT_TYPE_CONST , { . dbl = FF_MOV_FLAG_FRAG_CUSTOM } , INT_MIN , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM , " movflags " } ,
2011-05-21 13:58:43 +02:00
FF_RTP_FLAG_OPTS ( MOVMuxContext , rtp_flags ) ,
2011-12-16 02:45:03 +01:00
{ " skip_iods " , " Skip writing iods atom. " , offsetof ( MOVMuxContext , iods_skip ) , AV_OPT_TYPE_INT , { . dbl = 1 } , 0 , 1 , AV_OPT_FLAG_ENCODING_PARAM } ,
2011-10-04 20:44:25 +02:00
{ " iods_audio_profile " , " iods audio profile atom. " , offsetof ( MOVMuxContext , iods_audio_profile ) , AV_OPT_TYPE_INT , { . dbl = - 1 } , - 1 , 255 , AV_OPT_FLAG_ENCODING_PARAM } ,
{ " iods_video_profile " , " iods video profile atom. " , offsetof ( MOVMuxContext , iods_video_profile ) , AV_OPT_TYPE_INT , { . dbl = - 1 } , - 1 , 255 , AV_OPT_FLAG_ENCODING_PARAM } ,
2012-01-05 12:57:05 +01:00
{ " frag_duration " , " Maximum fragment duration " , offsetof ( MOVMuxContext , max_fragment_duration ) , AV_OPT_TYPE_INT , { . dbl = 0 } , 0 , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM } ,
{ " frag_size " , " Maximum fragment size " , offsetof ( MOVMuxContext , max_fragment_size ) , AV_OPT_TYPE_INT , { . dbl = 0 } , 0 , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM } ,
2012-01-09 16:58:26 +01:00
{ " ism_lookahead " , " Number of lookahead entries for ISM files " , offsetof ( MOVMuxContext , ism_lookahead ) , AV_OPT_TYPE_INT , { . dbl = 0 } , 0 , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM } ,
2011-05-21 13:57:04 +02:00
{ NULL } ,
} ;
2011-10-03 19:14:03 +02:00
# define MOV_CLASS(flavor)\
static const AVClass flavor # # _muxer_class = { \
. class_name = # flavor " muxer " , \
. item_name = av_default_item_name , \
. option = options , \
. version = LIBAVUTIL_VERSION_INT , \
2011-05-21 13:57:04 +02:00
} ;
2007-06-12 20:50:50 +02:00
//FIXME support 64 bit variant with wide placeholders
2011-02-20 11:04:12 +01:00
static int64_t updateSize ( AVIOContext * pb , int64_t pos )
2003-08-26 22:23:13 +02:00
{
2011-03-03 20:11:45 +01:00
int64_t curpos = avio_tell ( pb ) ;
2011-02-28 14:57:54 +01:00
avio_seek ( pb , pos , SEEK_SET ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , curpos - pos ) ; /* rewrite size */
2011-02-28 14:57:54 +01:00
avio_seek ( pb , curpos , SEEK_SET ) ;
2003-09-10 01:03:04 +02:00
return curpos - pos ;
2003-08-26 22:23:13 +02:00
}
2003-11-03 22:51:07 +01:00
/* Chunk offset atom */
2011-02-20 11:04:12 +01:00
static int mov_write_stco_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 22:23:13 +02:00
{
int i ;
2006-01-23 15:28:50 +01:00
int mode64 = 0 ; // use 32 bit size variant if possible
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2006-01-23 15:12:03 +01:00
if ( pos > UINT32_MAX ) {
mode64 = 1 ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " co64 " ) ;
2006-01-23 15:12:03 +01:00
} else
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " stco " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* version & flags */
2011-11-29 04:03:22 +01:00
avio_wb32 ( pb , track - > chunkCount ) ; /* entry count */
2003-08-26 22:23:13 +02:00
for ( i = 0 ; i < track - > entry ; i + + ) {
2011-11-29 04:03:22 +01:00
if ( ! track - > cluster [ i ] . chunkNum )
continue ;
2006-01-23 15:12:03 +01:00
if ( mode64 = = 1 )
2011-02-21 19:28:17 +01:00
avio_wb64 ( pb , track - > cluster [ i ] . pos ) ;
2006-01-23 15:12:03 +01:00
else
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , track - > cluster [ i ] . pos ) ;
2003-08-26 22:23:13 +02:00
}
2008-06-11 10:50:41 +02:00
return updateSize ( pb , pos ) ;
2003-08-26 22:23:13 +02:00
}
2003-11-03 22:51:07 +01:00
/* Sample size atom */
2011-02-20 11:04:12 +01:00
static int mov_write_stsz_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 22:23:13 +02:00
{
2003-09-28 23:09:32 +02:00
int equalChunks = 1 ;
2003-11-03 22:51:07 +01:00
int i , j , entries = 0 , tst = - 1 , oldtst = - 1 ;
2003-08-26 22:23:13 +02:00
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " stsz " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* version & flags */
2003-08-26 22:23:13 +02:00
2003-09-28 23:09:32 +02:00
for ( i = 0 ; i < track - > entry ; i + + ) {
2006-06-24 20:09:20 +02:00
tst = track - > cluster [ i ] . size / track - > cluster [ i ] . entries ;
2003-11-03 22:51:07 +01:00
if ( oldtst ! = - 1 & & tst ! = oldtst ) {
equalChunks = 0 ;
2003-09-28 23:09:32 +02:00
}
oldtst = tst ;
2006-06-24 20:09:20 +02:00
entries + = track - > cluster [ i ] . entries ;
2003-09-28 23:09:32 +02:00
}
2012-01-05 12:57:05 +01:00
if ( equalChunks & & track - > entry ) {
int sSize = track - > entry ? track - > cluster [ 0 ] . size / track - > cluster [ 0 ] . entries : 0 ;
2011-02-28 01:29:21 +01:00
sSize = FFMAX ( 1 , sSize ) ; // adpcm mono case could make sSize == 0
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , sSize ) ; // sample size
avio_wb32 ( pb , entries ) ; // sample count
2003-08-26 22:23:13 +02:00
}
2003-09-28 23:09:32 +02:00
else {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; // sample size
avio_wb32 ( pb , entries ) ; // sample count
2003-09-28 23:09:32 +02:00
for ( i = 0 ; i < track - > entry ; i + + ) {
2008-02-06 19:57:00 +01:00
for ( j = 0 ; j < track - > cluster [ i ] . entries ; j + + ) {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , track - > cluster [ i ] . size /
2006-06-24 20:09:20 +02:00
track - > cluster [ i ] . entries ) ;
2003-11-03 22:51:07 +01:00
}
2003-08-26 22:23:13 +02:00
}
}
2008-06-11 10:50:41 +02:00
return updateSize ( pb , pos ) ;
2003-08-26 22:23:13 +02:00
}
2003-11-03 22:51:07 +01:00
/* Sample to chunk atom */
2011-02-20 11:04:12 +01:00
static int mov_write_stsc_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 22:23:13 +02:00
{
2006-01-23 15:12:03 +01:00
int index = 0 , oldval = - 1 , i ;
2008-10-03 12:16:29 +02:00
int64_t entryPos , curpos ;
2003-09-28 23:09:32 +02:00
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " stsc " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; // version & flags
2011-03-03 20:11:45 +01:00
entryPos = avio_tell ( pb ) ;
2011-11-29 04:03:22 +01:00
avio_wb32 ( pb , track - > chunkCount ) ; // entry count
2003-09-28 23:09:32 +02:00
for ( i = 0 ; i < track - > entry ; i + + ) {
2011-11-29 04:03:22 +01:00
if ( oldval ! = track - > cluster [ i ] . samplesInChunk & & track - > cluster [ i ] . chunkNum )
2003-09-28 23:09:32 +02:00
{
2011-11-29 04:03:22 +01:00
avio_wb32 ( pb , track - > cluster [ i ] . chunkNum ) ; // first chunk
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , track - > cluster [ i ] . samplesInChunk ) ; // samples per chunk
avio_wb32 ( pb , 0x1 ) ; // sample description index
2006-06-24 20:09:20 +02:00
oldval = track - > cluster [ i ] . samplesInChunk ;
2003-09-28 23:09:32 +02:00
index + + ;
2003-08-26 22:23:13 +02:00
}
}
2011-03-03 20:11:45 +01:00
curpos = avio_tell ( pb ) ;
2011-02-28 14:57:54 +01:00
avio_seek ( pb , entryPos , SEEK_SET ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , index ) ; // rewrite size
2011-02-28 14:57:54 +01:00
avio_seek ( pb , curpos , SEEK_SET ) ;
2003-08-26 22:23:13 +02:00
2008-06-11 10:50:41 +02:00
return updateSize ( pb , pos ) ;
2003-08-26 22:23:13 +02:00
}
2003-11-03 22:51:07 +01:00
/* Sync sample atom */
2011-02-20 11:04:12 +01:00
static int mov_write_stss_tag ( AVIOContext * pb , MOVTrack * track , uint32_t flag )
2003-08-26 22:23:13 +02:00
{
2008-10-03 12:16:29 +02:00
int64_t curpos , entryPos ;
2006-01-23 15:12:03 +01:00
int i , index = 0 ;
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; // size
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , flag = = MOV_SYNC_SAMPLE ? " stss " : " stps " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; // version & flags
2011-03-03 20:11:45 +01:00
entryPos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , track - > entry ) ; // entry count
2003-09-28 23:09:32 +02:00
for ( i = 0 ; i < track - > entry ; i + + ) {
2009-05-15 08:11:53 +02:00
if ( track - > cluster [ i ] . flags & flag ) {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , i + 1 ) ;
2003-09-28 23:09:32 +02:00
index + + ;
}
}
2011-03-03 20:11:45 +01:00
curpos = avio_tell ( pb ) ;
2011-02-28 14:57:54 +01:00
avio_seek ( pb , entryPos , SEEK_SET ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , index ) ; // rewrite size
2011-02-28 14:57:54 +01:00
avio_seek ( pb , curpos , SEEK_SET ) ;
2008-06-11 10:50:41 +02:00
return updateSize ( pb , pos ) ;
2003-08-26 22:23:13 +02:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_amr_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 22:23:13 +02:00
{
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x11 ) ; /* size */
2011-02-24 07:36:02 +01:00
if ( track - > mode = = MODE_MOV ) ffio_wfourcc ( pb , " samr " ) ;
else ffio_wfourcc ( pb , " damr " ) ;
ffio_wfourcc ( pb , " FFMP " ) ;
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0 ) ; /* decoder version */
2006-05-13 23:00:52 +02:00
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 0x81FF ) ; /* Mode set (all modes for AMR_NB) */
avio_w8 ( pb , 0x00 ) ; /* Mode change period (no restriction) */
avio_w8 ( pb , 0x01 ) ; /* Frames per sample */
2006-05-13 23:00:52 +02:00
return 0x11 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_ac3_tag ( AVIOContext * pb , MOVTrack * track )
2008-09-03 21:05:22 +02:00
{
GetBitContext gbc ;
PutBitContext pbc ;
uint8_t buf [ 3 ] ;
int fscod , bsid , bsmod , acmod , lfeon , frmsizecod ;
if ( track - > vosLen < 7 )
return - 1 ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 11 ) ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " dac3 " ) ;
2008-09-03 21:05:22 +02:00
2011-09-09 23:46:00 +02:00
init_get_bits ( & gbc , track - > vosData + 4 , ( track - > vosLen - 4 ) * 8 ) ;
2008-09-03 21:05:22 +02:00
fscod = get_bits ( & gbc , 2 ) ;
frmsizecod = get_bits ( & gbc , 6 ) ;
bsid = get_bits ( & gbc , 5 ) ;
bsmod = get_bits ( & gbc , 3 ) ;
acmod = get_bits ( & gbc , 3 ) ;
if ( acmod = = 2 ) {
skip_bits ( & gbc , 2 ) ; // dsurmod
} else {
if ( ( acmod & 1 ) & & acmod ! = 1 )
skip_bits ( & gbc , 2 ) ; // cmixlev
if ( acmod & 4 )
skip_bits ( & gbc , 2 ) ; // surmixlev
}
lfeon = get_bits1 ( & gbc ) ;
init_put_bits ( & pbc , buf , sizeof ( buf ) ) ;
put_bits ( & pbc , 2 , fscod ) ;
put_bits ( & pbc , 5 , bsid ) ;
put_bits ( & pbc , 3 , bsmod ) ;
put_bits ( & pbc , 3 , acmod ) ;
put_bits ( & pbc , 1 , lfeon ) ;
put_bits ( & pbc , 5 , frmsizecod > > 1 ) ; // bit_rate_code
put_bits ( & pbc , 5 , 0 ) ; // reserved
flush_put_bits ( & pbc ) ;
2011-02-21 19:28:17 +01:00
avio_write ( pb , buf , sizeof ( buf ) ) ;
2008-09-03 21:05:22 +02:00
return 11 ;
}
2008-04-24 15:59:39 +02:00
/**
* This function writes extradata " as is " .
2011-10-05 14:12:42 +02:00
* Extradata must be formatted like a valid atom ( with size and tag ) .
2008-04-24 15:59:39 +02:00
*/
2011-02-20 11:04:12 +01:00
static int mov_write_extradata_tag ( AVIOContext * pb , MOVTrack * track )
2008-04-24 15:59:39 +02:00
{
2011-02-21 19:28:17 +01:00
avio_write ( pb , track - > enc - > extradata , track - > enc - > extradata_size ) ;
2008-04-24 15:59:39 +02:00
return track - > enc - > extradata_size ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_enda_tag ( AVIOContext * pb )
2006-05-13 22:05:02 +02:00
{
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 10 ) ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " enda " ) ;
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 1 ) ; /* little endian */
2006-05-13 22:05:02 +02:00
return 10 ;
}
2011-02-20 11:04:12 +01:00
static void putDescr ( AVIOContext * pb , int tag , unsigned int size )
2006-05-13 20:01:16 +02:00
{
2011-03-30 23:08:16 +02:00
int i = 3 ;
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , tag ) ;
2006-05-13 20:45:29 +02:00
for ( ; i > 0 ; i - - )
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , ( size > > ( 7 * i ) ) | 0x80 ) ;
avio_w8 ( pb , size & 0x7F ) ;
2006-05-13 20:01:16 +02:00
}
2011-04-17 01:19:10 +02:00
static unsigned compute_avg_bitrate ( MOVTrack * track )
{
uint64_t size = 0 ;
int i ;
for ( i = 0 ; i < track - > entry ; i + + )
size + = track - > cluster [ i ] . size ;
return size * 8 * track - > timescale / track - > trackDuration ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_esds_tag ( AVIOContext * pb , MOVTrack * track ) // Basic
2006-05-13 20:01:16 +02:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-03-30 23:08:16 +02:00
int decoderSpecificInfoLen = track - > vosLen ? 5 + track - > vosLen : 0 ;
2011-04-17 01:19:10 +02:00
unsigned avg_bitrate ;
2006-05-13 20:01:16 +02:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; // size
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " esds " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; // Version
2006-05-13 20:01:16 +02:00
// ES descriptor
2011-03-30 23:08:16 +02:00
putDescr ( pb , 0x03 , 3 + 5 + 13 + decoderSpecificInfoLen + 5 + 1 ) ;
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , track - > trackID ) ;
avio_w8 ( pb , 0x00 ) ; // flags (= no flags)
2006-05-13 20:01:16 +02:00
// DecoderConfig descriptor
putDescr ( pb , 0x04 , 13 + decoderSpecificInfoLen ) ;
// Object type indication
2008-09-02 22:48:45 +02:00
if ( ( track - > enc - > codec_id = = CODEC_ID_MP2 | |
track - > enc - > codec_id = = CODEC_ID_MP3 ) & &
2008-09-03 21:42:09 +02:00
track - > enc - > sample_rate > 24000 )
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0x6B ) ; // 11172-3
2008-09-02 22:48:45 +02:00
else
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , ff_codec_get_tag ( ff_mp4_obj_type , track - > enc - > codec_id ) ) ;
2006-05-13 20:01:16 +02:00
// the following fields is made of 6 bits to identify the streamtype (4 for video, 5 for audio)
// plus 1 bit to indicate upstream and 1 bit set to 1 (reserved)
2010-03-31 01:30:55 +02:00
if ( track - > enc - > codec_type = = AVMEDIA_TYPE_AUDIO )
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0x15 ) ; // flags (= Audiostream)
2006-05-13 20:01:16 +02:00
else
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0x11 ) ; // flags (= Visualstream)
2006-05-13 20:01:16 +02:00
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , track - > enc - > rc_buffer_size > > ( 3 + 16 ) ) ; // Buffersize DB (24 bits)
avio_wb16 ( pb , ( track - > enc - > rc_buffer_size > > 3 ) & 0xFFFF ) ; // Buffersize DB
2006-05-13 20:01:16 +02:00
2011-04-17 01:19:10 +02:00
avg_bitrate = compute_avg_bitrate ( track ) ;
// maxbitrate (FIXME should be max rate in any 1 sec window)
avio_wb32 ( pb , FFMAX3 ( track - > enc - > bit_rate , track - > enc - > rc_max_rate , avg_bitrate ) ) ;
avio_wb32 ( pb , avg_bitrate ) ;
2006-05-13 20:01:16 +02:00
2008-04-24 19:22:39 +02:00
if ( track - > vosLen ) {
2006-05-13 20:01:16 +02:00
// DecoderSpecific info descriptor
putDescr ( pb , 0x05 , track - > vosLen ) ;
2011-02-21 19:28:17 +01:00
avio_write ( pb , track - > vosData , track - > vosLen ) ;
2006-05-13 20:01:16 +02:00
}
// SL descriptor
putDescr ( pb , 0x06 , 1 ) ;
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0x02 ) ;
2008-06-11 10:50:41 +02:00
return updateSize ( pb , pos ) ;
2006-05-13 20:01:16 +02:00
}
2009-11-29 03:27:08 +01:00
static int mov_pcm_le_gt16 ( enum CodecID codec_id )
{
return codec_id = = CODEC_ID_PCM_S24LE | |
codec_id = = CODEC_ID_PCM_S32LE | |
codec_id = = CODEC_ID_PCM_F32LE | |
codec_id = = CODEC_ID_PCM_F64LE ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_ms_tag ( AVIOContext * pb , MOVTrack * track )
2011-01-20 22:14:12 +01:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ;
avio_wl32 ( pb , track - > tag ) ; // store it byteswapped
2011-01-27 17:26:20 +01:00
track - > enc - > codec_tag = av_bswap16 ( track - > tag > > 16 ) ;
2011-01-20 22:14:12 +01:00
ff_put_wav_header ( pb , track - > enc ) ;
return updateSize ( pb , pos ) ;
}
2011-12-03 19:32:45 +01:00
static int mov_write_chan_tag ( AVIOContext * pb , MOVTrack * track )
{
uint32_t layout_tag , bitmap ;
int64_t pos = avio_tell ( pb ) ;
layout_tag = ff_mov_get_channel_layout_tag ( track - > enc - > codec_id ,
track - > enc - > channel_layout ,
& bitmap ) ;
if ( ! layout_tag ) {
av_log ( track - > enc , AV_LOG_WARNING , " not writing 'chan' tag due to "
" lack of channel information \n " ) ;
return 0 ;
}
avio_wb32 ( pb , 0 ) ; // Size
ffio_wfourcc ( pb , " chan " ) ; // Type
avio_w8 ( pb , 0 ) ; // Version
avio_wb24 ( pb , 0 ) ; // Flags
avio_wb32 ( pb , layout_tag ) ; // mChannelLayoutTag
avio_wb32 ( pb , bitmap ) ; // mChannelBitmap
avio_wb32 ( pb , 0 ) ; // mNumberChannelDescriptions
return updateSize ( pb , pos ) ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_wave_tag ( AVIOContext * pb , MOVTrack * track )
2004-02-14 20:08:09 +01:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2004-02-14 20:08:09 +01:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " wave " ) ;
2004-02-14 20:08:09 +01:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 12 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " frma " ) ;
2011-02-21 19:28:17 +01:00
avio_wl32 ( pb , track - > tag ) ;
2004-02-14 20:08:09 +01:00
2006-05-13 22:05:02 +02:00
if ( track - > enc - > codec_id = = CODEC_ID_AAC ) {
2006-06-19 13:28:28 +02:00
/* useless atom needed by mplayer, ipod, not needed by quicktime */
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 12 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " mp4a " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ;
2006-05-13 22:05:02 +02:00
mov_write_esds_tag ( pb , track ) ;
2009-11-29 03:27:08 +01:00
} else if ( mov_pcm_le_gt16 ( track - > enc - > codec_id ) ) {
2006-05-13 22:05:02 +02:00
mov_write_enda_tag ( pb ) ;
2006-05-13 23:00:52 +02:00
} else if ( track - > enc - > codec_id = = CODEC_ID_AMR_NB ) {
2006-07-06 16:38:50 +02:00
mov_write_amr_tag ( pb , track ) ;
2008-09-03 21:05:22 +02:00
} else if ( track - > enc - > codec_id = = CODEC_ID_AC3 ) {
2011-12-06 23:35:37 +01:00
mov_write_chan_tag ( pb , track ) ;
2008-09-03 21:05:22 +02:00
mov_write_ac3_tag ( pb , track ) ;
2008-04-24 15:59:39 +02:00
} else if ( track - > enc - > codec_id = = CODEC_ID_ALAC ) {
mov_write_extradata_tag ( pb , track ) ;
2011-01-20 22:14:12 +01:00
} else if ( track - > enc - > codec_id = = CODEC_ID_ADPCM_MS | |
track - > enc - > codec_id = = CODEC_ID_ADPCM_IMA_WAV ) {
mov_write_ms_tag ( pb , track ) ;
2006-05-13 22:05:02 +02:00
}
2004-02-14 20:08:09 +01:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 8 ) ; /* size */
avio_wb32 ( pb , 0 ) ; /* null tag */
2004-02-14 20:08:09 +01:00
2008-06-11 10:50:41 +02:00
return updateSize ( pb , pos ) ;
2004-02-14 20:08:09 +01:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_glbl_tag ( AVIOContext * pb , MOVTrack * track )
2007-12-19 17:00:08 +01:00
{
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , track - > vosLen + 8 ) ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " glbl " ) ;
2011-02-21 19:28:17 +01:00
avio_write ( pb , track - > vosData , track - > vosLen ) ;
2007-12-19 17:00:08 +01:00
return 8 + track - > vosLen ;
}
2009-11-29 03:03:24 +01:00
/**
* Compute flags for ' lpcm ' tag .
* See CoreAudioTypes and AudioStreamBasicDescription at Apple .
*/
static int mov_get_lpcm_flags ( enum CodecID codec_id )
{
switch ( codec_id ) {
case CODEC_ID_PCM_F32BE :
case CODEC_ID_PCM_F64BE :
return 11 ;
case CODEC_ID_PCM_F32LE :
case CODEC_ID_PCM_F64LE :
return 9 ;
case CODEC_ID_PCM_U8 :
return 10 ;
case CODEC_ID_PCM_S16BE :
case CODEC_ID_PCM_S24BE :
case CODEC_ID_PCM_S32BE :
return 14 ;
case CODEC_ID_PCM_S8 :
case CODEC_ID_PCM_S16LE :
case CODEC_ID_PCM_S24LE :
case CODEC_ID_PCM_S32LE :
return 12 ;
default :
return 0 ;
}
}
2011-02-20 11:04:12 +01:00
static int mov_write_audio_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 22:23:13 +02:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2009-11-29 03:03:24 +01:00
int version = 0 ;
uint32_t tag = track - > tag ;
if ( track - > mode = = MODE_MOV ) {
2012-01-22 23:23:23 +01:00
if ( track - > timescale > UINT16_MAX ) {
if ( mov_get_lpcm_flags ( track - > enc - > codec_id ) )
tag = AV_RL32 ( " lpcm " ) ;
version = 2 ;
} else if ( track - > audio_vbr | | mov_pcm_le_gt16 ( track - > enc - > codec_id ) | |
track - > enc - > codec_id = = CODEC_ID_ADPCM_MS | |
track - > enc - > codec_id = = CODEC_ID_ADPCM_IMA_WAV ) {
version = 1 ;
}
2009-11-29 03:03:24 +01:00
}
2005-12-17 19:14:38 +01:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
avio_wl32 ( pb , tag ) ; // store it byteswapped
avio_wb32 ( pb , 0 ) ; /* Reserved */
avio_wb16 ( pb , 0 ) ; /* Reserved */
avio_wb16 ( pb , 1 ) ; /* Data-reference index, XXX == 1 */
2004-02-14 20:08:09 +01:00
2003-11-03 22:51:07 +01:00
/* SoundDescription */
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , version ) ; /* Version */
avio_wb16 ( pb , 0 ) ; /* Revision level */
avio_wb32 ( pb , 0 ) ; /* Reserved */
2003-08-26 22:23:13 +02:00
2009-11-29 03:03:24 +01:00
if ( version = = 2 ) {
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 3 ) ;
avio_wb16 ( pb , 16 ) ;
avio_wb16 ( pb , 0xfffe ) ;
avio_wb16 ( pb , 0 ) ;
avio_wb32 ( pb , 0x00010000 ) ;
avio_wb32 ( pb , 72 ) ;
2012-01-09 10:05:33 +01:00
avio_wb64 ( pb , av_double2int ( track - > enc - > sample_rate ) ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , track - > enc - > channels ) ;
avio_wb32 ( pb , 0x7F000000 ) ;
avio_wb32 ( pb , av_get_bits_per_sample ( track - > enc - > codec_id ) ) ;
avio_wb32 ( pb , mov_get_lpcm_flags ( track - > enc - > codec_id ) ) ;
avio_wb32 ( pb , track - > sampleSize ) ;
2012-01-22 23:23:23 +01:00
avio_wb32 ( pb , track - > enc - > frame_size ) ;
2009-11-29 03:03:24 +01:00
} else {
2012-01-22 23:23:23 +01:00
if ( track - > mode = = MODE_MOV ) {
avio_wb16 ( pb , track - > enc - > channels ) ;
if ( track - > enc - > codec_id = = CODEC_ID_PCM_U8 | |
track - > enc - > codec_id = = CODEC_ID_PCM_S8 )
avio_wb16 ( pb , 8 ) ; /* bits per sample */
else
avio_wb16 ( pb , 16 ) ;
avio_wb16 ( pb , track - > audio_vbr ? - 2 : 0 ) ; /* compression ID */
} else { /* reserved for mp4/3gp */
avio_wb16 ( pb , 2 ) ;
avio_wb16 ( pb , 16 ) ;
avio_wb16 ( pb , 0 ) ;
}
2004-07-28 11:40:59 +02:00
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 0 ) ; /* packet size (= 0) */
2012-01-09 10:05:33 +01:00
avio_wb16 ( pb , track - > enc - > sample_rate ) ;
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 0 ) ; /* Reserved */
2009-11-29 03:03:24 +01:00
}
2003-08-26 22:23:13 +02:00
2012-01-22 23:23:23 +01:00
if ( version = = 1 ) { /* SoundDescription V1 extended info */
avio_wb32 ( pb , track - > enc - > frame_size ) ; /* Samples per packet */
avio_wb32 ( pb , track - > sampleSize / track - > enc - > channels ) ; /* Bytes per packet */
avio_wb32 ( pb , track - > sampleSize ) ; /* Bytes per frame */
avio_wb32 ( pb , 2 ) ; /* Bytes per sample */
}
2006-05-14 00:25:17 +02:00
if ( track - > mode = = MODE_MOV & &
( track - > enc - > codec_id = = CODEC_ID_AAC | |
2008-09-04 20:25:55 +02:00
track - > enc - > codec_id = = CODEC_ID_AC3 | |
2006-05-14 00:25:17 +02:00
track - > enc - > codec_id = = CODEC_ID_AMR_NB | |
2009-11-29 03:27:08 +01:00
track - > enc - > codec_id = = CODEC_ID_ALAC | |
2011-01-20 22:14:12 +01:00
track - > enc - > codec_id = = CODEC_ID_ADPCM_MS | |
track - > enc - > codec_id = = CODEC_ID_ADPCM_IMA_WAV | |
2009-11-29 03:27:08 +01:00
mov_pcm_le_gt16 ( track - > enc - > codec_id ) ) )
2006-05-13 22:05:02 +02:00
mov_write_wave_tag ( pb , track ) ;
2008-01-28 14:44:27 +01:00
else if ( track - > tag = = MKTAG ( ' m ' , ' p ' , ' 4 ' , ' a ' ) )
2006-05-13 23:00:52 +02:00
mov_write_esds_tag ( pb , track ) ;
else if ( track - > enc - > codec_id = = CODEC_ID_AMR_NB )
2006-07-06 16:38:50 +02:00
mov_write_amr_tag ( pb , track ) ;
2008-09-04 20:25:55 +02:00
else if ( track - > enc - > codec_id = = CODEC_ID_AC3 )
mov_write_ac3_tag ( pb , track ) ;
2008-09-04 20:26:31 +02:00
else if ( track - > enc - > codec_id = = CODEC_ID_ALAC )
2008-06-11 10:17:38 +02:00
mov_write_extradata_tag ( pb , track ) ;
2007-12-19 17:00:08 +01:00
else if ( track - > vosLen > 0 )
mov_write_glbl_tag ( pb , track ) ;
2006-05-13 23:00:52 +02:00
2008-06-11 10:50:41 +02:00
return updateSize ( pb , pos ) ;
2003-08-26 22:23:13 +02:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_d263_tag ( AVIOContext * pb )
2003-08-26 22:23:13 +02:00
{
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0xf ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " d263 " ) ;
ffio_wfourcc ( pb , " FFMP " ) ;
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0 ) ; /* decoder version */
2006-08-07 17:04:15 +02:00
/* FIXME use AVCodecContext level/profile, when encoder will set values */
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0xa ) ; /* level */
avio_w8 ( pb , 0 ) ; /* profile */
2003-08-26 22:23:13 +02:00
return 0xf ;
}
2003-09-28 23:09:32 +02:00
/* TODO: No idea about these values */
2011-02-20 11:04:12 +01:00
static int mov_write_svq3_tag ( AVIOContext * pb )
2003-08-26 22:23:13 +02:00
{
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x15 ) ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " SMI " ) ;
ffio_wfourcc ( pb , " SEQH " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x5 ) ;
avio_wb32 ( pb , 0xe2c0211d ) ;
avio_wb32 ( pb , 0xc0000000 ) ;
avio_w8 ( pb , 0 ) ;
2003-09-28 23:09:32 +02:00
return 0x15 ;
2003-08-26 22:23:13 +02:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_avcc_tag ( AVIOContext * pb , MOVTrack * track )
2008-01-11 02:04:01 +01:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2008-01-11 02:04:01 +01:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " avcC " ) ;
2008-01-12 00:33:32 +01:00
ff_isom_write_avcc ( pb , track - > vosData , track - > vosLen ) ;
2006-03-11 19:18:17 +01:00
return updateSize ( pb , pos ) ;
}
2007-10-08 13:27:18 +02:00
/* also used by all avid codecs (dv, imx, meridien) and their variants */
2011-02-20 11:04:12 +01:00
static int mov_write_avid_tag ( AVIOContext * pb , MOVTrack * track )
2007-10-08 13:27:18 +02:00
{
int i ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 24 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " ACLR " ) ;
ffio_wfourcc ( pb , " ACLR " ) ;
ffio_wfourcc ( pb , " 0001 " ) ;
2011-03-23 18:22:05 +01:00
avio_wb32 ( pb , 2 ) ; /* yuv range: full 1 / normal 2 */
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* unknown */
2007-10-08 13:27:18 +02:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 24 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " APRG " ) ;
ffio_wfourcc ( pb , " APRG " ) ;
ffio_wfourcc ( pb , " 0001 " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 1 ) ; /* unknown */
avio_wb32 ( pb , 0 ) ; /* unknown */
2007-10-08 13:27:18 +02:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 120 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " ARES " ) ;
ffio_wfourcc ( pb , " ARES " ) ;
ffio_wfourcc ( pb , " 0001 " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , AV_RB32 ( track - > vosData + 0x28 ) ) ; /* dnxhd cid, some id ? */
avio_wb32 ( pb , track - > enc - > width ) ;
2007-10-08 13:27:18 +02:00
/* values below are based on samples created with quicktime and avid codecs */
if ( track - > vosData [ 5 ] & 2 ) { // interlaced
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , track - > enc - > height / 2 ) ;
avio_wb32 ( pb , 2 ) ; /* unknown */
avio_wb32 ( pb , 0 ) ; /* unknown */
avio_wb32 ( pb , 4 ) ; /* unknown */
2007-10-08 13:27:18 +02:00
} else {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , track - > enc - > height ) ;
avio_wb32 ( pb , 1 ) ; /* unknown */
avio_wb32 ( pb , 0 ) ; /* unknown */
2007-12-03 11:44:25 +01:00
if ( track - > enc - > height = = 1080 )
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 5 ) ; /* unknown */
2007-12-03 11:44:25 +01:00
else
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 6 ) ; /* unknown */
2007-10-08 13:27:18 +02:00
}
/* padding */
for ( i = 0 ; i < 10 ; i + + )
2011-02-21 19:28:17 +01:00
avio_wb64 ( pb , 0 ) ;
2007-10-08 13:27:18 +02:00
/* extra padding for stsd needed */
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ;
2007-10-08 13:27:18 +02:00
return 0 ;
}
2009-04-08 00:19:58 +02:00
static int mp4_get_codec_tag ( AVFormatContext * s , MOVTrack * track )
2006-03-08 01:39:23 +01:00
{
2006-05-13 20:01:16 +02:00
int tag = track - > enc - > codec_tag ;
2009-04-08 00:21:43 +02:00
2009-06-23 01:09:34 +02:00
if ( ! ff_codec_get_tag ( ff_mp4_obj_type , track - > enc - > codec_id ) )
2009-04-08 00:21:43 +02:00
return 0 ;
if ( track - > enc - > codec_id = = CODEC_ID_H264 ) tag = MKTAG ( ' a ' , ' v ' , ' c ' , ' 1 ' ) ;
else if ( track - > enc - > codec_id = = CODEC_ID_AC3 ) tag = MKTAG ( ' a ' , ' c ' , ' - ' , ' 3 ' ) ;
else if ( track - > enc - > codec_id = = CODEC_ID_DIRAC ) tag = MKTAG ( ' d ' , ' r ' , ' a ' , ' c ' ) ;
else if ( track - > enc - > codec_id = = CODEC_ID_MOV_TEXT ) tag = MKTAG ( ' t ' , ' x ' , ' 3 ' , ' g ' ) ;
2010-03-31 01:30:55 +02:00
else if ( track - > enc - > codec_type = = AVMEDIA_TYPE_VIDEO ) tag = MKTAG ( ' m ' , ' p ' , ' 4 ' , ' v ' ) ;
else if ( track - > enc - > codec_type = = AVMEDIA_TYPE_AUDIO ) tag = MKTAG ( ' m ' , ' p ' , ' 4 ' , ' a ' ) ;
2009-04-08 00:19:58 +02:00
return tag ;
}
2009-04-08 00:34:05 +02:00
static const AVCodecTag codec_ipod_tags [ ] = {
{ CODEC_ID_H264 , MKTAG ( ' a ' , ' v ' , ' c ' , ' 1 ' ) } ,
{ CODEC_ID_MPEG4 , MKTAG ( ' m ' , ' p ' , ' 4 ' , ' v ' ) } ,
{ CODEC_ID_AAC , MKTAG ( ' m ' , ' p ' , ' 4 ' , ' a ' ) } ,
{ CODEC_ID_ALAC , MKTAG ( ' a ' , ' l ' , ' a ' , ' c ' ) } ,
{ CODEC_ID_AC3 , MKTAG ( ' a ' , ' c ' , ' - ' , ' 3 ' ) } ,
{ CODEC_ID_MOV_TEXT , MKTAG ( ' t ' , ' x ' , ' 3 ' , ' g ' ) } ,
{ CODEC_ID_MOV_TEXT , MKTAG ( ' t ' , ' e ' , ' x ' , ' t ' ) } ,
{ CODEC_ID_NONE , 0 } ,
} ;
2009-04-08 00:19:58 +02:00
static int ipod_get_codec_tag ( AVFormatContext * s , MOVTrack * track )
{
int tag = track - > enc - > codec_tag ;
2009-04-08 00:26:47 +02:00
// keep original tag for subs, ipod supports both formats
2010-03-31 01:30:55 +02:00
if ( ! ( track - > enc - > codec_type = = AVMEDIA_TYPE_SUBTITLE & &
2009-04-08 00:21:43 +02:00
( tag = = MKTAG ( ' t ' , ' x ' , ' 3 ' , ' g ' ) | |
2009-04-08 00:28:39 +02:00
tag = = MKTAG ( ' t ' , ' e ' , ' x ' , ' t ' ) ) ) )
2009-06-23 01:09:34 +02:00
tag = ff_codec_get_tag ( codec_ipod_tags , track - > enc - > codec_id ) ;
2009-04-08 00:31:17 +02:00
2010-01-17 16:57:51 +01:00
if ( ! av_match_ext ( s - > filename , " m4a " ) & & ! av_match_ext ( s - > filename , " m4v " ) )
2009-04-08 00:21:43 +02:00
av_log ( s , AV_LOG_WARNING , " Warning, extension is not .m4a nor .m4v "
" Quicktime/Ipod might not play the file \n " ) ;
2009-04-08 00:19:58 +02:00
return tag ;
}
static int mov_get_dv_codec_tag ( AVFormatContext * s , MOVTrack * track )
{
int tag ;
2011-03-02 00:54:29 +01:00
if ( track - > enc - > width = = 720 ) /* SD */
if ( track - > enc - > height = = 480 ) /* NTSC */
if ( track - > enc - > pix_fmt = = PIX_FMT_YUV422P ) tag = MKTAG ( ' d ' , ' v ' , ' 5 ' , ' n ' ) ;
else tag = MKTAG ( ' d ' , ' v ' , ' c ' , ' ' ) ;
else if ( track - > enc - > pix_fmt = = PIX_FMT_YUV422P ) tag = MKTAG ( ' d ' , ' v ' , ' 5 ' , ' p ' ) ;
else if ( track - > enc - > pix_fmt = = PIX_FMT_YUV420P ) tag = MKTAG ( ' d ' , ' v ' , ' c ' , ' p ' ) ;
else tag = MKTAG ( ' d ' , ' v ' , ' p ' , ' p ' ) ;
else if ( track - > enc - > height = = 720 ) /* HD 720 line */
if ( track - > enc - > time_base . den = = 50 ) tag = MKTAG ( ' d ' , ' v ' , ' h ' , ' q ' ) ;
else tag = MKTAG ( ' d ' , ' v ' , ' h ' , ' p ' ) ;
else if ( track - > enc - > height = = 1080 ) /* HD 1080 line */
if ( track - > enc - > time_base . den = = 25 ) tag = MKTAG ( ' d ' , ' v ' , ' h ' , ' 5 ' ) ;
else tag = MKTAG ( ' d ' , ' v ' , ' h ' , ' 6 ' ) ;
else {
av_log ( s , AV_LOG_ERROR , " unsupported height for dv codec \n " ) ;
return 0 ;
}
2009-04-08 00:19:58 +02:00
return tag ;
}
2009-04-08 00:34:05 +02:00
static const struct {
enum PixelFormat pix_fmt ;
uint32_t tag ;
unsigned bps ;
} mov_pix_fmt_tags [ ] = {
{ PIX_FMT_YUYV422 , MKTAG ( ' y ' , ' u ' , ' v ' , ' s ' ) , 0 } ,
{ PIX_FMT_UYVY422 , MKTAG ( ' 2 ' , ' v ' , ' u ' , ' y ' ) , 0 } ,
2010-06-05 01:30:58 +02:00
{ PIX_FMT_RGB555BE , MKTAG ( ' r ' , ' a ' , ' w ' , ' ' ) , 16 } ,
2009-05-10 22:41:01 +02:00
{ PIX_FMT_RGB555LE , MKTAG ( ' L ' , ' 5 ' , ' 5 ' , ' 5 ' ) , 16 } ,
{ PIX_FMT_RGB565LE , MKTAG ( ' L ' , ' 5 ' , ' 6 ' , ' 5 ' ) , 16 } ,
{ PIX_FMT_RGB565BE , MKTAG ( ' B ' , ' 5 ' , ' 6 ' , ' 5 ' ) , 16 } ,
2010-06-15 11:14:00 +02:00
{ PIX_FMT_GRAY16BE , MKTAG ( ' b ' , ' 1 ' , ' 6 ' , ' g ' ) , 16 } ,
2009-04-08 00:34:05 +02:00
{ PIX_FMT_RGB24 , MKTAG ( ' r ' , ' a ' , ' w ' , ' ' ) , 24 } ,
2009-05-10 22:41:01 +02:00
{ PIX_FMT_BGR24 , MKTAG ( ' 2 ' , ' 4 ' , ' B ' , ' G ' ) , 24 } ,
2009-05-10 22:12:36 +02:00
{ PIX_FMT_ARGB , MKTAG ( ' r ' , ' a ' , ' w ' , ' ' ) , 32 } ,
2009-05-10 22:14:42 +02:00
{ PIX_FMT_BGRA , MKTAG ( ' B ' , ' G ' , ' R ' , ' A ' ) , 32 } ,
2009-05-10 22:11:11 +02:00
{ PIX_FMT_RGBA , MKTAG ( ' R ' , ' G ' , ' B ' , ' A ' ) , 32 } ,
2010-06-05 01:30:58 +02:00
{ PIX_FMT_ABGR , MKTAG ( ' A ' , ' B ' , ' G ' , ' R ' ) , 32 } ,
2010-06-15 11:14:00 +02:00
{ PIX_FMT_RGB48BE , MKTAG ( ' b ' , ' 4 ' , ' 8 ' , ' r ' ) , 48 } ,
2009-04-08 00:34:05 +02:00
} ;
2009-04-08 00:19:58 +02:00
static int mov_get_rawvideo_codec_tag ( AVFormatContext * s , MOVTrack * track )
{
int tag = track - > enc - > codec_tag ;
2009-04-08 00:21:43 +02:00
int i ;
for ( i = 0 ; i < FF_ARRAY_ELEMS ( mov_pix_fmt_tags ) ; i + + ) {
if ( track - > enc - > pix_fmt = = mov_pix_fmt_tags [ i ] . pix_fmt ) {
tag = mov_pix_fmt_tags [ i ] . tag ;
track - > enc - > bits_per_coded_sample = mov_pix_fmt_tags [ i ] . bps ;
break ;
}
}
2009-04-08 00:19:58 +02:00
return tag ;
}
static int mov_get_codec_tag ( AVFormatContext * s , MOVTrack * track )
{
int tag = track - > enc - > codec_tag ;
if ( ! tag | | ( track - > enc - > strict_std_compliance > = FF_COMPLIANCE_NORMAL & &
2011-01-07 20:56:31 +01:00
( track - > enc - > codec_id = = CODEC_ID_DVVIDEO | |
2009-04-08 00:19:58 +02:00
track - > enc - > codec_id = = CODEC_ID_RAWVIDEO | |
2011-01-07 20:55:08 +01:00
track - > enc - > codec_id = = CODEC_ID_H263 | |
2009-04-08 00:19:58 +02:00
av_get_bits_per_sample ( track - > enc - > codec_id ) ) ) ) { // pcm audio
2009-04-08 00:31:17 +02:00
if ( track - > enc - > codec_id = = CODEC_ID_DVVIDEO )
2009-04-08 00:19:58 +02:00
tag = mov_get_dv_codec_tag ( s , track ) ;
2009-04-08 00:31:17 +02:00
else if ( track - > enc - > codec_id = = CODEC_ID_RAWVIDEO )
2009-04-08 00:19:58 +02:00
tag = mov_get_rawvideo_codec_tag ( s , track ) ;
2010-03-31 01:30:55 +02:00
else if ( track - > enc - > codec_type = = AVMEDIA_TYPE_VIDEO ) {
2009-06-23 01:09:34 +02:00
tag = ff_codec_get_tag ( codec_movvideo_tags , track - > enc - > codec_id ) ;
2009-04-08 00:31:53 +02:00
if ( ! tag ) { // if no mac fcc found, try with Microsoft tags
2009-06-23 01:09:34 +02:00
tag = ff_codec_get_tag ( ff_codec_bmp_tags , track - > enc - > codec_id ) ;
2009-04-08 00:31:53 +02:00
if ( tag )
2011-08-11 16:33:03 +02:00
av_log ( s , AV_LOG_WARNING , " Using MS style video codec tag, "
2009-04-08 00:31:53 +02:00
" the file may be unplayable! \n " ) ;
}
2010-03-31 01:30:55 +02:00
} else if ( track - > enc - > codec_type = = AVMEDIA_TYPE_AUDIO ) {
2009-06-23 01:09:34 +02:00
tag = ff_codec_get_tag ( codec_movaudio_tags , track - > enc - > codec_id ) ;
2009-04-08 00:31:53 +02:00
if ( ! tag ) { // if no mac fcc found, try with Microsoft tags
2009-06-23 01:09:34 +02:00
int ms_tag = ff_codec_get_tag ( ff_codec_wav_tags , track - > enc - > codec_id ) ;
2009-04-08 00:31:53 +02:00
if ( ms_tag ) {
tag = MKTAG ( ' m ' , ' s ' , ( ( ms_tag > > 8 ) & 0xff ) , ( ms_tag & 0xff ) ) ;
2011-08-11 16:33:03 +02:00
av_log ( s , AV_LOG_WARNING , " Using MS style audio codec tag, "
2009-04-08 00:31:53 +02:00
" the file may be unplayable! \n " ) ;
2008-01-28 16:23:37 +01:00
}
2009-04-08 00:31:53 +02:00
}
2010-03-31 01:30:55 +02:00
} else if ( track - > enc - > codec_type = = AVMEDIA_TYPE_SUBTITLE )
2009-06-23 01:09:34 +02:00
tag = ff_codec_get_tag ( ff_codec_movsubtitle_tags , track - > enc - > codec_id ) ;
2008-01-28 16:22:07 +01:00
}
2009-04-08 00:21:43 +02:00
2006-03-08 01:39:23 +01:00
return tag ;
}
2009-04-08 00:34:05 +02:00
static const AVCodecTag codec_3gp_tags [ ] = {
{ CODEC_ID_H263 , MKTAG ( ' s ' , ' 2 ' , ' 6 ' , ' 3 ' ) } ,
{ CODEC_ID_H264 , MKTAG ( ' a ' , ' v ' , ' c ' , ' 1 ' ) } ,
{ CODEC_ID_MPEG4 , MKTAG ( ' m ' , ' p ' , ' 4 ' , ' v ' ) } ,
{ CODEC_ID_AAC , MKTAG ( ' m ' , ' p ' , ' 4 ' , ' a ' ) } ,
{ CODEC_ID_AMR_NB , MKTAG ( ' s ' , ' a ' , ' m ' , ' r ' ) } ,
{ CODEC_ID_AMR_WB , MKTAG ( ' s ' , ' a ' , ' w ' , ' b ' ) } ,
{ CODEC_ID_MOV_TEXT , MKTAG ( ' t ' , ' x ' , ' 3 ' , ' g ' ) } ,
{ CODEC_ID_NONE , 0 } ,
} ;
2009-04-08 00:19:58 +02:00
static int mov_find_codec_tag ( AVFormatContext * s , MOVTrack * track )
{
int tag = track - > enc - > codec_tag ;
2012-01-09 16:58:26 +01:00
if ( track - > mode = = MODE_MP4 | | track - > mode = = MODE_PSP | | track - > mode = = MODE_ISM )
2009-04-08 00:19:58 +02:00
tag = mp4_get_codec_tag ( s , track ) ;
else if ( track - > mode = = MODE_IPOD )
tag = ipod_get_codec_tag ( s , track ) ;
else if ( track - > mode & MODE_3GP )
2009-06-23 01:09:34 +02:00
tag = ff_codec_get_tag ( codec_3gp_tags , track - > enc - > codec_id ) ;
2009-04-08 00:19:58 +02:00
else
tag = mov_get_codec_tag ( s , track ) ;
return tag ;
}
2008-03-16 14:36:36 +01:00
/** Write uuid atom.
* Needed to make file play in iPods running newest firmware
* goes after avcC atom in moov . trak . mdia . minf . stbl . stsd . avc1
*/
2011-02-20 11:04:12 +01:00
static int mov_write_uuid_tag_ipod ( AVIOContext * pb )
2008-03-16 14:36:36 +01:00
{
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 28 ) ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " uuid " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x6b6840f2 ) ;
avio_wb32 ( pb , 0x5f244fc5 ) ;
avio_wb32 ( pb , 0xba39a51b ) ;
avio_wb32 ( pb , 0xcf0323f3 ) ;
avio_wb32 ( pb , 0x0 ) ;
2008-03-16 14:36:36 +01:00
return 28 ;
}
2011-12-14 03:49:06 +01:00
static const uint16_t fiel_data [ ] = {
0x0000 , 0x0100 , 0x0201 , 0x0206 , 0x0209 , 0x020e
} ;
static int mov_write_fiel_tag ( AVIOContext * pb , MOVTrack * track )
{
unsigned mov_field_order = 0 ;
if ( track - > enc - > field_order < FF_ARRAY_ELEMS ( fiel_data ) )
mov_field_order = fiel_data [ track - > enc - > field_order ] ;
else
return 0 ;
avio_wb32 ( pb , 10 ) ;
ffio_wfourcc ( pb , " fiel " ) ;
avio_wb16 ( pb , mov_field_order ) ;
return 10 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_subtitle_tag ( AVIOContext * pb , MOVTrack * track )
2009-01-11 11:26:44 +01:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
avio_wl32 ( pb , track - > tag ) ; // store it byteswapped
avio_wb32 ( pb , 0 ) ; /* Reserved */
avio_wb16 ( pb , 0 ) ; /* Reserved */
avio_wb16 ( pb , 1 ) ; /* Data-reference index */
2009-01-11 11:26:44 +01:00
if ( track - > enc - > extradata_size )
2011-02-21 19:28:17 +01:00
avio_write ( pb , track - > enc - > extradata , track - > enc - > extradata_size ) ;
2009-01-11 11:26:44 +01:00
return updateSize ( pb , pos ) ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_pasp_tag ( AVIOContext * pb , MOVTrack * track )
2010-07-08 23:57:20 +02:00
{
AVRational sar ;
av_reduce ( & sar . num , & sar . den , track - > enc - > sample_aspect_ratio . num ,
track - > enc - > sample_aspect_ratio . den , INT_MAX ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 16 ) ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " pasp " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , sar . num ) ;
avio_wb32 ( pb , sar . den ) ;
2010-07-08 23:57:20 +02:00
return 16 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_video_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 22:23:13 +02:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2005-01-26 10:31:49 +01:00
char compressor_name [ 32 ] ;
2004-04-07 14:47:33 +02:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
avio_wl32 ( pb , track - > tag ) ; // store it byteswapped
avio_wb32 ( pb , 0 ) ; /* Reserved */
avio_wb16 ( pb , 0 ) ; /* Reserved */
avio_wb16 ( pb , 1 ) ; /* Data-reference index */
2003-09-28 23:09:32 +02:00
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 0 ) ; /* Codec stream version */
avio_wb16 ( pb , 0 ) ; /* Codec stream revision (=0) */
2006-08-07 16:18:43 +02:00
if ( track - > mode = = MODE_MOV ) {
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " FFMP " ) ; /* Vendor */
2006-08-07 16:19:33 +02:00
if ( track - > enc - > codec_id = = CODEC_ID_RAWVIDEO ) {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* Temporal Quality */
avio_wb32 ( pb , 0x400 ) ; /* Spatial Quality = lossless*/
2006-08-07 16:19:33 +02:00
} else {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x200 ) ; /* Temporal Quality = normal */
avio_wb32 ( pb , 0x200 ) ; /* Spatial Quality = normal */
2006-08-07 16:19:33 +02:00
}
2006-08-07 16:18:43 +02:00
} else {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* Reserved */
avio_wb32 ( pb , 0 ) ; /* Reserved */
avio_wb32 ( pb , 0 ) ; /* Reserved */
2006-08-07 16:18:43 +02:00
}
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , track - > enc - > width ) ; /* Video width */
avio_wb16 ( pb , track - > height ) ; /* Video height */
avio_wb32 ( pb , 0x00480000 ) ; /* Horizontal resolution 72dpi */
avio_wb32 ( pb , 0x00480000 ) ; /* Vertical resolution 72dpi */
avio_wb32 ( pb , 0 ) ; /* Data size (= 0) */
avio_wb16 ( pb , 1 ) ; /* Frame count (= 1) */
2005-12-17 19:14:38 +01:00
2005-01-26 10:31:49 +01:00
memset ( compressor_name , 0 , 32 ) ;
2006-08-07 16:18:43 +02:00
/* FIXME not sure, ISO 14496-1 draft where it shall be set to 0 */
if ( track - > mode = = MODE_MOV & & track - > enc - > codec & & track - > enc - > codec - > name )
2011-05-03 20:19:31 +02:00
av_strlcpy ( compressor_name , track - > enc - > codec - > name , 32 ) ;
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , strlen ( compressor_name ) ) ;
avio_write ( pb , compressor_name , 31 ) ;
2005-12-17 19:14:38 +01:00
2008-09-08 16:24:59 +02:00
if ( track - > mode = = MODE_MOV & & track - > enc - > bits_per_coded_sample )
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , track - > enc - > bits_per_coded_sample ) ;
2008-01-31 12:31:56 +01:00
else
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 0x18 ) ; /* Reserved */
avio_wb16 ( pb , 0xffff ) ; /* Reserved */
2008-01-28 14:44:27 +01:00
if ( track - > tag = = MKTAG ( ' m ' , ' p ' , ' 4 ' , ' v ' ) )
2003-09-28 23:09:32 +02:00
mov_write_esds_tag ( pb , track ) ;
else if ( track - > enc - > codec_id = = CODEC_ID_H263 )
mov_write_d263_tag ( pb ) ;
else if ( track - > enc - > codec_id = = CODEC_ID_SVQ3 )
2005-12-17 19:14:38 +01:00
mov_write_svq3_tag ( pb ) ;
2008-03-20 19:42:44 +01:00
else if ( track - > enc - > codec_id = = CODEC_ID_DNXHD )
mov_write_avid_tag ( pb , track ) ;
2008-03-16 14:36:36 +01:00
else if ( track - > enc - > codec_id = = CODEC_ID_H264 ) {
2006-03-11 19:18:17 +01:00
mov_write_avcc_tag ( pb , track ) ;
2008-03-16 14:36:36 +01:00
if ( track - > mode = = MODE_IPOD )
mov_write_uuid_tag_ipod ( pb ) ;
2011-12-14 03:49:06 +01:00
} else if ( track - > enc - > field_order ! = AV_FIELD_UNKNOWN )
mov_write_fiel_tag ( pb , track ) ;
else if ( track - > vosLen > 0 )
2007-12-19 17:00:08 +01:00
mov_write_glbl_tag ( pb , track ) ;
2003-09-28 23:09:32 +02:00
2011-02-15 11:44:08 +01:00
if ( track - > enc - > sample_aspect_ratio . den & & track - > enc - > sample_aspect_ratio . num & &
2010-07-08 23:57:20 +02:00
track - > enc - > sample_aspect_ratio . den ! = track - > enc - > sample_aspect_ratio . num ) {
mov_write_pasp_tag ( pb , track ) ;
}
2008-06-11 10:50:41 +02:00
return updateSize ( pb , pos ) ;
2003-08-26 22:23:13 +02:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_rtp_tag ( AVIOContext * pb , MOVTrack * track )
2010-05-18 21:47:24 +02:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " rtp " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* Reserved */
avio_wb16 ( pb , 0 ) ; /* Reserved */
avio_wb16 ( pb , 1 ) ; /* Data-reference index */
2010-05-18 21:47:24 +02:00
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 1 ) ; /* Hint track version */
avio_wb16 ( pb , 1 ) ; /* Highest compatible version */
avio_wb32 ( pb , track - > max_packet_size ) ; /* Max packet size */
2010-05-18 21:47:24 +02:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 12 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " tims " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , track - > timescale ) ;
2010-05-18 21:47:24 +02:00
return updateSize ( pb , pos ) ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_stsd_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 22:23:13 +02:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " stsd " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* version & flags */
avio_wb32 ( pb , 1 ) ; /* entry count */
2010-03-31 01:30:55 +02:00
if ( track - > enc - > codec_type = = AVMEDIA_TYPE_VIDEO )
2003-09-28 23:09:32 +02:00
mov_write_video_tag ( pb , track ) ;
2010-03-31 01:30:55 +02:00
else if ( track - > enc - > codec_type = = AVMEDIA_TYPE_AUDIO )
2003-09-28 23:09:32 +02:00
mov_write_audio_tag ( pb , track ) ;
2010-03-31 01:30:55 +02:00
else if ( track - > enc - > codec_type = = AVMEDIA_TYPE_SUBTITLE )
2009-01-11 11:26:44 +01:00
mov_write_subtitle_tag ( pb , track ) ;
2010-05-18 21:47:24 +02:00
else if ( track - > enc - > codec_tag = = MKTAG ( ' r ' , ' t ' , ' p ' , ' ' ) )
mov_write_rtp_tag ( pb , track ) ;
2003-09-10 01:03:04 +02:00
return updateSize ( pb , pos ) ;
2003-08-26 22:23:13 +02:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_ctts_tag ( AVIOContext * pb , MOVTrack * track )
2006-02-23 00:46:20 +01:00
{
2008-12-11 21:06:56 +01:00
MOVStts * ctts_entries ;
2006-02-23 00:46:20 +01:00
uint32_t entries = 0 ;
uint32_t atom_size ;
int i ;
ctts_entries = av_malloc ( ( track - > entry + 1 ) * sizeof ( * ctts_entries ) ) ; /* worst case */
ctts_entries [ 0 ] . count = 1 ;
2006-06-24 20:09:20 +02:00
ctts_entries [ 0 ] . duration = track - > cluster [ 0 ] . cts ;
2006-02-23 00:46:20 +01:00
for ( i = 1 ; i < track - > entry ; i + + ) {
2006-06-24 20:09:20 +02:00
if ( track - > cluster [ i ] . cts = = ctts_entries [ entries ] . duration ) {
2006-02-23 00:46:20 +01:00
ctts_entries [ entries ] . count + + ; /* compress */
} else {
entries + + ;
2006-06-24 20:09:20 +02:00
ctts_entries [ entries ] . duration = track - > cluster [ i ] . cts ;
2006-02-23 00:46:20 +01:00
ctts_entries [ entries ] . count = 1 ;
}
}
entries + + ; /* last one */
atom_size = 16 + ( entries * 8 ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , atom_size ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " ctts " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* version & flags */
avio_wb32 ( pb , entries ) ; /* entry count */
2006-02-23 00:46:20 +01:00
for ( i = 0 ; i < entries ; i + + ) {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , ctts_entries [ i ] . count ) ;
avio_wb32 ( pb , ctts_entries [ i ] . duration ) ;
2006-02-23 00:46:20 +01:00
}
av_free ( ctts_entries ) ;
return atom_size ;
}
2003-11-03 22:51:07 +01:00
/* Time to sample atom */
2011-02-20 11:04:12 +01:00
static int mov_write_stts_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 22:23:13 +02:00
{
2008-12-11 21:06:56 +01:00
MOVStts * stts_entries ;
2006-11-01 18:27:39 +01:00
uint32_t entries = - 1 ;
uint32_t atom_size ;
int i ;
2010-03-31 01:30:55 +02:00
if ( track - > enc - > codec_type = = AVMEDIA_TYPE_AUDIO & & ! track - > audio_vbr ) {
2006-11-01 18:27:39 +01:00
stts_entries = av_malloc ( sizeof ( * stts_entries ) ) ; /* one entry */
stts_entries [ 0 ] . count = track - > sampleCount ;
stts_entries [ 0 ] . duration = 1 ;
entries = 1 ;
} else {
2012-01-05 12:57:05 +01:00
stts_entries = track - > entry ?
av_malloc ( track - > entry * sizeof ( * stts_entries ) ) : /* worst case */
NULL ;
2006-11-01 18:27:39 +01:00
for ( i = 0 ; i < track - > entry ; i + + ) {
int64_t duration = i + 1 = = track - > entry ?
2012-01-07 00:32:53 +01:00
track - > trackDuration - track - > cluster [ i ] . dts + track - > start_dts : /* readjusting */
2006-11-01 18:27:39 +01:00
track - > cluster [ i + 1 ] . dts - track - > cluster [ i ] . dts ;
if ( i & & duration = = stts_entries [ entries ] . duration ) {
stts_entries [ entries ] . count + + ; /* compress */
} else {
entries + + ;
stts_entries [ entries ] . duration = duration ;
stts_entries [ entries ] . count = 1 ;
}
}
entries + + ; /* last one */
}
atom_size = 16 + ( entries * 8 ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , atom_size ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " stts " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* version & flags */
avio_wb32 ( pb , entries ) ; /* entry count */
2006-11-01 18:27:39 +01:00
for ( i = 0 ; i < entries ; i + + ) {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , stts_entries [ i ] . count ) ;
avio_wb32 ( pb , stts_entries [ i ] . duration ) ;
2006-11-01 18:27:39 +01:00
}
av_free ( stts_entries ) ;
return atom_size ;
2003-08-26 22:23:13 +02:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_dref_tag ( AVIOContext * pb )
2003-08-26 22:23:13 +02:00
{
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 28 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " dref " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* version & flags */
avio_wb32 ( pb , 1 ) ; /* entry count */
2003-08-26 22:23:13 +02:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0xc ) ; /* size */
2012-01-14 14:10:19 +01:00
//FIXME add the alis and rsrc atom
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " url " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 1 ) ; /* version & flags */
2003-08-26 22:23:13 +02:00
return 28 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_stbl_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 22:23:13 +02:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " stbl " ) ;
2003-09-10 01:03:04 +02:00
mov_write_stsd_tag ( pb , track ) ;
mov_write_stts_tag ( pb , track ) ;
2010-05-18 21:47:24 +02:00
if ( ( track - > enc - > codec_type = = AVMEDIA_TYPE_VIDEO | |
track - > enc - > codec_tag = = MKTAG ( ' r ' , ' t ' , ' p ' , ' ' ) ) & &
2008-03-30 23:18:22 +02:00
track - > hasKeyframes & & track - > hasKeyframes < track - > entry )
2009-05-15 08:11:53 +02:00
mov_write_stss_tag ( pb , track , MOV_SYNC_SAMPLE ) ;
if ( track - > mode = = MODE_MOV & & track - > flags & MOV_TRACK_STPS )
mov_write_stss_tag ( pb , track , MOV_PARTIAL_SYNC_SAMPLE ) ;
2010-03-31 01:30:55 +02:00
if ( track - > enc - > codec_type = = AVMEDIA_TYPE_VIDEO & &
2009-05-15 08:11:53 +02:00
track - > flags & MOV_TRACK_CTTS )
2006-02-23 00:46:20 +01:00
mov_write_ctts_tag ( pb , track ) ;
2003-09-10 01:03:04 +02:00
mov_write_stsc_tag ( pb , track ) ;
mov_write_stsz_tag ( pb , track ) ;
mov_write_stco_tag ( pb , track ) ;
return updateSize ( pb , pos ) ;
2003-08-26 22:23:13 +02:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_dinf_tag ( AVIOContext * pb )
2003-08-26 22:23:13 +02:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " dinf " ) ;
2003-09-10 01:03:04 +02:00
mov_write_dref_tag ( pb ) ;
return updateSize ( pb , pos ) ;
2003-08-26 22:23:13 +02:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_nmhd_tag ( AVIOContext * pb )
2009-01-11 11:26:44 +01:00
{
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 12 ) ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " nmhd " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ;
2009-01-11 11:26:44 +01:00
return 12 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_gmhd_tag ( AVIOContext * pb )
2009-01-11 11:26:44 +01:00
{
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x20 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " gmhd " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x18 ) ; /* gmin size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " gmin " ) ; /* generic media info */
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* version & flags */
avio_wb16 ( pb , 0x40 ) ; /* graphics mode = */
avio_wb16 ( pb , 0x8000 ) ; /* opColor (r?) */
avio_wb16 ( pb , 0x8000 ) ; /* opColor (g?) */
avio_wb16 ( pb , 0x8000 ) ; /* opColor (b?) */
avio_wb16 ( pb , 0 ) ; /* balance */
avio_wb16 ( pb , 0 ) ; /* reserved */
2009-01-11 11:26:44 +01:00
return 0x20 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_smhd_tag ( AVIOContext * pb )
2003-08-26 22:23:13 +02:00
{
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 16 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " smhd " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* version & flags */
avio_wb16 ( pb , 0 ) ; /* reserved (balance, normally = 0) */
avio_wb16 ( pb , 0 ) ; /* reserved */
2003-08-26 22:23:13 +02:00
return 16 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_vmhd_tag ( AVIOContext * pb )
2003-08-26 22:23:13 +02:00
{
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x14 ) ; /* size (always 0x14) */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " vmhd " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x01 ) ; /* version & flags */
avio_wb64 ( pb , 0 ) ; /* reserved (graphics mode = copy) */
2003-08-26 22:23:13 +02:00
return 0x14 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_hdlr_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 22:23:13 +02:00
{
2009-01-11 11:41:43 +01:00
const char * hdlr , * descr = NULL , * hdlr_type = NULL ;
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2005-12-17 19:14:38 +01:00
2004-03-28 04:17:06 +02:00
if ( ! track ) { /* no media --> data handler */
2005-12-22 02:10:11 +01:00
hdlr = " dhlr " ;
hdlr_type = " url " ;
descr = " DataHandler " ;
2004-03-28 04:17:06 +02:00
} else {
2005-12-22 02:10:11 +01:00
hdlr = ( track - > mode = = MODE_MOV ) ? " mhlr " : " \0 \0 \0 \0 " ;
2010-03-31 01:30:55 +02:00
if ( track - > enc - > codec_type = = AVMEDIA_TYPE_VIDEO ) {
2005-12-22 02:10:11 +01:00
hdlr_type = " vide " ;
descr = " VideoHandler " ;
2010-03-31 01:30:55 +02:00
} else if ( track - > enc - > codec_type = = AVMEDIA_TYPE_AUDIO ) {
2005-12-22 02:10:11 +01:00
hdlr_type = " soun " ;
descr = " SoundHandler " ;
2010-03-31 01:30:55 +02:00
} else if ( track - > enc - > codec_type = = AVMEDIA_TYPE_SUBTITLE ) {
2009-03-11 09:01:39 +01:00
if ( track - > tag = = MKTAG ( ' t ' , ' x ' , ' 3 ' , ' g ' ) ) hdlr_type = " sbtl " ;
2009-03-11 09:02:59 +01:00
else hdlr_type = " text " ;
2009-01-11 11:26:44 +01:00
descr = " SubtitleHandler " ;
2010-05-18 21:47:24 +02:00
} else if ( track - > enc - > codec_tag = = MKTAG ( ' r ' , ' t ' , ' p ' , ' ' ) ) {
hdlr_type = " hint " ;
descr = " HintHandler " ;
2005-12-22 02:10:11 +01:00
}
2004-03-28 04:17:06 +02:00
}
2005-12-17 19:14:38 +01:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " hdlr " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* Version & flags */
avio_write ( pb , hdlr , 4 ) ; /* handler */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , hdlr_type ) ; /* handler type */
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* reserved */
avio_wb32 ( pb , 0 ) ; /* reserved */
avio_wb32 ( pb , 0 ) ; /* reserved */
2009-05-23 09:17:17 +02:00
if ( ! track | | track - > mode = = MODE_MOV )
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , strlen ( descr ) ) ; /* pascal string */
avio_write ( pb , descr , strlen ( descr ) ) ; /* handler description */
2009-05-23 09:17:17 +02:00
if ( track & & track - > mode ! = MODE_MOV )
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0 ) ; /* c string */
2004-03-28 04:17:06 +02:00
return updateSize ( pb , pos ) ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_hmhd_tag ( AVIOContext * pb )
2010-05-18 21:47:24 +02:00
{
/* This atom must be present, but leaving the values at zero
* seems harmless . */
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 28 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " hmhd " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* version, flags */
avio_wb16 ( pb , 0 ) ; /* maxPDUsize */
avio_wb16 ( pb , 0 ) ; /* avgPDUsize */
avio_wb32 ( pb , 0 ) ; /* maxbitrate */
avio_wb32 ( pb , 0 ) ; /* avgbitrate */
avio_wb32 ( pb , 0 ) ; /* reserved */
2010-05-18 21:47:24 +02:00
return 28 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_minf_tag ( AVIOContext * pb , MOVTrack * track )
2004-03-28 04:17:06 +02:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " minf " ) ;
2010-03-31 01:30:55 +02:00
if ( track - > enc - > codec_type = = AVMEDIA_TYPE_VIDEO )
2004-03-28 04:17:06 +02:00
mov_write_vmhd_tag ( pb ) ;
2010-03-31 01:30:55 +02:00
else if ( track - > enc - > codec_type = = AVMEDIA_TYPE_AUDIO )
2004-03-28 04:17:06 +02:00
mov_write_smhd_tag ( pb ) ;
2010-03-31 01:30:55 +02:00
else if ( track - > enc - > codec_type = = AVMEDIA_TYPE_SUBTITLE ) {
2009-03-11 09:01:39 +01:00
if ( track - > tag = = MKTAG ( ' t ' , ' e ' , ' x ' , ' t ' ) ) mov_write_gmhd_tag ( pb ) ;
2009-03-11 09:02:59 +01:00
else mov_write_nmhd_tag ( pb ) ;
2010-05-18 21:47:24 +02:00
} else if ( track - > tag = = MKTAG ( ' r ' , ' t ' , ' p ' , ' ' ) ) {
mov_write_hmhd_tag ( pb ) ;
2009-01-11 11:26:44 +01:00
}
2004-03-28 04:17:06 +02:00
if ( track - > mode = = MODE_MOV ) /* FIXME: Why do it for MODE_MOV only ? */
mov_write_hdlr_tag ( pb , NULL ) ;
mov_write_dinf_tag ( pb ) ;
mov_write_stbl_tag ( pb , track ) ;
2003-09-28 23:09:32 +02:00
return updateSize ( pb , pos ) ;
2003-08-26 22:23:13 +02:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_mdhd_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 22:23:13 +02:00
{
2006-03-26 15:34:51 +02:00
int version = track - > trackDuration < INT32_MAX ? 0 : 1 ;
2011-02-21 19:28:17 +01:00
( version = = 1 ) ? avio_wb32 ( pb , 44 ) : avio_wb32 ( pb , 32 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " mdhd " ) ;
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , version ) ;
avio_wb24 ( pb , 0 ) ; /* flags */
2006-03-26 15:34:51 +02:00
if ( version = = 1 ) {
2011-02-21 19:28:17 +01:00
avio_wb64 ( pb , track - > time ) ;
avio_wb64 ( pb , track - > time ) ;
2006-03-26 15:34:51 +02:00
} else {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , track - > time ) ; /* creation time */
avio_wb32 ( pb , track - > time ) ; /* modification time */
2006-03-26 15:34:51 +02:00
}
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , track - > timescale ) ; /* time scale (sample rate for audio) */
( version = = 1 ) ? avio_wb64 ( pb , track - > trackDuration ) : avio_wb32 ( pb , track - > trackDuration ) ; /* duration */
avio_wb16 ( pb , track - > language ) ; /* language */
avio_wb16 ( pb , 0 ) ; /* reserved (quality) */
2007-05-30 02:08:32 +02:00
if ( version ! = 0 & & track - > mode = = MODE_MOV ) {
av_log ( NULL , AV_LOG_ERROR ,
" FATAL error, file duration too long for timebase, this file will not be \n "
2007-05-30 12:04:37 +02:00
" playable with quicktime. Choose a different timebase or a different \n "
2007-05-30 02:08:32 +02:00
" container format \n " ) ;
}
2003-08-26 22:23:13 +02:00
return 32 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_mdia_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 22:23:13 +02:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " mdia " ) ;
2003-09-10 01:03:04 +02:00
mov_write_mdhd_tag ( pb , track ) ;
mov_write_hdlr_tag ( pb , track ) ;
mov_write_minf_tag ( pb , track ) ;
return updateSize ( pb , pos ) ;
2003-08-26 22:23:13 +02:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_tkhd_tag ( AVIOContext * pb , MOVTrack * track , AVStream * st )
2003-08-26 22:23:13 +02:00
{
2009-11-29 03:46:49 +01:00
int64_t duration = av_rescale_rnd ( track - > trackDuration , MOV_TIMESCALE ,
track - > timescale , AV_ROUND_UP ) ;
2006-03-26 15:34:51 +02:00
int version = duration < INT32_MAX ? 0 : 1 ;
2011-02-21 19:28:17 +01:00
( version = = 1 ) ? avio_wb32 ( pb , 104 ) : avio_wb32 ( pb , 92 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " tkhd " ) ;
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , version ) ;
avio_wb24 ( pb , 0xf ) ; /* flags (track enabled) */
2006-03-26 15:34:51 +02:00
if ( version = = 1 ) {
2011-02-21 19:28:17 +01:00
avio_wb64 ( pb , track - > time ) ;
avio_wb64 ( pb , track - > time ) ;
2006-03-26 15:34:51 +02:00
} else {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , track - > time ) ; /* creation time */
avio_wb32 ( pb , track - > time ) ; /* modification time */
2006-03-26 15:34:51 +02:00
}
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , track - > trackID ) ; /* track-id */
avio_wb32 ( pb , 0 ) ; /* reserved */
( version = = 1 ) ? avio_wb64 ( pb , duration ) : avio_wb32 ( pb , duration ) ;
2003-08-26 22:23:13 +02:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* reserved */
avio_wb32 ( pb , 0 ) ; /* reserved */
2011-09-16 16:06:45 +02:00
avio_wb16 ( pb , 0 ) ; /* layer */
2011-09-19 15:15:56 +02:00
avio_wb16 ( pb , st ? st - > codec - > codec_type : 0 ) ; /* alternate group) */
2003-08-26 22:23:13 +02:00
/* Volume, only for audio */
2010-03-31 01:30:55 +02:00
if ( track - > enc - > codec_type = = AVMEDIA_TYPE_AUDIO )
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 0x0100 ) ;
2003-08-26 22:23:13 +02:00
else
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 0 ) ;
avio_wb16 ( pb , 0 ) ; /* reserved */
2003-08-26 22:23:13 +02:00
/* Matrix structure */
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x00010000 ) ; /* reserved */
avio_wb32 ( pb , 0x0 ) ; /* reserved */
avio_wb32 ( pb , 0x0 ) ; /* reserved */
avio_wb32 ( pb , 0x0 ) ; /* reserved */
avio_wb32 ( pb , 0x00010000 ) ; /* reserved */
avio_wb32 ( pb , 0x0 ) ; /* reserved */
avio_wb32 ( pb , 0x0 ) ; /* reserved */
avio_wb32 ( pb , 0x0 ) ; /* reserved */
avio_wb32 ( pb , 0x40000000 ) ; /* reserved */
2003-08-26 22:23:13 +02:00
/* Track width and height, for visual only */
2010-05-05 10:41:10 +02:00
if ( st & & ( track - > enc - > codec_type = = AVMEDIA_TYPE_VIDEO | |
track - > enc - > codec_type = = AVMEDIA_TYPE_SUBTITLE ) ) {
2011-02-25 16:41:55 +01:00
if ( track - > mode = = MODE_MOV ) {
avio_wb32 ( pb , track - > enc - > width < < 16 ) ;
2011-03-02 00:36:48 +01:00
avio_wb32 ( pb , track - > height < < 16 ) ;
2011-02-25 16:41:55 +01:00
} else {
2011-02-25 16:42:56 +01:00
double sample_aspect_ratio = av_q2d ( st - > sample_aspect_ratio ) ;
if ( ! sample_aspect_ratio | | track - > height ! = track - > enc - > height )
sample_aspect_ratio = 1 ;
avio_wb32 ( pb , sample_aspect_ratio * track - > enc - > width * 0x10000 ) ;
avio_wb32 ( pb , track - > height * 0x10000 ) ;
2011-02-25 16:41:55 +01:00
}
2003-08-26 22:23:13 +02:00
}
else {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ;
avio_wb32 ( pb , 0 ) ;
2003-08-26 22:23:13 +02:00
}
return 0x5c ;
}
2011-02-25 23:41:00 +01:00
static int mov_write_tapt_tag ( AVIOContext * pb , MOVTrack * track )
2011-02-25 16:41:55 +01:00
{
int32_t width = av_rescale ( track - > enc - > sample_aspect_ratio . num , track - > enc - > width ,
track - > enc - > sample_aspect_ratio . den ) ;
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-25 16:41:55 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-25 23:41:00 +01:00
ffio_wfourcc ( pb , " tapt " ) ;
2011-02-25 16:41:55 +01:00
avio_wb32 ( pb , 20 ) ;
2011-02-25 23:41:00 +01:00
ffio_wfourcc ( pb , " clef " ) ;
2011-02-25 16:41:55 +01:00
avio_wb32 ( pb , 0 ) ;
avio_wb32 ( pb , width < < 16 ) ;
avio_wb32 ( pb , track - > enc - > height < < 16 ) ;
avio_wb32 ( pb , 20 ) ;
2011-02-25 23:41:00 +01:00
ffio_wfourcc ( pb , " enof " ) ;
2011-02-25 16:41:55 +01:00
avio_wb32 ( pb , 0 ) ;
avio_wb32 ( pb , track - > enc - > width < < 16 ) ;
avio_wb32 ( pb , track - > enc - > height < < 16 ) ;
return updateSize ( pb , pos ) ;
2011-12-11 18:07:04 +01:00
}
2011-02-25 16:41:55 +01:00
2005-01-27 15:48:15 +01:00
// This box seems important for the psp playback ... without it the movie seems to hang
2011-02-20 11:04:12 +01:00
static int mov_write_edts_tag ( AVIOContext * pb , MOVTrack * track )
2005-01-27 15:48:15 +01:00
{
2011-05-02 19:25:28 +02:00
int64_t duration = av_rescale_rnd ( track - > trackDuration , MOV_TIMESCALE ,
track - > timescale , AV_ROUND_UP ) ;
int version = duration < INT32_MAX ? 0 : 1 ;
int entry_size , entry_count , size ;
int64_t delay , start_ct = track - > cluster [ 0 ] . cts ;
delay = av_rescale_rnd ( track - > cluster [ 0 ] . dts + start_ct , MOV_TIMESCALE ,
track - > timescale , AV_ROUND_DOWN ) ;
version | = delay < INT32_MAX ? 0 : 1 ;
entry_size = ( version = = 1 ) ? 20 : 12 ;
entry_count = 1 + ( delay > 0 ) ;
size = 24 + entry_count * entry_size ;
/* write the atom data */
avio_wb32 ( pb , size ) ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " edts " ) ;
2011-05-02 19:25:28 +02:00
avio_wb32 ( pb , size - 8 ) ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " elst " ) ;
2011-05-02 19:25:28 +02:00
avio_w8 ( pb , version ) ;
avio_wb24 ( pb , 0 ) ; /* flags */
2005-01-27 15:48:15 +01:00
2011-05-02 19:25:28 +02:00
avio_wb32 ( pb , entry_count ) ;
if ( delay > 0 ) { /* add an empty edit to delay presentation */
if ( version = = 1 ) {
avio_wb64 ( pb , delay ) ;
avio_wb64 ( pb , - 1 ) ;
} else {
avio_wb32 ( pb , delay ) ;
avio_wb32 ( pb , - 1 ) ;
}
avio_wb32 ( pb , 0x00010000 ) ;
}
2005-01-27 15:48:15 +01:00
2011-05-02 19:25:28 +02:00
/* duration */
if ( version = = 1 ) {
avio_wb64 ( pb , duration ) ;
avio_wb64 ( pb , start_ct ) ;
} else {
avio_wb32 ( pb , duration ) ;
avio_wb32 ( pb , start_ct ) ;
}
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x00010000 ) ;
2011-05-02 19:25:28 +02:00
return size ;
2005-01-27 15:48:15 +01:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_tref_tag ( AVIOContext * pb , MOVTrack * track )
2010-05-05 10:41:10 +02:00
{
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 20 ) ; // size
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " tref " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 12 ) ; // size (subatom)
avio_wl32 ( pb , track - > tref_tag ) ;
avio_wb32 ( pb , track - > tref_id ) ;
2010-05-05 10:41:10 +02:00
return 20 ;
}
2005-01-27 15:48:15 +01:00
// goes at the end of each track! ... Critical for PSP playback ("Incompatible data" without it)
2011-02-20 11:04:12 +01:00
static int mov_write_uuid_tag_psp ( AVIOContext * pb , MOVTrack * mov )
2005-01-27 15:48:15 +01:00
{
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x34 ) ; /* size ... reports as 28 in mp4box! */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " uuid " ) ;
ffio_wfourcc ( pb , " USMT " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x21d24fce ) ;
avio_wb32 ( pb , 0xbb88695c ) ;
avio_wb32 ( pb , 0xfac9c740 ) ;
avio_wb32 ( pb , 0x1c ) ; // another size here!
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " MTDT " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x00010012 ) ;
avio_wb32 ( pb , 0x0a ) ;
avio_wb32 ( pb , 0x55c40000 ) ;
avio_wb32 ( pb , 0x1 ) ;
avio_wb32 ( pb , 0x0 ) ;
2005-01-27 15:48:15 +01:00
return 0x34 ;
}
2011-05-18 15:21:47 +02:00
static int mov_write_udta_sdp ( AVIOContext * pb , AVFormatContext * ctx , int index )
2010-05-18 21:47:24 +02:00
{
char buf [ 1000 ] = " " ;
int len ;
2011-05-18 14:41:38 +02:00
ff_sdp_write_media ( buf , sizeof ( buf ) , ctx - > streams [ 0 ] - > codec , NULL , NULL , 0 , 0 , ctx ) ;
2010-05-18 21:47:24 +02:00
av_strlcatf ( buf , sizeof ( buf ) , " a=control:streamid=%d \r \n " , index ) ;
len = strlen ( buf ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , len + 24 ) ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " udta " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , len + 16 ) ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " hnti " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , len + 8 ) ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " sdp " ) ;
2011-02-21 19:28:17 +01:00
avio_write ( pb , buf , len ) ;
2010-05-18 21:47:24 +02:00
return len + 24 ;
}
2012-01-05 12:57:05 +01:00
static int mov_write_trak_tag ( AVIOContext * pb , MOVMuxContext * mov ,
MOVTrack * track , AVStream * st )
2003-08-26 22:23:13 +02:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " trak " ) ;
2008-08-24 01:43:20 +02:00
mov_write_tkhd_tag ( pb , track , st ) ;
2012-01-26 01:52:29 +01:00
if ( ! ( mov - > flags & FF_MOV_FLAG_FRAGMENT ) ) // EDTS with fragments is tricky as we dont know the duration when its written
2011-12-07 05:06:13 +01:00
mov_write_edts_tag ( pb , track ) ; // PSP Movies and several other cases require edts box
2010-05-05 10:41:10 +02:00
if ( track - > tref_tag )
mov_write_tref_tag ( pb , track ) ;
2003-09-10 01:03:04 +02:00
mov_write_mdia_tag ( pb , track ) ;
2005-12-17 19:14:38 +01:00
if ( track - > mode = = MODE_PSP )
2005-01-27 15:48:15 +01:00
mov_write_uuid_tag_psp ( pb , track ) ; // PSP Movies require this uuid box
2010-05-18 21:47:24 +02:00
if ( track - > tag = = MKTAG ( ' r ' , ' t ' , ' p ' , ' ' ) )
2011-05-18 15:21:47 +02:00
mov_write_udta_sdp ( pb , track - > rtp_ctx , track - > trackID ) ;
2011-02-25 16:41:55 +01:00
if ( track - > enc - > codec_type = = AVMEDIA_TYPE_VIDEO & & track - > mode = = MODE_MOV ) {
double sample_aspect_ratio = av_q2d ( st - > sample_aspect_ratio ) ;
if ( 0.0 ! = sample_aspect_ratio & & 1.0 ! = sample_aspect_ratio )
mov_write_tapt_tag ( pb , track ) ;
} ;
2003-09-10 01:03:04 +02:00
return updateSize ( pb , pos ) ;
2003-08-26 22:23:13 +02:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_iods_tag ( AVIOContext * pb , MOVMuxContext * mov )
2003-08-26 22:23:13 +02:00
{
2011-10-04 20:44:25 +02:00
int i , has_audio = 0 , has_video = 0 ;
int64_t pos = avio_tell ( pb ) ;
int audio_profile = mov - > iods_audio_profile ;
int video_profile = mov - > iods_video_profile ;
for ( i = 0 ; i < mov - > nb_streams ; i + + ) {
if ( mov - > tracks [ i ] . entry > 0 ) {
has_audio | = mov - > tracks [ i ] . enc - > codec_type = = AVMEDIA_TYPE_AUDIO ;
has_video | = mov - > tracks [ i ] . enc - > codec_type = = AVMEDIA_TYPE_VIDEO ;
}
}
if ( audio_profile < 0 )
audio_profile = 0xFF - has_audio ;
if ( video_profile < 0 )
video_profile = 0xFF - has_video ;
avio_wb32 ( pb , 0x0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " iods " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* version & flags */
2011-10-04 20:44:25 +02:00
putDescr ( pb , 0x10 , 7 ) ;
avio_wb16 ( pb , 0x004f ) ;
avio_w8 ( pb , 0xff ) ;
avio_w8 ( pb , 0xff ) ;
avio_w8 ( pb , audio_profile ) ;
avio_w8 ( pb , video_profile ) ;
avio_w8 ( pb , 0xff ) ;
return updateSize ( pb , pos ) ;
2003-08-26 22:23:13 +02:00
}
2012-01-05 12:57:05 +01:00
static int mov_write_trex_tag ( AVIOContext * pb , MOVTrack * track )
{
avio_wb32 ( pb , 0x20 ) ; /* size */
ffio_wfourcc ( pb , " trex " ) ;
avio_wb32 ( pb , 0 ) ; /* version & flags */
avio_wb32 ( pb , track - > trackID ) ; /* track ID */
avio_wb32 ( pb , 1 ) ; /* default sample description index */
avio_wb32 ( pb , 0 ) ; /* default sample duration */
avio_wb32 ( pb , 0 ) ; /* default sample size */
avio_wb32 ( pb , 0 ) ; /* default sample flags */
return 0 ;
}
static int mov_write_mvex_tag ( AVIOContext * pb , MOVMuxContext * mov )
{
int64_t pos = avio_tell ( pb ) ;
int i ;
avio_wb32 ( pb , 0x0 ) ; /* size */
ffio_wfourcc ( pb , " mvex " ) ;
for ( i = 0 ; i < mov - > nb_streams ; i + + )
mov_write_trex_tag ( pb , & mov - > tracks [ i ] ) ;
return updateSize ( pb , pos ) ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_mvhd_tag ( AVIOContext * pb , MOVMuxContext * mov )
2003-08-26 22:23:13 +02:00
{
2006-01-23 15:12:03 +01:00
int maxTrackID = 1 , i ;
int64_t maxTrackLenTemp , maxTrackLen = 0 ;
2006-03-26 15:34:51 +02:00
int version ;
2003-08-26 22:23:13 +02:00
2006-06-25 02:10:52 +02:00
for ( i = 0 ; i < mov - > nb_streams ; i + + ) {
2003-08-26 22:23:13 +02:00
if ( mov - > tracks [ i ] . entry > 0 ) {
2009-11-29 03:46:49 +01:00
maxTrackLenTemp = av_rescale_rnd ( mov - > tracks [ i ] . trackDuration ,
MOV_TIMESCALE ,
mov - > tracks [ i ] . timescale ,
AV_ROUND_UP ) ;
2003-09-28 23:09:32 +02:00
if ( maxTrackLen < maxTrackLenTemp )
maxTrackLen = maxTrackLenTemp ;
2003-08-26 22:23:13 +02:00
if ( maxTrackID < mov - > tracks [ i ] . trackID )
maxTrackID = mov - > tracks [ i ] . trackID ;
}
}
2006-03-26 15:34:51 +02:00
version = maxTrackLen < UINT32_MAX ? 0 : 1 ;
2011-02-21 19:28:17 +01:00
( version = = 1 ) ? avio_wb32 ( pb , 120 ) : avio_wb32 ( pb , 108 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " mvhd " ) ;
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , version ) ;
avio_wb24 ( pb , 0 ) ; /* flags */
2006-03-26 15:34:51 +02:00
if ( version = = 1 ) {
2011-02-21 19:28:17 +01:00
avio_wb64 ( pb , mov - > time ) ;
avio_wb64 ( pb , mov - > time ) ;
2006-03-26 15:34:51 +02:00
} else {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , mov - > time ) ; /* creation time */
avio_wb32 ( pb , mov - > time ) ; /* modification time */
2006-03-26 15:34:51 +02:00
}
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , MOV_TIMESCALE ) ;
( version = = 1 ) ? avio_wb64 ( pb , maxTrackLen ) : avio_wb32 ( pb , maxTrackLen ) ; /* duration of longest track */
2003-08-26 22:23:13 +02:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x00010000 ) ; /* reserved (preferred rate) 1.0 = normal */
avio_wb16 ( pb , 0x0100 ) ; /* reserved (preferred volume) 1.0 = normal */
avio_wb16 ( pb , 0 ) ; /* reserved */
avio_wb32 ( pb , 0 ) ; /* reserved */
avio_wb32 ( pb , 0 ) ; /* reserved */
2003-08-26 22:23:13 +02:00
/* Matrix structure */
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x00010000 ) ; /* reserved */
avio_wb32 ( pb , 0x0 ) ; /* reserved */
avio_wb32 ( pb , 0x0 ) ; /* reserved */
avio_wb32 ( pb , 0x0 ) ; /* reserved */
avio_wb32 ( pb , 0x00010000 ) ; /* reserved */
avio_wb32 ( pb , 0x0 ) ; /* reserved */
avio_wb32 ( pb , 0x0 ) ; /* reserved */
avio_wb32 ( pb , 0x0 ) ; /* reserved */
avio_wb32 ( pb , 0x40000000 ) ; /* reserved */
avio_wb32 ( pb , 0 ) ; /* reserved (preview time) */
avio_wb32 ( pb , 0 ) ; /* reserved (preview duration) */
avio_wb32 ( pb , 0 ) ; /* reserved (poster time) */
avio_wb32 ( pb , 0 ) ; /* reserved (selection time) */
avio_wb32 ( pb , 0 ) ; /* reserved (selection duration) */
avio_wb32 ( pb , 0 ) ; /* reserved (current time) */
avio_wb32 ( pb , maxTrackID + 1 ) ; /* Next track id */
2003-08-26 22:23:13 +02:00
return 0x6c ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_itunes_hdlr_tag ( AVIOContext * pb , MOVMuxContext * mov ,
2004-08-14 16:05:48 +02:00
AVFormatContext * s )
{
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 33 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " hdlr " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ;
avio_wb32 ( pb , 0 ) ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " mdir " ) ;
ffio_wfourcc ( pb , " appl " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ;
avio_wb32 ( pb , 0 ) ;
avio_w8 ( pb , 0 ) ;
2010-04-12 09:24:30 +02:00
return 33 ;
2004-08-14 16:05:48 +02:00
}
/* helper function to write a data tag with the specified string as data */
2011-02-20 11:04:12 +01:00
static int mov_write_string_data_tag ( AVIOContext * pb , const char * data , int lang , int long_style )
2004-08-14 16:05:48 +02:00
{
2006-05-19 00:49:27 +02:00
if ( long_style ) {
2010-04-12 09:24:30 +02:00
int size = 16 + strlen ( data ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , size ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " data " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 1 ) ;
avio_wb32 ( pb , 0 ) ;
avio_write ( pb , data , strlen ( data ) ) ;
2010-04-12 09:24:30 +02:00
return size ;
2006-05-19 00:49:27 +02:00
} else {
2010-03-09 02:53:16 +01:00
if ( ! lang )
lang = ff_mov_iso639_to_lang ( " und " , 1 ) ;
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , strlen ( data ) ) ; /* string length */
avio_wb16 ( pb , lang ) ;
avio_write ( pb , data , strlen ( data ) ) ;
2006-05-19 00:49:27 +02:00
return strlen ( data ) + 4 ;
2004-08-14 16:05:48 +02:00
}
}
2011-02-20 11:04:12 +01:00
static int mov_write_string_tag ( AVIOContext * pb , const char * name , const char * value , int lang , int long_style ) {
2004-08-14 16:05:48 +02:00
int size = 0 ;
2008-02-06 19:57:00 +01:00
if ( value & & value [ 0 ] ) {
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , name ) ;
2009-02-26 14:13:48 +01:00
mov_write_string_data_tag ( pb , value , lang , long_style ) ;
2006-05-19 00:49:27 +02:00
size = updateSize ( pb , pos ) ;
2004-08-14 16:05:48 +02:00
}
return size ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_string_metadata ( AVFormatContext * s , AVIOContext * pb ,
2009-02-26 14:06:49 +01:00
const char * name , const char * tag ,
int long_style )
2004-08-14 16:05:48 +02:00
{
2009-02-26 14:13:48 +01:00
int l , lang = 0 , len , len2 ;
2011-05-22 12:46:29 +02:00
AVDictionaryEntry * t , * t2 = NULL ;
2009-02-26 14:13:48 +01:00
char tag2 [ 16 ] ;
2009-02-26 14:06:49 +01:00
2011-05-22 12:46:29 +02:00
if ( ! ( t = av_dict_get ( s - > metadata , tag , NULL , 0 ) ) )
2006-05-19 00:49:27 +02:00
return 0 ;
2009-02-26 14:06:49 +01:00
2009-02-26 14:13:48 +01:00
len = strlen ( t - > key ) ;
snprintf ( tag2 , sizeof ( tag2 ) , " %s- " , tag ) ;
2011-05-22 12:46:29 +02:00
while ( ( t2 = av_dict_get ( s - > metadata , tag2 , t2 , AV_DICT_IGNORE_SUFFIX ) ) ) {
2009-02-26 14:13:48 +01:00
len2 = strlen ( t2 - > key ) ;
if ( len2 = = len + 4 & & ! strcmp ( t - > value , t2 - > value )
2010-03-09 02:21:09 +01:00
& & ( l = ff_mov_iso639_to_lang ( & t2 - > key [ len2 - 3 ] , 1 ) ) > = 0 ) {
2009-02-26 14:13:48 +01:00
lang = l ;
break ;
}
}
return mov_write_string_tag ( pb , name , t - > value , lang , long_style ) ;
2004-08-14 16:05:48 +02:00
}
/* iTunes track number */
2011-02-20 11:04:12 +01:00
static int mov_write_trkn_tag ( AVIOContext * pb , MOVMuxContext * mov ,
2004-08-14 16:05:48 +02:00
AVFormatContext * s )
{
2011-05-22 12:46:29 +02:00
AVDictionaryEntry * t = av_dict_get ( s - > metadata , " track " , NULL , 0 ) ;
2009-02-26 14:06:49 +01:00
int size = 0 , track = t ? atoi ( t - > value ) : 0 ;
if ( track ) {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 32 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " trkn " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 24 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " data " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; // 8 bytes empty
avio_wb32 ( pb , 0 ) ;
avio_wb16 ( pb , 0 ) ; // empty
avio_wb16 ( pb , track ) ; // track number
avio_wb16 ( pb , 0 ) ; // total track number
avio_wb16 ( pb , 0 ) ; // empty
2010-04-12 09:24:30 +02:00
size = 32 ;
2004-08-14 16:05:48 +02:00
}
return size ;
}
/* iTunes meta data list */
2011-02-20 11:04:12 +01:00
static int mov_write_ilst_tag ( AVIOContext * pb , MOVMuxContext * mov ,
2004-08-14 16:05:48 +02:00
AVFormatContext * s )
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " ilst " ) ;
2009-02-26 14:06:49 +01:00
mov_write_string_metadata ( s , pb , " \251 nam " , " title " , 1 ) ;
2010-05-23 23:03:14 +02:00
mov_write_string_metadata ( s , pb , " \251 ART " , " artist " , 1 ) ;
2010-01-04 03:52:40 +01:00
mov_write_string_metadata ( s , pb , " aART " , " album_artist " , 1 ) ;
2009-11-23 09:47:40 +01:00
mov_write_string_metadata ( s , pb , " \251 wrt " , " composer " , 1 ) ;
2009-02-26 14:06:49 +01:00
mov_write_string_metadata ( s , pb , " \251 alb " , " album " , 1 ) ;
2010-02-01 12:39:10 +01:00
mov_write_string_metadata ( s , pb , " \251 day " , " date " , 1 ) ;
2009-02-26 14:13:48 +01:00
mov_write_string_tag ( pb , " \251 too " , LIBAVFORMAT_IDENT , 0 , 1 ) ;
2009-02-26 14:06:49 +01:00
mov_write_string_metadata ( s , pb , " \251 cmt " , " comment " , 1 ) ;
mov_write_string_metadata ( s , pb , " \251 gen " , " genre " , 1 ) ;
mov_write_string_metadata ( s , pb , " \251 cpy " , " copyright " , 1 ) ;
2010-01-04 03:52:40 +01:00
mov_write_string_metadata ( s , pb , " \251 grp " , " grouping " , 1 ) ;
mov_write_string_metadata ( s , pb , " \251 lyr " , " lyrics " , 1 ) ;
2009-11-23 09:47:44 +01:00
mov_write_string_metadata ( s , pb , " desc " , " description " , 1 ) ;
mov_write_string_metadata ( s , pb , " ldes " , " synopsis " , 1 ) ;
mov_write_string_metadata ( s , pb , " tvsh " , " show " , 1 ) ;
mov_write_string_metadata ( s , pb , " tven " , " episode_id " , 1 ) ;
mov_write_string_metadata ( s , pb , " tvnn " , " network " , 1 ) ;
2004-08-14 16:05:48 +02:00
mov_write_trkn_tag ( pb , mov , s ) ;
return updateSize ( pb , pos ) ;
}
/* iTunes meta data tag */
2011-02-20 11:04:12 +01:00
static int mov_write_meta_tag ( AVIOContext * pb , MOVMuxContext * mov ,
2004-08-14 16:05:48 +02:00
AVFormatContext * s )
{
int size = 0 ;
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " meta " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ;
2009-02-26 14:08:22 +01:00
mov_write_itunes_hdlr_tag ( pb , mov , s ) ;
mov_write_ilst_tag ( pb , mov , s ) ;
size = updateSize ( pb , pos ) ;
2004-08-14 16:05:48 +02:00
return size ;
}
2005-12-17 19:14:38 +01:00
2008-06-11 11:35:02 +02:00
static int utf8len ( const uint8_t * b )
{
int len = 0 ;
int val ;
while ( * b ) {
GET_UTF8 ( val , * b + + , return - 1 ; )
len + + ;
}
return len ;
}
2011-02-20 11:04:12 +01:00
static int ascii_to_wc ( AVIOContext * pb , const uint8_t * b )
2008-06-11 11:35:02 +02:00
{
int val ;
while ( * b ) {
GET_UTF8 ( val , * b + + , return - 1 ; )
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , val ) ;
2008-06-11 11:35:02 +02:00
}
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 0x00 ) ;
2008-06-11 11:35:02 +02:00
return 0 ;
}
static uint16_t language_code ( const char * str )
{
return ( ( ( str [ 0 ] - 0x60 ) & 0x1F ) < < 10 ) + ( ( ( str [ 1 ] - 0x60 ) & 0x1F ) < < 5 ) + ( ( str [ 2 ] - 0x60 ) & 0x1F ) ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_3gp_udta_tag ( AVIOContext * pb , AVFormatContext * s ,
2008-06-11 11:40:22 +02:00
const char * tag , const char * str )
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-05-22 12:46:29 +02:00
AVDictionaryEntry * t = av_dict_get ( s - > metadata , str , NULL , 0 ) ;
2009-02-26 14:06:49 +01:00
if ( ! t | | ! utf8len ( t - > value ) )
2008-06-11 11:40:22 +02:00
return 0 ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , tag ) ; /* type */
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* version + flags */
2008-06-11 11:40:22 +02:00
if ( ! strcmp ( tag , " yrrc " ) )
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , atoi ( t - > value ) ) ;
2008-06-11 11:40:22 +02:00
else {
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , language_code ( " eng " ) ) ; /* language */
avio_write ( pb , t - > value , strlen ( t - > value ) + 1 ) ; /* UTF8 string value */
2009-02-26 14:06:49 +01:00
if ( ! strcmp ( tag , " albm " ) & &
2011-05-22 12:46:29 +02:00
( t = av_dict_get ( s - > metadata , " track " , NULL , 0 ) ) )
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , atoi ( t - > value ) ) ;
2008-06-11 11:40:22 +02:00
}
return updateSize ( pb , pos ) ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_chpl_tag ( AVIOContext * pb , AVFormatContext * s )
2010-04-21 08:36:05 +02:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2010-04-21 08:36:05 +02:00
int i , nb_chapters = FFMIN ( s - > nb_chapters , 255 ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; // size
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " chpl " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x01000000 ) ; // version + flags
avio_wb32 ( pb , 0 ) ; // unknown
avio_w8 ( pb , nb_chapters ) ;
2010-04-21 08:36:05 +02:00
for ( i = 0 ; i < nb_chapters ; i + + ) {
AVChapter * c = s - > chapters [ i ] ;
2011-05-22 12:46:29 +02:00
AVDictionaryEntry * t ;
2011-02-21 19:28:17 +01:00
avio_wb64 ( pb , av_rescale_q ( c - > start , c - > time_base , ( AVRational ) { 1 , 10000000 } ) ) ;
2010-04-21 08:36:05 +02:00
2011-05-22 12:46:29 +02:00
if ( ( t = av_dict_get ( c - > metadata , " title " , NULL , 0 ) ) ) {
2010-04-21 08:36:05 +02:00
int len = FFMIN ( strlen ( t - > value ) , 255 ) ;
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , len ) ;
avio_write ( pb , t - > value , len ) ;
2010-04-21 08:36:05 +02:00
} else
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0 ) ;
2010-04-21 08:36:05 +02:00
}
return updateSize ( pb , pos ) ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_udta_tag ( AVIOContext * pb , MOVMuxContext * mov ,
2004-02-14 20:08:09 +01:00
AVFormatContext * s )
{
2011-02-20 11:04:12 +01:00
AVIOContext * pb_buf ;
2009-02-26 14:06:49 +01:00
int i , ret , size ;
uint8_t * buf ;
2007-10-24 16:20:15 +02:00
2008-06-09 22:13:44 +02:00
for ( i = 0 ; i < s - > nb_streams ; i + + )
if ( mov - > tracks [ i ] . enc - > flags & CODEC_FLAG_BITEXACT ) {
2009-02-26 14:06:49 +01:00
return 0 ;
2008-06-09 22:13:44 +02:00
}
2011-03-17 08:13:34 +01:00
ret = avio_open_dyn_buf ( & pb_buf ) ;
2009-02-26 14:06:49 +01:00
if ( ret < 0 )
return ret ;
2004-02-14 20:08:09 +01:00
2012-01-14 14:10:17 +01:00
if ( mov - > mode & MODE_3GP ) {
mov_write_3gp_udta_tag ( pb_buf , s , " perf " , " artist " ) ;
mov_write_3gp_udta_tag ( pb_buf , s , " titl " , " title " ) ;
mov_write_3gp_udta_tag ( pb_buf , s , " auth " , " author " ) ;
mov_write_3gp_udta_tag ( pb_buf , s , " gnre " , " genre " ) ;
mov_write_3gp_udta_tag ( pb_buf , s , " dscp " , " comment " ) ;
mov_write_3gp_udta_tag ( pb_buf , s , " albm " , " album " ) ;
mov_write_3gp_udta_tag ( pb_buf , s , " cprt " , " copyright " ) ;
mov_write_3gp_udta_tag ( pb_buf , s , " yrrc " , " date " ) ;
} else if ( mov - > mode = = MODE_MOV ) { // the title field breaks gtkpod with mp4 and my suspicion is that stuff is not valid in mp4
mov_write_string_metadata ( s , pb_buf , " \251 ART " , " artist " , 0 ) ;
mov_write_string_metadata ( s , pb_buf , " \251 nam " , " title " , 0 ) ;
mov_write_string_metadata ( s , pb_buf , " \251 aut " , " author " , 0 ) ;
mov_write_string_metadata ( s , pb_buf , " \251 alb " , " album " , 0 ) ;
mov_write_string_metadata ( s , pb_buf , " \251 day " , " date " , 0 ) ;
mov_write_string_metadata ( s , pb_buf , " \251 swr " , " encoder " , 0 ) ;
mov_write_string_metadata ( s , pb_buf , " \251 des " , " comment " , 0 ) ;
mov_write_string_metadata ( s , pb_buf , " \251 gen " , " genre " , 0 ) ;
mov_write_string_metadata ( s , pb_buf , " \251 cpy " , " copyright " , 0 ) ;
} else {
/* iTunes meta data */
mov_write_meta_tag ( pb_buf , mov , s ) ;
}
2009-02-26 14:06:49 +01:00
2012-01-14 14:10:17 +01:00
if ( s - > nb_chapters )
mov_write_chpl_tag ( pb_buf , s ) ;
2010-04-21 08:36:05 +02:00
2011-03-17 08:16:07 +01:00
if ( ( size = avio_close_dyn_buf ( pb_buf , & buf ) ) > 0 ) {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , size + 8 ) ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " udta " ) ;
2011-02-21 19:28:17 +01:00
avio_write ( pb , buf , size ) ;
2007-10-24 16:20:15 +02:00
}
2010-07-20 07:23:28 +02:00
av_free ( buf ) ;
2007-10-24 16:20:15 +02:00
return 0 ;
2004-02-14 20:08:09 +01:00
}
2011-02-20 11:04:12 +01:00
static void mov_write_psp_udta_tag ( AVIOContext * pb ,
2008-06-11 11:02:01 +02:00
const char * str , const char * lang , int type )
{
int len = utf8len ( str ) + 1 ;
if ( len < = 0 )
2008-06-11 11:06:27 +02:00
return ;
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , len * 2 + 10 ) ; /* size */
avio_wb32 ( pb , type ) ; /* type */
avio_wb16 ( pb , language_code ( lang ) ) ; /* language */
avio_wb16 ( pb , 0x01 ) ; /* ? */
2008-06-11 11:02:01 +02:00
ascii_to_wc ( pb , str ) ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_uuidusmt_tag ( AVIOContext * pb , AVFormatContext * s )
2006-01-24 09:03:42 +01:00
{
2011-05-22 12:46:29 +02:00
AVDictionaryEntry * title = av_dict_get ( s - > metadata , " title " , NULL , 0 ) ;
2008-10-03 12:16:29 +02:00
int64_t pos , pos2 ;
2006-01-24 09:03:42 +01:00
2009-02-26 14:06:49 +01:00
if ( title ) {
2011-03-03 20:11:45 +01:00
pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size placeholder*/
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " uuid " ) ;
ffio_wfourcc ( pb , " USMT " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x21d24fce ) ; /* 96 bit UUID */
avio_wb32 ( pb , 0xbb88695c ) ;
avio_wb32 ( pb , 0xfac9c740 ) ;
2006-01-24 09:03:42 +01:00
2011-03-03 20:11:45 +01:00
pos2 = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size placeholder*/
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " MTDT " ) ;
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 4 ) ;
2006-01-24 09:03:42 +01:00
2006-05-19 03:53:59 +02:00
// ?
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 0x0C ) ; /* size */
avio_wb32 ( pb , 0x0B ) ; /* type */
avio_wb16 ( pb , language_code ( " und " ) ) ; /* language */
avio_wb16 ( pb , 0x0 ) ; /* ? */
avio_wb16 ( pb , 0x021C ) ; /* data */
2006-05-19 03:53:59 +02:00
2008-06-11 11:06:27 +02:00
mov_write_psp_udta_tag ( pb , LIBAVCODEC_IDENT , " eng " , 0x04 ) ;
2009-02-26 14:06:49 +01:00
mov_write_psp_udta_tag ( pb , title - > value , " eng " , 0x01 ) ;
2006-05-19 03:53:59 +02:00
// snprintf(dt,32,"%04d/%02d/%02d %02d:%02d:%02d",t_st->tm_year+1900,t_st->tm_mon+1,t_st->tm_mday,t_st->tm_hour,t_st->tm_min,t_st->tm_sec);
2008-06-11 11:06:27 +02:00
mov_write_psp_udta_tag ( pb , " 2006/04/01 11:11:11 " , " und " , 0x03 ) ;
updateSize ( pb , pos2 ) ;
return updateSize ( pb , pos ) ;
2006-01-24 09:03:42 +01:00
}
2008-06-11 11:06:27 +02:00
return 0 ;
2006-01-24 09:03:42 +01:00
}
2011-11-29 04:03:22 +01:00
static void build_chunks ( MOVTrack * trk )
{
int i ;
MOVIentry * chunk = & trk - > cluster [ 0 ] ;
2011-11-29 16:50:25 +01:00
uint64_t chunkSize = chunk - > size ;
2011-11-29 04:03:22 +01:00
chunk - > chunkNum = 1 ;
trk - > chunkCount = 1 ;
for ( i = 1 ; i < trk - > entry ; i + + ) {
2012-01-23 12:59:37 +01:00
if ( chunk - > pos + chunkSize = = trk - > cluster [ i ] . pos & &
2012-01-23 20:52:37 +01:00
chunkSize + trk - > cluster [ i ] . size < ( 1 < < 20 ) ) {
2011-11-29 16:50:25 +01:00
chunkSize + = trk - > cluster [ i ] . size ;
2011-11-29 04:03:22 +01:00
chunk - > samplesInChunk + = trk - > cluster [ i ] . entries ;
} else {
trk - > cluster [ i ] . chunkNum = chunk - > chunkNum + 1 ;
chunk = & trk - > cluster [ i ] ;
2011-11-29 16:50:25 +01:00
chunkSize = chunk - > size ;
2011-11-29 04:03:22 +01:00
trk - > chunkCount + + ;
}
}
}
2011-02-20 11:04:12 +01:00
static int mov_write_moov_tag ( AVIOContext * pb , MOVMuxContext * mov ,
2004-02-14 20:08:09 +01:00
AVFormatContext * s )
2003-08-26 22:23:13 +02:00
{
2006-01-23 15:12:03 +01:00
int i ;
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size placeholder*/
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " moov " ) ;
2003-08-26 22:23:13 +02:00
2006-06-25 02:10:52 +02:00
for ( i = 0 ; i < mov - > nb_streams ; i + + ) {
2012-01-05 12:57:05 +01:00
if ( mov - > tracks [ i ] . entry < = 0 & & ! ( mov - > flags & FF_MOV_FLAG_FRAGMENT ) )
continue ;
2003-11-03 22:51:07 +01:00
mov - > tracks [ i ] . time = mov - > time ;
mov - > tracks [ i ] . trackID = i + 1 ;
2011-11-29 04:03:22 +01:00
build_chunks ( & mov - > tracks [ i ] ) ;
2003-08-26 22:23:13 +02:00
}
2010-05-05 10:41:10 +02:00
if ( mov - > chapter_track )
for ( i = 0 ; i < s - > nb_streams ; i + + ) {
mov - > tracks [ i ] . tref_tag = MKTAG ( ' c ' , ' h ' , ' a ' , ' p ' ) ;
mov - > tracks [ i ] . tref_id = mov - > tracks [ mov - > chapter_track ] . trackID ;
}
2010-05-18 21:47:24 +02:00
for ( i = 0 ; i < mov - > nb_streams ; i + + ) {
if ( mov - > tracks [ i ] . tag = = MKTAG ( ' r ' , ' t ' , ' p ' , ' ' ) ) {
mov - > tracks [ i ] . tref_tag = MKTAG ( ' h ' , ' i ' , ' n ' , ' t ' ) ;
mov - > tracks [ i ] . tref_id =
mov - > tracks [ mov - > tracks [ i ] . src_track ] . trackID ;
}
}
2010-05-05 10:41:10 +02:00
2003-09-10 01:03:04 +02:00
mov_write_mvhd_tag ( pb , mov ) ;
2011-10-04 20:44:25 +02:00
if ( mov - > mode ! = MODE_MOV & & ! mov - > iods_skip )
mov_write_iods_tag ( pb , mov ) ;
2006-06-25 02:10:52 +02:00
for ( i = 0 ; i < mov - > nb_streams ; i + + ) {
2012-01-05 12:57:05 +01:00
if ( mov - > tracks [ i ] . entry > 0 | | mov - > flags & FF_MOV_FLAG_FRAGMENT ) {
2011-12-06 16:15:35 +01:00
mov_write_trak_tag ( pb , mov , & ( mov - > tracks [ i ] ) , i < s - > nb_streams ? s - > streams [ i ] : NULL ) ;
2003-08-26 22:23:13 +02:00
}
}
2012-01-05 12:57:05 +01:00
if ( mov - > flags & FF_MOV_FLAG_FRAGMENT )
mov_write_mvex_tag ( pb , mov ) ; /* QuickTime requires trak to precede this */
2011-12-06 16:15:35 +01:00
2006-01-24 09:03:42 +01:00
if ( mov - > mode = = MODE_PSP )
mov_write_uuidusmt_tag ( pb , s ) ;
2008-06-11 11:40:22 +02:00
else
2006-11-01 22:09:14 +01:00
mov_write_udta_tag ( pb , mov , s ) ;
2004-02-14 20:08:09 +01:00
2003-09-10 01:03:04 +02:00
return updateSize ( pb , pos ) ;
2003-08-26 22:23:13 +02:00
}
2011-12-06 16:15:35 +01:00
static int mov_write_mfhd_tag ( AVIOContext * pb , MOVMuxContext * mov )
{
2012-01-05 12:57:05 +01:00
avio_wb32 ( pb , 16 ) ;
2011-12-06 16:15:35 +01:00
ffio_wfourcc ( pb , " mfhd " ) ;
avio_wb32 ( pb , 0 ) ;
2012-01-05 12:57:05 +01:00
avio_wb32 ( pb , mov - > fragments ) ;
return 0 ;
}
static int mov_write_tfhd_tag ( AVIOContext * pb , MOVTrack * track ,
int64_t moof_offset )
{
int64_t pos = avio_tell ( pb ) ;
/* default-sample-size + default-sample-duration + base-data-offset */
uint32_t flags = 0x19 ;
if ( ! track - > entry ) {
flags | = 0x010000 ; /* duration-is-empty */
} else {
flags | = 0x20 ; /* default-sample-flags-present */
}
2011-12-06 16:15:35 +01:00
2012-01-09 16:58:26 +01:00
/* Don't set a default sample size when creating data for silverlight,
* the player refuses to play files with that set . */
if ( track - > mode = = MODE_ISM )
flags & = ~ 0x10 ;
2012-01-05 12:57:05 +01:00
avio_wb32 ( pb , 0 ) ; /* size placeholder */
ffio_wfourcc ( pb , " tfhd " ) ;
avio_w8 ( pb , 0 ) ; /* version */
avio_wb24 ( pb , flags ) ;
avio_wb32 ( pb , track - > trackID ) ; /* track-id */
if ( flags & 0x01 )
avio_wb64 ( pb , moof_offset ) ;
if ( flags & 0x08 ) {
track - > default_duration = track - > audio_vbr ? track - > enc - > frame_size : 1 ;
avio_wb32 ( pb , track - > default_duration ) ;
}
if ( flags & 0x10 ) {
track - > default_size = track - > entry ? track - > cluster [ 0 ] . size : 1 ;
avio_wb32 ( pb , track - > default_size ) ;
} else
track - > default_size = - 1 ;
if ( flags & 0x20 ) {
track - > default_sample_flags =
track - > enc - > codec_type = = AVMEDIA_TYPE_VIDEO ?
0x01010000 : 0x02000000 ;
avio_wb32 ( pb , track - > default_sample_flags ) ;
}
return updateSize ( pb , pos ) ;
2011-12-06 16:15:35 +01:00
}
2012-01-05 12:57:05 +01:00
static uint32_t get_sample_flags ( MOVTrack * track , MOVIentry * entry )
2011-12-06 16:15:35 +01:00
{
2012-01-05 12:57:05 +01:00
return entry - > flags & MOV_SYNC_SAMPLE ? 0x02000000 : 0x01010000 ;
}
static int mov_write_trun_tag ( AVIOContext * pb , MOVTrack * track )
{
int64_t pos = avio_tell ( pb ) ;
uint32_t flags = 1 ; /* data-offset-present */
2011-12-06 16:15:35 +01:00
int i ;
2012-01-05 12:57:05 +01:00
for ( i = 0 ; i < track - > entry ; i + + ) {
int64_t duration = i + 1 = = track - > entry ?
track - > trackDuration - track - > cluster [ i ] . dts + track - > start_dts :
track - > cluster [ i + 1 ] . dts - track - > cluster [ i ] . dts ;
if ( duration ! = track - > default_duration )
flags | = 0x100 ; /* sample-duration-present */
if ( track - > cluster [ i ] . size ! = track - > default_size )
flags | = 0x200 ; /* sample-size-present */
if ( i > 0 & & get_sample_flags ( track , & track - > cluster [ i ] ) ! = track - > default_sample_flags )
flags | = 0x400 ; /* sample-flags-present */
}
if ( ! ( flags & 0x400 ) )
flags | = 0x4 ; /* first-sample-flags-present */
if ( track - > flags & MOV_TRACK_CTTS )
flags | = 0x800 ; /* sample-composition-time-offsets-present */
avio_wb32 ( pb , 0 ) ; /* size placeholder */
ffio_wfourcc ( pb , " trun " ) ;
avio_w8 ( pb , 0 ) ; /* version */
avio_wb24 ( pb , flags ) ;
avio_wb32 ( pb , track - > entry ) ; /* sample count */
track - > moof_size_offset = avio_tell ( pb ) ;
avio_wb32 ( pb , 0 ) ; /* data offset */
if ( flags & 0x4 ) /* first sample flags */
avio_wb32 ( pb , get_sample_flags ( track , & track - > cluster [ 0 ] ) ) ;
for ( i = 0 ; i < track - > entry ; i + + ) {
int64_t duration = i + 1 = = track - > entry ?
track - > trackDuration - track - > cluster [ i ] . dts + track - > start_dts :
track - > cluster [ i + 1 ] . dts - track - > cluster [ i ] . dts ;
if ( flags & 0x100 )
avio_wb32 ( pb , duration ) ;
if ( flags & 0x200 )
avio_wb32 ( pb , track - > cluster [ i ] . size ) ;
if ( flags & 0x400 )
avio_wb32 ( pb , get_sample_flags ( track , & track - > cluster [ i ] ) ) ;
if ( flags & 0x800 )
avio_wb32 ( pb , track - > cluster [ i ] . cts ) ;
}
return updateSize ( pb , pos ) ;
}
2012-01-09 16:58:26 +01:00
static int mov_write_tfxd_tag ( AVIOContext * pb , MOVTrack * track )
{
2011-12-06 16:15:35 +01:00
int64_t pos = avio_tell ( pb ) ;
2012-01-09 16:58:26 +01:00
const uint8_t uuid [ ] = {
0x6d , 0x1d , 0x9b , 0x05 , 0x42 , 0xd5 , 0x44 , 0xe6 ,
0x80 , 0xe2 , 0x14 , 0x1d , 0xaf , 0xf7 , 0x57 , 0xb2
} ;
avio_wb32 ( pb , 0 ) ; /* size placeholder */
ffio_wfourcc ( pb , " uuid " ) ;
avio_write ( pb , uuid , sizeof ( uuid ) ) ;
avio_w8 ( pb , 1 ) ;
avio_wb24 ( pb , 0 ) ;
avio_wb64 ( pb , track - > frag_start ) ;
avio_wb64 ( pb , track - > start_dts + track - > trackDuration -
track - > cluster [ 0 ] . dts ) ;
return updateSize ( pb , pos ) ;
}
static int mov_write_tfrf_tag ( AVIOContext * pb , MOVMuxContext * mov ,
MOVTrack * track , int entry )
{
int n = track - > nb_frag_info - 1 - entry , i ;
int size = 8 + 16 + 4 + 1 + 16 * n ;
const uint8_t uuid [ ] = {
0xd4 , 0x80 , 0x7e , 0xf2 , 0xca , 0x39 , 0x46 , 0x95 ,
0x8e , 0x54 , 0x26 , 0xcb , 0x9e , 0x46 , 0xa7 , 0x9f
} ;
if ( entry < 0 )
return 0 ;
avio_seek ( pb , track - > frag_info [ entry ] . tfrf_offset , SEEK_SET ) ;
avio_wb32 ( pb , size ) ;
ffio_wfourcc ( pb , " uuid " ) ;
avio_write ( pb , uuid , sizeof ( uuid ) ) ;
avio_w8 ( pb , 1 ) ;
avio_wb24 ( pb , 0 ) ;
avio_w8 ( pb , n ) ;
for ( i = 0 ; i < n ; i + + ) {
int index = entry + 1 + i ;
avio_wb64 ( pb , track - > frag_info [ index ] . time ) ;
avio_wb64 ( pb , track - > frag_info [ index ] . duration ) ;
}
if ( n < mov - > ism_lookahead ) {
int free_size = 16 * ( mov - > ism_lookahead - n ) ;
avio_wb32 ( pb , free_size ) ;
ffio_wfourcc ( pb , " free " ) ;
for ( i = 0 ; i < free_size - 8 ; i + + )
avio_w8 ( pb , 0 ) ;
}
return 0 ;
}
static int mov_write_tfrf_tags ( AVIOContext * pb , MOVMuxContext * mov ,
MOVTrack * track )
{
int64_t pos = avio_tell ( pb ) ;
int i ;
for ( i = 0 ; i < mov - > ism_lookahead ; i + + ) {
/* Update the tfrf tag for the last ism_lookahead fragments,
* nb_frag_info - 1 is the next fragment to be written . */
mov_write_tfrf_tag ( pb , mov , track , track - > nb_frag_info - 2 - i ) ;
}
avio_seek ( pb , pos , SEEK_SET ) ;
return 0 ;
}
static int mov_write_traf_tag ( AVIOContext * pb , MOVMuxContext * mov ,
MOVTrack * track , int64_t moof_offset )
2012-01-05 12:57:05 +01:00
{
int64_t pos = avio_tell ( pb ) ;
avio_wb32 ( pb , 0 ) ; /* size placeholder */
ffio_wfourcc ( pb , " traf " ) ;
mov_write_tfhd_tag ( pb , track , moof_offset ) ;
mov_write_trun_tag ( pb , track ) ;
2012-01-09 16:58:26 +01:00
if ( mov - > mode = = MODE_ISM ) {
mov_write_tfxd_tag ( pb , track ) ;
if ( mov - > ism_lookahead ) {
int i , size = 16 + 4 + 1 + 16 * mov - > ism_lookahead ;
track - > tfrf_offset = avio_tell ( pb ) ;
avio_wb32 ( pb , 8 + size ) ;
ffio_wfourcc ( pb , " free " ) ;
for ( i = 0 ; i < size ; i + + )
avio_w8 ( pb , 0 ) ;
}
}
2012-01-05 12:57:05 +01:00
return updateSize ( pb , pos ) ;
}
static int mov_write_moof_tag ( AVIOContext * pb , MOVMuxContext * mov , int tracks )
{
int64_t pos = avio_tell ( pb ) , end ;
int i , moof_size ;
avio_wb32 ( pb , 0 ) ; /* size placeholder */
2011-12-06 16:15:35 +01:00
ffio_wfourcc ( pb , " moof " ) ;
mov_write_mfhd_tag ( pb , mov ) ;
2012-01-05 12:57:05 +01:00
for ( i = 0 ; i < mov - > nb_streams ; i + + ) {
MOVTrack * track = & mov - > tracks [ i ] ;
if ( tracks > = 0 & & i ! = tracks )
continue ;
if ( ! track - > entry )
continue ;
2012-01-09 16:58:26 +01:00
mov_write_traf_tag ( pb , mov , track , pos ) ;
2012-01-05 12:57:05 +01:00
}
end = avio_tell ( pb ) ;
moof_size = end - pos ;
for ( i = 0 ; i < mov - > nb_streams ; i + + ) {
MOVTrack * track = & mov - > tracks [ i ] ;
if ( tracks > = 0 & & i ! = tracks )
continue ;
if ( ! track - > entry )
continue ;
avio_seek ( pb , mov - > tracks [ i ] . moof_size_offset , SEEK_SET ) ;
avio_wb32 ( pb , moof_size + 8 + mov - > tracks [ i ] . data_offset ) ;
2011-12-06 16:15:35 +01:00
}
2012-01-05 12:57:05 +01:00
avio_seek ( pb , end , SEEK_SET ) ;
return updateSize ( pb , pos ) ;
}
static int mov_write_tfra_tag ( AVIOContext * pb , MOVTrack * track )
{
int64_t pos = avio_tell ( pb ) ;
int i ;
avio_wb32 ( pb , 0 ) ; /* size placeholder */
ffio_wfourcc ( pb , " tfra " ) ;
avio_w8 ( pb , 1 ) ; /* version */
avio_wb24 ( pb , 0 ) ;
avio_wb32 ( pb , track - > trackID ) ;
avio_wb32 ( pb , 0 ) ; /* length of traf/trun/sample num */
avio_wb32 ( pb , track - > nb_frag_info ) ;
for ( i = 0 ; i < track - > nb_frag_info ; i + + ) {
avio_wb64 ( pb , track - > frag_info [ i ] . time ) ;
avio_wb64 ( pb , track - > frag_info [ i ] . offset ) ;
avio_w8 ( pb , 1 ) ; /* traf number */
avio_w8 ( pb , 1 ) ; /* trun number */
avio_w8 ( pb , 1 ) ; /* sample number */
}
return updateSize ( pb , pos ) ;
}
static int mov_write_mfra_tag ( AVIOContext * pb , MOVMuxContext * mov )
{
int64_t pos = avio_tell ( pb ) ;
int i ;
avio_wb32 ( pb , 0 ) ; /* size placeholder */
ffio_wfourcc ( pb , " mfra " ) ;
for ( i = 0 ; i < mov - > nb_streams ; i + + ) {
MOVTrack * track = & mov - > tracks [ i ] ;
if ( track - > nb_frag_info )
mov_write_tfra_tag ( pb , track ) ;
}
avio_wb32 ( pb , 16 ) ;
ffio_wfourcc ( pb , " mfro " ) ;
avio_wb32 ( pb , 0 ) ; /* version + flags */
avio_wb32 ( pb , avio_tell ( pb ) + 4 - pos ) ;
2011-12-06 16:15:35 +01:00
return updateSize ( pb , pos ) ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_mdat_tag ( AVIOContext * pb , MOVMuxContext * mov )
2003-08-26 22:23:13 +02:00
{
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 8 ) ; // placeholder for extended size field (64 bit)
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , mov - > mode = = MODE_MOV ? " wide " : " free " ) ;
2006-01-23 15:12:03 +01:00
2011-03-03 20:11:45 +01:00
mov - > mdat_pos = avio_tell ( pb ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size placeholder*/
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " mdat " ) ;
2003-08-26 22:23:13 +02:00
return 0 ;
}
/* TODO: This needs to be more general */
2011-02-20 11:04:12 +01:00
static int mov_write_ftyp_tag ( AVIOContext * pb , AVFormatContext * s )
2003-08-26 22:23:13 +02:00
{
2009-02-28 17:02:29 +01:00
MOVMuxContext * mov = s - > priv_data ;
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb ) ;
2008-06-14 23:07:59 +02:00
int has_h264 = 0 , has_video = 0 ;
2008-06-15 03:38:38 +02:00
int minor = 0x200 ;
2008-06-11 11:33:35 +02:00
int i ;
2004-02-14 20:08:09 +01:00
2008-06-14 23:05:28 +02:00
for ( i = 0 ; i < s - > nb_streams ; i + + ) {
AVStream * st = s - > streams [ i ] ;
2010-03-31 01:30:55 +02:00
if ( st - > codec - > codec_type = = AVMEDIA_TYPE_VIDEO )
2008-06-14 23:07:59 +02:00
has_video = 1 ;
2008-06-14 23:05:28 +02:00
if ( st - > codec - > codec_id = = CODEC_ID_H264 )
has_h264 = 1 ;
}
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " ftyp " ) ;
2003-11-03 22:51:07 +01:00
2008-06-15 03:38:38 +02:00
if ( mov - > mode = = MODE_3GP ) {
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , has_h264 ? " 3gp6 " : " 3gp4 " ) ;
2008-06-15 03:38:38 +02:00
minor = has_h264 ? 0x100 : 0x200 ;
} else if ( mov - > mode & MODE_3G2 ) {
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , has_h264 ? " 3g2b " : " 3g2a " ) ;
2008-06-15 03:38:38 +02:00
minor = has_h264 ? 0x20000 : 0x10000 ;
} else if ( mov - > mode = = MODE_PSP )
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " MSNV " ) ;
2008-06-11 11:33:35 +02:00
else if ( mov - > mode = = MODE_MP4 )
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " isom " ) ;
2008-06-14 23:07:59 +02:00
else if ( mov - > mode = = MODE_IPOD )
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , has_video ? " M4V " : " M4A " ) ;
2012-01-09 16:58:26 +01:00
else if ( mov - > mode = = MODE_ISM )
ffio_wfourcc ( pb , " isml " ) ;
2008-06-14 23:07:59 +02:00
else
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " qt " ) ;
2003-11-03 22:51:07 +01:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , minor ) ;
2003-11-03 22:51:07 +01:00
2008-06-14 23:12:51 +02:00
if ( mov - > mode = = MODE_MOV )
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " qt " ) ;
2012-01-09 16:58:26 +01:00
else if ( mov - > mode = = MODE_ISM ) {
ffio_wfourcc ( pb , " piff " ) ;
ffio_wfourcc ( pb , " iso2 " ) ;
} else {
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " isom " ) ;
ffio_wfourcc ( pb , " iso2 " ) ;
2008-06-14 23:05:28 +02:00
if ( has_h264 )
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " avc1 " ) ;
2008-06-14 23:12:51 +02:00
}
2008-06-13 14:19:01 +02:00
2008-02-06 19:57:00 +01:00
if ( mov - > mode = = MODE_3GP )
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , has_h264 ? " 3gp6 " : " 3gp4 " ) ;
2008-06-12 05:08:41 +02:00
else if ( mov - > mode & MODE_3G2 )
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , has_h264 ? " 3g2b " : " 3g2a " ) ;
2008-02-06 19:57:00 +01:00
else if ( mov - > mode = = MODE_PSP )
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " MSNV " ) ;
2008-06-11 11:33:35 +02:00
else if ( mov - > mode = = MODE_MP4 )
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " mp41 " ) ;
2008-06-13 13:30:36 +02:00
return updateSize ( pb , pos ) ;
2003-08-26 22:23:13 +02:00
}
2011-02-20 11:04:12 +01:00
static void mov_write_uuidprof_tag ( AVIOContext * pb , AVFormatContext * s )
2005-01-27 15:48:15 +01:00
{
2006-01-24 09:03:42 +01:00
AVCodecContext * VideoCodec = s - > streams [ 0 ] - > codec ;
AVCodecContext * AudioCodec = s - > streams [ 1 ] - > codec ;
int AudioRate = AudioCodec - > sample_rate ;
int FrameRate = ( ( VideoCodec - > time_base . den ) * ( 0x10000 ) ) / ( VideoCodec - > time_base . num ) ;
2006-04-04 14:58:56 +02:00
int audio_kbitrate = AudioCodec - > bit_rate / 1000 ;
int video_kbitrate = FFMIN ( VideoCodec - > bit_rate / 1000 , 800 - audio_kbitrate ) ;
2005-01-27 15:48:15 +01:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x94 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " uuid " ) ;
ffio_wfourcc ( pb , " PROF " ) ;
2005-01-27 15:48:15 +01:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x21d24fce ) ; /* 96 bit UUID */
avio_wb32 ( pb , 0xbb88695c ) ;
avio_wb32 ( pb , 0xfac9c740 ) ;
2005-01-27 15:48:15 +01:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x0 ) ; /* ? */
avio_wb32 ( pb , 0x3 ) ; /* 3 sections ? */
2005-01-27 15:48:15 +01:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x14 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " FPRF " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x0 ) ; /* ? */
avio_wb32 ( pb , 0x0 ) ; /* ? */
avio_wb32 ( pb , 0x0 ) ; /* ? */
2005-01-27 15:48:15 +01:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x2c ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " APRF " ) ; /* audio */
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x0 ) ;
avio_wb32 ( pb , 0x2 ) ; /* TrackID */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " mp4a " ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x20f ) ;
avio_wb32 ( pb , 0x0 ) ;
avio_wb32 ( pb , audio_kbitrate ) ;
avio_wb32 ( pb , audio_kbitrate ) ;
avio_wb32 ( pb , AudioRate ) ;
avio_wb32 ( pb , AudioCodec - > channels ) ;
avio_wb32 ( pb , 0x34 ) ; /* size */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " VPRF " ) ; /* video */
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x0 ) ;
avio_wb32 ( pb , 0x1 ) ; /* TrackID */
2006-04-04 19:44:59 +02:00
if ( VideoCodec - > codec_id = = CODEC_ID_H264 ) {
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " avc1 " ) ;
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 0x014D ) ;
avio_wb16 ( pb , 0x0015 ) ;
2006-04-04 19:44:59 +02:00
} else {
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , " mp4v " ) ;
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 0x0000 ) ;
avio_wb16 ( pb , 0x0103 ) ;
2006-04-04 19:44:59 +02:00
}
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x0 ) ;
avio_wb32 ( pb , video_kbitrate ) ;
avio_wb32 ( pb , video_kbitrate ) ;
avio_wb32 ( pb , FrameRate ) ;
avio_wb32 ( pb , FrameRate ) ;
avio_wb16 ( pb , VideoCodec - > width ) ;
avio_wb16 ( pb , VideoCodec - > height ) ;
avio_wb32 ( pb , 0x010001 ) ; /* ? */
2005-01-27 15:48:15 +01:00
}
2009-05-15 08:11:53 +02:00
static int mov_parse_mpeg2_frame ( AVPacket * pkt , uint32_t * flags )
{
uint32_t c = - 1 ;
int i , closed_gop = 0 ;
for ( i = 0 ; i < pkt - > size - 4 ; i + + ) {
c = ( c < < 8 ) + pkt - > data [ i ] ;
if ( c = = 0x1b8 ) { // gop
closed_gop = pkt - > data [ i + 4 ] > > 6 & 0x01 ;
} else if ( c = = 0x100 ) { // pic
int temp_ref = ( pkt - > data [ i + 1 ] < < 2 ) | ( pkt - > data [ i + 2 ] > > 6 ) ;
if ( ! temp_ref | | closed_gop ) // I picture is not reordered
* flags = MOV_SYNC_SAMPLE ;
else
* flags = MOV_PARTIAL_SYNC_SAMPLE ;
break ;
}
}
return 0 ;
}
2012-01-05 12:57:05 +01:00
static int mov_flush_fragment ( AVFormatContext * s )
{
2011-12-06 16:15:35 +01:00
MOVMuxContext * mov = s - > priv_data ;
2012-01-05 12:57:05 +01:00
int i , first_track = - 1 ;
int64_t mdat_size = 0 ;
2011-12-06 16:15:35 +01:00
2012-01-05 12:57:05 +01:00
if ( ! ( mov - > flags & FF_MOV_FLAG_FRAGMENT ) )
return 0 ;
if ( ! ( mov - > flags & FF_MOV_FLAG_EMPTY_MOOV ) & & mov - > fragments = = 0 ) {
int64_t pos = avio_tell ( s - > pb ) ;
for ( i = 0 ; i < mov - > nb_streams ; i + + )
if ( ! mov - > tracks [ i ] . entry )
break ;
/* Don't write the initial moov unless all tracks have data */
if ( i < mov - > nb_streams )
return 0 ;
avio_seek ( s - > pb , mov - > mdat_pos , SEEK_SET ) ;
avio_wb32 ( s - > pb , mov - > mdat_size + 8 ) ;
avio_seek ( s - > pb , pos , SEEK_SET ) ;
mov_write_moov_tag ( s - > pb , mov , s ) ;
mov - > fragments + + ;
mov - > mdat_size = 0 ;
for ( i = 0 ; i < mov - > nb_streams ; i + + ) {
if ( mov - > tracks [ i ] . entry )
mov - > tracks [ i ] . frag_start + = mov - > tracks [ i ] . start_dts +
mov - > tracks [ i ] . trackDuration -
mov - > tracks [ i ] . cluster [ 0 ] . dts ;
mov - > tracks [ i ] . entry = 0 ;
2011-12-06 16:15:35 +01:00
}
2012-01-05 12:57:05 +01:00
return 0 ;
2011-12-06 16:15:35 +01:00
}
2012-01-05 12:57:05 +01:00
for ( i = 0 ; i < mov - > nb_streams ; i + + ) {
MOVTrack * track = & mov - > tracks [ i ] ;
if ( mov - > flags & FF_MOV_FLAG_SEPARATE_MOOF )
track - > data_offset = 0 ;
else
track - > data_offset = mdat_size ;
if ( ! track - > mdat_buf )
continue ;
mdat_size + = avio_tell ( track - > mdat_buf ) ;
if ( first_track < 0 )
first_track = i ;
2011-12-06 16:15:35 +01:00
}
2012-01-05 12:57:05 +01:00
if ( ! mdat_size )
return 0 ;
for ( i = 0 ; i < mov - > nb_streams ; i + + ) {
MOVTrack * track = & mov - > tracks [ i ] ;
int buf_size , write_moof = 1 , moof_tracks = - 1 ;
uint8_t * buf ;
int64_t duration = 0 ;
if ( track - > entry )
duration = track - > start_dts + track - > trackDuration -
track - > cluster [ 0 ] . dts ;
if ( mov - > flags & FF_MOV_FLAG_SEPARATE_MOOF ) {
if ( ! track - > mdat_buf )
continue ;
mdat_size = avio_tell ( track - > mdat_buf ) ;
moof_tracks = i ;
} else {
write_moof = i = = first_track ;
}
if ( write_moof ) {
MOVFragmentInfo * info ;
track - > nb_frag_info + + ;
track - > frag_info = av_realloc ( track - > frag_info ,
sizeof ( * track - > frag_info ) *
track - > nb_frag_info ) ;
info = & track - > frag_info [ track - > nb_frag_info - 1 ] ;
info - > offset = avio_tell ( s - > pb ) ;
info - > time = mov - > tracks [ i ] . frag_start ;
2012-01-09 16:58:26 +01:00
info - > duration = duration ;
mov_write_tfrf_tags ( s - > pb , mov , track ) ;
2012-01-05 12:57:05 +01:00
mov_write_moof_tag ( s - > pb , mov , moof_tracks ) ;
2012-01-09 16:58:26 +01:00
info - > tfrf_offset = track - > tfrf_offset ;
2012-01-05 12:57:05 +01:00
mov - > fragments + + ;
avio_wb32 ( s - > pb , mdat_size + 8 ) ;
ffio_wfourcc ( s - > pb , " mdat " ) ;
}
if ( track - > entry )
track - > frag_start + = duration ;
track - > entry = 0 ;
if ( ! track - > mdat_buf )
continue ;
buf_size = avio_close_dyn_buf ( track - > mdat_buf , & buf ) ;
track - > mdat_buf = NULL ;
avio_write ( s - > pb , buf , buf_size ) ;
av_free ( buf ) ;
}
mov - > mdat_size = 0 ;
2011-12-06 16:15:35 +01:00
return 0 ;
}
2012-01-20 18:29:33 +01:00
static int mov_write_packet_internal ( AVFormatContext * s , AVPacket * pkt )
2003-08-26 22:23:13 +02:00
{
2009-02-28 17:02:29 +01:00
MOVMuxContext * mov = s - > priv_data ;
2011-02-20 11:04:12 +01:00
AVIOContext * pb = s - > pb ;
2006-05-13 20:01:16 +02:00
MOVTrack * trk = & mov - > tracks [ pkt - > stream_index ] ;
AVCodecContext * enc = trk - > enc ;
2003-11-03 22:51:07 +01:00
unsigned int samplesInChunk = 0 ;
2004-05-29 04:06:32 +02:00
int size = pkt - > size ;
2011-12-09 20:19:57 +01:00
uint8_t * reformatted_data = NULL ;
2003-08-26 22:23:13 +02:00
2011-03-05 21:06:46 +01:00
if ( ! s - > pb - > seekable ) return 0 ; /* Can't handle that */
2003-11-03 22:51:07 +01:00
if ( ! size ) return 0 ; /* Discard 0 sized packets */
2003-08-26 22:23:13 +02:00
2012-01-05 12:57:05 +01:00
if ( ( mov - > max_fragment_duration & & trk - > entry & &
av_rescale_q ( pkt - > dts - trk - > cluster [ 0 ] . dts ,
s - > streams [ pkt - > stream_index ] - > time_base ,
AV_TIME_BASE_Q ) > = mov - > max_fragment_duration ) | |
( mov - > max_fragment_size & & mov - > mdat_size + size > = mov - > max_fragment_size ) | |
( mov - > flags & FF_MOV_FLAG_FRAG_KEYFRAME & &
enc - > codec_type = = AVMEDIA_TYPE_VIDEO & &
trk - > entry & & pkt - > flags & AV_PKT_FLAG_KEY ) ) {
mov_flush_fragment ( s ) ;
}
if ( mov - > flags & FF_MOV_FLAG_FRAGMENT & & mov - > fragments > 0 ) {
if ( ! trk - > mdat_buf ) {
int ret ;
if ( ( ret = avio_open_dyn_buf ( & trk - > mdat_buf ) ) < 0 )
return ret ;
}
pb = trk - > mdat_buf ;
2011-12-06 16:15:35 +01:00
}
2006-07-06 16:57:55 +02:00
if ( enc - > codec_id = = CODEC_ID_AMR_NB ) {
2006-07-04 16:03:59 +02:00
/* We must find out how many AMR blocks there are in one packet */
static uint16_t packed_size [ 16 ] =
2011-10-19 11:20:48 +02:00
{ 13 , 14 , 16 , 18 , 20 , 21 , 27 , 32 , 6 , 0 , 0 , 0 , 0 , 0 , 0 , 1 } ;
2006-07-04 16:03:59 +02:00
int len = 0 ;
while ( len < size & & samplesInChunk < 100 ) {
len + = packed_size [ ( pkt - > data [ len ] > > 3 ) & 0x0F ] ;
samplesInChunk + + ;
2003-08-26 22:23:13 +02:00
}
2006-07-06 14:27:43 +02:00
if ( samplesInChunk > 1 ) {
2007-09-12 01:46:46 +02:00
av_log ( s , AV_LOG_ERROR , " fatal error, input is not a single packet, implement a AVParser for it \n " ) ;
2006-07-06 14:27:43 +02:00
return - 1 ;
}
2012-01-22 23:23:23 +01:00
} else if ( enc - > codec_id = = CODEC_ID_ADPCM_MS | |
enc - > codec_id = = CODEC_ID_ADPCM_IMA_WAV ) {
samplesInChunk = enc - > frame_size ;
2006-07-04 16:03:59 +02:00
} else if ( trk - > sampleSize )
samplesInChunk = size / trk - > sampleSize ;
else
2006-04-14 12:51:32 +02:00
samplesInChunk = 1 ;
2003-11-03 22:51:07 +01:00
2006-04-14 12:51:32 +02:00
/* copy extradata if it exists */
if ( trk - > vosLen = = 0 & & enc - > extradata_size > 0 ) {
2003-11-03 22:51:07 +01:00
trk - > vosLen = enc - > extradata_size ;
trk - > vosData = av_malloc ( trk - > vosLen ) ;
memcpy ( trk - > vosData , enc - > extradata , trk - > vosLen ) ;
}
2009-01-16 02:12:32 +01:00
if ( enc - > codec_id = = CODEC_ID_H264 & & trk - > vosLen > 0 & & * ( uint8_t * ) trk - > vosData ! = 1 ) {
/* from x264 or from bytestream h264 */
/* nal reformating needed */
2011-12-09 20:19:57 +01:00
if ( trk - > hint_track > = 0 & & trk - > hint_track < mov - > nb_streams ) {
ff_avc_parse_nal_units_buf ( pkt - > data , & reformatted_data ,
& size ) ;
avio_write ( pb , reformatted_data , size ) ;
} else {
2011-12-06 16:15:35 +01:00
size = ff_avc_parse_nal_units ( pb , pkt - > data , pkt - > size ) ;
2011-12-09 20:19:57 +01:00
}
2011-01-29 06:32:09 +01:00
} else if ( enc - > codec_id = = CODEC_ID_AAC & & pkt - > size > 2 & &
( AV_RB16 ( pkt - > data ) & 0xfff0 ) = = 0xfff0 ) {
av_log ( s , AV_LOG_ERROR , " malformated aac bitstream, use -absf aac_adtstoasc \n " ) ;
return - 1 ;
2009-01-16 02:12:32 +01:00
} else {
2011-02-21 19:28:17 +01:00
avio_write ( pb , pkt - > data , size ) ;
2009-01-16 02:12:32 +01:00
}
2009-01-15 15:03:07 +01:00
if ( ( enc - > codec_id = = CODEC_ID_DNXHD | |
2009-05-13 06:20:23 +02:00
enc - > codec_id = = CODEC_ID_AC3 ) & & ! trk - > vosLen ) {
2008-08-31 22:20:12 +02:00
/* copy frame to create needed atoms */
trk - > vosLen = size ;
trk - > vosData = av_malloc ( size ) ;
2008-08-31 22:21:00 +02:00
if ( ! trk - > vosData )
return AVERROR ( ENOMEM ) ;
2008-08-31 22:20:12 +02:00
memcpy ( trk - > vosData , pkt - > data , size ) ;
2006-03-11 19:18:17 +01:00
}
2006-06-24 20:09:20 +02:00
if ( ! ( trk - > entry % MOV_INDEX_CLUSTER_SIZE ) ) {
2012-01-26 01:52:29 +01:00
trk - > cluster = av_realloc_f ( trk - > cluster , sizeof ( * trk - > cluster ) , ( trk - > entry + MOV_INDEX_CLUSTER_SIZE ) ) ;
2003-11-03 22:51:07 +01:00
if ( ! trk - > cluster )
return - 1 ;
}
2011-03-03 20:11:45 +01:00
trk - > cluster [ trk - > entry ] . pos = avio_tell ( pb ) - size ;
2006-06-24 20:09:20 +02:00
trk - > cluster [ trk - > entry ] . samplesInChunk = samplesInChunk ;
2011-11-30 23:11:49 +01:00
trk - > cluster [ trk - > entry ] . chunkNum = 0 ;
2006-06-24 20:09:20 +02:00
trk - > cluster [ trk - > entry ] . size = size ;
trk - > cluster [ trk - > entry ] . entries = samplesInChunk ;
2006-11-01 18:27:39 +01:00
trk - > cluster [ trk - > entry ] . dts = pkt - > dts ;
2012-01-05 12:57:05 +01:00
if ( ! trk - > entry & & trk - > start_dts ! = AV_NOPTS_VALUE ) {
/* First packet of a new fragment. We already wrote the duration
* of the last packet of the previous fragment based on trackDuration ,
* which might not exactly match our dts . Therefore adjust the dts
* of this packet to be what the previous packets duration implies . */
trk - > cluster [ trk - > entry ] . dts = trk - > start_dts + trk - > trackDuration ;
}
2012-01-07 00:32:53 +01:00
if ( trk - > start_dts = = AV_NOPTS_VALUE )
trk - > start_dts = pkt - > dts ;
trk - > trackDuration = pkt - > dts - trk - > start_dts + pkt - > duration ;
2006-11-01 18:27:39 +01:00
2008-05-29 02:58:41 +02:00
if ( pkt - > pts = = AV_NOPTS_VALUE ) {
av_log ( s , AV_LOG_WARNING , " pts has no value \n " ) ;
pkt - > pts = pkt - > dts ;
}
2008-05-29 02:54:33 +02:00
if ( pkt - > dts ! = pkt - > pts )
2009-05-15 08:11:53 +02:00
trk - > flags | = MOV_TRACK_CTTS ;
2008-05-29 02:54:33 +02:00
trk - > cluster [ trk - > entry ] . cts = pkt - > pts - pkt - > dts ;
2009-05-15 08:11:53 +02:00
trk - > cluster [ trk - > entry ] . flags = 0 ;
2010-03-31 14:29:58 +02:00
if ( pkt - > flags & AV_PKT_FLAG_KEY ) {
2011-02-13 01:18:45 +01:00
if ( mov - > mode = = MODE_MOV & & enc - > codec_id = = CODEC_ID_MPEG2VIDEO & &
trk - > entry > 0 ) { // force sync sample for the first key frame
2009-05-15 08:11:53 +02:00
mov_parse_mpeg2_frame ( pkt , & trk - > cluster [ trk - > entry ] . flags ) ;
if ( trk - > cluster [ trk - > entry ] . flags & MOV_PARTIAL_SYNC_SAMPLE )
trk - > flags | = MOV_TRACK_STPS ;
} else {
trk - > cluster [ trk - > entry ] . flags = MOV_SYNC_SAMPLE ;
}
if ( trk - > cluster [ trk - > entry ] . flags & MOV_SYNC_SAMPLE )
trk - > hasKeyframes + + ;
2009-05-14 23:05:52 +02:00
}
2003-11-03 22:51:07 +01:00
trk - > entry + + ;
trk - > sampleCount + = samplesInChunk ;
2006-05-13 20:01:16 +02:00
mov - > mdat_size + = size ;
2003-11-03 22:51:07 +01:00
2011-03-14 20:39:06 +01:00
avio_flush ( pb ) ;
2010-05-18 21:47:24 +02:00
if ( trk - > hint_track > = 0 & & trk - > hint_track < mov - > nb_streams )
2011-12-09 20:19:57 +01:00
ff_mov_add_hinted_packet ( s , pkt , trk - > hint_track , trk - > entry ,
reformatted_data , size ) ;
av_free ( reformatted_data ) ;
2003-08-26 22:23:13 +02:00
return 0 ;
}
2012-01-20 18:29:33 +01:00
int ff_mov_write_packet ( AVFormatContext * s , AVPacket * pkt )
{
if ( ! pkt ) {
mov_flush_fragment ( s ) ;
return 1 ;
} else {
return mov_write_packet_internal ( s , pkt ) ;
}
}
2010-05-05 10:41:10 +02:00
// QuickTime chapters involve an additional text track with the chapter names
// as samples, and a tref pointing from the other tracks to the chapter one.
static void mov_create_chapter_track ( AVFormatContext * s , int tracknum )
{
MOVMuxContext * mov = s - > priv_data ;
MOVTrack * track = & mov - > tracks [ tracknum ] ;
AVPacket pkt = { . stream_index = tracknum , . flags = AV_PKT_FLAG_KEY } ;
int i , len ;
track - > mode = mov - > mode ;
track - > tag = MKTAG ( ' t ' , ' e ' , ' x ' , ' t ' ) ;
track - > timescale = MOV_TIMESCALE ;
2011-06-18 13:40:48 +02:00
track - > enc = avcodec_alloc_context3 ( NULL ) ;
2010-05-05 10:41:10 +02:00
track - > enc - > codec_type = AVMEDIA_TYPE_SUBTITLE ;
for ( i = 0 ; i < s - > nb_chapters ; i + + ) {
AVChapter * c = s - > chapters [ i ] ;
2011-05-22 12:46:29 +02:00
AVDictionaryEntry * t ;
2010-05-05 10:41:10 +02:00
int64_t end = av_rescale_q ( c - > end , c - > time_base , ( AVRational ) { 1 , MOV_TIMESCALE } ) ;
pkt . pts = pkt . dts = av_rescale_q ( c - > start , c - > time_base , ( AVRational ) { 1 , MOV_TIMESCALE } ) ;
pkt . duration = end - pkt . dts ;
2011-05-22 12:46:29 +02:00
if ( ( t = av_dict_get ( c - > metadata , " title " , NULL , 0 ) ) ) {
2010-05-05 10:41:10 +02:00
len = strlen ( t - > value ) ;
pkt . size = len + 2 ;
pkt . data = av_malloc ( pkt . size ) ;
AV_WB16 ( pkt . data , len ) ;
memcpy ( pkt . data + 2 , t - > value , len ) ;
2010-05-18 21:40:17 +02:00
ff_mov_write_packet ( s , & pkt ) ;
2010-05-05 10:41:10 +02:00
av_freep ( & pkt . data ) ;
}
}
}
2010-05-05 10:41:06 +02:00
static int mov_write_header ( AVFormatContext * s )
{
2011-02-20 11:04:12 +01:00
AVIOContext * pb = s - > pb ;
2010-05-05 10:41:06 +02:00
MOVMuxContext * mov = s - > priv_data ;
2011-07-07 11:25:03 +02:00
AVDictionaryEntry * t ;
2010-05-18 21:47:24 +02:00
int i , hint_track = 0 ;
2010-05-05 10:41:06 +02:00
2011-03-05 21:06:46 +01:00
if ( ! s - > pb - > seekable ) {
2010-05-05 10:41:06 +02:00
av_log ( s , AV_LOG_ERROR , " muxer does not support non seekable output \n " ) ;
return - 1 ;
}
/* Default mode == MP4 */
mov - > mode = MODE_MP4 ;
if ( s - > oformat ! = NULL ) {
if ( ! strcmp ( " 3gp " , s - > oformat - > name ) ) mov - > mode = MODE_3GP ;
else if ( ! strcmp ( " 3g2 " , s - > oformat - > name ) ) mov - > mode = MODE_3GP | MODE_3G2 ;
else if ( ! strcmp ( " mov " , s - > oformat - > name ) ) mov - > mode = MODE_MOV ;
else if ( ! strcmp ( " psp " , s - > oformat - > name ) ) mov - > mode = MODE_PSP ;
else if ( ! strcmp ( " ipod " , s - > oformat - > name ) ) mov - > mode = MODE_IPOD ;
2012-01-09 16:58:26 +01:00
else if ( ! strcmp ( " ismv " , s - > oformat - > name ) ) mov - > mode = MODE_ISM ;
2010-05-05 10:41:06 +02:00
mov_write_ftyp_tag ( pb , s ) ;
if ( mov - > mode = = MODE_PSP ) {
if ( s - > nb_streams ! = 2 ) {
av_log ( s , AV_LOG_ERROR , " PSP mode need one video and one audio stream \n " ) ;
return - 1 ;
}
mov_write_uuidprof_tag ( pb , s ) ;
}
}
2010-05-05 10:41:10 +02:00
mov - > nb_streams = s - > nb_streams ;
if ( mov - > mode & ( MODE_MOV | MODE_IPOD ) & & s - > nb_chapters )
mov - > chapter_track = mov - > nb_streams + + ;
2011-05-20 11:27:02 +02:00
# if FF_API_FLAG_RTP_HINT
2010-05-18 21:47:24 +02:00
if ( s - > flags & AVFMT_FLAG_RTP_HINT ) {
2011-05-20 11:27:02 +02:00
av_log ( s , AV_LOG_WARNING , " The RTP_HINT flag is deprecated, enable it "
" via the -movflags rtphint muxer option "
" instead. \n " ) ;
mov - > flags | = FF_MOV_FLAG_RTP_HINT ;
}
# endif
if ( mov - > flags & FF_MOV_FLAG_RTP_HINT ) {
2010-05-18 21:47:24 +02:00
/* Add hint tracks for each audio and video stream */
hint_track = mov - > nb_streams ;
for ( i = 0 ; i < s - > nb_streams ; i + + ) {
AVStream * st = s - > streams [ i ] ;
if ( st - > codec - > codec_type = = AVMEDIA_TYPE_VIDEO | |
st - > codec - > codec_type = = AVMEDIA_TYPE_AUDIO ) {
mov - > nb_streams + + ;
}
}
}
2010-05-05 10:41:10 +02:00
mov - > tracks = av_mallocz ( mov - > nb_streams * sizeof ( * mov - > tracks ) ) ;
2010-05-05 10:41:06 +02:00
if ( ! mov - > tracks )
return AVERROR ( ENOMEM ) ;
for ( i = 0 ; i < s - > nb_streams ; i + + ) {
AVStream * st = s - > streams [ i ] ;
MOVTrack * track = & mov - > tracks [ i ] ;
2011-05-22 12:46:29 +02:00
AVDictionaryEntry * lang = av_dict_get ( st - > metadata , " language " , NULL , 0 ) ;
2010-05-05 10:41:06 +02:00
track - > enc = st - > codec ;
track - > language = ff_mov_iso639_to_lang ( lang ? lang - > value : " und " , mov - > mode ! = MODE_MOV ) ;
if ( track - > language < 0 )
track - > language = 0 ;
track - > mode = mov - > mode ;
track - > tag = mov_find_codec_tag ( s , track ) ;
if ( ! track - > tag ) {
av_log ( s , AV_LOG_ERROR , " track %d: could not find tag, "
" codec not currently supported in container \n " , i ) ;
goto error ;
}
2010-05-18 21:47:24 +02:00
/* If hinting of this track is enabled by a later hint track,
* this is updated . */
track - > hint_track = - 1 ;
2012-01-07 00:32:53 +01:00
track - > start_dts = AV_NOPTS_VALUE ;
2010-05-05 10:41:06 +02:00
if ( st - > codec - > codec_type = = AVMEDIA_TYPE_VIDEO ) {
if ( track - > tag = = MKTAG ( ' m ' , ' x ' , ' 3 ' , ' p ' ) | | track - > tag = = MKTAG ( ' m ' , ' x ' , ' 3 ' , ' n ' ) | |
track - > tag = = MKTAG ( ' m ' , ' x ' , ' 4 ' , ' p ' ) | | track - > tag = = MKTAG ( ' m ' , ' x ' , ' 4 ' , ' n ' ) | |
track - > tag = = MKTAG ( ' m ' , ' x ' , ' 5 ' , ' p ' ) | | track - > tag = = MKTAG ( ' m ' , ' x ' , ' 5 ' , ' n ' ) ) {
if ( st - > codec - > width ! = 720 | | ( st - > codec - > height ! = 608 & & st - > codec - > height ! = 512 ) ) {
av_log ( s , AV_LOG_ERROR , " D-10/IMX must use 720x608 or 720x512 video resolution \n " ) ;
goto error ;
}
track - > height = track - > tag > > 24 = = ' n ' ? 486 : 576 ;
}
track - > timescale = st - > codec - > time_base . den ;
if ( track - > mode = = MODE_MOV & & track - > timescale > 100000 )
av_log ( s , AV_LOG_WARNING ,
" WARNING codec timebase is very high. If duration is too long, \n "
" file may not be playable by quicktime. Specify a shorter timebase \n "
" or choose different container. \n " ) ;
} else if ( st - > codec - > codec_type = = AVMEDIA_TYPE_AUDIO ) {
track - > timescale = st - > codec - > sample_rate ;
2012-01-22 23:23:23 +01:00
if ( ! st - > codec - > frame_size & & ! av_get_bits_per_sample ( st - > codec - > codec_id ) ) {
av_log ( s , AV_LOG_ERROR , " track %d: codec frame size is not set \n " , i ) ;
goto error ;
} else if ( st - > codec - > codec_id = = CODEC_ID_ADPCM_MS | |
st - > codec - > codec_id = = CODEC_ID_ADPCM_IMA_WAV ) {
2011-01-20 22:14:12 +01:00
if ( ! st - > codec - > block_align ) {
2012-01-22 23:23:23 +01:00
av_log ( s , AV_LOG_ERROR , " track %d: codec block align is not set for adpcm \n " , i ) ;
2011-01-20 22:14:12 +01:00
goto error ;
}
track - > sampleSize = st - > codec - > block_align ;
2012-01-22 23:23:23 +01:00
} else if ( st - > codec - > frame_size > 1 ) { /* assume compressed audio */
2010-05-05 10:41:06 +02:00
track - > audio_vbr = 1 ;
2012-01-22 23:23:23 +01:00
} else {
st - > codec - > frame_size = 1 ;
track - > sampleSize = ( av_get_bits_per_sample ( st - > codec - > codec_id ) > > 3 ) * st - > codec - > channels ;
2010-05-05 10:41:06 +02:00
}
if ( track - > mode ! = MODE_MOV ) {
if ( track - > timescale > UINT16_MAX ) {
av_log ( s , AV_LOG_ERROR , " track %d: output format does not support "
" sample rate %dhz \n " , i , track - > timescale ) ;
goto error ;
}
if ( track - > enc - > codec_id = = CODEC_ID_MP3 & & track - > timescale < 16000 ) {
av_log ( s , AV_LOG_ERROR , " track %d: muxing mp3 at %dhz is not supported \n " ,
i , track - > enc - > sample_rate ) ;
goto error ;
}
}
} else if ( st - > codec - > codec_type = = AVMEDIA_TYPE_SUBTITLE ) {
track - > timescale = st - > codec - > time_base . den ;
}
if ( ! track - > height )
track - > height = st - > codec - > height ;
2012-01-09 16:58:26 +01:00
/* The ism specific timescale isn't mandatory, but is assumed by
* some tools , such as mp4split . */
if ( mov - > mode = = MODE_ISM )
track - > timescale = 10000000 ;
2010-05-05 10:41:06 +02:00
2011-11-29 19:28:15 +01:00
avpriv_set_pts_info ( st , 64 , 1 , track - > timescale ) ;
2012-01-05 12:57:05 +01:00
/* copy extradata if it exists */
if ( st - > codec - > extradata_size ) {
track - > vosLen = st - > codec - > extradata_size ;
track - > vosData = av_malloc ( track - > vosLen ) ;
memcpy ( track - > vosData , st - > codec - > extradata , track - > vosLen ) ;
}
2010-05-05 10:41:06 +02:00
}
2012-01-09 16:58:26 +01:00
if ( mov - > mode = = MODE_ISM ) {
/* If no fragmentation options have been set, set a default. */
if ( ! ( mov - > flags & ( FF_MOV_FLAG_FRAG_KEYFRAME |
FF_MOV_FLAG_FRAG_CUSTOM ) ) & &
! mov - > max_fragment_duration & & ! mov - > max_fragment_size )
mov - > max_fragment_duration = 5000000 ;
mov - > flags | = FF_MOV_FLAG_EMPTY_MOOV | FF_MOV_FLAG_SEPARATE_MOOF ;
2010-05-05 10:41:06 +02:00
}
2011-10-26 22:09:26 +02:00
if ( mov - > reserved_moov_size ) {
mov - > reserved_moov_pos = avio_tell ( pb ) ;
avio_skip ( pb , mov - > reserved_moov_size ) ;
}
2012-01-05 12:57:05 +01:00
/* Set the FRAGMENT flag if any of the fragmentation methods are
* enabled . */
if ( mov - > max_fragment_duration | | mov - > max_fragment_size | |
mov - > flags & ( FF_MOV_FLAG_EMPTY_MOOV |
2012-01-20 18:29:33 +01:00
FF_MOV_FLAG_FRAG_KEYFRAME |
FF_MOV_FLAG_FRAG_CUSTOM ) )
2012-01-05 12:57:05 +01:00
mov - > flags | = FF_MOV_FLAG_FRAGMENT ;
if ( ! ( mov - > flags & FF_MOV_FLAG_EMPTY_MOOV ) )
mov_write_mdat_tag ( pb , mov ) ;
2011-07-07 11:25:03 +02:00
# if FF_API_TIMESTAMP
if ( s - > timestamp )
mov - > time = s - > timestamp ;
else
# endif
2011-07-13 11:45:17 +02:00
if ( t = av_dict_get ( s - > metadata , " creation_time " , NULL , 0 ) )
mov - > time = ff_iso8601_to_unix_time ( t - > value ) ;
2012-01-09 17:35:01 +01:00
if ( mov - > time )
mov - > time + = 0x7C25B080 ; // 1970 based -> 1904 based
2010-05-05 10:41:10 +02:00
if ( mov - > chapter_track )
mov_create_chapter_track ( s , mov - > chapter_track ) ;
2010-05-05 10:41:06 +02:00
2011-05-20 11:27:02 +02:00
if ( mov - > flags & FF_MOV_FLAG_RTP_HINT ) {
2010-05-18 21:47:24 +02:00
/* Initialize the hint tracks for each audio and video stream */
for ( i = 0 ; i < s - > nb_streams ; i + + ) {
AVStream * st = s - > streams [ i ] ;
if ( st - > codec - > codec_type = = AVMEDIA_TYPE_VIDEO | |
st - > codec - > codec_type = = AVMEDIA_TYPE_AUDIO ) {
ff_mov_init_hinting ( s , hint_track , i ) ;
hint_track + + ;
}
}
}
2011-03-14 20:39:06 +01:00
avio_flush ( pb ) ;
2010-05-05 10:41:06 +02:00
2012-01-05 12:57:05 +01:00
if ( mov - > flags & FF_MOV_FLAG_EMPTY_MOOV ) {
mov_write_moov_tag ( pb , mov , s ) ;
mov - > fragments + + ;
}
2010-05-05 10:41:06 +02:00
return 0 ;
error :
av_freep ( & mov - > tracks ) ;
return - 1 ;
}
2003-08-26 22:23:13 +02:00
static int mov_write_trailer ( AVFormatContext * s )
{
2009-02-28 17:02:29 +01:00
MOVMuxContext * mov = s - > priv_data ;
2011-02-20 11:04:12 +01:00
AVIOContext * pb = s - > pb ;
2003-08-26 22:23:13 +02:00
int res = 0 ;
2006-06-24 20:09:20 +02:00
int i ;
2003-08-26 22:23:13 +02:00
2011-03-03 20:11:45 +01:00
int64_t moov_pos = avio_tell ( pb ) ;
2003-08-26 22:23:13 +02:00
2012-01-05 12:57:05 +01:00
if ( ! ( mov - > flags & FF_MOV_FLAG_FRAGMENT ) ) {
2012-01-25 11:40:05 +01:00
/* Write size of mdat tag */
if ( mov - > mdat_size + 8 < = UINT32_MAX ) {
avio_seek ( pb , mov - > mdat_pos , SEEK_SET ) ;
avio_wb32 ( pb , mov - > mdat_size + 8 ) ;
} else {
/* overwrite 'wide' placeholder atom */
avio_seek ( pb , mov - > mdat_pos - 8 , SEEK_SET ) ;
/* special value: real atom size will be 64 bit value after
* tag field */
avio_wb32 ( pb , 1 ) ;
ffio_wfourcc ( pb , " mdat " ) ;
avio_wb64 ( pb , mov - > mdat_size + 16 ) ;
}
2012-01-26 01:52:29 +01:00
avio_seek ( pb , mov - > reserved_moov_size ? mov - > reserved_moov_pos : moov_pos , SEEK_SET ) ;
2003-08-26 22:23:13 +02:00
2012-01-25 11:40:05 +01:00
mov_write_moov_tag ( pb , mov , s ) ;
2012-01-26 01:52:29 +01:00
if ( mov - > reserved_moov_size ) {
int64_t size = mov - > reserved_moov_size - ( avio_tell ( pb ) - mov - > reserved_moov_pos ) ;
if ( size < 8 ) {
av_log ( s , AV_LOG_ERROR , " reserved_moov_size is too small, needed % " PRId64 " additional \n " , 8 - size ) ;
return - 1 ;
}
avio_wb32 ( pb , size ) ;
ffio_wfourcc ( pb , " free " ) ;
for ( i = 0 ; i < size ; i + + )
avio_w8 ( pb , 0 ) ;
avio_seek ( pb , moov_pos , SEEK_SET ) ;
2011-10-26 22:09:26 +02:00
}
2012-01-05 12:57:05 +01:00
} else {
mov_flush_fragment ( s ) ;
mov_write_mfra_tag ( pb , mov ) ;
2011-10-26 22:09:26 +02:00
}
2003-08-26 22:23:13 +02:00
2010-05-05 10:41:10 +02:00
if ( mov - > chapter_track )
av_freep ( & mov - > tracks [ mov - > chapter_track ] . enc ) ;
2006-06-25 02:10:52 +02:00
for ( i = 0 ; i < mov - > nb_streams ; i + + ) {
2010-05-18 21:47:24 +02:00
if ( mov - > tracks [ i ] . tag = = MKTAG ( ' r ' , ' t ' , ' p ' , ' ' ) )
ff_mov_close_hinting ( & mov - > tracks [ i ] ) ;
2006-06-24 20:09:20 +02:00
av_freep ( & mov - > tracks [ i ] . cluster ) ;
2012-01-05 12:57:05 +01:00
av_freep ( & mov - > tracks [ i ] . frag_info ) ;
2006-06-24 20:09:20 +02:00
2008-02-06 19:57:00 +01:00
if ( mov - > tracks [ i ] . vosLen ) av_free ( mov - > tracks [ i ] . vosData ) ;
2004-01-08 20:01:16 +01:00
2003-08-26 22:23:13 +02:00
}
2004-02-14 20:08:09 +01:00
2011-03-14 20:39:06 +01:00
avio_flush ( pb ) ;
2003-08-26 22:23:13 +02:00
2009-03-22 04:52:55 +01:00
av_freep ( & mov - > tracks ) ;
2003-08-26 22:23:13 +02:00
return res ;
}
2009-01-14 00:44:16 +01:00
# if CONFIG_MOV_MUXER
2011-10-03 19:14:03 +02:00
MOV_CLASS ( mov )
2011-01-25 23:03:28 +01:00
AVOutputFormat ff_mov_muxer = {
2011-07-16 22:18:12 +02:00
. name = " mov " ,
. long_name = NULL_IF_CONFIG_SMALL ( " MOV format " ) ,
. extensions = " mov " ,
. priv_data_size = sizeof ( MOVMuxContext ) ,
. audio_codec = CODEC_ID_AAC ,
2011-08-20 16:14:58 +02:00
# if CONFIG_LIBX264_ENCODER
. video_codec = CODEC_ID_H264 ,
# else
2011-07-16 22:18:12 +02:00
. video_codec = CODEC_ID_MPEG4 ,
2011-08-20 16:14:58 +02:00
# endif
2011-07-16 22:18:12 +02:00
. write_header = mov_write_header ,
. write_packet = ff_mov_write_packet ,
. write_trailer = mov_write_trailer ,
2012-01-20 18:29:33 +01:00
. flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH ,
2008-08-24 18:51:50 +02:00
. codec_tag = ( const AVCodecTag * const [ ] ) { codec_movvideo_tags , codec_movaudio_tags , 0 } ,
2011-05-21 13:57:04 +02:00
. priv_class = & mov_muxer_class ,
2003-08-26 22:23:13 +02:00
} ;
2006-07-10 23:14:37 +02:00
# endif
2009-01-14 00:44:16 +01:00
# if CONFIG_TGP_MUXER
2011-10-03 19:14:03 +02:00
MOV_CLASS ( tgp )
2011-01-25 23:03:28 +01:00
AVOutputFormat ff_tgp_muxer = {
2011-07-16 22:18:12 +02:00
. name = " 3gp " ,
. long_name = NULL_IF_CONFIG_SMALL ( " 3GP format " ) ,
. extensions = " 3gp " ,
. priv_data_size = sizeof ( MOVMuxContext ) ,
. audio_codec = CODEC_ID_AMR_NB ,
. video_codec = CODEC_ID_H263 ,
. write_header = mov_write_header ,
. write_packet = ff_mov_write_packet ,
. write_trailer = mov_write_trailer ,
2012-01-20 18:29:33 +01:00
. flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH ,
2008-08-24 18:51:50 +02:00
. codec_tag = ( const AVCodecTag * const [ ] ) { codec_3gp_tags , 0 } ,
2011-10-03 19:14:03 +02:00
. priv_class = & tgp_muxer_class ,
2003-08-26 22:23:13 +02:00
} ;
2006-07-10 23:14:37 +02:00
# endif
2009-01-14 00:44:16 +01:00
# if CONFIG_MP4_MUXER
2011-10-03 19:14:03 +02:00
MOV_CLASS ( mp4 )
2011-01-25 23:03:28 +01:00
AVOutputFormat ff_mp4_muxer = {
2011-07-16 22:18:12 +02:00
. name = " mp4 " ,
. long_name = NULL_IF_CONFIG_SMALL ( " MP4 format " ) ,
. mime_type = " application/mp4 " ,
. extensions = " mp4 " ,
. priv_data_size = sizeof ( MOVMuxContext ) ,
. audio_codec = CODEC_ID_AAC ,
2011-08-20 16:14:58 +02:00
# if CONFIG_LIBX264_ENCODER
. video_codec = CODEC_ID_H264 ,
# else
2011-07-16 22:18:12 +02:00
. video_codec = CODEC_ID_MPEG4 ,
2011-08-20 16:14:58 +02:00
# endif
2011-07-16 22:18:12 +02:00
. write_header = mov_write_header ,
. write_packet = ff_mov_write_packet ,
. write_trailer = mov_write_trailer ,
2012-01-20 18:29:33 +01:00
. flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH ,
2008-08-24 18:51:50 +02:00
. codec_tag = ( const AVCodecTag * const [ ] ) { ff_mp4_obj_type , 0 } ,
2011-10-03 19:14:03 +02:00
. priv_class = & mp4_muxer_class ,
2003-08-26 22:23:13 +02:00
} ;
2006-07-10 23:14:37 +02:00
# endif
2009-01-14 00:44:16 +01:00
# if CONFIG_PSP_MUXER
2011-10-03 19:14:03 +02:00
MOV_CLASS ( psp )
2011-01-25 23:03:28 +01:00
AVOutputFormat ff_psp_muxer = {
2011-07-16 22:18:12 +02:00
. name = " psp " ,
. long_name = NULL_IF_CONFIG_SMALL ( " PSP MP4 format " ) ,
. extensions = " mp4,psp " ,
. priv_data_size = sizeof ( MOVMuxContext ) ,
. audio_codec = CODEC_ID_AAC ,
2011-08-20 16:14:58 +02:00
# if CONFIG_LIBX264_ENCODER
. video_codec = CODEC_ID_H264 ,
# else
2011-07-16 22:18:12 +02:00
. video_codec = CODEC_ID_MPEG4 ,
2011-08-20 16:14:58 +02:00
# endif
2011-07-16 22:18:12 +02:00
. write_header = mov_write_header ,
. write_packet = ff_mov_write_packet ,
. write_trailer = mov_write_trailer ,
2012-01-20 18:29:33 +01:00
. flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH ,
2008-08-24 18:51:50 +02:00
. codec_tag = ( const AVCodecTag * const [ ] ) { ff_mp4_obj_type , 0 } ,
2011-10-03 19:14:03 +02:00
. priv_class = & psp_muxer_class ,
2005-01-27 15:48:15 +01:00
} ;
2006-07-10 23:14:37 +02:00
# endif
2009-01-14 00:44:16 +01:00
# if CONFIG_TG2_MUXER
2011-10-03 19:14:03 +02:00
MOV_CLASS ( tg2 )
2011-01-25 23:03:28 +01:00
AVOutputFormat ff_tg2_muxer = {
2011-07-16 22:18:12 +02:00
. name = " 3g2 " ,
. long_name = NULL_IF_CONFIG_SMALL ( " 3GP2 format " ) ,
. extensions = " 3g2 " ,
. priv_data_size = sizeof ( MOVMuxContext ) ,
. audio_codec = CODEC_ID_AMR_NB ,
. video_codec = CODEC_ID_H263 ,
. write_header = mov_write_header ,
. write_packet = ff_mov_write_packet ,
. write_trailer = mov_write_trailer ,
2012-01-20 18:29:33 +01:00
. flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH ,
2008-08-24 18:51:50 +02:00
. codec_tag = ( const AVCodecTag * const [ ] ) { codec_3gp_tags , 0 } ,
2011-10-03 19:14:03 +02:00
. priv_class = & tg2_muxer_class ,
2005-02-17 00:14:38 +01:00
} ;
2006-07-10 23:14:37 +02:00
# endif
2009-01-14 00:44:16 +01:00
# if CONFIG_IPOD_MUXER
2011-10-03 19:14:03 +02:00
MOV_CLASS ( ipod )
2011-01-25 23:03:28 +01:00
AVOutputFormat ff_ipod_muxer = {
2011-07-16 22:18:12 +02:00
. name = " ipod " ,
. long_name = NULL_IF_CONFIG_SMALL ( " iPod H.264 MP4 format " ) ,
. mime_type = " application/mp4 " ,
. extensions = " m4v,m4a " ,
. priv_data_size = sizeof ( MOVMuxContext ) ,
. audio_codec = CODEC_ID_AAC ,
. video_codec = CODEC_ID_H264 ,
. write_header = mov_write_header ,
. write_packet = ff_mov_write_packet ,
. write_trailer = mov_write_trailer ,
2012-01-20 18:29:33 +01:00
. flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH ,
2009-03-11 08:59:38 +01:00
. codec_tag = ( const AVCodecTag * const [ ] ) { codec_ipod_tags , 0 } ,
2011-10-03 19:14:03 +02:00
. priv_class = & ipod_muxer_class ,
2008-03-16 14:36:36 +01:00
} ;
# endif
2012-01-09 16:58:26 +01:00
# if CONFIG_ISMV_MUXER
MOV_CLASS ( ismv )
AVOutputFormat ff_ismv_muxer = {
. name = " ismv " ,
. long_name = NULL_IF_CONFIG_SMALL ( " ISMV/ISMA (Smooth Streaming) format " ) ,
. mime_type = " application/mp4 " ,
. extensions = " ismv,isma " ,
. priv_data_size = sizeof ( MOVMuxContext ) ,
. audio_codec = CODEC_ID_AAC ,
. video_codec = CODEC_ID_H264 ,
. write_header = mov_write_header ,
. write_packet = ff_mov_write_packet ,
. write_trailer = mov_write_trailer ,
. flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH ,
. codec_tag = ( const AVCodecTag * const [ ] ) { ff_mp4_obj_type , 0 } ,
. priv_class = & ismv_muxer_class ,
} ;
# endif