mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-03-28 12:32:17 +02:00
lavf/mov: add support for sidx fragment indexes
Fixes trac #3842 (cherry picked from commit 4ab56667594842283dc5ae07f0daba2a2cb4d3af) Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
This commit is contained in:
parent
8d634be4ce
commit
edf5e88eac
@ -103,6 +103,7 @@ typedef struct MOVSbgp {
|
|||||||
typedef struct MOVFragmentIndexItem {
|
typedef struct MOVFragmentIndexItem {
|
||||||
int64_t moof_offset;
|
int64_t moof_offset;
|
||||||
int64_t time;
|
int64_t time;
|
||||||
|
int headers_read;
|
||||||
} MOVFragmentIndexItem;
|
} MOVFragmentIndexItem;
|
||||||
|
|
||||||
typedef struct MOVFragmentIndex {
|
typedef struct MOVFragmentIndex {
|
||||||
@ -197,6 +198,7 @@ typedef struct MOVContext {
|
|||||||
int has_looked_for_mfra;
|
int has_looked_for_mfra;
|
||||||
MOVFragmentIndex** fragment_index_data;
|
MOVFragmentIndex** fragment_index_data;
|
||||||
unsigned fragment_index_count;
|
unsigned fragment_index_count;
|
||||||
|
int fragment_index_complete;
|
||||||
int atom_depth;
|
int atom_depth;
|
||||||
unsigned int aax_mode; ///< 'aax' file has been detected
|
unsigned int aax_mode; ///< 'aax' file has been detected
|
||||||
uint8_t file_key[20];
|
uint8_t file_key[20];
|
||||||
|
@ -3294,7 +3294,7 @@ static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
|
|||||||
MOVFragment *frag = &c->fragment;
|
MOVFragment *frag = &c->fragment;
|
||||||
MOVTrackExt *trex = NULL;
|
MOVTrackExt *trex = NULL;
|
||||||
MOVFragmentIndex* index = NULL;
|
MOVFragmentIndex* index = NULL;
|
||||||
int flags, track_id, i;
|
int flags, track_id, i, found = 0;
|
||||||
|
|
||||||
avio_r8(pb); /* version */
|
avio_r8(pb); /* version */
|
||||||
flags = avio_rb24(pb);
|
flags = avio_rb24(pb);
|
||||||
@ -3312,15 +3312,6 @@ static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
|
|||||||
av_log(c->fc, AV_LOG_ERROR, "could not find corresponding trex\n");
|
av_log(c->fc, AV_LOG_ERROR, "could not find corresponding trex\n");
|
||||||
return AVERROR_INVALIDDATA;
|
return AVERROR_INVALIDDATA;
|
||||||
}
|
}
|
||||||
for (i = 0; i < c->fragment_index_count; i++) {
|
|
||||||
MOVFragmentIndex* candidate = c->fragment_index_data[i];
|
|
||||||
if (candidate->track_id == frag->track_id) {
|
|
||||||
av_log(c->fc, AV_LOG_DEBUG,
|
|
||||||
"found fragment index for track %u\n", frag->track_id);
|
|
||||||
index = candidate;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
frag->base_data_offset = flags & MOV_TFHD_BASE_DATA_OFFSET ?
|
frag->base_data_offset = flags & MOV_TFHD_BASE_DATA_OFFSET ?
|
||||||
avio_rb64(pb) : flags & MOV_TFHD_DEFAULT_BASE_IS_MOOF ?
|
avio_rb64(pb) : flags & MOV_TFHD_DEFAULT_BASE_IS_MOOF ?
|
||||||
@ -3334,23 +3325,32 @@ static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
|
|||||||
frag->flags = flags & MOV_TFHD_DEFAULT_FLAGS ?
|
frag->flags = flags & MOV_TFHD_DEFAULT_FLAGS ?
|
||||||
avio_rb32(pb) : trex->flags;
|
avio_rb32(pb) : trex->flags;
|
||||||
frag->time = AV_NOPTS_VALUE;
|
frag->time = AV_NOPTS_VALUE;
|
||||||
if (index) {
|
for (i = 0; i < c->fragment_index_count; i++) {
|
||||||
int i, found = 0;
|
int j;
|
||||||
for (i = index->current_item; i < index->item_count; i++) {
|
MOVFragmentIndex* candidate = c->fragment_index_data[i];
|
||||||
if (frag->implicit_offset == index->items[i].moof_offset) {
|
if (candidate->track_id == frag->track_id) {
|
||||||
av_log(c->fc, AV_LOG_DEBUG, "found fragment index entry "
|
av_log(c->fc, AV_LOG_DEBUG,
|
||||||
"for track %u and moof_offset %"PRId64"\n",
|
"found fragment index for track %u\n", frag->track_id);
|
||||||
frag->track_id, index->items[i].moof_offset);
|
index = candidate;
|
||||||
frag->time = index->items[i].time;
|
for (j = index->current_item; j < index->item_count; j++) {
|
||||||
index->current_item = i + 1;
|
if (frag->implicit_offset == index->items[j].moof_offset) {
|
||||||
found = 1;
|
av_log(c->fc, AV_LOG_DEBUG, "found fragment index entry "
|
||||||
|
"for track %u and moof_offset %"PRId64"\n",
|
||||||
|
frag->track_id, index->items[j].moof_offset);
|
||||||
|
frag->time = index->items[j].time;
|
||||||
|
index->current_item = j + 1;
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (found)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (!found) {
|
}
|
||||||
av_log(c->fc, AV_LOG_WARNING, "track %u has a fragment index "
|
if (index && !found) {
|
||||||
"but it doesn't have an (in-order) entry for moof_offset "
|
av_log(c->fc, AV_LOG_DEBUG, "track %u has a fragment index but "
|
||||||
"%"PRId64"\n", frag->track_id, frag->implicit_offset);
|
"it doesn't have an (in-order) entry for moof_offset "
|
||||||
}
|
"%"PRId64"\n", frag->track_id, frag->implicit_offset);
|
||||||
}
|
}
|
||||||
av_log(c->fc, AV_LOG_TRACE, "frag flags 0x%x\n", frag->flags);
|
av_log(c->fc, AV_LOG_TRACE, "frag flags 0x%x\n", frag->flags);
|
||||||
return 0;
|
return 0;
|
||||||
@ -3541,7 +3541,106 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
|
|||||||
return AVERROR_EOF;
|
return AVERROR_EOF;
|
||||||
|
|
||||||
frag->implicit_offset = offset;
|
frag->implicit_offset = offset;
|
||||||
st->duration = sc->track_end = dts + sc->time_offset;
|
|
||||||
|
sc->track_end = dts + sc->time_offset;
|
||||||
|
if (st->duration < sc->track_end)
|
||||||
|
st->duration = sc->track_end;
|
||||||
|
|
||||||
|
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;
|
||||||
|
unsigned i, track_id;
|
||||||
|
AVStream *st = NULL;
|
||||||
|
MOVStreamContext *sc;
|
||||||
|
MOVFragmentIndex *index = NULL;
|
||||||
|
MOVFragmentIndex **tmp;
|
||||||
|
AVRational timescale;
|
||||||
|
|
||||||
|
version = avio_r8(pb);
|
||||||
|
if (version > 1) {
|
||||||
|
avpriv_request_sample(c->fc, "sidx version %u", version);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < index->item_count; i++) {
|
||||||
|
uint32_t size = avio_rb32(pb);
|
||||||
|
uint32_t duration = avio_rb32(pb);
|
||||||
|
if (size & 0x80000000) {
|
||||||
|
avpriv_request_sample(c->fc, "sidx reference_type 1");
|
||||||
|
av_freep(&index->items);
|
||||||
|
av_freep(&index);
|
||||||
|
return AVERROR_PATCHWELCOME;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (offset == avio_size(pb))
|
||||||
|
c->fragment_index_complete = 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3799,6 +3898,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = {
|
|||||||
{ MKTAG('a','l','a','c'), mov_read_alac }, /* alac specific atom */
|
{ MKTAG('a','l','a','c'), mov_read_alac }, /* alac specific atom */
|
||||||
{ MKTAG('a','v','c','C'), mov_read_glbl },
|
{ MKTAG('a','v','c','C'), mov_read_glbl },
|
||||||
{ MKTAG('p','a','s','p'), mov_read_pasp },
|
{ 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','b','l'), mov_read_default },
|
||||||
{ MKTAG('s','t','c','o'), mov_read_stco },
|
{ MKTAG('s','t','c','o'), mov_read_stco },
|
||||||
{ MKTAG('s','t','p','s'), mov_read_stps },
|
{ MKTAG('s','t','p','s'), mov_read_stps },
|
||||||
@ -3922,9 +4022,9 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
if (c->found_moov && c->found_mdat &&
|
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_complete) ||
|
||||||
start_pos + a.size == avio_size(pb))) {
|
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_complete)
|
||||||
c->next_root_atom = start_pos + a.size;
|
c->next_root_atom = start_pos + a.size;
|
||||||
c->atom_depth --;
|
c->atom_depth --;
|
||||||
return 0;
|
return 0;
|
||||||
@ -4529,6 +4629,52 @@ static int should_retry(AVIOContext *pb, int error_code) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mov_switch_root(AVFormatContext *s, int64_t target)
|
||||||
|
{
|
||||||
|
MOVContext *mov = s->priv_data;
|
||||||
|
int i, j;
|
||||||
|
int already_read = 0;
|
||||||
|
|
||||||
|
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->next_root_atom = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < mov->fragment_index_count; i++) {
|
||||||
|
MOVFragmentIndex *index = mov->fragment_index_data[i];
|
||||||
|
int found = 0;
|
||||||
|
for (j = 0; j < index->item_count; j++) {
|
||||||
|
MOVFragmentIndexItem *item = &index->items[j];
|
||||||
|
if (found) {
|
||||||
|
mov->next_root_atom = item->moof_offset;
|
||||||
|
break; // Advance to next index in outer loop
|
||||||
|
} else if (item->moof_offset == target) {
|
||||||
|
index->current_item = FFMIN(j, index->current_item);
|
||||||
|
if (item->headers_read)
|
||||||
|
already_read = 1;
|
||||||
|
item->headers_read = 1;
|
||||||
|
found = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
index->current_item = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (already_read)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
mov->found_mdat = 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));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
|
static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
|
||||||
{
|
{
|
||||||
MOVContext *mov = s->priv_data;
|
MOVContext *mov = s->priv_data;
|
||||||
@ -4539,19 +4685,11 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
mov->fc = s;
|
mov->fc = s;
|
||||||
retry:
|
retry:
|
||||||
sample = mov_find_next_sample(s, &st);
|
sample = mov_find_next_sample(s, &st);
|
||||||
if (!sample) {
|
if (!sample || (mov->next_root_atom && sample->pos > mov->next_root_atom)) {
|
||||||
mov->found_mdat = 0;
|
|
||||||
if (!mov->next_root_atom)
|
if (!mov->next_root_atom)
|
||||||
return AVERROR_EOF;
|
return AVERROR_EOF;
|
||||||
if (avio_seek(s->pb, mov->next_root_atom, SEEK_SET) != mov->next_root_atom) {
|
if ((ret = mov_switch_root(s, mov->next_root_atom)) < 0)
|
||||||
av_log(mov->fc, AV_LOG_ERROR, "next root atom offset 0x%"PRIx64": partial file\n", mov->next_root_atom);
|
return ret;
|
||||||
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));
|
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
sc = st->priv_data;
|
sc = st->priv_data;
|
||||||
@ -4629,12 +4767,41 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mov_seek_fragment(AVFormatContext *s, AVStream *st, int64_t timestamp)
|
||||||
|
{
|
||||||
|
MOVContext *mov = s->priv_data;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
if (!mov->fragment_index_complete)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < mov->fragment_index_count; i++) {
|
||||||
|
if (mov->fragment_index_data[i]->track_id == st->id) {
|
||||||
|
MOVFragmentIndex *index = index = mov->fragment_index_data[i];
|
||||||
|
for (j = index->item_count - 1; j >= 0; j--) {
|
||||||
|
if (index->items[j].time <= timestamp) {
|
||||||
|
if (index->items[j].headers_read)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return mov_switch_root(s, index->items[j].moof_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp, int flags)
|
static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp, int flags)
|
||||||
{
|
{
|
||||||
MOVStreamContext *sc = st->priv_data;
|
MOVStreamContext *sc = st->priv_data;
|
||||||
int sample, time_sample;
|
int sample, time_sample;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
int ret = mov_seek_fragment(s, st, timestamp);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
sample = av_index_search_timestamp(st, timestamp, flags);
|
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);
|
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)
|
if (sample < 0 && st->nb_index_entries && timestamp < st->index_entries[0].timestamp)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user