Skip to content

Instantly share code, notes, and snippets.

@rcombs
Created July 20, 2015 18:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rcombs/e649a7bc0c0f9c7459a2 to your computer and use it in GitHub Desktop.
Save rcombs/e649a7bc0c0f9c7459a2 to your computer and use it in GitHub Desktop.
diff --git a/libavformat/isom.h b/libavformat/isom.h
index 5d48989..1578529 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -103,6 +103,7 @@ typedef struct MOVSbgp {
typedef struct MOVFragmentIndexItem {
int64_t moof_offset;
int64_t time;
+ int headers_read;
} MOVFragmentIndexItem;
typedef struct MOVFragmentIndex {
diff --git a/libavformat/mov.c b/libavformat/mov.c
index d24faa7..f2a289c 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -3355,7 +3355,95 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return AVERROR_EOF;
frag->implicit_offset = offset;
- st->duration = sc->track_end = dts + sc->time_offset;
+
+ if (st->duration < (dts + sc->time_offset))
+ st->duration = sc->track_end = dts + sc->time_offset;
+
+ return 0;
+}
+
+static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ int64_t offset = avio_tell(pb) + atom.size, pts;
+ uint8_t version;
+ int i, track_id;
+ AVStream *st = NULL;
+ MOVStreamContext *sc;
+ MOVFragmentIndex *index;
+ MOVFragmentIndex **tmp;
+ AVRational timescale;
+
+ version = avio_r8(pb);
+ if (version > 1)
+ return AVERROR_PATCHWELCOME;
+
+ avio_rb24(pb); // flags
+
+ track_id = avio_rb32(pb); // Reference ID
+ for (i = 0; i < c->fc->nb_streams; i++) {
+ if (c->fc->streams[i]->id == track_id) {
+ st = c->fc->streams[i];
+ break;
+ }
+ }
+ if (!st) {
+ av_log(c->fc, AV_LOG_ERROR, "could not find corresponding track id %d\n", track_id);
+ return AVERROR_INVALIDDATA;
+ }
+
+ sc = st->priv_data;
+
+ timescale = av_make_q(1, avio_rb32(pb));
+
+ if (version == 0) {
+ pts = avio_rb32(pb);
+ offset += avio_rb32(pb);
+ } else {
+ pts = avio_rb64(pb);
+ offset += avio_rb64(pb);
+ }
+
+ avio_rb16(pb); // reserved
+
+ index = av_mallocz(sizeof(MOVFragmentIndex));
+ if (!index) {
+ return AVERROR(ENOMEM);
+ }
+
+ index->track_id = track_id;
+
+ index->item_count = avio_rb16(pb);
+ index->items = av_mallocz_array(
+ index->item_count, sizeof(MOVFragmentIndexItem));
+
+ if (!index->items) {
+ av_freep(&index);
+ return AVERROR(ENOMEM);
+ }
+
+ tmp = av_realloc_array(c->fragment_index_data,
+ c->fragment_index_count + 1,
+ sizeof(MOVFragmentIndex*));
+ if (!tmp) {
+ av_freep(&index->items);
+ av_freep(&index);
+ return AVERROR(ENOMEM);
+ }
+ c->fragment_index_data = tmp;
+ c->fragment_index_data[c->fragment_index_count++] = index;
+
+ for (i = 0; i < index->item_count; i++) {
+ int32_t size = avio_rb32(pb);
+ int32_t duration = avio_rb32(pb);
+ avio_rb32(pb); // sap_flags
+ index->items[i].moof_offset = offset;
+ index->items[i].time = av_rescale_q(pts, st->time_base, timescale);
+ offset += size;
+ pts += duration;
+ }
+
+ st->duration = sc->track_end = pts;
+
return 0;
}
@@ -3612,6 +3700,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = {
{ MKTAG('a','l','a','c'), mov_read_alac }, /* alac specific atom */
{ MKTAG('a','v','c','C'), mov_read_glbl },
{ MKTAG('p','a','s','p'), mov_read_pasp },
+{ MKTAG('s','i','d','x'), mov_read_sidx },
{ MKTAG('s','t','b','l'), mov_read_default },
{ MKTAG('s','t','c','o'), mov_read_stco },
{ MKTAG('s','t','p','s'), mov_read_stps },
@@ -3735,9 +3824,9 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return err;
}
if (c->found_moov && c->found_mdat &&
- ((!pb->seekable || c->fc->flags & AVFMT_FLAG_IGNIDX) ||
+ ((!pb->seekable || c->fc->flags & AVFMT_FLAG_IGNIDX || c->fragment_index_data) ||
start_pos + a.size == avio_size(pb))) {
- if (!pb->seekable || c->fc->flags & AVFMT_FLAG_IGNIDX)
+ if (!pb->seekable || c->fc->flags & AVFMT_FLAG_IGNIDX || c->fragment_index_data)
c->next_root_atom = start_pos + a.size;
c->atom_depth --;
return 0;
@@ -4437,12 +4526,66 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
return 0;
}
+static int mov_seek_fragment(AVFormatContext *s, AVStream *st, int64_t timestamp)
+{
+ MOVContext *mov = s->priv_data;
+ int64_t offset = -1;
+ MOVFragmentIndex *index = NULL;
+ int i;
+
+ if (!mov->fragment_index_count)
+ return 0;
+
+ for (i = 0; i < mov->fragment_index_count; i++) {
+ if (mov->fragment_index_data[i]->track_id == st->index + 1) {
+ index = mov->fragment_index_data[i];
+ break;
+ }
+ }
+ if (!index)
+ return 0;
+
+ for (i = index->item_count - 1; i >= 0; i--) {
+ if (index->items[i].time < timestamp) {
+ if (index->items[i].headers_read)
+ return 0;
+
+ offset = index->items[i].moof_offset;
+ break;
+ }
+ }
+
+ if (offset == -1)
+ return 0;
+
+ if (avio_seek(s->pb, offset, SEEK_SET) != offset) {
+ av_log(mov->fc, AV_LOG_ERROR, "target moof atom offset 0x%"PRIx64": partial file\n", offset);
+ return AVERROR_INVALIDDATA;
+ }
+
+ mov->found_mdat = 0;
+ mov->next_root_atom = 0;
+ index->current_item = i;
+ if (mov_read_default(mov, s->pb, (MOVAtom){ AV_RL32("root"), INT64_MAX }) < 0 ||
+ avio_feof(s->pb)) {
+ return AVERROR_EOF;
+ }
+
+ index->items[i].headers_read = 1;
+
+ return 1;
+}
+
static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp, int flags)
{
MOVStreamContext *sc = st->priv_data;
int sample, time_sample;
int i;
+ int ret = mov_seek_fragment(s, st, timestamp);
+ if (ret < 0)
+ return ret;
+
sample = av_index_search_timestamp(st, timestamp, flags);
av_log(s, AV_LOG_TRACE, "stream %d, timestamp %"PRId64", sample %d\n", st->index, timestamp, sample);
if (sample < 0 && st->nb_index_entries && timestamp < st->index_entries[0].timestamp)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment