diff mbox series

[FFmpeg-devel,2/3] libavformat/hls: add support for decryption of HLS streams in MPEG-TS format protected using SAMPLE-AES encryption

Message ID 20210224111321.9704-1-nachiket.programmer@gmail.com
State New
Headers show
Series [FFmpeg-devel,1/3] libavcodec/adts_header: add frame_length field and avpriv function to parse AAC ADTS header | expand

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

Nachiket Tarate Feb. 24, 2021, 11:13 a.m. UTC
Apple HTTP Live Streaming Sample Encryption:

https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption

Signed-off-by: Nachiket Tarate <nachiket.programmer@gmail.com>
---
 libavformat/Makefile         |   2 +-
 libavformat/hls.c            | 105 ++++++++--
 libavformat/hls_sample_aes.c | 391 +++++++++++++++++++++++++++++++++++
 libavformat/hls_sample_aes.h |  66 ++++++
 libavformat/mpegts.c         |  12 ++
 5 files changed, 562 insertions(+), 14 deletions(-)
 create mode 100644 libavformat/hls_sample_aes.c
 create mode 100644 libavformat/hls_sample_aes.h

Comments

Nachiket Tarate March 1, 2021, 4:35 a.m. UTC | #1
@Steven Liu <lq@chinaffmpeg.org>

Can we merge this patch ?

Best Regards,
Nachiket Tarate

On Wed, Feb 24, 2021 at 4:44 PM Nachiket Tarate <
nachiket.programmer@gmail.com> wrote:

> Apple HTTP Live Streaming Sample Encryption:
>
>
> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
>
> Signed-off-by: Nachiket Tarate <nachiket.programmer@gmail.com>
> ---
>  libavformat/Makefile         |   2 +-
>  libavformat/hls.c            | 105 ++++++++--
>  libavformat/hls_sample_aes.c | 391 +++++++++++++++++++++++++++++++++++
>  libavformat/hls_sample_aes.h |  66 ++++++
>  libavformat/mpegts.c         |  12 ++
>  5 files changed, 562 insertions(+), 14 deletions(-)
>  create mode 100644 libavformat/hls_sample_aes.c
>  create mode 100644 libavformat/hls_sample_aes.h
>
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index fcb39ce133..62627d50a6 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -236,7 +236,7 @@ OBJS-$(CONFIG_HCOM_DEMUXER)              += hcom.o
> pcm.o
>  OBJS-$(CONFIG_HDS_MUXER)                 += hdsenc.o
>  OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
>  OBJS-$(CONFIG_HEVC_MUXER)                += rawenc.o
> -OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o
> +OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o hls_sample_aes.o
>  OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o hlsplaylist.o avc.o
>  OBJS-$(CONFIG_HNM_DEMUXER)               += hnm.o
>  OBJS-$(CONFIG_ICO_DEMUXER)               += icodec.o
> diff --git a/libavformat/hls.c b/libavformat/hls.c
> index af2468ad9b..3cb3853c79 100644
> --- a/libavformat/hls.c
> +++ b/libavformat/hls.c
> @@ -2,6 +2,7 @@
>   * Apple HTTP Live Streaming demuxer
>   * Copyright (c) 2010 Martin Storsjo
>   * Copyright (c) 2013 Anssi Hannula
> + * Copyright (c) 2021 Nachiket Tarate
>   *
>   * This file is part of FFmpeg.
>   *
> @@ -39,6 +40,8 @@
>  #include "avio_internal.h"
>  #include "id3v2.h"
>
> +#include "hls_sample_aes.h"
> +
>  #define INITIAL_BUFFER_SIZE 32768
>
>  #define MAX_FIELD_LEN 64
> @@ -145,6 +148,10 @@ struct playlist {
>      int id3_changed; /* ID3 tag data has changed at some point */
>      ID3v2ExtraMeta *id3_deferred_extra; /* stored here until subdemuxer
> is opened */
>
> +    /* Used in case of SAMPLE-AES encryption method */
> +    HLSAudioSetupInfo audio_setup_info;
> +    HLSCryptoContext  crypto_ctx;
> +
>      int64_t seek_timestamp;
>      int seek_flags;
>      int seek_stream_index; /* into subdemuxer stream array */
> @@ -266,6 +273,8 @@ static void free_playlist_list(HLSContext *c)
>              pls->ctx->pb = NULL;
>              avformat_close_input(&pls->ctx);
>          }
> +        if (pls->crypto_ctx.aes_ctx)
> +             av_free(pls->crypto_ctx.aes_ctx);
>          av_free(pls);
>      }
>      av_freep(&c->playlists);
> @@ -994,7 +1003,10 @@ fail:
>
>  static struct segment *current_segment(struct playlist *pls)
>  {
> -    return pls->segments[pls->cur_seq_no - pls->start_seq_no];
> +    int64_t n = pls->cur_seq_no - pls->start_seq_no;
> +    if (n >= pls->n_segments)
> +        return NULL;
> +    return pls->segments[n];
>  }
>
>  static struct segment *next_segment(struct playlist *pls)
> @@ -1023,10 +1035,11 @@ static int read_from_url(struct playlist *pls,
> struct segment *seg,
>
>  /* Parse the raw ID3 data and pass contents to caller */
>  static void parse_id3(AVFormatContext *s, AVIOContext *pb,
> -                      AVDictionary **metadata, int64_t *dts,
> +                      AVDictionary **metadata, int64_t *dts,
> HLSAudioSetupInfo *audio_setup_info,
>                        ID3v2ExtraMetaAPIC **apic, ID3v2ExtraMeta
> **extra_meta)
>  {
>      static const char id3_priv_owner_ts[] =
> "com.apple.streaming.transportStreamTimestamp";
> +    static const char id3_priv_owner_audio_setup[] =
> "com.apple.streaming.audioDescription";
>      ID3v2ExtraMeta *meta;
>
>      ff_id3v2_read_dict(pb, metadata, ID3v2_DEFAULT_MAGIC, extra_meta);
> @@ -1041,6 +1054,8 @@ static void parse_id3(AVFormatContext *s,
> AVIOContext *pb,
>                      *dts = ts;
>                  else
>                      av_log(s, AV_LOG_ERROR, "Invalid HLS ID3 audio
> timestamp %"PRId64"\n", ts);
> +            } else if (priv->datasize >= 8 && !strcmp(priv->owner,
> id3_priv_owner_audio_setup)) {
> +                ff_hls_read_audio_setup_info(audio_setup_info,
> priv->data, priv->datasize);
>              }
>          } else if (!strcmp(meta->tag, "APIC") && apic)
>              *apic = &meta->data.apic;
> @@ -1084,7 +1099,7 @@ static void handle_id3(AVIOContext *pb, struct
> playlist *pls)
>      ID3v2ExtraMeta *extra_meta = NULL;
>      int64_t timestamp = AV_NOPTS_VALUE;
>
> -    parse_id3(pls->ctx, pb, &metadata, &timestamp, &apic, &extra_meta);
> +    parse_id3(pls->ctx, pb, &metadata, &timestamp,
> &pls->audio_setup_info, &apic, &extra_meta);
>
>      if (timestamp != AV_NOPTS_VALUE) {
>          pls->id3_mpegts_timestamp = timestamp;
> @@ -1238,10 +1253,7 @@ static int open_input(HLSContext *c, struct
> playlist *pls, struct segment *seg,
>      av_log(pls->parent, AV_LOG_VERBOSE, "HLS request for url '%s', offset
> %"PRId64", playlist %d\n",
>             seg->url, seg->url_offset, pls->index);
>
> -    if (seg->key_type == KEY_NONE) {
> -        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
> &is_http);
> -    } else if (seg->key_type == KEY_AES_128) {
> -        char iv[33], key[33], url[MAX_URL_SIZE];
> +    if (seg->key_type == KEY_AES_128 || seg->key_type == KEY_SAMPLE_AES) {
>          if (strcmp(seg->key, pls->key_url)) {
>              AVIOContext *pb = NULL;
>              if (open_url(pls->parent, &pb, seg->key, &c->avio_opts, opts,
> NULL) == 0) {
> @@ -1257,6 +1269,10 @@ static int open_input(HLSContext *c, struct
> playlist *pls, struct segment *seg,
>              }
>              av_strlcpy(pls->key_url, seg->key, sizeof(pls->key_url));
>          }
> +    }
> +
> +    if (seg->key_type == KEY_AES_128) {
> +        char iv[33], key[33], url[MAX_URL_SIZE];
>          ff_data_to_hex(iv, seg->iv, sizeof(seg->iv), 0);
>          ff_data_to_hex(key, pls->key, sizeof(pls->key), 0);
>          iv[32] = key[32] = '\0';
> @@ -1273,13 +1289,9 @@ static int open_input(HLSContext *c, struct
> playlist *pls, struct segment *seg,
>              goto cleanup;
>          }
>          ret = 0;
> -    } else if (seg->key_type == KEY_SAMPLE_AES) {
> -        av_log(pls->parent, AV_LOG_ERROR,
> -               "SAMPLE-AES encryption is not supported yet\n");
> -        ret = AVERROR_PATCHWELCOME;
> +    } else {
> +        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
> &is_http);
>      }
> -    else
> -      ret = AVERROR(ENOSYS);
>
>      /* Seek to the requested position. If this was a HTTP request, the
> offset
>       * should already be where want it to, but this allows e.g. local
> testing
> @@ -1948,6 +1960,7 @@ static int hls_read_header(AVFormatContext *s)
>          struct playlist *pls = c->playlists[i];
>          char *url;
>          ff_const59 AVInputFormat *in_fmt = NULL;
> +        struct segment *seg = NULL;
>
>          if (!(pls->ctx = avformat_alloc_context())) {
>              ret = AVERROR(ENOMEM);
> @@ -1980,8 +1993,41 @@ static int hls_read_header(AVFormatContext *s)
>              pls->ctx = NULL;
>              goto fail;
>          }
> +
>          ffio_init_context(&pls->pb, pls->read_buffer,
> INITIAL_BUFFER_SIZE, 0, pls,
>                            read_data, NULL, NULL);
> +
> +        /*
> +         * If encryption scheme is SAMPLE-AES, try to read  ID3 tags of
> +         * external audio track that contains audio setup information
> +         */
> +        seg = current_segment(pls);
> +        if (seg && seg->key_type == KEY_SAMPLE_AES && pls->n_renditions >
> 0 &&
> +            pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO) {
> +            uint8_t buf[HLS_MAX_ID3_TAGS_DATA_LEN];
> +            if ((ret = avio_read(&pls->pb, buf,
> HLS_MAX_ID3_TAGS_DATA_LEN)) < 0) {
> +                /* Fail if error was not end of file */
> +                if (ret != AVERROR_EOF) {
> +                    avformat_free_context(pls->ctx);
> +                    pls->ctx = NULL;
> +                    goto fail;
> +                }
> +            }
> +            ret = 0;
> +        }
> +
> +        /*
> +         * If encryption scheme is SAMPLE-AES and audio setup information
> is present in external audio track,
> +         * use that information to find the media format, otherwise probe
> input data
> +         */
> +        if (seg && seg->key_type == KEY_SAMPLE_AES &&
> pls->is_id3_timestamped &&
> +            pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) {
> +            void *iter = NULL;
> +            while ((in_fmt = (ff_const59 AVInputFormat
> *)av_demuxer_iterate(&iter)))
> +                if (in_fmt->raw_codec_id ==
> pls->audio_setup_info.codec_id) {
> +                    break;
> +                }
> +        } else {
>          pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 * 4;
>          pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ?
> s->max_analyze_duration : 4 * AV_TIME_BASE;
>          pls->ctx->interrupt_callback = s->interrupt_callback;
> @@ -1999,6 +2045,25 @@ static int hls_read_header(AVFormatContext *s)
>              goto fail;
>          }
>          av_free(url);
> +        }
> +
> +        if (seg && seg->key_type == KEY_SAMPLE_AES) {
> +            if (!pls->is_id3_timestamped && pls->n_renditions > 0 &&
> pls->renditions[0]->type != AVMEDIA_TYPE_AUDIO &&
> +                strcmp(in_fmt->name, "mpegts")) {
> +                av_log(s, AV_LOG_ERROR, "SAMPLE-AES encryption is not
> supported for fragmented MP4 format yet\n");
> +                ret = AVERROR_PATCHWELCOME;
> +            } else {
> +                pls->crypto_ctx.aes_ctx = av_aes_alloc();
> +                if (!pls->crypto_ctx.aes_ctx)
> +                    ret = AVERROR(ENOMEM);
> +            }
> +            if (ret != 0) {
> +                avformat_free_context(pls->ctx);
> +                pls->ctx = NULL;
> +                goto fail;
> +            }
> +        }
> +
>          pls->ctx->pb       = &pls->pb;
>          pls->ctx->io_open  = nested_io_open;
>          pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
> @@ -2027,7 +2092,12 @@ static int hls_read_header(AVFormatContext *s)
>           * on us if they want to.
>           */
>          if (pls->is_id3_timestamped || (pls->n_renditions > 0 &&
> pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO)) {
> +            if (seg && seg->key_type == KEY_SAMPLE_AES &&
> pls->audio_setup_info.setup_data_length > 0 &&
> +                pls->ctx->nb_streams == 1)
> +                ret = ff_hls_parse_audio_setup_info(pls->ctx->streams[0],
> &pls->audio_setup_info);
> +            else
>              ret = avformat_find_stream_info(pls->ctx, NULL);
> +
>              if (ret < 0)
>                  goto fail;
>          }
> @@ -2157,6 +2227,7 @@ static int hls_read_packet(AVFormatContext *s,
> AVPacket *pkt)
>              while (1) {
>                  int64_t ts_diff;
>                  AVRational tb;
> +                struct segment *seg = NULL;
>                  ret = av_read_frame(pls->ctx, &pls->pkt);
>                  if (ret < 0) {
>                      if (!avio_feof(&pls->pb) && ret != AVERROR_EOF)
> @@ -2175,6 +2246,14 @@ static int hls_read_packet(AVFormatContext *s,
> AVPacket *pkt)
>                              get_timebase(pls), AV_TIME_BASE_Q);
>                  }
>
> +                seg = current_segment(pls);
> +                if (seg && seg->key_type == KEY_SAMPLE_AES) {
> +                    enum AVCodecID codec_id =
> pls->ctx->streams[pls->pkt.stream_index]->codecpar->codec_id;
> +                    memcpy(pls->crypto_ctx.iv, seg->iv, sizeof(seg->iv));
> +                    memcpy(pls->crypto_ctx.key, pls->key,
> sizeof(pls->key));
> +                    ff_hls_decrypt_frame(codec_id, &pls->crypto_ctx,
> &pls->pkt);
> +                }
> +
>                  if (pls->seek_timestamp == AV_NOPTS_VALUE)
>                      break;
>
> diff --git a/libavformat/hls_sample_aes.c b/libavformat/hls_sample_aes.c
> new file mode 100644
> index 0000000000..0407a15b0f
> --- /dev/null
> +++ b/libavformat/hls_sample_aes.c
> @@ -0,0 +1,391 @@
> +/*
> + * Apple HTTP Live Streaming Sample Encryption/Decryption
> + *
> + * Copyright (c) 2021 Nachiket Tarate
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> + */
> +
> +/**
> + * @file
> + * Apple HTTP Live Streaming Sample Encryption
> + *
> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> + */
> +
> +#include "hls_sample_aes.h"
> +
> +#include "libavcodec/adts_header.h"
> +#include "libavcodec/adts_parser.h"
> +#include "libavcodec/ac3_parser_internal.h"
> +
> +
> +typedef struct NALUnit {
> +    uint8_t     *data;
> +    int         type;
> +    int         length;
> +    int         start_code_length;
> +} NALUnit;
> +
> +typedef struct AudioFrame {
> +    uint8_t     *data;
> +    int         length;
> +    int         header_length;
> +} AudioFrame;
> +
> +typedef struct CodecParserContext {
> +    const uint8_t   *buf_ptr;
> +    const uint8_t   *buf_end;
> +} CodecParserContext;
> +
> +static const int eac3_sample_rate_tab[] = { 48000, 44100, 32000, 0 };
> +
> +void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const uint8_t
> *buf, size_t size)
> +{
> +    if (size < 8)
> +        return;
> +
> +    info->codec_tag             = AV_RL32(buf);
> +
> +    if (info->codec_tag == MKTAG('z','a', 'a', 'c'))
> +        info->codec_id = AV_CODEC_ID_AAC;
> +    else if (info->codec_tag == MKTAG('z','a', 'c', '3'))
> +        info->codec_id = AV_CODEC_ID_AC3;
> +    else if (info->codec_tag == MKTAG('z','e', 'c', '3'))
> +        info->codec_id = AV_CODEC_ID_EAC3;
> +    else
> +        info->codec_id = AV_CODEC_ID_NONE;
> +
> +    buf += 4;
> +    info->priming               = AV_RL16(buf);
> +    buf += 2;
> +    info->version               = *buf++;
> +    info->setup_data_length     = *buf++;
> +
> +    if (info->setup_data_length > size - 8)
> +        info->setup_data_length = size - 8;
> +
> +    if (info->setup_data_length > HLS_MAX_AUDIO_SETUP_DATA_LEN)
> +        return;
> +
> +    memcpy(info->setup_data, buf, info->setup_data_length);
> +}
> +
> +int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo *info)
> +{
> +    int ret = 0;
> +
> +    st->codecpar->codec_tag = info->codec_tag;
> +
> +    if (st->codecpar->codec_id == AV_CODEC_ID_AAC)
> +        return 0;
> +
> +    if (st->codecpar->codec_id != AV_CODEC_ID_AC3 &&
> st->codecpar->codec_id != AV_CODEC_ID_EAC3)
> +        return AVERROR_INVALIDDATA;
> +
> +    if (st->codecpar->codec_id == AV_CODEC_ID_AC3) {
> +
> +        AC3HeaderInfo *ac3hdr = NULL;
> +
> +        ret = avpriv_ac3_parse_header(&ac3hdr, info->setup_data,
> info->setup_data_length);
> +        if (ret < 0) {
> +            if (ret != AVERROR(ENOMEM))
> +                av_free(ac3hdr);
> +            return ret;
> +        }
> +
> +        st->codecpar->sample_rate       = ac3hdr->sample_rate;
> +        st->codecpar->channels          = ac3hdr->channels;
> +        st->codecpar->channel_layout    = ac3hdr->channel_layout;
> +        st->codecpar->bit_rate          = ac3hdr->bit_rate;
> +
> +        av_free(ac3hdr);
> +    } else {  /*  Parse 'dec3' EC3SpecificBox */
> +
> +        GetBitContext gb;
> +        int data_rate, fscod, acmod, lfeon;
> +
> +        ret = init_get_bits8(&gb, info->setup_data,
> info->setup_data_length);
> +        if (ret < 0)
> +            return AVERROR_INVALIDDATA;
> +
> +        data_rate = get_bits(&gb, 13);
> +        skip_bits(&gb, 3);
> +        fscod = get_bits(&gb, 2);
> +        skip_bits(&gb, 10);
> +        acmod = get_bits(&gb, 3);
> +        lfeon = get_bits(&gb, 1);
> +
> +        st->codecpar->sample_rate = eac3_sample_rate_tab[fscod];
> +
> +        st->codecpar->channel_layout =
> avpriv_ac3_channel_layout_tab[acmod];
> +        if (lfeon)
> +            st->codecpar->channel_layout |= AV_CH_LOW_FREQUENCY;
> +
> +        st->codecpar->channels =
> av_get_channel_layout_nb_channels(st->codecpar->channel_layout);
> +
> +        st->codecpar->bit_rate = data_rate*1000;
> +    }
> +
> +    return 0;
> +}
> +
> +/*
> + * Remove start code emulation prevention 0x03 bytes
> + */
> +static void remove_scep_3_bytes(NALUnit *nalu)
> +{
> +    int i = 0;
> +    int j = 0;
> +
> +    uint8_t *data = nalu->data;
> +
> +    while (i < nalu->length) {
> +        if (nalu->length - i > 3 && AV_RB24(&data[i]) == 0x000003) {
> +            data[j++] = data[i++];
> +            data[j++] = data[i++];
> +            i++;
> +        } else {
> +            data[j++] = data[i++];
> +        }
> +    }
> +
> +    nalu->length = j;
> +}
> +
> +static int get_next_nal_unit(CodecParserContext *ctx, NALUnit *nalu)
> +{
> +    const uint8_t *nalu_start = ctx->buf_ptr;
> +
> +    if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr) ==
> 0x00000001)
> +        nalu->start_code_length = 4;
> +    else if (ctx->buf_end - ctx->buf_ptr >= 3 && AV_RB24(ctx->buf_ptr) ==
> 0x000001)
> +        nalu->start_code_length = 3;
> +    else /* No start code at the beginning of the NAL unit */
> +        return -1;
> +
> +    ctx->buf_ptr += nalu->start_code_length;
> +
> +    while (ctx->buf_ptr < ctx->buf_end) {
> +        if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr) ==
> 0x00000001)
> +            break;
> +        else if (ctx->buf_end - ctx->buf_ptr >= 3 &&
> AV_RB24(ctx->buf_ptr) == 0x000001)
> +            break;
> +        ctx->buf_ptr++;
> +    }
> +
> +    nalu->data  = (uint8_t *)nalu_start + nalu->start_code_length;
> +    nalu->length = ctx->buf_ptr - nalu->data;
> +    nalu->type  = *nalu->data & 0x1F;
> +
> +    return 0;
> +}
> +
> +static int decrypt_nal_unit(HLSCryptoContext *crypto_ctx, NALUnit *nalu)
> +{
> +    int ret = 0;
> +    int rem_bytes;
> +    uint8_t *data;
> +    uint8_t iv[16];
> +
> +    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
> +    if (ret < 0)
> +        return ret;
> +
> +    /* Remove start code emulation prevention 0x03 bytes */
> +    remove_scep_3_bytes(nalu);
> +
> +    data = nalu->data + 32;
> +    rem_bytes = nalu->length - 32;
> +
> +    memcpy(iv, crypto_ctx->iv, 16);
> +
> +    while (rem_bytes > 0) {
> +        if (rem_bytes > 16) {
> +            av_aes_crypt(crypto_ctx->aes_ctx, data, data, 1, iv, 1);
> +            data += 16;
> +            rem_bytes -= 16;
> +        }
> +        data += FFMIN(144, rem_bytes);
> +        rem_bytes -= FFMIN(144, rem_bytes);
> +    }
> +
> +    return 0;
> +}
> +
> +static int decrypt_video_frame(HLSCryptoContext *crypto_ctx, AVPacket
> *pkt)
> +{
> +    int ret = 0;
> +    CodecParserContext  ctx;
> +    NALUnit nalu;
> +    uint8_t *data_ptr;
> +    int move_nalu = 0;
> +
> +    memset(&ctx, 0, sizeof(ctx));
> +    ctx.buf_ptr  = pkt->data;
> +    ctx.buf_end = pkt->data + pkt->size;
> +
> +    data_ptr = pkt->data;
> +
> +    while (ctx.buf_ptr < ctx.buf_end) {
> +        memset(&nalu, 0, sizeof(nalu));
> +        ret = get_next_nal_unit(&ctx, &nalu);
> +        if (ret < 0)
> +            return ret;
> +        if ((nalu.type == 0x01 || nalu.type == 0x05) && nalu.length > 48)
> {
> +            int encrypted_nalu_length = nalu.length;
> +            ret = decrypt_nal_unit(crypto_ctx, &nalu);
> +            if (ret < 0)
> +                return ret;
> +            move_nalu = nalu.length != encrypted_nalu_length;
> +        }
> +        if (move_nalu)
> +            memmove(data_ptr, nalu.data - nalu.start_code_length,
> nalu.start_code_length + nalu.length);
> +        data_ptr += nalu.start_code_length + nalu.length;
> +    }
> +
> +    av_shrink_packet(pkt, data_ptr - pkt->data);
> +
> +    return 0;
> +}
> +
> +static int get_next_adts_frame(CodecParserContext *ctx, AudioFrame *frame)
> +{
> +    int ret = 0;
> +
> +    AACADTSHeaderInfo *adts_hdr = NULL;
> +
> +    /* Find next sync word 0xFFF */
> +    while (ctx->buf_ptr < ctx->buf_end - 1) {
> +        if (*ctx->buf_ptr == 0xFF && *(ctx->buf_ptr + 1) & 0xF0 == 0xF0)
> +            break;
> +        ctx->buf_ptr++;
> +    }
> +
> +    if (ctx->buf_ptr >= ctx->buf_end - 1)
> +        return -1;
> +
> +    frame->data = (uint8_t*)ctx->buf_ptr;
> +
> +    ret = avpriv_adts_header_parse (&adts_hdr, frame->data, ctx->buf_end
> - frame->data);
> +    if (ret < 0)
> +        return ret;
> +
> +    frame->header_length = adts_hdr->crc_absent ? AV_AAC_ADTS_HEADER_SIZE
> : AV_AAC_ADTS_HEADER_SIZE + 2;
> +    frame->length = adts_hdr->frame_length;
> +
> +    av_free(adts_hdr);
> +
> +    return 0;
> +}
> +
> +static int get_next_ac3_eac3_sync_frame(CodecParserContext *ctx,
> AudioFrame *frame)
> +{
> +    int ret = 0;
> +
> +    AC3HeaderInfo *hdr = NULL;
> +
> +    /* Find next sync word 0x0B77 */
> +    while (ctx->buf_ptr < ctx->buf_end - 1) {
> +        if (*ctx->buf_ptr == 0x0B && *(ctx->buf_ptr + 1) == 0x77)
> +            break;
> +        ctx->buf_ptr++;
> +    }
> +
> +    if (ctx->buf_ptr >= ctx->buf_end - 1)
> +        return -1;
> +
> +    frame->data = (uint8_t*)ctx->buf_ptr;
> +    frame->header_length = 0;
> +
> +    ret = avpriv_ac3_parse_header(&hdr, frame->data, ctx->buf_end -
> frame->data);
> +    if (ret < 0) {
> +        if (ret != AVERROR(ENOMEM))
> +            av_free(hdr);
> +        return ret;
> +    }
> +
> +    frame->length = hdr->frame_size;
> +
> +    av_free(hdr);
> +
> +    return 0;
> +}
> +
> +static int get_next_sync_frame(enum AVCodecID codec_id,
> CodecParserContext *ctx, AudioFrame *frame)
> +{
> +    if (codec_id == AV_CODEC_ID_AAC)
> +        return get_next_adts_frame(ctx, frame);
> +    else if (codec_id == AV_CODEC_ID_AC3 || codec_id == AV_CODEC_ID_EAC3)
> +        return get_next_ac3_eac3_sync_frame(ctx, frame);
> +    else
> +        return AVERROR_INVALIDDATA;
> +}
> +
> +static int decrypt_sync_frame(enum AVCodecID codec_id, HLSCryptoContext
> *crypto_ctx, AudioFrame *frame)
> +{
> +    int ret = 0;
> +    uint8_t *data;
> +    int num_of_encrypted_blocks;
> +
> +    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
> +    if (ret < 0)
> +        return ret;
> +
> +    data = frame->data + frame->header_length + 16;
> +
> +    num_of_encrypted_blocks = (frame->length - frame->header_length -
> 16)/16;
> +
> +    av_aes_crypt(crypto_ctx->aes_ctx, data, data,
> num_of_encrypted_blocks, crypto_ctx->iv, 1);
> +
> +    return 0;
> +}
> +
> +static int decrypt_audio_frame(enum AVCodecID codec_id, HLSCryptoContext
> *crypto_ctx, AVPacket *pkt)
> +{
> +    int ret = 0;
> +    CodecParserContext  ctx;
> +    AudioFrame frame;
> +
> +    memset(&ctx, 0, sizeof(ctx));
> +    ctx.buf_ptr        = pkt->data;
> +    ctx.buf_end = pkt->data + pkt->size;
> +
> +    while (ctx.buf_ptr < ctx.buf_end) {
> +        memset(&frame, 0, sizeof(frame));
> +        ret = get_next_sync_frame(codec_id, &ctx, &frame);
> +        if (ret < 0)
> +            return ret;
> +        if (frame.length - frame.header_length > 31) {
> +            ret = decrypt_sync_frame(codec_id, crypto_ctx, &frame);
> +            if (ret < 0)
> +                return ret;
> +        }
> +        ctx.buf_ptr += frame.length;
> +    }
> +
> +    return 0;
> +}
> +
> +int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext
> *crypto_ctx, AVPacket *pkt)
> +{
> +    if (codec_id == AV_CODEC_ID_H264)
> +        return decrypt_video_frame(crypto_ctx, pkt);
> +    else if (codec_id == AV_CODEC_ID_AAC || codec_id == AV_CODEC_ID_AC3
> || codec_id == AV_CODEC_ID_EAC3)
> +        return decrypt_audio_frame(codec_id, crypto_ctx, pkt);
> +
> +    return AVERROR_INVALIDDATA;
> +}
> diff --git a/libavformat/hls_sample_aes.h b/libavformat/hls_sample_aes.h
> new file mode 100644
> index 0000000000..cf80e41cb0
> --- /dev/null
> +++ b/libavformat/hls_sample_aes.h
> @@ -0,0 +1,66 @@
> +/*
> + * Apple HTTP Live Streaming Sample Encryption/Decryption
> + *
> + * Copyright (c) 2021 Nachiket Tarate
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> + */
> +
> +/**
> + * @file
> + * Apple HTTP Live Streaming Sample Encryption
> + *
> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> + */
> +
> +#ifndef AVFORMAT_HLS_SAMPLE_AES_H
> +#define AVFORMAT_HLS_SAMPLE_AES_H
> +
> +#include <stdint.h>
> +
> +#include "avformat.h"
> +
> +#include "libavcodec/avcodec.h"
> +#include "libavutil/aes.h"
> +
> +#define HLS_MAX_ID3_TAGS_DATA_LEN       138
> +#define HLS_MAX_AUDIO_SETUP_DATA_LEN    10
> +
> +
> +typedef struct HLSCryptoContext {
> +    struct AVAES   *aes_ctx;
> +    uint8_t            key[16];
> +    uint8_t            iv[16];
> +} HLSCryptoContext;
> +
> +typedef struct HLSAudioSetupInfo {
> +    enum AVCodecID      codec_id;
> +    uint32_t            codec_tag;
> +    uint16_t            priming;
> +    uint8_t             version;
> +    uint8_t             setup_data_length;
> +    uint8_t             setup_data[HLS_MAX_AUDIO_SETUP_DATA_LEN];
> +} HLSAudioSetupInfo;
> +
> +
> +void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const uint8_t
> *buf, size_t size);
> +
> +int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo *info);
> +
> +int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext
> *crypto_ctx, AVPacket *pkt);
> +
> +#endif /* AVFORMAT_HLS_SAMPLE_AES_H */
> +
> diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
> index e283ec09d7..dc611ae788 100644
> --- a/libavformat/mpegts.c
> +++ b/libavformat/mpegts.c
> @@ -839,6 +839,16 @@ static const StreamType MISC_types[] = {
>      { 0 },
>  };
>
> +/* HLS Sample Encryption Types  */
> +static const StreamType HLS_SAMPLE_ENC_types[] = {
> +    { 0xdb, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264},
> +    { 0xcf, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
> +    { 0xc1, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 },
> +    { 0xc2, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3},
> +    { 0 },
> +};
> +
> +
>  static const StreamType REGD_types[] = {
>      { MKTAG('d', 'r', 'a', 'c'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC },
>      { MKTAG('A', 'C', '-', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3   },
> @@ -948,6 +958,8 @@ static int mpegts_set_stream_info(AVStream *st,
> PESContext *pes,
>      }
>      if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
>          mpegts_find_stream_type(st, pes->stream_type, MISC_types);
> +    if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
> +        mpegts_find_stream_type(st, pes->stream_type,
> HLS_SAMPLE_ENC_types);
>      if (st->codecpar->codec_id == AV_CODEC_ID_NONE) {
>          st->codecpar->codec_id  = old_codec_id;
>          st->codecpar->codec_type = old_codec_type;
> --
> 2.17.1
>
>
Liu Steven March 1, 2021, 4:42 a.m. UTC | #2
> 2021年3月1日 下午12:35,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
> 
> @Steven Liu <lq@chinaffmpeg.org>
> 
> Can we merge this patch ?
I’m waiting update patch for fragment mp4 encryption.
After new version of the patchset I will test and review.
> 
> Best Regards,
> Nachiket Tarate
> 
> On Wed, Feb 24, 2021 at 4:44 PM Nachiket Tarate <
> nachiket.programmer@gmail.com> wrote:
> 
>> Apple HTTP Live Streaming Sample Encryption:
>> 
>> 
>> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
>> 
>> Signed-off-by: Nachiket Tarate <nachiket.programmer@gmail.com>
>> ---
>> libavformat/Makefile         |   2 +-
>> libavformat/hls.c            | 105 ++++++++--
>> libavformat/hls_sample_aes.c | 391 +++++++++++++++++++++++++++++++++++
>> libavformat/hls_sample_aes.h |  66 ++++++
>> libavformat/mpegts.c         |  12 ++
>> 5 files changed, 562 insertions(+), 14 deletions(-)
>> create mode 100644 libavformat/hls_sample_aes.c
>> create mode 100644 libavformat/hls_sample_aes.h
>> 
>> diff --git a/libavformat/Makefile b/libavformat/Makefile
>> index fcb39ce133..62627d50a6 100644
>> --- a/libavformat/Makefile
>> +++ b/libavformat/Makefile
>> @@ -236,7 +236,7 @@ OBJS-$(CONFIG_HCOM_DEMUXER)              += hcom.o
>> pcm.o
>> OBJS-$(CONFIG_HDS_MUXER)                 += hdsenc.o
>> OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
>> OBJS-$(CONFIG_HEVC_MUXER)                += rawenc.o
>> -OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o
>> +OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o hls_sample_aes.o
>> OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o hlsplaylist.o avc.o
>> OBJS-$(CONFIG_HNM_DEMUXER)               += hnm.o
>> OBJS-$(CONFIG_ICO_DEMUXER)               += icodec.o
>> diff --git a/libavformat/hls.c b/libavformat/hls.c
>> index af2468ad9b..3cb3853c79 100644
>> --- a/libavformat/hls.c
>> +++ b/libavformat/hls.c
>> @@ -2,6 +2,7 @@
>>  * Apple HTTP Live Streaming demuxer
>>  * Copyright (c) 2010 Martin Storsjo
>>  * Copyright (c) 2013 Anssi Hannula
>> + * Copyright (c) 2021 Nachiket Tarate
>>  *
>>  * This file is part of FFmpeg.
>>  *
>> @@ -39,6 +40,8 @@
>> #include "avio_internal.h"
>> #include "id3v2.h"
>> 
>> +#include "hls_sample_aes.h"
>> +
>> #define INITIAL_BUFFER_SIZE 32768
>> 
>> #define MAX_FIELD_LEN 64
>> @@ -145,6 +148,10 @@ struct playlist {
>>     int id3_changed; /* ID3 tag data has changed at some point */
>>     ID3v2ExtraMeta *id3_deferred_extra; /* stored here until subdemuxer
>> is opened */
>> 
>> +    /* Used in case of SAMPLE-AES encryption method */
>> +    HLSAudioSetupInfo audio_setup_info;
>> +    HLSCryptoContext  crypto_ctx;
>> +
>>     int64_t seek_timestamp;
>>     int seek_flags;
>>     int seek_stream_index; /* into subdemuxer stream array */
>> @@ -266,6 +273,8 @@ static void free_playlist_list(HLSContext *c)
>>             pls->ctx->pb = NULL;
>>             avformat_close_input(&pls->ctx);
>>         }
>> +        if (pls->crypto_ctx.aes_ctx)
>> +             av_free(pls->crypto_ctx.aes_ctx);
>>         av_free(pls);
>>     }
>>     av_freep(&c->playlists);
>> @@ -994,7 +1003,10 @@ fail:
>> 
>> static struct segment *current_segment(struct playlist *pls)
>> {
>> -    return pls->segments[pls->cur_seq_no - pls->start_seq_no];
>> +    int64_t n = pls->cur_seq_no - pls->start_seq_no;
>> +    if (n >= pls->n_segments)
>> +        return NULL;
>> +    return pls->segments[n];
>> }
>> 
>> static struct segment *next_segment(struct playlist *pls)
>> @@ -1023,10 +1035,11 @@ static int read_from_url(struct playlist *pls,
>> struct segment *seg,
>> 
>> /* Parse the raw ID3 data and pass contents to caller */
>> static void parse_id3(AVFormatContext *s, AVIOContext *pb,
>> -                      AVDictionary **metadata, int64_t *dts,
>> +                      AVDictionary **metadata, int64_t *dts,
>> HLSAudioSetupInfo *audio_setup_info,
>>                       ID3v2ExtraMetaAPIC **apic, ID3v2ExtraMeta
>> **extra_meta)
>> {
>>     static const char id3_priv_owner_ts[] =
>> "com.apple.streaming.transportStreamTimestamp";
>> +    static const char id3_priv_owner_audio_setup[] =
>> "com.apple.streaming.audioDescription";
>>     ID3v2ExtraMeta *meta;
>> 
>>     ff_id3v2_read_dict(pb, metadata, ID3v2_DEFAULT_MAGIC, extra_meta);
>> @@ -1041,6 +1054,8 @@ static void parse_id3(AVFormatContext *s,
>> AVIOContext *pb,
>>                     *dts = ts;
>>                 else
>>                     av_log(s, AV_LOG_ERROR, "Invalid HLS ID3 audio
>> timestamp %"PRId64"\n", ts);
>> +            } else if (priv->datasize >= 8 && !strcmp(priv->owner,
>> id3_priv_owner_audio_setup)) {
>> +                ff_hls_read_audio_setup_info(audio_setup_info,
>> priv->data, priv->datasize);
>>             }
>>         } else if (!strcmp(meta->tag, "APIC") && apic)
>>             *apic = &meta->data.apic;
>> @@ -1084,7 +1099,7 @@ static void handle_id3(AVIOContext *pb, struct
>> playlist *pls)
>>     ID3v2ExtraMeta *extra_meta = NULL;
>>     int64_t timestamp = AV_NOPTS_VALUE;
>> 
>> -    parse_id3(pls->ctx, pb, &metadata, &timestamp, &apic, &extra_meta);
>> +    parse_id3(pls->ctx, pb, &metadata, &timestamp,
>> &pls->audio_setup_info, &apic, &extra_meta);
>> 
>>     if (timestamp != AV_NOPTS_VALUE) {
>>         pls->id3_mpegts_timestamp = timestamp;
>> @@ -1238,10 +1253,7 @@ static int open_input(HLSContext *c, struct
>> playlist *pls, struct segment *seg,
>>     av_log(pls->parent, AV_LOG_VERBOSE, "HLS request for url '%s', offset
>> %"PRId64", playlist %d\n",
>>            seg->url, seg->url_offset, pls->index);
>> 
>> -    if (seg->key_type == KEY_NONE) {
>> -        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
>> &is_http);
>> -    } else if (seg->key_type == KEY_AES_128) {
>> -        char iv[33], key[33], url[MAX_URL_SIZE];
>> +    if (seg->key_type == KEY_AES_128 || seg->key_type == KEY_SAMPLE_AES) {
>>         if (strcmp(seg->key, pls->key_url)) {
>>             AVIOContext *pb = NULL;
>>             if (open_url(pls->parent, &pb, seg->key, &c->avio_opts, opts,
>> NULL) == 0) {
>> @@ -1257,6 +1269,10 @@ static int open_input(HLSContext *c, struct
>> playlist *pls, struct segment *seg,
>>             }
>>             av_strlcpy(pls->key_url, seg->key, sizeof(pls->key_url));
>>         }
>> +    }
>> +
>> +    if (seg->key_type == KEY_AES_128) {
>> +        char iv[33], key[33], url[MAX_URL_SIZE];
>>         ff_data_to_hex(iv, seg->iv, sizeof(seg->iv), 0);
>>         ff_data_to_hex(key, pls->key, sizeof(pls->key), 0);
>>         iv[32] = key[32] = '\0';
>> @@ -1273,13 +1289,9 @@ static int open_input(HLSContext *c, struct
>> playlist *pls, struct segment *seg,
>>             goto cleanup;
>>         }
>>         ret = 0;
>> -    } else if (seg->key_type == KEY_SAMPLE_AES) {
>> -        av_log(pls->parent, AV_LOG_ERROR,
>> -               "SAMPLE-AES encryption is not supported yet\n");
>> -        ret = AVERROR_PATCHWELCOME;
>> +    } else {
>> +        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
>> &is_http);
>>     }
>> -    else
>> -      ret = AVERROR(ENOSYS);
>> 
>>     /* Seek to the requested position. If this was a HTTP request, the
>> offset
>>      * should already be where want it to, but this allows e.g. local
>> testing
>> @@ -1948,6 +1960,7 @@ static int hls_read_header(AVFormatContext *s)
>>         struct playlist *pls = c->playlists[i];
>>         char *url;
>>         ff_const59 AVInputFormat *in_fmt = NULL;
>> +        struct segment *seg = NULL;
>> 
>>         if (!(pls->ctx = avformat_alloc_context())) {
>>             ret = AVERROR(ENOMEM);
>> @@ -1980,8 +1993,41 @@ static int hls_read_header(AVFormatContext *s)
>>             pls->ctx = NULL;
>>             goto fail;
>>         }
>> +
>>         ffio_init_context(&pls->pb, pls->read_buffer,
>> INITIAL_BUFFER_SIZE, 0, pls,
>>                           read_data, NULL, NULL);
>> +
>> +        /*
>> +         * If encryption scheme is SAMPLE-AES, try to read  ID3 tags of
>> +         * external audio track that contains audio setup information
>> +         */
>> +        seg = current_segment(pls);
>> +        if (seg && seg->key_type == KEY_SAMPLE_AES && pls->n_renditions >
>> 0 &&
>> +            pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO) {
>> +            uint8_t buf[HLS_MAX_ID3_TAGS_DATA_LEN];
>> +            if ((ret = avio_read(&pls->pb, buf,
>> HLS_MAX_ID3_TAGS_DATA_LEN)) < 0) {
>> +                /* Fail if error was not end of file */
>> +                if (ret != AVERROR_EOF) {
>> +                    avformat_free_context(pls->ctx);
>> +                    pls->ctx = NULL;
>> +                    goto fail;
>> +                }
>> +            }
>> +            ret = 0;
>> +        }
>> +
>> +        /*
>> +         * If encryption scheme is SAMPLE-AES and audio setup information
>> is present in external audio track,
>> +         * use that information to find the media format, otherwise probe
>> input data
>> +         */
>> +        if (seg && seg->key_type == KEY_SAMPLE_AES &&
>> pls->is_id3_timestamped &&
>> +            pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) {
>> +            void *iter = NULL;
>> +            while ((in_fmt = (ff_const59 AVInputFormat
>> *)av_demuxer_iterate(&iter)))
>> +                if (in_fmt->raw_codec_id ==
>> pls->audio_setup_info.codec_id) {
>> +                    break;
>> +                }
>> +        } else {
>>         pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 * 4;
>>         pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ?
>> s->max_analyze_duration : 4 * AV_TIME_BASE;
>>         pls->ctx->interrupt_callback = s->interrupt_callback;
>> @@ -1999,6 +2045,25 @@ static int hls_read_header(AVFormatContext *s)
>>             goto fail;
>>         }
>>         av_free(url);
>> +        }
>> +
>> +        if (seg && seg->key_type == KEY_SAMPLE_AES) {
>> +            if (!pls->is_id3_timestamped && pls->n_renditions > 0 &&
>> pls->renditions[0]->type != AVMEDIA_TYPE_AUDIO &&
>> +                strcmp(in_fmt->name, "mpegts")) {
>> +                av_log(s, AV_LOG_ERROR, "SAMPLE-AES encryption is not
>> supported for fragmented MP4 format yet\n");
>> +                ret = AVERROR_PATCHWELCOME;
>> +            } else {
>> +                pls->crypto_ctx.aes_ctx = av_aes_alloc();
>> +                if (!pls->crypto_ctx.aes_ctx)
>> +                    ret = AVERROR(ENOMEM);
>> +            }
>> +            if (ret != 0) {
>> +                avformat_free_context(pls->ctx);
>> +                pls->ctx = NULL;
>> +                goto fail;
>> +            }
>> +        }
>> +
>>         pls->ctx->pb       = &pls->pb;
>>         pls->ctx->io_open  = nested_io_open;
>>         pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
>> @@ -2027,7 +2092,12 @@ static int hls_read_header(AVFormatContext *s)
>>          * on us if they want to.
>>          */
>>         if (pls->is_id3_timestamped || (pls->n_renditions > 0 &&
>> pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO)) {
>> +            if (seg && seg->key_type == KEY_SAMPLE_AES &&
>> pls->audio_setup_info.setup_data_length > 0 &&
>> +                pls->ctx->nb_streams == 1)
>> +                ret = ff_hls_parse_audio_setup_info(pls->ctx->streams[0],
>> &pls->audio_setup_info);
>> +            else
>>             ret = avformat_find_stream_info(pls->ctx, NULL);
>> +
>>             if (ret < 0)
>>                 goto fail;
>>         }
>> @@ -2157,6 +2227,7 @@ static int hls_read_packet(AVFormatContext *s,
>> AVPacket *pkt)
>>             while (1) {
>>                 int64_t ts_diff;
>>                 AVRational tb;
>> +                struct segment *seg = NULL;
>>                 ret = av_read_frame(pls->ctx, &pls->pkt);
>>                 if (ret < 0) {
>>                     if (!avio_feof(&pls->pb) && ret != AVERROR_EOF)
>> @@ -2175,6 +2246,14 @@ static int hls_read_packet(AVFormatContext *s,
>> AVPacket *pkt)
>>                             get_timebase(pls), AV_TIME_BASE_Q);
>>                 }
>> 
>> +                seg = current_segment(pls);
>> +                if (seg && seg->key_type == KEY_SAMPLE_AES) {
>> +                    enum AVCodecID codec_id =
>> pls->ctx->streams[pls->pkt.stream_index]->codecpar->codec_id;
>> +                    memcpy(pls->crypto_ctx.iv, seg->iv, sizeof(seg->iv));
>> +                    memcpy(pls->crypto_ctx.key, pls->key,
>> sizeof(pls->key));
>> +                    ff_hls_decrypt_frame(codec_id, &pls->crypto_ctx,
>> &pls->pkt);
>> +                }
>> +
>>                 if (pls->seek_timestamp == AV_NOPTS_VALUE)
>>                     break;
>> 
>> diff --git a/libavformat/hls_sample_aes.c b/libavformat/hls_sample_aes.c
>> new file mode 100644
>> index 0000000000..0407a15b0f
>> --- /dev/null
>> +++ b/libavformat/hls_sample_aes.c
>> @@ -0,0 +1,391 @@
>> +/*
>> + * Apple HTTP Live Streaming Sample Encryption/Decryption
>> + *
>> + * Copyright (c) 2021 Nachiket Tarate
>> + *
>> + * This file is part of FFmpeg.
>> + *
>> + * FFmpeg is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * FFmpeg is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with FFmpeg; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>> 02110-1301 USA
>> + */
>> +
>> +/**
>> + * @file
>> + * Apple HTTP Live Streaming Sample Encryption
>> + *
>> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
>> + */
>> +
>> +#include "hls_sample_aes.h"
>> +
>> +#include "libavcodec/adts_header.h"
>> +#include "libavcodec/adts_parser.h"
>> +#include "libavcodec/ac3_parser_internal.h"
>> +
>> +
>> +typedef struct NALUnit {
>> +    uint8_t     *data;
>> +    int         type;
>> +    int         length;
>> +    int         start_code_length;
>> +} NALUnit;
>> +
>> +typedef struct AudioFrame {
>> +    uint8_t     *data;
>> +    int         length;
>> +    int         header_length;
>> +} AudioFrame;
>> +
>> +typedef struct CodecParserContext {
>> +    const uint8_t   *buf_ptr;
>> +    const uint8_t   *buf_end;
>> +} CodecParserContext;
>> +
>> +static const int eac3_sample_rate_tab[] = { 48000, 44100, 32000, 0 };
>> +
>> +void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const uint8_t
>> *buf, size_t size)
>> +{
>> +    if (size < 8)
>> +        return;
>> +
>> +    info->codec_tag             = AV_RL32(buf);
>> +
>> +    if (info->codec_tag == MKTAG('z','a', 'a', 'c'))
>> +        info->codec_id = AV_CODEC_ID_AAC;
>> +    else if (info->codec_tag == MKTAG('z','a', 'c', '3'))
>> +        info->codec_id = AV_CODEC_ID_AC3;
>> +    else if (info->codec_tag == MKTAG('z','e', 'c', '3'))
>> +        info->codec_id = AV_CODEC_ID_EAC3;
>> +    else
>> +        info->codec_id = AV_CODEC_ID_NONE;
>> +
>> +    buf += 4;
>> +    info->priming               = AV_RL16(buf);
>> +    buf += 2;
>> +    info->version               = *buf++;
>> +    info->setup_data_length     = *buf++;
>> +
>> +    if (info->setup_data_length > size - 8)
>> +        info->setup_data_length = size - 8;
>> +
>> +    if (info->setup_data_length > HLS_MAX_AUDIO_SETUP_DATA_LEN)
>> +        return;
>> +
>> +    memcpy(info->setup_data, buf, info->setup_data_length);
>> +}
>> +
>> +int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo *info)
>> +{
>> +    int ret = 0;
>> +
>> +    st->codecpar->codec_tag = info->codec_tag;
>> +
>> +    if (st->codecpar->codec_id == AV_CODEC_ID_AAC)
>> +        return 0;
>> +
>> +    if (st->codecpar->codec_id != AV_CODEC_ID_AC3 &&
>> st->codecpar->codec_id != AV_CODEC_ID_EAC3)
>> +        return AVERROR_INVALIDDATA;
>> +
>> +    if (st->codecpar->codec_id == AV_CODEC_ID_AC3) {
>> +
>> +        AC3HeaderInfo *ac3hdr = NULL;
>> +
>> +        ret = avpriv_ac3_parse_header(&ac3hdr, info->setup_data,
>> info->setup_data_length);
>> +        if (ret < 0) {
>> +            if (ret != AVERROR(ENOMEM))
>> +                av_free(ac3hdr);
>> +            return ret;
>> +        }
>> +
>> +        st->codecpar->sample_rate       = ac3hdr->sample_rate;
>> +        st->codecpar->channels          = ac3hdr->channels;
>> +        st->codecpar->channel_layout    = ac3hdr->channel_layout;
>> +        st->codecpar->bit_rate          = ac3hdr->bit_rate;
>> +
>> +        av_free(ac3hdr);
>> +    } else {  /*  Parse 'dec3' EC3SpecificBox */
>> +
>> +        GetBitContext gb;
>> +        int data_rate, fscod, acmod, lfeon;
>> +
>> +        ret = init_get_bits8(&gb, info->setup_data,
>> info->setup_data_length);
>> +        if (ret < 0)
>> +            return AVERROR_INVALIDDATA;
>> +
>> +        data_rate = get_bits(&gb, 13);
>> +        skip_bits(&gb, 3);
>> +        fscod = get_bits(&gb, 2);
>> +        skip_bits(&gb, 10);
>> +        acmod = get_bits(&gb, 3);
>> +        lfeon = get_bits(&gb, 1);
>> +
>> +        st->codecpar->sample_rate = eac3_sample_rate_tab[fscod];
>> +
>> +        st->codecpar->channel_layout =
>> avpriv_ac3_channel_layout_tab[acmod];
>> +        if (lfeon)
>> +            st->codecpar->channel_layout |= AV_CH_LOW_FREQUENCY;
>> +
>> +        st->codecpar->channels =
>> av_get_channel_layout_nb_channels(st->codecpar->channel_layout);
>> +
>> +        st->codecpar->bit_rate = data_rate*1000;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +/*
>> + * Remove start code emulation prevention 0x03 bytes
>> + */
>> +static void remove_scep_3_bytes(NALUnit *nalu)
>> +{
>> +    int i = 0;
>> +    int j = 0;
>> +
>> +    uint8_t *data = nalu->data;
>> +
>> +    while (i < nalu->length) {
>> +        if (nalu->length - i > 3 && AV_RB24(&data[i]) == 0x000003) {
>> +            data[j++] = data[i++];
>> +            data[j++] = data[i++];
>> +            i++;
>> +        } else {
>> +            data[j++] = data[i++];
>> +        }
>> +    }
>> +
>> +    nalu->length = j;
>> +}
>> +
>> +static int get_next_nal_unit(CodecParserContext *ctx, NALUnit *nalu)
>> +{
>> +    const uint8_t *nalu_start = ctx->buf_ptr;
>> +
>> +    if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr) ==
>> 0x00000001)
>> +        nalu->start_code_length = 4;
>> +    else if (ctx->buf_end - ctx->buf_ptr >= 3 && AV_RB24(ctx->buf_ptr) ==
>> 0x000001)
>> +        nalu->start_code_length = 3;
>> +    else /* No start code at the beginning of the NAL unit */
>> +        return -1;
>> +
>> +    ctx->buf_ptr += nalu->start_code_length;
>> +
>> +    while (ctx->buf_ptr < ctx->buf_end) {
>> +        if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr) ==
>> 0x00000001)
>> +            break;
>> +        else if (ctx->buf_end - ctx->buf_ptr >= 3 &&
>> AV_RB24(ctx->buf_ptr) == 0x000001)
>> +            break;
>> +        ctx->buf_ptr++;
>> +    }
>> +
>> +    nalu->data  = (uint8_t *)nalu_start + nalu->start_code_length;
>> +    nalu->length = ctx->buf_ptr - nalu->data;
>> +    nalu->type  = *nalu->data & 0x1F;
>> +
>> +    return 0;
>> +}
>> +
>> +static int decrypt_nal_unit(HLSCryptoContext *crypto_ctx, NALUnit *nalu)
>> +{
>> +    int ret = 0;
>> +    int rem_bytes;
>> +    uint8_t *data;
>> +    uint8_t iv[16];
>> +
>> +    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    /* Remove start code emulation prevention 0x03 bytes */
>> +    remove_scep_3_bytes(nalu);
>> +
>> +    data = nalu->data + 32;
>> +    rem_bytes = nalu->length - 32;
>> +
>> +    memcpy(iv, crypto_ctx->iv, 16);
>> +
>> +    while (rem_bytes > 0) {
>> +        if (rem_bytes > 16) {
>> +            av_aes_crypt(crypto_ctx->aes_ctx, data, data, 1, iv, 1);
>> +            data += 16;
>> +            rem_bytes -= 16;
>> +        }
>> +        data += FFMIN(144, rem_bytes);
>> +        rem_bytes -= FFMIN(144, rem_bytes);
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int decrypt_video_frame(HLSCryptoContext *crypto_ctx, AVPacket
>> *pkt)
>> +{
>> +    int ret = 0;
>> +    CodecParserContext  ctx;
>> +    NALUnit nalu;
>> +    uint8_t *data_ptr;
>> +    int move_nalu = 0;
>> +
>> +    memset(&ctx, 0, sizeof(ctx));
>> +    ctx.buf_ptr  = pkt->data;
>> +    ctx.buf_end = pkt->data + pkt->size;
>> +
>> +    data_ptr = pkt->data;
>> +
>> +    while (ctx.buf_ptr < ctx.buf_end) {
>> +        memset(&nalu, 0, sizeof(nalu));
>> +        ret = get_next_nal_unit(&ctx, &nalu);
>> +        if (ret < 0)
>> +            return ret;
>> +        if ((nalu.type == 0x01 || nalu.type == 0x05) && nalu.length > 48)
>> {
>> +            int encrypted_nalu_length = nalu.length;
>> +            ret = decrypt_nal_unit(crypto_ctx, &nalu);
>> +            if (ret < 0)
>> +                return ret;
>> +            move_nalu = nalu.length != encrypted_nalu_length;
>> +        }
>> +        if (move_nalu)
>> +            memmove(data_ptr, nalu.data - nalu.start_code_length,
>> nalu.start_code_length + nalu.length);
>> +        data_ptr += nalu.start_code_length + nalu.length;
>> +    }
>> +
>> +    av_shrink_packet(pkt, data_ptr - pkt->data);
>> +
>> +    return 0;
>> +}
>> +
>> +static int get_next_adts_frame(CodecParserContext *ctx, AudioFrame *frame)
>> +{
>> +    int ret = 0;
>> +
>> +    AACADTSHeaderInfo *adts_hdr = NULL;
>> +
>> +    /* Find next sync word 0xFFF */
>> +    while (ctx->buf_ptr < ctx->buf_end - 1) {
>> +        if (*ctx->buf_ptr == 0xFF && *(ctx->buf_ptr + 1) & 0xF0 == 0xF0)
>> +            break;
>> +        ctx->buf_ptr++;
>> +    }
>> +
>> +    if (ctx->buf_ptr >= ctx->buf_end - 1)
>> +        return -1;
>> +
>> +    frame->data = (uint8_t*)ctx->buf_ptr;
>> +
>> +    ret = avpriv_adts_header_parse (&adts_hdr, frame->data, ctx->buf_end
>> - frame->data);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    frame->header_length = adts_hdr->crc_absent ? AV_AAC_ADTS_HEADER_SIZE
>> : AV_AAC_ADTS_HEADER_SIZE + 2;
>> +    frame->length = adts_hdr->frame_length;
>> +
>> +    av_free(adts_hdr);
>> +
>> +    return 0;
>> +}
>> +
>> +static int get_next_ac3_eac3_sync_frame(CodecParserContext *ctx,
>> AudioFrame *frame)
>> +{
>> +    int ret = 0;
>> +
>> +    AC3HeaderInfo *hdr = NULL;
>> +
>> +    /* Find next sync word 0x0B77 */
>> +    while (ctx->buf_ptr < ctx->buf_end - 1) {
>> +        if (*ctx->buf_ptr == 0x0B && *(ctx->buf_ptr + 1) == 0x77)
>> +            break;
>> +        ctx->buf_ptr++;
>> +    }
>> +
>> +    if (ctx->buf_ptr >= ctx->buf_end - 1)
>> +        return -1;
>> +
>> +    frame->data = (uint8_t*)ctx->buf_ptr;
>> +    frame->header_length = 0;
>> +
>> +    ret = avpriv_ac3_parse_header(&hdr, frame->data, ctx->buf_end -
>> frame->data);
>> +    if (ret < 0) {
>> +        if (ret != AVERROR(ENOMEM))
>> +            av_free(hdr);
>> +        return ret;
>> +    }
>> +
>> +    frame->length = hdr->frame_size;
>> +
>> +    av_free(hdr);
>> +
>> +    return 0;
>> +}
>> +
>> +static int get_next_sync_frame(enum AVCodecID codec_id,
>> CodecParserContext *ctx, AudioFrame *frame)
>> +{
>> +    if (codec_id == AV_CODEC_ID_AAC)
>> +        return get_next_adts_frame(ctx, frame);
>> +    else if (codec_id == AV_CODEC_ID_AC3 || codec_id == AV_CODEC_ID_EAC3)
>> +        return get_next_ac3_eac3_sync_frame(ctx, frame);
>> +    else
>> +        return AVERROR_INVALIDDATA;
>> +}
>> +
>> +static int decrypt_sync_frame(enum AVCodecID codec_id, HLSCryptoContext
>> *crypto_ctx, AudioFrame *frame)
>> +{
>> +    int ret = 0;
>> +    uint8_t *data;
>> +    int num_of_encrypted_blocks;
>> +
>> +    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    data = frame->data + frame->header_length + 16;
>> +
>> +    num_of_encrypted_blocks = (frame->length - frame->header_length -
>> 16)/16;
>> +
>> +    av_aes_crypt(crypto_ctx->aes_ctx, data, data,
>> num_of_encrypted_blocks, crypto_ctx->iv, 1);
>> +
>> +    return 0;
>> +}
>> +
>> +static int decrypt_audio_frame(enum AVCodecID codec_id, HLSCryptoContext
>> *crypto_ctx, AVPacket *pkt)
>> +{
>> +    int ret = 0;
>> +    CodecParserContext  ctx;
>> +    AudioFrame frame;
>> +
>> +    memset(&ctx, 0, sizeof(ctx));
>> +    ctx.buf_ptr        = pkt->data;
>> +    ctx.buf_end = pkt->data + pkt->size;
>> +
>> +    while (ctx.buf_ptr < ctx.buf_end) {
>> +        memset(&frame, 0, sizeof(frame));
>> +        ret = get_next_sync_frame(codec_id, &ctx, &frame);
>> +        if (ret < 0)
>> +            return ret;
>> +        if (frame.length - frame.header_length > 31) {
>> +            ret = decrypt_sync_frame(codec_id, crypto_ctx, &frame);
>> +            if (ret < 0)
>> +                return ret;
>> +        }
>> +        ctx.buf_ptr += frame.length;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext
>> *crypto_ctx, AVPacket *pkt)
>> +{
>> +    if (codec_id == AV_CODEC_ID_H264)
>> +        return decrypt_video_frame(crypto_ctx, pkt);
>> +    else if (codec_id == AV_CODEC_ID_AAC || codec_id == AV_CODEC_ID_AC3
>> || codec_id == AV_CODEC_ID_EAC3)
>> +        return decrypt_audio_frame(codec_id, crypto_ctx, pkt);
>> +
>> +    return AVERROR_INVALIDDATA;
>> +}
>> diff --git a/libavformat/hls_sample_aes.h b/libavformat/hls_sample_aes.h
>> new file mode 100644
>> index 0000000000..cf80e41cb0
>> --- /dev/null
>> +++ b/libavformat/hls_sample_aes.h
>> @@ -0,0 +1,66 @@
>> +/*
>> + * Apple HTTP Live Streaming Sample Encryption/Decryption
>> + *
>> + * Copyright (c) 2021 Nachiket Tarate
>> + *
>> + * This file is part of FFmpeg.
>> + *
>> + * FFmpeg is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * FFmpeg is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with FFmpeg; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>> 02110-1301 USA
>> + */
>> +
>> +/**
>> + * @file
>> + * Apple HTTP Live Streaming Sample Encryption
>> + *
>> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
>> + */
>> +
>> +#ifndef AVFORMAT_HLS_SAMPLE_AES_H
>> +#define AVFORMAT_HLS_SAMPLE_AES_H
>> +
>> +#include <stdint.h>
>> +
>> +#include "avformat.h"
>> +
>> +#include "libavcodec/avcodec.h"
>> +#include "libavutil/aes.h"
>> +
>> +#define HLS_MAX_ID3_TAGS_DATA_LEN       138
>> +#define HLS_MAX_AUDIO_SETUP_DATA_LEN    10
>> +
>> +
>> +typedef struct HLSCryptoContext {
>> +    struct AVAES   *aes_ctx;
>> +    uint8_t            key[16];
>> +    uint8_t            iv[16];
>> +} HLSCryptoContext;
>> +
>> +typedef struct HLSAudioSetupInfo {
>> +    enum AVCodecID      codec_id;
>> +    uint32_t            codec_tag;
>> +    uint16_t            priming;
>> +    uint8_t             version;
>> +    uint8_t             setup_data_length;
>> +    uint8_t             setup_data[HLS_MAX_AUDIO_SETUP_DATA_LEN];
>> +} HLSAudioSetupInfo;
>> +
>> +
>> +void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const uint8_t
>> *buf, size_t size);
>> +
>> +int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo *info);
>> +
>> +int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext
>> *crypto_ctx, AVPacket *pkt);
>> +
>> +#endif /* AVFORMAT_HLS_SAMPLE_AES_H */
>> +
>> diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
>> index e283ec09d7..dc611ae788 100644
>> --- a/libavformat/mpegts.c
>> +++ b/libavformat/mpegts.c
>> @@ -839,6 +839,16 @@ static const StreamType MISC_types[] = {
>>     { 0 },
>> };
>> 
>> +/* HLS Sample Encryption Types  */
>> +static const StreamType HLS_SAMPLE_ENC_types[] = {
>> +    { 0xdb, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264},
>> +    { 0xcf, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
>> +    { 0xc1, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 },
>> +    { 0xc2, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3},
>> +    { 0 },
>> +};
>> +
>> +
>> static const StreamType REGD_types[] = {
>>     { MKTAG('d', 'r', 'a', 'c'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC },
>>     { MKTAG('A', 'C', '-', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3   },
>> @@ -948,6 +958,8 @@ static int mpegts_set_stream_info(AVStream *st,
>> PESContext *pes,
>>     }
>>     if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
>>         mpegts_find_stream_type(st, pes->stream_type, MISC_types);
>> +    if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
>> +        mpegts_find_stream_type(st, pes->stream_type,
>> HLS_SAMPLE_ENC_types);
>>     if (st->codecpar->codec_id == AV_CODEC_ID_NONE) {
>>         st->codecpar->codec_id  = old_codec_id;
>>         st->codecpar->codec_type = old_codec_type;
>> --
>> 2.17.1
>> 
>> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

Thanks

Steven Liu
Nachiket Tarate March 1, 2021, 4:55 a.m. UTC | #3
This is an updated version of the patch in which I have added the check. If
the segments are in Fragmented MP4 format, HLS demuxer quits by giving an
error message:

"SAMPLE-AES encryption is not supported for fragmented MP4 format yet"

Best Regards,
Nachiket Tarate

On Mon, Mar 1, 2021 at 10:13 AM Steven Liu <lq@chinaffmpeg.org> wrote:

>
>
> > 2021年3月1日 下午12:35,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
> >
> > @Steven Liu <lq@chinaffmpeg.org>
> >
> > Can we merge this patch ?
> I’m waiting update patch for fragment mp4 encryption.
> After new version of the patchset I will test and review.
> >
> > Best Regards,
> > Nachiket Tarate
> >
> > On Wed, Feb 24, 2021 at 4:44 PM Nachiket Tarate <
> > nachiket.programmer@gmail.com> wrote:
> >
> >> Apple HTTP Live Streaming Sample Encryption:
> >>
> >>
> >>
> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> >>
> >> Signed-off-by: Nachiket Tarate <nachiket.programmer@gmail.com>
> >> ---
> >> libavformat/Makefile         |   2 +-
> >> libavformat/hls.c            | 105 ++++++++--
> >> libavformat/hls_sample_aes.c | 391 +++++++++++++++++++++++++++++++++++
> >> libavformat/hls_sample_aes.h |  66 ++++++
> >> libavformat/mpegts.c         |  12 ++
> >> 5 files changed, 562 insertions(+), 14 deletions(-)
> >> create mode 100644 libavformat/hls_sample_aes.c
> >> create mode 100644 libavformat/hls_sample_aes.h
> >>
> >> diff --git a/libavformat/Makefile b/libavformat/Makefile
> >> index fcb39ce133..62627d50a6 100644
> >> --- a/libavformat/Makefile
> >> +++ b/libavformat/Makefile
> >> @@ -236,7 +236,7 @@ OBJS-$(CONFIG_HCOM_DEMUXER)              += hcom.o
> >> pcm.o
> >> OBJS-$(CONFIG_HDS_MUXER)                 += hdsenc.o
> >> OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
> >> OBJS-$(CONFIG_HEVC_MUXER)                += rawenc.o
> >> -OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o
> >> +OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o hls_sample_aes.o
> >> OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o hlsplaylist.o avc.o
> >> OBJS-$(CONFIG_HNM_DEMUXER)               += hnm.o
> >> OBJS-$(CONFIG_ICO_DEMUXER)               += icodec.o
> >> diff --git a/libavformat/hls.c b/libavformat/hls.c
> >> index af2468ad9b..3cb3853c79 100644
> >> --- a/libavformat/hls.c
> >> +++ b/libavformat/hls.c
> >> @@ -2,6 +2,7 @@
> >>  * Apple HTTP Live Streaming demuxer
> >>  * Copyright (c) 2010 Martin Storsjo
> >>  * Copyright (c) 2013 Anssi Hannula
> >> + * Copyright (c) 2021 Nachiket Tarate
> >>  *
> >>  * This file is part of FFmpeg.
> >>  *
> >> @@ -39,6 +40,8 @@
> >> #include "avio_internal.h"
> >> #include "id3v2.h"
> >>
> >> +#include "hls_sample_aes.h"
> >> +
> >> #define INITIAL_BUFFER_SIZE 32768
> >>
> >> #define MAX_FIELD_LEN 64
> >> @@ -145,6 +148,10 @@ struct playlist {
> >>     int id3_changed; /* ID3 tag data has changed at some point */
> >>     ID3v2ExtraMeta *id3_deferred_extra; /* stored here until subdemuxer
> >> is opened */
> >>
> >> +    /* Used in case of SAMPLE-AES encryption method */
> >> +    HLSAudioSetupInfo audio_setup_info;
> >> +    HLSCryptoContext  crypto_ctx;
> >> +
> >>     int64_t seek_timestamp;
> >>     int seek_flags;
> >>     int seek_stream_index; /* into subdemuxer stream array */
> >> @@ -266,6 +273,8 @@ static void free_playlist_list(HLSContext *c)
> >>             pls->ctx->pb = NULL;
> >>             avformat_close_input(&pls->ctx);
> >>         }
> >> +        if (pls->crypto_ctx.aes_ctx)
> >> +             av_free(pls->crypto_ctx.aes_ctx);
> >>         av_free(pls);
> >>     }
> >>     av_freep(&c->playlists);
> >> @@ -994,7 +1003,10 @@ fail:
> >>
> >> static struct segment *current_segment(struct playlist *pls)
> >> {
> >> -    return pls->segments[pls->cur_seq_no - pls->start_seq_no];
> >> +    int64_t n = pls->cur_seq_no - pls->start_seq_no;
> >> +    if (n >= pls->n_segments)
> >> +        return NULL;
> >> +    return pls->segments[n];
> >> }
> >>
> >> static struct segment *next_segment(struct playlist *pls)
> >> @@ -1023,10 +1035,11 @@ static int read_from_url(struct playlist *pls,
> >> struct segment *seg,
> >>
> >> /* Parse the raw ID3 data and pass contents to caller */
> >> static void parse_id3(AVFormatContext *s, AVIOContext *pb,
> >> -                      AVDictionary **metadata, int64_t *dts,
> >> +                      AVDictionary **metadata, int64_t *dts,
> >> HLSAudioSetupInfo *audio_setup_info,
> >>                       ID3v2ExtraMetaAPIC **apic, ID3v2ExtraMeta
> >> **extra_meta)
> >> {
> >>     static const char id3_priv_owner_ts[] =
> >> "com.apple.streaming.transportStreamTimestamp";
> >> +    static const char id3_priv_owner_audio_setup[] =
> >> "com.apple.streaming.audioDescription";
> >>     ID3v2ExtraMeta *meta;
> >>
> >>     ff_id3v2_read_dict(pb, metadata, ID3v2_DEFAULT_MAGIC, extra_meta);
> >> @@ -1041,6 +1054,8 @@ static void parse_id3(AVFormatContext *s,
> >> AVIOContext *pb,
> >>                     *dts = ts;
> >>                 else
> >>                     av_log(s, AV_LOG_ERROR, "Invalid HLS ID3 audio
> >> timestamp %"PRId64"\n", ts);
> >> +            } else if (priv->datasize >= 8 && !strcmp(priv->owner,
> >> id3_priv_owner_audio_setup)) {
> >> +                ff_hls_read_audio_setup_info(audio_setup_info,
> >> priv->data, priv->datasize);
> >>             }
> >>         } else if (!strcmp(meta->tag, "APIC") && apic)
> >>             *apic = &meta->data.apic;
> >> @@ -1084,7 +1099,7 @@ static void handle_id3(AVIOContext *pb, struct
> >> playlist *pls)
> >>     ID3v2ExtraMeta *extra_meta = NULL;
> >>     int64_t timestamp = AV_NOPTS_VALUE;
> >>
> >> -    parse_id3(pls->ctx, pb, &metadata, &timestamp, &apic, &extra_meta);
> >> +    parse_id3(pls->ctx, pb, &metadata, &timestamp,
> >> &pls->audio_setup_info, &apic, &extra_meta);
> >>
> >>     if (timestamp != AV_NOPTS_VALUE) {
> >>         pls->id3_mpegts_timestamp = timestamp;
> >> @@ -1238,10 +1253,7 @@ static int open_input(HLSContext *c, struct
> >> playlist *pls, struct segment *seg,
> >>     av_log(pls->parent, AV_LOG_VERBOSE, "HLS request for url '%s',
> offset
> >> %"PRId64", playlist %d\n",
> >>            seg->url, seg->url_offset, pls->index);
> >>
> >> -    if (seg->key_type == KEY_NONE) {
> >> -        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
> >> &is_http);
> >> -    } else if (seg->key_type == KEY_AES_128) {
> >> -        char iv[33], key[33], url[MAX_URL_SIZE];
> >> +    if (seg->key_type == KEY_AES_128 || seg->key_type ==
> KEY_SAMPLE_AES) {
> >>         if (strcmp(seg->key, pls->key_url)) {
> >>             AVIOContext *pb = NULL;
> >>             if (open_url(pls->parent, &pb, seg->key, &c->avio_opts,
> opts,
> >> NULL) == 0) {
> >> @@ -1257,6 +1269,10 @@ static int open_input(HLSContext *c, struct
> >> playlist *pls, struct segment *seg,
> >>             }
> >>             av_strlcpy(pls->key_url, seg->key, sizeof(pls->key_url));
> >>         }
> >> +    }
> >> +
> >> +    if (seg->key_type == KEY_AES_128) {
> >> +        char iv[33], key[33], url[MAX_URL_SIZE];
> >>         ff_data_to_hex(iv, seg->iv, sizeof(seg->iv), 0);
> >>         ff_data_to_hex(key, pls->key, sizeof(pls->key), 0);
> >>         iv[32] = key[32] = '\0';
> >> @@ -1273,13 +1289,9 @@ static int open_input(HLSContext *c, struct
> >> playlist *pls, struct segment *seg,
> >>             goto cleanup;
> >>         }
> >>         ret = 0;
> >> -    } else if (seg->key_type == KEY_SAMPLE_AES) {
> >> -        av_log(pls->parent, AV_LOG_ERROR,
> >> -               "SAMPLE-AES encryption is not supported yet\n");
> >> -        ret = AVERROR_PATCHWELCOME;
> >> +    } else {
> >> +        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
> >> &is_http);
> >>     }
> >> -    else
> >> -      ret = AVERROR(ENOSYS);
> >>
> >>     /* Seek to the requested position. If this was a HTTP request, the
> >> offset
> >>      * should already be where want it to, but this allows e.g. local
> >> testing
> >> @@ -1948,6 +1960,7 @@ static int hls_read_header(AVFormatContext *s)
> >>         struct playlist *pls = c->playlists[i];
> >>         char *url;
> >>         ff_const59 AVInputFormat *in_fmt = NULL;
> >> +        struct segment *seg = NULL;
> >>
> >>         if (!(pls->ctx = avformat_alloc_context())) {
> >>             ret = AVERROR(ENOMEM);
> >> @@ -1980,8 +1993,41 @@ static int hls_read_header(AVFormatContext *s)
> >>             pls->ctx = NULL;
> >>             goto fail;
> >>         }
> >> +
> >>         ffio_init_context(&pls->pb, pls->read_buffer,
> >> INITIAL_BUFFER_SIZE, 0, pls,
> >>                           read_data, NULL, NULL);
> >> +
> >> +        /*
> >> +         * If encryption scheme is SAMPLE-AES, try to read  ID3 tags of
> >> +         * external audio track that contains audio setup information
> >> +         */
> >> +        seg = current_segment(pls);
> >> +        if (seg && seg->key_type == KEY_SAMPLE_AES &&
> pls->n_renditions >
> >> 0 &&
> >> +            pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO) {
> >> +            uint8_t buf[HLS_MAX_ID3_TAGS_DATA_LEN];
> >> +            if ((ret = avio_read(&pls->pb, buf,
> >> HLS_MAX_ID3_TAGS_DATA_LEN)) < 0) {
> >> +                /* Fail if error was not end of file */
> >> +                if (ret != AVERROR_EOF) {
> >> +                    avformat_free_context(pls->ctx);
> >> +                    pls->ctx = NULL;
> >> +                    goto fail;
> >> +                }
> >> +            }
> >> +            ret = 0;
> >> +        }
> >> +
> >> +        /*
> >> +         * If encryption scheme is SAMPLE-AES and audio setup
> information
> >> is present in external audio track,
> >> +         * use that information to find the media format, otherwise
> probe
> >> input data
> >> +         */
> >> +        if (seg && seg->key_type == KEY_SAMPLE_AES &&
> >> pls->is_id3_timestamped &&
> >> +            pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) {
> >> +            void *iter = NULL;
> >> +            while ((in_fmt = (ff_const59 AVInputFormat
> >> *)av_demuxer_iterate(&iter)))
> >> +                if (in_fmt->raw_codec_id ==
> >> pls->audio_setup_info.codec_id) {
> >> +                    break;
> >> +                }
> >> +        } else {
> >>         pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 *
> 4;
> >>         pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ?
> >> s->max_analyze_duration : 4 * AV_TIME_BASE;
> >>         pls->ctx->interrupt_callback = s->interrupt_callback;
> >> @@ -1999,6 +2045,25 @@ static int hls_read_header(AVFormatContext *s)
> >>             goto fail;
> >>         }
> >>         av_free(url);
> >> +        }
> >> +
> >> +        if (seg && seg->key_type == KEY_SAMPLE_AES) {
> >> +            if (!pls->is_id3_timestamped && pls->n_renditions > 0 &&
> >> pls->renditions[0]->type != AVMEDIA_TYPE_AUDIO &&
> >> +                strcmp(in_fmt->name, "mpegts")) {
> >> +                av_log(s, AV_LOG_ERROR, "SAMPLE-AES encryption is not
> >> supported for fragmented MP4 format yet\n");
> >> +                ret = AVERROR_PATCHWELCOME;
> >> +            } else {
> >> +                pls->crypto_ctx.aes_ctx = av_aes_alloc();
> >> +                if (!pls->crypto_ctx.aes_ctx)
> >> +                    ret = AVERROR(ENOMEM);
> >> +            }
> >> +            if (ret != 0) {
> >> +                avformat_free_context(pls->ctx);
> >> +                pls->ctx = NULL;
> >> +                goto fail;
> >> +            }
> >> +        }
> >> +
> >>         pls->ctx->pb       = &pls->pb;
> >>         pls->ctx->io_open  = nested_io_open;
> >>         pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
> >> @@ -2027,7 +2092,12 @@ static int hls_read_header(AVFormatContext *s)
> >>          * on us if they want to.
> >>          */
> >>         if (pls->is_id3_timestamped || (pls->n_renditions > 0 &&
> >> pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO)) {
> >> +            if (seg && seg->key_type == KEY_SAMPLE_AES &&
> >> pls->audio_setup_info.setup_data_length > 0 &&
> >> +                pls->ctx->nb_streams == 1)
> >> +                ret =
> ff_hls_parse_audio_setup_info(pls->ctx->streams[0],
> >> &pls->audio_setup_info);
> >> +            else
> >>             ret = avformat_find_stream_info(pls->ctx, NULL);
> >> +
> >>             if (ret < 0)
> >>                 goto fail;
> >>         }
> >> @@ -2157,6 +2227,7 @@ static int hls_read_packet(AVFormatContext *s,
> >> AVPacket *pkt)
> >>             while (1) {
> >>                 int64_t ts_diff;
> >>                 AVRational tb;
> >> +                struct segment *seg = NULL;
> >>                 ret = av_read_frame(pls->ctx, &pls->pkt);
> >>                 if (ret < 0) {
> >>                     if (!avio_feof(&pls->pb) && ret != AVERROR_EOF)
> >> @@ -2175,6 +2246,14 @@ static int hls_read_packet(AVFormatContext *s,
> >> AVPacket *pkt)
> >>                             get_timebase(pls), AV_TIME_BASE_Q);
> >>                 }
> >>
> >> +                seg = current_segment(pls);
> >> +                if (seg && seg->key_type == KEY_SAMPLE_AES) {
> >> +                    enum AVCodecID codec_id =
> >> pls->ctx->streams[pls->pkt.stream_index]->codecpar->codec_id;
> >> +                    memcpy(pls->crypto_ctx.iv, seg->iv,
> sizeof(seg->iv));
> >> +                    memcpy(pls->crypto_ctx.key, pls->key,
> >> sizeof(pls->key));
> >> +                    ff_hls_decrypt_frame(codec_id, &pls->crypto_ctx,
> >> &pls->pkt);
> >> +                }
> >> +
> >>                 if (pls->seek_timestamp == AV_NOPTS_VALUE)
> >>                     break;
> >>
> >> diff --git a/libavformat/hls_sample_aes.c b/libavformat/hls_sample_aes.c
> >> new file mode 100644
> >> index 0000000000..0407a15b0f
> >> --- /dev/null
> >> +++ b/libavformat/hls_sample_aes.c
> >> @@ -0,0 +1,391 @@
> >> +/*
> >> + * Apple HTTP Live Streaming Sample Encryption/Decryption
> >> + *
> >> + * Copyright (c) 2021 Nachiket Tarate
> >> + *
> >> + * This file is part of FFmpeg.
> >> + *
> >> + * FFmpeg is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU Lesser General Public
> >> + * License as published by the Free Software Foundation; either
> >> + * version 2.1 of the License, or (at your option) any later version.
> >> + *
> >> + * FFmpeg is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> >> + * Lesser General Public License for more details.
> >> + *
> >> + * You should have received a copy of the GNU Lesser General Public
> >> + * License along with FFmpeg; if not, write to the Free Software
> >> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> >> 02110-1301 USA
> >> + */
> >> +
> >> +/**
> >> + * @file
> >> + * Apple HTTP Live Streaming Sample Encryption
> >> + *
> >>
> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> >> + */
> >> +
> >> +#include "hls_sample_aes.h"
> >> +
> >> +#include "libavcodec/adts_header.h"
> >> +#include "libavcodec/adts_parser.h"
> >> +#include "libavcodec/ac3_parser_internal.h"
> >> +
> >> +
> >> +typedef struct NALUnit {
> >> +    uint8_t     *data;
> >> +    int         type;
> >> +    int         length;
> >> +    int         start_code_length;
> >> +} NALUnit;
> >> +
> >> +typedef struct AudioFrame {
> >> +    uint8_t     *data;
> >> +    int         length;
> >> +    int         header_length;
> >> +} AudioFrame;
> >> +
> >> +typedef struct CodecParserContext {
> >> +    const uint8_t   *buf_ptr;
> >> +    const uint8_t   *buf_end;
> >> +} CodecParserContext;
> >> +
> >> +static const int eac3_sample_rate_tab[] = { 48000, 44100, 32000, 0 };
> >> +
> >> +void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const
> uint8_t
> >> *buf, size_t size)
> >> +{
> >> +    if (size < 8)
> >> +        return;
> >> +
> >> +    info->codec_tag             = AV_RL32(buf);
> >> +
> >> +    if (info->codec_tag == MKTAG('z','a', 'a', 'c'))
> >> +        info->codec_id = AV_CODEC_ID_AAC;
> >> +    else if (info->codec_tag == MKTAG('z','a', 'c', '3'))
> >> +        info->codec_id = AV_CODEC_ID_AC3;
> >> +    else if (info->codec_tag == MKTAG('z','e', 'c', '3'))
> >> +        info->codec_id = AV_CODEC_ID_EAC3;
> >> +    else
> >> +        info->codec_id = AV_CODEC_ID_NONE;
> >> +
> >> +    buf += 4;
> >> +    info->priming               = AV_RL16(buf);
> >> +    buf += 2;
> >> +    info->version               = *buf++;
> >> +    info->setup_data_length     = *buf++;
> >> +
> >> +    if (info->setup_data_length > size - 8)
> >> +        info->setup_data_length = size - 8;
> >> +
> >> +    if (info->setup_data_length > HLS_MAX_AUDIO_SETUP_DATA_LEN)
> >> +        return;
> >> +
> >> +    memcpy(info->setup_data, buf, info->setup_data_length);
> >> +}
> >> +
> >> +int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo
> *info)
> >> +{
> >> +    int ret = 0;
> >> +
> >> +    st->codecpar->codec_tag = info->codec_tag;
> >> +
> >> +    if (st->codecpar->codec_id == AV_CODEC_ID_AAC)
> >> +        return 0;
> >> +
> >> +    if (st->codecpar->codec_id != AV_CODEC_ID_AC3 &&
> >> st->codecpar->codec_id != AV_CODEC_ID_EAC3)
> >> +        return AVERROR_INVALIDDATA;
> >> +
> >> +    if (st->codecpar->codec_id == AV_CODEC_ID_AC3) {
> >> +
> >> +        AC3HeaderInfo *ac3hdr = NULL;
> >> +
> >> +        ret = avpriv_ac3_parse_header(&ac3hdr, info->setup_data,
> >> info->setup_data_length);
> >> +        if (ret < 0) {
> >> +            if (ret != AVERROR(ENOMEM))
> >> +                av_free(ac3hdr);
> >> +            return ret;
> >> +        }
> >> +
> >> +        st->codecpar->sample_rate       = ac3hdr->sample_rate;
> >> +        st->codecpar->channels          = ac3hdr->channels;
> >> +        st->codecpar->channel_layout    = ac3hdr->channel_layout;
> >> +        st->codecpar->bit_rate          = ac3hdr->bit_rate;
> >> +
> >> +        av_free(ac3hdr);
> >> +    } else {  /*  Parse 'dec3' EC3SpecificBox */
> >> +
> >> +        GetBitContext gb;
> >> +        int data_rate, fscod, acmod, lfeon;
> >> +
> >> +        ret = init_get_bits8(&gb, info->setup_data,
> >> info->setup_data_length);
> >> +        if (ret < 0)
> >> +            return AVERROR_INVALIDDATA;
> >> +
> >> +        data_rate = get_bits(&gb, 13);
> >> +        skip_bits(&gb, 3);
> >> +        fscod = get_bits(&gb, 2);
> >> +        skip_bits(&gb, 10);
> >> +        acmod = get_bits(&gb, 3);
> >> +        lfeon = get_bits(&gb, 1);
> >> +
> >> +        st->codecpar->sample_rate = eac3_sample_rate_tab[fscod];
> >> +
> >> +        st->codecpar->channel_layout =
> >> avpriv_ac3_channel_layout_tab[acmod];
> >> +        if (lfeon)
> >> +            st->codecpar->channel_layout |= AV_CH_LOW_FREQUENCY;
> >> +
> >> +        st->codecpar->channels =
> >> av_get_channel_layout_nb_channels(st->codecpar->channel_layout);
> >> +
> >> +        st->codecpar->bit_rate = data_rate*1000;
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +/*
> >> + * Remove start code emulation prevention 0x03 bytes
> >> + */
> >> +static void remove_scep_3_bytes(NALUnit *nalu)
> >> +{
> >> +    int i = 0;
> >> +    int j = 0;
> >> +
> >> +    uint8_t *data = nalu->data;
> >> +
> >> +    while (i < nalu->length) {
> >> +        if (nalu->length - i > 3 && AV_RB24(&data[i]) == 0x000003) {
> >> +            data[j++] = data[i++];
> >> +            data[j++] = data[i++];
> >> +            i++;
> >> +        } else {
> >> +            data[j++] = data[i++];
> >> +        }
> >> +    }
> >> +
> >> +    nalu->length = j;
> >> +}
> >> +
> >> +static int get_next_nal_unit(CodecParserContext *ctx, NALUnit *nalu)
> >> +{
> >> +    const uint8_t *nalu_start = ctx->buf_ptr;
> >> +
> >> +    if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr) ==
> >> 0x00000001)
> >> +        nalu->start_code_length = 4;
> >> +    else if (ctx->buf_end - ctx->buf_ptr >= 3 && AV_RB24(ctx->buf_ptr)
> ==
> >> 0x000001)
> >> +        nalu->start_code_length = 3;
> >> +    else /* No start code at the beginning of the NAL unit */
> >> +        return -1;
> >> +
> >> +    ctx->buf_ptr += nalu->start_code_length;
> >> +
> >> +    while (ctx->buf_ptr < ctx->buf_end) {
> >> +        if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr)
> ==
> >> 0x00000001)
> >> +            break;
> >> +        else if (ctx->buf_end - ctx->buf_ptr >= 3 &&
> >> AV_RB24(ctx->buf_ptr) == 0x000001)
> >> +            break;
> >> +        ctx->buf_ptr++;
> >> +    }
> >> +
> >> +    nalu->data  = (uint8_t *)nalu_start + nalu->start_code_length;
> >> +    nalu->length = ctx->buf_ptr - nalu->data;
> >> +    nalu->type  = *nalu->data & 0x1F;
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int decrypt_nal_unit(HLSCryptoContext *crypto_ctx, NALUnit
> *nalu)
> >> +{
> >> +    int ret = 0;
> >> +    int rem_bytes;
> >> +    uint8_t *data;
> >> +    uint8_t iv[16];
> >> +
> >> +    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
> >> +    if (ret < 0)
> >> +        return ret;
> >> +
> >> +    /* Remove start code emulation prevention 0x03 bytes */
> >> +    remove_scep_3_bytes(nalu);
> >> +
> >> +    data = nalu->data + 32;
> >> +    rem_bytes = nalu->length - 32;
> >> +
> >> +    memcpy(iv, crypto_ctx->iv, 16);
> >> +
> >> +    while (rem_bytes > 0) {
> >> +        if (rem_bytes > 16) {
> >> +            av_aes_crypt(crypto_ctx->aes_ctx, data, data, 1, iv, 1);
> >> +            data += 16;
> >> +            rem_bytes -= 16;
> >> +        }
> >> +        data += FFMIN(144, rem_bytes);
> >> +        rem_bytes -= FFMIN(144, rem_bytes);
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int decrypt_video_frame(HLSCryptoContext *crypto_ctx, AVPacket
> >> *pkt)
> >> +{
> >> +    int ret = 0;
> >> +    CodecParserContext  ctx;
> >> +    NALUnit nalu;
> >> +    uint8_t *data_ptr;
> >> +    int move_nalu = 0;
> >> +
> >> +    memset(&ctx, 0, sizeof(ctx));
> >> +    ctx.buf_ptr  = pkt->data;
> >> +    ctx.buf_end = pkt->data + pkt->size;
> >> +
> >> +    data_ptr = pkt->data;
> >> +
> >> +    while (ctx.buf_ptr < ctx.buf_end) {
> >> +        memset(&nalu, 0, sizeof(nalu));
> >> +        ret = get_next_nal_unit(&ctx, &nalu);
> >> +        if (ret < 0)
> >> +            return ret;
> >> +        if ((nalu.type == 0x01 || nalu.type == 0x05) && nalu.length >
> 48)
> >> {
> >> +            int encrypted_nalu_length = nalu.length;
> >> +            ret = decrypt_nal_unit(crypto_ctx, &nalu);
> >> +            if (ret < 0)
> >> +                return ret;
> >> +            move_nalu = nalu.length != encrypted_nalu_length;
> >> +        }
> >> +        if (move_nalu)
> >> +            memmove(data_ptr, nalu.data - nalu.start_code_length,
> >> nalu.start_code_length + nalu.length);
> >> +        data_ptr += nalu.start_code_length + nalu.length;
> >> +    }
> >> +
> >> +    av_shrink_packet(pkt, data_ptr - pkt->data);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int get_next_adts_frame(CodecParserContext *ctx, AudioFrame
> *frame)
> >> +{
> >> +    int ret = 0;
> >> +
> >> +    AACADTSHeaderInfo *adts_hdr = NULL;
> >> +
> >> +    /* Find next sync word 0xFFF */
> >> +    while (ctx->buf_ptr < ctx->buf_end - 1) {
> >> +        if (*ctx->buf_ptr == 0xFF && *(ctx->buf_ptr + 1) & 0xF0 ==
> 0xF0)
> >> +            break;
> >> +        ctx->buf_ptr++;
> >> +    }
> >> +
> >> +    if (ctx->buf_ptr >= ctx->buf_end - 1)
> >> +        return -1;
> >> +
> >> +    frame->data = (uint8_t*)ctx->buf_ptr;
> >> +
> >> +    ret = avpriv_adts_header_parse (&adts_hdr, frame->data,
> ctx->buf_end
> >> - frame->data);
> >> +    if (ret < 0)
> >> +        return ret;
> >> +
> >> +    frame->header_length = adts_hdr->crc_absent ?
> AV_AAC_ADTS_HEADER_SIZE
> >> : AV_AAC_ADTS_HEADER_SIZE + 2;
> >> +    frame->length = adts_hdr->frame_length;
> >> +
> >> +    av_free(adts_hdr);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int get_next_ac3_eac3_sync_frame(CodecParserContext *ctx,
> >> AudioFrame *frame)
> >> +{
> >> +    int ret = 0;
> >> +
> >> +    AC3HeaderInfo *hdr = NULL;
> >> +
> >> +    /* Find next sync word 0x0B77 */
> >> +    while (ctx->buf_ptr < ctx->buf_end - 1) {
> >> +        if (*ctx->buf_ptr == 0x0B && *(ctx->buf_ptr + 1) == 0x77)
> >> +            break;
> >> +        ctx->buf_ptr++;
> >> +    }
> >> +
> >> +    if (ctx->buf_ptr >= ctx->buf_end - 1)
> >> +        return -1;
> >> +
> >> +    frame->data = (uint8_t*)ctx->buf_ptr;
> >> +    frame->header_length = 0;
> >> +
> >> +    ret = avpriv_ac3_parse_header(&hdr, frame->data, ctx->buf_end -
> >> frame->data);
> >> +    if (ret < 0) {
> >> +        if (ret != AVERROR(ENOMEM))
> >> +            av_free(hdr);
> >> +        return ret;
> >> +    }
> >> +
> >> +    frame->length = hdr->frame_size;
> >> +
> >> +    av_free(hdr);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int get_next_sync_frame(enum AVCodecID codec_id,
> >> CodecParserContext *ctx, AudioFrame *frame)
> >> +{
> >> +    if (codec_id == AV_CODEC_ID_AAC)
> >> +        return get_next_adts_frame(ctx, frame);
> >> +    else if (codec_id == AV_CODEC_ID_AC3 || codec_id ==
> AV_CODEC_ID_EAC3)
> >> +        return get_next_ac3_eac3_sync_frame(ctx, frame);
> >> +    else
> >> +        return AVERROR_INVALIDDATA;
> >> +}
> >> +
> >> +static int decrypt_sync_frame(enum AVCodecID codec_id, HLSCryptoContext
> >> *crypto_ctx, AudioFrame *frame)
> >> +{
> >> +    int ret = 0;
> >> +    uint8_t *data;
> >> +    int num_of_encrypted_blocks;
> >> +
> >> +    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
> >> +    if (ret < 0)
> >> +        return ret;
> >> +
> >> +    data = frame->data + frame->header_length + 16;
> >> +
> >> +    num_of_encrypted_blocks = (frame->length - frame->header_length -
> >> 16)/16;
> >> +
> >> +    av_aes_crypt(crypto_ctx->aes_ctx, data, data,
> >> num_of_encrypted_blocks, crypto_ctx->iv, 1);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int decrypt_audio_frame(enum AVCodecID codec_id,
> HLSCryptoContext
> >> *crypto_ctx, AVPacket *pkt)
> >> +{
> >> +    int ret = 0;
> >> +    CodecParserContext  ctx;
> >> +    AudioFrame frame;
> >> +
> >> +    memset(&ctx, 0, sizeof(ctx));
> >> +    ctx.buf_ptr        = pkt->data;
> >> +    ctx.buf_end = pkt->data + pkt->size;
> >> +
> >> +    while (ctx.buf_ptr < ctx.buf_end) {
> >> +        memset(&frame, 0, sizeof(frame));
> >> +        ret = get_next_sync_frame(codec_id, &ctx, &frame);
> >> +        if (ret < 0)
> >> +            return ret;
> >> +        if (frame.length - frame.header_length > 31) {
> >> +            ret = decrypt_sync_frame(codec_id, crypto_ctx, &frame);
> >> +            if (ret < 0)
> >> +                return ret;
> >> +        }
> >> +        ctx.buf_ptr += frame.length;
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext
> >> *crypto_ctx, AVPacket *pkt)
> >> +{
> >> +    if (codec_id == AV_CODEC_ID_H264)
> >> +        return decrypt_video_frame(crypto_ctx, pkt);
> >> +    else if (codec_id == AV_CODEC_ID_AAC || codec_id == AV_CODEC_ID_AC3
> >> || codec_id == AV_CODEC_ID_EAC3)
> >> +        return decrypt_audio_frame(codec_id, crypto_ctx, pkt);
> >> +
> >> +    return AVERROR_INVALIDDATA;
> >> +}
> >> diff --git a/libavformat/hls_sample_aes.h b/libavformat/hls_sample_aes.h
> >> new file mode 100644
> >> index 0000000000..cf80e41cb0
> >> --- /dev/null
> >> +++ b/libavformat/hls_sample_aes.h
> >> @@ -0,0 +1,66 @@
> >> +/*
> >> + * Apple HTTP Live Streaming Sample Encryption/Decryption
> >> + *
> >> + * Copyright (c) 2021 Nachiket Tarate
> >> + *
> >> + * This file is part of FFmpeg.
> >> + *
> >> + * FFmpeg is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU Lesser General Public
> >> + * License as published by the Free Software Foundation; either
> >> + * version 2.1 of the License, or (at your option) any later version.
> >> + *
> >> + * FFmpeg is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> >> + * Lesser General Public License for more details.
> >> + *
> >> + * You should have received a copy of the GNU Lesser General Public
> >> + * License along with FFmpeg; if not, write to the Free Software
> >> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> >> 02110-1301 USA
> >> + */
> >> +
> >> +/**
> >> + * @file
> >> + * Apple HTTP Live Streaming Sample Encryption
> >> + *
> >>
> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> >> + */
> >> +
> >> +#ifndef AVFORMAT_HLS_SAMPLE_AES_H
> >> +#define AVFORMAT_HLS_SAMPLE_AES_H
> >> +
> >> +#include <stdint.h>
> >> +
> >> +#include "avformat.h"
> >> +
> >> +#include "libavcodec/avcodec.h"
> >> +#include "libavutil/aes.h"
> >> +
> >> +#define HLS_MAX_ID3_TAGS_DATA_LEN       138
> >> +#define HLS_MAX_AUDIO_SETUP_DATA_LEN    10
> >> +
> >> +
> >> +typedef struct HLSCryptoContext {
> >> +    struct AVAES   *aes_ctx;
> >> +    uint8_t            key[16];
> >> +    uint8_t            iv[16];
> >> +} HLSCryptoContext;
> >> +
> >> +typedef struct HLSAudioSetupInfo {
> >> +    enum AVCodecID      codec_id;
> >> +    uint32_t            codec_tag;
> >> +    uint16_t            priming;
> >> +    uint8_t             version;
> >> +    uint8_t             setup_data_length;
> >> +    uint8_t             setup_data[HLS_MAX_AUDIO_SETUP_DATA_LEN];
> >> +} HLSAudioSetupInfo;
> >> +
> >> +
> >> +void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const
> uint8_t
> >> *buf, size_t size);
> >> +
> >> +int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo
> *info);
> >> +
> >> +int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext
> >> *crypto_ctx, AVPacket *pkt);
> >> +
> >> +#endif /* AVFORMAT_HLS_SAMPLE_AES_H */
> >> +
> >> diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
> >> index e283ec09d7..dc611ae788 100644
> >> --- a/libavformat/mpegts.c
> >> +++ b/libavformat/mpegts.c
> >> @@ -839,6 +839,16 @@ static const StreamType MISC_types[] = {
> >>     { 0 },
> >> };
> >>
> >> +/* HLS Sample Encryption Types  */
> >> +static const StreamType HLS_SAMPLE_ENC_types[] = {
> >> +    { 0xdb, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264},
> >> +    { 0xcf, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
> >> +    { 0xc1, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 },
> >> +    { 0xc2, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3},
> >> +    { 0 },
> >> +};
> >> +
> >> +
> >> static const StreamType REGD_types[] = {
> >>     { MKTAG('d', 'r', 'a', 'c'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC
> },
> >>     { MKTAG('A', 'C', '-', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3
>  },
> >> @@ -948,6 +958,8 @@ static int mpegts_set_stream_info(AVStream *st,
> >> PESContext *pes,
> >>     }
> >>     if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
> >>         mpegts_find_stream_type(st, pes->stream_type, MISC_types);
> >> +    if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
> >> +        mpegts_find_stream_type(st, pes->stream_type,
> >> HLS_SAMPLE_ENC_types);
> >>     if (st->codecpar->codec_id == AV_CODEC_ID_NONE) {
> >>         st->codecpar->codec_id  = old_codec_id;
> >>         st->codecpar->codec_type = old_codec_type;
> >> --
> >> 2.17.1
> >>
> >>
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel@ffmpeg.org
> > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >
> > To unsubscribe, visit link above, or email
> > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
> Thanks
>
> Steven Liu
>
>
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Liu Steven March 1, 2021, 5:59 a.m. UTC | #4
> 2021年3月1日 下午12:55,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
> 
> This is an updated version of the patch in which I have added the check. If
> the segments are in Fragmented MP4 format, HLS demuxer quits by giving an
> error message:
> 
> "SAMPLE-AES encryption is not supported for fragmented MP4 format yet”
I don’t think  is a good resolution for SAMPLE-AES encryption and decryption.
You should support that if you want support SAMPLE-AES in hls,
because SAMPLE-AES not only support in MPEG-TS, but also support fragment mp4.
Whatever, if you only support mpegts en[de]cryption, it should be a half part patch.

> 
> Best Regards,
> Nachiket Tarate
> 
> On Mon, Mar 1, 2021 at 10:13 AM Steven Liu <lq@chinaffmpeg.org> wrote:
> 
>> 
>> 
>>> 2021年3月1日 下午12:35,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
>>> 
>>> @Steven Liu <lq@chinaffmpeg.org>
>>> 
>>> Can we merge this patch ?
>> I’m waiting update patch for fragment mp4 encryption.
>> After new version of the patchset I will test and review.
>>> 
>>> Best Regards,
>>> Nachiket Tarate
>>> 
>>> On Wed, Feb 24, 2021 at 4:44 PM Nachiket Tarate <
>>> nachiket.programmer@gmail.com> wrote:
>>> 
>>>> Apple HTTP Live Streaming Sample Encryption:
>>>> 
>>>> 
>>>> 
>> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
>>>> 
>>>> Signed-off-by: Nachiket Tarate <nachiket.programmer@gmail.com>
>>>> ---
>>>> libavformat/Makefile         |   2 +-
>>>> libavformat/hls.c            | 105 ++++++++--
>>>> libavformat/hls_sample_aes.c | 391 +++++++++++++++++++++++++++++++++++
>>>> libavformat/hls_sample_aes.h |  66 ++++++
>>>> libavformat/mpegts.c         |  12 ++
>>>> 5 files changed, 562 insertions(+), 14 deletions(-)
>>>> create mode 100644 libavformat/hls_sample_aes.c
>>>> create mode 100644 libavformat/hls_sample_aes.h
>>>> 
>>>> diff --git a/libavformat/Makefile b/libavformat/Makefile
>>>> index fcb39ce133..62627d50a6 100644
>>>> --- a/libavformat/Makefile
>>>> +++ b/libavformat/Makefile
>>>> @@ -236,7 +236,7 @@ OBJS-$(CONFIG_HCOM_DEMUXER)              += hcom.o
>>>> pcm.o
>>>> OBJS-$(CONFIG_HDS_MUXER)                 += hdsenc.o
>>>> OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
>>>> OBJS-$(CONFIG_HEVC_MUXER)                += rawenc.o
>>>> -OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o
>>>> +OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o hls_sample_aes.o
>>>> OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o hlsplaylist.o avc.o
>>>> OBJS-$(CONFIG_HNM_DEMUXER)               += hnm.o
>>>> OBJS-$(CONFIG_ICO_DEMUXER)               += icodec.o
>>>> diff --git a/libavformat/hls.c b/libavformat/hls.c
>>>> index af2468ad9b..3cb3853c79 100644
>>>> --- a/libavformat/hls.c
>>>> +++ b/libavformat/hls.c
>>>> @@ -2,6 +2,7 @@
>>>> * Apple HTTP Live Streaming demuxer
>>>> * Copyright (c) 2010 Martin Storsjo
>>>> * Copyright (c) 2013 Anssi Hannula
>>>> + * Copyright (c) 2021 Nachiket Tarate
>>>> *
>>>> * This file is part of FFmpeg.
>>>> *
>>>> @@ -39,6 +40,8 @@
>>>> #include "avio_internal.h"
>>>> #include "id3v2.h"
>>>> 
>>>> +#include "hls_sample_aes.h"
>>>> +
>>>> #define INITIAL_BUFFER_SIZE 32768
>>>> 
>>>> #define MAX_FIELD_LEN 64
>>>> @@ -145,6 +148,10 @@ struct playlist {
>>>>    int id3_changed; /* ID3 tag data has changed at some point */
>>>>    ID3v2ExtraMeta *id3_deferred_extra; /* stored here until subdemuxer
>>>> is opened */
>>>> 
>>>> +    /* Used in case of SAMPLE-AES encryption method */
>>>> +    HLSAudioSetupInfo audio_setup_info;
>>>> +    HLSCryptoContext  crypto_ctx;
>>>> +
>>>>    int64_t seek_timestamp;
>>>>    int seek_flags;
>>>>    int seek_stream_index; /* into subdemuxer stream array */
>>>> @@ -266,6 +273,8 @@ static void free_playlist_list(HLSContext *c)
>>>>            pls->ctx->pb = NULL;
>>>>            avformat_close_input(&pls->ctx);
>>>>        }
>>>> +        if (pls->crypto_ctx.aes_ctx)
>>>> +             av_free(pls->crypto_ctx.aes_ctx);
>>>>        av_free(pls);
>>>>    }
>>>>    av_freep(&c->playlists);
>>>> @@ -994,7 +1003,10 @@ fail:
>>>> 
>>>> static struct segment *current_segment(struct playlist *pls)
>>>> {
>>>> -    return pls->segments[pls->cur_seq_no - pls->start_seq_no];
>>>> +    int64_t n = pls->cur_seq_no - pls->start_seq_no;
>>>> +    if (n >= pls->n_segments)
>>>> +        return NULL;
>>>> +    return pls->segments[n];
>>>> }
>>>> 
>>>> static struct segment *next_segment(struct playlist *pls)
>>>> @@ -1023,10 +1035,11 @@ static int read_from_url(struct playlist *pls,
>>>> struct segment *seg,
>>>> 
>>>> /* Parse the raw ID3 data and pass contents to caller */
>>>> static void parse_id3(AVFormatContext *s, AVIOContext *pb,
>>>> -                      AVDictionary **metadata, int64_t *dts,
>>>> +                      AVDictionary **metadata, int64_t *dts,
>>>> HLSAudioSetupInfo *audio_setup_info,
>>>>                      ID3v2ExtraMetaAPIC **apic, ID3v2ExtraMeta
>>>> **extra_meta)
>>>> {
>>>>    static const char id3_priv_owner_ts[] =
>>>> "com.apple.streaming.transportStreamTimestamp";
>>>> +    static const char id3_priv_owner_audio_setup[] =
>>>> "com.apple.streaming.audioDescription";
>>>>    ID3v2ExtraMeta *meta;
>>>> 
>>>>    ff_id3v2_read_dict(pb, metadata, ID3v2_DEFAULT_MAGIC, extra_meta);
>>>> @@ -1041,6 +1054,8 @@ static void parse_id3(AVFormatContext *s,
>>>> AVIOContext *pb,
>>>>                    *dts = ts;
>>>>                else
>>>>                    av_log(s, AV_LOG_ERROR, "Invalid HLS ID3 audio
>>>> timestamp %"PRId64"\n", ts);
>>>> +            } else if (priv->datasize >= 8 && !strcmp(priv->owner,
>>>> id3_priv_owner_audio_setup)) {
>>>> +                ff_hls_read_audio_setup_info(audio_setup_info,
>>>> priv->data, priv->datasize);
>>>>            }
>>>>        } else if (!strcmp(meta->tag, "APIC") && apic)
>>>>            *apic = &meta->data.apic;
>>>> @@ -1084,7 +1099,7 @@ static void handle_id3(AVIOContext *pb, struct
>>>> playlist *pls)
>>>>    ID3v2ExtraMeta *extra_meta = NULL;
>>>>    int64_t timestamp = AV_NOPTS_VALUE;
>>>> 
>>>> -    parse_id3(pls->ctx, pb, &metadata, &timestamp, &apic, &extra_meta);
>>>> +    parse_id3(pls->ctx, pb, &metadata, &timestamp,
>>>> &pls->audio_setup_info, &apic, &extra_meta);
>>>> 
>>>>    if (timestamp != AV_NOPTS_VALUE) {
>>>>        pls->id3_mpegts_timestamp = timestamp;
>>>> @@ -1238,10 +1253,7 @@ static int open_input(HLSContext *c, struct
>>>> playlist *pls, struct segment *seg,
>>>>    av_log(pls->parent, AV_LOG_VERBOSE, "HLS request for url '%s',
>> offset
>>>> %"PRId64", playlist %d\n",
>>>>           seg->url, seg->url_offset, pls->index);
>>>> 
>>>> -    if (seg->key_type == KEY_NONE) {
>>>> -        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
>>>> &is_http);
>>>> -    } else if (seg->key_type == KEY_AES_128) {
>>>> -        char iv[33], key[33], url[MAX_URL_SIZE];
>>>> +    if (seg->key_type == KEY_AES_128 || seg->key_type ==
>> KEY_SAMPLE_AES) {
>>>>        if (strcmp(seg->key, pls->key_url)) {
>>>>            AVIOContext *pb = NULL;
>>>>            if (open_url(pls->parent, &pb, seg->key, &c->avio_opts,
>> opts,
>>>> NULL) == 0) {
>>>> @@ -1257,6 +1269,10 @@ static int open_input(HLSContext *c, struct
>>>> playlist *pls, struct segment *seg,
>>>>            }
>>>>            av_strlcpy(pls->key_url, seg->key, sizeof(pls->key_url));
>>>>        }
>>>> +    }
>>>> +
>>>> +    if (seg->key_type == KEY_AES_128) {
>>>> +        char iv[33], key[33], url[MAX_URL_SIZE];
>>>>        ff_data_to_hex(iv, seg->iv, sizeof(seg->iv), 0);
>>>>        ff_data_to_hex(key, pls->key, sizeof(pls->key), 0);
>>>>        iv[32] = key[32] = '\0';
>>>> @@ -1273,13 +1289,9 @@ static int open_input(HLSContext *c, struct
>>>> playlist *pls, struct segment *seg,
>>>>            goto cleanup;
>>>>        }
>>>>        ret = 0;
>>>> -    } else if (seg->key_type == KEY_SAMPLE_AES) {
>>>> -        av_log(pls->parent, AV_LOG_ERROR,
>>>> -               "SAMPLE-AES encryption is not supported yet\n");
>>>> -        ret = AVERROR_PATCHWELCOME;
>>>> +    } else {
>>>> +        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
>>>> &is_http);
>>>>    }
>>>> -    else
>>>> -      ret = AVERROR(ENOSYS);
>>>> 
>>>>    /* Seek to the requested position. If this was a HTTP request, the
>>>> offset
>>>>     * should already be where want it to, but this allows e.g. local
>>>> testing
>>>> @@ -1948,6 +1960,7 @@ static int hls_read_header(AVFormatContext *s)
>>>>        struct playlist *pls = c->playlists[i];
>>>>        char *url;
>>>>        ff_const59 AVInputFormat *in_fmt = NULL;
>>>> +        struct segment *seg = NULL;
>>>> 
>>>>        if (!(pls->ctx = avformat_alloc_context())) {
>>>>            ret = AVERROR(ENOMEM);
>>>> @@ -1980,8 +1993,41 @@ static int hls_read_header(AVFormatContext *s)
>>>>            pls->ctx = NULL;
>>>>            goto fail;
>>>>        }
>>>> +
>>>>        ffio_init_context(&pls->pb, pls->read_buffer,
>>>> INITIAL_BUFFER_SIZE, 0, pls,
>>>>                          read_data, NULL, NULL);
>>>> +
>>>> +        /*
>>>> +         * If encryption scheme is SAMPLE-AES, try to read  ID3 tags of
>>>> +         * external audio track that contains audio setup information
>>>> +         */
>>>> +        seg = current_segment(pls);
>>>> +        if (seg && seg->key_type == KEY_SAMPLE_AES &&
>> pls->n_renditions >
>>>> 0 &&
>>>> +            pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO) {
>>>> +            uint8_t buf[HLS_MAX_ID3_TAGS_DATA_LEN];
>>>> +            if ((ret = avio_read(&pls->pb, buf,
>>>> HLS_MAX_ID3_TAGS_DATA_LEN)) < 0) {
>>>> +                /* Fail if error was not end of file */
>>>> +                if (ret != AVERROR_EOF) {
>>>> +                    avformat_free_context(pls->ctx);
>>>> +                    pls->ctx = NULL;
>>>> +                    goto fail;
>>>> +                }
>>>> +            }
>>>> +            ret = 0;
>>>> +        }
>>>> +
>>>> +        /*
>>>> +         * If encryption scheme is SAMPLE-AES and audio setup
>> information
>>>> is present in external audio track,
>>>> +         * use that information to find the media format, otherwise
>> probe
>>>> input data
>>>> +         */
>>>> +        if (seg && seg->key_type == KEY_SAMPLE_AES &&
>>>> pls->is_id3_timestamped &&
>>>> +            pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) {
>>>> +            void *iter = NULL;
>>>> +            while ((in_fmt = (ff_const59 AVInputFormat
>>>> *)av_demuxer_iterate(&iter)))
>>>> +                if (in_fmt->raw_codec_id ==
>>>> pls->audio_setup_info.codec_id) {
>>>> +                    break;
>>>> +                }
>>>> +        } else {
>>>>        pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 *
>> 4;
>>>>        pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ?
>>>> s->max_analyze_duration : 4 * AV_TIME_BASE;
>>>>        pls->ctx->interrupt_callback = s->interrupt_callback;
>>>> @@ -1999,6 +2045,25 @@ static int hls_read_header(AVFormatContext *s)
>>>>            goto fail;
>>>>        }
>>>>        av_free(url);
>>>> +        }
>>>> +
>>>> +        if (seg && seg->key_type == KEY_SAMPLE_AES) {
>>>> +            if (!pls->is_id3_timestamped && pls->n_renditions > 0 &&
>>>> pls->renditions[0]->type != AVMEDIA_TYPE_AUDIO &&
>>>> +                strcmp(in_fmt->name, "mpegts")) {
>>>> +                av_log(s, AV_LOG_ERROR, "SAMPLE-AES encryption is not
>>>> supported for fragmented MP4 format yet\n");
>>>> +                ret = AVERROR_PATCHWELCOME;
>>>> +            } else {
>>>> +                pls->crypto_ctx.aes_ctx = av_aes_alloc();
>>>> +                if (!pls->crypto_ctx.aes_ctx)
>>>> +                    ret = AVERROR(ENOMEM);
>>>> +            }
>>>> +            if (ret != 0) {
>>>> +                avformat_free_context(pls->ctx);
>>>> +                pls->ctx = NULL;
>>>> +                goto fail;
>>>> +            }
>>>> +        }
>>>> +
>>>>        pls->ctx->pb       = &pls->pb;
>>>>        pls->ctx->io_open  = nested_io_open;
>>>>        pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
>>>> @@ -2027,7 +2092,12 @@ static int hls_read_header(AVFormatContext *s)
>>>>         * on us if they want to.
>>>>         */
>>>>        if (pls->is_id3_timestamped || (pls->n_renditions > 0 &&
>>>> pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO)) {
>>>> +            if (seg && seg->key_type == KEY_SAMPLE_AES &&
>>>> pls->audio_setup_info.setup_data_length > 0 &&
>>>> +                pls->ctx->nb_streams == 1)
>>>> +                ret =
>> ff_hls_parse_audio_setup_info(pls->ctx->streams[0],
>>>> &pls->audio_setup_info);
>>>> +            else
>>>>            ret = avformat_find_stream_info(pls->ctx, NULL);
>>>> +
>>>>            if (ret < 0)
>>>>                goto fail;
>>>>        }
>>>> @@ -2157,6 +2227,7 @@ static int hls_read_packet(AVFormatContext *s,
>>>> AVPacket *pkt)
>>>>            while (1) {
>>>>                int64_t ts_diff;
>>>>                AVRational tb;
>>>> +                struct segment *seg = NULL;
>>>>                ret = av_read_frame(pls->ctx, &pls->pkt);
>>>>                if (ret < 0) {
>>>>                    if (!avio_feof(&pls->pb) && ret != AVERROR_EOF)
>>>> @@ -2175,6 +2246,14 @@ static int hls_read_packet(AVFormatContext *s,
>>>> AVPacket *pkt)
>>>>                            get_timebase(pls), AV_TIME_BASE_Q);
>>>>                }
>>>> 
>>>> +                seg = current_segment(pls);
>>>> +                if (seg && seg->key_type == KEY_SAMPLE_AES) {
>>>> +                    enum AVCodecID codec_id =
>>>> pls->ctx->streams[pls->pkt.stream_index]->codecpar->codec_id;
>>>> +                    memcpy(pls->crypto_ctx.iv, seg->iv,
>> sizeof(seg->iv));
>>>> +                    memcpy(pls->crypto_ctx.key, pls->key,
>>>> sizeof(pls->key));
>>>> +                    ff_hls_decrypt_frame(codec_id, &pls->crypto_ctx,
>>>> &pls->pkt);
>>>> +                }
>>>> +
>>>>                if (pls->seek_timestamp == AV_NOPTS_VALUE)
>>>>                    break;
>>>> 
>>>> diff --git a/libavformat/hls_sample_aes.c b/libavformat/hls_sample_aes.c
>>>> new file mode 100644
>>>> index 0000000000..0407a15b0f
>>>> --- /dev/null
>>>> +++ b/libavformat/hls_sample_aes.c
>>>> @@ -0,0 +1,391 @@
>>>> +/*
>>>> + * Apple HTTP Live Streaming Sample Encryption/Decryption
>>>> + *
>>>> + * Copyright (c) 2021 Nachiket Tarate
>>>> + *
>>>> + * This file is part of FFmpeg.
>>>> + *
>>>> + * FFmpeg is free software; you can redistribute it and/or
>>>> + * modify it under the terms of the GNU Lesser General Public
>>>> + * License as published by the Free Software Foundation; either
>>>> + * version 2.1 of the License, or (at your option) any later version.
>>>> + *
>>>> + * FFmpeg is distributed in the hope that it will be useful,
>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>>>> + * Lesser General Public License for more details.
>>>> + *
>>>> + * You should have received a copy of the GNU Lesser General Public
>>>> + * License along with FFmpeg; if not, write to the Free Software
>>>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>>>> 02110-1301 USA
>>>> + */
>>>> +
>>>> +/**
>>>> + * @file
>>>> + * Apple HTTP Live Streaming Sample Encryption
>>>> + *
>>>> 
>> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
>>>> + */
>>>> +
>>>> +#include "hls_sample_aes.h"
>>>> +
>>>> +#include "libavcodec/adts_header.h"
>>>> +#include "libavcodec/adts_parser.h"
>>>> +#include "libavcodec/ac3_parser_internal.h"
>>>> +
>>>> +
>>>> +typedef struct NALUnit {
>>>> +    uint8_t     *data;
>>>> +    int         type;
>>>> +    int         length;
>>>> +    int         start_code_length;
>>>> +} NALUnit;
>>>> +
>>>> +typedef struct AudioFrame {
>>>> +    uint8_t     *data;
>>>> +    int         length;
>>>> +    int         header_length;
>>>> +} AudioFrame;
>>>> +
>>>> +typedef struct CodecParserContext {
>>>> +    const uint8_t   *buf_ptr;
>>>> +    const uint8_t   *buf_end;
>>>> +} CodecParserContext;
>>>> +
>>>> +static const int eac3_sample_rate_tab[] = { 48000, 44100, 32000, 0 };
>>>> +
>>>> +void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const
>> uint8_t
>>>> *buf, size_t size)
>>>> +{
>>>> +    if (size < 8)
>>>> +        return;
>>>> +
>>>> +    info->codec_tag             = AV_RL32(buf);
>>>> +
>>>> +    if (info->codec_tag == MKTAG('z','a', 'a', 'c'))
>>>> +        info->codec_id = AV_CODEC_ID_AAC;
>>>> +    else if (info->codec_tag == MKTAG('z','a', 'c', '3'))
>>>> +        info->codec_id = AV_CODEC_ID_AC3;
>>>> +    else if (info->codec_tag == MKTAG('z','e', 'c', '3'))
>>>> +        info->codec_id = AV_CODEC_ID_EAC3;
>>>> +    else
>>>> +        info->codec_id = AV_CODEC_ID_NONE;
>>>> +
>>>> +    buf += 4;
>>>> +    info->priming               = AV_RL16(buf);
>>>> +    buf += 2;
>>>> +    info->version               = *buf++;
>>>> +    info->setup_data_length     = *buf++;
>>>> +
>>>> +    if (info->setup_data_length > size - 8)
>>>> +        info->setup_data_length = size - 8;
>>>> +
>>>> +    if (info->setup_data_length > HLS_MAX_AUDIO_SETUP_DATA_LEN)
>>>> +        return;
>>>> +
>>>> +    memcpy(info->setup_data, buf, info->setup_data_length);
>>>> +}
>>>> +
>>>> +int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo
>> *info)
>>>> +{
>>>> +    int ret = 0;
>>>> +
>>>> +    st->codecpar->codec_tag = info->codec_tag;
>>>> +
>>>> +    if (st->codecpar->codec_id == AV_CODEC_ID_AAC)
>>>> +        return 0;
>>>> +
>>>> +    if (st->codecpar->codec_id != AV_CODEC_ID_AC3 &&
>>>> st->codecpar->codec_id != AV_CODEC_ID_EAC3)
>>>> +        return AVERROR_INVALIDDATA;
>>>> +
>>>> +    if (st->codecpar->codec_id == AV_CODEC_ID_AC3) {
>>>> +
>>>> +        AC3HeaderInfo *ac3hdr = NULL;
>>>> +
>>>> +        ret = avpriv_ac3_parse_header(&ac3hdr, info->setup_data,
>>>> info->setup_data_length);
>>>> +        if (ret < 0) {
>>>> +            if (ret != AVERROR(ENOMEM))
>>>> +                av_free(ac3hdr);
>>>> +            return ret;
>>>> +        }
>>>> +
>>>> +        st->codecpar->sample_rate       = ac3hdr->sample_rate;
>>>> +        st->codecpar->channels          = ac3hdr->channels;
>>>> +        st->codecpar->channel_layout    = ac3hdr->channel_layout;
>>>> +        st->codecpar->bit_rate          = ac3hdr->bit_rate;
>>>> +
>>>> +        av_free(ac3hdr);
>>>> +    } else {  /*  Parse 'dec3' EC3SpecificBox */
>>>> +
>>>> +        GetBitContext gb;
>>>> +        int data_rate, fscod, acmod, lfeon;
>>>> +
>>>> +        ret = init_get_bits8(&gb, info->setup_data,
>>>> info->setup_data_length);
>>>> +        if (ret < 0)
>>>> +            return AVERROR_INVALIDDATA;
>>>> +
>>>> +        data_rate = get_bits(&gb, 13);
>>>> +        skip_bits(&gb, 3);
>>>> +        fscod = get_bits(&gb, 2);
>>>> +        skip_bits(&gb, 10);
>>>> +        acmod = get_bits(&gb, 3);
>>>> +        lfeon = get_bits(&gb, 1);
>>>> +
>>>> +        st->codecpar->sample_rate = eac3_sample_rate_tab[fscod];
>>>> +
>>>> +        st->codecpar->channel_layout =
>>>> avpriv_ac3_channel_layout_tab[acmod];
>>>> +        if (lfeon)
>>>> +            st->codecpar->channel_layout |= AV_CH_LOW_FREQUENCY;
>>>> +
>>>> +        st->codecpar->channels =
>>>> av_get_channel_layout_nb_channels(st->codecpar->channel_layout);
>>>> +
>>>> +        st->codecpar->bit_rate = data_rate*1000;
>>>> +    }
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Remove start code emulation prevention 0x03 bytes
>>>> + */
>>>> +static void remove_scep_3_bytes(NALUnit *nalu)
>>>> +{
>>>> +    int i = 0;
>>>> +    int j = 0;
>>>> +
>>>> +    uint8_t *data = nalu->data;
>>>> +
>>>> +    while (i < nalu->length) {
>>>> +        if (nalu->length - i > 3 && AV_RB24(&data[i]) == 0x000003) {
>>>> +            data[j++] = data[i++];
>>>> +            data[j++] = data[i++];
>>>> +            i++;
>>>> +        } else {
>>>> +            data[j++] = data[i++];
>>>> +        }
>>>> +    }
>>>> +
>>>> +    nalu->length = j;
>>>> +}
>>>> +
>>>> +static int get_next_nal_unit(CodecParserContext *ctx, NALUnit *nalu)
>>>> +{
>>>> +    const uint8_t *nalu_start = ctx->buf_ptr;
>>>> +
>>>> +    if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr) ==
>>>> 0x00000001)
>>>> +        nalu->start_code_length = 4;
>>>> +    else if (ctx->buf_end - ctx->buf_ptr >= 3 && AV_RB24(ctx->buf_ptr)
>> ==
>>>> 0x000001)
>>>> +        nalu->start_code_length = 3;
>>>> +    else /* No start code at the beginning of the NAL unit */
>>>> +        return -1;
>>>> +
>>>> +    ctx->buf_ptr += nalu->start_code_length;
>>>> +
>>>> +    while (ctx->buf_ptr < ctx->buf_end) {
>>>> +        if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr)
>> ==
>>>> 0x00000001)
>>>> +            break;
>>>> +        else if (ctx->buf_end - ctx->buf_ptr >= 3 &&
>>>> AV_RB24(ctx->buf_ptr) == 0x000001)
>>>> +            break;
>>>> +        ctx->buf_ptr++;
>>>> +    }
>>>> +
>>>> +    nalu->data  = (uint8_t *)nalu_start + nalu->start_code_length;
>>>> +    nalu->length = ctx->buf_ptr - nalu->data;
>>>> +    nalu->type  = *nalu->data & 0x1F;
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int decrypt_nal_unit(HLSCryptoContext *crypto_ctx, NALUnit
>> *nalu)
>>>> +{
>>>> +    int ret = 0;
>>>> +    int rem_bytes;
>>>> +    uint8_t *data;
>>>> +    uint8_t iv[16];
>>>> +
>>>> +    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    /* Remove start code emulation prevention 0x03 bytes */
>>>> +    remove_scep_3_bytes(nalu);
>>>> +
>>>> +    data = nalu->data + 32;
>>>> +    rem_bytes = nalu->length - 32;
>>>> +
>>>> +    memcpy(iv, crypto_ctx->iv, 16);
>>>> +
>>>> +    while (rem_bytes > 0) {
>>>> +        if (rem_bytes > 16) {
>>>> +            av_aes_crypt(crypto_ctx->aes_ctx, data, data, 1, iv, 1);
>>>> +            data += 16;
>>>> +            rem_bytes -= 16;
>>>> +        }
>>>> +        data += FFMIN(144, rem_bytes);
>>>> +        rem_bytes -= FFMIN(144, rem_bytes);
>>>> +    }
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int decrypt_video_frame(HLSCryptoContext *crypto_ctx, AVPacket
>>>> *pkt)
>>>> +{
>>>> +    int ret = 0;
>>>> +    CodecParserContext  ctx;
>>>> +    NALUnit nalu;
>>>> +    uint8_t *data_ptr;
>>>> +    int move_nalu = 0;
>>>> +
>>>> +    memset(&ctx, 0, sizeof(ctx));
>>>> +    ctx.buf_ptr  = pkt->data;
>>>> +    ctx.buf_end = pkt->data + pkt->size;
>>>> +
>>>> +    data_ptr = pkt->data;
>>>> +
>>>> +    while (ctx.buf_ptr < ctx.buf_end) {
>>>> +        memset(&nalu, 0, sizeof(nalu));
>>>> +        ret = get_next_nal_unit(&ctx, &nalu);
>>>> +        if (ret < 0)
>>>> +            return ret;
>>>> +        if ((nalu.type == 0x01 || nalu.type == 0x05) && nalu.length >
>> 48)
>>>> {
>>>> +            int encrypted_nalu_length = nalu.length;
>>>> +            ret = decrypt_nal_unit(crypto_ctx, &nalu);
>>>> +            if (ret < 0)
>>>> +                return ret;
>>>> +            move_nalu = nalu.length != encrypted_nalu_length;
>>>> +        }
>>>> +        if (move_nalu)
>>>> +            memmove(data_ptr, nalu.data - nalu.start_code_length,
>>>> nalu.start_code_length + nalu.length);
>>>> +        data_ptr += nalu.start_code_length + nalu.length;
>>>> +    }
>>>> +
>>>> +    av_shrink_packet(pkt, data_ptr - pkt->data);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int get_next_adts_frame(CodecParserContext *ctx, AudioFrame
>> *frame)
>>>> +{
>>>> +    int ret = 0;
>>>> +
>>>> +    AACADTSHeaderInfo *adts_hdr = NULL;
>>>> +
>>>> +    /* Find next sync word 0xFFF */
>>>> +    while (ctx->buf_ptr < ctx->buf_end - 1) {
>>>> +        if (*ctx->buf_ptr == 0xFF && *(ctx->buf_ptr + 1) & 0xF0 ==
>> 0xF0)
>>>> +            break;
>>>> +        ctx->buf_ptr++;
>>>> +    }
>>>> +
>>>> +    if (ctx->buf_ptr >= ctx->buf_end - 1)
>>>> +        return -1;
>>>> +
>>>> +    frame->data = (uint8_t*)ctx->buf_ptr;
>>>> +
>>>> +    ret = avpriv_adts_header_parse (&adts_hdr, frame->data,
>> ctx->buf_end
>>>> - frame->data);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    frame->header_length = adts_hdr->crc_absent ?
>> AV_AAC_ADTS_HEADER_SIZE
>>>> : AV_AAC_ADTS_HEADER_SIZE + 2;
>>>> +    frame->length = adts_hdr->frame_length;
>>>> +
>>>> +    av_free(adts_hdr);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int get_next_ac3_eac3_sync_frame(CodecParserContext *ctx,
>>>> AudioFrame *frame)
>>>> +{
>>>> +    int ret = 0;
>>>> +
>>>> +    AC3HeaderInfo *hdr = NULL;
>>>> +
>>>> +    /* Find next sync word 0x0B77 */
>>>> +    while (ctx->buf_ptr < ctx->buf_end - 1) {
>>>> +        if (*ctx->buf_ptr == 0x0B && *(ctx->buf_ptr + 1) == 0x77)
>>>> +            break;
>>>> +        ctx->buf_ptr++;
>>>> +    }
>>>> +
>>>> +    if (ctx->buf_ptr >= ctx->buf_end - 1)
>>>> +        return -1;
>>>> +
>>>> +    frame->data = (uint8_t*)ctx->buf_ptr;
>>>> +    frame->header_length = 0;
>>>> +
>>>> +    ret = avpriv_ac3_parse_header(&hdr, frame->data, ctx->buf_end -
>>>> frame->data);
>>>> +    if (ret < 0) {
>>>> +        if (ret != AVERROR(ENOMEM))
>>>> +            av_free(hdr);
>>>> +        return ret;
>>>> +    }
>>>> +
>>>> +    frame->length = hdr->frame_size;
>>>> +
>>>> +    av_free(hdr);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int get_next_sync_frame(enum AVCodecID codec_id,
>>>> CodecParserContext *ctx, AudioFrame *frame)
>>>> +{
>>>> +    if (codec_id == AV_CODEC_ID_AAC)
>>>> +        return get_next_adts_frame(ctx, frame);
>>>> +    else if (codec_id == AV_CODEC_ID_AC3 || codec_id ==
>> AV_CODEC_ID_EAC3)
>>>> +        return get_next_ac3_eac3_sync_frame(ctx, frame);
>>>> +    else
>>>> +        return AVERROR_INVALIDDATA;
>>>> +}
>>>> +
>>>> +static int decrypt_sync_frame(enum AVCodecID codec_id, HLSCryptoContext
>>>> *crypto_ctx, AudioFrame *frame)
>>>> +{
>>>> +    int ret = 0;
>>>> +    uint8_t *data;
>>>> +    int num_of_encrypted_blocks;
>>>> +
>>>> +    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    data = frame->data + frame->header_length + 16;
>>>> +
>>>> +    num_of_encrypted_blocks = (frame->length - frame->header_length -
>>>> 16)/16;
>>>> +
>>>> +    av_aes_crypt(crypto_ctx->aes_ctx, data, data,
>>>> num_of_encrypted_blocks, crypto_ctx->iv, 1);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int decrypt_audio_frame(enum AVCodecID codec_id,
>> HLSCryptoContext
>>>> *crypto_ctx, AVPacket *pkt)
>>>> +{
>>>> +    int ret = 0;
>>>> +    CodecParserContext  ctx;
>>>> +    AudioFrame frame;
>>>> +
>>>> +    memset(&ctx, 0, sizeof(ctx));
>>>> +    ctx.buf_ptr        = pkt->data;
>>>> +    ctx.buf_end = pkt->data + pkt->size;
>>>> +
>>>> +    while (ctx.buf_ptr < ctx.buf_end) {
>>>> +        memset(&frame, 0, sizeof(frame));
>>>> +        ret = get_next_sync_frame(codec_id, &ctx, &frame);
>>>> +        if (ret < 0)
>>>> +            return ret;
>>>> +        if (frame.length - frame.header_length > 31) {
>>>> +            ret = decrypt_sync_frame(codec_id, crypto_ctx, &frame);
>>>> +            if (ret < 0)
>>>> +                return ret;
>>>> +        }
>>>> +        ctx.buf_ptr += frame.length;
>>>> +    }
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext
>>>> *crypto_ctx, AVPacket *pkt)
>>>> +{
>>>> +    if (codec_id == AV_CODEC_ID_H264)
>>>> +        return decrypt_video_frame(crypto_ctx, pkt);
>>>> +    else if (codec_id == AV_CODEC_ID_AAC || codec_id == AV_CODEC_ID_AC3
>>>> || codec_id == AV_CODEC_ID_EAC3)
>>>> +        return decrypt_audio_frame(codec_id, crypto_ctx, pkt);
>>>> +
>>>> +    return AVERROR_INVALIDDATA;
>>>> +}
>>>> diff --git a/libavformat/hls_sample_aes.h b/libavformat/hls_sample_aes.h
>>>> new file mode 100644
>>>> index 0000000000..cf80e41cb0
>>>> --- /dev/null
>>>> +++ b/libavformat/hls_sample_aes.h
>>>> @@ -0,0 +1,66 @@
>>>> +/*
>>>> + * Apple HTTP Live Streaming Sample Encryption/Decryption
>>>> + *
>>>> + * Copyright (c) 2021 Nachiket Tarate
>>>> + *
>>>> + * This file is part of FFmpeg.
>>>> + *
>>>> + * FFmpeg is free software; you can redistribute it and/or
>>>> + * modify it under the terms of the GNU Lesser General Public
>>>> + * License as published by the Free Software Foundation; either
>>>> + * version 2.1 of the License, or (at your option) any later version.
>>>> + *
>>>> + * FFmpeg is distributed in the hope that it will be useful,
>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>>>> + * Lesser General Public License for more details.
>>>> + *
>>>> + * You should have received a copy of the GNU Lesser General Public
>>>> + * License along with FFmpeg; if not, write to the Free Software
>>>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>>>> 02110-1301 USA
>>>> + */
>>>> +
>>>> +/**
>>>> + * @file
>>>> + * Apple HTTP Live Streaming Sample Encryption
>>>> + *
>>>> 
>> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
>>>> + */
>>>> +
>>>> +#ifndef AVFORMAT_HLS_SAMPLE_AES_H
>>>> +#define AVFORMAT_HLS_SAMPLE_AES_H
>>>> +
>>>> +#include <stdint.h>
>>>> +
>>>> +#include "avformat.h"
>>>> +
>>>> +#include "libavcodec/avcodec.h"
>>>> +#include "libavutil/aes.h"
>>>> +
>>>> +#define HLS_MAX_ID3_TAGS_DATA_LEN       138
>>>> +#define HLS_MAX_AUDIO_SETUP_DATA_LEN    10
>>>> +
>>>> +
>>>> +typedef struct HLSCryptoContext {
>>>> +    struct AVAES   *aes_ctx;
>>>> +    uint8_t            key[16];
>>>> +    uint8_t            iv[16];
>>>> +} HLSCryptoContext;
>>>> +
>>>> +typedef struct HLSAudioSetupInfo {
>>>> +    enum AVCodecID      codec_id;
>>>> +    uint32_t            codec_tag;
>>>> +    uint16_t            priming;
>>>> +    uint8_t             version;
>>>> +    uint8_t             setup_data_length;
>>>> +    uint8_t             setup_data[HLS_MAX_AUDIO_SETUP_DATA_LEN];
>>>> +} HLSAudioSetupInfo;
>>>> +
>>>> +
>>>> +void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const
>> uint8_t
>>>> *buf, size_t size);
>>>> +
>>>> +int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo
>> *info);
>>>> +
>>>> +int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext
>>>> *crypto_ctx, AVPacket *pkt);
>>>> +
>>>> +#endif /* AVFORMAT_HLS_SAMPLE_AES_H */
>>>> +
>>>> diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
>>>> index e283ec09d7..dc611ae788 100644
>>>> --- a/libavformat/mpegts.c
>>>> +++ b/libavformat/mpegts.c
>>>> @@ -839,6 +839,16 @@ static const StreamType MISC_types[] = {
>>>>    { 0 },
>>>> };
>>>> 
>>>> +/* HLS Sample Encryption Types  */
>>>> +static const StreamType HLS_SAMPLE_ENC_types[] = {
>>>> +    { 0xdb, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264},
>>>> +    { 0xcf, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
>>>> +    { 0xc1, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 },
>>>> +    { 0xc2, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3},
>>>> +    { 0 },
>>>> +};
>>>> +
>>>> +
>>>> static const StreamType REGD_types[] = {
>>>>    { MKTAG('d', 'r', 'a', 'c'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC
>> },
>>>>    { MKTAG('A', 'C', '-', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3
>> },
>>>> @@ -948,6 +958,8 @@ static int mpegts_set_stream_info(AVStream *st,
>>>> PESContext *pes,
>>>>    }
>>>>    if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
>>>>        mpegts_find_stream_type(st, pes->stream_type, MISC_types);
>>>> +    if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
>>>> +        mpegts_find_stream_type(st, pes->stream_type,
>>>> HLS_SAMPLE_ENC_types);
>>>>    if (st->codecpar->codec_id == AV_CODEC_ID_NONE) {
>>>>        st->codecpar->codec_id  = old_codec_id;
>>>>        st->codecpar->codec_type = old_codec_type;
>>>> --
>>>> 2.17.1
>>>> 
>>>> 
>>> _______________________________________________
>>> ffmpeg-devel mailing list
>>> ffmpeg-devel@ffmpeg.org
>>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>> 
>>> To unsubscribe, visit link above, or email
>>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>> 
>> Thanks
>> 
>> Steven Liu
>> 
>> 
>> 
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>> 
>> To unsubscribe, visit link above, or email
>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

Thanks

Steven Liu
Nachiket Tarate March 1, 2021, 7:22 a.m. UTC | #5
On Mon, Mar 1, 2021 at 11:30 AM Steven Liu <lq@chinaffmpeg.org> wrote:
>
>
>
> > 2021年3月1日 下午12:55,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
> >
> > This is an updated version of the patch in which I have added the check. If
> > the segments are in Fragmented MP4 format, HLS demuxer quits by giving an
> > error message:
> >
> > "SAMPLE-AES encryption is not supported for fragmented MP4 format yet”
> I don’t think  is a good resolution for SAMPLE-AES encryption and decryption.
> You should support that if you want support SAMPLE-AES in hls,
> because SAMPLE-AES not only support in MPEG-TS, but also support fragment mp4.
> Whatever, if you only support mpegts en[de]cryption, it should be a half part patch.

Two completely different technologies/specifications have been used
for SAMPLE-AES encryption of HLS streams in MPEG-TS and fragmented MP4
formats.

Fragmented MP4 media segments are encrypted using the 'cbcs' scheme of
Common Encryption [CENC]:

https://www.iso.org/standard/68042.html

Encryption of other media segment formats such as MPEG-TS or external
audio tracks containing H.264, AAC, AC-3 and Enhanced AC-3 media
streams is described in the Apple's HTTP Live Streaming (HLS) Sample
Encryption specifications:

https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption

This patch implements the later specifications and enables decryption
of media segments in MPEG-TS, AAC, AC-3 and Enhanced AC-3 formats. So
I think we should merge this patch right now.

In future, we will add support for CENC or see how can we use existing
things from MOV demuxer.

Best Regards,
Nachiket Tarate

> >
> > Best Regards,
> > Nachiket Tarate
> >
> > On Mon, Mar 1, 2021 at 10:13 AM Steven Liu <lq@chinaffmpeg.org> wrote:
> >
> >>
> >>
> >>> 2021年3月1日 下午12:35,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
> >>>
> >>> @Steven Liu <lq@chinaffmpeg.org>
> >>>
> >>> Can we merge this patch ?
> >> I’m waiting update patch for fragment mp4 encryption.
> >> After new version of the patchset I will test and review.
> >>>
> >>> Best Regards,
> >>> Nachiket Tarate
> >>>
> >>> On Wed, Feb 24, 2021 at 4:44 PM Nachiket Tarate <
> >>> nachiket.programmer@gmail.com> wrote:
> >>>
> >>>> Apple HTTP Live Streaming Sample Encryption:
> >>>>
> >>>>
> >>>>
> >> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> >>>>
> >>>> Signed-off-by: Nachiket Tarate <nachiket.programmer@gmail.com>
> >>>> ---
> >>>> libavformat/Makefile         |   2 +-
> >>>> libavformat/hls.c            | 105 ++++++++--
> >>>> libavformat/hls_sample_aes.c | 391 +++++++++++++++++++++++++++++++++++
> >>>> libavformat/hls_sample_aes.h |  66 ++++++
> >>>> libavformat/mpegts.c         |  12 ++
> >>>> 5 files changed, 562 insertions(+), 14 deletions(-)
> >>>> create mode 100644 libavformat/hls_sample_aes.c
> >>>> create mode 100644 libavformat/hls_sample_aes.h
> >>>>
> >>>> diff --git a/libavformat/Makefile b/libavformat/Makefile
> >>>> index fcb39ce133..62627d50a6 100644
> >>>> --- a/libavformat/Makefile
> >>>> +++ b/libavformat/Makefile
> >>>> @@ -236,7 +236,7 @@ OBJS-$(CONFIG_HCOM_DEMUXER)              += hcom.o
> >>>> pcm.o
> >>>> OBJS-$(CONFIG_HDS_MUXER)                 += hdsenc.o
> >>>> OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
> >>>> OBJS-$(CONFIG_HEVC_MUXER)                += rawenc.o
> >>>> -OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o
> >>>> +OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o hls_sample_aes.o
> >>>> OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o hlsplaylist.o avc.o
> >>>> OBJS-$(CONFIG_HNM_DEMUXER)               += hnm.o
> >>>> OBJS-$(CONFIG_ICO_DEMUXER)               += icodec.o
> >>>> diff --git a/libavformat/hls.c b/libavformat/hls.c
> >>>> index af2468ad9b..3cb3853c79 100644
> >>>> --- a/libavformat/hls.c
> >>>> +++ b/libavformat/hls.c
> >>>> @@ -2,6 +2,7 @@
> >>>> * Apple HTTP Live Streaming demuxer
> >>>> * Copyright (c) 2010 Martin Storsjo
> >>>> * Copyright (c) 2013 Anssi Hannula
> >>>> + * Copyright (c) 2021 Nachiket Tarate
> >>>> *
> >>>> * This file is part of FFmpeg.
> >>>> *
> >>>> @@ -39,6 +40,8 @@
> >>>> #include "avio_internal.h"
> >>>> #include "id3v2.h"
> >>>>
> >>>> +#include "hls_sample_aes.h"
> >>>> +
> >>>> #define INITIAL_BUFFER_SIZE 32768
> >>>>
> >>>> #define MAX_FIELD_LEN 64
> >>>> @@ -145,6 +148,10 @@ struct playlist {
> >>>>    int id3_changed; /* ID3 tag data has changed at some point */
> >>>>    ID3v2ExtraMeta *id3_deferred_extra; /* stored here until subdemuxer
> >>>> is opened */
> >>>>
> >>>> +    /* Used in case of SAMPLE-AES encryption method */
> >>>> +    HLSAudioSetupInfo audio_setup_info;
> >>>> +    HLSCryptoContext  crypto_ctx;
> >>>> +
> >>>>    int64_t seek_timestamp;
> >>>>    int seek_flags;
> >>>>    int seek_stream_index; /* into subdemuxer stream array */
> >>>> @@ -266,6 +273,8 @@ static void free_playlist_list(HLSContext *c)
> >>>>            pls->ctx->pb = NULL;
> >>>>            avformat_close_input(&pls->ctx);
> >>>>        }
> >>>> +        if (pls->crypto_ctx.aes_ctx)
> >>>> +             av_free(pls->crypto_ctx.aes_ctx);
> >>>>        av_free(pls);
> >>>>    }
> >>>>    av_freep(&c->playlists);
> >>>> @@ -994,7 +1003,10 @@ fail:
> >>>>
> >>>> static struct segment *current_segment(struct playlist *pls)
> >>>> {
> >>>> -    return pls->segments[pls->cur_seq_no - pls->start_seq_no];
> >>>> +    int64_t n = pls->cur_seq_no - pls->start_seq_no;
> >>>> +    if (n >= pls->n_segments)
> >>>> +        return NULL;
> >>>> +    return pls->segments[n];
> >>>> }
> >>>>
> >>>> static struct segment *next_segment(struct playlist *pls)
> >>>> @@ -1023,10 +1035,11 @@ static int read_from_url(struct playlist *pls,
> >>>> struct segment *seg,
> >>>>
> >>>> /* Parse the raw ID3 data and pass contents to caller */
> >>>> static void parse_id3(AVFormatContext *s, AVIOContext *pb,
> >>>> -                      AVDictionary **metadata, int64_t *dts,
> >>>> +                      AVDictionary **metadata, int64_t *dts,
> >>>> HLSAudioSetupInfo *audio_setup_info,
> >>>>                      ID3v2ExtraMetaAPIC **apic, ID3v2ExtraMeta
> >>>> **extra_meta)
> >>>> {
> >>>>    static const char id3_priv_owner_ts[] =
> >>>> "com.apple.streaming.transportStreamTimestamp";
> >>>> +    static const char id3_priv_owner_audio_setup[] =
> >>>> "com.apple.streaming.audioDescription";
> >>>>    ID3v2ExtraMeta *meta;
> >>>>
> >>>>    ff_id3v2_read_dict(pb, metadata, ID3v2_DEFAULT_MAGIC, extra_meta);
> >>>> @@ -1041,6 +1054,8 @@ static void parse_id3(AVFormatContext *s,
> >>>> AVIOContext *pb,
> >>>>                    *dts = ts;
> >>>>                else
> >>>>                    av_log(s, AV_LOG_ERROR, "Invalid HLS ID3 audio
> >>>> timestamp %"PRId64"\n", ts);
> >>>> +            } else if (priv->datasize >= 8 && !strcmp(priv->owner,
> >>>> id3_priv_owner_audio_setup)) {
> >>>> +                ff_hls_read_audio_setup_info(audio_setup_info,
> >>>> priv->data, priv->datasize);
> >>>>            }
> >>>>        } else if (!strcmp(meta->tag, "APIC") && apic)
> >>>>            *apic = &meta->data.apic;
> >>>> @@ -1084,7 +1099,7 @@ static void handle_id3(AVIOContext *pb, struct
> >>>> playlist *pls)
> >>>>    ID3v2ExtraMeta *extra_meta = NULL;
> >>>>    int64_t timestamp = AV_NOPTS_VALUE;
> >>>>
> >>>> -    parse_id3(pls->ctx, pb, &metadata, &timestamp, &apic, &extra_meta);
> >>>> +    parse_id3(pls->ctx, pb, &metadata, &timestamp,
> >>>> &pls->audio_setup_info, &apic, &extra_meta);
> >>>>
> >>>>    if (timestamp != AV_NOPTS_VALUE) {
> >>>>        pls->id3_mpegts_timestamp = timestamp;
> >>>> @@ -1238,10 +1253,7 @@ static int open_input(HLSContext *c, struct
> >>>> playlist *pls, struct segment *seg,
> >>>>    av_log(pls->parent, AV_LOG_VERBOSE, "HLS request for url '%s',
> >> offset
> >>>> %"PRId64", playlist %d\n",
> >>>>           seg->url, seg->url_offset, pls->index);
> >>>>
> >>>> -    if (seg->key_type == KEY_NONE) {
> >>>> -        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
> >>>> &is_http);
> >>>> -    } else if (seg->key_type == KEY_AES_128) {
> >>>> -        char iv[33], key[33], url[MAX_URL_SIZE];
> >>>> +    if (seg->key_type == KEY_AES_128 || seg->key_type ==
> >> KEY_SAMPLE_AES) {
> >>>>        if (strcmp(seg->key, pls->key_url)) {
> >>>>            AVIOContext *pb = NULL;
> >>>>            if (open_url(pls->parent, &pb, seg->key, &c->avio_opts,
> >> opts,
> >>>> NULL) == 0) {
> >>>> @@ -1257,6 +1269,10 @@ static int open_input(HLSContext *c, struct
> >>>> playlist *pls, struct segment *seg,
> >>>>            }
> >>>>            av_strlcpy(pls->key_url, seg->key, sizeof(pls->key_url));
> >>>>        }
> >>>> +    }
> >>>> +
> >>>> +    if (seg->key_type == KEY_AES_128) {
> >>>> +        char iv[33], key[33], url[MAX_URL_SIZE];
> >>>>        ff_data_to_hex(iv, seg->iv, sizeof(seg->iv), 0);
> >>>>        ff_data_to_hex(key, pls->key, sizeof(pls->key), 0);
> >>>>        iv[32] = key[32] = '\0';
> >>>> @@ -1273,13 +1289,9 @@ static int open_input(HLSContext *c, struct
> >>>> playlist *pls, struct segment *seg,
> >>>>            goto cleanup;
> >>>>        }
> >>>>        ret = 0;
> >>>> -    } else if (seg->key_type == KEY_SAMPLE_AES) {
> >>>> -        av_log(pls->parent, AV_LOG_ERROR,
> >>>> -               "SAMPLE-AES encryption is not supported yet\n");
> >>>> -        ret = AVERROR_PATCHWELCOME;
> >>>> +    } else {
> >>>> +        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
> >>>> &is_http);
> >>>>    }
> >>>> -    else
> >>>> -      ret = AVERROR(ENOSYS);
> >>>>
> >>>>    /* Seek to the requested position. If this was a HTTP request, the
> >>>> offset
> >>>>     * should already be where want it to, but this allows e.g. local
> >>>> testing
> >>>> @@ -1948,6 +1960,7 @@ static int hls_read_header(AVFormatContext *s)
> >>>>        struct playlist *pls = c->playlists[i];
> >>>>        char *url;
> >>>>        ff_const59 AVInputFormat *in_fmt = NULL;
> >>>> +        struct segment *seg = NULL;
> >>>>
> >>>>        if (!(pls->ctx = avformat_alloc_context())) {
> >>>>            ret = AVERROR(ENOMEM);
> >>>> @@ -1980,8 +1993,41 @@ static int hls_read_header(AVFormatContext *s)
> >>>>            pls->ctx = NULL;
> >>>>            goto fail;
> >>>>        }
> >>>> +
> >>>>        ffio_init_context(&pls->pb, pls->read_buffer,
> >>>> INITIAL_BUFFER_SIZE, 0, pls,
> >>>>                          read_data, NULL, NULL);
> >>>> +
> >>>> +        /*
> >>>> +         * If encryption scheme is SAMPLE-AES, try to read  ID3 tags of
> >>>> +         * external audio track that contains audio setup information
> >>>> +         */
> >>>> +        seg = current_segment(pls);
> >>>> +        if (seg && seg->key_type == KEY_SAMPLE_AES &&
> >> pls->n_renditions >
> >>>> 0 &&
> >>>> +            pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO) {
> >>>> +            uint8_t buf[HLS_MAX_ID3_TAGS_DATA_LEN];
> >>>> +            if ((ret = avio_read(&pls->pb, buf,
> >>>> HLS_MAX_ID3_TAGS_DATA_LEN)) < 0) {
> >>>> +                /* Fail if error was not end of file */
> >>>> +                if (ret != AVERROR_EOF) {
> >>>> +                    avformat_free_context(pls->ctx);
> >>>> +                    pls->ctx = NULL;
> >>>> +                    goto fail;
> >>>> +                }
> >>>> +            }
> >>>> +            ret = 0;
> >>>> +        }
> >>>> +
> >>>> +        /*
> >>>> +         * If encryption scheme is SAMPLE-AES and audio setup
> >> information
> >>>> is present in external audio track,
> >>>> +         * use that information to find the media format, otherwise
> >> probe
> >>>> input data
> >>>> +         */
> >>>> +        if (seg && seg->key_type == KEY_SAMPLE_AES &&
> >>>> pls->is_id3_timestamped &&
> >>>> +            pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) {
> >>>> +            void *iter = NULL;
> >>>> +            while ((in_fmt = (ff_const59 AVInputFormat
> >>>> *)av_demuxer_iterate(&iter)))
> >>>> +                if (in_fmt->raw_codec_id ==
> >>>> pls->audio_setup_info.codec_id) {
> >>>> +                    break;
> >>>> +                }
> >>>> +        } else {
> >>>>        pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 *
> >> 4;
> >>>>        pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ?
> >>>> s->max_analyze_duration : 4 * AV_TIME_BASE;
> >>>>        pls->ctx->interrupt_callback = s->interrupt_callback;
> >>>> @@ -1999,6 +2045,25 @@ static int hls_read_header(AVFormatContext *s)
> >>>>            goto fail;
> >>>>        }
> >>>>        av_free(url);
> >>>> +        }
> >>>> +
> >>>> +        if (seg && seg->key_type == KEY_SAMPLE_AES) {
> >>>> +            if (!pls->is_id3_timestamped && pls->n_renditions > 0 &&
> >>>> pls->renditions[0]->type != AVMEDIA_TYPE_AUDIO &&
> >>>> +                strcmp(in_fmt->name, "mpegts")) {
> >>>> +                av_log(s, AV_LOG_ERROR, "SAMPLE-AES encryption is not
> >>>> supported for fragmented MP4 format yet\n");
> >>>> +                ret = AVERROR_PATCHWELCOME;
> >>>> +            } else {
> >>>> +                pls->crypto_ctx.aes_ctx = av_aes_alloc();
> >>>> +                if (!pls->crypto_ctx.aes_ctx)
> >>>> +                    ret = AVERROR(ENOMEM);
> >>>> +            }
> >>>> +            if (ret != 0) {
> >>>> +                avformat_free_context(pls->ctx);
> >>>> +                pls->ctx = NULL;
> >>>> +                goto fail;
> >>>> +            }
> >>>> +        }
> >>>> +
> >>>>        pls->ctx->pb       = &pls->pb;
> >>>>        pls->ctx->io_open  = nested_io_open;
> >>>>        pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
> >>>> @@ -2027,7 +2092,12 @@ static int hls_read_header(AVFormatContext *s)
> >>>>         * on us if they want to.
> >>>>         */
> >>>>        if (pls->is_id3_timestamped || (pls->n_renditions > 0 &&
> >>>> pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO)) {
> >>>> +            if (seg && seg->key_type == KEY_SAMPLE_AES &&
> >>>> pls->audio_setup_info.setup_data_length > 0 &&
> >>>> +                pls->ctx->nb_streams == 1)
> >>>> +                ret =
> >> ff_hls_parse_audio_setup_info(pls->ctx->streams[0],
> >>>> &pls->audio_setup_info);
> >>>> +            else
> >>>>            ret = avformat_find_stream_info(pls->ctx, NULL);
> >>>> +
> >>>>            if (ret < 0)
> >>>>                goto fail;
> >>>>        }
> >>>> @@ -2157,6 +2227,7 @@ static int hls_read_packet(AVFormatContext *s,
> >>>> AVPacket *pkt)
> >>>>            while (1) {
> >>>>                int64_t ts_diff;
> >>>>                AVRational tb;
> >>>> +                struct segment *seg = NULL;
> >>>>                ret = av_read_frame(pls->ctx, &pls->pkt);
> >>>>                if (ret < 0) {
> >>>>                    if (!avio_feof(&pls->pb) && ret != AVERROR_EOF)
> >>>> @@ -2175,6 +2246,14 @@ static int hls_read_packet(AVFormatContext *s,
> >>>> AVPacket *pkt)
> >>>>                            get_timebase(pls), AV_TIME_BASE_Q);
> >>>>                }
> >>>>
> >>>> +                seg = current_segment(pls);
> >>>> +                if (seg && seg->key_type == KEY_SAMPLE_AES) {
> >>>> +                    enum AVCodecID codec_id =
> >>>> pls->ctx->streams[pls->pkt.stream_index]->codecpar->codec_id;
> >>>> +                    memcpy(pls->crypto_ctx.iv, seg->iv,
> >> sizeof(seg->iv));
> >>>> +                    memcpy(pls->crypto_ctx.key, pls->key,
> >>>> sizeof(pls->key));
> >>>> +                    ff_hls_decrypt_frame(codec_id, &pls->crypto_ctx,
> >>>> &pls->pkt);
> >>>> +                }
> >>>> +
> >>>>                if (pls->seek_timestamp == AV_NOPTS_VALUE)
> >>>>                    break;
> >>>>
> >>>> diff --git a/libavformat/hls_sample_aes.c b/libavformat/hls_sample_aes.c
> >>>> new file mode 100644
> >>>> index 0000000000..0407a15b0f
> >>>> --- /dev/null
> >>>> +++ b/libavformat/hls_sample_aes.c
> >>>> @@ -0,0 +1,391 @@
> >>>> +/*
> >>>> + * Apple HTTP Live Streaming Sample Encryption/Decryption
> >>>> + *
> >>>> + * Copyright (c) 2021 Nachiket Tarate
> >>>> + *
> >>>> + * This file is part of FFmpeg.
> >>>> + *
> >>>> + * FFmpeg is free software; you can redistribute it and/or
> >>>> + * modify it under the terms of the GNU Lesser General Public
> >>>> + * License as published by the Free Software Foundation; either
> >>>> + * version 2.1 of the License, or (at your option) any later version.
> >>>> + *
> >>>> + * FFmpeg is distributed in the hope that it will be useful,
> >>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> >>>> + * Lesser General Public License for more details.
> >>>> + *
> >>>> + * You should have received a copy of the GNU Lesser General Public
> >>>> + * License along with FFmpeg; if not, write to the Free Software
> >>>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> >>>> 02110-1301 USA
> >>>> + */
> >>>> +
> >>>> +/**
> >>>> + * @file
> >>>> + * Apple HTTP Live Streaming Sample Encryption
> >>>> + *
> >>>>
> >> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> >>>> + */
> >>>> +
> >>>> +#include "hls_sample_aes.h"
> >>>> +
> >>>> +#include "libavcodec/adts_header.h"
> >>>> +#include "libavcodec/adts_parser.h"
> >>>> +#include "libavcodec/ac3_parser_internal.h"
> >>>> +
> >>>> +
> >>>> +typedef struct NALUnit {
> >>>> +    uint8_t     *data;
> >>>> +    int         type;
> >>>> +    int         length;
> >>>> +    int         start_code_length;
> >>>> +} NALUnit;
> >>>> +
> >>>> +typedef struct AudioFrame {
> >>>> +    uint8_t     *data;
> >>>> +    int         length;
> >>>> +    int         header_length;
> >>>> +} AudioFrame;
> >>>> +
> >>>> +typedef struct CodecParserContext {
> >>>> +    const uint8_t   *buf_ptr;
> >>>> +    const uint8_t   *buf_end;
> >>>> +} CodecParserContext;
> >>>> +
> >>>> +static const int eac3_sample_rate_tab[] = { 48000, 44100, 32000, 0 };
> >>>> +
> >>>> +void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const
> >> uint8_t
> >>>> *buf, size_t size)
> >>>> +{
> >>>> +    if (size < 8)
> >>>> +        return;
> >>>> +
> >>>> +    info->codec_tag             = AV_RL32(buf);
> >>>> +
> >>>> +    if (info->codec_tag == MKTAG('z','a', 'a', 'c'))
> >>>> +        info->codec_id = AV_CODEC_ID_AAC;
> >>>> +    else if (info->codec_tag == MKTAG('z','a', 'c', '3'))
> >>>> +        info->codec_id = AV_CODEC_ID_AC3;
> >>>> +    else if (info->codec_tag == MKTAG('z','e', 'c', '3'))
> >>>> +        info->codec_id = AV_CODEC_ID_EAC3;
> >>>> +    else
> >>>> +        info->codec_id = AV_CODEC_ID_NONE;
> >>>> +
> >>>> +    buf += 4;
> >>>> +    info->priming               = AV_RL16(buf);
> >>>> +    buf += 2;
> >>>> +    info->version               = *buf++;
> >>>> +    info->setup_data_length     = *buf++;
> >>>> +
> >>>> +    if (info->setup_data_length > size - 8)
> >>>> +        info->setup_data_length = size - 8;
> >>>> +
> >>>> +    if (info->setup_data_length > HLS_MAX_AUDIO_SETUP_DATA_LEN)
> >>>> +        return;
> >>>> +
> >>>> +    memcpy(info->setup_data, buf, info->setup_data_length);
> >>>> +}
> >>>> +
> >>>> +int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo
> >> *info)
> >>>> +{
> >>>> +    int ret = 0;
> >>>> +
> >>>> +    st->codecpar->codec_tag = info->codec_tag;
> >>>> +
> >>>> +    if (st->codecpar->codec_id == AV_CODEC_ID_AAC)
> >>>> +        return 0;
> >>>> +
> >>>> +    if (st->codecpar->codec_id != AV_CODEC_ID_AC3 &&
> >>>> st->codecpar->codec_id != AV_CODEC_ID_EAC3)
> >>>> +        return AVERROR_INVALIDDATA;
> >>>> +
> >>>> +    if (st->codecpar->codec_id == AV_CODEC_ID_AC3) {
> >>>> +
> >>>> +        AC3HeaderInfo *ac3hdr = NULL;
> >>>> +
> >>>> +        ret = avpriv_ac3_parse_header(&ac3hdr, info->setup_data,
> >>>> info->setup_data_length);
> >>>> +        if (ret < 0) {
> >>>> +            if (ret != AVERROR(ENOMEM))
> >>>> +                av_free(ac3hdr);
> >>>> +            return ret;
> >>>> +        }
> >>>> +
> >>>> +        st->codecpar->sample_rate       = ac3hdr->sample_rate;
> >>>> +        st->codecpar->channels          = ac3hdr->channels;
> >>>> +        st->codecpar->channel_layout    = ac3hdr->channel_layout;
> >>>> +        st->codecpar->bit_rate          = ac3hdr->bit_rate;
> >>>> +
> >>>> +        av_free(ac3hdr);
> >>>> +    } else {  /*  Parse 'dec3' EC3SpecificBox */
> >>>> +
> >>>> +        GetBitContext gb;
> >>>> +        int data_rate, fscod, acmod, lfeon;
> >>>> +
> >>>> +        ret = init_get_bits8(&gb, info->setup_data,
> >>>> info->setup_data_length);
> >>>> +        if (ret < 0)
> >>>> +            return AVERROR_INVALIDDATA;
> >>>> +
> >>>> +        data_rate = get_bits(&gb, 13);
> >>>> +        skip_bits(&gb, 3);
> >>>> +        fscod = get_bits(&gb, 2);
> >>>> +        skip_bits(&gb, 10);
> >>>> +        acmod = get_bits(&gb, 3);
> >>>> +        lfeon = get_bits(&gb, 1);
> >>>> +
> >>>> +        st->codecpar->sample_rate = eac3_sample_rate_tab[fscod];
> >>>> +
> >>>> +        st->codecpar->channel_layout =
> >>>> avpriv_ac3_channel_layout_tab[acmod];
> >>>> +        if (lfeon)
> >>>> +            st->codecpar->channel_layout |= AV_CH_LOW_FREQUENCY;
> >>>> +
> >>>> +        st->codecpar->channels =
> >>>> av_get_channel_layout_nb_channels(st->codecpar->channel_layout);
> >>>> +
> >>>> +        st->codecpar->bit_rate = data_rate*1000;
> >>>> +    }
> >>>> +
> >>>> +    return 0;
> >>>> +}
> >>>> +
> >>>> +/*
> >>>> + * Remove start code emulation prevention 0x03 bytes
> >>>> + */
> >>>> +static void remove_scep_3_bytes(NALUnit *nalu)
> >>>> +{
> >>>> +    int i = 0;
> >>>> +    int j = 0;
> >>>> +
> >>>> +    uint8_t *data = nalu->data;
> >>>> +
> >>>> +    while (i < nalu->length) {
> >>>> +        if (nalu->length - i > 3 && AV_RB24(&data[i]) == 0x000003) {
> >>>> +            data[j++] = data[i++];
> >>>> +            data[j++] = data[i++];
> >>>> +            i++;
> >>>> +        } else {
> >>>> +            data[j++] = data[i++];
> >>>> +        }
> >>>> +    }
> >>>> +
> >>>> +    nalu->length = j;
> >>>> +}
> >>>> +
> >>>> +static int get_next_nal_unit(CodecParserContext *ctx, NALUnit *nalu)
> >>>> +{
> >>>> +    const uint8_t *nalu_start = ctx->buf_ptr;
> >>>> +
> >>>> +    if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr) ==
> >>>> 0x00000001)
> >>>> +        nalu->start_code_length = 4;
> >>>> +    else if (ctx->buf_end - ctx->buf_ptr >= 3 && AV_RB24(ctx->buf_ptr)
> >> ==
> >>>> 0x000001)
> >>>> +        nalu->start_code_length = 3;
> >>>> +    else /* No start code at the beginning of the NAL unit */
> >>>> +        return -1;
> >>>> +
> >>>> +    ctx->buf_ptr += nalu->start_code_length;
> >>>> +
> >>>> +    while (ctx->buf_ptr < ctx->buf_end) {
> >>>> +        if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr)
> >> ==
> >>>> 0x00000001)
> >>>> +            break;
> >>>> +        else if (ctx->buf_end - ctx->buf_ptr >= 3 &&
> >>>> AV_RB24(ctx->buf_ptr) == 0x000001)
> >>>> +            break;
> >>>> +        ctx->buf_ptr++;
> >>>> +    }
> >>>> +
> >>>> +    nalu->data  = (uint8_t *)nalu_start + nalu->start_code_length;
> >>>> +    nalu->length = ctx->buf_ptr - nalu->data;
> >>>> +    nalu->type  = *nalu->data & 0x1F;
> >>>> +
> >>>> +    return 0;
> >>>> +}
> >>>> +
> >>>> +static int decrypt_nal_unit(HLSCryptoContext *crypto_ctx, NALUnit
> >> *nalu)
> >>>> +{
> >>>> +    int ret = 0;
> >>>> +    int rem_bytes;
> >>>> +    uint8_t *data;
> >>>> +    uint8_t iv[16];
> >>>> +
> >>>> +    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
> >>>> +    if (ret < 0)
> >>>> +        return ret;
> >>>> +
> >>>> +    /* Remove start code emulation prevention 0x03 bytes */
> >>>> +    remove_scep_3_bytes(nalu);
> >>>> +
> >>>> +    data = nalu->data + 32;
> >>>> +    rem_bytes = nalu->length - 32;
> >>>> +
> >>>> +    memcpy(iv, crypto_ctx->iv, 16);
> >>>> +
> >>>> +    while (rem_bytes > 0) {
> >>>> +        if (rem_bytes > 16) {
> >>>> +            av_aes_crypt(crypto_ctx->aes_ctx, data, data, 1, iv, 1);
> >>>> +            data += 16;
> >>>> +            rem_bytes -= 16;
> >>>> +        }
> >>>> +        data += FFMIN(144, rem_bytes);
> >>>> +        rem_bytes -= FFMIN(144, rem_bytes);
> >>>> +    }
> >>>> +
> >>>> +    return 0;
> >>>> +}
> >>>> +
> >>>> +static int decrypt_video_frame(HLSCryptoContext *crypto_ctx, AVPacket
> >>>> *pkt)
> >>>> +{
> >>>> +    int ret = 0;
> >>>> +    CodecParserContext  ctx;
> >>>> +    NALUnit nalu;
> >>>> +    uint8_t *data_ptr;
> >>>> +    int move_nalu = 0;
> >>>> +
> >>>> +    memset(&ctx, 0, sizeof(ctx));
> >>>> +    ctx.buf_ptr  = pkt->data;
> >>>> +    ctx.buf_end = pkt->data + pkt->size;
> >>>> +
> >>>> +    data_ptr = pkt->data;
> >>>> +
> >>>> +    while (ctx.buf_ptr < ctx.buf_end) {
> >>>> +        memset(&nalu, 0, sizeof(nalu));
> >>>> +        ret = get_next_nal_unit(&ctx, &nalu);
> >>>> +        if (ret < 0)
> >>>> +            return ret;
> >>>> +        if ((nalu.type == 0x01 || nalu.type == 0x05) && nalu.length >
> >> 48)
> >>>> {
> >>>> +            int encrypted_nalu_length = nalu.length;
> >>>> +            ret = decrypt_nal_unit(crypto_ctx, &nalu);
> >>>> +            if (ret < 0)
> >>>> +                return ret;
> >>>> +            move_nalu = nalu.length != encrypted_nalu_length;
> >>>> +        }
> >>>> +        if (move_nalu)
> >>>> +            memmove(data_ptr, nalu.data - nalu.start_code_length,
> >>>> nalu.start_code_length + nalu.length);
> >>>> +        data_ptr += nalu.start_code_length + nalu.length;
> >>>> +    }
> >>>> +
> >>>> +    av_shrink_packet(pkt, data_ptr - pkt->data);
> >>>> +
> >>>> +    return 0;
> >>>> +}
> >>>> +
> >>>> +static int get_next_adts_frame(CodecParserContext *ctx, AudioFrame
> >> *frame)
> >>>> +{
> >>>> +    int ret = 0;
> >>>> +
> >>>> +    AACADTSHeaderInfo *adts_hdr = NULL;
> >>>> +
> >>>> +    /* Find next sync word 0xFFF */
> >>>> +    while (ctx->buf_ptr < ctx->buf_end - 1) {
> >>>> +        if (*ctx->buf_ptr == 0xFF && *(ctx->buf_ptr + 1) & 0xF0 ==
> >> 0xF0)
> >>>> +            break;
> >>>> +        ctx->buf_ptr++;
> >>>> +    }
> >>>> +
> >>>> +    if (ctx->buf_ptr >= ctx->buf_end - 1)
> >>>> +        return -1;
> >>>> +
> >>>> +    frame->data = (uint8_t*)ctx->buf_ptr;
> >>>> +
> >>>> +    ret = avpriv_adts_header_parse (&adts_hdr, frame->data,
> >> ctx->buf_end
> >>>> - frame->data);
> >>>> +    if (ret < 0)
> >>>> +        return ret;
> >>>> +
> >>>> +    frame->header_length = adts_hdr->crc_absent ?
> >> AV_AAC_ADTS_HEADER_SIZE
> >>>> : AV_AAC_ADTS_HEADER_SIZE + 2;
> >>>> +    frame->length = adts_hdr->frame_length;
> >>>> +
> >>>> +    av_free(adts_hdr);
> >>>> +
> >>>> +    return 0;
> >>>> +}
> >>>> +
> >>>> +static int get_next_ac3_eac3_sync_frame(CodecParserContext *ctx,
> >>>> AudioFrame *frame)
> >>>> +{
> >>>> +    int ret = 0;
> >>>> +
> >>>> +    AC3HeaderInfo *hdr = NULL;
> >>>> +
> >>>> +    /* Find next sync word 0x0B77 */
> >>>> +    while (ctx->buf_ptr < ctx->buf_end - 1) {
> >>>> +        if (*ctx->buf_ptr == 0x0B && *(ctx->buf_ptr + 1) == 0x77)
> >>>> +            break;
> >>>> +        ctx->buf_ptr++;
> >>>> +    }
> >>>> +
> >>>> +    if (ctx->buf_ptr >= ctx->buf_end - 1)
> >>>> +        return -1;
> >>>> +
> >>>> +    frame->data = (uint8_t*)ctx->buf_ptr;
> >>>> +    frame->header_length = 0;
> >>>> +
> >>>> +    ret = avpriv_ac3_parse_header(&hdr, frame->data, ctx->buf_end -
> >>>> frame->data);
> >>>> +    if (ret < 0) {
> >>>> +        if (ret != AVERROR(ENOMEM))
> >>>> +            av_free(hdr);
> >>>> +        return ret;
> >>>> +    }
> >>>> +
> >>>> +    frame->length = hdr->frame_size;
> >>>> +
> >>>> +    av_free(hdr);
> >>>> +
> >>>> +    return 0;
> >>>> +}
> >>>> +
> >>>> +static int get_next_sync_frame(enum AVCodecID codec_id,
> >>>> CodecParserContext *ctx, AudioFrame *frame)
> >>>> +{
> >>>> +    if (codec_id == AV_CODEC_ID_AAC)
> >>>> +        return get_next_adts_frame(ctx, frame);
> >>>> +    else if (codec_id == AV_CODEC_ID_AC3 || codec_id ==
> >> AV_CODEC_ID_EAC3)
> >>>> +        return get_next_ac3_eac3_sync_frame(ctx, frame);
> >>>> +    else
> >>>> +        return AVERROR_INVALIDDATA;
> >>>> +}
> >>>> +
> >>>> +static int decrypt_sync_frame(enum AVCodecID codec_id, HLSCryptoContext
> >>>> *crypto_ctx, AudioFrame *frame)
> >>>> +{
> >>>> +    int ret = 0;
> >>>> +    uint8_t *data;
> >>>> +    int num_of_encrypted_blocks;
> >>>> +
> >>>> +    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
> >>>> +    if (ret < 0)
> >>>> +        return ret;
> >>>> +
> >>>> +    data = frame->data + frame->header_length + 16;
> >>>> +
> >>>> +    num_of_encrypted_blocks = (frame->length - frame->header_length -
> >>>> 16)/16;
> >>>> +
> >>>> +    av_aes_crypt(crypto_ctx->aes_ctx, data, data,
> >>>> num_of_encrypted_blocks, crypto_ctx->iv, 1);
> >>>> +
> >>>> +    return 0;
> >>>> +}
> >>>> +
> >>>> +static int decrypt_audio_frame(enum AVCodecID codec_id,
> >> HLSCryptoContext
> >>>> *crypto_ctx, AVPacket *pkt)
> >>>> +{
> >>>> +    int ret = 0;
> >>>> +    CodecParserContext  ctx;
> >>>> +    AudioFrame frame;
> >>>> +
> >>>> +    memset(&ctx, 0, sizeof(ctx));
> >>>> +    ctx.buf_ptr        = pkt->data;
> >>>> +    ctx.buf_end = pkt->data + pkt->size;
> >>>> +
> >>>> +    while (ctx.buf_ptr < ctx.buf_end) {
> >>>> +        memset(&frame, 0, sizeof(frame));
> >>>> +        ret = get_next_sync_frame(codec_id, &ctx, &frame);
> >>>> +        if (ret < 0)
> >>>> +            return ret;
> >>>> +        if (frame.length - frame.header_length > 31) {
> >>>> +            ret = decrypt_sync_frame(codec_id, crypto_ctx, &frame);
> >>>> +            if (ret < 0)
> >>>> +                return ret;
> >>>> +        }
> >>>> +        ctx.buf_ptr += frame.length;
> >>>> +    }
> >>>> +
> >>>> +    return 0;
> >>>> +}
> >>>> +
> >>>> +int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext
> >>>> *crypto_ctx, AVPacket *pkt)
> >>>> +{
> >>>> +    if (codec_id == AV_CODEC_ID_H264)
> >>>> +        return decrypt_video_frame(crypto_ctx, pkt);
> >>>> +    else if (codec_id == AV_CODEC_ID_AAC || codec_id == AV_CODEC_ID_AC3
> >>>> || codec_id == AV_CODEC_ID_EAC3)
> >>>> +        return decrypt_audio_frame(codec_id, crypto_ctx, pkt);
> >>>> +
> >>>> +    return AVERROR_INVALIDDATA;
> >>>> +}
> >>>> diff --git a/libavformat/hls_sample_aes.h b/libavformat/hls_sample_aes.h
> >>>> new file mode 100644
> >>>> index 0000000000..cf80e41cb0
> >>>> --- /dev/null
> >>>> +++ b/libavformat/hls_sample_aes.h
> >>>> @@ -0,0 +1,66 @@
> >>>> +/*
> >>>> + * Apple HTTP Live Streaming Sample Encryption/Decryption
> >>>> + *
> >>>> + * Copyright (c) 2021 Nachiket Tarate
> >>>> + *
> >>>> + * This file is part of FFmpeg.
> >>>> + *
> >>>> + * FFmpeg is free software; you can redistribute it and/or
> >>>> + * modify it under the terms of the GNU Lesser General Public
> >>>> + * License as published by the Free Software Foundation; either
> >>>> + * version 2.1 of the License, or (at your option) any later version.
> >>>> + *
> >>>> + * FFmpeg is distributed in the hope that it will be useful,
> >>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> >>>> + * Lesser General Public License for more details.
> >>>> + *
> >>>> + * You should have received a copy of the GNU Lesser General Public
> >>>> + * License along with FFmpeg; if not, write to the Free Software
> >>>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> >>>> 02110-1301 USA
> >>>> + */
> >>>> +
> >>>> +/**
> >>>> + * @file
> >>>> + * Apple HTTP Live Streaming Sample Encryption
> >>>> + *
> >>>>
> >> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> >>>> + */
> >>>> +
> >>>> +#ifndef AVFORMAT_HLS_SAMPLE_AES_H
> >>>> +#define AVFORMAT_HLS_SAMPLE_AES_H
> >>>> +
> >>>> +#include <stdint.h>
> >>>> +
> >>>> +#include "avformat.h"
> >>>> +
> >>>> +#include "libavcodec/avcodec.h"
> >>>> +#include "libavutil/aes.h"
> >>>> +
> >>>> +#define HLS_MAX_ID3_TAGS_DATA_LEN       138
> >>>> +#define HLS_MAX_AUDIO_SETUP_DATA_LEN    10
> >>>> +
> >>>> +
> >>>> +typedef struct HLSCryptoContext {
> >>>> +    struct AVAES   *aes_ctx;
> >>>> +    uint8_t            key[16];
> >>>> +    uint8_t            iv[16];
> >>>> +} HLSCryptoContext;
> >>>> +
> >>>> +typedef struct HLSAudioSetupInfo {
> >>>> +    enum AVCodecID      codec_id;
> >>>> +    uint32_t            codec_tag;
> >>>> +    uint16_t            priming;
> >>>> +    uint8_t             version;
> >>>> +    uint8_t             setup_data_length;
> >>>> +    uint8_t             setup_data[HLS_MAX_AUDIO_SETUP_DATA_LEN];
> >>>> +} HLSAudioSetupInfo;
> >>>> +
> >>>> +
> >>>> +void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const
> >> uint8_t
> >>>> *buf, size_t size);
> >>>> +
> >>>> +int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo
> >> *info);
> >>>> +
> >>>> +int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext
> >>>> *crypto_ctx, AVPacket *pkt);
> >>>> +
> >>>> +#endif /* AVFORMAT_HLS_SAMPLE_AES_H */
> >>>> +
> >>>> diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
> >>>> index e283ec09d7..dc611ae788 100644
> >>>> --- a/libavformat/mpegts.c
> >>>> +++ b/libavformat/mpegts.c
> >>>> @@ -839,6 +839,16 @@ static const StreamType MISC_types[] = {
> >>>>    { 0 },
> >>>> };
> >>>>
> >>>> +/* HLS Sample Encryption Types  */
> >>>> +static const StreamType HLS_SAMPLE_ENC_types[] = {
> >>>> +    { 0xdb, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264},
> >>>> +    { 0xcf, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
> >>>> +    { 0xc1, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 },
> >>>> +    { 0xc2, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3},
> >>>> +    { 0 },
> >>>> +};
> >>>> +
> >>>> +
> >>>> static const StreamType REGD_types[] = {
> >>>>    { MKTAG('d', 'r', 'a', 'c'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC
> >> },
> >>>>    { MKTAG('A', 'C', '-', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3
> >> },
> >>>> @@ -948,6 +958,8 @@ static int mpegts_set_stream_info(AVStream *st,
> >>>> PESContext *pes,
> >>>>    }
> >>>>    if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
> >>>>        mpegts_find_stream_type(st, pes->stream_type, MISC_types);
> >>>> +    if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
> >>>> +        mpegts_find_stream_type(st, pes->stream_type,
> >>>> HLS_SAMPLE_ENC_types);
> >>>>    if (st->codecpar->codec_id == AV_CODEC_ID_NONE) {
> >>>>        st->codecpar->codec_id  = old_codec_id;
> >>>>        st->codecpar->codec_type = old_codec_type;
> >>>> --
> >>>> 2.17.1
> >>>>
> >>>>
> >>> _______________________________________________
> >>> ffmpeg-devel mailing list
> >>> ffmpeg-devel@ffmpeg.org
> >>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >>>
> >>> To unsubscribe, visit link above, or email
> >>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> >>
> >> Thanks
> >>
> >> Steven Liu
> >>
> >>
> >>
> >> _______________________________________________
> >> ffmpeg-devel mailing list
> >> ffmpeg-devel@ffmpeg.org
> >> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >>
> >> To unsubscribe, visit link above, or email
> >> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel@ffmpeg.org
> > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >
> > To unsubscribe, visit link above, or email
> > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
> Thanks
>
> Steven Liu
>
>
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Liu Steven March 1, 2021, 7:35 a.m. UTC | #6
> 2021年3月1日 下午3:22,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
> 
> On Mon, Mar 1, 2021 at 11:30 AM Steven Liu <lq@chinaffmpeg.org> wrote:
>> 
>> 
>> 
>>> 2021年3月1日 下午12:55,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
>>> 
>>> This is an updated version of the patch in which I have added the check. If
>>> the segments are in Fragmented MP4 format, HLS demuxer quits by giving an
>>> error message:
>>> 
>>> "SAMPLE-AES encryption is not supported for fragmented MP4 format yet”
>> I don’t think  is a good resolution for SAMPLE-AES encryption and decryption.
>> You should support that if you want support SAMPLE-AES in hls,
>> because SAMPLE-AES not only support in MPEG-TS, but also support fragment mp4.
>> Whatever, if you only support mpegts en[de]cryption, it should be a half part patch.
> 
> Two completely different technologies/specifications have been used
> for SAMPLE-AES encryption of HLS streams in MPEG-TS and fragmented MP4
> formats.
> 
> Fragmented MP4 media segments are encrypted using the 'cbcs' scheme of
> Common Encryption [CENC]:
> 
> https://www.iso.org/standard/68042.html
> 
> Encryption of other media segment formats such as MPEG-TS or external
> audio tracks containing H.264, AAC, AC-3 and Enhanced AC-3 media
> streams is described in the Apple's HTTP Live Streaming (HLS) Sample
> Encryption specifications:
> 
> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> 
> This patch implements the later specifications and enables decryption
> of media segments in MPEG-TS, AAC, AC-3 and Enhanced AC-3 formats. So
> I think we should merge this patch right now.

I think sample ads should support not only mpegts, but also support fmp4 too.
Reference rfc8216 context:
     An encryption method of SAMPLE-AES means that the Media Segments
     contain media samples, such as audio or video, that are encrypted
     using the Advanced Encryption Standard [AES_128].  How these media
     streams are encrypted and encapsulated in a segment depends on the
     media encoding and the media format of the segment.  fMP4 Media
     Segments are encrypted using the 'cbcs' scheme of Common
     Encryption [COMMON_ENC].  Encryption of other Media Segment
     formats containing H.264 [H_264], AAC [ISO_14496], AC-3 [AC_3],
     and Enhanced AC-3 [AC_3] media streams is described in the HTTP
     Live Streaming (HLS) Sample Encryption specification [SampleEnc].
     The IV attribute MAY be present; see Section 5.2.

And I saw the m3u8 context, the METHOD is SAMPLE-AES.

(base) liuqi05:ufbuild liuqi$ head -n10  prog_index.m3u8
#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:7
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-INDEPENDENT-SEGMENTS

Focus on this line.
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="http://127.0.0.1/keyOnly.bin",IV=0xcece273e2737a58ca785e257eb080482


#EXT-X-MAP:URI="fileSequence0.mp4"
#EXTINF:8.31667,
#EXT-X-BITRATE:7064


You can point me the part if I misunderstood SAMPLE-AES.

> 
> In future, we will add support for CENC or see how can we use existing
> things from MOV demuxer.
> 
> Best Regards,
> Nachiket Tarate
> 
>>> 
>>> Best Regards,
>>> Nachiket Tarate
>>> 
>>> On Mon, Mar 1, 2021 at 10:13 AM Steven Liu <lq@chinaffmpeg.org> wrote:
>>> 
>>>> 
>>>> 
>>>>> 2021年3月1日 下午12:35,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
>>>>> 
>>>>> @Steven Liu <lq@chinaffmpeg.org>
>>>>> 
>>>>> Can we merge this patch ?
>>>> I’m waiting update patch for fragment mp4 encryption.
>>>> After new version of the patchset I will test and review.
>>>>> 
>>>>> Best Regards,
>>>>> Nachiket Tarate
>>>>> 
>>>>> On Wed, Feb 24, 2021 at 4:44 PM Nachiket Tarate <
>>>>> nachiket.programmer@gmail.com> wrote:
>>>>> 
>>>>>> Apple HTTP Live Streaming Sample Encryption:
>>>>>> 
>>>>>> 
>>>>>> 
>>>> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
>>>>>> 
>>>>>> Signed-off-by: Nachiket Tarate <nachiket.programmer@gmail.com>
>>>>>> ---
>>>>>> libavformat/Makefile         |   2 +-
>>>>>> libavformat/hls.c            | 105 ++++++++--
>>>>>> libavformat/hls_sample_aes.c | 391 +++++++++++++++++++++++++++++++++++
>>>>>> libavformat/hls_sample_aes.h |  66 ++++++
>>>>>> libavformat/mpegts.c         |  12 ++
>>>>>> 5 files changed, 562 insertions(+), 14 deletions(-)
>>>>>> create mode 100644 libavformat/hls_sample_aes.c
>>>>>> create mode 100644 libavformat/hls_sample_aes.h
>>>>>> 
>>>>>> diff --git a/libavformat/Makefile b/libavformat/Makefile
>>>>>> index fcb39ce133..62627d50a6 100644
>>>>>> --- a/libavformat/Makefile
>>>>>> +++ b/libavformat/Makefile
>>>>>> @@ -236,7 +236,7 @@ OBJS-$(CONFIG_HCOM_DEMUXER)              += hcom.o
>>>>>> pcm.o
>>>>>> OBJS-$(CONFIG_HDS_MUXER)                 += hdsenc.o
>>>>>> OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
>>>>>> OBJS-$(CONFIG_HEVC_MUXER)                += rawenc.o
>>>>>> -OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o
>>>>>> +OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o hls_sample_aes.o
>>>>>> OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o hlsplaylist.o avc.o
>>>>>> OBJS-$(CONFIG_HNM_DEMUXER)               += hnm.o
>>>>>> OBJS-$(CONFIG_ICO_DEMUXER)               += icodec.o
>>>>>> diff --git a/libavformat/hls.c b/libavformat/hls.c
>>>>>> index af2468ad9b..3cb3853c79 100644
>>>>>> --- a/libavformat/hls.c
>>>>>> +++ b/libavformat/hls.c
>>>>>> @@ -2,6 +2,7 @@
>>>>>> * Apple HTTP Live Streaming demuxer
>>>>>> * Copyright (c) 2010 Martin Storsjo
>>>>>> * Copyright (c) 2013 Anssi Hannula
>>>>>> + * Copyright (c) 2021 Nachiket Tarate
>>>>>> *
>>>>>> * This file is part of FFmpeg.
>>>>>> *
>>>>>> @@ -39,6 +40,8 @@
>>>>>> #include "avio_internal.h"
>>>>>> #include "id3v2.h"
>>>>>> 
>>>>>> +#include "hls_sample_aes.h"
>>>>>> +
>>>>>> #define INITIAL_BUFFER_SIZE 32768
>>>>>> 
>>>>>> #define MAX_FIELD_LEN 64
>>>>>> @@ -145,6 +148,10 @@ struct playlist {
>>>>>>   int id3_changed; /* ID3 tag data has changed at some point */
>>>>>>   ID3v2ExtraMeta *id3_deferred_extra; /* stored here until subdemuxer
>>>>>> is opened */
>>>>>> 
>>>>>> +    /* Used in case of SAMPLE-AES encryption method */
>>>>>> +    HLSAudioSetupInfo audio_setup_info;
>>>>>> +    HLSCryptoContext  crypto_ctx;
>>>>>> +
>>>>>>   int64_t seek_timestamp;
>>>>>>   int seek_flags;
>>>>>>   int seek_stream_index; /* into subdemuxer stream array */
>>>>>> @@ -266,6 +273,8 @@ static void free_playlist_list(HLSContext *c)
>>>>>>           pls->ctx->pb = NULL;
>>>>>>           avformat_close_input(&pls->ctx);
>>>>>>       }
>>>>>> +        if (pls->crypto_ctx.aes_ctx)
>>>>>> +             av_free(pls->crypto_ctx.aes_ctx);
>>>>>>       av_free(pls);
>>>>>>   }
>>>>>>   av_freep(&c->playlists);
>>>>>> @@ -994,7 +1003,10 @@ fail:
>>>>>> 
>>>>>> static struct segment *current_segment(struct playlist *pls)
>>>>>> {
>>>>>> -    return pls->segments[pls->cur_seq_no - pls->start_seq_no];
>>>>>> +    int64_t n = pls->cur_seq_no - pls->start_seq_no;
>>>>>> +    if (n >= pls->n_segments)
>>>>>> +        return NULL;
>>>>>> +    return pls->segments[n];
>>>>>> }
>>>>>> 
>>>>>> static struct segment *next_segment(struct playlist *pls)
>>>>>> @@ -1023,10 +1035,11 @@ static int read_from_url(struct playlist *pls,
>>>>>> struct segment *seg,
>>>>>> 
>>>>>> /* Parse the raw ID3 data and pass contents to caller */
>>>>>> static void parse_id3(AVFormatContext *s, AVIOContext *pb,
>>>>>> -                      AVDictionary **metadata, int64_t *dts,
>>>>>> +                      AVDictionary **metadata, int64_t *dts,
>>>>>> HLSAudioSetupInfo *audio_setup_info,
>>>>>>                     ID3v2ExtraMetaAPIC **apic, ID3v2ExtraMeta
>>>>>> **extra_meta)
>>>>>> {
>>>>>>   static const char id3_priv_owner_ts[] =
>>>>>> "com.apple.streaming.transportStreamTimestamp";
>>>>>> +    static const char id3_priv_owner_audio_setup[] =
>>>>>> "com.apple.streaming.audioDescription";
>>>>>>   ID3v2ExtraMeta *meta;
>>>>>> 
>>>>>>   ff_id3v2_read_dict(pb, metadata, ID3v2_DEFAULT_MAGIC, extra_meta);
>>>>>> @@ -1041,6 +1054,8 @@ static void parse_id3(AVFormatContext *s,
>>>>>> AVIOContext *pb,
>>>>>>                   *dts = ts;
>>>>>>               else
>>>>>>                   av_log(s, AV_LOG_ERROR, "Invalid HLS ID3 audio
>>>>>> timestamp %"PRId64"\n", ts);
>>>>>> +            } else if (priv->datasize >= 8 && !strcmp(priv->owner,
>>>>>> id3_priv_owner_audio_setup)) {
>>>>>> +                ff_hls_read_audio_setup_info(audio_setup_info,
>>>>>> priv->data, priv->datasize);
>>>>>>           }
>>>>>>       } else if (!strcmp(meta->tag, "APIC") && apic)
>>>>>>           *apic = &meta->data.apic;
>>>>>> @@ -1084,7 +1099,7 @@ static void handle_id3(AVIOContext *pb, struct
>>>>>> playlist *pls)
>>>>>>   ID3v2ExtraMeta *extra_meta = NULL;
>>>>>>   int64_t timestamp = AV_NOPTS_VALUE;
>>>>>> 
>>>>>> -    parse_id3(pls->ctx, pb, &metadata, &timestamp, &apic, &extra_meta);
>>>>>> +    parse_id3(pls->ctx, pb, &metadata, &timestamp,
>>>>>> &pls->audio_setup_info, &apic, &extra_meta);
>>>>>> 
>>>>>>   if (timestamp != AV_NOPTS_VALUE) {
>>>>>>       pls->id3_mpegts_timestamp = timestamp;
>>>>>> @@ -1238,10 +1253,7 @@ static int open_input(HLSContext *c, struct
>>>>>> playlist *pls, struct segment *seg,
>>>>>>   av_log(pls->parent, AV_LOG_VERBOSE, "HLS request for url '%s',
>>>> offset
>>>>>> %"PRId64", playlist %d\n",
>>>>>>          seg->url, seg->url_offset, pls->index);
>>>>>> 
>>>>>> -    if (seg->key_type == KEY_NONE) {
>>>>>> -        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
>>>>>> &is_http);
>>>>>> -    } else if (seg->key_type == KEY_AES_128) {
>>>>>> -        char iv[33], key[33], url[MAX_URL_SIZE];
>>>>>> +    if (seg->key_type == KEY_AES_128 || seg->key_type ==
>>>> KEY_SAMPLE_AES) {
>>>>>>       if (strcmp(seg->key, pls->key_url)) {
>>>>>>           AVIOContext *pb = NULL;
>>>>>>           if (open_url(pls->parent, &pb, seg->key, &c->avio_opts,
>>>> opts,
>>>>>> NULL) == 0) {
>>>>>> @@ -1257,6 +1269,10 @@ static int open_input(HLSContext *c, struct
>>>>>> playlist *pls, struct segment *seg,
>>>>>>           }
>>>>>>           av_strlcpy(pls->key_url, seg->key, sizeof(pls->key_url));
>>>>>>       }
>>>>>> +    }
>>>>>> +
>>>>>> +    if (seg->key_type == KEY_AES_128) {
>>>>>> +        char iv[33], key[33], url[MAX_URL_SIZE];
>>>>>>       ff_data_to_hex(iv, seg->iv, sizeof(seg->iv), 0);
>>>>>>       ff_data_to_hex(key, pls->key, sizeof(pls->key), 0);
>>>>>>       iv[32] = key[32] = '\0';
>>>>>> @@ -1273,13 +1289,9 @@ static int open_input(HLSContext *c, struct
>>>>>> playlist *pls, struct segment *seg,
>>>>>>           goto cleanup;
>>>>>>       }
>>>>>>       ret = 0;
>>>>>> -    } else if (seg->key_type == KEY_SAMPLE_AES) {
>>>>>> -        av_log(pls->parent, AV_LOG_ERROR,
>>>>>> -               "SAMPLE-AES encryption is not supported yet\n");
>>>>>> -        ret = AVERROR_PATCHWELCOME;
>>>>>> +    } else {
>>>>>> +        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
>>>>>> &is_http);
>>>>>>   }
>>>>>> -    else
>>>>>> -      ret = AVERROR(ENOSYS);
>>>>>> 
>>>>>>   /* Seek to the requested position. If this was a HTTP request, the
>>>>>> offset
>>>>>>    * should already be where want it to, but this allows e.g. local
>>>>>> testing
>>>>>> @@ -1948,6 +1960,7 @@ static int hls_read_header(AVFormatContext *s)
>>>>>>       struct playlist *pls = c->playlists[i];
>>>>>>       char *url;
>>>>>>       ff_const59 AVInputFormat *in_fmt = NULL;
>>>>>> +        struct segment *seg = NULL;
>>>>>> 
>>>>>>       if (!(pls->ctx = avformat_alloc_context())) {
>>>>>>           ret = AVERROR(ENOMEM);
>>>>>> @@ -1980,8 +1993,41 @@ static int hls_read_header(AVFormatContext *s)
>>>>>>           pls->ctx = NULL;
>>>>>>           goto fail;
>>>>>>       }
>>>>>> +
>>>>>>       ffio_init_context(&pls->pb, pls->read_buffer,
>>>>>> INITIAL_BUFFER_SIZE, 0, pls,
>>>>>>                         read_data, NULL, NULL);
>>>>>> +
>>>>>> +        /*
>>>>>> +         * If encryption scheme is SAMPLE-AES, try to read  ID3 tags of
>>>>>> +         * external audio track that contains audio setup information
>>>>>> +         */
>>>>>> +        seg = current_segment(pls);
>>>>>> +        if (seg && seg->key_type == KEY_SAMPLE_AES &&
>>>> pls->n_renditions >
>>>>>> 0 &&
>>>>>> +            pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO) {
>>>>>> +            uint8_t buf[HLS_MAX_ID3_TAGS_DATA_LEN];
>>>>>> +            if ((ret = avio_read(&pls->pb, buf,
>>>>>> HLS_MAX_ID3_TAGS_DATA_LEN)) < 0) {
>>>>>> +                /* Fail if error was not end of file */
>>>>>> +                if (ret != AVERROR_EOF) {
>>>>>> +                    avformat_free_context(pls->ctx);
>>>>>> +                    pls->ctx = NULL;
>>>>>> +                    goto fail;
>>>>>> +                }
>>>>>> +            }
>>>>>> +            ret = 0;
>>>>>> +        }
>>>>>> +
>>>>>> +        /*
>>>>>> +         * If encryption scheme is SAMPLE-AES and audio setup
>>>> information
>>>>>> is present in external audio track,
>>>>>> +         * use that information to find the media format, otherwise
>>>> probe
>>>>>> input data
>>>>>> +         */
>>>>>> +        if (seg && seg->key_type == KEY_SAMPLE_AES &&
>>>>>> pls->is_id3_timestamped &&
>>>>>> +            pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) {
>>>>>> +            void *iter = NULL;
>>>>>> +            while ((in_fmt = (ff_const59 AVInputFormat
>>>>>> *)av_demuxer_iterate(&iter)))
>>>>>> +                if (in_fmt->raw_codec_id ==
>>>>>> pls->audio_setup_info.codec_id) {
>>>>>> +                    break;
>>>>>> +                }
>>>>>> +        } else {
>>>>>>       pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 *
>>>> 4;
>>>>>>       pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ?
>>>>>> s->max_analyze_duration : 4 * AV_TIME_BASE;
>>>>>>       pls->ctx->interrupt_callback = s->interrupt_callback;
>>>>>> @@ -1999,6 +2045,25 @@ static int hls_read_header(AVFormatContext *s)
>>>>>>           goto fail;
>>>>>>       }
>>>>>>       av_free(url);
>>>>>> +        }
>>>>>> +
>>>>>> +        if (seg && seg->key_type == KEY_SAMPLE_AES) {
>>>>>> +            if (!pls->is_id3_timestamped && pls->n_renditions > 0 &&
>>>>>> pls->renditions[0]->type != AVMEDIA_TYPE_AUDIO &&
>>>>>> +                strcmp(in_fmt->name, "mpegts")) {
>>>>>> +                av_log(s, AV_LOG_ERROR, "SAMPLE-AES encryption is not
>>>>>> supported for fragmented MP4 format yet\n");
>>>>>> +                ret = AVERROR_PATCHWELCOME;
>>>>>> +            } else {
>>>>>> +                pls->crypto_ctx.aes_ctx = av_aes_alloc();
>>>>>> +                if (!pls->crypto_ctx.aes_ctx)
>>>>>> +                    ret = AVERROR(ENOMEM);
>>>>>> +            }
>>>>>> +            if (ret != 0) {
>>>>>> +                avformat_free_context(pls->ctx);
>>>>>> +                pls->ctx = NULL;
>>>>>> +                goto fail;
>>>>>> +            }
>>>>>> +        }
>>>>>> +
>>>>>>       pls->ctx->pb       = &pls->pb;
>>>>>>       pls->ctx->io_open  = nested_io_open;
>>>>>>       pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
>>>>>> @@ -2027,7 +2092,12 @@ static int hls_read_header(AVFormatContext *s)
>>>>>>        * on us if they want to.
>>>>>>        */
>>>>>>       if (pls->is_id3_timestamped || (pls->n_renditions > 0 &&
>>>>>> pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO)) {
>>>>>> +            if (seg && seg->key_type == KEY_SAMPLE_AES &&
>>>>>> pls->audio_setup_info.setup_data_length > 0 &&
>>>>>> +                pls->ctx->nb_streams == 1)
>>>>>> +                ret =
>>>> ff_hls_parse_audio_setup_info(pls->ctx->streams[0],
>>>>>> &pls->audio_setup_info);
>>>>>> +            else
>>>>>>           ret = avformat_find_stream_info(pls->ctx, NULL);
>>>>>> +
>>>>>>           if (ret < 0)
>>>>>>               goto fail;
>>>>>>       }
>>>>>> @@ -2157,6 +2227,7 @@ static int hls_read_packet(AVFormatContext *s,
>>>>>> AVPacket *pkt)
>>>>>>           while (1) {
>>>>>>               int64_t ts_diff;
>>>>>>               AVRational tb;
>>>>>> +                struct segment *seg = NULL;
>>>>>>               ret = av_read_frame(pls->ctx, &pls->pkt);
>>>>>>               if (ret < 0) {
>>>>>>                   if (!avio_feof(&pls->pb) && ret != AVERROR_EOF)
>>>>>> @@ -2175,6 +2246,14 @@ static int hls_read_packet(AVFormatContext *s,
>>>>>> AVPacket *pkt)
>>>>>>                           get_timebase(pls), AV_TIME_BASE_Q);
>>>>>>               }
>>>>>> 
>>>>>> +                seg = current_segment(pls);
>>>>>> +                if (seg && seg->key_type == KEY_SAMPLE_AES) {
>>>>>> +                    enum AVCodecID codec_id =
>>>>>> pls->ctx->streams[pls->pkt.stream_index]->codecpar->codec_id;
>>>>>> +                    memcpy(pls->crypto_ctx.iv, seg->iv,
>>>> sizeof(seg->iv));
>>>>>> +                    memcpy(pls->crypto_ctx.key, pls->key,
>>>>>> sizeof(pls->key));
>>>>>> +                    ff_hls_decrypt_frame(codec_id, &pls->crypto_ctx,
>>>>>> &pls->pkt);
>>>>>> +                }
>>>>>> +
>>>>>>               if (pls->seek_timestamp == AV_NOPTS_VALUE)
>>>>>>                   break;
>>>>>> 
>>>>>> diff --git a/libavformat/hls_sample_aes.c b/libavformat/hls_sample_aes.c
>>>>>> new file mode 100644
>>>>>> index 0000000000..0407a15b0f
>>>>>> --- /dev/null
>>>>>> +++ b/libavformat/hls_sample_aes.c
>>>>>> @@ -0,0 +1,391 @@
>>>>>> +/*
>>>>>> + * Apple HTTP Live Streaming Sample Encryption/Decryption
>>>>>> + *
>>>>>> + * Copyright (c) 2021 Nachiket Tarate
>>>>>> + *
>>>>>> + * This file is part of FFmpeg.
>>>>>> + *
>>>>>> + * FFmpeg is free software; you can redistribute it and/or
>>>>>> + * modify it under the terms of the GNU Lesser General Public
>>>>>> + * License as published by the Free Software Foundation; either
>>>>>> + * version 2.1 of the License, or (at your option) any later version.
>>>>>> + *
>>>>>> + * FFmpeg is distributed in the hope that it will be useful,
>>>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>>>>>> + * Lesser General Public License for more details.
>>>>>> + *
>>>>>> + * You should have received a copy of the GNU Lesser General Public
>>>>>> + * License along with FFmpeg; if not, write to the Free Software
>>>>>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>>>>>> 02110-1301 USA
>>>>>> + */
>>>>>> +
>>>>>> +/**
>>>>>> + * @file
>>>>>> + * Apple HTTP Live Streaming Sample Encryption
>>>>>> + *
>>>>>> 
>>>> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
>>>>>> + */
>>>>>> +
>>>>>> +#include "hls_sample_aes.h"
>>>>>> +
>>>>>> +#include "libavcodec/adts_header.h"
>>>>>> +#include "libavcodec/adts_parser.h"
>>>>>> +#include "libavcodec/ac3_parser_internal.h"
>>>>>> +
>>>>>> +
>>>>>> +typedef struct NALUnit {
>>>>>> +    uint8_t     *data;
>>>>>> +    int         type;
>>>>>> +    int         length;
>>>>>> +    int         start_code_length;
>>>>>> +} NALUnit;
>>>>>> +
>>>>>> +typedef struct AudioFrame {
>>>>>> +    uint8_t     *data;
>>>>>> +    int         length;
>>>>>> +    int         header_length;
>>>>>> +} AudioFrame;
>>>>>> +
>>>>>> +typedef struct CodecParserContext {
>>>>>> +    const uint8_t   *buf_ptr;
>>>>>> +    const uint8_t   *buf_end;
>>>>>> +} CodecParserContext;
>>>>>> +
>>>>>> +static const int eac3_sample_rate_tab[] = { 48000, 44100, 32000, 0 };
>>>>>> +
>>>>>> +void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const
>>>> uint8_t
>>>>>> *buf, size_t size)
>>>>>> +{
>>>>>> +    if (size < 8)
>>>>>> +        return;
>>>>>> +
>>>>>> +    info->codec_tag             = AV_RL32(buf);
>>>>>> +
>>>>>> +    if (info->codec_tag == MKTAG('z','a', 'a', 'c'))
>>>>>> +        info->codec_id = AV_CODEC_ID_AAC;
>>>>>> +    else if (info->codec_tag == MKTAG('z','a', 'c', '3'))
>>>>>> +        info->codec_id = AV_CODEC_ID_AC3;
>>>>>> +    else if (info->codec_tag == MKTAG('z','e', 'c', '3'))
>>>>>> +        info->codec_id = AV_CODEC_ID_EAC3;
>>>>>> +    else
>>>>>> +        info->codec_id = AV_CODEC_ID_NONE;
>>>>>> +
>>>>>> +    buf += 4;
>>>>>> +    info->priming               = AV_RL16(buf);
>>>>>> +    buf += 2;
>>>>>> +    info->version               = *buf++;
>>>>>> +    info->setup_data_length     = *buf++;
>>>>>> +
>>>>>> +    if (info->setup_data_length > size - 8)
>>>>>> +        info->setup_data_length = size - 8;
>>>>>> +
>>>>>> +    if (info->setup_data_length > HLS_MAX_AUDIO_SETUP_DATA_LEN)
>>>>>> +        return;
>>>>>> +
>>>>>> +    memcpy(info->setup_data, buf, info->setup_data_length);
>>>>>> +}
>>>>>> +
>>>>>> +int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo
>>>> *info)
>>>>>> +{
>>>>>> +    int ret = 0;
>>>>>> +
>>>>>> +    st->codecpar->codec_tag = info->codec_tag;
>>>>>> +
>>>>>> +    if (st->codecpar->codec_id == AV_CODEC_ID_AAC)
>>>>>> +        return 0;
>>>>>> +
>>>>>> +    if (st->codecpar->codec_id != AV_CODEC_ID_AC3 &&
>>>>>> st->codecpar->codec_id != AV_CODEC_ID_EAC3)
>>>>>> +        return AVERROR_INVALIDDATA;
>>>>>> +
>>>>>> +    if (st->codecpar->codec_id == AV_CODEC_ID_AC3) {
>>>>>> +
>>>>>> +        AC3HeaderInfo *ac3hdr = NULL;
>>>>>> +
>>>>>> +        ret = avpriv_ac3_parse_header(&ac3hdr, info->setup_data,
>>>>>> info->setup_data_length);
>>>>>> +        if (ret < 0) {
>>>>>> +            if (ret != AVERROR(ENOMEM))
>>>>>> +                av_free(ac3hdr);
>>>>>> +            return ret;
>>>>>> +        }
>>>>>> +
>>>>>> +        st->codecpar->sample_rate       = ac3hdr->sample_rate;
>>>>>> +        st->codecpar->channels          = ac3hdr->channels;
>>>>>> +        st->codecpar->channel_layout    = ac3hdr->channel_layout;
>>>>>> +        st->codecpar->bit_rate          = ac3hdr->bit_rate;
>>>>>> +
>>>>>> +        av_free(ac3hdr);
>>>>>> +    } else {  /*  Parse 'dec3' EC3SpecificBox */
>>>>>> +
>>>>>> +        GetBitContext gb;
>>>>>> +        int data_rate, fscod, acmod, lfeon;
>>>>>> +
>>>>>> +        ret = init_get_bits8(&gb, info->setup_data,
>>>>>> info->setup_data_length);
>>>>>> +        if (ret < 0)
>>>>>> +            return AVERROR_INVALIDDATA;
>>>>>> +
>>>>>> +        data_rate = get_bits(&gb, 13);
>>>>>> +        skip_bits(&gb, 3);
>>>>>> +        fscod = get_bits(&gb, 2);
>>>>>> +        skip_bits(&gb, 10);
>>>>>> +        acmod = get_bits(&gb, 3);
>>>>>> +        lfeon = get_bits(&gb, 1);
>>>>>> +
>>>>>> +        st->codecpar->sample_rate = eac3_sample_rate_tab[fscod];
>>>>>> +
>>>>>> +        st->codecpar->channel_layout =
>>>>>> avpriv_ac3_channel_layout_tab[acmod];
>>>>>> +        if (lfeon)
>>>>>> +            st->codecpar->channel_layout |= AV_CH_LOW_FREQUENCY;
>>>>>> +
>>>>>> +        st->codecpar->channels =
>>>>>> av_get_channel_layout_nb_channels(st->codecpar->channel_layout);
>>>>>> +
>>>>>> +        st->codecpar->bit_rate = data_rate*1000;
>>>>>> +    }
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>> +/*
>>>>>> + * Remove start code emulation prevention 0x03 bytes
>>>>>> + */
>>>>>> +static void remove_scep_3_bytes(NALUnit *nalu)
>>>>>> +{
>>>>>> +    int i = 0;
>>>>>> +    int j = 0;
>>>>>> +
>>>>>> +    uint8_t *data = nalu->data;
>>>>>> +
>>>>>> +    while (i < nalu->length) {
>>>>>> +        if (nalu->length - i > 3 && AV_RB24(&data[i]) == 0x000003) {
>>>>>> +            data[j++] = data[i++];
>>>>>> +            data[j++] = data[i++];
>>>>>> +            i++;
>>>>>> +        } else {
>>>>>> +            data[j++] = data[i++];
>>>>>> +        }
>>>>>> +    }
>>>>>> +
>>>>>> +    nalu->length = j;
>>>>>> +}
>>>>>> +
>>>>>> +static int get_next_nal_unit(CodecParserContext *ctx, NALUnit *nalu)
>>>>>> +{
>>>>>> +    const uint8_t *nalu_start = ctx->buf_ptr;
>>>>>> +
>>>>>> +    if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr) ==
>>>>>> 0x00000001)
>>>>>> +        nalu->start_code_length = 4;
>>>>>> +    else if (ctx->buf_end - ctx->buf_ptr >= 3 && AV_RB24(ctx->buf_ptr)
>>>> ==
>>>>>> 0x000001)
>>>>>> +        nalu->start_code_length = 3;
>>>>>> +    else /* No start code at the beginning of the NAL unit */
>>>>>> +        return -1;
>>>>>> +
>>>>>> +    ctx->buf_ptr += nalu->start_code_length;
>>>>>> +
>>>>>> +    while (ctx->buf_ptr < ctx->buf_end) {
>>>>>> +        if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr)
>>>> ==
>>>>>> 0x00000001)
>>>>>> +            break;
>>>>>> +        else if (ctx->buf_end - ctx->buf_ptr >= 3 &&
>>>>>> AV_RB24(ctx->buf_ptr) == 0x000001)
>>>>>> +            break;
>>>>>> +        ctx->buf_ptr++;
>>>>>> +    }
>>>>>> +
>>>>>> +    nalu->data  = (uint8_t *)nalu_start + nalu->start_code_length;
>>>>>> +    nalu->length = ctx->buf_ptr - nalu->data;
>>>>>> +    nalu->type  = *nalu->data & 0x1F;
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int decrypt_nal_unit(HLSCryptoContext *crypto_ctx, NALUnit
>>>> *nalu)
>>>>>> +{
>>>>>> +    int ret = 0;
>>>>>> +    int rem_bytes;
>>>>>> +    uint8_t *data;
>>>>>> +    uint8_t iv[16];
>>>>>> +
>>>>>> +    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
>>>>>> +    if (ret < 0)
>>>>>> +        return ret;
>>>>>> +
>>>>>> +    /* Remove start code emulation prevention 0x03 bytes */
>>>>>> +    remove_scep_3_bytes(nalu);
>>>>>> +
>>>>>> +    data = nalu->data + 32;
>>>>>> +    rem_bytes = nalu->length - 32;
>>>>>> +
>>>>>> +    memcpy(iv, crypto_ctx->iv, 16);
>>>>>> +
>>>>>> +    while (rem_bytes > 0) {
>>>>>> +        if (rem_bytes > 16) {
>>>>>> +            av_aes_crypt(crypto_ctx->aes_ctx, data, data, 1, iv, 1);
>>>>>> +            data += 16;
>>>>>> +            rem_bytes -= 16;
>>>>>> +        }
>>>>>> +        data += FFMIN(144, rem_bytes);
>>>>>> +        rem_bytes -= FFMIN(144, rem_bytes);
>>>>>> +    }
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int decrypt_video_frame(HLSCryptoContext *crypto_ctx, AVPacket
>>>>>> *pkt)
>>>>>> +{
>>>>>> +    int ret = 0;
>>>>>> +    CodecParserContext  ctx;
>>>>>> +    NALUnit nalu;
>>>>>> +    uint8_t *data_ptr;
>>>>>> +    int move_nalu = 0;
>>>>>> +
>>>>>> +    memset(&ctx, 0, sizeof(ctx));
>>>>>> +    ctx.buf_ptr  = pkt->data;
>>>>>> +    ctx.buf_end = pkt->data + pkt->size;
>>>>>> +
>>>>>> +    data_ptr = pkt->data;
>>>>>> +
>>>>>> +    while (ctx.buf_ptr < ctx.buf_end) {
>>>>>> +        memset(&nalu, 0, sizeof(nalu));
>>>>>> +        ret = get_next_nal_unit(&ctx, &nalu);
>>>>>> +        if (ret < 0)
>>>>>> +            return ret;
>>>>>> +        if ((nalu.type == 0x01 || nalu.type == 0x05) && nalu.length >
>>>> 48)
>>>>>> {
>>>>>> +            int encrypted_nalu_length = nalu.length;
>>>>>> +            ret = decrypt_nal_unit(crypto_ctx, &nalu);
>>>>>> +            if (ret < 0)
>>>>>> +                return ret;
>>>>>> +            move_nalu = nalu.length != encrypted_nalu_length;
>>>>>> +        }
>>>>>> +        if (move_nalu)
>>>>>> +            memmove(data_ptr, nalu.data - nalu.start_code_length,
>>>>>> nalu.start_code_length + nalu.length);
>>>>>> +        data_ptr += nalu.start_code_length + nalu.length;
>>>>>> +    }
>>>>>> +
>>>>>> +    av_shrink_packet(pkt, data_ptr - pkt->data);
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int get_next_adts_frame(CodecParserContext *ctx, AudioFrame
>>>> *frame)
>>>>>> +{
>>>>>> +    int ret = 0;
>>>>>> +
>>>>>> +    AACADTSHeaderInfo *adts_hdr = NULL;
>>>>>> +
>>>>>> +    /* Find next sync word 0xFFF */
>>>>>> +    while (ctx->buf_ptr < ctx->buf_end - 1) {
>>>>>> +        if (*ctx->buf_ptr == 0xFF && *(ctx->buf_ptr + 1) & 0xF0 ==
>>>> 0xF0)
>>>>>> +            break;
>>>>>> +        ctx->buf_ptr++;
>>>>>> +    }
>>>>>> +
>>>>>> +    if (ctx->buf_ptr >= ctx->buf_end - 1)
>>>>>> +        return -1;
>>>>>> +
>>>>>> +    frame->data = (uint8_t*)ctx->buf_ptr;
>>>>>> +
>>>>>> +    ret = avpriv_adts_header_parse (&adts_hdr, frame->data,
>>>> ctx->buf_end
>>>>>> - frame->data);
>>>>>> +    if (ret < 0)
>>>>>> +        return ret;
>>>>>> +
>>>>>> +    frame->header_length = adts_hdr->crc_absent ?
>>>> AV_AAC_ADTS_HEADER_SIZE
>>>>>> : AV_AAC_ADTS_HEADER_SIZE + 2;
>>>>>> +    frame->length = adts_hdr->frame_length;
>>>>>> +
>>>>>> +    av_free(adts_hdr);
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int get_next_ac3_eac3_sync_frame(CodecParserContext *ctx,
>>>>>> AudioFrame *frame)
>>>>>> +{
>>>>>> +    int ret = 0;
>>>>>> +
>>>>>> +    AC3HeaderInfo *hdr = NULL;
>>>>>> +
>>>>>> +    /* Find next sync word 0x0B77 */
>>>>>> +    while (ctx->buf_ptr < ctx->buf_end - 1) {
>>>>>> +        if (*ctx->buf_ptr == 0x0B && *(ctx->buf_ptr + 1) == 0x77)
>>>>>> +            break;
>>>>>> +        ctx->buf_ptr++;
>>>>>> +    }
>>>>>> +
>>>>>> +    if (ctx->buf_ptr >= ctx->buf_end - 1)
>>>>>> +        return -1;
>>>>>> +
>>>>>> +    frame->data = (uint8_t*)ctx->buf_ptr;
>>>>>> +    frame->header_length = 0;
>>>>>> +
>>>>>> +    ret = avpriv_ac3_parse_header(&hdr, frame->data, ctx->buf_end -
>>>>>> frame->data);
>>>>>> +    if (ret < 0) {
>>>>>> +        if (ret != AVERROR(ENOMEM))
>>>>>> +            av_free(hdr);
>>>>>> +        return ret;
>>>>>> +    }
>>>>>> +
>>>>>> +    frame->length = hdr->frame_size;
>>>>>> +
>>>>>> +    av_free(hdr);
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int get_next_sync_frame(enum AVCodecID codec_id,
>>>>>> CodecParserContext *ctx, AudioFrame *frame)
>>>>>> +{
>>>>>> +    if (codec_id == AV_CODEC_ID_AAC)
>>>>>> +        return get_next_adts_frame(ctx, frame);
>>>>>> +    else if (codec_id == AV_CODEC_ID_AC3 || codec_id ==
>>>> AV_CODEC_ID_EAC3)
>>>>>> +        return get_next_ac3_eac3_sync_frame(ctx, frame);
>>>>>> +    else
>>>>>> +        return AVERROR_INVALIDDATA;
>>>>>> +}
>>>>>> +
>>>>>> +static int decrypt_sync_frame(enum AVCodecID codec_id, HLSCryptoContext
>>>>>> *crypto_ctx, AudioFrame *frame)
>>>>>> +{
>>>>>> +    int ret = 0;
>>>>>> +    uint8_t *data;
>>>>>> +    int num_of_encrypted_blocks;
>>>>>> +
>>>>>> +    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
>>>>>> +    if (ret < 0)
>>>>>> +        return ret;
>>>>>> +
>>>>>> +    data = frame->data + frame->header_length + 16;
>>>>>> +
>>>>>> +    num_of_encrypted_blocks = (frame->length - frame->header_length -
>>>>>> 16)/16;
>>>>>> +
>>>>>> +    av_aes_crypt(crypto_ctx->aes_ctx, data, data,
>>>>>> num_of_encrypted_blocks, crypto_ctx->iv, 1);
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int decrypt_audio_frame(enum AVCodecID codec_id,
>>>> HLSCryptoContext
>>>>>> *crypto_ctx, AVPacket *pkt)
>>>>>> +{
>>>>>> +    int ret = 0;
>>>>>> +    CodecParserContext  ctx;
>>>>>> +    AudioFrame frame;
>>>>>> +
>>>>>> +    memset(&ctx, 0, sizeof(ctx));
>>>>>> +    ctx.buf_ptr        = pkt->data;
>>>>>> +    ctx.buf_end = pkt->data + pkt->size;
>>>>>> +
>>>>>> +    while (ctx.buf_ptr < ctx.buf_end) {
>>>>>> +        memset(&frame, 0, sizeof(frame));
>>>>>> +        ret = get_next_sync_frame(codec_id, &ctx, &frame);
>>>>>> +        if (ret < 0)
>>>>>> +            return ret;
>>>>>> +        if (frame.length - frame.header_length > 31) {
>>>>>> +            ret = decrypt_sync_frame(codec_id, crypto_ctx, &frame);
>>>>>> +            if (ret < 0)
>>>>>> +                return ret;
>>>>>> +        }
>>>>>> +        ctx.buf_ptr += frame.length;
>>>>>> +    }
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>> +int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext
>>>>>> *crypto_ctx, AVPacket *pkt)
>>>>>> +{
>>>>>> +    if (codec_id == AV_CODEC_ID_H264)
>>>>>> +        return decrypt_video_frame(crypto_ctx, pkt);
>>>>>> +    else if (codec_id == AV_CODEC_ID_AAC || codec_id == AV_CODEC_ID_AC3
>>>>>> || codec_id == AV_CODEC_ID_EAC3)
>>>>>> +        return decrypt_audio_frame(codec_id, crypto_ctx, pkt);
>>>>>> +
>>>>>> +    return AVERROR_INVALIDDATA;
>>>>>> +}
>>>>>> diff --git a/libavformat/hls_sample_aes.h b/libavformat/hls_sample_aes.h
>>>>>> new file mode 100644
>>>>>> index 0000000000..cf80e41cb0
>>>>>> --- /dev/null
>>>>>> +++ b/libavformat/hls_sample_aes.h
>>>>>> @@ -0,0 +1,66 @@
>>>>>> +/*
>>>>>> + * Apple HTTP Live Streaming Sample Encryption/Decryption
>>>>>> + *
>>>>>> + * Copyright (c) 2021 Nachiket Tarate
>>>>>> + *
>>>>>> + * This file is part of FFmpeg.
>>>>>> + *
>>>>>> + * FFmpeg is free software; you can redistribute it and/or
>>>>>> + * modify it under the terms of the GNU Lesser General Public
>>>>>> + * License as published by the Free Software Foundation; either
>>>>>> + * version 2.1 of the License, or (at your option) any later version.
>>>>>> + *
>>>>>> + * FFmpeg is distributed in the hope that it will be useful,
>>>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>>>>>> + * Lesser General Public License for more details.
>>>>>> + *
>>>>>> + * You should have received a copy of the GNU Lesser General Public
>>>>>> + * License along with FFmpeg; if not, write to the Free Software
>>>>>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>>>>>> 02110-1301 USA
>>>>>> + */
>>>>>> +
>>>>>> +/**
>>>>>> + * @file
>>>>>> + * Apple HTTP Live Streaming Sample Encryption
>>>>>> + *
>>>>>> 
>>>> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
>>>>>> + */
>>>>>> +
>>>>>> +#ifndef AVFORMAT_HLS_SAMPLE_AES_H
>>>>>> +#define AVFORMAT_HLS_SAMPLE_AES_H
>>>>>> +
>>>>>> +#include <stdint.h>
>>>>>> +
>>>>>> +#include "avformat.h"
>>>>>> +
>>>>>> +#include "libavcodec/avcodec.h"
>>>>>> +#include "libavutil/aes.h"
>>>>>> +
>>>>>> +#define HLS_MAX_ID3_TAGS_DATA_LEN       138
>>>>>> +#define HLS_MAX_AUDIO_SETUP_DATA_LEN    10
>>>>>> +
>>>>>> +
>>>>>> +typedef struct HLSCryptoContext {
>>>>>> +    struct AVAES   *aes_ctx;
>>>>>> +    uint8_t            key[16];
>>>>>> +    uint8_t            iv[16];
>>>>>> +} HLSCryptoContext;
>>>>>> +
>>>>>> +typedef struct HLSAudioSetupInfo {
>>>>>> +    enum AVCodecID      codec_id;
>>>>>> +    uint32_t            codec_tag;
>>>>>> +    uint16_t            priming;
>>>>>> +    uint8_t             version;
>>>>>> +    uint8_t             setup_data_length;
>>>>>> +    uint8_t             setup_data[HLS_MAX_AUDIO_SETUP_DATA_LEN];
>>>>>> +} HLSAudioSetupInfo;
>>>>>> +
>>>>>> +
>>>>>> +void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const
>>>> uint8_t
>>>>>> *buf, size_t size);
>>>>>> +
>>>>>> +int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo
>>>> *info);
>>>>>> +
>>>>>> +int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext
>>>>>> *crypto_ctx, AVPacket *pkt);
>>>>>> +
>>>>>> +#endif /* AVFORMAT_HLS_SAMPLE_AES_H */
>>>>>> +
>>>>>> diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
>>>>>> index e283ec09d7..dc611ae788 100644
>>>>>> --- a/libavformat/mpegts.c
>>>>>> +++ b/libavformat/mpegts.c
>>>>>> @@ -839,6 +839,16 @@ static const StreamType MISC_types[] = {
>>>>>>   { 0 },
>>>>>> };
>>>>>> 
>>>>>> +/* HLS Sample Encryption Types  */
>>>>>> +static const StreamType HLS_SAMPLE_ENC_types[] = {
>>>>>> +    { 0xdb, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264},
>>>>>> +    { 0xcf, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
>>>>>> +    { 0xc1, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 },
>>>>>> +    { 0xc2, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3},
>>>>>> +    { 0 },
>>>>>> +};
>>>>>> +
>>>>>> +
>>>>>> static const StreamType REGD_types[] = {
>>>>>>   { MKTAG('d', 'r', 'a', 'c'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC
>>>> },
>>>>>>   { MKTAG('A', 'C', '-', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3
>>>> },
>>>>>> @@ -948,6 +958,8 @@ static int mpegts_set_stream_info(AVStream *st,
>>>>>> PESContext *pes,
>>>>>>   }
>>>>>>   if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
>>>>>>       mpegts_find_stream_type(st, pes->stream_type, MISC_types);
>>>>>> +    if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
>>>>>> +        mpegts_find_stream_type(st, pes->stream_type,
>>>>>> HLS_SAMPLE_ENC_types);
>>>>>>   if (st->codecpar->codec_id == AV_CODEC_ID_NONE) {
>>>>>>       st->codecpar->codec_id  = old_codec_id;
>>>>>>       st->codecpar->codec_type = old_codec_type;
>>>>>> --
>>>>>> 2.17.1
>>>>>> 
>>>>>> 
>>>>> _______________________________________________
>>>>> ffmpeg-devel mailing list
>>>>> ffmpeg-devel@ffmpeg.org
>>>>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>>>> 
>>>>> To unsubscribe, visit link above, or email
>>>>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>>>> 
>>>> Thanks
>>>> 
>>>> Steven Liu
>>>> 
>>>> 
>>>> 
>>>> _______________________________________________
>>>> ffmpeg-devel mailing list
>>>> ffmpeg-devel@ffmpeg.org
>>>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>>> 
>>>> To unsubscribe, visit link above, or email
>>>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>>> _______________________________________________
>>> ffmpeg-devel mailing list
>>> ffmpeg-devel@ffmpeg.org
>>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>> 
>>> To unsubscribe, visit link above, or email
>>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>> 
>> Thanks
>> 
>> Steven Liu
>> 
>> 
>> 
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>> 
>> To unsubscribe, visit link above, or email
>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

Thanks

Steven Liu
Nachiket Tarate March 3, 2021, 5:45 a.m. UTC | #7
On Mon, Mar 1, 2021 at 1:05 PM Steven Liu <lq@chinaffmpeg.org> wrote:
>
>
>
> > 2021年3月1日 下午3:22,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
> >
> > On Mon, Mar 1, 2021 at 11:30 AM Steven Liu <lq@chinaffmpeg.org> wrote:
> >>
> >>
> >>
> >>> 2021年3月1日 下午12:55,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
> >>>
> >>> This is an updated version of the patch in which I have added the check. If
> >>> the segments are in Fragmented MP4 format, HLS demuxer quits by giving an
> >>> error message:
> >>>
> >>> "SAMPLE-AES encryption is not supported for fragmented MP4 format yet”
> >> I don’t think  is a good resolution for SAMPLE-AES encryption and decryption.
> >> You should support that if you want support SAMPLE-AES in hls,
> >> because SAMPLE-AES not only support in MPEG-TS, but also support fragment mp4.
> >> Whatever, if you only support mpegts en[de]cryption, it should be a half part patch.
> >
> > Two completely different technologies/specifications have been used
> > for SAMPLE-AES encryption of HLS streams in MPEG-TS and fragmented MP4
> > formats.
> >
> > Fragmented MP4 media segments are encrypted using the 'cbcs' scheme of
> > Common Encryption [CENC]:
> >
> > https://www.iso.org/standard/68042.html
> >
> > Encryption of other media segment formats such as MPEG-TS or external
> > audio tracks containing H.264, AAC, AC-3 and Enhanced AC-3 media
> > streams is described in the Apple's HTTP Live Streaming (HLS) Sample
> > Encryption specifications:
> >
> > https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> >
> > This patch implements the later specifications and enables decryption
> > of media segments in MPEG-TS, AAC, AC-3 and Enhanced AC-3 formats. So
> > I think we should merge this patch right now.
>
> I think sample ads should support not only mpegts, but also support fmp4 too.
> Reference rfc8216 context:
>      An encryption method of SAMPLE-AES means that the Media Segments
>      contain media samples, such as audio or video, that are encrypted
>      using the Advanced Encryption Standard [AES_128].  How these media
>      streams are encrypted and encapsulated in a segment depends on the
>      media encoding and the media format of the segment.  fMP4 Media
>      Segments are encrypted using the 'cbcs' scheme of Common
>      Encryption [COMMON_ENC].  Encryption of other Media Segment
>      formats containing H.264 [H_264], AAC [ISO_14496], AC-3 [AC_3],
>      and Enhanced AC-3 [AC_3] media streams is described in the HTTP
>      Live Streaming (HLS) Sample Encryption specification [SampleEnc].
>      The IV attribute MAY be present; see Section 5.2.
>
> And I saw the m3u8 context, the METHOD is SAMPLE-AES.
>
> (base) liuqi05:ufbuild liuqi$ head -n10  prog_index.m3u8
> #EXTM3U
> #EXT-X-TARGETDURATION:10
> #EXT-X-VERSION:7
> #EXT-X-MEDIA-SEQUENCE:0
> #EXT-X-PLAYLIST-TYPE:VOD
> #EXT-X-INDEPENDENT-SEGMENTS
>
> Focus on this line.
> #EXT-X-KEY:METHOD=SAMPLE-AES,URI="http://127.0.0.1/keyOnly.bin",IV=0xcece273e2737a58ca785e257eb080482
>
>
> #EXT-X-MAP:URI="fileSequence0.mp4"
> #EXTINF:8.31667,
> #EXT-X-BITRATE:7064
>
>
> You can point me the part if I misunderstood SAMPLE-AES.

Your understanding of SAMPLE-AES is absolutely correct.

I am insisting that let us add support for it in 2 phases as
completely 2 different specifications have been used.

Phase 1 : Media segments in MPEG-TS, AAC, AC-3 and Enhanced AC-3
formats (Apple's specifications)

Phase 2 : Media segments in fragmented MP4 format (CENC specifications)

Kindly let me know your opinion.

> >
> > In future, we will add support for CENC or see how can we use existing
> > things from MOV demuxer.
> >
> > Best Regards,
> > Nachiket Tarate
> >
> >>>
> >>> Best Regards,
> >>> Nachiket Tarate
> >>>
> >>> On Mon, Mar 1, 2021 at 10:13 AM Steven Liu <lq@chinaffmpeg.org> wrote:
> >>>
> >>>>
> >>>>
> >>>>> 2021年3月1日 下午12:35,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
> >>>>>
> >>>>> @Steven Liu <lq@chinaffmpeg.org>
> >>>>>
> >>>>> Can we merge this patch ?
> >>>> I’m waiting update patch for fragment mp4 encryption.
> >>>> After new version of the patchset I will test and review.
> >>>>>
> >>>>> Best Regards,
> >>>>> Nachiket Tarate
> >>>>>
> >>>>> On Wed, Feb 24, 2021 at 4:44 PM Nachiket Tarate <
> >>>>> nachiket.programmer@gmail.com> wrote:
> >>>>>
> >>>>>> Apple HTTP Live Streaming Sample Encryption:
> >>>>>>
> >>>>>>
> >>>>>>
> >>>> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> >>>>>>
> >>>>>> Signed-off-by: Nachiket Tarate <nachiket.programmer@gmail.com>
> >>>>>> ---
> >>>>>> libavformat/Makefile         |   2 +-
> >>>>>> libavformat/hls.c            | 105 ++++++++--
> >>>>>> libavformat/hls_sample_aes.c | 391 +++++++++++++++++++++++++++++++++++
> >>>>>> libavformat/hls_sample_aes.h |  66 ++++++
> >>>>>> libavformat/mpegts.c         |  12 ++
> >>>>>> 5 files changed, 562 insertions(+), 14 deletions(-)
> >>>>>> create mode 100644 libavformat/hls_sample_aes.c
> >>>>>> create mode 100644 libavformat/hls_sample_aes.h
> >>>>>>
> >>>>>> diff --git a/libavformat/Makefile b/libavformat/Makefile
> >>>>>> index fcb39ce133..62627d50a6 100644
> >>>>>> --- a/libavformat/Makefile
> >>>>>> +++ b/libavformat/Makefile
> >>>>>> @@ -236,7 +236,7 @@ OBJS-$(CONFIG_HCOM_DEMUXER)              += hcom.o
> >>>>>> pcm.o
> >>>>>> OBJS-$(CONFIG_HDS_MUXER)                 += hdsenc.o
> >>>>>> OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
> >>>>>> OBJS-$(CONFIG_HEVC_MUXER)                += rawenc.o
> >>>>>> -OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o
> >>>>>> +OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o hls_sample_aes.o
> >>>>>> OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o hlsplaylist.o avc.o
> >>>>>> OBJS-$(CONFIG_HNM_DEMUXER)               += hnm.o
> >>>>>> OBJS-$(CONFIG_ICO_DEMUXER)               += icodec.o
> >>>>>> diff --git a/libavformat/hls.c b/libavformat/hls.c
> >>>>>> index af2468ad9b..3cb3853c79 100644
> >>>>>> --- a/libavformat/hls.c
> >>>>>> +++ b/libavformat/hls.c
> >>>>>> @@ -2,6 +2,7 @@
> >>>>>> * Apple HTTP Live Streaming demuxer
> >>>>>> * Copyright (c) 2010 Martin Storsjo
> >>>>>> * Copyright (c) 2013 Anssi Hannula
> >>>>>> + * Copyright (c) 2021 Nachiket Tarate
> >>>>>> *
> >>>>>> * This file is part of FFmpeg.
> >>>>>> *
> >>>>>> @@ -39,6 +40,8 @@
> >>>>>> #include "avio_internal.h"
> >>>>>> #include "id3v2.h"
> >>>>>>
> >>>>>> +#include "hls_sample_aes.h"
> >>>>>> +
> >>>>>> #define INITIAL_BUFFER_SIZE 32768
> >>>>>>
> >>>>>> #define MAX_FIELD_LEN 64
> >>>>>> @@ -145,6 +148,10 @@ struct playlist {
> >>>>>>   int id3_changed; /* ID3 tag data has changed at some point */
> >>>>>>   ID3v2ExtraMeta *id3_deferred_extra; /* stored here until subdemuxer
> >>>>>> is opened */
> >>>>>>
> >>>>>> +    /* Used in case of SAMPLE-AES encryption method */
> >>>>>> +    HLSAudioSetupInfo audio_setup_info;
> >>>>>> +    HLSCryptoContext  crypto_ctx;
> >>>>>> +
> >>>>>>   int64_t seek_timestamp;
> >>>>>>   int seek_flags;
> >>>>>>   int seek_stream_index; /* into subdemuxer stream array */
> >>>>>> @@ -266,6 +273,8 @@ static void free_playlist_list(HLSContext *c)
> >>>>>>           pls->ctx->pb = NULL;
> >>>>>>           avformat_close_input(&pls->ctx);
> >>>>>>       }
> >>>>>> +        if (pls->crypto_ctx.aes_ctx)
> >>>>>> +             av_free(pls->crypto_ctx.aes_ctx);
> >>>>>>       av_free(pls);
> >>>>>>   }
> >>>>>>   av_freep(&c->playlists);
> >>>>>> @@ -994,7 +1003,10 @@ fail:
> >>>>>>
> >>>>>> static struct segment *current_segment(struct playlist *pls)
> >>>>>> {
> >>>>>> -    return pls->segments[pls->cur_seq_no - pls->start_seq_no];
> >>>>>> +    int64_t n = pls->cur_seq_no - pls->start_seq_no;
> >>>>>> +    if (n >= pls->n_segments)
> >>>>>> +        return NULL;
> >>>>>> +    return pls->segments[n];
> >>>>>> }
> >>>>>>
> >>>>>> static struct segment *next_segment(struct playlist *pls)
> >>>>>> @@ -1023,10 +1035,11 @@ static int read_from_url(struct playlist *pls,
> >>>>>> struct segment *seg,
> >>>>>>
> >>>>>> /* Parse the raw ID3 data and pass contents to caller */
> >>>>>> static void parse_id3(AVFormatContext *s, AVIOContext *pb,
> >>>>>> -                      AVDictionary **metadata, int64_t *dts,
> >>>>>> +                      AVDictionary **metadata, int64_t *dts,
> >>>>>> HLSAudioSetupInfo *audio_setup_info,
> >>>>>>                     ID3v2ExtraMetaAPIC **apic, ID3v2ExtraMeta
> >>>>>> **extra_meta)
> >>>>>> {
> >>>>>>   static const char id3_priv_owner_ts[] =
> >>>>>> "com.apple.streaming.transportStreamTimestamp";
> >>>>>> +    static const char id3_priv_owner_audio_setup[] =
> >>>>>> "com.apple.streaming.audioDescription";
> >>>>>>   ID3v2ExtraMeta *meta;
> >>>>>>
> >>>>>>   ff_id3v2_read_dict(pb, metadata, ID3v2_DEFAULT_MAGIC, extra_meta);
> >>>>>> @@ -1041,6 +1054,8 @@ static void parse_id3(AVFormatContext *s,
> >>>>>> AVIOContext *pb,
> >>>>>>                   *dts = ts;
> >>>>>>               else
> >>>>>>                   av_log(s, AV_LOG_ERROR, "Invalid HLS ID3 audio
> >>>>>> timestamp %"PRId64"\n", ts);
> >>>>>> +            } else if (priv->datasize >= 8 && !strcmp(priv->owner,
> >>>>>> id3_priv_owner_audio_setup)) {
> >>>>>> +                ff_hls_read_audio_setup_info(audio_setup_info,
> >>>>>> priv->data, priv->datasize);
> >>>>>>           }
> >>>>>>       } else if (!strcmp(meta->tag, "APIC") && apic)
> >>>>>>           *apic = &meta->data.apic;
> >>>>>> @@ -1084,7 +1099,7 @@ static void handle_id3(AVIOContext *pb, struct
> >>>>>> playlist *pls)
> >>>>>>   ID3v2ExtraMeta *extra_meta = NULL;
> >>>>>>   int64_t timestamp = AV_NOPTS_VALUE;
> >>>>>>
> >>>>>> -    parse_id3(pls->ctx, pb, &metadata, &timestamp, &apic, &extra_meta);
> >>>>>> +    parse_id3(pls->ctx, pb, &metadata, &timestamp,
> >>>>>> &pls->audio_setup_info, &apic, &extra_meta);
> >>>>>>
> >>>>>>   if (timestamp != AV_NOPTS_VALUE) {
> >>>>>>       pls->id3_mpegts_timestamp = timestamp;
> >>>>>> @@ -1238,10 +1253,7 @@ static int open_input(HLSContext *c, struct
> >>>>>> playlist *pls, struct segment *seg,
> >>>>>>   av_log(pls->parent, AV_LOG_VERBOSE, "HLS request for url '%s',
> >>>> offset
> >>>>>> %"PRId64", playlist %d\n",
> >>>>>>          seg->url, seg->url_offset, pls->index);
> >>>>>>
> >>>>>> -    if (seg->key_type == KEY_NONE) {
> >>>>>> -        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
> >>>>>> &is_http);
> >>>>>> -    } else if (seg->key_type == KEY_AES_128) {
> >>>>>> -        char iv[33], key[33], url[MAX_URL_SIZE];
> >>>>>> +    if (seg->key_type == KEY_AES_128 || seg->key_type ==
> >>>> KEY_SAMPLE_AES) {
> >>>>>>       if (strcmp(seg->key, pls->key_url)) {
> >>>>>>           AVIOContext *pb = NULL;
> >>>>>>           if (open_url(pls->parent, &pb, seg->key, &c->avio_opts,
> >>>> opts,
> >>>>>> NULL) == 0) {
> >>>>>> @@ -1257,6 +1269,10 @@ static int open_input(HLSContext *c, struct
> >>>>>> playlist *pls, struct segment *seg,
> >>>>>>           }
> >>>>>>           av_strlcpy(pls->key_url, seg->key, sizeof(pls->key_url));
> >>>>>>       }
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    if (seg->key_type == KEY_AES_128) {
> >>>>>> +        char iv[33], key[33], url[MAX_URL_SIZE];
> >>>>>>       ff_data_to_hex(iv, seg->iv, sizeof(seg->iv), 0);
> >>>>>>       ff_data_to_hex(key, pls->key, sizeof(pls->key), 0);
> >>>>>>       iv[32] = key[32] = '\0';
> >>>>>> @@ -1273,13 +1289,9 @@ static int open_input(HLSContext *c, struct
> >>>>>> playlist *pls, struct segment *seg,
> >>>>>>           goto cleanup;
> >>>>>>       }
> >>>>>>       ret = 0;
> >>>>>> -    } else if (seg->key_type == KEY_SAMPLE_AES) {
> >>>>>> -        av_log(pls->parent, AV_LOG_ERROR,
> >>>>>> -               "SAMPLE-AES encryption is not supported yet\n");
> >>>>>> -        ret = AVERROR_PATCHWELCOME;
> >>>>>> +    } else {
> >>>>>> +        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
> >>>>>> &is_http);
> >>>>>>   }
> >>>>>> -    else
> >>>>>> -      ret = AVERROR(ENOSYS);
> >>>>>>
> >>>>>>   /* Seek to the requested position. If this was a HTTP request, the
> >>>>>> offset
> >>>>>>    * should already be where want it to, but this allows e.g. local
> >>>>>> testing
> >>>>>> @@ -1948,6 +1960,7 @@ static int hls_read_header(AVFormatContext *s)
> >>>>>>       struct playlist *pls = c->playlists[i];
> >>>>>>       char *url;
> >>>>>>       ff_const59 AVInputFormat *in_fmt = NULL;
> >>>>>> +        struct segment *seg = NULL;
> >>>>>>
> >>>>>>       if (!(pls->ctx = avformat_alloc_context())) {
> >>>>>>           ret = AVERROR(ENOMEM);
> >>>>>> @@ -1980,8 +1993,41 @@ static int hls_read_header(AVFormatContext *s)
> >>>>>>           pls->ctx = NULL;
> >>>>>>           goto fail;
> >>>>>>       }
> >>>>>> +
> >>>>>>       ffio_init_context(&pls->pb, pls->read_buffer,
> >>>>>> INITIAL_BUFFER_SIZE, 0, pls,
> >>>>>>                         read_data, NULL, NULL);
> >>>>>> +
> >>>>>> +        /*
> >>>>>> +         * If encryption scheme is SAMPLE-AES, try to read  ID3 tags of
> >>>>>> +         * external audio track that contains audio setup information
> >>>>>> +         */
> >>>>>> +        seg = current_segment(pls);
> >>>>>> +        if (seg && seg->key_type == KEY_SAMPLE_AES &&
> >>>> pls->n_renditions >
> >>>>>> 0 &&
> >>>>>> +            pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO) {
> >>>>>> +            uint8_t buf[HLS_MAX_ID3_TAGS_DATA_LEN];
> >>>>>> +            if ((ret = avio_read(&pls->pb, buf,
> >>>>>> HLS_MAX_ID3_TAGS_DATA_LEN)) < 0) {
> >>>>>> +                /* Fail if error was not end of file */
> >>>>>> +                if (ret != AVERROR_EOF) {
> >>>>>> +                    avformat_free_context(pls->ctx);
> >>>>>> +                    pls->ctx = NULL;
> >>>>>> +                    goto fail;
> >>>>>> +                }
> >>>>>> +            }
> >>>>>> +            ret = 0;
> >>>>>> +        }
> >>>>>> +
> >>>>>> +        /*
> >>>>>> +         * If encryption scheme is SAMPLE-AES and audio setup
> >>>> information
> >>>>>> is present in external audio track,
> >>>>>> +         * use that information to find the media format, otherwise
> >>>> probe
> >>>>>> input data
> >>>>>> +         */
> >>>>>> +        if (seg && seg->key_type == KEY_SAMPLE_AES &&
> >>>>>> pls->is_id3_timestamped &&
> >>>>>> +            pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) {
> >>>>>> +            void *iter = NULL;
> >>>>>> +            while ((in_fmt = (ff_const59 AVInputFormat
> >>>>>> *)av_demuxer_iterate(&iter)))
> >>>>>> +                if (in_fmt->raw_codec_id ==
> >>>>>> pls->audio_setup_info.codec_id) {
> >>>>>> +                    break;
> >>>>>> +                }
> >>>>>> +        } else {
> >>>>>>       pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 *
> >>>> 4;
> >>>>>>       pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ?
> >>>>>> s->max_analyze_duration : 4 * AV_TIME_BASE;
> >>>>>>       pls->ctx->interrupt_callback = s->interrupt_callback;
> >>>>>> @@ -1999,6 +2045,25 @@ static int hls_read_header(AVFormatContext *s)
> >>>>>>           goto fail;
> >>>>>>       }
> >>>>>>       av_free(url);
> >>>>>> +        }
> >>>>>> +
> >>>>>> +        if (seg && seg->key_type == KEY_SAMPLE_AES) {
> >>>>>> +            if (!pls->is_id3_timestamped && pls->n_renditions > 0 &&
> >>>>>> pls->renditions[0]->type != AVMEDIA_TYPE_AUDIO &&
> >>>>>> +                strcmp(in_fmt->name, "mpegts")) {
> >>>>>> +                av_log(s, AV_LOG_ERROR, "SAMPLE-AES encryption is not
> >>>>>> supported for fragmented MP4 format yet\n");
> >>>>>> +                ret = AVERROR_PATCHWELCOME;
> >>>>>> +            } else {
> >>>>>> +                pls->crypto_ctx.aes_ctx = av_aes_alloc();
> >>>>>> +                if (!pls->crypto_ctx.aes_ctx)
> >>>>>> +                    ret = AVERROR(ENOMEM);
> >>>>>> +            }
> >>>>>> +            if (ret != 0) {
> >>>>>> +                avformat_free_context(pls->ctx);
> >>>>>> +                pls->ctx = NULL;
> >>>>>> +                goto fail;
> >>>>>> +            }
> >>>>>> +        }
> >>>>>> +
> >>>>>>       pls->ctx->pb       = &pls->pb;
> >>>>>>       pls->ctx->io_open  = nested_io_open;
> >>>>>>       pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
> >>>>>> @@ -2027,7 +2092,12 @@ static int hls_read_header(AVFormatContext *s)
> >>>>>>        * on us if they want to.
> >>>>>>        */
> >>>>>>       if (pls->is_id3_timestamped || (pls->n_renditions > 0 &&
> >>>>>> pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO)) {
> >>>>>> +            if (seg && seg->key_type == KEY_SAMPLE_AES &&
> >>>>>> pls->audio_setup_info.setup_data_length > 0 &&
> >>>>>> +                pls->ctx->nb_streams == 1)
> >>>>>> +                ret =
> >>>> ff_hls_parse_audio_setup_info(pls->ctx->streams[0],
> >>>>>> &pls->audio_setup_info);
> >>>>>> +            else
> >>>>>>           ret = avformat_find_stream_info(pls->ctx, NULL);
> >>>>>> +
> >>>>>>           if (ret < 0)
> >>>>>>               goto fail;
> >>>>>>       }
> >>>>>> @@ -2157,6 +2227,7 @@ static int hls_read_packet(AVFormatContext *s,
> >>>>>> AVPacket *pkt)
> >>>>>>           while (1) {
> >>>>>>               int64_t ts_diff;
> >>>>>>               AVRational tb;
> >>>>>> +                struct segment *seg = NULL;
> >>>>>>               ret = av_read_frame(pls->ctx, &pls->pkt);
> >>>>>>               if (ret < 0) {
> >>>>>>                   if (!avio_feof(&pls->pb) && ret != AVERROR_EOF)
> >>>>>> @@ -2175,6 +2246,14 @@ static int hls_read_packet(AVFormatContext *s,
> >>>>>> AVPacket *pkt)
> >>>>>>                           get_timebase(pls), AV_TIME_BASE_Q);
> >>>>>>               }
> >>>>>>
> >>>>>> +                seg = current_segment(pls);
> >>>>>> +                if (seg && seg->key_type == KEY_SAMPLE_AES) {
> >>>>>> +                    enum AVCodecID codec_id =
> >>>>>> pls->ctx->streams[pls->pkt.stream_index]->codecpar->codec_id;
> >>>>>> +                    memcpy(pls->crypto_ctx.iv, seg->iv,
> >>>> sizeof(seg->iv));
> >>>>>> +                    memcpy(pls->crypto_ctx.key, pls->key,
> >>>>>> sizeof(pls->key));
> >>>>>> +                    ff_hls_decrypt_frame(codec_id, &pls->crypto_ctx,
> >>>>>> &pls->pkt);
> >>>>>> +                }
> >>>>>> +
> >>>>>>               if (pls->seek_timestamp == AV_NOPTS_VALUE)
> >>>>>>                   break;
> >>>>>>
> >>>>>> diff --git a/libavformat/hls_sample_aes.c b/libavformat/hls_sample_aes.c
> >>>>>> new file mode 100644
> >>>>>> index 0000000000..0407a15b0f
> >>>>>> --- /dev/null
> >>>>>> +++ b/libavformat/hls_sample_aes.c
> >>>>>> @@ -0,0 +1,391 @@
> >>>>>> +/*
> >>>>>> + * Apple HTTP Live Streaming Sample Encryption/Decryption
> >>>>>> + *
> >>>>>> + * Copyright (c) 2021 Nachiket Tarate
> >>>>>> + *
> >>>>>> + * This file is part of FFmpeg.
> >>>>>> + *
> >>>>>> + * FFmpeg is free software; you can redistribute it and/or
> >>>>>> + * modify it under the terms of the GNU Lesser General Public
> >>>>>> + * License as published by the Free Software Foundation; either
> >>>>>> + * version 2.1 of the License, or (at your option) any later version.
> >>>>>> + *
> >>>>>> + * FFmpeg is distributed in the hope that it will be useful,
> >>>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> >>>>>> + * Lesser General Public License for more details.
> >>>>>> + *
> >>>>>> + * You should have received a copy of the GNU Lesser General Public
> >>>>>> + * License along with FFmpeg; if not, write to the Free Software
> >>>>>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> >>>>>> 02110-1301 USA
> >>>>>> + */
> >>>>>> +
> >>>>>> +/**
> >>>>>> + * @file
> >>>>>> + * Apple HTTP Live Streaming Sample Encryption
> >>>>>> + *
> >>>>>>
> >>>> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> >>>>>> + */
> >>>>>> +
> >>>>>> +#include "hls_sample_aes.h"
> >>>>>> +
> >>>>>> +#include "libavcodec/adts_header.h"
> >>>>>> +#include "libavcodec/adts_parser.h"
> >>>>>> +#include "libavcodec/ac3_parser_internal.h"
> >>>>>> +
> >>>>>> +
> >>>>>> +typedef struct NALUnit {
> >>>>>> +    uint8_t     *data;
> >>>>>> +    int         type;
> >>>>>> +    int         length;
> >>>>>> +    int         start_code_length;
> >>>>>> +} NALUnit;
> >>>>>> +
> >>>>>> +typedef struct AudioFrame {
> >>>>>> +    uint8_t     *data;
> >>>>>> +    int         length;
> >>>>>> +    int         header_length;
> >>>>>> +} AudioFrame;
> >>>>>> +
> >>>>>> +typedef struct CodecParserContext {
> >>>>>> +    const uint8_t   *buf_ptr;
> >>>>>> +    const uint8_t   *buf_end;
> >>>>>> +} CodecParserContext;
> >>>>>> +
> >>>>>> +static const int eac3_sample_rate_tab[] = { 48000, 44100, 32000, 0 };
> >>>>>> +
> >>>>>> +void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const
> >>>> uint8_t
> >>>>>> *buf, size_t size)
> >>>>>> +{
> >>>>>> +    if (size < 8)
> >>>>>> +        return;
> >>>>>> +
> >>>>>> +    info->codec_tag             = AV_RL32(buf);
> >>>>>> +
> >>>>>> +    if (info->codec_tag == MKTAG('z','a', 'a', 'c'))
> >>>>>> +        info->codec_id = AV_CODEC_ID_AAC;
> >>>>>> +    else if (info->codec_tag == MKTAG('z','a', 'c', '3'))
> >>>>>> +        info->codec_id = AV_CODEC_ID_AC3;
> >>>>>> +    else if (info->codec_tag == MKTAG('z','e', 'c', '3'))
> >>>>>> +        info->codec_id = AV_CODEC_ID_EAC3;
> >>>>>> +    else
> >>>>>> +        info->codec_id = AV_CODEC_ID_NONE;
> >>>>>> +
> >>>>>> +    buf += 4;
> >>>>>> +    info->priming               = AV_RL16(buf);
> >>>>>> +    buf += 2;
> >>>>>> +    info->version               = *buf++;
> >>>>>> +    info->setup_data_length     = *buf++;
> >>>>>> +
> >>>>>> +    if (info->setup_data_length > size - 8)
> >>>>>> +        info->setup_data_length = size - 8;
> >>>>>> +
> >>>>>> +    if (info->setup_data_length > HLS_MAX_AUDIO_SETUP_DATA_LEN)
> >>>>>> +        return;
> >>>>>> +
> >>>>>> +    memcpy(info->setup_data, buf, info->setup_data_length);
> >>>>>> +}
> >>>>>> +
> >>>>>> +int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo
> >>>> *info)
> >>>>>> +{
> >>>>>> +    int ret = 0;
> >>>>>> +
> >>>>>> +    st->codecpar->codec_tag = info->codec_tag;
> >>>>>> +
> >>>>>> +    if (st->codecpar->codec_id == AV_CODEC_ID_AAC)
> >>>>>> +        return 0;
> >>>>>> +
> >>>>>> +    if (st->codecpar->codec_id != AV_CODEC_ID_AC3 &&
> >>>>>> st->codecpar->codec_id != AV_CODEC_ID_EAC3)
> >>>>>> +        return AVERROR_INVALIDDATA;
> >>>>>> +
> >>>>>> +    if (st->codecpar->codec_id == AV_CODEC_ID_AC3) {
> >>>>>> +
> >>>>>> +        AC3HeaderInfo *ac3hdr = NULL;
> >>>>>> +
> >>>>>> +        ret = avpriv_ac3_parse_header(&ac3hdr, info->setup_data,
> >>>>>> info->setup_data_length);
> >>>>>> +        if (ret < 0) {
> >>>>>> +            if (ret != AVERROR(ENOMEM))
> >>>>>> +                av_free(ac3hdr);
> >>>>>> +            return ret;
> >>>>>> +        }
> >>>>>> +
> >>>>>> +        st->codecpar->sample_rate       = ac3hdr->sample_rate;
> >>>>>> +        st->codecpar->channels          = ac3hdr->channels;
> >>>>>> +        st->codecpar->channel_layout    = ac3hdr->channel_layout;
> >>>>>> +        st->codecpar->bit_rate          = ac3hdr->bit_rate;
> >>>>>> +
> >>>>>> +        av_free(ac3hdr);
> >>>>>> +    } else {  /*  Parse 'dec3' EC3SpecificBox */
> >>>>>> +
> >>>>>> +        GetBitContext gb;
> >>>>>> +        int data_rate, fscod, acmod, lfeon;
> >>>>>> +
> >>>>>> +        ret = init_get_bits8(&gb, info->setup_data,
> >>>>>> info->setup_data_length);
> >>>>>> +        if (ret < 0)
> >>>>>> +            return AVERROR_INVALIDDATA;
> >>>>>> +
> >>>>>> +        data_rate = get_bits(&gb, 13);
> >>>>>> +        skip_bits(&gb, 3);
> >>>>>> +        fscod = get_bits(&gb, 2);
> >>>>>> +        skip_bits(&gb, 10);
> >>>>>> +        acmod = get_bits(&gb, 3);
> >>>>>> +        lfeon = get_bits(&gb, 1);
> >>>>>> +
> >>>>>> +        st->codecpar->sample_rate = eac3_sample_rate_tab[fscod];
> >>>>>> +
> >>>>>> +        st->codecpar->channel_layout =
> >>>>>> avpriv_ac3_channel_layout_tab[acmod];
> >>>>>> +        if (lfeon)
> >>>>>> +            st->codecpar->channel_layout |= AV_CH_LOW_FREQUENCY;
> >>>>>> +
> >>>>>> +        st->codecpar->channels =
> >>>>>> av_get_channel_layout_nb_channels(st->codecpar->channel_layout);
> >>>>>> +
> >>>>>> +        st->codecpar->bit_rate = data_rate*1000;
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    return 0;
> >>>>>> +}
> >>>>>> +
> >>>>>> +/*
> >>>>>> + * Remove start code emulation prevention 0x03 bytes
> >>>>>> + */
> >>>>>> +static void remove_scep_3_bytes(NALUnit *nalu)
> >>>>>> +{
> >>>>>> +    int i = 0;
> >>>>>> +    int j = 0;
> >>>>>> +
> >>>>>> +    uint8_t *data = nalu->data;
> >>>>>> +
> >>>>>> +    while (i < nalu->length) {
> >>>>>> +        if (nalu->length - i > 3 && AV_RB24(&data[i]) == 0x000003) {
> >>>>>> +            data[j++] = data[i++];
> >>>>>> +            data[j++] = data[i++];
> >>>>>> +            i++;
> >>>>>> +        } else {
> >>>>>> +            data[j++] = data[i++];
> >>>>>> +        }
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    nalu->length = j;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int get_next_nal_unit(CodecParserContext *ctx, NALUnit *nalu)
> >>>>>> +{
> >>>>>> +    const uint8_t *nalu_start = ctx->buf_ptr;
> >>>>>> +
> >>>>>> +    if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr) ==
> >>>>>> 0x00000001)
> >>>>>> +        nalu->start_code_length = 4;
> >>>>>> +    else if (ctx->buf_end - ctx->buf_ptr >= 3 && AV_RB24(ctx->buf_ptr)
> >>>> ==
> >>>>>> 0x000001)
> >>>>>> +        nalu->start_code_length = 3;
> >>>>>> +    else /* No start code at the beginning of the NAL unit */
> >>>>>> +        return -1;
> >>>>>> +
> >>>>>> +    ctx->buf_ptr += nalu->start_code_length;
> >>>>>> +
> >>>>>> +    while (ctx->buf_ptr < ctx->buf_end) {
> >>>>>> +        if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr)
> >>>> ==
> >>>>>> 0x00000001)
> >>>>>> +            break;
> >>>>>> +        else if (ctx->buf_end - ctx->buf_ptr >= 3 &&
> >>>>>> AV_RB24(ctx->buf_ptr) == 0x000001)
> >>>>>> +            break;
> >>>>>> +        ctx->buf_ptr++;
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    nalu->data  = (uint8_t *)nalu_start + nalu->start_code_length;
> >>>>>> +    nalu->length = ctx->buf_ptr - nalu->data;
> >>>>>> +    nalu->type  = *nalu->data & 0x1F;
> >>>>>> +
> >>>>>> +    return 0;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int decrypt_nal_unit(HLSCryptoContext *crypto_ctx, NALUnit
> >>>> *nalu)
> >>>>>> +{
> >>>>>> +    int ret = 0;
> >>>>>> +    int rem_bytes;
> >>>>>> +    uint8_t *data;
> >>>>>> +    uint8_t iv[16];
> >>>>>> +
> >>>>>> +    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
> >>>>>> +    if (ret < 0)
> >>>>>> +        return ret;
> >>>>>> +
> >>>>>> +    /* Remove start code emulation prevention 0x03 bytes */
> >>>>>> +    remove_scep_3_bytes(nalu);
> >>>>>> +
> >>>>>> +    data = nalu->data + 32;
> >>>>>> +    rem_bytes = nalu->length - 32;
> >>>>>> +
> >>>>>> +    memcpy(iv, crypto_ctx->iv, 16);
> >>>>>> +
> >>>>>> +    while (rem_bytes > 0) {
> >>>>>> +        if (rem_bytes > 16) {
> >>>>>> +            av_aes_crypt(crypto_ctx->aes_ctx, data, data, 1, iv, 1);
> >>>>>> +            data += 16;
> >>>>>> +            rem_bytes -= 16;
> >>>>>> +        }
> >>>>>> +        data += FFMIN(144, rem_bytes);
> >>>>>> +        rem_bytes -= FFMIN(144, rem_bytes);
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    return 0;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int decrypt_video_frame(HLSCryptoContext *crypto_ctx, AVPacket
> >>>>>> *pkt)
> >>>>>> +{
> >>>>>> +    int ret = 0;
> >>>>>> +    CodecParserContext  ctx;
> >>>>>> +    NALUnit nalu;
> >>>>>> +    uint8_t *data_ptr;
> >>>>>> +    int move_nalu = 0;
> >>>>>> +
> >>>>>> +    memset(&ctx, 0, sizeof(ctx));
> >>>>>> +    ctx.buf_ptr  = pkt->data;
> >>>>>> +    ctx.buf_end = pkt->data + pkt->size;
> >>>>>> +
> >>>>>> +    data_ptr = pkt->data;
> >>>>>> +
> >>>>>> +    while (ctx.buf_ptr < ctx.buf_end) {
> >>>>>> +        memset(&nalu, 0, sizeof(nalu));
> >>>>>> +        ret = get_next_nal_unit(&ctx, &nalu);
> >>>>>> +        if (ret < 0)
> >>>>>> +            return ret;
> >>>>>> +        if ((nalu.type == 0x01 || nalu.type == 0x05) && nalu.length >
> >>>> 48)
> >>>>>> {
> >>>>>> +            int encrypted_nalu_length = nalu.length;
> >>>>>> +            ret = decrypt_nal_unit(crypto_ctx, &nalu);
> >>>>>> +            if (ret < 0)
> >>>>>> +                return ret;
> >>>>>> +            move_nalu = nalu.length != encrypted_nalu_length;
> >>>>>> +        }
> >>>>>> +        if (move_nalu)
> >>>>>> +            memmove(data_ptr, nalu.data - nalu.start_code_length,
> >>>>>> nalu.start_code_length + nalu.length);
> >>>>>> +        data_ptr += nalu.start_code_length + nalu.length;
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    av_shrink_packet(pkt, data_ptr - pkt->data);
> >>>>>> +
> >>>>>> +    return 0;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int get_next_adts_frame(CodecParserContext *ctx, AudioFrame
> >>>> *frame)
> >>>>>> +{
> >>>>>> +    int ret = 0;
> >>>>>> +
> >>>>>> +    AACADTSHeaderInfo *adts_hdr = NULL;
> >>>>>> +
> >>>>>> +    /* Find next sync word 0xFFF */
> >>>>>> +    while (ctx->buf_ptr < ctx->buf_end - 1) {
> >>>>>> +        if (*ctx->buf_ptr == 0xFF && *(ctx->buf_ptr + 1) & 0xF0 ==
> >>>> 0xF0)
> >>>>>> +            break;
> >>>>>> +        ctx->buf_ptr++;
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    if (ctx->buf_ptr >= ctx->buf_end - 1)
> >>>>>> +        return -1;
> >>>>>> +
> >>>>>> +    frame->data = (uint8_t*)ctx->buf_ptr;
> >>>>>> +
> >>>>>> +    ret = avpriv_adts_header_parse (&adts_hdr, frame->data,
> >>>> ctx->buf_end
> >>>>>> - frame->data);
> >>>>>> +    if (ret < 0)
> >>>>>> +        return ret;
> >>>>>> +
> >>>>>> +    frame->header_length = adts_hdr->crc_absent ?
> >>>> AV_AAC_ADTS_HEADER_SIZE
> >>>>>> : AV_AAC_ADTS_HEADER_SIZE + 2;
> >>>>>> +    frame->length = adts_hdr->frame_length;
> >>>>>> +
> >>>>>> +    av_free(adts_hdr);
> >>>>>> +
> >>>>>> +    return 0;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int get_next_ac3_eac3_sync_frame(CodecParserContext *ctx,
> >>>>>> AudioFrame *frame)
> >>>>>> +{
> >>>>>> +    int ret = 0;
> >>>>>> +
> >>>>>> +    AC3HeaderInfo *hdr = NULL;
> >>>>>> +
> >>>>>> +    /* Find next sync word 0x0B77 */
> >>>>>> +    while (ctx->buf_ptr < ctx->buf_end - 1) {
> >>>>>> +        if (*ctx->buf_ptr == 0x0B && *(ctx->buf_ptr + 1) == 0x77)
> >>>>>> +            break;
> >>>>>> +        ctx->buf_ptr++;
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    if (ctx->buf_ptr >= ctx->buf_end - 1)
> >>>>>> +        return -1;
> >>>>>> +
> >>>>>> +    frame->data = (uint8_t*)ctx->buf_ptr;
> >>>>>> +    frame->header_length = 0;
> >>>>>> +
> >>>>>> +    ret = avpriv_ac3_parse_header(&hdr, frame->data, ctx->buf_end -
> >>>>>> frame->data);
> >>>>>> +    if (ret < 0) {
> >>>>>> +        if (ret != AVERROR(ENOMEM))
> >>>>>> +            av_free(hdr);
> >>>>>> +        return ret;
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    frame->length = hdr->frame_size;
> >>>>>> +
> >>>>>> +    av_free(hdr);
> >>>>>> +
> >>>>>> +    return 0;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int get_next_sync_frame(enum AVCodecID codec_id,
> >>>>>> CodecParserContext *ctx, AudioFrame *frame)
> >>>>>> +{
> >>>>>> +    if (codec_id == AV_CODEC_ID_AAC)
> >>>>>> +        return get_next_adts_frame(ctx, frame);
> >>>>>> +    else if (codec_id == AV_CODEC_ID_AC3 || codec_id ==
> >>>> AV_CODEC_ID_EAC3)
> >>>>>> +        return get_next_ac3_eac3_sync_frame(ctx, frame);
> >>>>>> +    else
> >>>>>> +        return AVERROR_INVALIDDATA;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int decrypt_sync_frame(enum AVCodecID codec_id, HLSCryptoContext
> >>>>>> *crypto_ctx, AudioFrame *frame)
> >>>>>> +{
> >>>>>> +    int ret = 0;
> >>>>>> +    uint8_t *data;
> >>>>>> +    int num_of_encrypted_blocks;
> >>>>>> +
> >>>>>> +    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
> >>>>>> +    if (ret < 0)
> >>>>>> +        return ret;
> >>>>>> +
> >>>>>> +    data = frame->data + frame->header_length + 16;
> >>>>>> +
> >>>>>> +    num_of_encrypted_blocks = (frame->length - frame->header_length -
> >>>>>> 16)/16;
> >>>>>> +
> >>>>>> +    av_aes_crypt(crypto_ctx->aes_ctx, data, data,
> >>>>>> num_of_encrypted_blocks, crypto_ctx->iv, 1);
> >>>>>> +
> >>>>>> +    return 0;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int decrypt_audio_frame(enum AVCodecID codec_id,
> >>>> HLSCryptoContext
> >>>>>> *crypto_ctx, AVPacket *pkt)
> >>>>>> +{
> >>>>>> +    int ret = 0;
> >>>>>> +    CodecParserContext  ctx;
> >>>>>> +    AudioFrame frame;
> >>>>>> +
> >>>>>> +    memset(&ctx, 0, sizeof(ctx));
> >>>>>> +    ctx.buf_ptr        = pkt->data;
> >>>>>> +    ctx.buf_end = pkt->data + pkt->size;
> >>>>>> +
> >>>>>> +    while (ctx.buf_ptr < ctx.buf_end) {
> >>>>>> +        memset(&frame, 0, sizeof(frame));
> >>>>>> +        ret = get_next_sync_frame(codec_id, &ctx, &frame);
> >>>>>> +        if (ret < 0)
> >>>>>> +            return ret;
> >>>>>> +        if (frame.length - frame.header_length > 31) {
> >>>>>> +            ret = decrypt_sync_frame(codec_id, crypto_ctx, &frame);
> >>>>>> +            if (ret < 0)
> >>>>>> +                return ret;
> >>>>>> +        }
> >>>>>> +        ctx.buf_ptr += frame.length;
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    return 0;
> >>>>>> +}
> >>>>>> +
> >>>>>> +int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext
> >>>>>> *crypto_ctx, AVPacket *pkt)
> >>>>>> +{
> >>>>>> +    if (codec_id == AV_CODEC_ID_H264)
> >>>>>> +        return decrypt_video_frame(crypto_ctx, pkt);
> >>>>>> +    else if (codec_id == AV_CODEC_ID_AAC || codec_id == AV_CODEC_ID_AC3
> >>>>>> || codec_id == AV_CODEC_ID_EAC3)
> >>>>>> +        return decrypt_audio_frame(codec_id, crypto_ctx, pkt);
> >>>>>> +
> >>>>>> +    return AVERROR_INVALIDDATA;
> >>>>>> +}
> >>>>>> diff --git a/libavformat/hls_sample_aes.h b/libavformat/hls_sample_aes.h
> >>>>>> new file mode 100644
> >>>>>> index 0000000000..cf80e41cb0
> >>>>>> --- /dev/null
> >>>>>> +++ b/libavformat/hls_sample_aes.h
> >>>>>> @@ -0,0 +1,66 @@
> >>>>>> +/*
> >>>>>> + * Apple HTTP Live Streaming Sample Encryption/Decryption
> >>>>>> + *
> >>>>>> + * Copyright (c) 2021 Nachiket Tarate
> >>>>>> + *
> >>>>>> + * This file is part of FFmpeg.
> >>>>>> + *
> >>>>>> + * FFmpeg is free software; you can redistribute it and/or
> >>>>>> + * modify it under the terms of the GNU Lesser General Public
> >>>>>> + * License as published by the Free Software Foundation; either
> >>>>>> + * version 2.1 of the License, or (at your option) any later version.
> >>>>>> + *
> >>>>>> + * FFmpeg is distributed in the hope that it will be useful,
> >>>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> >>>>>> + * Lesser General Public License for more details.
> >>>>>> + *
> >>>>>> + * You should have received a copy of the GNU Lesser General Public
> >>>>>> + * License along with FFmpeg; if not, write to the Free Software
> >>>>>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> >>>>>> 02110-1301 USA
> >>>>>> + */
> >>>>>> +
> >>>>>> +/**
> >>>>>> + * @file
> >>>>>> + * Apple HTTP Live Streaming Sample Encryption
> >>>>>> + *
> >>>>>>
> >>>> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> >>>>>> + */
> >>>>>> +
> >>>>>> +#ifndef AVFORMAT_HLS_SAMPLE_AES_H
> >>>>>> +#define AVFORMAT_HLS_SAMPLE_AES_H
> >>>>>> +
> >>>>>> +#include <stdint.h>
> >>>>>> +
> >>>>>> +#include "avformat.h"
> >>>>>> +
> >>>>>> +#include "libavcodec/avcodec.h"
> >>>>>> +#include "libavutil/aes.h"
> >>>>>> +
> >>>>>> +#define HLS_MAX_ID3_TAGS_DATA_LEN       138
> >>>>>> +#define HLS_MAX_AUDIO_SETUP_DATA_LEN    10
> >>>>>> +
> >>>>>> +
> >>>>>> +typedef struct HLSCryptoContext {
> >>>>>> +    struct AVAES   *aes_ctx;
> >>>>>> +    uint8_t            key[16];
> >>>>>> +    uint8_t            iv[16];
> >>>>>> +} HLSCryptoContext;
> >>>>>> +
> >>>>>> +typedef struct HLSAudioSetupInfo {
> >>>>>> +    enum AVCodecID      codec_id;
> >>>>>> +    uint32_t            codec_tag;
> >>>>>> +    uint16_t            priming;
> >>>>>> +    uint8_t             version;
> >>>>>> +    uint8_t             setup_data_length;
> >>>>>> +    uint8_t             setup_data[HLS_MAX_AUDIO_SETUP_DATA_LEN];
> >>>>>> +} HLSAudioSetupInfo;
> >>>>>> +
> >>>>>> +
> >>>>>> +void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const
> >>>> uint8_t
> >>>>>> *buf, size_t size);
> >>>>>> +
> >>>>>> +int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo
> >>>> *info);
> >>>>>> +
> >>>>>> +int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext
> >>>>>> *crypto_ctx, AVPacket *pkt);
> >>>>>> +
> >>>>>> +#endif /* AVFORMAT_HLS_SAMPLE_AES_H */
> >>>>>> +
> >>>>>> diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
> >>>>>> index e283ec09d7..dc611ae788 100644
> >>>>>> --- a/libavformat/mpegts.c
> >>>>>> +++ b/libavformat/mpegts.c
> >>>>>> @@ -839,6 +839,16 @@ static const StreamType MISC_types[] = {
> >>>>>>   { 0 },
> >>>>>> };
> >>>>>>
> >>>>>> +/* HLS Sample Encryption Types  */
> >>>>>> +static const StreamType HLS_SAMPLE_ENC_types[] = {
> >>>>>> +    { 0xdb, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264},
> >>>>>> +    { 0xcf, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
> >>>>>> +    { 0xc1, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 },
> >>>>>> +    { 0xc2, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3},
> >>>>>> +    { 0 },
> >>>>>> +};
> >>>>>> +
> >>>>>> +
> >>>>>> static const StreamType REGD_types[] = {
> >>>>>>   { MKTAG('d', 'r', 'a', 'c'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC
> >>>> },
> >>>>>>   { MKTAG('A', 'C', '-', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3
> >>>> },
> >>>>>> @@ -948,6 +958,8 @@ static int mpegts_set_stream_info(AVStream *st,
> >>>>>> PESContext *pes,
> >>>>>>   }
> >>>>>>   if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
> >>>>>>       mpegts_find_stream_type(st, pes->stream_type, MISC_types);
> >>>>>> +    if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
> >>>>>> +        mpegts_find_stream_type(st, pes->stream_type,
> >>>>>> HLS_SAMPLE_ENC_types);
> >>>>>>   if (st->codecpar->codec_id == AV_CODEC_ID_NONE) {
> >>>>>>       st->codecpar->codec_id  = old_codec_id;
> >>>>>>       st->codecpar->codec_type = old_codec_type;
> >>>>>> --
> >>>>>> 2.17.1
> >>>>>>
> >>>>>>
> >>>>> _______________________________________________
> >>>>> ffmpeg-devel mailing list
> >>>>> ffmpeg-devel@ffmpeg.org
> >>>>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >>>>>
> >>>>> To unsubscribe, visit link above, or email
> >>>>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> >>>>
> >>>> Thanks
> >>>>
> >>>> Steven Liu
> >>>>
> >>>>
> >>>>
> >>>> _______________________________________________
> >>>> ffmpeg-devel mailing list
> >>>> ffmpeg-devel@ffmpeg.org
> >>>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >>>>
> >>>> To unsubscribe, visit link above, or email
> >>>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> >>> _______________________________________________
> >>> ffmpeg-devel mailing list
> >>> ffmpeg-devel@ffmpeg.org
> >>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >>>
> >>> To unsubscribe, visit link above, or email
> >>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> >>
> >> Thanks
> >>
> >> Steven Liu
> >>
> >>
> >>
> >> _______________________________________________
> >> ffmpeg-devel mailing list
> >> ffmpeg-devel@ffmpeg.org
> >> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >>
> >> To unsubscribe, visit link above, or email
> >> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
> Thanks
>
> Steven Liu
>
>
>
Nachiket Tarate March 8, 2021, 7:15 a.m. UTC | #8
On Wed, Mar 3, 2021 at 11:15 AM Nachiket Tarate
<nachiket.programmer@gmail.com> wrote:
>
> On Mon, Mar 1, 2021 at 1:05 PM Steven Liu <lq@chinaffmpeg.org> wrote:
> >
> >
> >
> > > 2021年3月1日 下午3:22,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
> > >
> > > On Mon, Mar 1, 2021 at 11:30 AM Steven Liu <lq@chinaffmpeg.org> wrote:
> > >>
> > >>
> > >>
> > >>> 2021年3月1日 下午12:55,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
> > >>>
> > >>> This is an updated version of the patch in which I have added the check. If
> > >>> the segments are in Fragmented MP4 format, HLS demuxer quits by giving an
> > >>> error message:
> > >>>
> > >>> "SAMPLE-AES encryption is not supported for fragmented MP4 format yet”
> > >> I don’t think  is a good resolution for SAMPLE-AES encryption and decryption.
> > >> You should support that if you want support SAMPLE-AES in hls,
> > >> because SAMPLE-AES not only support in MPEG-TS, but also support fragment mp4.
> > >> Whatever, if you only support mpegts en[de]cryption, it should be a half part patch.
> > >
> > > Two completely different technologies/specifications have been used
> > > for SAMPLE-AES encryption of HLS streams in MPEG-TS and fragmented MP4
> > > formats.
> > >
> > > Fragmented MP4 media segments are encrypted using the 'cbcs' scheme of
> > > Common Encryption [CENC]:
> > >
> > > https://www.iso.org/standard/68042.html
> > >
> > > Encryption of other media segment formats such as MPEG-TS or external
> > > audio tracks containing H.264, AAC, AC-3 and Enhanced AC-3 media
> > > streams is described in the Apple's HTTP Live Streaming (HLS) Sample
> > > Encryption specifications:
> > >
> > > https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> > >
> > > This patch implements the later specifications and enables decryption
> > > of media segments in MPEG-TS, AAC, AC-3 and Enhanced AC-3 formats. So
> > > I think we should merge this patch right now.
> >
> > I think sample ads should support not only mpegts, but also support fmp4 too.
> > Reference rfc8216 context:
> >      An encryption method of SAMPLE-AES means that the Media Segments
> >      contain media samples, such as audio or video, that are encrypted
> >      using the Advanced Encryption Standard [AES_128].  How these media
> >      streams are encrypted and encapsulated in a segment depends on the
> >      media encoding and the media format of the segment.  fMP4 Media
> >      Segments are encrypted using the 'cbcs' scheme of Common
> >      Encryption [COMMON_ENC].  Encryption of other Media Segment
> >      formats containing H.264 [H_264], AAC [ISO_14496], AC-3 [AC_3],
> >      and Enhanced AC-3 [AC_3] media streams is described in the HTTP
> >      Live Streaming (HLS) Sample Encryption specification [SampleEnc].
> >      The IV attribute MAY be present; see Section 5.2.
> >
> > And I saw the m3u8 context, the METHOD is SAMPLE-AES.
> >
> > (base) liuqi05:ufbuild liuqi$ head -n10  prog_index.m3u8
> > #EXTM3U
> > #EXT-X-TARGETDURATION:10
> > #EXT-X-VERSION:7
> > #EXT-X-MEDIA-SEQUENCE:0
> > #EXT-X-PLAYLIST-TYPE:VOD
> > #EXT-X-INDEPENDENT-SEGMENTS
> >
> > Focus on this line.
> > #EXT-X-KEY:METHOD=SAMPLE-AES,URI="http://127.0.0.1/keyOnly.bin",IV=0xcece273e2737a58ca785e257eb080482
> >
> >
> > #EXT-X-MAP:URI="fileSequence0.mp4"
> > #EXTINF:8.31667,
> > #EXT-X-BITRATE:7064
> >
> >
> > You can point me the part if I misunderstood SAMPLE-AES.
>
> Your understanding of SAMPLE-AES is absolutely correct.
>
> I am insisting that let us add support for it in 2 phases as
> completely 2 different specifications have been used.
>
> Phase 1 : Media segments in MPEG-TS, AAC, AC-3 and Enhanced AC-3
> formats (Apple's specifications)
>
> Phase 2 : Media segments in fragmented MP4 format (CENC specifications)
>
> Kindly let me know your opinion.

Hello Steven,

I am looking forward to hearing from you.

Best Regards,
Nachiket
>
> > >
> > > In future, we will add support for CENC or see how can we use existing
> > > things from MOV demuxer.
> > >
> > > Best Regards,
> > > Nachiket Tarate
> > >
> > >>>
> > >>> Best Regards,
> > >>> Nachiket Tarate
> > >>>
> > >>> On Mon, Mar 1, 2021 at 10:13 AM Steven Liu <lq@chinaffmpeg.org> wrote:
> > >>>
> > >>>>
> > >>>>
> > >>>>> 2021年3月1日 下午12:35,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
> > >>>>>
> > >>>>> @Steven Liu <lq@chinaffmpeg.org>
> > >>>>>
> > >>>>> Can we merge this patch ?
> > >>>> I’m waiting update patch for fragment mp4 encryption.
> > >>>> After new version of the patchset I will test and review.
> > >>>>>
> > >>>>> Best Regards,
> > >>>>> Nachiket Tarate
> > >>>>>
> > >>>>> On Wed, Feb 24, 2021 at 4:44 PM Nachiket Tarate <
> > >>>>> nachiket.programmer@gmail.com> wrote:
> > >>>>>
> > >>>>>> Apple HTTP Live Streaming Sample Encryption:
> > >>>>>>
> > >>>>>>
> > >>>>>>
> > >>>> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> > >>>>>>
> > >>>>>> Signed-off-by: Nachiket Tarate <nachiket.programmer@gmail.com>
> > >>>>>> ---
> > >>>>>> libavformat/Makefile         |   2 +-
> > >>>>>> libavformat/hls.c            | 105 ++++++++--
> > >>>>>> libavformat/hls_sample_aes.c | 391 +++++++++++++++++++++++++++++++++++
> > >>>>>> libavformat/hls_sample_aes.h |  66 ++++++
> > >>>>>> libavformat/mpegts.c         |  12 ++
> > >>>>>> 5 files changed, 562 insertions(+), 14 deletions(-)
> > >>>>>> create mode 100644 libavformat/hls_sample_aes.c
> > >>>>>> create mode 100644 libavformat/hls_sample_aes.h
> > >>>>>>
> > >>>>>> diff --git a/libavformat/Makefile b/libavformat/Makefile
> > >>>>>> index fcb39ce133..62627d50a6 100644
> > >>>>>> --- a/libavformat/Makefile
> > >>>>>> +++ b/libavformat/Makefile
> > >>>>>> @@ -236,7 +236,7 @@ OBJS-$(CONFIG_HCOM_DEMUXER)              += hcom.o
> > >>>>>> pcm.o
> > >>>>>> OBJS-$(CONFIG_HDS_MUXER)                 += hdsenc.o
> > >>>>>> OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
> > >>>>>> OBJS-$(CONFIG_HEVC_MUXER)                += rawenc.o
> > >>>>>> -OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o
> > >>>>>> +OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o hls_sample_aes.o
> > >>>>>> OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o hlsplaylist.o avc.o
> > >>>>>> OBJS-$(CONFIG_HNM_DEMUXER)               += hnm.o
> > >>>>>> OBJS-$(CONFIG_ICO_DEMUXER)               += icodec.o
> > >>>>>> diff --git a/libavformat/hls.c b/libavformat/hls.c
> > >>>>>> index af2468ad9b..3cb3853c79 100644
> > >>>>>> --- a/libavformat/hls.c
> > >>>>>> +++ b/libavformat/hls.c
> > >>>>>> @@ -2,6 +2,7 @@
> > >>>>>> * Apple HTTP Live Streaming demuxer
> > >>>>>> * Copyright (c) 2010 Martin Storsjo
> > >>>>>> * Copyright (c) 2013 Anssi Hannula
> > >>>>>> + * Copyright (c) 2021 Nachiket Tarate
> > >>>>>> *
> > >>>>>> * This file is part of FFmpeg.
> > >>>>>> *
> > >>>>>> @@ -39,6 +40,8 @@
> > >>>>>> #include "avio_internal.h"
> > >>>>>> #include "id3v2.h"
> > >>>>>>
> > >>>>>> +#include "hls_sample_aes.h"
> > >>>>>> +
> > >>>>>> #define INITIAL_BUFFER_SIZE 32768
> > >>>>>>
> > >>>>>> #define MAX_FIELD_LEN 64
> > >>>>>> @@ -145,6 +148,10 @@ struct playlist {
> > >>>>>>   int id3_changed; /* ID3 tag data has changed at some point */
> > >>>>>>   ID3v2ExtraMeta *id3_deferred_extra; /* stored here until subdemuxer
> > >>>>>> is opened */
> > >>>>>>
> > >>>>>> +    /* Used in case of SAMPLE-AES encryption method */
> > >>>>>> +    HLSAudioSetupInfo audio_setup_info;
> > >>>>>> +    HLSCryptoContext  crypto_ctx;
> > >>>>>> +
> > >>>>>>   int64_t seek_timestamp;
> > >>>>>>   int seek_flags;
> > >>>>>>   int seek_stream_index; /* into subdemuxer stream array */
> > >>>>>> @@ -266,6 +273,8 @@ static void free_playlist_list(HLSContext *c)
> > >>>>>>           pls->ctx->pb = NULL;
> > >>>>>>           avformat_close_input(&pls->ctx);
> > >>>>>>       }
> > >>>>>> +        if (pls->crypto_ctx.aes_ctx)
> > >>>>>> +             av_free(pls->crypto_ctx.aes_ctx);
> > >>>>>>       av_free(pls);
> > >>>>>>   }
> > >>>>>>   av_freep(&c->playlists);
> > >>>>>> @@ -994,7 +1003,10 @@ fail:
> > >>>>>>
> > >>>>>> static struct segment *current_segment(struct playlist *pls)
> > >>>>>> {
> > >>>>>> -    return pls->segments[pls->cur_seq_no - pls->start_seq_no];
> > >>>>>> +    int64_t n = pls->cur_seq_no - pls->start_seq_no;
> > >>>>>> +    if (n >= pls->n_segments)
> > >>>>>> +        return NULL;
> > >>>>>> +    return pls->segments[n];
> > >>>>>> }
> > >>>>>>
> > >>>>>> static struct segment *next_segment(struct playlist *pls)
> > >>>>>> @@ -1023,10 +1035,11 @@ static int read_from_url(struct playlist *pls,
> > >>>>>> struct segment *seg,
> > >>>>>>
> > >>>>>> /* Parse the raw ID3 data and pass contents to caller */
> > >>>>>> static void parse_id3(AVFormatContext *s, AVIOContext *pb,
> > >>>>>> -                      AVDictionary **metadata, int64_t *dts,
> > >>>>>> +                      AVDictionary **metadata, int64_t *dts,
> > >>>>>> HLSAudioSetupInfo *audio_setup_info,
> > >>>>>>                     ID3v2ExtraMetaAPIC **apic, ID3v2ExtraMeta
> > >>>>>> **extra_meta)
> > >>>>>> {
> > >>>>>>   static const char id3_priv_owner_ts[] =
> > >>>>>> "com.apple.streaming.transportStreamTimestamp";
> > >>>>>> +    static const char id3_priv_owner_audio_setup[] =
> > >>>>>> "com.apple.streaming.audioDescription";
> > >>>>>>   ID3v2ExtraMeta *meta;
> > >>>>>>
> > >>>>>>   ff_id3v2_read_dict(pb, metadata, ID3v2_DEFAULT_MAGIC, extra_meta);
> > >>>>>> @@ -1041,6 +1054,8 @@ static void parse_id3(AVFormatContext *s,
> > >>>>>> AVIOContext *pb,
> > >>>>>>                   *dts = ts;
> > >>>>>>               else
> > >>>>>>                   av_log(s, AV_LOG_ERROR, "Invalid HLS ID3 audio
> > >>>>>> timestamp %"PRId64"\n", ts);
> > >>>>>> +            } else if (priv->datasize >= 8 && !strcmp(priv->owner,
> > >>>>>> id3_priv_owner_audio_setup)) {
> > >>>>>> +                ff_hls_read_audio_setup_info(audio_setup_info,
> > >>>>>> priv->data, priv->datasize);
> > >>>>>>           }
> > >>>>>>       } else if (!strcmp(meta->tag, "APIC") && apic)
> > >>>>>>           *apic = &meta->data.apic;
> > >>>>>> @@ -1084,7 +1099,7 @@ static void handle_id3(AVIOContext *pb, struct
> > >>>>>> playlist *pls)
> > >>>>>>   ID3v2ExtraMeta *extra_meta = NULL;
> > >>>>>>   int64_t timestamp = AV_NOPTS_VALUE;
> > >>>>>>
> > >>>>>> -    parse_id3(pls->ctx, pb, &metadata, &timestamp, &apic, &extra_meta);
> > >>>>>> +    parse_id3(pls->ctx, pb, &metadata, &timestamp,
> > >>>>>> &pls->audio_setup_info, &apic, &extra_meta);
> > >>>>>>
> > >>>>>>   if (timestamp != AV_NOPTS_VALUE) {
> > >>>>>>       pls->id3_mpegts_timestamp = timestamp;
> > >>>>>> @@ -1238,10 +1253,7 @@ static int open_input(HLSContext *c, struct
> > >>>>>> playlist *pls, struct segment *seg,
> > >>>>>>   av_log(pls->parent, AV_LOG_VERBOSE, "HLS request for url '%s',
> > >>>> offset
> > >>>>>> %"PRId64", playlist %d\n",
> > >>>>>>          seg->url, seg->url_offset, pls->index);
> > >>>>>>
> > >>>>>> -    if (seg->key_type == KEY_NONE) {
> > >>>>>> -        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
> > >>>>>> &is_http);
> > >>>>>> -    } else if (seg->key_type == KEY_AES_128) {
> > >>>>>> -        char iv[33], key[33], url[MAX_URL_SIZE];
> > >>>>>> +    if (seg->key_type == KEY_AES_128 || seg->key_type ==
> > >>>> KEY_SAMPLE_AES) {
> > >>>>>>       if (strcmp(seg->key, pls->key_url)) {
> > >>>>>>           AVIOContext *pb = NULL;
> > >>>>>>           if (open_url(pls->parent, &pb, seg->key, &c->avio_opts,
> > >>>> opts,
> > >>>>>> NULL) == 0) {
> > >>>>>> @@ -1257,6 +1269,10 @@ static int open_input(HLSContext *c, struct
> > >>>>>> playlist *pls, struct segment *seg,
> > >>>>>>           }
> > >>>>>>           av_strlcpy(pls->key_url, seg->key, sizeof(pls->key_url));
> > >>>>>>       }
> > >>>>>> +    }
> > >>>>>> +
> > >>>>>> +    if (seg->key_type == KEY_AES_128) {
> > >>>>>> +        char iv[33], key[33], url[MAX_URL_SIZE];
> > >>>>>>       ff_data_to_hex(iv, seg->iv, sizeof(seg->iv), 0);
> > >>>>>>       ff_data_to_hex(key, pls->key, sizeof(pls->key), 0);
> > >>>>>>       iv[32] = key[32] = '\0';
> > >>>>>> @@ -1273,13 +1289,9 @@ static int open_input(HLSContext *c, struct
> > >>>>>> playlist *pls, struct segment *seg,
> > >>>>>>           goto cleanup;
> > >>>>>>       }
> > >>>>>>       ret = 0;
> > >>>>>> -    } else if (seg->key_type == KEY_SAMPLE_AES) {
> > >>>>>> -        av_log(pls->parent, AV_LOG_ERROR,
> > >>>>>> -               "SAMPLE-AES encryption is not supported yet\n");
> > >>>>>> -        ret = AVERROR_PATCHWELCOME;
> > >>>>>> +    } else {
> > >>>>>> +        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
> > >>>>>> &is_http);
> > >>>>>>   }
> > >>>>>> -    else
> > >>>>>> -      ret = AVERROR(ENOSYS);
> > >>>>>>
> > >>>>>>   /* Seek to the requested position. If this was a HTTP request, the
> > >>>>>> offset
> > >>>>>>    * should already be where want it to, but this allows e.g. local
> > >>>>>> testing
> > >>>>>> @@ -1948,6 +1960,7 @@ static int hls_read_header(AVFormatContext *s)
> > >>>>>>       struct playlist *pls = c->playlists[i];
> > >>>>>>       char *url;
> > >>>>>>       ff_const59 AVInputFormat *in_fmt = NULL;
> > >>>>>> +        struct segment *seg = NULL;
> > >>>>>>
> > >>>>>>       if (!(pls->ctx = avformat_alloc_context())) {
> > >>>>>>           ret = AVERROR(ENOMEM);
> > >>>>>> @@ -1980,8 +1993,41 @@ static int hls_read_header(AVFormatContext *s)
> > >>>>>>           pls->ctx = NULL;
> > >>>>>>           goto fail;
> > >>>>>>       }
> > >>>>>> +
> > >>>>>>       ffio_init_context(&pls->pb, pls->read_buffer,
> > >>>>>> INITIAL_BUFFER_SIZE, 0, pls,
> > >>>>>>                         read_data, NULL, NULL);
> > >>>>>> +
> > >>>>>> +        /*
> > >>>>>> +         * If encryption scheme is SAMPLE-AES, try to read  ID3 tags of
> > >>>>>> +         * external audio track that contains audio setup information
> > >>>>>> +         */
> > >>>>>> +        seg = current_segment(pls);
> > >>>>>> +        if (seg && seg->key_type == KEY_SAMPLE_AES &&
> > >>>> pls->n_renditions >
> > >>>>>> 0 &&
> > >>>>>> +            pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO) {
> > >>>>>> +            uint8_t buf[HLS_MAX_ID3_TAGS_DATA_LEN];
> > >>>>>> +            if ((ret = avio_read(&pls->pb, buf,
> > >>>>>> HLS_MAX_ID3_TAGS_DATA_LEN)) < 0) {
> > >>>>>> +                /* Fail if error was not end of file */
> > >>>>>> +                if (ret != AVERROR_EOF) {
> > >>>>>> +                    avformat_free_context(pls->ctx);
> > >>>>>> +                    pls->ctx = NULL;
> > >>>>>> +                    goto fail;
> > >>>>>> +                }
> > >>>>>> +            }
> > >>>>>> +            ret = 0;
> > >>>>>> +        }
> > >>>>>> +
> > >>>>>> +        /*
> > >>>>>> +         * If encryption scheme is SAMPLE-AES and audio setup
> > >>>> information
> > >>>>>> is present in external audio track,
> > >>>>>> +         * use that information to find the media format, otherwise
> > >>>> probe
> > >>>>>> input data
> > >>>>>> +         */
> > >>>>>> +        if (seg && seg->key_type == KEY_SAMPLE_AES &&
> > >>>>>> pls->is_id3_timestamped &&
> > >>>>>> +            pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) {
> > >>>>>> +            void *iter = NULL;
> > >>>>>> +            while ((in_fmt = (ff_const59 AVInputFormat
> > >>>>>> *)av_demuxer_iterate(&iter)))
> > >>>>>> +                if (in_fmt->raw_codec_id ==
> > >>>>>> pls->audio_setup_info.codec_id) {
> > >>>>>> +                    break;
> > >>>>>> +                }
> > >>>>>> +        } else {
> > >>>>>>       pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 *
> > >>>> 4;
> > >>>>>>       pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ?
> > >>>>>> s->max_analyze_duration : 4 * AV_TIME_BASE;
> > >>>>>>       pls->ctx->interrupt_callback = s->interrupt_callback;
> > >>>>>> @@ -1999,6 +2045,25 @@ static int hls_read_header(AVFormatContext *s)
> > >>>>>>           goto fail;
> > >>>>>>       }
> > >>>>>>       av_free(url);
> > >>>>>> +        }
> > >>>>>> +
> > >>>>>> +        if (seg && seg->key_type == KEY_SAMPLE_AES) {
> > >>>>>> +            if (!pls->is_id3_timestamped && pls->n_renditions > 0 &&
> > >>>>>> pls->renditions[0]->type != AVMEDIA_TYPE_AUDIO &&
> > >>>>>> +                strcmp(in_fmt->name, "mpegts")) {
> > >>>>>> +                av_log(s, AV_LOG_ERROR, "SAMPLE-AES encryption is not
> > >>>>>> supported for fragmented MP4 format yet\n");
> > >>>>>> +                ret = AVERROR_PATCHWELCOME;
> > >>>>>> +            } else {
> > >>>>>> +                pls->crypto_ctx.aes_ctx = av_aes_alloc();
> > >>>>>> +                if (!pls->crypto_ctx.aes_ctx)
> > >>>>>> +                    ret = AVERROR(ENOMEM);
> > >>>>>> +            }
> > >>>>>> +            if (ret != 0) {
> > >>>>>> +                avformat_free_context(pls->ctx);
> > >>>>>> +                pls->ctx = NULL;
> > >>>>>> +                goto fail;
> > >>>>>> +            }
> > >>>>>> +        }
> > >>>>>> +
> > >>>>>>       pls->ctx->pb       = &pls->pb;
> > >>>>>>       pls->ctx->io_open  = nested_io_open;
> > >>>>>>       pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
> > >>>>>> @@ -2027,7 +2092,12 @@ static int hls_read_header(AVFormatContext *s)
> > >>>>>>        * on us if they want to.
> > >>>>>>        */
> > >>>>>>       if (pls->is_id3_timestamped || (pls->n_renditions > 0 &&
> > >>>>>> pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO)) {
> > >>>>>> +            if (seg && seg->key_type == KEY_SAMPLE_AES &&
> > >>>>>> pls->audio_setup_info.setup_data_length > 0 &&
> > >>>>>> +                pls->ctx->nb_streams == 1)
> > >>>>>> +                ret =
> > >>>> ff_hls_parse_audio_setup_info(pls->ctx->streams[0],
> > >>>>>> &pls->audio_setup_info);
> > >>>>>> +            else
> > >>>>>>           ret = avformat_find_stream_info(pls->ctx, NULL);
> > >>>>>> +
> > >>>>>>           if (ret < 0)
> > >>>>>>               goto fail;
> > >>>>>>       }
> > >>>>>> @@ -2157,6 +2227,7 @@ static int hls_read_packet(AVFormatContext *s,
> > >>>>>> AVPacket *pkt)
> > >>>>>>           while (1) {
> > >>>>>>               int64_t ts_diff;
> > >>>>>>               AVRational tb;
> > >>>>>> +                struct segment *seg = NULL;
> > >>>>>>               ret = av_read_frame(pls->ctx, &pls->pkt);
> > >>>>>>               if (ret < 0) {
> > >>>>>>                   if (!avio_feof(&pls->pb) && ret != AVERROR_EOF)
> > >>>>>> @@ -2175,6 +2246,14 @@ static int hls_read_packet(AVFormatContext *s,
> > >>>>>> AVPacket *pkt)
> > >>>>>>                           get_timebase(pls), AV_TIME_BASE_Q);
> > >>>>>>               }
> > >>>>>>
> > >>>>>> +                seg = current_segment(pls);
> > >>>>>> +                if (seg && seg->key_type == KEY_SAMPLE_AES) {
> > >>>>>> +                    enum AVCodecID codec_id =
> > >>>>>> pls->ctx->streams[pls->pkt.stream_index]->codecpar->codec_id;
> > >>>>>> +                    memcpy(pls->crypto_ctx.iv, seg->iv,
> > >>>> sizeof(seg->iv));
> > >>>>>> +                    memcpy(pls->crypto_ctx.key, pls->key,
> > >>>>>> sizeof(pls->key));
> > >>>>>> +                    ff_hls_decrypt_frame(codec_id, &pls->crypto_ctx,
> > >>>>>> &pls->pkt);
> > >>>>>> +                }
> > >>>>>> +
> > >>>>>>               if (pls->seek_timestamp == AV_NOPTS_VALUE)
> > >>>>>>                   break;
> > >>>>>>
> > >>>>>> diff --git a/libavformat/hls_sample_aes.c b/libavformat/hls_sample_aes.c
> > >>>>>> new file mode 100644
> > >>>>>> index 0000000000..0407a15b0f
> > >>>>>> --- /dev/null
> > >>>>>> +++ b/libavformat/hls_sample_aes.c
> > >>>>>> @@ -0,0 +1,391 @@
> > >>>>>> +/*
> > >>>>>> + * Apple HTTP Live Streaming Sample Encryption/Decryption
> > >>>>>> + *
> > >>>>>> + * Copyright (c) 2021 Nachiket Tarate
> > >>>>>> + *
> > >>>>>> + * This file is part of FFmpeg.
> > >>>>>> + *
> > >>>>>> + * FFmpeg is free software; you can redistribute it and/or
> > >>>>>> + * modify it under the terms of the GNU Lesser General Public
> > >>>>>> + * License as published by the Free Software Foundation; either
> > >>>>>> + * version 2.1 of the License, or (at your option) any later version.
> > >>>>>> + *
> > >>>>>> + * FFmpeg is distributed in the hope that it will be useful,
> > >>>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > >>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > >>>>>> + * Lesser General Public License for more details.
> > >>>>>> + *
> > >>>>>> + * You should have received a copy of the GNU Lesser General Public
> > >>>>>> + * License along with FFmpeg; if not, write to the Free Software
> > >>>>>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> > >>>>>> 02110-1301 USA
> > >>>>>> + */
> > >>>>>> +
> > >>>>>> +/**
> > >>>>>> + * @file
> > >>>>>> + * Apple HTTP Live Streaming Sample Encryption
> > >>>>>> + *
> > >>>>>>
> > >>>> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> > >>>>>> + */
> > >>>>>> +
> > >>>>>> +#include "hls_sample_aes.h"
> > >>>>>> +
> > >>>>>> +#include "libavcodec/adts_header.h"
> > >>>>>> +#include "libavcodec/adts_parser.h"
> > >>>>>> +#include "libavcodec/ac3_parser_internal.h"
> > >>>>>> +
> > >>>>>> +
> > >>>>>> +typedef struct NALUnit {
> > >>>>>> +    uint8_t     *data;
> > >>>>>> +    int         type;
> > >>>>>> +    int         length;
> > >>>>>> +    int         start_code_length;
> > >>>>>> +} NALUnit;
> > >>>>>> +
> > >>>>>> +typedef struct AudioFrame {
> > >>>>>> +    uint8_t     *data;
> > >>>>>> +    int         length;
> > >>>>>> +    int         header_length;
> > >>>>>> +} AudioFrame;
> > >>>>>> +
> > >>>>>> +typedef struct CodecParserContext {
> > >>>>>> +    const uint8_t   *buf_ptr;
> > >>>>>> +    const uint8_t   *buf_end;
> > >>>>>> +} CodecParserContext;
> > >>>>>> +
> > >>>>>> +static const int eac3_sample_rate_tab[] = { 48000, 44100, 32000, 0 };
> > >>>>>> +
> > >>>>>> +void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const
> > >>>> uint8_t
> > >>>>>> *buf, size_t size)
> > >>>>>> +{
> > >>>>>> +    if (size < 8)
> > >>>>>> +        return;
> > >>>>>> +
> > >>>>>> +    info->codec_tag             = AV_RL32(buf);
> > >>>>>> +
> > >>>>>> +    if (info->codec_tag == MKTAG('z','a', 'a', 'c'))
> > >>>>>> +        info->codec_id = AV_CODEC_ID_AAC;
> > >>>>>> +    else if (info->codec_tag == MKTAG('z','a', 'c', '3'))
> > >>>>>> +        info->codec_id = AV_CODEC_ID_AC3;
> > >>>>>> +    else if (info->codec_tag == MKTAG('z','e', 'c', '3'))
> > >>>>>> +        info->codec_id = AV_CODEC_ID_EAC3;
> > >>>>>> +    else
> > >>>>>> +        info->codec_id = AV_CODEC_ID_NONE;
> > >>>>>> +
> > >>>>>> +    buf += 4;
> > >>>>>> +    info->priming               = AV_RL16(buf);
> > >>>>>> +    buf += 2;
> > >>>>>> +    info->version               = *buf++;
> > >>>>>> +    info->setup_data_length     = *buf++;
> > >>>>>> +
> > >>>>>> +    if (info->setup_data_length > size - 8)
> > >>>>>> +        info->setup_data_length = size - 8;
> > >>>>>> +
> > >>>>>> +    if (info->setup_data_length > HLS_MAX_AUDIO_SETUP_DATA_LEN)
> > >>>>>> +        return;
> > >>>>>> +
> > >>>>>> +    memcpy(info->setup_data, buf, info->setup_data_length);
> > >>>>>> +}
> > >>>>>> +
> > >>>>>> +int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo
> > >>>> *info)
> > >>>>>> +{
> > >>>>>> +    int ret = 0;
> > >>>>>> +
> > >>>>>> +    st->codecpar->codec_tag = info->codec_tag;
> > >>>>>> +
> > >>>>>> +    if (st->codecpar->codec_id == AV_CODEC_ID_AAC)
> > >>>>>> +        return 0;
> > >>>>>> +
> > >>>>>> +    if (st->codecpar->codec_id != AV_CODEC_ID_AC3 &&
> > >>>>>> st->codecpar->codec_id != AV_CODEC_ID_EAC3)
> > >>>>>> +        return AVERROR_INVALIDDATA;
> > >>>>>> +
> > >>>>>> +    if (st->codecpar->codec_id == AV_CODEC_ID_AC3) {
> > >>>>>> +
> > >>>>>> +        AC3HeaderInfo *ac3hdr = NULL;
> > >>>>>> +
> > >>>>>> +        ret = avpriv_ac3_parse_header(&ac3hdr, info->setup_data,
> > >>>>>> info->setup_data_length);
> > >>>>>> +        if (ret < 0) {
> > >>>>>> +            if (ret != AVERROR(ENOMEM))
> > >>>>>> +                av_free(ac3hdr);
> > >>>>>> +            return ret;
> > >>>>>> +        }
> > >>>>>> +
> > >>>>>> +        st->codecpar->sample_rate       = ac3hdr->sample_rate;
> > >>>>>> +        st->codecpar->channels          = ac3hdr->channels;
> > >>>>>> +        st->codecpar->channel_layout    = ac3hdr->channel_layout;
> > >>>>>> +        st->codecpar->bit_rate          = ac3hdr->bit_rate;
> > >>>>>> +
> > >>>>>> +        av_free(ac3hdr);
> > >>>>>> +    } else {  /*  Parse 'dec3' EC3SpecificBox */
> > >>>>>> +
> > >>>>>> +        GetBitContext gb;
> > >>>>>> +        int data_rate, fscod, acmod, lfeon;
> > >>>>>> +
> > >>>>>> +        ret = init_get_bits8(&gb, info->setup_data,
> > >>>>>> info->setup_data_length);
> > >>>>>> +        if (ret < 0)
> > >>>>>> +            return AVERROR_INVALIDDATA;
> > >>>>>> +
> > >>>>>> +        data_rate = get_bits(&gb, 13);
> > >>>>>> +        skip_bits(&gb, 3);
> > >>>>>> +        fscod = get_bits(&gb, 2);
> > >>>>>> +        skip_bits(&gb, 10);
> > >>>>>> +        acmod = get_bits(&gb, 3);
> > >>>>>> +        lfeon = get_bits(&gb, 1);
> > >>>>>> +
> > >>>>>> +        st->codecpar->sample_rate = eac3_sample_rate_tab[fscod];
> > >>>>>> +
> > >>>>>> +        st->codecpar->channel_layout =
> > >>>>>> avpriv_ac3_channel_layout_tab[acmod];
> > >>>>>> +        if (lfeon)
> > >>>>>> +            st->codecpar->channel_layout |= AV_CH_LOW_FREQUENCY;
> > >>>>>> +
> > >>>>>> +        st->codecpar->channels =
> > >>>>>> av_get_channel_layout_nb_channels(st->codecpar->channel_layout);
> > >>>>>> +
> > >>>>>> +        st->codecpar->bit_rate = data_rate*1000;
> > >>>>>> +    }
> > >>>>>> +
> > >>>>>> +    return 0;
> > >>>>>> +}
> > >>>>>> +
> > >>>>>> +/*
> > >>>>>> + * Remove start code emulation prevention 0x03 bytes
> > >>>>>> + */
> > >>>>>> +static void remove_scep_3_bytes(NALUnit *nalu)
> > >>>>>> +{
> > >>>>>> +    int i = 0;
> > >>>>>> +    int j = 0;
> > >>>>>> +
> > >>>>>> +    uint8_t *data = nalu->data;
> > >>>>>> +
> > >>>>>> +    while (i < nalu->length) {
> > >>>>>> +        if (nalu->length - i > 3 && AV_RB24(&data[i]) == 0x000003) {
> > >>>>>> +            data[j++] = data[i++];
> > >>>>>> +            data[j++] = data[i++];
> > >>>>>> +            i++;
> > >>>>>> +        } else {
> > >>>>>> +            data[j++] = data[i++];
> > >>>>>> +        }
> > >>>>>> +    }
> > >>>>>> +
> > >>>>>> +    nalu->length = j;
> > >>>>>> +}
> > >>>>>> +
> > >>>>>> +static int get_next_nal_unit(CodecParserContext *ctx, NALUnit *nalu)
> > >>>>>> +{
> > >>>>>> +    const uint8_t *nalu_start = ctx->buf_ptr;
> > >>>>>> +
> > >>>>>> +    if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr) ==
> > >>>>>> 0x00000001)
> > >>>>>> +        nalu->start_code_length = 4;
> > >>>>>> +    else if (ctx->buf_end - ctx->buf_ptr >= 3 && AV_RB24(ctx->buf_ptr)
> > >>>> ==
> > >>>>>> 0x000001)
> > >>>>>> +        nalu->start_code_length = 3;
> > >>>>>> +    else /* No start code at the beginning of the NAL unit */
> > >>>>>> +        return -1;
> > >>>>>> +
> > >>>>>> +    ctx->buf_ptr += nalu->start_code_length;
> > >>>>>> +
> > >>>>>> +    while (ctx->buf_ptr < ctx->buf_end) {
> > >>>>>> +        if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr)
> > >>>> ==
> > >>>>>> 0x00000001)
> > >>>>>> +            break;
> > >>>>>> +        else if (ctx->buf_end - ctx->buf_ptr >= 3 &&
> > >>>>>> AV_RB24(ctx->buf_ptr) == 0x000001)
> > >>>>>> +            break;
> > >>>>>> +        ctx->buf_ptr++;
> > >>>>>> +    }
> > >>>>>> +
> > >>>>>> +    nalu->data  = (uint8_t *)nalu_start + nalu->start_code_length;
> > >>>>>> +    nalu->length = ctx->buf_ptr - nalu->data;
> > >>>>>> +    nalu->type  = *nalu->data & 0x1F;
> > >>>>>> +
> > >>>>>> +    return 0;
> > >>>>>> +}
> > >>>>>> +
> > >>>>>> +static int decrypt_nal_unit(HLSCryptoContext *crypto_ctx, NALUnit
> > >>>> *nalu)
> > >>>>>> +{
> > >>>>>> +    int ret = 0;
> > >>>>>> +    int rem_bytes;
> > >>>>>> +    uint8_t *data;
> > >>>>>> +    uint8_t iv[16];
> > >>>>>> +
> > >>>>>> +    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
> > >>>>>> +    if (ret < 0)
> > >>>>>> +        return ret;
> > >>>>>> +
> > >>>>>> +    /* Remove start code emulation prevention 0x03 bytes */
> > >>>>>> +    remove_scep_3_bytes(nalu);
> > >>>>>> +
> > >>>>>> +    data = nalu->data + 32;
> > >>>>>> +    rem_bytes = nalu->length - 32;
> > >>>>>> +
> > >>>>>> +    memcpy(iv, crypto_ctx->iv, 16);
> > >>>>>> +
> > >>>>>> +    while (rem_bytes > 0) {
> > >>>>>> +        if (rem_bytes > 16) {
> > >>>>>> +            av_aes_crypt(crypto_ctx->aes_ctx, data, data, 1, iv, 1);
> > >>>>>> +            data += 16;
> > >>>>>> +            rem_bytes -= 16;
> > >>>>>> +        }
> > >>>>>> +        data += FFMIN(144, rem_bytes);
> > >>>>>> +        rem_bytes -= FFMIN(144, rem_bytes);
> > >>>>>> +    }
> > >>>>>> +
> > >>>>>> +    return 0;
> > >>>>>> +}
> > >>>>>> +
> > >>>>>> +static int decrypt_video_frame(HLSCryptoContext *crypto_ctx, AVPacket
> > >>>>>> *pkt)
> > >>>>>> +{
> > >>>>>> +    int ret = 0;
> > >>>>>> +    CodecParserContext  ctx;
> > >>>>>> +    NALUnit nalu;
> > >>>>>> +    uint8_t *data_ptr;
> > >>>>>> +    int move_nalu = 0;
> > >>>>>> +
> > >>>>>> +    memset(&ctx, 0, sizeof(ctx));
> > >>>>>> +    ctx.buf_ptr  = pkt->data;
> > >>>>>> +    ctx.buf_end = pkt->data + pkt->size;
> > >>>>>> +
> > >>>>>> +    data_ptr = pkt->data;
> > >>>>>> +
> > >>>>>> +    while (ctx.buf_ptr < ctx.buf_end) {
> > >>>>>> +        memset(&nalu, 0, sizeof(nalu));
> > >>>>>> +        ret = get_next_nal_unit(&ctx, &nalu);
> > >>>>>> +        if (ret < 0)
> > >>>>>> +            return ret;
> > >>>>>> +        if ((nalu.type == 0x01 || nalu.type == 0x05) && nalu.length >
> > >>>> 48)
> > >>>>>> {
> > >>>>>> +            int encrypted_nalu_length = nalu.length;
> > >>>>>> +            ret = decrypt_nal_unit(crypto_ctx, &nalu);
> > >>>>>> +            if (ret < 0)
> > >>>>>> +                return ret;
> > >>>>>> +            move_nalu = nalu.length != encrypted_nalu_length;
> > >>>>>> +        }
> > >>>>>> +        if (move_nalu)
> > >>>>>> +            memmove(data_ptr, nalu.data - nalu.start_code_length,
> > >>>>>> nalu.start_code_length + nalu.length);
> > >>>>>> +        data_ptr += nalu.start_code_length + nalu.length;
> > >>>>>> +    }
> > >>>>>> +
> > >>>>>> +    av_shrink_packet(pkt, data_ptr - pkt->data);
> > >>>>>> +
> > >>>>>> +    return 0;
> > >>>>>> +}
> > >>>>>> +
> > >>>>>> +static int get_next_adts_frame(CodecParserContext *ctx, AudioFrame
> > >>>> *frame)
> > >>>>>> +{
> > >>>>>> +    int ret = 0;
> > >>>>>> +
> > >>>>>> +    AACADTSHeaderInfo *adts_hdr = NULL;
> > >>>>>> +
> > >>>>>> +    /* Find next sync word 0xFFF */
> > >>>>>> +    while (ctx->buf_ptr < ctx->buf_end - 1) {
> > >>>>>> +        if (*ctx->buf_ptr == 0xFF && *(ctx->buf_ptr + 1) & 0xF0 ==
> > >>>> 0xF0)
> > >>>>>> +            break;
> > >>>>>> +        ctx->buf_ptr++;
> > >>>>>> +    }
> > >>>>>> +
> > >>>>>> +    if (ctx->buf_ptr >= ctx->buf_end - 1)
> > >>>>>> +        return -1;
> > >>>>>> +
> > >>>>>> +    frame->data = (uint8_t*)ctx->buf_ptr;
> > >>>>>> +
> > >>>>>> +    ret = avpriv_adts_header_parse (&adts_hdr, frame->data,
> > >>>> ctx->buf_end
> > >>>>>> - frame->data);
> > >>>>>> +    if (ret < 0)
> > >>>>>> +        return ret;
> > >>>>>> +
> > >>>>>> +    frame->header_length = adts_hdr->crc_absent ?
> > >>>> AV_AAC_ADTS_HEADER_SIZE
> > >>>>>> : AV_AAC_ADTS_HEADER_SIZE + 2;
> > >>>>>> +    frame->length = adts_hdr->frame_length;
> > >>>>>> +
> > >>>>>> +    av_free(adts_hdr);
> > >>>>>> +
> > >>>>>> +    return 0;
> > >>>>>> +}
> > >>>>>> +
> > >>>>>> +static int get_next_ac3_eac3_sync_frame(CodecParserContext *ctx,
> > >>>>>> AudioFrame *frame)
> > >>>>>> +{
> > >>>>>> +    int ret = 0;
> > >>>>>> +
> > >>>>>> +    AC3HeaderInfo *hdr = NULL;
> > >>>>>> +
> > >>>>>> +    /* Find next sync word 0x0B77 */
> > >>>>>> +    while (ctx->buf_ptr < ctx->buf_end - 1) {
> > >>>>>> +        if (*ctx->buf_ptr == 0x0B && *(ctx->buf_ptr + 1) == 0x77)
> > >>>>>> +            break;
> > >>>>>> +        ctx->buf_ptr++;
> > >>>>>> +    }
> > >>>>>> +
> > >>>>>> +    if (ctx->buf_ptr >= ctx->buf_end - 1)
> > >>>>>> +        return -1;
> > >>>>>> +
> > >>>>>> +    frame->data = (uint8_t*)ctx->buf_ptr;
> > >>>>>> +    frame->header_length = 0;
> > >>>>>> +
> > >>>>>> +    ret = avpriv_ac3_parse_header(&hdr, frame->data, ctx->buf_end -
> > >>>>>> frame->data);
> > >>>>>> +    if (ret < 0) {
> > >>>>>> +        if (ret != AVERROR(ENOMEM))
> > >>>>>> +            av_free(hdr);
> > >>>>>> +        return ret;
> > >>>>>> +    }
> > >>>>>> +
> > >>>>>> +    frame->length = hdr->frame_size;
> > >>>>>> +
> > >>>>>> +    av_free(hdr);
> > >>>>>> +
> > >>>>>> +    return 0;
> > >>>>>> +}
> > >>>>>> +
> > >>>>>> +static int get_next_sync_frame(enum AVCodecID codec_id,
> > >>>>>> CodecParserContext *ctx, AudioFrame *frame)
> > >>>>>> +{
> > >>>>>> +    if (codec_id == AV_CODEC_ID_AAC)
> > >>>>>> +        return get_next_adts_frame(ctx, frame);
> > >>>>>> +    else if (codec_id == AV_CODEC_ID_AC3 || codec_id ==
> > >>>> AV_CODEC_ID_EAC3)
> > >>>>>> +        return get_next_ac3_eac3_sync_frame(ctx, frame);
> > >>>>>> +    else
> > >>>>>> +        return AVERROR_INVALIDDATA;
> > >>>>>> +}
> > >>>>>> +
> > >>>>>> +static int decrypt_sync_frame(enum AVCodecID codec_id, HLSCryptoContext
> > >>>>>> *crypto_ctx, AudioFrame *frame)
> > >>>>>> +{
> > >>>>>> +    int ret = 0;
> > >>>>>> +    uint8_t *data;
> > >>>>>> +    int num_of_encrypted_blocks;
> > >>>>>> +
> > >>>>>> +    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
> > >>>>>> +    if (ret < 0)
> > >>>>>> +        return ret;
> > >>>>>> +
> > >>>>>> +    data = frame->data + frame->header_length + 16;
> > >>>>>> +
> > >>>>>> +    num_of_encrypted_blocks = (frame->length - frame->header_length -
> > >>>>>> 16)/16;
> > >>>>>> +
> > >>>>>> +    av_aes_crypt(crypto_ctx->aes_ctx, data, data,
> > >>>>>> num_of_encrypted_blocks, crypto_ctx->iv, 1);
> > >>>>>> +
> > >>>>>> +    return 0;
> > >>>>>> +}
> > >>>>>> +
> > >>>>>> +static int decrypt_audio_frame(enum AVCodecID codec_id,
> > >>>> HLSCryptoContext
> > >>>>>> *crypto_ctx, AVPacket *pkt)
> > >>>>>> +{
> > >>>>>> +    int ret = 0;
> > >>>>>> +    CodecParserContext  ctx;
> > >>>>>> +    AudioFrame frame;
> > >>>>>> +
> > >>>>>> +    memset(&ctx, 0, sizeof(ctx));
> > >>>>>> +    ctx.buf_ptr        = pkt->data;
> > >>>>>> +    ctx.buf_end = pkt->data + pkt->size;
> > >>>>>> +
> > >>>>>> +    while (ctx.buf_ptr < ctx.buf_end) {
> > >>>>>> +        memset(&frame, 0, sizeof(frame));
> > >>>>>> +        ret = get_next_sync_frame(codec_id, &ctx, &frame);
> > >>>>>> +        if (ret < 0)
> > >>>>>> +            return ret;
> > >>>>>> +        if (frame.length - frame.header_length > 31) {
> > >>>>>> +            ret = decrypt_sync_frame(codec_id, crypto_ctx, &frame);
> > >>>>>> +            if (ret < 0)
> > >>>>>> +                return ret;
> > >>>>>> +        }
> > >>>>>> +        ctx.buf_ptr += frame.length;
> > >>>>>> +    }
> > >>>>>> +
> > >>>>>> +    return 0;
> > >>>>>> +}
> > >>>>>> +
> > >>>>>> +int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext
> > >>>>>> *crypto_ctx, AVPacket *pkt)
> > >>>>>> +{
> > >>>>>> +    if (codec_id == AV_CODEC_ID_H264)
> > >>>>>> +        return decrypt_video_frame(crypto_ctx, pkt);
> > >>>>>> +    else if (codec_id == AV_CODEC_ID_AAC || codec_id == AV_CODEC_ID_AC3
> > >>>>>> || codec_id == AV_CODEC_ID_EAC3)
> > >>>>>> +        return decrypt_audio_frame(codec_id, crypto_ctx, pkt);
> > >>>>>> +
> > >>>>>> +    return AVERROR_INVALIDDATA;
> > >>>>>> +}
> > >>>>>> diff --git a/libavformat/hls_sample_aes.h b/libavformat/hls_sample_aes.h
> > >>>>>> new file mode 100644
> > >>>>>> index 0000000000..cf80e41cb0
> > >>>>>> --- /dev/null
> > >>>>>> +++ b/libavformat/hls_sample_aes.h
> > >>>>>> @@ -0,0 +1,66 @@
> > >>>>>> +/*
> > >>>>>> + * Apple HTTP Live Streaming Sample Encryption/Decryption
> > >>>>>> + *
> > >>>>>> + * Copyright (c) 2021 Nachiket Tarate
> > >>>>>> + *
> > >>>>>> + * This file is part of FFmpeg.
> > >>>>>> + *
> > >>>>>> + * FFmpeg is free software; you can redistribute it and/or
> > >>>>>> + * modify it under the terms of the GNU Lesser General Public
> > >>>>>> + * License as published by the Free Software Foundation; either
> > >>>>>> + * version 2.1 of the License, or (at your option) any later version.
> > >>>>>> + *
> > >>>>>> + * FFmpeg is distributed in the hope that it will be useful,
> > >>>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > >>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > >>>>>> + * Lesser General Public License for more details.
> > >>>>>> + *
> > >>>>>> + * You should have received a copy of the GNU Lesser General Public
> > >>>>>> + * License along with FFmpeg; if not, write to the Free Software
> > >>>>>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> > >>>>>> 02110-1301 USA
> > >>>>>> + */
> > >>>>>> +
> > >>>>>> +/**
> > >>>>>> + * @file
> > >>>>>> + * Apple HTTP Live Streaming Sample Encryption
> > >>>>>> + *
> > >>>>>>
> > >>>> https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
> > >>>>>> + */
> > >>>>>> +
> > >>>>>> +#ifndef AVFORMAT_HLS_SAMPLE_AES_H
> > >>>>>> +#define AVFORMAT_HLS_SAMPLE_AES_H
> > >>>>>> +
> > >>>>>> +#include <stdint.h>
> > >>>>>> +
> > >>>>>> +#include "avformat.h"
> > >>>>>> +
> > >>>>>> +#include "libavcodec/avcodec.h"
> > >>>>>> +#include "libavutil/aes.h"
> > >>>>>> +
> > >>>>>> +#define HLS_MAX_ID3_TAGS_DATA_LEN       138
> > >>>>>> +#define HLS_MAX_AUDIO_SETUP_DATA_LEN    10
> > >>>>>> +
> > >>>>>> +
> > >>>>>> +typedef struct HLSCryptoContext {
> > >>>>>> +    struct AVAES   *aes_ctx;
> > >>>>>> +    uint8_t            key[16];
> > >>>>>> +    uint8_t            iv[16];
> > >>>>>> +} HLSCryptoContext;
> > >>>>>> +
> > >>>>>> +typedef struct HLSAudioSetupInfo {
> > >>>>>> +    enum AVCodecID      codec_id;
> > >>>>>> +    uint32_t            codec_tag;
> > >>>>>> +    uint16_t            priming;
> > >>>>>> +    uint8_t             version;
> > >>>>>> +    uint8_t             setup_data_length;
> > >>>>>> +    uint8_t             setup_data[HLS_MAX_AUDIO_SETUP_DATA_LEN];
> > >>>>>> +} HLSAudioSetupInfo;
> > >>>>>> +
> > >>>>>> +
> > >>>>>> +void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const
> > >>>> uint8_t
> > >>>>>> *buf, size_t size);
> > >>>>>> +
> > >>>>>> +int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo
> > >>>> *info);
> > >>>>>> +
> > >>>>>> +int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext
> > >>>>>> *crypto_ctx, AVPacket *pkt);
> > >>>>>> +
> > >>>>>> +#endif /* AVFORMAT_HLS_SAMPLE_AES_H */
> > >>>>>> +
> > >>>>>> diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
> > >>>>>> index e283ec09d7..dc611ae788 100644
> > >>>>>> --- a/libavformat/mpegts.c
> > >>>>>> +++ b/libavformat/mpegts.c
> > >>>>>> @@ -839,6 +839,16 @@ static const StreamType MISC_types[] = {
> > >>>>>>   { 0 },
> > >>>>>> };
> > >>>>>>
> > >>>>>> +/* HLS Sample Encryption Types  */
> > >>>>>> +static const StreamType HLS_SAMPLE_ENC_types[] = {
> > >>>>>> +    { 0xdb, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264},
> > >>>>>> +    { 0xcf, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
> > >>>>>> +    { 0xc1, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 },
> > >>>>>> +    { 0xc2, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3},
> > >>>>>> +    { 0 },
> > >>>>>> +};
> > >>>>>> +
> > >>>>>> +
> > >>>>>> static const StreamType REGD_types[] = {
> > >>>>>>   { MKTAG('d', 'r', 'a', 'c'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC
> > >>>> },
> > >>>>>>   { MKTAG('A', 'C', '-', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3
> > >>>> },
> > >>>>>> @@ -948,6 +958,8 @@ static int mpegts_set_stream_info(AVStream *st,
> > >>>>>> PESContext *pes,
> > >>>>>>   }
> > >>>>>>   if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
> > >>>>>>       mpegts_find_stream_type(st, pes->stream_type, MISC_types);
> > >>>>>> +    if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
> > >>>>>> +        mpegts_find_stream_type(st, pes->stream_type,
> > >>>>>> HLS_SAMPLE_ENC_types);
> > >>>>>>   if (st->codecpar->codec_id == AV_CODEC_ID_NONE) {
> > >>>>>>       st->codecpar->codec_id  = old_codec_id;
> > >>>>>>       st->codecpar->codec_type = old_codec_type;
> > >>>>>> --
> > >>>>>> 2.17.1
> > >>>>>>
> > >>>>>>
> > >>>>> _______________________________________________
> > >>>>> ffmpeg-devel mailing list
> > >>>>> ffmpeg-devel@ffmpeg.org
> > >>>>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> > >>>>>
> > >>>>> To unsubscribe, visit link above, or email
> > >>>>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> > >>>>
> > >>>> Thanks
> > >>>>
> > >>>> Steven Liu
> > >>>>
> > >>>>
> > >>>>
> > >>>> _______________________________________________
> > >>>> ffmpeg-devel mailing list
> > >>>> ffmpeg-devel@ffmpeg.org
> > >>>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> > >>>>
> > >>>> To unsubscribe, visit link above, or email
> > >>>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> > >>> _______________________________________________
> > >>> ffmpeg-devel mailing list
> > >>> ffmpeg-devel@ffmpeg.org
> > >>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> > >>>
> > >>> To unsubscribe, visit link above, or email
> > >>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> > >>
> > >> Thanks
> > >>
> > >> Steven Liu
> > >>
> > >>
> > >>
> > >> _______________________________________________
> > >> ffmpeg-devel mailing list
> > >> ffmpeg-devel@ffmpeg.org
> > >> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> > >>
> > >> To unsubscribe, visit link above, or email
> > >> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> >
> > Thanks
> >
> > Steven Liu
> >
> >
> >
Liu Steven March 8, 2021, 8:28 a.m. UTC | #9
> 2021年3月8日 下午3:15,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
>> 
>> Your understanding of SAMPLE-AES is absolutely correct.
>> 
>> I am insisting that let us add support for it in 2 phases as
>> completely 2 different specifications have been used.
>> 
>> Phase 1 : Media segments in MPEG-TS, AAC, AC-3 and Enhanced AC-3
>> formats (Apple's specifications)
>> 
>> Phase 2 : Media segments in fragmented MP4 format (CENC specifications)
>> 
>> Kindly let me know your opinion.
> 
> Hello Steven,
> 
> I am looking forward to hearing from you.
You could make 4 or 5 patch merge two phase to one patchset.
> 
> Best Regards,
> Nachiket

Thanks

Steven Liu
diff mbox series

Patch

diff --git a/libavformat/Makefile b/libavformat/Makefile
index fcb39ce133..62627d50a6 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -236,7 +236,7 @@  OBJS-$(CONFIG_HCOM_DEMUXER)              += hcom.o pcm.o
 OBJS-$(CONFIG_HDS_MUXER)                 += hdsenc.o
 OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
 OBJS-$(CONFIG_HEVC_MUXER)                += rawenc.o
-OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o
+OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o hls_sample_aes.o
 OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o hlsplaylist.o avc.o
 OBJS-$(CONFIG_HNM_DEMUXER)               += hnm.o
 OBJS-$(CONFIG_ICO_DEMUXER)               += icodec.o
diff --git a/libavformat/hls.c b/libavformat/hls.c
index af2468ad9b..3cb3853c79 100644
--- a/libavformat/hls.c
+++ b/libavformat/hls.c
@@ -2,6 +2,7 @@ 
  * Apple HTTP Live Streaming demuxer
  * Copyright (c) 2010 Martin Storsjo
  * Copyright (c) 2013 Anssi Hannula
+ * Copyright (c) 2021 Nachiket Tarate
  *
  * This file is part of FFmpeg.
  *
@@ -39,6 +40,8 @@ 
 #include "avio_internal.h"
 #include "id3v2.h"
 
+#include "hls_sample_aes.h"
+
 #define INITIAL_BUFFER_SIZE 32768
 
 #define MAX_FIELD_LEN 64
@@ -145,6 +148,10 @@  struct playlist {
     int id3_changed; /* ID3 tag data has changed at some point */
     ID3v2ExtraMeta *id3_deferred_extra; /* stored here until subdemuxer is opened */
 
+    /* Used in case of SAMPLE-AES encryption method */
+    HLSAudioSetupInfo audio_setup_info;
+    HLSCryptoContext  crypto_ctx;
+
     int64_t seek_timestamp;
     int seek_flags;
     int seek_stream_index; /* into subdemuxer stream array */
@@ -266,6 +273,8 @@  static void free_playlist_list(HLSContext *c)
             pls->ctx->pb = NULL;
             avformat_close_input(&pls->ctx);
         }
+        if (pls->crypto_ctx.aes_ctx)
+             av_free(pls->crypto_ctx.aes_ctx);
         av_free(pls);
     }
     av_freep(&c->playlists);
@@ -994,7 +1003,10 @@  fail:
 
 static struct segment *current_segment(struct playlist *pls)
 {
-    return pls->segments[pls->cur_seq_no - pls->start_seq_no];
+    int64_t n = pls->cur_seq_no - pls->start_seq_no;
+    if (n >= pls->n_segments)
+        return NULL;
+    return pls->segments[n];
 }
 
 static struct segment *next_segment(struct playlist *pls)
@@ -1023,10 +1035,11 @@  static int read_from_url(struct playlist *pls, struct segment *seg,
 
 /* Parse the raw ID3 data and pass contents to caller */
 static void parse_id3(AVFormatContext *s, AVIOContext *pb,
-                      AVDictionary **metadata, int64_t *dts,
+                      AVDictionary **metadata, int64_t *dts, HLSAudioSetupInfo *audio_setup_info,
                       ID3v2ExtraMetaAPIC **apic, ID3v2ExtraMeta **extra_meta)
 {
     static const char id3_priv_owner_ts[] = "com.apple.streaming.transportStreamTimestamp";
+    static const char id3_priv_owner_audio_setup[] = "com.apple.streaming.audioDescription";
     ID3v2ExtraMeta *meta;
 
     ff_id3v2_read_dict(pb, metadata, ID3v2_DEFAULT_MAGIC, extra_meta);
@@ -1041,6 +1054,8 @@  static void parse_id3(AVFormatContext *s, AVIOContext *pb,
                     *dts = ts;
                 else
                     av_log(s, AV_LOG_ERROR, "Invalid HLS ID3 audio timestamp %"PRId64"\n", ts);
+            } else if (priv->datasize >= 8 && !strcmp(priv->owner, id3_priv_owner_audio_setup)) {
+                ff_hls_read_audio_setup_info(audio_setup_info, priv->data, priv->datasize);
             }
         } else if (!strcmp(meta->tag, "APIC") && apic)
             *apic = &meta->data.apic;
@@ -1084,7 +1099,7 @@  static void handle_id3(AVIOContext *pb, struct playlist *pls)
     ID3v2ExtraMeta *extra_meta = NULL;
     int64_t timestamp = AV_NOPTS_VALUE;
 
-    parse_id3(pls->ctx, pb, &metadata, &timestamp, &apic, &extra_meta);
+    parse_id3(pls->ctx, pb, &metadata, &timestamp, &pls->audio_setup_info, &apic, &extra_meta);
 
     if (timestamp != AV_NOPTS_VALUE) {
         pls->id3_mpegts_timestamp = timestamp;
@@ -1238,10 +1253,7 @@  static int open_input(HLSContext *c, struct playlist *pls, struct segment *seg,
     av_log(pls->parent, AV_LOG_VERBOSE, "HLS request for url '%s', offset %"PRId64", playlist %d\n",
            seg->url, seg->url_offset, pls->index);
 
-    if (seg->key_type == KEY_NONE) {
-        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts, &is_http);
-    } else if (seg->key_type == KEY_AES_128) {
-        char iv[33], key[33], url[MAX_URL_SIZE];
+    if (seg->key_type == KEY_AES_128 || seg->key_type == KEY_SAMPLE_AES) {
         if (strcmp(seg->key, pls->key_url)) {
             AVIOContext *pb = NULL;
             if (open_url(pls->parent, &pb, seg->key, &c->avio_opts, opts, NULL) == 0) {
@@ -1257,6 +1269,10 @@  static int open_input(HLSContext *c, struct playlist *pls, struct segment *seg,
             }
             av_strlcpy(pls->key_url, seg->key, sizeof(pls->key_url));
         }
+    }
+
+    if (seg->key_type == KEY_AES_128) {
+        char iv[33], key[33], url[MAX_URL_SIZE];
         ff_data_to_hex(iv, seg->iv, sizeof(seg->iv), 0);
         ff_data_to_hex(key, pls->key, sizeof(pls->key), 0);
         iv[32] = key[32] = '\0';
@@ -1273,13 +1289,9 @@  static int open_input(HLSContext *c, struct playlist *pls, struct segment *seg,
             goto cleanup;
         }
         ret = 0;
-    } else if (seg->key_type == KEY_SAMPLE_AES) {
-        av_log(pls->parent, AV_LOG_ERROR,
-               "SAMPLE-AES encryption is not supported yet\n");
-        ret = AVERROR_PATCHWELCOME;
+    } else {
+        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts, &is_http);
     }
-    else
-      ret = AVERROR(ENOSYS);
 
     /* Seek to the requested position. If this was a HTTP request, the offset
      * should already be where want it to, but this allows e.g. local testing
@@ -1948,6 +1960,7 @@  static int hls_read_header(AVFormatContext *s)
         struct playlist *pls = c->playlists[i];
         char *url;
         ff_const59 AVInputFormat *in_fmt = NULL;
+        struct segment *seg = NULL;
 
         if (!(pls->ctx = avformat_alloc_context())) {
             ret = AVERROR(ENOMEM);
@@ -1980,8 +1993,41 @@  static int hls_read_header(AVFormatContext *s)
             pls->ctx = NULL;
             goto fail;
         }
+
         ffio_init_context(&pls->pb, pls->read_buffer, INITIAL_BUFFER_SIZE, 0, pls,
                           read_data, NULL, NULL);
+
+        /*
+         * If encryption scheme is SAMPLE-AES, try to read  ID3 tags of
+         * external audio track that contains audio setup information
+         */
+        seg = current_segment(pls);
+        if (seg && seg->key_type == KEY_SAMPLE_AES && pls->n_renditions > 0 &&
+            pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO) {
+            uint8_t buf[HLS_MAX_ID3_TAGS_DATA_LEN];
+            if ((ret = avio_read(&pls->pb, buf, HLS_MAX_ID3_TAGS_DATA_LEN)) < 0) {
+                /* Fail if error was not end of file */
+                if (ret != AVERROR_EOF) {
+                    avformat_free_context(pls->ctx);
+                    pls->ctx = NULL;
+                    goto fail;
+                }
+            }
+            ret = 0;
+        }
+
+        /*
+         * If encryption scheme is SAMPLE-AES and audio setup information is present in external audio track,
+         * use that information to find the media format, otherwise probe input data
+         */
+        if (seg && seg->key_type == KEY_SAMPLE_AES && pls->is_id3_timestamped &&
+            pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) {
+            void *iter = NULL;
+            while ((in_fmt = (ff_const59 AVInputFormat *)av_demuxer_iterate(&iter)))
+                if (in_fmt->raw_codec_id == pls->audio_setup_info.codec_id) {
+                    break;
+                }
+        } else {
         pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 * 4;
         pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ? s->max_analyze_duration : 4 * AV_TIME_BASE;
         pls->ctx->interrupt_callback = s->interrupt_callback;
@@ -1999,6 +2045,25 @@  static int hls_read_header(AVFormatContext *s)
             goto fail;
         }
         av_free(url);
+        }
+
+        if (seg && seg->key_type == KEY_SAMPLE_AES) {
+            if (!pls->is_id3_timestamped && pls->n_renditions > 0 && pls->renditions[0]->type != AVMEDIA_TYPE_AUDIO &&
+                strcmp(in_fmt->name, "mpegts")) {
+                av_log(s, AV_LOG_ERROR, "SAMPLE-AES encryption is not supported for fragmented MP4 format yet\n");
+                ret = AVERROR_PATCHWELCOME;
+            } else {
+                pls->crypto_ctx.aes_ctx = av_aes_alloc();
+                if (!pls->crypto_ctx.aes_ctx)
+                    ret = AVERROR(ENOMEM);
+            }
+            if (ret != 0) {
+                avformat_free_context(pls->ctx);
+                pls->ctx = NULL;
+                goto fail;
+            }
+        }
+
         pls->ctx->pb       = &pls->pb;
         pls->ctx->io_open  = nested_io_open;
         pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
@@ -2027,7 +2092,12 @@  static int hls_read_header(AVFormatContext *s)
          * on us if they want to.
          */
         if (pls->is_id3_timestamped || (pls->n_renditions > 0 && pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO)) {
+            if (seg && seg->key_type == KEY_SAMPLE_AES && pls->audio_setup_info.setup_data_length > 0 &&
+                pls->ctx->nb_streams == 1)
+                ret = ff_hls_parse_audio_setup_info(pls->ctx->streams[0], &pls->audio_setup_info);
+            else
             ret = avformat_find_stream_info(pls->ctx, NULL);
+
             if (ret < 0)
                 goto fail;
         }
@@ -2157,6 +2227,7 @@  static int hls_read_packet(AVFormatContext *s, AVPacket *pkt)
             while (1) {
                 int64_t ts_diff;
                 AVRational tb;
+                struct segment *seg = NULL;
                 ret = av_read_frame(pls->ctx, &pls->pkt);
                 if (ret < 0) {
                     if (!avio_feof(&pls->pb) && ret != AVERROR_EOF)
@@ -2175,6 +2246,14 @@  static int hls_read_packet(AVFormatContext *s, AVPacket *pkt)
                             get_timebase(pls), AV_TIME_BASE_Q);
                 }
 
+                seg = current_segment(pls);
+                if (seg && seg->key_type == KEY_SAMPLE_AES) {
+                    enum AVCodecID codec_id = pls->ctx->streams[pls->pkt.stream_index]->codecpar->codec_id;
+                    memcpy(pls->crypto_ctx.iv, seg->iv, sizeof(seg->iv));
+                    memcpy(pls->crypto_ctx.key, pls->key, sizeof(pls->key));
+                    ff_hls_decrypt_frame(codec_id, &pls->crypto_ctx, &pls->pkt);
+                }
+
                 if (pls->seek_timestamp == AV_NOPTS_VALUE)
                     break;
 
diff --git a/libavformat/hls_sample_aes.c b/libavformat/hls_sample_aes.c
new file mode 100644
index 0000000000..0407a15b0f
--- /dev/null
+++ b/libavformat/hls_sample_aes.c
@@ -0,0 +1,391 @@ 
+/*
+ * Apple HTTP Live Streaming Sample Encryption/Decryption
+ *
+ * Copyright (c) 2021 Nachiket Tarate
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Apple HTTP Live Streaming Sample Encryption
+ * https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
+ */
+
+#include "hls_sample_aes.h"
+
+#include "libavcodec/adts_header.h"
+#include "libavcodec/adts_parser.h"
+#include "libavcodec/ac3_parser_internal.h"
+
+
+typedef struct NALUnit {
+    uint8_t     *data;
+    int         type;
+    int         length;
+    int         start_code_length;
+} NALUnit;
+
+typedef struct AudioFrame {
+    uint8_t     *data;
+    int         length;
+    int         header_length;
+} AudioFrame;
+
+typedef struct CodecParserContext {
+    const uint8_t   *buf_ptr;
+    const uint8_t   *buf_end;
+} CodecParserContext;
+
+static const int eac3_sample_rate_tab[] = { 48000, 44100, 32000, 0 };
+
+void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const uint8_t *buf, size_t size)
+{
+    if (size < 8)
+        return;
+
+    info->codec_tag 		 = AV_RL32(buf);
+
+    if (info->codec_tag == MKTAG('z','a', 'a', 'c'))
+        info->codec_id = AV_CODEC_ID_AAC;
+    else if (info->codec_tag == MKTAG('z','a', 'c', '3'))
+        info->codec_id = AV_CODEC_ID_AC3;
+    else if (info->codec_tag == MKTAG('z','e', 'c', '3'))
+        info->codec_id = AV_CODEC_ID_EAC3;
+    else
+        info->codec_id = AV_CODEC_ID_NONE;
+
+    buf += 4;
+    info->priming               = AV_RL16(buf);
+    buf += 2;
+    info->version               = *buf++;
+    info->setup_data_length     = *buf++;
+
+    if (info->setup_data_length > size - 8)
+        info->setup_data_length = size - 8;
+
+    if (info->setup_data_length > HLS_MAX_AUDIO_SETUP_DATA_LEN)
+        return;
+
+    memcpy(info->setup_data, buf, info->setup_data_length);
+}
+
+int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo *info)
+{
+    int ret = 0;
+
+    st->codecpar->codec_tag = info->codec_tag;
+
+    if (st->codecpar->codec_id == AV_CODEC_ID_AAC)
+        return 0;
+
+    if (st->codecpar->codec_id != AV_CODEC_ID_AC3 && st->codecpar->codec_id != AV_CODEC_ID_EAC3)
+        return AVERROR_INVALIDDATA;
+
+    if (st->codecpar->codec_id == AV_CODEC_ID_AC3) {
+
+        AC3HeaderInfo *ac3hdr = NULL;
+
+        ret = avpriv_ac3_parse_header(&ac3hdr, info->setup_data, info->setup_data_length);
+        if (ret < 0) {
+            if (ret != AVERROR(ENOMEM))
+                av_free(ac3hdr);
+            return ret;
+        }
+
+        st->codecpar->sample_rate       = ac3hdr->sample_rate;
+        st->codecpar->channels          = ac3hdr->channels;
+        st->codecpar->channel_layout    = ac3hdr->channel_layout;
+        st->codecpar->bit_rate          = ac3hdr->bit_rate;
+
+        av_free(ac3hdr);
+    } else {  /*  Parse 'dec3' EC3SpecificBox */
+
+        GetBitContext gb;
+        int data_rate, fscod, acmod, lfeon;
+
+        ret = init_get_bits8(&gb, info->setup_data, info->setup_data_length);
+        if (ret < 0)
+            return AVERROR_INVALIDDATA;
+
+        data_rate = get_bits(&gb, 13);
+        skip_bits(&gb, 3);
+        fscod = get_bits(&gb, 2);
+        skip_bits(&gb, 10);
+        acmod = get_bits(&gb, 3);
+        lfeon = get_bits(&gb, 1);
+
+        st->codecpar->sample_rate = eac3_sample_rate_tab[fscod];
+
+        st->codecpar->channel_layout = avpriv_ac3_channel_layout_tab[acmod];
+        if (lfeon)
+            st->codecpar->channel_layout |= AV_CH_LOW_FREQUENCY;
+
+        st->codecpar->channels = av_get_channel_layout_nb_channels(st->codecpar->channel_layout);
+
+        st->codecpar->bit_rate = data_rate*1000;
+    }
+
+    return 0;
+}
+
+/*
+ * Remove start code emulation prevention 0x03 bytes
+ */
+static void remove_scep_3_bytes(NALUnit *nalu)
+{
+    int i = 0;
+    int j = 0;
+
+    uint8_t *data = nalu->data;
+
+    while (i < nalu->length) {
+        if (nalu->length - i > 3 && AV_RB24(&data[i]) == 0x000003) {
+            data[j++] = data[i++];
+            data[j++] = data[i++];
+            i++;
+        } else {
+            data[j++] = data[i++];
+        }
+    }
+
+    nalu->length = j;
+}
+
+static int get_next_nal_unit(CodecParserContext *ctx, NALUnit *nalu)
+{
+    const uint8_t *nalu_start = ctx->buf_ptr;
+
+    if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr) == 0x00000001)
+        nalu->start_code_length = 4;
+    else if (ctx->buf_end - ctx->buf_ptr >= 3 && AV_RB24(ctx->buf_ptr) == 0x000001)
+        nalu->start_code_length = 3;
+    else /* No start code at the beginning of the NAL unit */
+        return -1;
+
+    ctx->buf_ptr += nalu->start_code_length;
+
+    while (ctx->buf_ptr < ctx->buf_end) {
+        if (ctx->buf_end - ctx->buf_ptr >= 4 && AV_RB32(ctx->buf_ptr) == 0x00000001)
+            break;
+        else if (ctx->buf_end - ctx->buf_ptr >= 3 && AV_RB24(ctx->buf_ptr) == 0x000001)
+            break;
+        ctx->buf_ptr++;
+    }
+
+    nalu->data	 = (uint8_t *)nalu_start + nalu->start_code_length;
+    nalu->length = ctx->buf_ptr - nalu->data;
+    nalu->type	 = *nalu->data & 0x1F;
+
+    return 0;
+}
+
+static int decrypt_nal_unit(HLSCryptoContext *crypto_ctx, NALUnit *nalu)
+{
+    int ret = 0;
+    int rem_bytes;
+    uint8_t *data;
+    uint8_t iv[16];
+
+    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
+    if (ret < 0)
+        return ret;
+
+    /* Remove start code emulation prevention 0x03 bytes */
+    remove_scep_3_bytes(nalu);
+
+    data = nalu->data + 32;
+    rem_bytes = nalu->length - 32;
+
+    memcpy(iv, crypto_ctx->iv, 16);
+
+    while (rem_bytes > 0) {
+        if (rem_bytes > 16) {
+            av_aes_crypt(crypto_ctx->aes_ctx, data, data, 1, iv, 1);
+            data += 16;
+            rem_bytes -= 16;
+        }
+        data += FFMIN(144, rem_bytes);
+        rem_bytes -= FFMIN(144, rem_bytes);
+    }
+
+    return 0;
+}
+
+static int decrypt_video_frame(HLSCryptoContext *crypto_ctx, AVPacket *pkt)
+{
+    int ret = 0;
+    CodecParserContext  ctx;
+    NALUnit nalu;
+    uint8_t *data_ptr;
+    int move_nalu = 0;
+
+    memset(&ctx, 0, sizeof(ctx));
+    ctx.buf_ptr  = pkt->data;
+    ctx.buf_end = pkt->data + pkt->size;
+
+    data_ptr = pkt->data;
+
+    while (ctx.buf_ptr < ctx.buf_end) {
+        memset(&nalu, 0, sizeof(nalu));
+        ret = get_next_nal_unit(&ctx, &nalu);
+        if (ret < 0)
+            return ret;
+        if ((nalu.type == 0x01 || nalu.type == 0x05) && nalu.length > 48) {
+            int encrypted_nalu_length = nalu.length;
+            ret = decrypt_nal_unit(crypto_ctx, &nalu);
+            if (ret < 0)
+                return ret;
+            move_nalu = nalu.length != encrypted_nalu_length;
+        }
+        if (move_nalu)
+            memmove(data_ptr, nalu.data - nalu.start_code_length, nalu.start_code_length + nalu.length);
+        data_ptr += nalu.start_code_length + nalu.length;
+    }
+
+    av_shrink_packet(pkt, data_ptr - pkt->data);
+
+    return 0;
+}
+
+static int get_next_adts_frame(CodecParserContext *ctx, AudioFrame *frame)
+{
+    int ret = 0;
+
+    AACADTSHeaderInfo *adts_hdr = NULL;
+
+    /* Find next sync word 0xFFF */
+    while (ctx->buf_ptr < ctx->buf_end - 1) {
+        if (*ctx->buf_ptr == 0xFF && *(ctx->buf_ptr + 1) & 0xF0 == 0xF0)
+            break;
+        ctx->buf_ptr++;
+    }
+
+    if (ctx->buf_ptr >= ctx->buf_end - 1)
+        return -1;
+
+    frame->data = (uint8_t*)ctx->buf_ptr;
+
+    ret = avpriv_adts_header_parse (&adts_hdr, frame->data, ctx->buf_end - frame->data);
+    if (ret < 0)
+        return ret;
+
+    frame->header_length = adts_hdr->crc_absent ? AV_AAC_ADTS_HEADER_SIZE : AV_AAC_ADTS_HEADER_SIZE + 2;
+    frame->length = adts_hdr->frame_length;
+
+    av_free(adts_hdr);
+
+    return 0;
+}
+
+static int get_next_ac3_eac3_sync_frame(CodecParserContext *ctx, AudioFrame *frame)
+{
+    int ret = 0;
+
+    AC3HeaderInfo *hdr = NULL;
+
+    /* Find next sync word 0x0B77 */
+    while (ctx->buf_ptr < ctx->buf_end - 1) {
+        if (*ctx->buf_ptr == 0x0B && *(ctx->buf_ptr + 1) == 0x77)
+            break;
+        ctx->buf_ptr++;
+    }
+
+    if (ctx->buf_ptr >= ctx->buf_end - 1)
+        return -1;
+
+    frame->data = (uint8_t*)ctx->buf_ptr;
+    frame->header_length = 0;
+
+    ret = avpriv_ac3_parse_header(&hdr, frame->data, ctx->buf_end - frame->data);
+    if (ret < 0) {
+        if (ret != AVERROR(ENOMEM))
+            av_free(hdr);
+        return ret;
+    }
+
+    frame->length = hdr->frame_size;
+
+    av_free(hdr);
+
+    return 0;
+}
+
+static int get_next_sync_frame(enum AVCodecID codec_id, CodecParserContext *ctx, AudioFrame *frame)
+{
+    if (codec_id == AV_CODEC_ID_AAC)
+        return get_next_adts_frame(ctx, frame);
+    else if (codec_id == AV_CODEC_ID_AC3 || codec_id == AV_CODEC_ID_EAC3)
+        return get_next_ac3_eac3_sync_frame(ctx, frame);
+    else
+        return AVERROR_INVALIDDATA;
+}
+
+static int decrypt_sync_frame(enum AVCodecID codec_id, HLSCryptoContext *crypto_ctx, AudioFrame *frame)
+{
+    int ret = 0;
+    uint8_t *data;
+    int num_of_encrypted_blocks;
+
+    ret = av_aes_init(crypto_ctx->aes_ctx, crypto_ctx->key, 16 * 8, 1);
+    if (ret < 0)
+        return ret;
+
+    data = frame->data + frame->header_length + 16;
+
+    num_of_encrypted_blocks = (frame->length - frame->header_length - 16)/16;
+
+    av_aes_crypt(crypto_ctx->aes_ctx, data, data, num_of_encrypted_blocks, crypto_ctx->iv, 1);
+
+    return 0;
+}
+
+static int decrypt_audio_frame(enum AVCodecID codec_id, HLSCryptoContext *crypto_ctx, AVPacket *pkt)
+{
+    int ret = 0;
+    CodecParserContext  ctx;
+    AudioFrame frame;
+
+    memset(&ctx, 0, sizeof(ctx));
+    ctx.buf_ptr 	= pkt->data;
+    ctx.buf_end = pkt->data + pkt->size;
+
+    while (ctx.buf_ptr < ctx.buf_end) {
+        memset(&frame, 0, sizeof(frame));
+        ret = get_next_sync_frame(codec_id, &ctx, &frame);
+        if (ret < 0)
+            return ret;
+        if (frame.length - frame.header_length > 31) {
+            ret = decrypt_sync_frame(codec_id, crypto_ctx, &frame);
+            if (ret < 0)
+                return ret;
+        }
+        ctx.buf_ptr += frame.length;
+    }
+
+    return 0;
+}
+
+int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext *crypto_ctx, AVPacket *pkt)
+{
+    if (codec_id == AV_CODEC_ID_H264)
+        return decrypt_video_frame(crypto_ctx, pkt);
+    else if (codec_id == AV_CODEC_ID_AAC || codec_id == AV_CODEC_ID_AC3 || codec_id == AV_CODEC_ID_EAC3)
+        return decrypt_audio_frame(codec_id, crypto_ctx, pkt);
+
+    return AVERROR_INVALIDDATA;
+}
diff --git a/libavformat/hls_sample_aes.h b/libavformat/hls_sample_aes.h
new file mode 100644
index 0000000000..cf80e41cb0
--- /dev/null
+++ b/libavformat/hls_sample_aes.h
@@ -0,0 +1,66 @@ 
+/*
+ * Apple HTTP Live Streaming Sample Encryption/Decryption
+ *
+ * Copyright (c) 2021 Nachiket Tarate
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Apple HTTP Live Streaming Sample Encryption
+ * https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption
+ */
+
+#ifndef AVFORMAT_HLS_SAMPLE_AES_H
+#define AVFORMAT_HLS_SAMPLE_AES_H
+
+#include <stdint.h>
+
+#include "avformat.h"
+
+#include "libavcodec/avcodec.h"
+#include "libavutil/aes.h"
+
+#define HLS_MAX_ID3_TAGS_DATA_LEN       138
+#define HLS_MAX_AUDIO_SETUP_DATA_LEN    10
+
+
+typedef struct HLSCryptoContext {
+    struct AVAES   *aes_ctx;
+    uint8_t 		key[16];
+    uint8_t 		iv[16];
+} HLSCryptoContext;
+
+typedef struct HLSAudioSetupInfo {
+    enum AVCodecID      codec_id;
+    uint32_t            codec_tag;
+    uint16_t            priming;
+    uint8_t             version;
+    uint8_t             setup_data_length;
+    uint8_t             setup_data[HLS_MAX_AUDIO_SETUP_DATA_LEN];
+} HLSAudioSetupInfo;
+
+
+void ff_hls_read_audio_setup_info(HLSAudioSetupInfo *info, const uint8_t *buf, size_t size);
+
+int ff_hls_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo *info);
+
+int ff_hls_decrypt_frame(enum AVCodecID codec_id, HLSCryptoContext *crypto_ctx, AVPacket *pkt);
+
+#endif /* AVFORMAT_HLS_SAMPLE_AES_H */
+
diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
index e283ec09d7..dc611ae788 100644
--- a/libavformat/mpegts.c
+++ b/libavformat/mpegts.c
@@ -839,6 +839,16 @@  static const StreamType MISC_types[] = {
     { 0 },
 };
 
+/* HLS Sample Encryption Types  */
+static const StreamType HLS_SAMPLE_ENC_types[] = {
+    { 0xdb, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264},
+    { 0xcf, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
+    { 0xc1, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 },
+    { 0xc2, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3},
+    { 0 },
+};
+
+
 static const StreamType REGD_types[] = {
     { MKTAG('d', 'r', 'a', 'c'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC },
     { MKTAG('A', 'C', '-', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3   },
@@ -948,6 +958,8 @@  static int mpegts_set_stream_info(AVStream *st, PESContext *pes,
     }
     if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
         mpegts_find_stream_type(st, pes->stream_type, MISC_types);
+    if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
+        mpegts_find_stream_type(st, pes->stream_type, HLS_SAMPLE_ENC_types);
     if (st->codecpar->codec_id == AV_CODEC_ID_NONE) {
         st->codecpar->codec_id  = old_codec_id;
         st->codecpar->codec_type = old_codec_type;