[FFmpeg-devel] [PATCH] lavf/mov: add support for sidx fragment indexes

wm4 nfxjfg at googlemail.com
Mon Oct 5 16:27:03 CEST 2015


On Sat,  3 Oct 2015 11:57:03 -0500
Rodger Combs <rodger.combs at gmail.com> wrote:

> Fixes trac #3842
> ---
>  libavformat/isom.h |   2 +
>  libavformat/mov.c  | 250 ++++++++++++++++++++++++++++++++++++++++++++---------
>  2 files changed, 213 insertions(+), 39 deletions(-)
> 
> diff --git a/libavformat/isom.h b/libavformat/isom.h
> index aee9d6e..6e921c0 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 {
> @@ -197,6 +198,7 @@ typedef struct MOVContext {
>      int has_looked_for_mfra;
>      MOVFragmentIndex** fragment_index_data;
>      unsigned fragment_index_count;
> +    int fragment_index_complete;
>      int atom_depth;
>      unsigned int aax_mode;  ///< 'aax' file has been detected
>      uint8_t file_key[20];
> diff --git a/libavformat/mov.c b/libavformat/mov.c
> index da170a6..5aa7491 100644
> --- a/libavformat/mov.c
> +++ b/libavformat/mov.c
> @@ -3349,7 +3349,7 @@ static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
>      MOVFragment *frag = &c->fragment;
>      MOVTrackExt *trex = NULL;
>      MOVFragmentIndex* index = NULL;
> -    int flags, track_id, i;
> +    int flags, track_id, i, found = 0;
>  
>      avio_r8(pb); /* version */
>      flags = avio_rb24(pb);
> @@ -3367,15 +3367,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");
>          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 ?
>                               avio_rb64(pb) : flags & MOV_TFHD_DEFAULT_BASE_IS_MOOF ?
> @@ -3389,23 +3380,32 @@ static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
>      frag->flags    = flags & MOV_TFHD_DEFAULT_FLAGS ?
>                       avio_rb32(pb) : trex->flags;
>      frag->time     = AV_NOPTS_VALUE;
> -    if (index) {
> -        int i, found = 0;
> -        for (i = index->current_item; i < index->item_count; i++) {
> -            if (frag->implicit_offset == index->items[i].moof_offset) {
> -                av_log(c->fc, AV_LOG_DEBUG, "found fragment index entry "
> -                        "for track %u and moof_offset %"PRId64"\n",
> -                        frag->track_id, index->items[i].moof_offset);
> -                frag->time = index->items[i].time;
> -                index->current_item = i + 1;
> -                found = 1;
> +    for (i = 0; i < c->fragment_index_count; i++) {
> +        int j;
> +        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;
> +            for (j = index->current_item; j < index->item_count; j++) {
> +                if (frag->implicit_offset == index->items[j].moof_offset) {
> +                    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 "
> -                   "but it doesn't have an (in-order) entry for moof_offset "
> -                   "%"PRId64"\n", frag->track_id, frag->implicit_offset);
> -        }
> +    }
> +    if (index && !found) {
> +        av_log(c->fc, AV_LOG_DEBUG, "track %u has a fragment index but "
> +               "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);
>      return 0;
> @@ -3596,7 +3596,99 @@ 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;
> +
> +    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;
> +    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++) {
> +        uint32_t size = avio_rb32(pb) & 0x7FFFFFFF;
> +        uint32_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;
> +
> +    if (offset == avio_size(pb))
> +        c->fragment_index_complete = 1;
> +
>      return 0;
>  }
>  
> @@ -3854,6 +3946,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 },
> @@ -3978,9 +4071,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_complete) ||
>                   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->atom_depth --;
>                  return 0;
> @@ -4585,6 +4678,52 @@ 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;
> +    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)
>  {
>      MOVContext *mov = s->priv_data;
> @@ -4595,19 +4734,11 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
>      mov->fc = s;
>   retry:
>      sample = mov_find_next_sample(s, &st);
> -    if (!sample) {
> -        mov->found_mdat = 0;
> +    if (!sample || (mov->next_root_atom && sample->pos > mov->next_root_atom)) {
>          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;
> @@ -4685,12 +4816,53 @@ 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->id) {
> +            index = mov->fragment_index_data[i];
> +            break;
> +        }
> +    }
> +    if (!index)
> +        return 0;
> +
> +    if (mov->fragment_index_complete) {
> +        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;
> +
> +    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)

What's the status of this? Can it be merged?


More information about the ffmpeg-devel mailing list