#include <limits.h>
//#define DEBUG
-//#define DEBUG_METADATA
//#define MOV_EXPORT_ALL_METADATA
#include "libavutil/intreadwrite.h"
+#include "libavutil/intfloat_readwrite.h"
+#include "libavutil/mathematics.h"
#include "libavutil/avstring.h"
+#include "libavutil/dict.h"
#include "avformat.h"
#include "avio_internal.h"
#include "riff.h"
avio_rb16(pb); // unknown
snprintf(buf, sizeof(buf), "%d", avio_rb16(pb));
- av_metadata_set2(&c->fc->metadata, "track", buf, 0);
+ av_dict_set(&c->fc->metadata, "track", buf, 0);
avio_rb16(pb); // total tracks
avio_read(pb, str, str_size);
str[str_size] = 0;
}
- av_metadata_set2(&c->fc->metadata, key, str, 0);
+ av_dict_set(&c->fc->metadata, key, str, 0);
if (*language && strcmp(language, "und")) {
snprintf(key2, sizeof(key2), "%s-%s", key, language);
- av_metadata_set2(&c->fc->metadata, key2, str, 0);
+ av_dict_set(&c->fc->metadata, key2, str, 0);
}
}
-#ifdef DEBUG_METADATA
- av_log(c->fc, AV_LOG_DEBUG, "lang \"%3s\" ", language);
- av_log(c->fc, AV_LOG_DEBUG, "tag \"%s\" value \"%s\" atom \"%.4s\" %d %lld\n",
- key, str, (char*)&atom.type, str_size, atom.size);
-#endif
+ av_dlog(c->fc, "lang \"%3s\" ", language);
+ av_dlog(c->fc, "tag \"%s\" value \"%s\" atom \"%.4s\" %d %"PRId64"\n",
+ key, str, (char*)&atom.type, str_size, atom.size);
return 0;
}
{
AVStream *st;
uint32_t type;
- uint32_t ctype;
+ uint32_t av_unused ctype;
if (c->fc->nb_streams < 1) // meta before first trak
return 0;
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
else if(type == MKTAG('m','1','a',' '))
st->codec->codec_id = CODEC_ID_MP2;
- else if(type == MKTAG('s','u','b','p'))
+ else if((type == MKTAG('s','u','b','p')) || (type == MKTAG('c','l','c','p')))
st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
avio_rb32(pb); /* component manufacture */
int ff_mov_read_esds(AVFormatContext *fc, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
- int tag, len;
+ int tag;
if (fc->nb_streams < 1)
return 0;
st = fc->streams[fc->nb_streams-1];
avio_rb32(pb); /* version + flags */
- len = ff_mp4_read_descr(fc, pb, &tag);
+ ff_mp4_read_descr(fc, pb, &tag);
if (tag == MP4ESDescrTag) {
avio_rb16(pb); /* ID */
avio_r8(pb); /* priority */
} else
avio_rb16(pb); /* ID */
- len = ff_mp4_read_descr(fc, pb, &tag);
+ ff_mp4_read_descr(fc, pb, &tag);
if (tag == MP4DecConfigDescrTag)
ff_mp4_read_dec_config_descr(fc, st, pb);
return 0;
return 0;
}
+static int mov_read_wfex(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ AVStream *st;
+
+ if (c->fc->nb_streams < 1)
+ return 0;
+ st = c->fc->streams[c->fc->nb_streams-1];
+
+ ff_get_wav_header(pb, st->codec, atom.size);
+
+ return 0;
+}
+
static int mov_read_pasp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
const int num = avio_rb32(pb);
if (strcmp(type, "qt "))
c->isom = 1;
av_log(c->fc, AV_LOG_DEBUG, "ISO: File Type Major Brand: %.4s\n",(char *)&type);
- av_metadata_set2(&c->fc->metadata, "major_brand", type, 0);
+ av_dict_set(&c->fc->metadata, "major_brand", type, 0);
minor_ver = avio_rb32(pb); /* minor version */
snprintf(minor_ver_str, sizeof(minor_ver_str), "%d", minor_ver);
- av_metadata_set2(&c->fc->metadata, "minor_version", minor_ver_str, 0);
+ av_dict_set(&c->fc->metadata, "minor_version", minor_ver_str, 0);
comp_brand_size = atom.size - 8;
if (comp_brand_size < 0)
return AVERROR(ENOMEM);
avio_read(pb, comp_brands_str, comp_brand_size);
comp_brands_str[comp_brand_size] = 0;
- av_metadata_set2(&c->fc->metadata, "compatible_brands", comp_brands_str, 0);
+ av_dict_set(&c->fc->metadata, "compatible_brands", comp_brands_str, 0);
av_freep(&comp_brands_str);
return 0;
static int mov_read_moof(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
c->fragment.moof_offset = avio_tell(pb) - 8;
- av_dlog(c->fc, "moof offset %llx\n", c->fragment.moof_offset);
+ av_dlog(c->fc, "moof offset %"PRIx64"\n", c->fragment.moof_offset);
return mov_read_default(c, pb, atom);
}
-static void mov_metadata_creation_time(AVMetadata **metadata, time_t time)
+static void mov_metadata_creation_time(AVDictionary **metadata, time_t time)
{
char buffer[32];
if (time) {
ptm = gmtime(&time);
if (!ptm) return;
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", ptm);
- av_metadata_set2(metadata, "creation_time", buffer, 0);
+ av_dict_set(metadata, "creation_time", buffer, 0);
}
}
lang = avio_rb16(pb); /* language */
if (ff_mov_lang_to_iso639(lang, language))
- av_metadata_set2(&st->metadata, "language", language, 0);
+ av_dict_set(&st->metadata, "language", language, 0);
avio_rb16(pb); /* quality */
return 0;
/* adjust first dts according to edit list */
if (sc->time_offset && mov->time_scale > 0) {
- int rescaled = sc->time_offset < 0 ? av_rescale(sc->time_offset, sc->time_scale, mov->time_scale) : sc->time_offset;
- current_dts = -rescaled;
+ if (sc->time_offset < 0)
+ sc->time_offset = av_rescale(sc->time_offset, sc->time_scale, mov->time_scale);
+ current_dts = -sc->time_offset;
if (sc->ctts_data && sc->stts_data &&
sc->ctts_data[0].duration / sc->stts_data[0].duration > 16) {
/* more than 16 frames delay, dts are likely wrong
av_strlcat(filename, ref->path + l + 1, 1024);
- if (!avio_open(pb, filename, AVIO_RDONLY))
+ if (!avio_open(pb, filename, AVIO_FLAG_READ))
return 0;
}
}
MOVFragment *frag = &c->fragment;
AVStream *st = NULL;
MOVStreamContext *sc;
+ MOVStts *ctts_data;
uint64_t offset;
int64_t dts;
int data_offset = 0;
flags = avio_rb24(pb);
entries = avio_rb32(pb);
av_dlog(c->fc, "flags 0x%x entries %d\n", flags, entries);
- if (flags & 0x001) data_offset = avio_rb32(pb);
- if (flags & 0x004) first_sample_flags = avio_rb32(pb);
- if (flags & 0x800) {
- MOVStts *ctts_data;
- if ((uint64_t)entries+sc->ctts_count >= UINT_MAX/sizeof(*sc->ctts_data))
- return -1;
- ctts_data = av_realloc(sc->ctts_data,
- (entries+sc->ctts_count)*sizeof(*sc->ctts_data));
+
+ /* Always assume the presence of composition time offsets.
+ * Without this assumption, for instance, we cannot deal with a track in fragmented movies that meet the following.
+ * 1) in the initial movie, there are no samples.
+ * 2) in the first movie fragment, there is only one sample without composition time offset.
+ * 3) in the subsequent movie fragments, there are samples with composition time offset. */
+ if (!sc->ctts_count && sc->sample_count)
+ {
+ /* Complement ctts table if moov atom doesn't have ctts atom. */
+ ctts_data = av_malloc(sizeof(*sc->ctts_data));
if (!ctts_data)
return AVERROR(ENOMEM);
sc->ctts_data = ctts_data;
+ sc->ctts_data[sc->ctts_count].count = sc->sample_count;
+ sc->ctts_data[sc->ctts_count].duration = 0;
+ sc->ctts_count++;
}
- dts = st->duration;
+ if ((uint64_t)entries+sc->ctts_count >= UINT_MAX/sizeof(*sc->ctts_data))
+ return -1;
+ ctts_data = av_realloc(sc->ctts_data,
+ (entries+sc->ctts_count)*sizeof(*sc->ctts_data));
+ if (!ctts_data)
+ return AVERROR(ENOMEM);
+ sc->ctts_data = ctts_data;
+
+ if (flags & 0x001) data_offset = avio_rb32(pb);
+ if (flags & 0x004) first_sample_flags = avio_rb32(pb);
+ dts = st->duration - sc->time_offset;
offset = frag->base_data_offset + data_offset;
distance = 0;
av_dlog(c->fc, "first sample flags 0x%x\n", first_sample_flags);
if (flags & 0x100) sample_duration = avio_rb32(pb);
if (flags & 0x200) sample_size = avio_rb32(pb);
if (flags & 0x400) sample_flags = avio_rb32(pb);
- if (flags & 0x800) {
- sc->ctts_data[sc->ctts_count].count = 1;
- sc->ctts_data[sc->ctts_count].duration = avio_rb32(pb);
- sc->ctts_count++;
- }
+ sc->ctts_data[sc->ctts_count].count = 1;
+ sc->ctts_data[sc->ctts_count].duration = (flags & 0x800) ? avio_rb32(pb) : 0;
+ sc->ctts_count++;
if ((keyframe = st->codec->codec_type == AVMEDIA_TYPE_AUDIO ||
(flags & 0x004 && !i && !sample_flags) || sample_flags & 0x2000000))
distance = 0;
offset += sample_size;
}
frag->moof_offset = offset;
- st->duration = dts;
+ st->duration = dts + sc->time_offset;
return 0;
}
goto free_and_return;
atom.type = MKTAG('m','o','o','v');
atom.size = moov_len;
-#ifdef DEBUG
-// { int fd = open("/tmp/uncompheader.mov", O_WRONLY | O_CREAT); write(fd, moov_data, moov_len); close(fd); }
-#endif
ret = mov_read_default(c, &ctx, atom);
free_and_return:
av_free(moov_data);
static int mov_read_elst(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
MOVStreamContext *sc;
- int i, edit_count;
+ int i, edit_count, version;
if (c->fc->nb_streams < 1)
return 0;
sc = c->fc->streams[c->fc->nb_streams-1]->priv_data;
- avio_r8(pb); /* version */
+ version = avio_r8(pb); /* version */
avio_rb24(pb); /* flags */
edit_count = avio_rb32(pb); /* entries */
return -1;
for(i=0; i<edit_count; i++){
- int time;
- int duration = avio_rb32(pb); /* Track duration */
- time = avio_rb32(pb); /* Media time */
+ int64_t time;
+ int64_t duration;
+ if (version == 1) {
+ duration = avio_rb64(pb);
+ time = avio_rb64(pb);
+ } else {
+ duration = avio_rb32(pb); /* segment duration */
+ time = (int32_t)avio_rb32(pb); /* media time */
+ }
avio_rb32(pb); /* Media rate */
if (i == 0 && time >= -1) {
sc->time_offset = time != -1 ? time : -duration;
{ MKTAG('e','s','d','s'), mov_read_esds },
{ MKTAG('d','a','c','3'), mov_read_dac3 }, /* AC-3 info */
{ MKTAG('w','i','d','e'), mov_read_wide }, /* place holder */
+{ MKTAG('w','f','e','x'), mov_read_wfex },
{ MKTAG('c','m','o','v'), mov_read_cmov },
{ 0, NULL }
};
return score;
}
}
- return score;
}
// must be done after parsing all trak because there's no order requirement
av_log(s, AV_LOG_ERROR, "moov atom not found\n");
return -1;
}
- av_dlog(mov->fc, "on_parse_exit_offset=%lld\n", avio_tell(pb));
+ av_dlog(mov->fc, "on_parse_exit_offset=%"PRId64"\n", avio_tell(pb));
if (pb->seekable && mov->chapter_track > 0)
mov_read_chapters(s);
mov_read_default(mov, s->pb, (MOVAtom){ AV_RL32("root"), INT64_MAX }) < 0 ||
s->pb->eof_reached)
return AVERROR_EOF;
- av_dlog(s, "read fragments, offset 0x%llx\n", avio_tell(s->pb));
+ av_dlog(s, "read fragments, offset 0x%"PRIx64"\n", avio_tell(s->pb));
goto retry;
}
sc = st->priv_data;
}
AVInputFormat ff_mov_demuxer = {
- "mov,mp4,m4a,3gp,3g2,mj2",
- NULL_IF_CONFIG_SMALL("QuickTime/MPEG-4/Motion JPEG 2000 format"),
- sizeof(MOVContext),
- mov_probe,
- mov_read_header,
- mov_read_packet,
- mov_read_close,
- mov_read_seek,
+ .name = "mov,mp4,m4a,3gp,3g2,mj2",
+ .long_name = NULL_IF_CONFIG_SMALL("QuickTime/MPEG-4/Motion JPEG 2000 format"),
+ .priv_data_size = sizeof(MOVContext),
+ .read_probe = mov_probe,
+ .read_header = mov_read_header,
+ .read_packet = mov_read_packet,
+ .read_close = mov_read_close,
+ .read_seek = mov_read_seek,
};