Created
July 20, 2015 19:02
-
-
Save rcombs/9639c5bdcd2c566a274a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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..96910e0 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; | |
@@ -4340,6 +4429,38 @@ static int should_retry(AVIOContext *pb, int error_code) { | |
return 1; | |
} | |
+static int mov_switch_root(AVFormatContext *s, int64_t target) | |
+{ | |
+ MOVContext *mov = s->priv_data; | |
+ int i, j; | |
+ | |
+ if (avio_seek(s->pb, target, SEEK_SET) != target) { | |
+ av_log(mov->fc, AV_LOG_ERROR, "root atom offset 0x%"PRIx64": partial file\n", target); | |
+ return AVERROR_INVALIDDATA; | |
+ } | |
+ | |
+ mov->found_mdat = 0; | |
+ mov->next_root_atom = 0; | |
+ for (i = 0; i < mov->fragment_index_count; i++) { | |
+ MOVFragmentIndex *index = mov->fragment_index_data[i]; | |
+ for (j = 0; j < index->item_count; j++) { | |
+ MOVFragmentIndexItem *item = &index->items[j]; | |
+ if (item->moof_offset == target) { | |
+ item->headers_read = 1; | |
+ index->current_item = j; | |
+ break; // Advance to next index in outer loop | |
+ } | |
+ } | |
+ } | |
+ | |
+ if (mov_read_default(mov, s->pb, (MOVAtom){ AV_RL32("root"), INT64_MAX }) < 0 || | |
+ avio_feof(s->pb)) | |
+ return AVERROR_EOF; | |
+ av_log(s, AV_LOG_TRACE, "read fragments, offset 0x%"PRIx64"\n", avio_tell(s->pb)); | |
+ | |
+ return 1; | |
+} | |
+ | |
static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) | |
{ | |
MOVContext *mov = s->priv_data; | |
@@ -4351,18 +4472,10 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) | |
retry: | |
sample = mov_find_next_sample(s, &st); | |
if (!sample) { | |
- mov->found_mdat = 0; | |
if (!mov->next_root_atom) | |
return AVERROR_EOF; | |
- if (avio_seek(s->pb, mov->next_root_atom, SEEK_SET) != mov->next_root_atom) { | |
- av_log(mov->fc, AV_LOG_ERROR, "next root atom offset 0x%"PRIx64": partial file\n", mov->next_root_atom); | |
- return AVERROR_INVALIDDATA; | |
- } | |
- mov->next_root_atom = 0; | |
- if (mov_read_default(mov, s->pb, (MOVAtom){ AV_RL32("root"), INT64_MAX }) < 0 || | |
- avio_feof(s->pb)) | |
- return AVERROR_EOF; | |
- av_log(s, AV_LOG_TRACE, "read fragments, offset 0x%"PRIx64"\n", avio_tell(s->pb)); | |
+ if ((ret = mov_switch_root(s, mov->next_root_atom)) < 0) | |
+ return ret; | |
goto retry; | |
} | |
sc = st->priv_data; | |
@@ -4437,12 +4550,56 @@ 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; | |
+ } | |
+ | |
+ return mov_switch_root(s, offset); | |
+} | |
+ | |
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