#include "libavcodec/put_bits.h"
#include "internal.h"
#include "libavutil/avstring.h"
+#include "libavutil/opt.h"
#undef NDEBUG
#include <assert.h>
+static const AVOption options[] = {
+ { "movflags", "MOV muxer flags", offsetof(MOVMuxContext, flags), FF_OPT_TYPE_FLAGS, {.dbl = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
+ { "rtphint", "Add RTP hint tracks", 0, FF_OPT_TYPE_CONST, {.dbl = FF_MOV_FLAG_RTP_HINT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
+ { NULL },
+};
+
+static const AVClass mov_muxer_class = {
+ .class_name = "MOV/3GP/MP4/3G2 muxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
//FIXME support 64 bit variant with wide placeholders
static int64_t updateSize(AVIOContext *pb, int64_t pos)
{
return 10;
}
-static unsigned int descrLength(unsigned int len)
-{
- int i;
- for(i=1; len>>(7*i); i++);
- return len + 1 + i;
-}
-
static void putDescr(AVIOContext *pb, int tag, unsigned int size)
{
- int i= descrLength(size) - size - 2;
+ int i = 3;
avio_w8(pb, tag);
for(; i>0; i--)
avio_w8(pb, (size>>(7*i)) | 0x80);
avio_w8(pb, size & 0x7F);
}
+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;
+}
+
static int mov_write_esds_tag(AVIOContext *pb, MOVTrack *track) // Basic
{
int64_t pos = avio_tell(pb);
- int decoderSpecificInfoLen = track->vosLen ? descrLength(track->vosLen):0;
+ int decoderSpecificInfoLen = track->vosLen ? 5+track->vosLen : 0;
+ unsigned avg_bitrate;
avio_wb32(pb, 0); // size
ffio_wfourcc(pb, "esds");
avio_wb32(pb, 0); // Version
// ES descriptor
- putDescr(pb, 0x03, 3 + descrLength(13 + decoderSpecificInfoLen) +
- descrLength(1));
+ putDescr(pb, 0x03, 3 + 5+13 + decoderSpecificInfoLen + 5+1);
avio_wb16(pb, track->trackID);
avio_w8(pb, 0x00); // flags (= no flags)
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
- avio_wb32(pb, FFMAX(track->enc->bit_rate, track->enc->rc_max_rate)); // maxbitrate (FIXME should be max rate in any 1 sec window)
- if(track->enc->rc_max_rate != track->enc->rc_min_rate || track->enc->rc_min_rate==0)
- avio_wb32(pb, 0); // vbr
- else
- avio_wb32(pb, track->enc->rc_max_rate); // avg bitrate
+ 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);
if (track->vosLen) {
// DecoderSpecific info descriptor
ffio_wfourcc(pb, "ACLR");
ffio_wfourcc(pb, "ACLR");
ffio_wfourcc(pb, "0001");
- avio_wb32(pb, 1); /* yuv 1 / rgb 2 ? */
+ avio_wb32(pb, 2); /* yuv range: full 1 / normal 2 */
avio_wb32(pb, 0); /* unknown */
avio_wb32(pb, 24); /* size */
memset(compressor_name,0,32);
/* 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)
- strncpy(compressor_name,track->enc->codec->name,31);
+ av_strlcpy(compressor_name,track->enc->codec->name,32);
avio_w8(pb, strlen(compressor_name));
avio_write(pb, compressor_name, 31);
// This box seems important for the psp playback ... without it the movie seems to hang
static int mov_write_edts_tag(AVIOContext *pb, MOVTrack *track)
{
- avio_wb32(pb, 0x24); /* size */
+ 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);
ffio_wfourcc(pb, "edts");
- avio_wb32(pb, 0x1c); /* size */
+ avio_wb32(pb, size - 8);
ffio_wfourcc(pb, "elst");
- avio_wb32(pb, 0x0);
- avio_wb32(pb, 0x1);
+ avio_w8(pb, version);
+ avio_wb24(pb, 0); /* flags */
- /* duration ... doesn't seem to effect psp */
- avio_wb32(pb, av_rescale_rnd(track->trackDuration, MOV_TIMESCALE,
- track->timescale, AV_ROUND_UP));
+ 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);
+ }
- avio_wb32(pb, track->cluster[0].cts); /* first pts is cts since dts is 0 */
+ /* duration */
+ if (version == 1) {
+ avio_wb64(pb, duration);
+ avio_wb64(pb, start_ct);
+ } else {
+ avio_wb32(pb, duration);
+ avio_wb32(pb, start_ct);
+ }
avio_wb32(pb, 0x00010000);
- return 0x24;
+ return size;
}
static int mov_write_tref_tag(AVIOContext *pb, MOVTrack *track)
return 0x34;
}
-static int mov_write_udta_sdp(AVIOContext *pb, AVCodecContext *ctx, int index)
+static int mov_write_udta_sdp(AVIOContext *pb, AVFormatContext *ctx, int index)
{
char buf[1000] = "";
int len;
- ff_sdp_write_media(buf, sizeof(buf), ctx, NULL, NULL, 0, 0);
+ ff_sdp_write_media(buf, sizeof(buf), ctx->streams[0]->codec, NULL, NULL, 0, 0, ctx->flags);
av_strlcatf(buf, sizeof(buf), "a=control:streamid=%d\r\n", index);
len = strlen(buf);
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "trak");
mov_write_tkhd_tag(pb, track, st);
- if (track->mode == MODE_PSP || track->flags & MOV_TRACK_CTTS)
+ if (track->mode == MODE_PSP || track->flags & MOV_TRACK_CTTS || track->cluster[0].dts)
mov_write_edts_tag(pb, track); // PSP Movies require edts box
if (track->tref_tag)
mov_write_tref_tag(pb, track);
if (track->mode == MODE_PSP)
mov_write_uuid_tag_psp(pb,track); // PSP Movies require this uuid box
if (track->tag == MKTAG('r','t','p',' '))
- mov_write_udta_sdp(pb, track->rtp_ctx->streams[0]->codec, track->trackID);
+ mov_write_udta_sdp(pb, track->rtp_ctx, track->trackID);
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)
return 0;
}
- ret = url_open_dyn_buf(&pb_buf);
+ ret = avio_open_dyn_buf(&pb_buf);
if(ret < 0)
return ret;
if (s->nb_chapters)
mov_write_chpl_tag(pb_buf, s);
- if ((size = url_close_dyn_buf(pb_buf, &buf)) > 0) {
+ if ((size = avio_close_dyn_buf(pb_buf, &buf)) > 0) {
avio_wb32(pb, size+8);
ffio_wfourcc(pb, "udta");
avio_write(pb, buf, size);
unsigned int samplesInChunk = 0;
int size= pkt->size;
- if (url_is_streamed(s->pb)) return 0; /* Can't handle that */
+ if (!s->pb->seekable) return 0; /* Can't handle that */
if (!size) return 0; /* Discard 0 sized packets */
if (enc->codec_id == CODEC_ID_AMR_NB) {
/* from x264 or from bytestream h264 */
/* nal reformating needed */
size = ff_avc_parse_nal_units(pb, pkt->data, pkt->size);
+ } 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;
} else {
avio_write(pb, pkt->data, size);
}
MOVMuxContext *mov = s->priv_data;
int i, hint_track = 0;
- if (url_is_streamed(s->pb)) {
+ if (!s->pb->seekable) {
av_log(s, AV_LOG_ERROR, "muxer does not support non seekable output\n");
return -1;
}
if (mov->mode & (MODE_MOV|MODE_IPOD) && s->nb_chapters)
mov->chapter_track = mov->nb_streams++;
+#if FF_API_FLAG_RTP_HINT
if (s->flags & AVFMT_FLAG_RTP_HINT) {
+ 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) {
/* Add hint tracks for each audio and video stream */
hint_track = mov->nb_streams;
for (i = 0; i < s->nb_streams; i++) {
if (mov->chapter_track)
mov_create_chapter_track(s, mov->chapter_track);
- if (s->flags & AVFMT_FLAG_RTP_HINT) {
+ if (mov->flags & FF_MOV_FLAG_RTP_HINT) {
/* Initialize the hint tracks for each audio and video stream */
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
mov_write_trailer,
.flags = AVFMT_GLOBALHEADER,
.codec_tag = (const AVCodecTag* const []){codec_movvideo_tags, codec_movaudio_tags, 0},
+ .priv_class = &mov_muxer_class,
};
#endif
#if CONFIG_TGP_MUXER
mov_write_trailer,
.flags = AVFMT_GLOBALHEADER,
.codec_tag = (const AVCodecTag* const []){codec_3gp_tags, 0},
+ .priv_class = &mov_muxer_class,
};
#endif
#if CONFIG_MP4_MUXER
mov_write_trailer,
.flags = AVFMT_GLOBALHEADER,
.codec_tag = (const AVCodecTag* const []){ff_mp4_obj_type, 0},
+ .priv_class = &mov_muxer_class,
};
#endif
#if CONFIG_PSP_MUXER
mov_write_trailer,
.flags = AVFMT_GLOBALHEADER,
.codec_tag = (const AVCodecTag* const []){ff_mp4_obj_type, 0},
+ .priv_class = &mov_muxer_class,
};
#endif
#if CONFIG_TG2_MUXER
mov_write_trailer,
.flags = AVFMT_GLOBALHEADER,
.codec_tag = (const AVCodecTag* const []){codec_3gp_tags, 0},
+ .priv_class = &mov_muxer_class,
};
#endif
#if CONFIG_IPOD_MUXER
mov_write_trailer,
.flags = AVFMT_GLOBALHEADER,
.codec_tag = (const AVCodecTag* const []){codec_ipod_tags, 0},
+ .priv_class = &mov_muxer_class,
};
#endif