Merge commit '76729970049fe95659346503f7401a5d869f9959'
authorMatthieu Bouron <matthieu.bouron@stupeflix.com>
Wed, 13 Jul 2016 14:34:39 +0000 (16:34 +0200)
committerMatthieu Bouron <matthieu.bouron@stupeflix.com>
Wed, 13 Jul 2016 14:34:54 +0000 (16:34 +0200)
* commit '76729970049fe95659346503f7401a5d869f9959':
  mov: Implement support for multiple sample description tables

Notes:
  * The sc->stsc_data[index].id checks have been moved from the mov_read_stsc
  to mov_read_packet before the value is used in mov_change_extradata to
  not break playback of samples with broken stsc entries (see sample of
  ticket #1918).

  * sc->stsc_index is now checked against sc->stsc_count - 1 before it
  is incremented so it remains lesser than sc->stsc_count. Fixes a crash
  with:

  ./ffmpeg -i matrixbench_mpeg2.mpg -t 1 -frag_duration 200k test.mov
  ./ffprobe -show_packets test.mov

Merged-by: Matthieu Bouron <matthieu.bouron@stupeflix.com>
1  2 
libavformat/isom.h
libavformat/mov.c

@@@ -128,14 -105,13 +128,16 @@@ typedef struct MOVStreamContext 
      MOVStts *ctts_data;
      unsigned int stsc_count;
      MOVStsc *stsc_data;
+     int stsc_index;
+     int stsc_sample;
      unsigned int stps_count;
      unsigned *stps_data;  ///< partial sync sample for mpeg-2 open gop
 +    MOVElst *elst_data;
 +    unsigned int elst_count;
      int ctts_index;
      int ctts_sample;
 -    unsigned int sample_size;
 +    unsigned int sample_size; ///< may contain value calculated from stsd or value from stsz atom
 +    unsigned int stsz_sample_size; ///< always contains sample size from stsz atom
      unsigned int sample_count;
      int *sample_sizes;
      int keyframe_absent;
      unsigned int rap_group_count;
      MOVSbgp *rap_group;
  
 +    int nb_frames_for_fps;
 +    int64_t duration_for_fps;
 +
+     /** extradata array (and size) for multiple stsd */
+     uint8_t **extradata;
+     int *extradata_size;
+     int last_stsd_index;
+     int stsd_count;
      int32_t *display_matrix;
 +    uint32_t format;
 +
 +    struct {
 +        int use_subsamples;
 +        uint8_t* auxiliary_info;
 +        uint8_t* auxiliary_info_end;
 +        uint8_t* auxiliary_info_pos;
 +        uint8_t auxiliary_info_default_size;
 +        uint8_t* auxiliary_info_sizes;
 +        size_t auxiliary_info_sizes_count;
 +        struct AVAESCTR* aes_ctr;
 +    } cenc;
  } MOVStreamContext;
  
  typedef struct MOVContext {
@@@ -2215,11 -1784,6 +2215,10 @@@ static int mov_skip_multiple_stsd(MOVCo
          avio_skip(pb, size);
          return 1;
      }
-     if ( codec_tag == AV_RL32("avc1") ||
-          codec_tag == AV_RL32("hvc1") ||
++    if ( codec_tag == AV_RL32("hvc1") ||
 +         codec_tag == AV_RL32("hev1")
 +    )
 +        av_log(c->fc, AV_LOG_WARNING, "Concatenated H.264 or H.265 might not play correctly.\n");
  
      return 0;
  }
@@@ -3201,18 -2615,13 +3254,17 @@@ static int mov_read_trak(MOVContext *c
          st->codecpar->width = 0; /* let decoder init width/height */
          st->codecpar->height= 0;
          break;
 -    case AV_CODEC_ID_MP3:
 -        st->need_parsing = AVSTREAM_PARSE_FULL;
 -        break;
      }
  
 +    // If the duration of the mp3 packets is not constant, then they could need a parser
 +    if (st->codecpar->codec_id == AV_CODEC_ID_MP3
 +        && sc->stts_count > 3
 +        && sc->stts_count*10 > st->nb_frames
 +        && sc->time_scale == st->codecpar->sample_rate) {
 +            st->need_parsing = AVSTREAM_PARSE_FULL;
 +    }
      /* Do not need those anymore. */
      av_freep(&sc->chunk_offsets);
-     av_freep(&sc->stsc_data);
      av_freep(&sc->sample_sizes);
      av_freep(&sc->keyframes);
      av_freep(&sc->stts_data);
@@@ -4773,9 -3431,10 +4825,14 @@@ static int mov_read_close(AVFormatConte
          av_freep(&sc->rap_group);
          av_freep(&sc->display_matrix);
  
+         for (j = 0; j < sc->stsd_count; j++)
+             av_free(sc->extradata[j]);
+         av_freep(&sc->extradata);
+         av_freep(&sc->extradata_size);
++
 +        av_freep(&sc->cenc.auxiliary_info);
 +        av_freep(&sc->cenc.auxiliary_info_sizes);
 +        av_aes_ctr_free(sc->cenc.aes_ctr);
      }
  
      if (mov->dv_demux) {
@@@ -5137,59 -3566,29 +5194,82 @@@ static AVIndexEntry *mov_find_next_samp
      return sample;
  }
  
 +static int should_retry(AVIOContext *pb, int error_code) {
 +    if (error_code == AVERROR_EOF || avio_feof(pb))
 +        return 0;
 +
 +    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_change_extradata(MOVStreamContext *sc, AVPacket *pkt)
+ {
+     uint8_t *side, *extradata;
+     int extradata_size;
+     /* Save the current index. */
+     sc->last_stsd_index = sc->stsc_data[sc->stsc_index].id - 1;
+     /* Notify the decoder that extradata changed. */
+     extradata_size = sc->extradata_size[sc->last_stsd_index];
+     extradata = sc->extradata[sc->last_stsd_index];
+     if (extradata_size > 0 && extradata) {
+         side = av_packet_new_side_data(pkt,
+                                        AV_PKT_DATA_NEW_EXTRADATA,
+                                        extradata_size);
+         if (!side)
+             return AVERROR(ENOMEM);
+         memcpy(side, extradata, extradata_size);
+     }
+     return 0;
+ }
  static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
  {
      MOVContext *mov = s->priv_data;
          goto retry;
      pkt->flags |= sample->flags & AVINDEX_KEYFRAME ? AV_PKT_FLAG_KEY : 0;
      pkt->pos = sample->pos;
 -    av_log(s, AV_LOG_TRACE, "stream %d, pts %"PRId64", dts %"PRId64", pos 0x%"PRIx64", duration %"PRId64"\n",
 -            pkt->stream_index, pkt->pts, pkt->dts, pkt->pos, pkt->duration);
  
 -        if (sc->stsc_index < sc->stsc_count &&
+     /* Multiple stsd handling. */
+     if (sc->stsc_data) {
+         /* Keep track of the stsc index for the given sample, then check
+         * if the stsd index is different from the last used one. */
+         sc->stsc_sample++;
 -        } else if (sc->stsc_data[sc->stsc_index].id - 1 != sc->last_stsd_index) {
++        if (sc->stsc_index < sc->stsc_count - 1 &&
+             mov_get_stsc_samples(sc, sc->stsc_index) == sc->stsc_sample) {
+             sc->stsc_index++;
+             sc->stsc_sample = 0;
+         /* Do not check indexes after a switch. */
++        } else if (sc->stsc_data[sc->stsc_index].id > 0 &&
++                   sc->stsc_data[sc->stsc_index].id - 1 < sc->stsd_count &&
++                   sc->stsc_data[sc->stsc_index].id - 1 != sc->last_stsd_index) {
+             ret = mov_change_extradata(sc, pkt);
+             if (ret < 0)
+                 return ret;
+         }
+     }
 +    if (mov->aax_mode)
 +        aax_filter(pkt->data, pkt->size, mov);
 +
 +    if (sc->cenc.aes_ctr) {
 +        ret = cenc_filter(mov, sc, pkt->data, pkt->size);
 +        if (ret) {
 +            return ret;
 +        }
 +    }
 +
 +    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 = 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;
  }
  
@@@ -5352,11 -3720,18 +5451,23 @@@ static int mov_seek_stream(AVFormatCont
          }
      }
  
+     /* adjust stsd index */
+     time_sample = 0;
+     for (i = 0; i < sc->stsc_count; i++) {
+         int next = time_sample + mov_get_stsc_samples(sc, i);
+         if (next > sc->current_sample) {
+             sc->stsc_index = i;
+             sc->stsc_sample = sc->current_sample - time_sample;
+             break;
+         }
+         time_sample = next;
+     }
 +    ret = mov_seek_auxiliary_info(s, sc);
 +    if (ret < 0) {
 +        return ret;
 +    }
 +
      return sample;
  }