2003-08-26 20:23:13 +00:00
/*
2006-10-23 08:57:54 +00:00
* MOV, 3GP, MP4 muxer
2009-01-19 15:46:40 +00:00
* Copyright (c) 2003 Thomas Raivio
* Copyright (c) 2004 Gildas Bazin <gbazin at videolan dot org>
2009-03-15 10:53:12 +00:00
* Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com>
2003-08-26 20:23:13 +00:00
*
2006-10-07 15:30:46 +00:00
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
2003-08-26 20:23:13 +00:00
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
2006-10-07 15:30:46 +00:00
* version 2.1 of the License, or (at your option) any later version.
2003-08-26 20:23:13 +00:00
*
2006-10-07 15:30:46 +00:00
* FFmpeg is distributed in the hope that it will be useful,
2003-08-26 20:23:13 +00: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 15:30:46 +00:00
* License along with FFmpeg; if not, write to the Free Software
2006-01-12 22:43:26 +00:00
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2003-08-26 20:23:13 +00:00
*/
2009-03-15 10:49:52 +00:00
2010-05-18 19:38:37 +00:00
#include "movenc.h"
2003-08-26 20:23:13 +00:00
#include "avformat.h"
2011-02-24 07:36:02 +01:00
#include "avio_internal.h"
2006-07-12 00:09:34 +00:00
#include "riff.h"
2003-08-26 20:23:13 +00:00
#include "avio.h"
2006-08-01 14:58:15 +00:00
#include "isom.h"
2008-01-11 01:24:55 +00:00
#include "avc.h"
2009-04-13 16:20:26 +00:00
#include "libavcodec/get_bits.h"
2009-04-12 08:35:26 +00:00
#include "libavcodec/put_bits.h"
2010-05-18 19:47:24 +00:00
#include "internal.h"
#include "libavutil/avstring.h"
2011-11-27 14:04:16 +00:00
#include "libavutil/intfloat.h"
2011-06-04 12:58:23 +01:00
#include "libavutil/mathematics.h"
2011-05-21 14:57:04 +03:00
#include "libavutil/opt.h"
2011-05-22 12:46:29 +02:00
#include "libavutil/dict.h"
2011-05-21 14:58:43 +03:00
#include "rtpenc.h"
2011-12-03 13:32:45 -05:00
#include "mov_chan.h"
2003-08-26 20:23:13 +00:00
2003-09-09 23:03:04 +00:00
#undef NDEBUG
#include <assert.h>
2011-05-21 14:57:04 +03: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 },
2011-12-06 16:21:10 +01:00
{ "frag_size" , "maximum fragment size" , offsetof ( MOVMuxContext , max_fragment_size ), AV_OPT_TYPE_INT , {. dbl = 0 }, 0 , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM , 0 },
2011-12-06 16:15:35 +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 , 0 },
2011-05-21 14:58:43 +03: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 11:44:25 -07: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 },
2011-05-21 14:57:04 +03: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 14:57:04 +03:00
};
2011-12-06 16:15:35 +01:00
static int update_first_fragment ( AVFormatContext * s );
2007-06-12 18:50:50 +00: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 20:23:13 +00: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-09 23:03:04 +00:00
return curpos - pos ;
2003-08-26 20:23:13 +00:00
}
2003-11-03 21:51:07 +00: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 20:23:13 +00:00
{
int i ;
2006-01-23 14:28:50 +00: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 14:12:03 +00:00
if ( pos > UINT32_MAX ) {
mode64 = 1 ;
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , "co64" );
2006-01-23 14:12:03 +00: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 20:23:13 +00: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 14:12:03 +00:00
if ( mode64 == 1 )
2011-02-21 19:28:17 +01:00
avio_wb64 ( pb , track -> cluster [ i ]. pos );
2006-01-23 14:12:03 +00:00
else
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , track -> cluster [ i ]. pos );
2003-08-26 20:23:13 +00:00
}
2008-06-11 08:50:41 +00:00
return updateSize ( pb , pos );
2003-08-26 20:23:13 +00:00
}
2003-11-03 21:51:07 +00: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 20:23:13 +00:00
{
2003-09-28 21:09:32 +00:00
int equalChunks = 1 ;
2003-11-03 21:51:07 +00:00
int i , j , entries = 0 , tst = - 1 , oldtst = - 1 ;
2003-08-26 20:23:13 +00: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 20:23:13 +00:00
2003-09-28 21:09:32 +00:00
for ( i = 0 ; i < track -> entry ; i ++ ) {
2006-06-24 18:09:20 +00:00
tst = track -> cluster [ i ]. size / track -> cluster [ i ]. entries ;
2003-11-03 21:51:07 +00:00
if ( oldtst != - 1 && tst != oldtst ) {
equalChunks = 0 ;
2003-09-28 21:09:32 +00:00
}
oldtst = tst ;
2006-06-24 18:09:20 +00:00
entries += track -> cluster [ i ]. entries ;
2003-08-26 20:23:13 +00:00
}
2003-11-03 21:51:07 +00:00
if ( equalChunks ) {
2006-06-24 18:09:20 +00:00
int sSize = track -> cluster [ 0 ]. size / track -> cluster [ 0 ]. entries ;
2011-02-27 16:29:21 -08: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-09-28 21:09:32 +00: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 21:09:32 +00:00
for ( i = 0 ; i < track -> entry ; i ++ ) {
2008-02-06 18:57:00 +00: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 18:09:20 +00:00
track -> cluster [ i ]. entries );
2003-11-03 21:51:07 +00:00
}
2003-08-26 20:23:13 +00:00
}
}
2008-06-11 08:50:41 +00:00
return updateSize ( pb , pos );
2003-08-26 20:23:13 +00:00
}
2003-11-03 21:51:07 +00: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 20:23:13 +00:00
{
2006-01-23 14:12:03 +00:00
int index = 0 , oldval = - 1 , i ;
2008-10-03 10:16:29 +00:00
int64_t entryPos , curpos ;
2003-09-28 21:09:32 +00: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 21:09:32 +00: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 21:09:32 +00: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 18:09:20 +00:00
oldval = track -> cluster [ i ]. samplesInChunk ;
2003-09-28 21:09:32 +00:00
index ++ ;
2003-08-26 20:23:13 +00: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 20:23:13 +00:00
2008-06-11 08:50:41 +00:00
return updateSize ( pb , pos );
2003-08-26 20:23:13 +00:00
}
2003-11-03 21:51:07 +00: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 20:23:13 +00:00
{
2008-10-03 10:16:29 +00:00
int64_t curpos , entryPos ;
2006-01-23 14:12:03 +00: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 21:09:32 +00:00
for ( i = 0 ; i < track -> entry ; i ++ ) {
2009-05-15 06:11:53 +00:00
if ( track -> cluster [ i ]. flags & flag ) {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , i + 1 );
2003-09-28 21:09:32 +00: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 08:50:41 +00:00
return updateSize ( pb , pos );
2003-08-26 20:23:13 +00:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_amr_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 20:23:13 +00: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 21:00:52 +00: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 21:00:52 +00:00
return 0x11 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_ac3_tag ( AVIOContext * pb , MOVTrack * track )
2008-09-03 19:05:22 +00: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 19:05:22 +00:00
2011-09-09 23:46:00 +02:00
init_get_bits ( & gbc , track -> vosData + 4 , ( track -> vosLen - 4 ) * 8 );
2008-09-03 19:05:22 +00: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 19:05:22 +00:00
return 11 ;
}
2008-04-24 13:59:39 +00: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 13:59:39 +00:00
*/
2011-02-20 11:04:12 +01:00
static int mov_write_extradata_tag ( AVIOContext * pb , MOVTrack * track )
2008-04-24 13:59:39 +00:00
{
2011-02-21 19:28:17 +01:00
avio_write ( pb , track -> enc -> extradata , track -> enc -> extradata_size );
2008-04-24 13:59:39 +00: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 20:05:02 +00: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 20:05:02 +00:00
return 10 ;
}
2011-02-20 11:04:12 +01:00
static void putDescr ( AVIOContext * pb , int tag , unsigned int size )
2006-05-13 18:01:16 +00:00
{
2011-03-30 14:08:16 -07:00
int i = 3 ;
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , tag );
2006-05-13 18:45:29 +00: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 18:01:16 +00:00
}
2011-04-16 16:19:10 -07: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 18:01:16 +00:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb );
2011-03-30 14:08:16 -07:00
int decoderSpecificInfoLen = track -> vosLen ? 5 + track -> vosLen : 0 ;
2011-04-16 16:19:10 -07:00
unsigned avg_bitrate ;
2006-05-13 18:01:16 +00: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 18:01:16 +00:00
// ES descriptor
2011-03-30 14:08:16 -07: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 18:01:16 +00:00
// DecoderConfig descriptor
putDescr ( pb , 0x04 , 13 + decoderSpecificInfoLen );
// Object type indication
2008-09-02 20:48:45 +00:00
if (( track -> enc -> codec_id == CODEC_ID_MP2 ||
track -> enc -> codec_id == CODEC_ID_MP3 ) &&
2008-09-03 19:42:09 +00:00
track -> enc -> sample_rate > 24000 )
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0x6B ); // 11172-3
2008-09-02 20:48:45 +00: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 18:01:16 +00: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-30 23:30:55 +00: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 18:01:16 +00:00
else
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0x11 ); // flags (= Visualstream)
2006-05-13 18:01:16 +00: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 18:01:16 +00:00
2011-04-16 16:19:10 -07: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 18:01:16 +00:00
2008-04-24 17:22:39 +00:00
if ( track -> vosLen ) {
2006-05-13 18:01:16 +00: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 18:01:16 +00:00
}
// SL descriptor
putDescr ( pb , 0x06 , 1 );
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0x02 );
2008-06-11 08:50:41 +00:00
return updateSize ( pb , pos );
2006-05-13 18:01:16 +00:00
}
2009-11-29 02:27:08 +00: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 13:14:12 -08: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 13:14:12 -08:00
ff_put_wav_header ( pb , track -> enc );
return updateSize ( pb , pos );
}
2011-12-03 13:32:45 -05: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 19:08:09 +00:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb );
2004-02-14 19:08:09 +00: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 19:08:09 +00: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 19:08:09 +00:00
2006-05-13 20:05:02 +00:00
if ( track -> enc -> codec_id == CODEC_ID_AAC ) {
2006-06-19 11:28:28 +00: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 20:05:02 +00:00
mov_write_esds_tag ( pb , track );
2009-11-29 02:27:08 +00:00
} else if ( mov_pcm_le_gt16 ( track -> enc -> codec_id )) {
2006-05-13 20:05:02 +00:00
mov_write_enda_tag ( pb );
2006-05-13 21:00:52 +00:00
} else if ( track -> enc -> codec_id == CODEC_ID_AMR_NB ) {
2006-07-06 14:38:50 +00:00
mov_write_amr_tag ( pb , track );
2008-09-03 19:05:22 +00:00
} else if ( track -> enc -> codec_id == CODEC_ID_AC3 ) {
2011-12-06 17:35:37 -05:00
mov_write_chan_tag ( pb , track );
2008-09-03 19:05:22 +00:00
mov_write_ac3_tag ( pb , track );
2008-04-24 13:59:39 +00:00
} else if ( track -> enc -> codec_id == CODEC_ID_ALAC ) {
mov_write_extradata_tag ( pb , track );
2011-01-20 13:14:12 -08: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 20:05:02 +00:00
}
2004-02-14 19:08:09 +00:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 8 ); /* size */
avio_wb32 ( pb , 0 ); /* null tag */
2004-02-14 19:08:09 +00:00
2008-06-11 08:50:41 +00:00
return updateSize ( pb , pos );
2004-02-14 19:08:09 +00:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_glbl_tag ( AVIOContext * pb , MOVTrack * track )
2007-12-19 16:00:08 +00: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 16:00:08 +00:00
return 8 + track -> vosLen ;
}
2009-11-29 02:03:24 +00: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 20:23:13 +00:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb );
2009-11-29 02:03:24 +00:00
int version = 0 ;
uint32_t tag = track -> tag ;
if ( track -> mode == MODE_MOV ) {
2011-12-02 15:51:52 -05:00
if ( mov_get_lpcm_flags ( track -> enc -> codec_id ))
tag = AV_RL32 ( "lpcm" );
version = 2 ;
2009-11-29 02:03:24 +00:00
}
2005-12-17 18:14:38 +00: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 19:08:09 +00:00
2003-11-03 21:51:07 +00: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 20:23:13 +00:00
2009-11-29 02:03:24 +00: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 );
2011-11-27 14:04:16 +00:00
avio_wb64 ( pb , av_double2int ( track -> timescale ));
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 );
2011-12-02 15:51:52 -05:00
avio_wb32 ( pb , track -> audio_vbr ? track -> enc -> frame_size : 1 );
2009-11-29 02:03:24 +00:00
} else {
2011-12-02 15:51:52 -05:00
/* reserved for mp4/3gp */
avio_wb16 ( pb , 2 );
avio_wb16 ( pb , 16 );
avio_wb16 ( pb , 0 );
2004-07-28 09:40:59 +00:00
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 0 ); /* packet size (= 0) */
avio_wb16 ( pb , track -> timescale ); /* Time scale */
avio_wb16 ( pb , 0 ); /* Reserved */
2009-11-29 02:03:24 +00:00
}
2003-08-26 20:23:13 +00:00
2006-05-13 22:25:17 +00:00
if ( track -> mode == MODE_MOV &&
( track -> enc -> codec_id == CODEC_ID_AAC ||
2008-09-04 18:25:55 +00:00
track -> enc -> codec_id == CODEC_ID_AC3 ||
2006-05-13 22:25:17 +00:00
track -> enc -> codec_id == CODEC_ID_AMR_NB ||
2009-11-29 02:27:08 +00:00
track -> enc -> codec_id == CODEC_ID_ALAC ||
2011-01-20 13:14:12 -08:00
track -> enc -> codec_id == CODEC_ID_ADPCM_MS ||
track -> enc -> codec_id == CODEC_ID_ADPCM_IMA_WAV ||
2009-11-29 02:27:08 +00:00
mov_pcm_le_gt16 ( track -> enc -> codec_id )))
2006-05-13 20:05:02 +00:00
mov_write_wave_tag ( pb , track );
2008-01-28 13:44:27 +00:00
else if ( track -> tag == MKTAG ( 'm' , 'p' , '4' , 'a' ))
2006-05-13 21:00:52 +00:00
mov_write_esds_tag ( pb , track );
else if ( track -> enc -> codec_id == CODEC_ID_AMR_NB )
2006-07-06 14:38:50 +00:00
mov_write_amr_tag ( pb , track );
2008-09-04 18:25:55 +00:00
else if ( track -> enc -> codec_id == CODEC_ID_AC3 )
mov_write_ac3_tag ( pb , track );
2008-09-04 18:26:31 +00:00
else if ( track -> enc -> codec_id == CODEC_ID_ALAC )
2008-06-11 08:17:38 +00:00
mov_write_extradata_tag ( pb , track );
2007-12-19 16:00:08 +00:00
else if ( track -> vosLen > 0 )
mov_write_glbl_tag ( pb , track );
2006-05-13 21:00:52 +00:00
2008-06-11 08:50:41 +00:00
return updateSize ( pb , pos );
2003-08-26 20:23:13 +00:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_d263_tag ( AVIOContext * pb )
2003-08-26 20:23:13 +00: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 15:04:15 +00: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 20:23:13 +00:00
return 0xf ;
}
2003-09-28 21:09:32 +00: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 20:23:13 +00: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 21:09:32 +00:00
return 0x15 ;
2003-08-26 20:23:13 +00:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_avcc_tag ( AVIOContext * pb , MOVTrack * track )
2008-01-11 01:04:01 +00:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb );
2008-01-11 01:04:01 +00: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-11 23:33:32 +00:00
ff_isom_write_avcc ( pb , track -> vosData , track -> vosLen );
2006-03-11 18:18:17 +00:00
return updateSize ( pb , pos );
}
2007-10-08 11:27:18 +00: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 11:27:18 +00: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 10:22:05 -07: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 11:27:18 +00: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 11:27:18 +00: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 11:27:18 +00: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 11:27:18 +00: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 10:44:25 +00:00
if ( track -> enc -> height == 1080 )
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 5 ); /* unknown */
2007-12-03 10:44:25 +00:00
else
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 6 ); /* unknown */
2007-10-08 11:27:18 +00:00
}
/* padding */
for ( i = 0 ; i < 10 ; i ++ )
2011-02-21 19:28:17 +01:00
avio_wb64 ( pb , 0 );
2007-10-08 11:27:18 +00:00
/* extra padding for stsd needed */
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 );
2007-10-08 11:27:18 +00:00
return 0 ;
}
2009-04-07 22:19:58 +00:00
static int mp4_get_codec_tag ( AVFormatContext * s , MOVTrack * track )
2006-03-08 00:39:23 +00:00
{
2006-05-13 18:01:16 +00:00
int tag = track -> enc -> codec_tag ;
2009-04-07 22:21:43 +00:00
2009-06-22 23:09:34 +00:00
if ( ! ff_codec_get_tag ( ff_mp4_obj_type , track -> enc -> codec_id ))
2009-04-07 22:21:43 +00: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-30 23:30:55 +00: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-07 22:19:58 +00:00
return tag ;
}
2009-04-07 22:34:05 +00: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-07 22:19:58 +00:00
static int ipod_get_codec_tag ( AVFormatContext * s , MOVTrack * track )
{
int tag = track -> enc -> codec_tag ;
2009-04-07 22:26:47 +00:00
// keep original tag for subs, ipod supports both formats
2010-03-30 23:30:55 +00:00
if ( ! ( track -> enc -> codec_type == AVMEDIA_TYPE_SUBTITLE &&
2009-04-07 22:21:43 +00:00
( tag == MKTAG ( 't' , 'x' , '3' , 'g' ) ||
2009-04-07 22:28:39 +00:00
tag == MKTAG ( 't' , 'e' , 'x' , 't' ))))
2009-06-22 23:09:34 +00:00
tag = ff_codec_get_tag ( codec_ipod_tags , track -> enc -> codec_id );
2009-04-07 22:31:17 +00:00
2010-01-17 15:57:51 +00:00
if ( ! av_match_ext ( s -> filename , "m4a" ) && ! av_match_ext ( s -> filename , "m4v" ))
2009-04-07 22:21:43 +00:00
av_log ( s , AV_LOG_WARNING , "Warning, extension is not .m4a nor .m4v "
"Quicktime/Ipod might not play the file \n " );
2009-04-07 22:19:58 +00:00
return tag ;
}
static int mov_get_dv_codec_tag ( AVFormatContext * s , MOVTrack * track )
{
int tag ;
2011-03-01 23:54:29 +00: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-07 22:19:58 +00:00
return tag ;
}
2009-04-07 22:34:05 +00: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-04 23:30:58 +00:00
{ PIX_FMT_RGB555BE , MKTAG ( 'r' , 'a' , 'w' , ' ' ), 16 },
2009-05-10 20:41:01 +00: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 09:14:00 +00:00
{ PIX_FMT_GRAY16BE , MKTAG ( 'b' , '1' , '6' , 'g' ), 16 },
2009-04-07 22:34:05 +00:00
{ PIX_FMT_RGB24 , MKTAG ( 'r' , 'a' , 'w' , ' ' ), 24 },
2009-05-10 20:41:01 +00:00
{ PIX_FMT_BGR24 , MKTAG ( '2' , '4' , 'B' , 'G' ), 24 },
2009-05-10 20:12:36 +00:00
{ PIX_FMT_ARGB , MKTAG ( 'r' , 'a' , 'w' , ' ' ), 32 },
2009-05-10 20:14:42 +00:00
{ PIX_FMT_BGRA , MKTAG ( 'B' , 'G' , 'R' , 'A' ), 32 },
2009-05-10 20:11:11 +00:00
{ PIX_FMT_RGBA , MKTAG ( 'R' , 'G' , 'B' , 'A' ), 32 },
2010-06-04 23:30:58 +00:00
{ PIX_FMT_ABGR , MKTAG ( 'A' , 'B' , 'G' , 'R' ), 32 },
2010-06-15 09:14:00 +00:00
{ PIX_FMT_RGB48BE , MKTAG ( 'b' , '4' , '8' , 'r' ), 48 },
2009-04-07 22:34:05 +00:00
};
2009-04-07 22:19:58 +00:00
static int mov_get_rawvideo_codec_tag ( AVFormatContext * s , MOVTrack * track )
{
int tag = track -> enc -> codec_tag ;
2009-04-07 22:21:43 +00: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-07 22:19:58 +00: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 19:56:31 +00:00
( track -> enc -> codec_id == CODEC_ID_DVVIDEO ||
2009-04-07 22:19:58 +00:00
track -> enc -> codec_id == CODEC_ID_RAWVIDEO ||
2011-01-07 19:55:08 +00:00
track -> enc -> codec_id == CODEC_ID_H263 ||
2009-04-07 22:19:58 +00:00
av_get_bits_per_sample ( track -> enc -> codec_id )))) { // pcm audio
2009-04-07 22:31:17 +00:00
if ( track -> enc -> codec_id == CODEC_ID_DVVIDEO )
2009-04-07 22:19:58 +00:00
tag = mov_get_dv_codec_tag ( s , track );
2009-04-07 22:31:17 +00:00
else if ( track -> enc -> codec_id == CODEC_ID_RAWVIDEO )
2009-04-07 22:19:58 +00:00
tag = mov_get_rawvideo_codec_tag ( s , track );
2010-03-30 23:30:55 +00:00
else if ( track -> enc -> codec_type == AVMEDIA_TYPE_VIDEO ) {
2009-06-22 23:09:34 +00:00
tag = ff_codec_get_tag ( codec_movvideo_tags , track -> enc -> codec_id );
2009-04-07 22:31:53 +00:00
if ( ! tag ) { // if no mac fcc found, try with Microsoft tags
2009-06-22 23:09:34 +00:00
tag = ff_codec_get_tag ( ff_codec_bmp_tags , track -> enc -> codec_id );
2009-04-07 22:31:53 +00: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-07 22:31:53 +00:00
"the file may be unplayable! \n " );
}
2010-03-30 23:30:55 +00:00
} else if ( track -> enc -> codec_type == AVMEDIA_TYPE_AUDIO ) {
2009-06-22 23:09:34 +00:00
tag = ff_codec_get_tag ( codec_movaudio_tags , track -> enc -> codec_id );
2009-04-07 22:31:53 +00:00
if ( ! tag ) { // if no mac fcc found, try with Microsoft tags
2009-06-22 23:09:34 +00:00
int ms_tag = ff_codec_get_tag ( ff_codec_wav_tags , track -> enc -> codec_id );
2009-04-07 22:31:53 +00: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-07 22:31:53 +00:00
"the file may be unplayable! \n " );
2008-01-28 15:23:37 +00:00
}
2009-04-07 22:31:53 +00:00
}
2010-03-30 23:30:55 +00:00
} else if ( track -> enc -> codec_type == AVMEDIA_TYPE_SUBTITLE )
2009-06-22 23:09:34 +00:00
tag = ff_codec_get_tag ( ff_codec_movsubtitle_tags , track -> enc -> codec_id );
2008-01-28 15:22:07 +00:00
}
2009-04-07 22:21:43 +00:00
2006-03-08 00:39:23 +00:00
return tag ;
}
2009-04-07 22:34:05 +00: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-07 22:19:58 +00:00
static int mov_find_codec_tag ( AVFormatContext * s , MOVTrack * track )
{
int tag = track -> enc -> codec_tag ;
if ( track -> mode == MODE_MP4 || track -> mode == MODE_PSP )
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-22 23:09:34 +00:00
tag = ff_codec_get_tag ( codec_3gp_tags , track -> enc -> codec_id );
2009-04-07 22:19:58 +00:00
else
tag = mov_get_codec_tag ( s , track );
return tag ;
}
2008-03-16 13:36:36 +00: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 13:36:36 +00: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 13:36:36 +00:00
return 28 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_subtitle_tag ( AVIOContext * pb , MOVTrack * track )
2009-01-11 10:26:44 +00: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 10:26:44 +00: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 10:26:44 +00: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 21:57:20 +00: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 21:57:20 +00:00
return 16 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_video_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 20:23:13 +00:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb );
2005-01-26 09:31:49 +00:00
char compressor_name [ 32 ];
2004-04-07 12:47:33 +00: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 21:09:32 +00: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 14:18:43 +00:00
if ( track -> mode == MODE_MOV ) {
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , "FFMP" ); /* Vendor */
2006-08-07 14:19:33 +00: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 14:19:33 +00: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 14:19:33 +00:00
}
2006-08-07 14:18:43 +00: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 14:18:43 +00: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 18:14:38 +00:00
2005-01-26 09:31:49 +00:00
memset ( compressor_name , 0 , 32 );
2006-08-07 14:18:43 +00: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 11:19:31 -07: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 18:14:38 +00:00
2008-09-08 14:24:59 +00: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 11:31:56 +00:00
else
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 0x18 ); /* Reserved */
avio_wb16 ( pb , 0xffff ); /* Reserved */
2008-01-28 13:44:27 +00:00
if ( track -> tag == MKTAG ( 'm' , 'p' , '4' , 'v' ))
2003-09-28 21:09:32 +00: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 18:14:38 +00:00
mov_write_svq3_tag ( pb );
2008-03-20 18:42:44 +00:00
else if ( track -> enc -> codec_id == CODEC_ID_DNXHD )
mov_write_avid_tag ( pb , track );
2008-03-16 13:36:36 +00:00
else if ( track -> enc -> codec_id == CODEC_ID_H264 ) {
2006-03-11 18:18:17 +00:00
mov_write_avcc_tag ( pb , track );
2008-03-16 13:36:36 +00:00
if ( track -> mode == MODE_IPOD )
mov_write_uuid_tag_ipod ( pb );
2008-04-04 09:55:31 +00:00
} else if ( track -> vosLen > 0 )
2007-12-19 16:00:08 +00:00
mov_write_glbl_tag ( pb , track );
2003-09-28 21:09:32 +00:00
2011-02-15 12:44:08 +02:00
if ( track -> enc -> sample_aspect_ratio . den && track -> enc -> sample_aspect_ratio . num &&
2010-07-08 21:57:20 +00:00
track -> enc -> sample_aspect_ratio . den != track -> enc -> sample_aspect_ratio . num ) {
mov_write_pasp_tag ( pb , track );
}
2008-06-11 08:50:41 +00:00
return updateSize ( pb , pos );
2003-08-26 20:23:13 +00:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_rtp_tag ( AVIOContext * pb , MOVTrack * track )
2010-05-18 19:47:24 +00: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 19:47:24 +00: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 19:47:24 +00: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 19:47:24 +00: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 20:23:13 +00: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-30 23:30:55 +00:00
if ( track -> enc -> codec_type == AVMEDIA_TYPE_VIDEO )
2003-09-28 21:09:32 +00:00
mov_write_video_tag ( pb , track );
2010-03-30 23:30:55 +00:00
else if ( track -> enc -> codec_type == AVMEDIA_TYPE_AUDIO )
2003-09-28 21:09:32 +00:00
mov_write_audio_tag ( pb , track );
2010-03-30 23:30:55 +00:00
else if ( track -> enc -> codec_type == AVMEDIA_TYPE_SUBTITLE )
2009-01-11 10:26:44 +00:00
mov_write_subtitle_tag ( pb , track );
2010-05-18 19:47:24 +00:00
else if ( track -> enc -> codec_tag == MKTAG ( 'r' , 't' , 'p' , ' ' ))
mov_write_rtp_tag ( pb , track );
2003-09-09 23:03:04 +00:00
return updateSize ( pb , pos );
2003-08-26 20:23:13 +00:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_ctts_tag ( AVIOContext * pb , MOVTrack * track )
2006-02-22 23:46:20 +00:00
{
2008-12-11 20:06:56 +00:00
MOVStts * ctts_entries ;
2006-02-22 23:46:20 +00: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 18:09:20 +00:00
ctts_entries [ 0 ]. duration = track -> cluster [ 0 ]. cts ;
2006-02-22 23:46:20 +00:00
for ( i = 1 ; i < track -> entry ; i ++ ) {
2006-06-24 18:09:20 +00:00
if ( track -> cluster [ i ]. cts == ctts_entries [ entries ]. duration ) {
2006-02-22 23:46:20 +00:00
ctts_entries [ entries ]. count ++ ; /* compress */
} else {
entries ++ ;
2006-06-24 18:09:20 +00:00
ctts_entries [ entries ]. duration = track -> cluster [ i ]. cts ;
2006-02-22 23:46:20 +00: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-22 23:46:20 +00: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-22 23:46:20 +00:00
}
av_free ( ctts_entries );
return atom_size ;
}
2003-11-03 21:51:07 +00: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 20:23:13 +00:00
{
2008-12-11 20:06:56 +00:00
MOVStts * stts_entries ;
2006-11-01 17:27:39 +00:00
uint32_t entries = - 1 ;
uint32_t atom_size ;
int i ;
2010-03-30 23:30:55 +00:00
if ( track -> enc -> codec_type == AVMEDIA_TYPE_AUDIO && ! track -> audio_vbr ) {
2006-11-01 17:27:39 +00:00
stts_entries = av_malloc ( sizeof ( * stts_entries )); /* one entry */
stts_entries [ 0 ]. count = track -> sampleCount ;
stts_entries [ 0 ]. duration = 1 ;
entries = 1 ;
} else {
stts_entries = av_malloc ( track -> entry * sizeof ( * stts_entries )); /* worst case */
for ( i = 0 ; i < track -> entry ; i ++ ) {
int64_t duration = i + 1 == track -> entry ?
track -> trackDuration - track -> cluster [ i ]. dts + track -> cluster [ 0 ]. dts : /* readjusting */
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 17:27:39 +00: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 17:27:39 +00:00
}
av_free ( stts_entries );
return atom_size ;
2003-08-26 20:23:13 +00:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_dref_tag ( AVIOContext * pb )
2003-08-26 20:23:13 +00: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 20:23:13 +00:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0xc ); /* size */
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 20:23:13 +00:00
return 28 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_stbl_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 20:23:13 +00: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-09 23:03:04 +00:00
mov_write_stsd_tag ( pb , track );
mov_write_stts_tag ( pb , track );
2010-05-18 19:47:24 +00:00
if (( track -> enc -> codec_type == AVMEDIA_TYPE_VIDEO ||
track -> enc -> codec_tag == MKTAG ( 'r' , 't' , 'p' , ' ' )) &&
2008-03-30 21:18:22 +00:00
track -> hasKeyframes && track -> hasKeyframes < track -> entry )
2009-05-15 06:11:53 +00: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-30 23:30:55 +00:00
if ( track -> enc -> codec_type == AVMEDIA_TYPE_VIDEO &&
2009-05-15 06:11:53 +00:00
track -> flags & MOV_TRACK_CTTS )
2006-02-22 23:46:20 +00:00
mov_write_ctts_tag ( pb , track );
2003-09-09 23:03:04 +00: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 20:23:13 +00:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_dinf_tag ( AVIOContext * pb )
2003-08-26 20:23:13 +00: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-09 23:03:04 +00:00
mov_write_dref_tag ( pb );
return updateSize ( pb , pos );
2003-08-26 20:23:13 +00:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_nmhd_tag ( AVIOContext * pb )
2009-01-11 10:26:44 +00: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 10:26:44 +00:00
return 12 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_gmhd_tag ( AVIOContext * pb )
2009-01-11 10:26:44 +00: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 10:26:44 +00:00
return 0x20 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_smhd_tag ( AVIOContext * pb )
2003-08-26 20:23:13 +00: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 20:23:13 +00:00
return 16 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_vmhd_tag ( AVIOContext * pb )
2003-08-26 20:23:13 +00: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 20:23:13 +00:00
return 0x14 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_hdlr_tag ( AVIOContext * pb , MOVTrack * track )
2004-03-28 02:17:06 +00:00
{
2009-01-11 10:41:43 +00: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 18:14:38 +00:00
2004-03-28 02:17:06 +00:00
if ( ! track ) { /* no media --> data handler */
2005-12-22 01:10:11 +00:00
hdlr = "dhlr" ;
hdlr_type = "url " ;
descr = "DataHandler" ;
2004-03-28 02:17:06 +00:00
} else {
2005-12-22 01:10:11 +00:00
hdlr = ( track -> mode == MODE_MOV ) ? "mhlr" : " \0\0\0\0 " ;
2010-03-30 23:30:55 +00:00
if ( track -> enc -> codec_type == AVMEDIA_TYPE_VIDEO ) {
2005-12-22 01:10:11 +00:00
hdlr_type = "vide" ;
descr = "VideoHandler" ;
2010-03-30 23:30:55 +00:00
} else if ( track -> enc -> codec_type == AVMEDIA_TYPE_AUDIO ) {
2005-12-22 01:10:11 +00:00
hdlr_type = "soun" ;
descr = "SoundHandler" ;
2010-03-30 23:30:55 +00:00
} else if ( track -> enc -> codec_type == AVMEDIA_TYPE_SUBTITLE ) {
2009-03-11 08:01:39 +00:00
if ( track -> tag == MKTAG ( 't' , 'x' , '3' , 'g' )) hdlr_type = "sbtl" ;
2009-03-11 08:02:59 +00:00
else hdlr_type = "text" ;
2009-01-11 10:26:44 +00:00
descr = "SubtitleHandler" ;
2010-05-18 19:47:24 +00:00
} else if ( track -> enc -> codec_tag == MKTAG ( 'r' , 't' , 'p' , ' ' )) {
hdlr_type = "hint" ;
descr = "HintHandler" ;
2005-12-22 01:10:11 +00:00
}
2004-03-28 02:17:06 +00:00
}
2005-12-17 18:14:38 +00: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 07:17:17 +00: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 07:17:17 +00:00
if ( track && track -> mode != MODE_MOV )
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0 ); /* c string */
2004-03-28 02:17:06 +00:00
return updateSize ( pb , pos );
}
2011-02-20 11:04:12 +01:00
static int mov_write_hmhd_tag ( AVIOContext * pb )
2010-05-18 19:47:24 +00: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 19:47:24 +00:00
return 28 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_minf_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 20:23:13 +00: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-30 23:30:55 +00:00
if ( track -> enc -> codec_type == AVMEDIA_TYPE_VIDEO )
2003-09-09 23:03:04 +00:00
mov_write_vmhd_tag ( pb );
2010-03-30 23:30:55 +00:00
else if ( track -> enc -> codec_type == AVMEDIA_TYPE_AUDIO )
2003-09-09 23:03:04 +00:00
mov_write_smhd_tag ( pb );
2010-03-30 23:30:55 +00:00
else if ( track -> enc -> codec_type == AVMEDIA_TYPE_SUBTITLE ) {
2009-03-11 08:01:39 +00:00
if ( track -> tag == MKTAG ( 't' , 'e' , 'x' , 't' )) mov_write_gmhd_tag ( pb );
2009-03-11 08:02:59 +00:00
else mov_write_nmhd_tag ( pb );
2010-05-18 19:47:24 +00:00
} else if ( track -> tag == MKTAG ( 'r' , 't' , 'p' , ' ' )) {
mov_write_hmhd_tag ( pb );
2009-01-11 10:26:44 +00:00
}
2004-03-28 02:17:06 +00:00
if ( track -> mode == MODE_MOV ) /* FIXME: Why do it for MODE_MOV only ? */
mov_write_hdlr_tag ( pb , NULL );
2003-09-09 23:03:04 +00:00
mov_write_dinf_tag ( pb );
mov_write_stbl_tag ( pb , track );
return updateSize ( pb , pos );
2003-08-26 20:23:13 +00:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_mdhd_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 20:23:13 +00:00
{
2006-03-26 13:34:51 +00: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 13:34:51 +00: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 13:34:51 +00: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 13:34:51 +00: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 00:08:32 +00: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 10:04:37 +00:00
"playable with quicktime. Choose a different timebase or a different \n "
2007-05-30 00:08:32 +00:00
"container format \n " );
}
2003-08-26 20:23:13 +00:00
return 32 ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_mdia_tag ( AVIOContext * pb , MOVTrack * track )
2003-08-26 20:23:13 +00: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-09 23:03:04 +00: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 20:23:13 +00:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_tkhd_tag ( AVIOContext * pb , MOVTrack * track , AVStream * st )
2003-08-26 20:23:13 +00:00
{
2009-11-29 02:46:49 +00:00
int64_t duration = av_rescale_rnd ( track -> trackDuration , MOV_TIMESCALE ,
track -> timescale , AV_ROUND_UP );
2006-03-26 13:34:51 +00: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 13:34:51 +00: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 13:34:51 +00: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 13:34:51 +00: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 20:23:13 +00: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 20:23:13 +00:00
/* Volume, only for audio */
2010-03-30 23:30:55 +00:00
if ( track -> enc -> codec_type == AVMEDIA_TYPE_AUDIO )
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 0x0100 );
2003-08-26 20:23:13 +00:00
else
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 0 );
avio_wb16 ( pb , 0 ); /* reserved */
2003-08-26 20:23:13 +00: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 20:23:13 +00:00
/* Track width and height, for visual only */
2010-05-05 08:41:10 +00:00
if ( st && ( track -> enc -> codec_type == AVMEDIA_TYPE_VIDEO ||
track -> enc -> codec_type == AVMEDIA_TYPE_SUBTITLE )) {
2011-02-25 17:41:55 +02:00
if ( track -> mode == MODE_MOV ) {
avio_wb32 ( pb , track -> enc -> width << 16 );
2011-03-01 23:36:48 +00:00
avio_wb32 ( pb , track -> height << 16 );
2011-02-25 17:41:55 +02:00
} else {
2011-02-25 17:42:56 +02: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 17:41:55 +02:00
}
2003-08-26 20:23:13 +00:00
}
else {
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0 );
avio_wb32 ( pb , 0 );
2003-08-26 20:23:13 +00:00
}
return 0x5c ;
}
2011-02-25 17:41:00 -05:00
static int mov_write_tapt_tag ( AVIOContext * pb , MOVTrack * track )
2011-02-25 17:41:55 +02: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 17:41:55 +02:00
avio_wb32 ( pb , 0 ); /* size */
2011-02-25 17:41:00 -05:00
ffio_wfourcc ( pb , "tapt" );
2011-02-25 17:41:55 +02:00
avio_wb32 ( pb , 20 );
2011-02-25 17:41:00 -05:00
ffio_wfourcc ( pb , "clef" );
2011-02-25 17:41:55 +02:00
avio_wb32 ( pb , 0 );
avio_wb32 ( pb , width << 16 );
avio_wb32 ( pb , track -> enc -> height << 16 );
avio_wb32 ( pb , 20 );
2011-02-25 17:41:00 -05:00
ffio_wfourcc ( pb , "enof" );
2011-02-25 17:41:55 +02: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 17:07:04 +00:00
}
2011-02-25 17:41:55 +02:00
2005-01-27 14:48:15 +00: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 14:48:15 +00: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 14:48:15 +00: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 14:48:15 +00: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 14:48:15 +00:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_tref_tag ( AVIOContext * pb , MOVTrack * track )
2010-05-05 08:41:10 +00: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 08:41:10 +00:00
return 20 ;
}
2005-01-27 14:48:15 +00: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 14:48:15 +00: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 14:48:15 +00:00
return 0x34 ;
}
2011-05-18 16:21:47 +03:00
static int mov_write_udta_sdp ( AVIOContext * pb , AVFormatContext * ctx , int index )
2010-05-18 19:47:24 +00:00
{
char buf [ 1000 ] = "" ;
int len ;
2011-05-18 15:41:38 +03:00
ff_sdp_write_media ( buf , sizeof ( buf ), ctx -> streams [ 0 ] -> codec , NULL , NULL , 0 , 0 , ctx );
2010-05-18 19:47:24 +00: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 19:47:24 +00:00
return len + 24 ;
}
2011-12-06 16:15:35 +01:00
static int mov_write_trak_tag ( AVIOContext * pb , MOVMuxContext * mov , MOVTrack * track , AVStream * st )
2003-08-26 20:23:13 +00: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-23 23:43:20 +00:00
mov_write_tkhd_tag ( pb , track , st );
2011-12-07 05:06:13 +01:00
if ( ! mov -> fragments ) // EDTS with fragments is tricky as we dont know the duration when its written
mov_write_edts_tag ( pb , track ); // PSP Movies and several other cases require edts box
2010-05-05 08:41:10 +00:00
if ( track -> tref_tag )
mov_write_tref_tag ( pb , track );
2003-09-09 23:03:04 +00:00
mov_write_mdia_tag ( pb , track );
2005-12-17 18:14:38 +00:00
if ( track -> mode == MODE_PSP )
2005-01-27 14:48:15 +00:00
mov_write_uuid_tag_psp ( pb , track ); // PSP Movies require this uuid box
2010-05-18 19:47:24 +00:00
if ( track -> tag == MKTAG ( 'r' , 't' , 'p' , ' ' ))
2011-05-18 16:21:47 +03:00
mov_write_udta_sdp ( pb , track -> rtp_ctx , track -> trackID );
2011-02-25 17:41:55 +02: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-09 23:03:04 +00:00
return updateSize ( pb , pos );
2003-08-26 20:23:13 +00:00
}
2011-12-06 16:15:35 +01:00
static int mov_write_tfhd_tag ( AVIOContext * pb , MOVTrack * track , AVStream * st )
{
int64_t pos = avio_tell ( pb );
int flags = 1 ;
avio_wb32 ( pb , 0 );
ffio_wfourcc ( pb , "tfhd" );
avio_w8 ( pb , 0 );
avio_wb24 ( pb , flags );
avio_wb32 ( pb , track -> trackID );
track -> base_data_offset_pos = avio_tell ( pb );
if ( flags & 0x01 ) avio_wb64 ( pb , 0 );
return updateSize ( pb , pos );
}
static int mov_write_trun_tag ( AVIOContext * pb , MOVTrack * track )
{
int64_t pos = avio_tell ( pb );
int sample_count = track -> entry - track -> cluster_write_index ;
int tr_flags = 0 ;
int i ;
2011-12-13 02:55:37 +01:00
for ( i = track -> cluster_write_index ; i < track -> entry ; i ++ ){
2011-12-13 03:02:20 +01:00
int64_t duration = i + 1 == track -> entry ?
track -> trackDuration - track -> cluster [ i ]. dts + track -> cluster [ 0 ]. dts : /* readjusting */
track -> cluster [ i + 1 ]. dts - track -> cluster [ i ]. dts ;
if ( duration != 1 ) tr_flags |= 0x100 ;
2011-12-13 03:27:19 +01:00
if ( track -> trex_size != track -> cluster [ i ]. size ) tr_flags |= 0x200 ;
2011-12-13 03:11:53 +01:00
if ( track -> trex_flags != (( track -> cluster [ i ]. flags & MOV_SYNC_SAMPLE ) ? 0x02000000 : 0x01010000 ))
tr_flags |= 0x400 ;
2011-12-13 02:55:37 +01:00
if ( track -> cluster [ i ]. cts ) tr_flags |= 0x800 ;
}
2011-12-06 16:15:35 +01:00
avio_wb32 ( pb , 0 );
ffio_wfourcc ( pb , "trun" );
avio_w8 ( pb , 0 );
avio_wb24 ( pb , tr_flags );
avio_wb32 ( pb , sample_count );
if ( tr_flags & 1 ) avio_wb32 ( pb , 0 );
for ( i = track -> cluster_write_index ; i < track -> entry ; i ++ ){
int64_t duration = i + 1 == track -> entry ?
track -> trackDuration - track -> cluster [ i ]. dts + track -> cluster [ 0 ]. dts : /* readjusting */
track -> cluster [ i + 1 ]. dts - track -> cluster [ i ]. dts ;
if ( tr_flags & 0x100 ) avio_wb32 ( pb , duration );
if ( tr_flags & 0x200 ) avio_wb32 ( pb , track -> cluster [ i ]. size );
if ( tr_flags & 0x400 ) avio_wb32 ( pb , ( track -> cluster [ i ]. flags & MOV_SYNC_SAMPLE ) ? 0x02000000 : 0x01010000 );
if ( tr_flags & 0x800 ) avio_wb32 ( pb , track -> cluster [ i ]. cts );
}
return updateSize ( pb , pos );
}
static int mov_write_traf_tag ( AVIOContext * pb , MOVTrack * track , AVStream * st )
{
int64_t pos = avio_tell ( pb );
avio_wb32 ( pb , 0 ); /* size */
ffio_wfourcc ( pb , "traf" );
mov_write_tfhd_tag ( pb , track , st );
mov_write_trun_tag ( pb , track );
return updateSize ( pb , pos );
}
2011-02-20 11:04:12 +01:00
static int mov_write_iods_tag ( AVIOContext * pb , MOVMuxContext * mov )
2003-08-26 20:23:13 +00:00
{
2011-10-04 11:44:25 -07: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 11:44:25 -07: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 20:23:13 +00:00
}
2011-02-20 11:04:12 +01:00
static int mov_write_mvhd_tag ( AVIOContext * pb , MOVMuxContext * mov )
2003-08-26 20:23:13 +00:00
{
2006-01-23 14:12:03 +00:00
int maxTrackID = 1 , i ;
int64_t maxTrackLenTemp , maxTrackLen = 0 ;
2006-03-26 13:34:51 +00:00
int version ;
2003-08-26 20:23:13 +00:00
2006-06-25 00:10:52 +00:00
for ( i = 0 ; i < mov -> nb_streams ; i ++ ) {
2003-08-26 20:23:13 +00:00
if ( mov -> tracks [ i ]. entry > 0 ) {
2009-11-29 02:46:49 +00:00
maxTrackLenTemp = av_rescale_rnd ( mov -> tracks [ i ]. trackDuration ,
MOV_TIMESCALE ,
mov -> tracks [ i ]. timescale ,
AV_ROUND_UP );
2003-09-28 21:09:32 +00:00
if ( maxTrackLen < maxTrackLenTemp )
maxTrackLen = maxTrackLenTemp ;
2003-08-26 20:23:13 +00:00
if ( maxTrackID < mov -> tracks [ i ]. trackID )
maxTrackID = mov -> tracks [ i ]. trackID ;
}
}
2006-03-26 13:34:51 +00: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 13:34:51 +00: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 13:34:51 +00: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 13:34:51 +00: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 20:23:13 +00: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 20:23:13 +00: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 20:23:13 +00:00
2011-02-21 19:28:17 +01:00
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 20:23:13 +00: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 14:05:48 +00: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 07:24:30 +00:00
return 33 ;
2004-08-14 14:05:48 +00: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 14:05:48 +00:00
{
2006-05-18 22:49:27 +00:00
if ( long_style ){
2010-04-12 07:24:30 +00: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 07:24:30 +00:00
return size ;
2006-05-18 22:49:27 +00:00
} else {
2010-03-09 01:53:16 +00: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-18 22:49:27 +00:00
return strlen ( data ) + 4 ;
2004-08-14 14:05:48 +00: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 14:05:48 +00:00
int size = 0 ;
2008-02-06 18:57:00 +00: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 13:13:48 +00:00
mov_write_string_data_tag ( pb , value , lang , long_style );
2006-05-18 22:49:27 +00:00
size = updateSize ( pb , pos );
2004-08-14 14:05:48 +00:00
}
return size ;
}
2011-02-20 11:04:12 +01:00
static int mov_write_string_metadata ( AVFormatContext * s , AVIOContext * pb ,
2009-02-26 13:06:49 +00:00
const char * name , const char * tag ,
int long_style )
2004-08-14 14:05:48 +00:00
{
2009-02-26 13:13:48 +00:00
int l , lang = 0 , len , len2 ;
2011-05-22 12:46:29 +02:00
AVDictionaryEntry * t , * t2 = NULL ;
2009-02-26 13:13:48 +00:00
char tag2 [ 16 ];
2009-02-26 13:06:49 +00:00
2011-05-22 12:46:29 +02:00
if ( ! ( t = av_dict_get ( s -> metadata , tag , NULL , 0 )))
2006-05-18 22:49:27 +00:00
return 0 ;
2009-02-26 13:06:49 +00:00
2009-02-26 13:13:48 +00: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 13:13:48 +00:00
len2 = strlen ( t2 -> key );
if ( len2 == len + 4 && ! strcmp ( t -> value , t2 -> value )
2010-03-09 01:21:09 +00:00
&& ( l = ff_mov_iso639_to_lang ( & t2 -> key [ len2 - 3 ], 1 )) >= 0 ) {
2009-02-26 13:13:48 +00:00
lang = l ;
break ;
}
}
return mov_write_string_tag ( pb , name , t -> value , lang , long_style );
2004-08-14 14:05:48 +00: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 14:05:48 +00:00
AVFormatContext * s )
{
2011-05-22 12:46:29 +02:00
AVDictionaryEntry * t = av_dict_get ( s -> metadata , "track" , NULL , 0 );
2009-02-26 13:06:49 +00: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 07:24:30 +00:00
size = 32 ;
2004-08-14 14:05:48 +00: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 14:05:48 +00: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 13:06:49 +00:00
mov_write_string_metadata ( s , pb , " \251 nam" , "title" , 1 );
2010-05-23 21:03:14 +00:00
mov_write_string_metadata ( s , pb , " \251 ART" , "artist" , 1 );
2010-01-04 02:52:40 +00:00
mov_write_string_metadata ( s , pb , "aART" , "album_artist" , 1 );
2009-11-23 08:47:40 +00:00
mov_write_string_metadata ( s , pb , " \251 wrt" , "composer" , 1 );
2009-02-26 13:06:49 +00:00
mov_write_string_metadata ( s , pb , " \251 alb" , "album" , 1 );
2010-02-01 11:39:10 +00:00
mov_write_string_metadata ( s , pb , " \251 day" , "date" , 1 );
2009-02-26 13:13:48 +00:00
mov_write_string_tag ( pb , " \251 too" , LIBAVFORMAT_IDENT , 0 , 1 );
2009-02-26 13:06:49 +00: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 02:52:40 +00:00
mov_write_string_metadata ( s , pb , " \251 grp" , "grouping" , 1 );
mov_write_string_metadata ( s , pb , " \251 lyr" , "lyrics" , 1 );
2009-11-23 08:47:44 +00: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 14:05:48 +00: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 14:05:48 +00: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 13:08:22 +00:00
mov_write_itunes_hdlr_tag ( pb , mov , s );
mov_write_ilst_tag ( pb , mov , s );
size = updateSize ( pb , pos );
2004-08-14 14:05:48 +00:00
return size ;
}
2005-12-17 18:14:38 +00:00
2008-06-11 09:35:02 +00: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 09:35:02 +00: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 09:35:02 +00:00
}
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , 0x00 );
2008-06-11 09:35:02 +00: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 09:40:22 +00: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 13:06:49 +00:00
if ( ! t || ! utf8len ( t -> value ))
2008-06-11 09:40:22 +00: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 09:40:22 +00:00
if ( ! strcmp ( tag , "yrrc" ))
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , atoi ( t -> value ));
2008-06-11 09:40:22 +00: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 13:06:49 +00: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 09:40:22 +00: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 06:36:05 +00:00
{
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb );
2010-04-21 06:36:05 +00: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 06:36:05 +00: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 06:36:05 +00:00
2011-05-22 12:46:29 +02:00
if (( t = av_dict_get ( c -> metadata , "title" , NULL , 0 ))) {
2010-04-21 06:36:05 +00: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 06:36:05 +00:00
} else
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0 );
2010-04-21 06:36:05 +00: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 19:08:09 +00:00
AVFormatContext * s )
{
2011-02-20 11:04:12 +01:00
AVIOContext * pb_buf ;
2009-02-26 13:06:49 +00:00
int i , ret , size ;
uint8_t * buf ;
2007-10-24 14:20:15 +00:00
2008-06-09 20:13:44 +00:00
for ( i = 0 ; i < s -> nb_streams ; i ++ )
if ( mov -> tracks [ i ]. enc -> flags & CODEC_FLAG_BITEXACT ) {
2009-02-26 13:06:49 +00:00
return 0 ;
2008-06-09 20:13:44 +00:00
}
2011-03-17 08:13:34 +01:00
ret = avio_open_dyn_buf ( & pb_buf );
2009-02-26 13:06:49 +00:00
if ( ret < 0 )
return ret ;
2004-02-14 19:08:09 +00:00
2008-06-12 03:08:41 +00:00
if ( mov -> mode & MODE_3GP ) {
2010-05-23 21:06:11 +00:00
mov_write_3gp_udta_tag ( pb_buf , s , "perf" , "artist" );
2009-02-26 13:06:49 +00:00
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" );
2010-02-01 11:39:10 +00:00
mov_write_3gp_udta_tag ( pb_buf , s , "yrrc" , "date" );
2008-06-12 00:56:54 +00:00
} else if ( mov -> mode == MODE_MOV ) { // the title field breaks gtkpod with mp4 and my suspicion is that stuff is not valid in mp4
2010-10-15 23:23:47 +00:00
mov_write_string_metadata ( s , pb_buf , " \251 ART" , "artist" , 0 );
2009-02-26 13:06:49 +00:00
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 );
2010-02-01 11:39:10 +00:00
mov_write_string_metadata ( s , pb_buf , " \251 day" , "date" , 0 );
2010-10-15 23:16:11 +00:00
mov_write_string_metadata ( s , pb_buf , " \251 swr" , "encoder" , 0 );
2009-02-26 13:06:49 +00:00
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 );
2008-06-11 09:40:22 +00:00
} else {
2008-06-12 00:55:49 +00:00
/* iTunes meta data */
2009-02-26 13:06:49 +00:00
mov_write_meta_tag ( pb_buf , mov , s );
2008-06-11 09:40:22 +00:00
}
2009-02-26 13:06:49 +00:00
2010-04-21 06:36:05 +00:00
if ( s -> nb_chapters )
mov_write_chpl_tag ( pb_buf , s );
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 14:20:15 +00:00
}
2010-07-20 05:23:28 +00:00
av_free ( buf );
2007-10-24 14:20:15 +00:00
return 0 ;
2004-02-14 19:08:09 +00:00
}
2011-02-20 11:04:12 +01:00
static void mov_write_psp_udta_tag ( AVIOContext * pb ,
2008-06-11 09:02:01 +00:00
const char * str , const char * lang , int type )
{
int len = utf8len ( str ) + 1 ;
if ( len <= 0 )
2008-06-11 09:06:27 +00: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 09:02:01 +00: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 08:03:42 +00:00
{
2011-05-22 12:46:29 +02:00
AVDictionaryEntry * title = av_dict_get ( s -> metadata , "title" , NULL , 0 );
2008-10-03 10:16:29 +00:00
int64_t pos , pos2 ;
2006-01-24 08:03:42 +00:00
2009-02-26 13:06:49 +00: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 08:03:42 +00: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 08:03:42 +00:00
2006-05-19 01:53:59 +00: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 01:53:59 +00:00
2008-06-11 09:06:27 +00:00
mov_write_psp_udta_tag ( pb , LIBAVCODEC_IDENT , "eng" , 0x04 );
2009-02-26 13:06:49 +00:00
mov_write_psp_udta_tag ( pb , title -> value , "eng" , 0x01 );
2006-05-19 01:53:59 +00: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 09:06:27 +00:00
mov_write_psp_udta_tag ( pb , "2006/04/01 11:11:11" , "und" , 0x03 );
2006-05-19 01:53:59 +00:00
2008-06-11 09:06:27 +00:00
updateSize ( pb , pos2 );
return updateSize ( pb , pos );
2006-01-24 08:03:42 +00:00
}
2008-06-11 09:06:27 +00:00
return 0 ;
2006-01-24 08:03:42 +00: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 ++ ){
2011-11-29 16:50:25 +01:00
if ( chunk -> pos + chunkSize == trk -> cluster [ i ]. pos ){
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-12-06 16:15:35 +01:00
static int mov_write_trex_tag ( AVIOContext * pb , MOVTrack * track , AVStream * st )
{
int64_t pos = avio_tell ( pb );
avio_wb32 ( pb , 0 ); /* size */
ffio_wfourcc ( pb , "trex" );
avio_w8 ( pb , 0 );
avio_wb24 ( pb , 0 );
avio_wb32 ( pb , track -> trackID );
avio_wb32 ( pb , 1 ); // stsd_id
avio_wb32 ( pb , 1 ); // duration
2011-12-13 03:27:19 +01:00
track -> trex_size = track -> entry ? track -> cluster [ FFMIN ( 1 , track -> entry - 1 )]. size : 1 ;
avio_wb32 ( pb , track -> trex_size );
2011-12-13 03:11:53 +01:00
track -> trex_flags = st -> codec -> codec_type != AVMEDIA_TYPE_VIDEO ? 0x02000000 : 0x01010000 ;
avio_wb32 ( pb , track -> trex_flags );
2011-12-06 16:15:35 +01:00
return updateSize ( pb , pos );
}
static int mov_write_mvex_tag ( AVIOContext * pb , MOVMuxContext * mov ,
AVFormatContext * s )
{
int i ;
int64_t pos = avio_tell ( pb );
avio_wb32 ( pb , 0 ); /* size placeholder*/
ffio_wfourcc ( pb , "mvex" );
for ( i = 0 ; i < mov -> nb_streams ; i ++ ) {
if ( mov -> tracks [ i ]. entry > 0 ) {
mov_write_trex_tag ( pb , & ( mov -> tracks [ i ]), i < s -> nb_streams ? s -> streams [ i ] : NULL );
}
}
return updateSize ( pb , pos );
}
2011-02-20 11:04:12 +01:00
static int mov_write_moov_tag ( AVIOContext * pb , MOVMuxContext * mov ,
2004-02-14 19:08:09 +00:00
AVFormatContext * s )
2003-08-26 20:23:13 +00:00
{
2006-01-23 14:12:03 +00: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 20:23:13 +00:00
2006-06-25 00:10:52 +00:00
for ( i = 0 ; i < mov -> nb_streams ; i ++ ) {
2003-11-03 21:51:07 +00:00
if ( mov -> tracks [ i ]. entry <= 0 ) continue ;
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 20:23:13 +00:00
}
2010-05-05 08:41:10 +00: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 19:47:24 +00: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 08:41:10 +00:00
2003-09-09 23:03:04 +00:00
mov_write_mvhd_tag ( pb , mov );
2011-10-04 11:44:25 -07:00
if ( mov -> mode != MODE_MOV && ! mov -> iods_skip )
mov_write_iods_tag ( pb , mov );
2006-06-25 00:10:52 +00:00
for ( i = 0 ; i < mov -> nb_streams ; i ++ ) {
2003-08-26 20:23:13 +00:00
if ( mov -> tracks [ i ]. entry > 0 ) {
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 20:23:13 +00:00
}
}
2011-12-06 16:15:35 +01:00
if ( mov -> fragments )
mov_write_mvex_tag ( pb , mov , s );
2006-01-24 08:03:42 +00:00
if ( mov -> mode == MODE_PSP )
mov_write_uuidusmt_tag ( pb , s );
2008-06-11 09:40:22 +00:00
else
2006-11-01 21:09:14 +00:00
mov_write_udta_tag ( pb , mov , s );
2004-02-14 19:08:09 +00:00
2003-09-09 23:03:04 +00:00
return updateSize ( pb , pos );
2003-08-26 20:23:13 +00:00
}
2011-12-06 16:15:35 +01:00
static int mov_write_mfhd_tag ( AVIOContext * pb , MOVMuxContext * mov )
{
int i ;
int64_t pos = avio_tell ( pb );
avio_wb32 ( pb , 0 ); /* size placeholder*/
ffio_wfourcc ( pb , "mfhd" );
avio_wb32 ( pb , 0 );
avio_wb32 ( pb , mov -> frag_seq_num ++ );
return updateSize ( pb , pos ); //FIXME replace by hardcoded num also above
}
static int mov_write_moof_tag ( AVIOContext * pb , MOVMuxContext * mov ,
AVFormatContext * s )
{
int i ;
int64_t pos = avio_tell ( pb );
avio_wb32 ( pb , 0 ); /* size placeholder*/
ffio_wfourcc ( pb , "moof" );
mov_write_mfhd_tag ( pb , mov );
for ( i = 0 ; i < mov -> nb_streams ; i ++ ) {
if ( mov -> tracks [ i ]. entry > 0 ) {
mov_write_traf_tag ( pb , & ( mov -> tracks [ i ]), i < s -> nb_streams ? s -> streams [ i ] : NULL );
}
}
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 20:23:13 +00: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 14:12:03 +00: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 20:23:13 +00: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 20:23:13 +00:00
{
2009-02-28 16:02:29 +00:00
MOVMuxContext * mov = s -> priv_data ;
2011-03-03 20:11:45 +01:00
int64_t pos = avio_tell ( pb );
2008-06-14 21:07:59 +00:00
int has_h264 = 0 , has_video = 0 ;
2008-06-15 01:38:38 +00:00
int minor = 0x200 ;
2008-06-11 09:33:35 +00:00
int i ;
2004-02-14 19:08:09 +00:00
2008-06-14 21:05:28 +00:00
for ( i = 0 ; i < s -> nb_streams ; i ++ ) {
AVStream * st = s -> streams [ i ];
2010-03-30 23:30:55 +00:00
if ( st -> codec -> codec_type == AVMEDIA_TYPE_VIDEO )
2008-06-14 21:07:59 +00:00
has_video = 1 ;
2008-06-14 21:05:28 +00: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 21:51:07 +00:00
2008-06-15 01:38:38 +00:00
if ( mov -> mode == MODE_3GP ) {
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , has_h264 ? "3gp6" : "3gp4" );
2008-06-15 01:38:38 +00: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 01:38:38 +00: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 09:33:35 +00:00
else if ( mov -> mode == MODE_MP4 )
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , "isom" );
2008-06-14 21:07:59 +00:00
else if ( mov -> mode == MODE_IPOD )
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , has_video ? "M4V " : "M4A " );
2008-06-14 21:07:59 +00:00
else
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , "qt " );
2003-11-03 21:51:07 +00:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , minor );
2003-11-03 21:51:07 +00:00
2008-06-14 21:12:51 +00:00
if ( mov -> mode == MODE_MOV )
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , "qt " );
2008-06-14 21:12:51 +00:00
else {
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , "isom" );
ffio_wfourcc ( pb , "iso2" );
2008-06-14 21:05:28 +00:00
if ( has_h264 )
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , "avc1" );
2008-06-14 21:12:51 +00:00
}
2008-06-13 12:19:01 +00:00
2008-02-06 18:57:00 +00:00
if ( mov -> mode == MODE_3GP )
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , has_h264 ? "3gp6" : "3gp4" );
2008-06-12 03:08:41 +00: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 18:57:00 +00:00
else if ( mov -> mode == MODE_PSP )
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , "MSNV" );
2008-06-11 09:33:35 +00:00
else if ( mov -> mode == MODE_MP4 )
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , "mp41" );
2008-06-13 11:30:36 +00:00
return updateSize ( pb , pos );
2003-08-26 20:23:13 +00:00
}
2011-02-20 11:04:12 +01:00
static void mov_write_uuidprof_tag ( AVIOContext * pb , AVFormatContext * s )
2005-01-27 14:48:15 +00:00
{
2006-01-24 08:03:42 +00: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 12:58:56 +00:00
int audio_kbitrate = AudioCodec -> bit_rate / 1000 ;
int video_kbitrate = FFMIN ( VideoCodec -> bit_rate / 1000 , 800 - audio_kbitrate );
2005-01-27 14:48:15 +00: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 14:48:15 +00: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 14:48:15 +00:00
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 0x0 ); /* ? */
avio_wb32 ( pb , 0x3 ); /* 3 sections ? */
2005-01-27 14:48:15 +00: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 14:48:15 +00: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 );
2005-01-27 14:48:15 +00:00
2011-02-21 19:28:17 +01:00
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 17:44:59 +00: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 17:44:59 +00: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 17:44:59 +00: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 14:48:15 +00:00
}
2009-05-15 06:11:53 +00: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 ;
}
2011-12-06 16:15:35 +01:00
static int flush_cluster_buffer ( AVFormatContext * s ){
MOVMuxContext * mov = s -> priv_data ;
int i , j ;
int has_data = 0 ;
for ( i = 0 ; i < mov -> nb_streams ; i ++ ){
MOVTrack * track = & mov -> tracks [ i ];
if ( track -> entry != track -> cluster_write_index )
has_data = 1 ;
}
if ( mov -> frag_seq_num == 0 ){
update_first_fragment ( s );
mov -> frag_seq_num ++ ;
} else if ( has_data ) {
mov_write_moof_tag ( s -> pb , mov , s );
mov_write_mdat_tag ( s -> pb , mov );
for ( i = 0 ; i < mov -> nb_streams ; i ++ ){
MOVTrack * track = & mov -> tracks [ i ];
if ( track -> entry > 0 ) {
int64_t pos = avio_tell ( s -> pb );
avio_seek ( s -> pb , track -> base_data_offset_pos , SEEK_SET );
avio_wb64 ( s -> pb , pos );
avio_seek ( s -> pb , pos , SEEK_SET );
for ( j = track -> cluster_write_index ; j < track -> entry ; j ++ ){
avio_write ( s -> pb , track -> cluster [ j ]. data , track -> cluster [ j ]. size );
av_freep ( & track -> cluster [ j ]. data );
}
}
}
updateSize ( s -> pb , mov -> mdat_pos );
}
2011-12-06 16:21:10 +01:00
mov -> mdat_size = 0 ;
2011-12-06 16:15:35 +01:00
for ( i = 0 ; i < mov -> nb_streams ; i ++ ) {
MOVTrack * track = & mov -> tracks [ i ];
track -> cluster_write_index = track -> entry ;
}
avio_flush ( s -> pb );
return 0 ;
}
2010-05-18 19:40:17 +00:00
int ff_mov_write_packet ( AVFormatContext * s , AVPacket * pkt )
2003-08-26 20:23:13 +00:00
{
2009-02-28 16:02:29 +00:00
MOVMuxContext * mov = s -> priv_data ;
2011-02-20 11:04:12 +01:00
AVIOContext * pb = s -> pb ;
2006-05-13 18:01:16 +00:00
MOVTrack * trk = & mov -> tracks [ pkt -> stream_index ];
2011-12-06 16:15:35 +01:00
AVStream * st = s -> streams [ pkt -> stream_index ];
2006-05-13 18:01:16 +00:00
AVCodecContext * enc = trk -> enc ;
2003-11-03 21:51:07 +00:00
unsigned int samplesInChunk = 0 ;
2004-05-29 02:06:32 +00:00
int size = pkt -> size ;
2011-12-09 21:19:57 +02:00
uint8_t * reformatted_data = NULL ;
2003-08-26 20:23:13 +00:00
2011-03-05 21:06:46 +01:00
if ( ! s -> pb -> seekable ) return 0 ; /* Can't handle that */
2003-11-03 21:51:07 +00:00
if ( ! size ) return 0 ; /* Discard 0 sized packets */
2003-08-26 20:23:13 +00:00
2011-12-06 16:15:35 +01:00
if ( mov -> fragments && trk -> entry > trk -> cluster_write_index &&
2011-12-06 16:21:10 +01:00
( mov -> max_fragment_duration && av_rescale_q ( pkt -> dts - trk -> cluster [ trk -> cluster_write_index ]. dts , st -> time_base , AV_TIME_BASE_Q ) >= mov -> max_fragment_duration
|| mov -> max_fragment_size && mov -> mdat_size + size >= mov -> max_fragment_size )
){
2011-12-06 16:15:35 +01:00
flush_cluster_buffer ( s );
}
2006-07-06 14:57:55 +00:00
if ( enc -> codec_id == CODEC_ID_AMR_NB ) {
2006-07-04 14:03:59 +00: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 14:03:59 +00:00
int len = 0 ;
2003-11-03 21:51:07 +00:00
2006-07-04 14:03:59 +00:00
while ( len < size && samplesInChunk < 100 ) {
len += packed_size [( pkt -> data [ len ] >> 3 ) & 0x0F ];
samplesInChunk ++ ;
2003-11-03 21:51:07 +00:00
}
2006-07-06 12:27:43 +00:00
if ( samplesInChunk > 1 ){
2007-09-11 23:46:46 +00:00
av_log ( s , AV_LOG_ERROR , "fatal error, input is not a single packet, implement a AVParser for it \n " );
2006-07-06 12:27:43 +00:00
return - 1 ;
}
2006-07-04 14:03:59 +00:00
} else if ( trk -> sampleSize )
samplesInChunk = size / trk -> sampleSize ;
else
2006-04-14 10:51:32 +00:00
samplesInChunk = 1 ;
2003-11-03 21:51:07 +00:00
2006-04-14 10:51:32 +00:00
/* copy extradata if it exists */
if ( trk -> vosLen == 0 && enc -> extradata_size > 0 ) {
2003-11-03 21:51:07 +00:00
trk -> vosLen = enc -> extradata_size ;
trk -> vosData = av_malloc ( trk -> vosLen );
memcpy ( trk -> vosData , enc -> extradata , trk -> vosLen );
}
2011-12-06 16:15:35 +01:00
if ( ! ( trk -> entry % MOV_INDEX_CLUSTER_SIZE )) {
trk -> cluster = av_realloc_f ( trk -> cluster , sizeof ( * trk -> cluster ), ( trk -> entry + MOV_INDEX_CLUSTER_SIZE ));
if ( ! trk -> cluster )
return - 1 ;
}
2009-01-16 01:12:32 +00: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-06 16:15:35 +01:00
if ( mov -> frag_seq_num > 0 ){
uint8_t * buf = NULL ;
size = pkt -> size ;
2011-12-12 01:25:37 +01:00
2011-12-06 16:15:35 +01:00
if ( ff_avc_parse_nal_units_buf ( pkt -> data , & buf , & size ) < 0 ){
av_log ( s , AV_LOG_ERROR , "malformated H264 bitstream \n " );
return - 1 ;
}
trk -> cluster [ trk -> entry ]. data = buf ;
2011-12-12 01:25:37 +01:00
if ( trk -> hint_track >= 0 && trk -> hint_track < mov -> nb_streams ) {
reformatted_data = av_malloc ( size );
memcpy ( reformatted_data , buf , size );
}
} else if ( trk -> hint_track >= 0 && trk -> hint_track < mov -> nb_streams ) {
2011-12-09 21:19:57 +02:00
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 21:19:57 +02:00
}
2011-01-28 21:32:09 -08: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 ;
2011-12-06 16:15:35 +01:00
} else if ( mov -> frag_seq_num > 0 ){
trk -> cluster [ trk -> entry ]. data = av_malloc ( size );
if ( ! trk -> cluster [ trk -> entry ]. data )
return AVERROR ( ENOMEM );
memcpy ( trk -> cluster [ trk -> entry ]. data , pkt -> data , size );
2009-01-16 01:12:32 +00:00
} else {
2011-02-21 19:28:17 +01:00
avio_write ( pb , pkt -> data , size );
2009-01-16 01:12:32 +00:00
}
2009-01-15 14:03:07 +00:00
if (( enc -> codec_id == CODEC_ID_DNXHD ||
2009-05-13 04:20:23 +00:00
enc -> codec_id == CODEC_ID_AC3 ) && ! trk -> vosLen ) {
2008-08-31 20:20:12 +00:00
/* copy frame to create needed atoms */
trk -> vosLen = size ;
trk -> vosData = av_malloc ( size );
2008-08-31 20:21:00 +00:00
if ( ! trk -> vosData )
return AVERROR ( ENOMEM );
2008-08-31 20:20:12 +00:00
memcpy ( trk -> vosData , pkt -> data , size );
2006-03-11 18:18:17 +00:00
}
2011-12-06 16:15:35 +01:00
trk -> cluster [ trk -> entry ]. pos = avio_tell ( pb ) - ( mov -> frag_seq_num == 0 ? size : 0 );
2006-06-24 18:09:20 +00:00
trk -> cluster [ trk -> entry ]. samplesInChunk = samplesInChunk ;
2011-11-30 23:11:49 +01:00
trk -> cluster [ trk -> entry ]. chunkNum = 0 ;
2006-06-24 18:09:20 +00:00
trk -> cluster [ trk -> entry ]. size = size ;
trk -> cluster [ trk -> entry ]. entries = samplesInChunk ;
2006-11-01 17:27:39 +00:00
trk -> cluster [ trk -> entry ]. dts = pkt -> dts ;
trk -> trackDuration = pkt -> dts - trk -> cluster [ 0 ]. dts + pkt -> duration ;
2008-05-29 00:58:41 +00: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 00:54:33 +00:00
if ( pkt -> dts != pkt -> pts )
2009-05-15 06:11:53 +00:00
trk -> flags |= MOV_TRACK_CTTS ;
2008-05-29 00:54:33 +00:00
trk -> cluster [ trk -> entry ]. cts = pkt -> pts - pkt -> dts ;
2009-05-15 06:11:53 +00:00
trk -> cluster [ trk -> entry ]. flags = 0 ;
2010-03-31 12:29:58 +00:00
if ( pkt -> flags & AV_PKT_FLAG_KEY ) {
2011-02-13 09:18:45 +09: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 06:11:53 +00: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 21:05:52 +00:00
}
2003-11-03 21:51:07 +00:00
trk -> entry ++ ;
trk -> sampleCount += samplesInChunk ;
2006-05-13 18:01:16 +00:00
mov -> mdat_size += size ;
2003-11-03 21:51:07 +00:00
2011-03-14 20:39:06 +01:00
avio_flush ( pb );
2010-05-18 19:47:24 +00:00
if ( trk -> hint_track >= 0 && trk -> hint_track < mov -> nb_streams )
2011-12-09 21:19:57 +02:00
ff_mov_add_hinted_packet ( s , pkt , trk -> hint_track , trk -> entry ,
reformatted_data , size );
av_free ( reformatted_data );
2003-08-26 20:23:13 +00:00
return 0 ;
}
2010-05-05 08:41:10 +00: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 08:41:10 +00: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 08:41:10 +00: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 08:41:10 +00: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 19:40:17 +00:00
ff_mov_write_packet ( s , & pkt );
2010-05-05 08:41:10 +00:00
av_freep ( & pkt . data );
}
}
}
2010-05-05 08:41:06 +00:00
static int mov_write_header ( AVFormatContext * s )
{
2011-02-20 11:04:12 +01:00
AVIOContext * pb = s -> pb ;
2010-05-05 08:41:06 +00:00
MOVMuxContext * mov = s -> priv_data ;
2011-07-07 11:25:03 +02:00
AVDictionaryEntry * t ;
2010-05-18 19:47:24 +00:00
int i , hint_track = 0 ;
2010-05-05 08:41:06 +00:00
2011-03-05 21:06:46 +01:00
if ( ! s -> pb -> seekable ) {
2010-05-05 08:41:06 +00:00
av_log ( s , AV_LOG_ERROR , "muxer does not support non seekable output \n " );
return - 1 ;
}
/* Default mode == MP4 */
mov -> mode = MODE_MP4 ;
2011-12-06 16:21:10 +01:00
if ( mov -> max_fragment_duration || mov -> max_fragment_size ){
2011-12-06 16:15:35 +01:00
mov -> fragments = 1 ;
}
2010-05-05 08:41:06 +00:00
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 ;
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 08:41:10 +00: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 12:27:02 +03:00
#if FF_API_FLAG_RTP_HINT
2010-05-18 19:47:24 +00:00
if ( s -> flags & AVFMT_FLAG_RTP_HINT ) {
2011-05-20 12:27:02 +03: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 19:47:24 +00: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 08:41:10 +00:00
mov -> tracks = av_mallocz ( mov -> nb_streams * sizeof ( * mov -> tracks ));
2010-05-05 08:41:06 +00: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 08:41:06 +00: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 19:47:24 +00:00
/* If hinting of this track is enabled by a later hint track,
* this is updated. */
track -> hint_track = - 1 ;
2010-05-05 08:41:06 +00: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 ;
2011-12-02 15:51:52 -05:00
/* set sampleSize for PCM and ADPCM */
if ( av_get_bits_per_sample ( st -> codec -> codec_id )) {
2011-01-20 13:14:12 -08:00
if ( ! st -> codec -> block_align ) {
2011-12-02 15:51:52 -05:00
av_log ( s , AV_LOG_ERROR , "track %d: codec block align is not set \n " , i );
2011-01-20 13:14:12 -08:00
goto error ;
}
track -> sampleSize = st -> codec -> block_align ;
2011-12-02 15:51:52 -05:00
}
/* set audio_vbr for compressed audio */
if ( av_get_bits_per_sample ( st -> codec -> codec_id ) < 8 ) {
if ( ! st -> codec -> frame_size ) {
av_log ( s , AV_LOG_ERROR , "track %d: codec frame size is not set \n " , i );
goto error ;
}
2010-05-05 08:41:06 +00:00
track -> audio_vbr = 1 ;
}
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 ;
2011-11-29 19:28:15 +01:00
avpriv_set_pts_info ( st , 64 , 1 , track -> timescale );
2010-05-05 08:41:06 +00: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 );
}
2010-05-05 08:41:06 +00:00
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 );
2011-07-07 11:25:03 +02:00
mov -> time += 0x7C25B080 ; //1970 based -> 1904 based
2010-05-05 08:41:10 +00:00
if ( mov -> chapter_track )
mov_create_chapter_track ( s , mov -> chapter_track );
2010-05-05 08:41:06 +00:00
2011-05-20 12:27:02 +03:00
if ( mov -> flags & FF_MOV_FLAG_RTP_HINT ) {
2010-05-18 19:47:24 +00: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 08:41:06 +00:00
return 0 ;
error :
av_freep ( & mov -> tracks );
return - 1 ;
}
2011-12-06 16:15:35 +01:00
static int update_first_fragment ( AVFormatContext * s )
2003-08-26 20:23:13 +00:00
{
2009-02-28 16:02:29 +00:00
MOVMuxContext * mov = s -> priv_data ;
2011-02-20 11:04:12 +01:00
AVIOContext * pb = s -> pb ;
2003-08-26 20:23:13 +00:00
int res = 0 ;
2006-06-24 18:09:20 +00:00
int i ;
2003-08-26 20:23:13 +00:00
2011-03-03 20:11:45 +01:00
int64_t moov_pos = avio_tell ( pb );
2003-08-26 20:23:13 +00:00
/* Write size of mdat tag */
2006-05-13 18:01:16 +00:00
if ( mov -> mdat_size + 8 <= UINT32_MAX ) {
2011-02-28 14:57:54 +01:00
avio_seek ( pb , mov -> mdat_pos , SEEK_SET );
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , mov -> mdat_size + 8 );
2006-01-23 14:12:03 +00:00
} else {
/* overwrite 'wide' placeholder atom */
2011-02-28 14:57:54 +01:00
avio_seek ( pb , mov -> mdat_pos - 8 , SEEK_SET );
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , 1 ); /* special value: real atom size will be 64 bit value after tag field */
2011-02-24 07:36:02 +01:00
ffio_wfourcc ( pb , "mdat" );
2011-02-21 19:28:17 +01:00
avio_wb64 ( pb , mov -> mdat_size + 16 );
2006-01-23 14:12:03 +00:00
}
2011-10-26 22:09:26 +02:00
avio_seek ( pb , mov -> reserved_moov_size ? mov -> reserved_moov_pos : moov_pos , SEEK_SET );
2003-08-26 20:23:13 +00:00
2004-02-14 19:08:09 +00:00
mov_write_moov_tag ( pb , mov , s );
2011-10-26 22:09:26 +02:00
if ( mov -> reserved_moov_size ){
int64_t size = mov -> reserved_moov_size - ( avio_tell ( pb ) - mov -> reserved_moov_pos );
if ( size < 8 ){
2011-12-13 20:01:49 +01:00
av_log ( s , AV_LOG_ERROR , "reserved_moov_size is too small, needed %" PRId64 " additional \n " , 8 - size );
2011-10-26 22:09:26 +02:00
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 );
}
2003-08-26 20:23:13 +00:00
2011-12-06 16:15:35 +01:00
return 0 ;
}
static int mov_write_trailer ( AVFormatContext * s )
{
MOVMuxContext * mov = s -> priv_data ;
AVIOContext * pb = s -> pb ;
int res = 0 ;
int i ;
flush_cluster_buffer ( s );
2010-05-05 08:41:10 +00:00
if ( mov -> chapter_track )
av_freep ( & mov -> tracks [ mov -> chapter_track ]. enc );
2006-06-25 00:10:52 +00:00
for ( i = 0 ; i < mov -> nb_streams ; i ++ ) {
2010-05-18 19:47:24 +00:00
if ( mov -> tracks [ i ]. tag == MKTAG ( 'r' , 't' , 'p' , ' ' ))
ff_mov_close_hinting ( & mov -> tracks [ i ]);
2006-06-24 18:09:20 +00:00
av_freep ( & mov -> tracks [ i ]. cluster );
2008-02-06 18:57:00 +00:00
if ( mov -> tracks [ i ]. vosLen ) av_free ( mov -> tracks [ i ]. vosData );
2004-01-08 19:01:16 +00:00
2003-08-26 20:23:13 +00:00
}
2004-02-14 19:08:09 +00:00
2011-03-14 20:39:06 +01:00
avio_flush ( pb );
2003-08-26 20:23:13 +00:00
2009-03-22 03:52:55 +00:00
av_freep ( & mov -> tracks );
2003-08-26 20:23:13 +00:00
return res ;
}
2009-01-13 23:44:16 +00:00
#if CONFIG_MOV_MUXER
2011-10-03 19:14:03 +02:00
MOV_CLASS ( mov )
2011-01-25 22:03:28 +00: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 ,
2010-11-23 00:41:28 +00:00
. flags = AVFMT_GLOBALHEADER ,
2008-08-24 16:51:50 +00:00
. codec_tag = ( const AVCodecTag * const []){ codec_movvideo_tags , codec_movaudio_tags , 0 },
2011-05-21 14:57:04 +03:00
. priv_class = & mov_muxer_class ,
2003-08-26 20:23:13 +00:00
};
2006-07-10 21:14:37 +00:00
#endif
2009-01-13 23:44:16 +00:00
#if CONFIG_TGP_MUXER
2011-10-03 19:14:03 +02:00
MOV_CLASS ( tgp )
2011-01-25 22:03:28 +00: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 ,
2005-03-23 12:52:24 +00:00
. flags = AVFMT_GLOBALHEADER ,
2008-08-24 16:51:50 +00: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 20:23:13 +00:00
};
2006-07-10 21:14:37 +00:00
#endif
2009-01-13 23:44:16 +00:00
#if CONFIG_MP4_MUXER
2011-10-03 19:14:03 +02:00
MOV_CLASS ( mp4 )
2011-01-25 22:03:28 +00: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 ,
2010-11-23 00:41:28 +00:00
. flags = AVFMT_GLOBALHEADER ,
2008-08-24 16:51:50 +00: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 20:23:13 +00:00
};
2006-07-10 21:14:37 +00:00
#endif
2009-01-13 23:44:16 +00:00
#if CONFIG_PSP_MUXER
2011-10-03 19:14:03 +02:00
MOV_CLASS ( psp )
2011-01-25 22:03:28 +00: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 ,
2006-01-24 08:03:42 +00:00
. flags = AVFMT_GLOBALHEADER ,
2008-08-24 16:51:50 +00: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 14:48:15 +00:00
};
2006-07-10 21:14:37 +00:00
#endif
2009-01-13 23:44:16 +00:00
#if CONFIG_TG2_MUXER
2011-10-03 19:14:03 +02:00
MOV_CLASS ( tg2 )
2011-01-25 22:03:28 +00: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 ,
2005-03-23 12:52:24 +00:00
. flags = AVFMT_GLOBALHEADER ,
2008-08-24 16:51:50 +00: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-16 23:14:38 +00:00
};
2006-07-10 21:14:37 +00:00
#endif
2009-01-13 23:44:16 +00:00
#if CONFIG_IPOD_MUXER
2011-10-03 19:14:03 +02:00
MOV_CLASS ( ipod )
2011-01-25 22:03:28 +00: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 ,
2008-03-16 13:36:36 +00:00
. flags = AVFMT_GLOBALHEADER ,
2009-03-11 07:59:38 +00: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 13:36:36 +00:00
};
#endif