[FFmpeg-devel] mov: support for multiple edits and cenc decryption

Sasi Inguva isasi at google.com
Wed Jan 18 03:30:17 EET 2017


Can you add a fate test ? Rest looks fine to me.

>From 268c50f156f53c09692b3413c2ed1eb9bd29f163 Mon Sep 17 00:00:00 2001
> From: erankor <eran.kornblau at kaltura.com>
> Date: Thu, 12 Jan 2017 19:01:13 +0200
> Subject: [PATCH] mov: fix decryption with edit list
> Retain the ranges of frame indexes when applying edit list in
> mov_fix_index. The index ranges are then used to keep track of the frame
> index of the current sample. In case of a discontinuity in frame indexes
> due to edit, update the auxiliary info position accordingly.
> ---
>  libavformat/isom.h |   9 +++
>  libavformat/mov.c  | 169
> +++++++++++++++++++++++++++++++++++++++--------------
>  2 files changed, 134 insertions(+), 44 deletions(-)
> diff --git a/libavformat/isom.h b/libavformat/isom.h
> index 12cefc9..abcacab 100644
> --- a/libavformat/isom.h
> +++ b/libavformat/isom.h
> @@ -121,6 +121,11 @@ typedef struct MOVFragmentIndex {
>      MOVFragmentIndexItem *items;
>  } MOVFragmentIndex;
>
> +typedef struct MOVIndexRange {
> +    int64_t start;
> +    int64_t end;
> +} MOVIndexRange;
> +
>  typedef struct MOVStreamContext {
>      AVIOContext *pb;
>      int pb_is_copied;
> @@ -152,6 +157,9 @@ typedef struct MOVStreamContext {
>      int time_scale;
>      int64_t time_offset;  ///< time offset of the edit list entries
>      int current_sample;
> +    int64_t current_index;
> +    MOVIndexRange* index_ranges;
> +    MOVIndexRange* current_index_range;
>      unsigned int bytes_per_frame;
>      unsigned int samples_per_frame;
>      int dv_audio_container;
> @@ -198,6 +206,7 @@ typedef struct MOVStreamContext {
>          uint8_t auxiliary_info_default_size;
>          uint8_t* auxiliary_info_sizes;
>          size_t auxiliary_info_sizes_count;
> +        int64_t auxiliary_info_index;
>          struct AVAESCTR* aes_ctr;
>      } cenc;
>  } MOVStreamContext;
> diff --git a/libavformat/mov.c b/libavformat/mov.c
> index d1b9291..c9f37c5 100644
> --- a/libavformat/mov.c
> +++ b/libavformat/mov.c
> @@ -2947,6 +2947,52 @@ static int64_t add_ctts_entry(MOVStts** ctts_data,
> unsigned int* ctts_count, uns
>      return *ctts_count;
>  }
>
> +static void mov_current_sample_inc(MOVStreamContext *sc)
> +{
> +    sc->current_sample++;
> +    sc->current_index++;
> +    if (sc->index_ranges &&
> +        sc->current_index >= sc->current_index_range->end &&
> +        sc->current_index_range->end) {
> +        sc->current_index_range++;
> +        sc->current_index = sc->current_index_range->start;
> +    }
> +}
> +
> +static void mov_current_sample_dec(MOVStreamContext *sc)
> +{
> +    sc->current_sample--;
> +    sc->current_index--;
> +    if (sc->index_ranges &&
> +        sc->current_index < sc->current_index_range->start &&
> +        sc->current_index_range > sc->index_ranges) {
> +        sc->current_index_range--;
> +        sc->current_index = sc->current_index_range->end - 1;
> +    }
> +}
> +
> +static void mov_current_sample_set(MOVStreamContext *sc, int
> current_sample)
> +{
> +    int64_t range_size;
> +
> +    sc->current_sample = current_sample;
> +    sc->current_index = current_sample;
> +    if (!sc->index_ranges) {
> +        return;
> +    }
> +
> +    for (sc->current_index_range = sc->index_ranges;
> +        sc->current_index_range->end;
> +        sc->current_index_range++) {
> +        range_size = sc->current_index_range->end -
> sc->current_index_range->start;
> +        if (range_size > current_sample) {
> +            sc->current_index = sc->current_index_range->start +
> current_sample;
> +            break;
> +        }
> +        current_sample -= range_size;
> +    }
> +}
> +
>  /**
>   * Fix st->index_entries, so that it contains only the entries (and the
> entries
>   * which are needed to decode them) that fall in the edit list time
> ranges.
> @@ -2984,10 +3030,21 @@ static void mov_fix_index(MOVContext *mov,
> AVStream *st)
>      int num_discarded_begin = 0;
>      int first_non_zero_audio_edit = -1;
>      int packet_skip_samples = 0;
> +    MOVIndexRange *current_index_range;
>
>      if (!msc->elst_data || msc->elst_count <= 0 || nb_old <= 0) {
>          return;
>      }
> +
> +    // allocate the index ranges array
> +    msc->index_ranges = av_malloc((msc->elst_count + 1) *
> sizeof(msc->index_ranges[0]));
>
I don't see index_ranges allocated anywhere else. This will cause segfault ?

+    if (!msc->index_ranges) {
> +        av_log(mov->fc, AV_LOG_ERROR, "Cannot allocate index ranges
> buffer\n");
> +        return;
> +    }
> +    msc->current_index_range = msc->index_ranges;
> +    current_index_range = msc->index_ranges - 1;
> +
>      // Clean AVStream from traces of old index
>      st->index_entries = NULL;
>      st->index_entries_allocated_size = 0;
> @@ -3182,6 +3239,13 @@ static void mov_fix_index(MOVContext *mov, AVStream
> *st)
>                  break;
>              }
>
> +            // Update the index ranges array
> +            if (current_index_range < msc->index_ranges || index !=
> current_index_range->end) {
> +                current_index_range++;
> +                current_index_range->start = index;
> +            }
> +            current_index_range->end = index + 1;
> +
>
>            // Only start incrementing DTS in frame_duration amounts, when
> we encounter a frame in edit list.
>              if (edit_list_start_encountered > 0) {
>                  edit_list_dts_counter = edit_list_dts_counter +
> frame_duration;
> @@ -3212,6 +3276,12 @@ static void mov_fix_index(MOVContext *mov, AVStream
> *st)
>      // Free the old index and the old CTTS structures
>      av_free(e_old);
>      av_free(ctts_data_old);
> +
> +    // Null terminate the index ranges array
> +    current_index_range++;
> +    current_index_range->start = 0;
> +    current_index_range->end = 0;
> +    msc->current_index = msc->index_ranges[0].start;
>  }
>
>  static void mov_build_index(MOVContext *mov, AVStream *st)
> @@ -4908,8 +4978,8 @@ static int mov_read_senc(MOVContext *c, AVIOContext
> *pb, MOVAtom atom)
>      }
>
>      sc->cenc.auxiliary_info_end = sc->cenc.auxiliary_info +
> auxiliary_info_size;
> -
>      sc->cenc.auxiliary_info_pos = sc->cenc.auxiliary_info;
> +    sc->cenc.auxiliary_info_index = 0;
>
>      if (avio_read(pb, sc->cenc.auxiliary_info, auxiliary_info_size) !=
> auxiliary_info_size) {
>          av_log(c->fc, AV_LOG_ERROR, "failed to read the auxiliary info");
> @@ -5018,12 +5088,50 @@ static int mov_read_dfla(MOVContext *c,
> AVIOContext *pb, MOVAtom atom)
>      return 0;
>  }
>
> -static int cenc_filter(MOVContext *c, MOVStreamContext *sc, uint8_t
> *input, int size)
> +static int mov_seek_auxiliary_info(MOVContext *c, MOVStreamContext *sc,
> int64_t index)
> +{
> +    size_t auxiliary_info_seek_offset = 0;
> +    int i;
> +
> +    if (sc->cenc.auxiliary_info_default_size) {
> +        auxiliary_info_seek_offset =
> (size_t)sc->cenc.auxiliary_info_default_size * index;
> +    } else if (sc->cenc.auxiliary_info_sizes) {
> +        if (index > sc->cenc.auxiliary_info_sizes_count) {
> +            av_log(c, AV_LOG_ERROR, "current sample %"PRId64" greater
> than the number of auxiliary info sample sizes %"SIZE_SPECIFIER"\n",
> +                index, sc->cenc.auxiliary_info_sizes_count);
> +            return AVERROR_INVALIDDATA;
> +        }
> +
> +        for (i = 0; i < index; i++) {
> +            auxiliary_info_seek_offset +=
> sc->cenc.auxiliary_info_sizes[i];
> +        }
> +    }
> +
> +    if (auxiliary_info_seek_offset > sc->cenc.auxiliary_info_end -
> sc->cenc.auxiliary_info) {
> +        av_log(c, AV_LOG_ERROR, "auxiliary info offset %"SIZE_SPECIFIER"
> greater than auxiliary info size %"SIZE_SPECIFIER"\n",
> +            auxiliary_info_seek_offset,
> (size_t)(sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info));
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    sc->cenc.auxiliary_info_pos = sc->cenc.auxiliary_info +
> auxiliary_info_seek_offset;
> +    sc->cenc.auxiliary_info_index = index;
> +    return 0;
> +}
> +
> +static int cenc_filter(MOVContext *c, MOVStreamContext *sc, int64_t
> index, uint8_t *input, int size)
>  {
>      uint32_t encrypted_bytes;
>      uint16_t subsample_count;
>      uint16_t clear_bytes;
>      uint8_t* input_end = input + size;
> +    int ret;
> +
> +    if (index != sc->cenc.auxiliary_info_index) {
> +        ret = mov_seek_auxiliary_info(c, sc, index);
> +        if (ret < 0) {
> +            return ret;
> +        }
> +    }
>
>      /* read the iv */
>      if (AES_CTR_IV_SIZE > sc->cenc.auxiliary_info_end -
> sc->cenc.auxiliary_info_pos) {
> @@ -5081,36 +5189,7 @@ static int cenc_filter(MOVContext *c,
> MOVStreamContext *sc, uint8_t *input, int
>          return AVERROR_INVALIDDATA;
>      }
>
> -    return 0;
> -}
> -
> -static int mov_seek_auxiliary_info(AVFormatContext *s, MOVStreamContext
> *sc)
> -{
> -    size_t auxiliary_info_seek_offset = 0;
> -    int i;
> -
> -    if (sc->cenc.auxiliary_info_default_size) {
> -        auxiliary_info_seek_offset =
> (size_t)sc->cenc.auxiliary_info_default_size * sc->current_sample;
> -    } else if (sc->cenc.auxiliary_info_sizes) {
> -        if (sc->current_sample > sc->cenc.auxiliary_info_sizes_count) {
> -            av_log(s, AV_LOG_ERROR, "current sample %d greater than the
> number of auxiliary info sample sizes %"SIZE_SPECIFIER"\n",
> -                sc->current_sample, sc->cenc.auxiliary_info_sizes_count);
> -            return AVERROR_INVALIDDATA;
> -        }
> -
> -        for (i = 0; i < sc->current_sample; i++) {
> -            auxiliary_info_seek_offset +=
> sc->cenc.auxiliary_info_sizes[i];
> -        }
> -    }
> -
> -    if (auxiliary_info_seek_offset > sc->cenc.auxiliary_info_end -
> sc->cenc.auxiliary_info) {
> -        av_log(s, AV_LOG_ERROR, "auxiliary info offset %"SIZE_SPECIFIER"
> greater than auxiliary info size %"SIZE_SPECIFIER"\n",
> -            auxiliary_info_seek_offset,
> (size_t)(sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info));
> -        return AVERROR_INVALIDDATA;
> -    }
> -
> -    sc->cenc.auxiliary_info_pos = sc->cenc.auxiliary_info +
> auxiliary_info_seek_offset;
> -
> +    sc->cenc.auxiliary_info_index++;
>      return 0;
>  }
>
> @@ -5605,6 +5684,7 @@ static int mov_read_close(AVFormatContext *s)
>          av_freep(&sc->elst_data);
>          av_freep(&sc->rap_group);
>          av_freep(&sc->display_matrix);
> +        av_freep(&sc->index_ranges);
>
>          if (sc->extradata)
>              for (j = 0; j < sc->stsd_count; j++)
> @@ -6083,6 +6163,7 @@ static int mov_read_packet(AVFormatContext *s,
> AVPacket *pkt)
>      MOVStreamContext *sc;
>      AVIndexEntry *sample;
>      AVStream *st = NULL;
> +    int64_t current_index;
>      int ret;
>      mov->fc = s;
>   retry:
> @@ -6096,7 +6177,8 @@ static int mov_read_packet(AVFormatContext *s,
> AVPacket *pkt)
>      }
>      sc = st->priv_data;
>      /* must be done just before reading, to avoid infinite loop on sample
> */
> -    sc->current_sample++;
> +    current_index = sc->current_index;
> +    mov_current_sample_inc(sc);
>
>      if (mov->next_root_atom) {
>          sample->pos = FFMIN(sample->pos, mov->next_root_atom);
> @@ -6108,7 +6190,9 @@ static int mov_read_packet(AVFormatContext *s,
> AVPacket *pkt)
>          if (ret64 != sample->pos) {
>              av_log(mov->fc, AV_LOG_ERROR, "stream %d, offset 0x%"PRIx64":
> partial file\n",
>                     sc->ffindex, sample->pos);
> -            sc->current_sample -= should_retry(sc->pb, ret64);
> +            if (should_retry(sc->pb, ret64)) {
> +                mov_current_sample_dec(sc);
> +            }
>              return AVERROR_INVALIDDATA;
>          }
>
> @@ -6119,7 +6203,9 @@ static int mov_read_packet(AVFormatContext *s,
> AVPacket *pkt)
>
>          ret = av_get_packet(sc->pb, pkt, sample->size);
>          if (ret < 0) {
> -            sc->current_sample -= should_retry(sc->pb, ret);
> +            if (should_retry(sc->pb, ret)) {
> +                mov_current_sample_dec(sc);
> +            }
>              return ret;
>          }
>          if (sc->has_palette) {
> @@ -6197,7 +6283,7 @@ static int mov_read_packet(AVFormatContext *s,
> AVPacket *pkt)
>          aax_filter(pkt->data, pkt->size, mov);
>
>      if (sc->cenc.aes_ctr) {
> -        ret = cenc_filter(mov, sc, pkt->data, pkt->size);
> +        ret = cenc_filter(mov, sc, current_index, pkt->data, pkt->size);
>          if (ret) {
>              return ret;
>          }
> @@ -6248,7 +6334,7 @@ static int mov_seek_stream(AVFormatContext *s,
> AVStream *st, int64_t timestamp,
>          sample = 0;
>      if (sample < 0) /* not sure what to do */
>          return AVERROR_INVALIDDATA;
> -    sc->current_sample = sample;
> +    mov_current_sample_set(sc, sample);
>      av_log(s, AV_LOG_TRACE, "stream %d, found sample %d\n", st->index,
> sc->current_sample);
>      /* adjust ctts index */
>      if (sc->ctts_data) {
> @@ -6276,11 +6362,6 @@ static int mov_seek_stream(AVFormatContext *s,
> AVStream *st, int64_t timestamp,
>          time_sample = next;
>      }
>
> -    ret = mov_seek_auxiliary_info(s, sc);
> -    if (ret < 0) {
> -        return ret;
> -    }
> -
>      return sample;
>  }
>
> @@ -6320,7 +6401,7 @@ static int mov_read_seek(AVFormatContext *s, int
> stream_index, int64_t sample_ti
>              MOVStreamContext *sc;
>              st = s->streams[i];
>              sc = st->priv_data;
> -            sc->current_sample = 0;
> +            mov_current_sample_set(sc, 0);
>          }
>          while (1) {
>              MOVStreamContext *sc;
> @@ -6330,7 +6411,7 @@ static int mov_read_seek(AVFormatContext *s, int
> stream_index, int64_t sample_ti
>              sc = st->priv_data;
>              if (sc->ffindex == stream_index && sc->current_sample ==
> sample)
>                  break;
> -            sc->current_sample++;
> +            mov_current_sample_inc(sc);
>          }
>      }
>      return 0;
> --
> 2.9.2.windows.1
>
> On Thu, Jan 12, 2017 at 9:23 AM, Eran Kornblau <eran.kornblau at kaltura.com>
> wrote:
> > From: ffmpeg-devel [mailto:ffmpeg-devel-bounces at ffmpeg.org] On Behalf
> Of Sasi Inguva
> > Sent: Friday, January 6, 2017 8:25 AM
> > To: FFmpeg development discussions and patches <ffmpeg-devel at ffmpeg.org>
> > Subject: Re: [FFmpeg-devel] mov: support for multiple edits and cenc
> decryption
> >
> > I agree . #4 makes the most sense.
> >
> >
> Patch attached.
> A couple of notes about the implementation:
> 1. I added functions for updating MOVStreamContext::current_sample - inc
> / dec / set, all changes
> to this field are done using these functions.
> In addition to applying the change to current_sample, these functions keep
> track of the frame index
> in a new field called current_index.
> 2. mov_fix_index initializes a new array 'index_ranges' with the start/end
> values of each edit.
> The array is null terminated (end = 0).
> 3. A new field called auxiliary_info_index keeps track of the index of the
> frame whose auxiliary info
> matches auxiliary_info_pos - these 2 fields are always updated together.
> 4. In cenc_filter, if the auxiliary info index does not match the current
> frame index, the auxiliary info
> position is recalculated, using the code that was added previously for
> supporting seek.
> Thanks
> Eran
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> -----Original Message-----
>


More information about the ffmpeg-devel mailing list