diff mbox series

[FFmpeg-devel,2/3] libavformat/hls: add support for SAMPLE-AES decryption in HLS demuxer

Message ID 20210128151152.18154-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 Jan. 28, 2021, 3:11 p.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            | 101 +++++++--
 libavformat/hls_sample_aes.c | 403 +++++++++++++++++++++++++++++++++++
 libavformat/hls_sample_aes.h |  66 ++++++
 libavformat/mpegts.c         |  12 ++
 5 files changed, 568 insertions(+), 16 deletions(-)
 create mode 100644 libavformat/hls_sample_aes.c
 create mode 100644 libavformat/hls_sample_aes.h

Comments

Lynne Jan. 28, 2021, 3:53 p.m. UTC | #1
Jan 28, 2021, 16:11 by nachiket.programmer@gmail.com:

> 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            | 101 +++++++--
>  libavformat/hls_sample_aes.c | 403 +++++++++++++++++++++++++++++++++++
>  libavformat/hls_sample_aes.h |  66 ++++++
>  libavformat/mpegts.c         |  12 ++
>  5 files changed, 568 insertions(+), 16 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 3a8fbcbe5f..c97930d98b 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -237,7 +237,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..850068736e 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,7 +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 +1098,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 +1252,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 +1268,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 +1288,8 @@ 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 = AVERROR(ENOSYS);
> +    } else
> +        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts, &is_http);
>  
>  /* 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 +1958,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);
> @@ -1973,6 +1984,18 @@ static int hls_read_header(AVFormatContext *s)
>  pls->cur_seq_no = highest_cur_seq_no;
>  }
>  
> +        seg = current_segment(pls);
> +
> +        if (seg && seg->key_type == KEY_SAMPLE_AES) {
> +            pls->crypto_ctx.aes_ctx = av_aes_alloc();
> +            if (!pls->crypto_ctx.aes_ctx) {
> +                ret = AVERROR(ENOMEM);
> +                avformat_free_context(pls->ctx);
> +                pls->ctx = NULL;
> +                goto fail;
> +            }
> +        }
> +
>  pls->read_buffer = av_malloc(INITIAL_BUFFER_SIZE);
>  if (!pls->read_buffer){
>  ret = AVERROR(ENOMEM);
> @@ -1980,8 +2003,40 @@ 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
> +         */
> +        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;          /* error was end of file, nothing read */
> +            }
> +        }
> +
> +        /*
> +         * 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 == 1 &&
> +            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 +2054,8 @@ static int hls_read_header(AVFormatContext *s)
>  goto fail;
>  }
>  av_free(url);
> +        }
> +
>  pls->ctx->pb       = &pls->pb;
>  pls->ctx->io_open  = nested_io_open;
>  pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
> @@ -2027,7 +2084,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 +2219,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 +2238,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..e51fa25834
> --- /dev/null
> +++ b/libavformat/hls_sample_aes.c
> @@ -0,0 +1,403 @@
> +/*
> + * 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 is_start_code(const uint8_t *buf, int zeros_in_start_code)
> +{
> +    for (int i = 0; i < zeros_in_start_code; i++)
> +        if(*(buf++) != 0x00)
> +            return 0;
> +
> +    if (*buf != 0x01)
> +        return 0;
> +
> +    return 1;
> +}
> +
> +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 && is_start_code(ctx->buf_ptr, 3))
> +        nalu->start_code_length = 4;
> +    else if (ctx->buf_end - ctx->buf_ptr >= 3 && is_start_code(ctx->buf_ptr, 2))
> +        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 && is_start_code(ctx->buf_ptr, 3))
> +            break;
> +        else if (ctx->buf_end - ctx->buf_ptr >= 3 && is_start_code(ctx->buf_ptr, 2))
> +            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);
> +    }
>

We do not put brackets on single-line statements like
for (int)
    do_thing();
or
while (1)
    do_thing()
or
if (1)
    do_thing()
or
if (1)
    do_thing1()
else
    do_thing2()

But when part of another block which contains more than one line like
if (1) {
    do_thing1();
    do_thing2();
} else {
    do_thing3();
}
we do put brackets on all blocks. We even have a document with the
coding style which says what to do and what not to do.
But even then, the rest of the libavformat/hls.c file follows our style,
so you could have at least looked at the file you were working on to
see this.
James Almer Jan. 28, 2021, 3:59 p.m. UTC | #2
On 1/28/2021 12:53 PM, Lynne wrote:
> Jan 28, 2021, 16:11 by nachiket.programmer@gmail.com:
>> +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);
>> +    }
>>
> 
> We do not put brackets on single-line statements like
> for (int)
>      do_thing();
> or
> while (1)
>      do_thing()
> or
> if (1)
>      do_thing()
> or
> if (1)
>      do_thing1()
> else
>      do_thing2()
> 
> But when part of another block which contains more than one line like
> if (1) {
>      do_thing1();
>      do_thing2();
> } else {
>      do_thing3();
> }
> we do put brackets on all blocks. We even have a document with the
> coding style which says what to do and what not to do.
> But even then, the rest of the libavformat/hls.c file follows our style,
> so you could have at least looked at the file you were working on to
> see this.

Could we focus on a technical review of these patches? Cosmetics are 
last in the list of priorities.

I'm not saying that what you said is not true or that it does not need 
to be fixed, but when you ask for several iterations just to fix a 
single space or a line break, and only then start looking at the 
implementation, which may need even more iterations, it can get frustrating.

So what I'm asking is, is the patch correct from a technical PoV? Are 
the only issues left purely cosmetics? If not, point everything so it 
may all be fixed in a single iteration.
Liu Steven Feb. 1, 2021, 7:18 a.m. UTC | #3
> 2021年1月28日 下午11:11,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
> 
> 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            | 101 +++++++--
> libavformat/hls_sample_aes.c | 403 +++++++++++++++++++++++++++++++++++
> libavformat/hls_sample_aes.h |  66 ++++++
> libavformat/mpegts.c         |  12 ++
> 5 files changed, 568 insertions(+), 16 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 3a8fbcbe5f..c97930d98b 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -237,7 +237,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..850068736e 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,7 +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 +1098,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 +1252,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 +1268,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 +1288,8 @@ 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 = AVERROR(ENOSYS);
> +    } else
> +        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts, &is_http);
> 
>     /* 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 +1958,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);
> @@ -1973,6 +1984,18 @@ static int hls_read_header(AVFormatContext *s)
>             pls->cur_seq_no = highest_cur_seq_no;
>         }
> 
> +        seg = current_segment(pls);
> +
> +        if (seg && seg->key_type == KEY_SAMPLE_AES) {
> +            pls->crypto_ctx.aes_ctx = av_aes_alloc();
> +            if (!pls->crypto_ctx.aes_ctx) {
> +                ret = AVERROR(ENOMEM);
> +                avformat_free_context(pls->ctx);
> +                pls->ctx = NULL;
> +                goto fail;
> +            }
> +        }
> +
>         pls->read_buffer = av_malloc(INITIAL_BUFFER_SIZE);
>         if (!pls->read_buffer){
>             ret = AVERROR(ENOMEM);
> @@ -1980,8 +2003,40 @@ 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
> +         */
> +        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;          /* error was end of file, nothing read */
> +            }
> +        }
> +
> +        /*
> +         * 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 == 1 &&
> +            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 +2054,8 @@ static int hls_read_header(AVFormatContext *s)
>             goto fail;
>         }
>         av_free(url);
> +        }
> +
>         pls->ctx->pb       = &pls->pb;
>         pls->ctx->io_open  = nested_io_open;
>         pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
> @@ -2027,7 +2084,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 +2219,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 +2238,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..e51fa25834
> --- /dev/null
> +++ b/libavformat/hls_sample_aes.c
> @@ -0,0 +1,403 @@
> +/*
> + * 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 is_start_code(const uint8_t *buf, int zeros_in_start_code)
> +{
> +    for (int i = 0; i < zeros_in_start_code; i++)
> +        if(*(buf++) != 0x00)
> +            return 0;
> +
> +    if (*buf != 0x01)
> +        return 0;
> +
> +    return 1;
> +}
> +
> +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 && is_start_code(ctx->buf_ptr, 3))
> +        nalu->start_code_length = 4;
> +    else if (ctx->buf_end - ctx->buf_ptr >= 3 && is_start_code(ctx->buf_ptr, 2))
> +        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 && is_start_code(ctx->buf_ptr, 3))
> +            break;
> +        else if (ctx->buf_end - ctx->buf_ptr >= 3 && is_start_code(ctx->buf_ptr, 2))
> +            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".


Cannot test passed:

echo "0123456789ABCDEF" > keyfile
dd if=keyfile bs=16 count=1 of=inputkey
mediafilesegmenter  --encrypt-key-file=myKey.bin --encrypt-key-url=http://127.0.0.1/keyOnly.bin --stream-encrypt --iso-fragmented bbb_sunflower_native_60fps_normal.mp4


(base) liuqi05:ufbuild liuqi$ ./ffmpeg -i http://127.0.0.1/prog_index.m3u8
ffmpeg version N-100906-gad2cc0e2f4 Copyright (c) 2000-2021 the FFmpeg developers
  built with Apple clang version 12.0.0 (clang-1200.0.32.28)
  configuration: --prefix=/usr/local/ --libdir=/usr/local/lib/ --cc=clang --quiet --enable-htmlpages --enable-libx264 --enable-libxml2 --enable-gpl --extra-ldflags='-I/usr/local/opt/openssl/include/ -O0 -g3 -fsanitize=address -Wno-error -fPIC -I/usr/local/include' --extra-ldflags='-O0 -g3 -fsanitize=address -Wno-error -fPIC -L/usr/local/lib -L/usr/local/opt/openssl/lib' --enable-libfreetype --enable-fontconfig --enable-libspeex --enable-libopus --enable-libzmq --enable-libx265 --enable-libass --enable-videotoolbox --disable-optimizations --enable-audiotoolbox --enable-opengl --disable-stripping --disable-avresample --samples=../../fate-suite/ --enable-encoder=hevc_videotoolbox --enable-hwaccel=hevc_videotoolbox --enable-hwaccel=h264_videotoolbox --enable-openssl --enable-nonfree --disable-shared
  libavutil      56. 64.100 / 56. 64.100
  libavcodec     58.119.100 / 58.119.100
  libavformat    58. 65.101 / 58. 65.101
  libavdevice    58. 11.103 / 58. 11.103
  libavfilter     7.100.100 /  7.100.100
  libswscale      5.  8.100 /  5.  8.100
  libswresample   3.  8.100 /  3.  8.100
  libpostproc    55.  8.100 / 55.  8.100
[hls @ 0x61b000000780] Skip ('#EXT-X-VERSION:7')
[hls @ 0x61b000000780] Skip ('#EXT-X-INDEPENDENT-SEGMENTS')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7064')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:24744')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7641')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12316')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:17624')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5988')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7951')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:6340')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7686')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:6493')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12362')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:4466')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:3121')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:6974')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7953')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10594')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8449')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:9874')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8876')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8079')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:9088')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7332')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:11143')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:11152')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8131')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:11863')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5937')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:14297')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5888')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:13776')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10484')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7888')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:14966')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:9795')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8293')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:14174')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10475')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:14866')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12747')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:6856')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:13871')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:16641')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:18598')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:17133')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7259')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:4410')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7278')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:6319')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8747')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:18026')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:25287')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12894')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8372')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7237')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:24965')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:14493')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7249')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5254')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5074')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7405')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10608')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:9797')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10087')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10005')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7479')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12707')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12961')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:9036')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:15359')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:17265')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12945')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:4211')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:801')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:3434')
[hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5116')
[hls @ 0x61b000000780] Opening 'http://127.0.0.1/keyOnly.bin' for reading
[hls @ 0x61b000000780] Opening 'http://127.0.0.1/fileSequence0.mp4' for reading
[hls @ 0x61b000000780] Opening 'http://127.0.0.1/fileSequence1.m4s' for reading

Focus here:

[mov,mp4,m4a,3gp,3g2,mj2 @ 0x61b000000e80] Incorrect number of samples in encryption info
[hls @ 0x61b000000780] Could not find codec parameters for stream 0 (Video: h264 (avc1 / 0x31637661), none(progressive), 4000x2250, 245 kb/s): unspecified pixel format
Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options



Input #0, hls, from 'http://127.0.0.1/prog_index.m3u8':
  Duration: 00:10:34.53, bitrate: 0 kb/s
  Program 0
    Metadata:
      variant_bitrate : 0
    Stream #0:0: Video: h264 (avc1 / 0x31637661), none(progressive), 4000x2250, 245 kb/s, 60k tbr, 60k tbn, 120k tbc (default)
    Metadata:
      variant_bitrate : 0
      major_brand     : iso5
      minor_version   : 1
      compatible_brands: isomiso5hlsf
      creation_time   : 2021-02-01T07:10:21.000000Z
    Stream #0:1: Audio: ac3 (ac-3 / 0x332D6361), 48000 Hz, 5.1(side), fltp, 94 kb/s (default)
    Metadata:
      variant_bitrate : 0
    Side data:
      audio service type: main
At least one output file must be specified
(base) liuqi05:ufbuild liuqi$




Thanks

Steven Liu
Liu Steven Feb. 11, 2021, 9:50 a.m. UTC | #4
> 在 2021年2月3日,09:11,Steven Liu <lq@chinaffmpeg.org> 写道:
> 
> 
> 
>> 2021年2月2日 下午10:51,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
>> 
>> Hello Steven,
>> 
>> Was this test passing earlier (without my patch) ?
> Hi  Nachiket,
> no, and it to support sample aes without your patch
>> 
>> Because SAMPLE-AES encryption method is for MPEG-TS format and not for Fragmented MP4. So, my patch should not affect anything related to Fragmented MP4.
> I think sample ads should support not only mpegts, but also support fmp4 too.
> Reference rfc 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.
> 
>> 
>> Can you please confirm ?
>> 
>> Best Regards,
>> Nachiket Tarate
>> 
>> On Mon, Feb 1, 2021 at 12:48 PM Steven Liu <lq@chinaffmpeg.org> wrote:
>> 
>> 
>>> 2021年1月28日 下午11:11,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
>>> 
>>> 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            | 101 +++++++--
>>> libavformat/hls_sample_aes.c | 403 +++++++++++++++++++++++++++++++++++
>>> libavformat/hls_sample_aes.h |  66 ++++++
>>> libavformat/mpegts.c         |  12 ++
>>> 5 files changed, 568 insertions(+), 16 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 3a8fbcbe5f..c97930d98b 100644
>>> --- a/libavformat/Makefile
>>> +++ b/libavformat/Makefile
>>> @@ -237,7 +237,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..850068736e 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,7 +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 +1098,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 +1252,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 +1268,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 +1288,8 @@ 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 = AVERROR(ENOSYS);
>>> +    } else
>>> +        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts, &is_http);
>>> 
>>>    /* 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 +1958,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);
>>> @@ -1973,6 +1984,18 @@ static int hls_read_header(AVFormatContext *s)
>>>            pls->cur_seq_no = highest_cur_seq_no;
>>>        }
>>> 
>>> +        seg = current_segment(pls);
>>> +
>>> +        if (seg && seg->key_type == KEY_SAMPLE_AES) {
>>> +            pls->crypto_ctx.aes_ctx = av_aes_alloc();
>>> +            if (!pls->crypto_ctx.aes_ctx) {
>>> +                ret = AVERROR(ENOMEM);
>>> +                avformat_free_context(pls->ctx);
>>> +                pls->ctx = NULL;
>>> +                goto fail;
>>> +            }
>>> +        }
>>> +
>>>        pls->read_buffer = av_malloc(INITIAL_BUFFER_SIZE);
>>>        if (!pls->read_buffer){
>>>            ret = AVERROR(ENOMEM);
>>> @@ -1980,8 +2003,40 @@ 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
>>> +         */
>>> +        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;          /* error was end of file, nothing read */
>>> +            }
>>> +        }
>>> +
>>> +        /*
>>> +         * 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 == 1 &&
>>> +            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 +2054,8 @@ static int hls_read_header(AVFormatContext *s)
>>>            goto fail;
>>>        }
>>>        av_free(url);
>>> +        }
>>> +
>>>        pls->ctx->pb       = &pls->pb;
>>>        pls->ctx->io_open  = nested_io_open;
>>>        pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
>>> @@ -2027,7 +2084,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 +2219,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 +2238,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..e51fa25834
>>> --- /dev/null
>>> +++ b/libavformat/hls_sample_aes.c
>>> @@ -0,0 +1,403 @@
>>> +/*
>>> + * 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 is_start_code(const uint8_t *buf, int zeros_in_start_code)
>>> +{
>>> +    for (int i = 0; i < zeros_in_start_code; i++)
>>> +        if(*(buf++) != 0x00)
>>> +            return 0;
>>> +
>>> +    if (*buf != 0x01)
>>> +        return 0;
>>> +
>>> +    return 1;
>>> +}
>>> +
>>> +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 && is_start_code(ctx->buf_ptr, 3))
>>> +        nalu->start_code_length = 4;
>>> +    else if (ctx->buf_end - ctx->buf_ptr >= 3 && is_start_code(ctx->buf_ptr, 2))
>>> +        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 && is_start_code(ctx->buf_ptr, 3))
>>> +            break;
>>> +        else if (ctx->buf_end - ctx->buf_ptr >= 3 && is_start_code(ctx->buf_ptr, 2))
>>> +            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".
>> 
>> 
>> Cannot test passed:
>> 
>> echo "0123456789ABCDEF" > keyfile
>> dd if=keyfile bs=16 count=1 of=inputkey
>> mediafilesegmenter  --encrypt-key-file=myKey.bin --encrypt-key-url=http://127.0.0.1/keyOnly.bin --stream-encrypt --iso-fragmented bbb_sunflower_native_60fps_normal.mp4
>> 
>> 
>> (base) liuqi05:ufbuild liuqi$ ./ffmpeg -i http://127.0.0.1/prog_index.m3u8
>> ffmpeg version N-100906-gad2cc0e2f4 Copyright (c) 2000-2021 the FFmpeg developers
>>  built with Apple clang version 12.0.0 (clang-1200.0.32.28)
>>  configuration: --prefix=/usr/local/ --libdir=/usr/local/lib/ --cc=clang --quiet --enable-htmlpages --enable-libx264 --enable-libxml2 --enable-gpl --extra-ldflags='-I/usr/local/opt/openssl/include/ -O0 -g3 -fsanitize=address -Wno-error -fPIC -I/usr/local/include' --extra-ldflags='-O0 -g3 -fsanitize=address -Wno-error -fPIC -L/usr/local/lib -L/usr/local/opt/openssl/lib' --enable-libfreetype --enable-fontconfig --enable-libspeex --enable-libopus --enable-libzmq --enable-libx265 --enable-libass --enable-videotoolbox --disable-optimizations --enable-audiotoolbox --enable-opengl --disable-stripping --disable-avresample --samples=../../fate-suite/ --enable-encoder=hevc_videotoolbox --enable-hwaccel=hevc_videotoolbox --enable-hwaccel=h264_videotoolbox --enable-openssl --enable-nonfree --disable-shared
>>  libavutil      56. 64.100 / 56. 64.100
>>  libavcodec     58.119.100 / 58.119.100
>>  libavformat    58. 65.101 / 58. 65.101
>>  libavdevice    58. 11.103 / 58. 11.103
>>  libavfilter     7.100.100 /  7.100.100
>>  libswscale      5.  8.100 /  5.  8.100
>>  libswresample   3.  8.100 /  3.  8.100
>>  libpostproc    55.  8.100 / 55.  8.100
>> [hls @ 0x61b000000780] Skip ('#EXT-X-VERSION:7')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-INDEPENDENT-SEGMENTS')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7064')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:24744')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7641')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12316')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:17624')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5988')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7951')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:6340')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7686')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:6493')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12362')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:4466')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:3121')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:6974')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7953')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10594')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8449')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:9874')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8876')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8079')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:9088')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7332')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:11143')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:11152')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8131')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:11863')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5937')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:14297')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5888')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:13776')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10484')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7888')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:14966')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:9795')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8293')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:14174')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10475')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:14866')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12747')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:6856')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:13871')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:16641')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:18598')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:17133')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7259')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:4410')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7278')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:6319')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8747')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:18026')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:25287')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12894')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8372')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7237')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:24965')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:14493')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7249')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5254')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5074')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7405')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10608')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:9797')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10087')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10005')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7479')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12707')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12961')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:9036')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:15359')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:17265')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12945')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:4211')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:801')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:3434')
>> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5116')
>> [hls @ 0x61b000000780] Opening 'http://127.0.0.1/keyOnly.bin' for reading
>> [hls @ 0x61b000000780] Opening 'http://127.0.0.1/fileSequence0.mp4' for reading
>> [hls @ 0x61b000000780] Opening 'http://127.0.0.1/fileSequence1.m4s' for reading
>> 
>> Focus here:
>> 
>> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x61b000000e80] Incorrect number of samples in encryption info
>> [hls @ 0x61b000000780] Could not find codec parameters for stream 0 (Video: h264 (avc1 / 0x31637661), none(progressive), 4000x2250, 245 kb/s): unspecified pixel format
>> Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options
>> 
>> 
>> 
>> Input #0, hls, from 'http://127.0.0.1/prog_index.m3u8':
>>  Duration: 00:10:34.53, bitrate: 0 kb/s
>>  Program 0
>>    Metadata:
>>      variant_bitrate : 0
>>    Stream #0:0: Video: h264 (avc1 / 0x31637661), none(progressive), 4000x2250, 245 kb/s, 60k tbr, 60k tbn, 120k tbc (default)
>>    Metadata:
>>      variant_bitrate : 0
>>      major_brand     : iso5
>>      minor_version   : 1
>>      compatible_brands: isomiso5hlsf
>>      creation_time   : 2021-02-01T07:10:21.000000Z
>>    Stream #0:1: Audio: ac3 (ac-3 / 0x332D6361), 48000 Hz, 5.1(side), fltp, 94 kb/s (default)
>>    Metadata:
>>      variant_bitrate : 0
>>    Side data:
>>      audio service type: main
>> At least one output file must be specified
>> (base) liuqi05:ufbuild liuqi$
>> 
>> 
>> 
>> 
>> 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

Thanks
Steven
Nachiket Tarate Feb. 23, 2021, 9:30 a.m. UTC | #5
Hello Steven,

This patch enables SAMPLE-AES decryption for MPEG-TS format only and not
for fragmented MP4 format.

In the next version of the patch, I will add a check for it.

Best Regards,
Nachiket Tarate

On Mon, Feb 1, 2021 at 12:48 PM Steven Liu <lq@chinaffmpeg.org> wrote:

>
>
> > 2021年1月28日 下午11:11,Nachiket Tarate <nachiket.programmer@gmail.com> 写道:
> >
> > 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            | 101 +++++++--
> > libavformat/hls_sample_aes.c | 403 +++++++++++++++++++++++++++++++++++
> > libavformat/hls_sample_aes.h |  66 ++++++
> > libavformat/mpegts.c         |  12 ++
> > 5 files changed, 568 insertions(+), 16 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 3a8fbcbe5f..c97930d98b 100644
> > --- a/libavformat/Makefile
> > +++ b/libavformat/Makefile
> > @@ -237,7 +237,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..850068736e 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,7 +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 +1098,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 +1252,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 +1268,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 +1288,8 @@ 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 = AVERROR(ENOSYS);
> > +    } else
> > +        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts,
> &is_http);
> >
> >     /* 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 +1958,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);
> > @@ -1973,6 +1984,18 @@ static int hls_read_header(AVFormatContext *s)
> >             pls->cur_seq_no = highest_cur_seq_no;
> >         }
> >
> > +        seg = current_segment(pls);
> > +
> > +        if (seg && seg->key_type == KEY_SAMPLE_AES) {
> > +            pls->crypto_ctx.aes_ctx = av_aes_alloc();
> > +            if (!pls->crypto_ctx.aes_ctx) {
> > +                ret = AVERROR(ENOMEM);
> > +                avformat_free_context(pls->ctx);
> > +                pls->ctx = NULL;
> > +                goto fail;
> > +            }
> > +        }
> > +
> >         pls->read_buffer = av_malloc(INITIAL_BUFFER_SIZE);
> >         if (!pls->read_buffer){
> >             ret = AVERROR(ENOMEM);
> > @@ -1980,8 +2003,40 @@ 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
> > +         */
> > +        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;          /* error was end of file, nothing
> read */
> > +            }
> > +        }
> > +
> > +        /*
> > +         * 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 == 1 &&
> > +            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 +2054,8 @@ static int hls_read_header(AVFormatContext *s)
> >             goto fail;
> >         }
> >         av_free(url);
> > +        }
> > +
> >         pls->ctx->pb       = &pls->pb;
> >         pls->ctx->io_open  = nested_io_open;
> >         pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
> > @@ -2027,7 +2084,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 +2219,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 +2238,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..e51fa25834
> > --- /dev/null
> > +++ b/libavformat/hls_sample_aes.c
> > @@ -0,0 +1,403 @@
> > +/*
> > + * 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 is_start_code(const uint8_t *buf, int zeros_in_start_code)
> > +{
> > +    for (int i = 0; i < zeros_in_start_code; i++)
> > +        if(*(buf++) != 0x00)
> > +            return 0;
> > +
> > +    if (*buf != 0x01)
> > +        return 0;
> > +
> > +    return 1;
> > +}
> > +
> > +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 && is_start_code(ctx->buf_ptr,
> 3))
> > +        nalu->start_code_length = 4;
> > +    else if (ctx->buf_end - ctx->buf_ptr >= 3 &&
> is_start_code(ctx->buf_ptr, 2))
> > +        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 &&
> is_start_code(ctx->buf_ptr, 3))
> > +            break;
> > +        else if (ctx->buf_end - ctx->buf_ptr >= 3 &&
> is_start_code(ctx->buf_ptr, 2))
> > +            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".
>
>
> Cannot test passed:
>
> echo "0123456789ABCDEF" > keyfile
> dd if=keyfile bs=16 count=1 of=inputkey
> mediafilesegmenter  --encrypt-key-file=myKey.bin --encrypt-key-url=
> http://127.0.0.1/keyOnly.bin --stream-encrypt --iso-fragmented
> bbb_sunflower_native_60fps_normal.mp4
>
>
> (base) liuqi05:ufbuild liuqi$ ./ffmpeg -i http://127.0.0.1/prog_index.m3u8
> ffmpeg version N-100906-gad2cc0e2f4 Copyright (c) 2000-2021 the FFmpeg
> developers
>   built with Apple clang version 12.0.0 (clang-1200.0.32.28)
>   configuration: --prefix=/usr/local/ --libdir=/usr/local/lib/ --cc=clang
> --quiet --enable-htmlpages --enable-libx264 --enable-libxml2 --enable-gpl
> --extra-ldflags='-I/usr/local/opt/openssl/include/ -O0 -g3
> -fsanitize=address -Wno-error -fPIC -I/usr/local/include'
> --extra-ldflags='-O0 -g3 -fsanitize=address -Wno-error -fPIC
> -L/usr/local/lib -L/usr/local/opt/openssl/lib' --enable-libfreetype
> --enable-fontconfig --enable-libspeex --enable-libopus --enable-libzmq
> --enable-libx265 --enable-libass --enable-videotoolbox
> --disable-optimizations --enable-audiotoolbox --enable-opengl
> --disable-stripping --disable-avresample --samples=../../fate-suite/
> --enable-encoder=hevc_videotoolbox --enable-hwaccel=hevc_videotoolbox
> --enable-hwaccel=h264_videotoolbox --enable-openssl --enable-nonfree
> --disable-shared
>   libavutil      56. 64.100 / 56. 64.100
>   libavcodec     58.119.100 / 58.119.100
>   libavformat    58. 65.101 / 58. 65.101
>   libavdevice    58. 11.103 / 58. 11.103
>   libavfilter     7.100.100 /  7.100.100
>   libswscale      5.  8.100 /  5.  8.100
>   libswresample   3.  8.100 /  3.  8.100
>   libpostproc    55.  8.100 / 55.  8.100
> [hls @ 0x61b000000780] Skip ('#EXT-X-VERSION:7')
> [hls @ 0x61b000000780] Skip ('#EXT-X-INDEPENDENT-SEGMENTS')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7064')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:24744')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7641')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12316')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:17624')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5988')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7951')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:6340')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7686')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:6493')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12362')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:4466')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:3121')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:6974')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7953')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10594')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8449')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:9874')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8876')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8079')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:9088')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7332')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:11143')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:11152')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8131')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:11863')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5937')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:14297')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5888')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:13776')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10484')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7888')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:14966')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:9795')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8293')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:14174')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10475')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:14866')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12747')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:6856')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:13871')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:16641')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:18598')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:17133')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7259')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:4410')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7278')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:6319')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8747')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:18026')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:25287')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12894')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:8372')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7237')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:24965')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:14493')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7249')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5254')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5074')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7405')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10608')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:9797')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10087')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:10005')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:7479')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12707')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12961')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:9036')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:15359')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:17265')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:12945')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:4211')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:801')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:3434')
> [hls @ 0x61b000000780] Skip ('#EXT-X-BITRATE:5116')
> [hls @ 0x61b000000780] Opening 'http://127.0.0.1/keyOnly.bin' for reading
> [hls @ 0x61b000000780] Opening 'http://127.0.0.1/fileSequence0.mp4' for
> reading
> [hls @ 0x61b000000780] Opening 'http://127.0.0.1/fileSequence1.m4s' for
> reading
>
> Focus here:
>
> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x61b000000e80] Incorrect number of samples in
> encryption info
> [hls @ 0x61b000000780] Could not find codec parameters for stream 0
> (Video: h264 (avc1 / 0x31637661), none(progressive), 4000x2250, 245 kb/s):
> unspecified pixel format
> Consider increasing the value for the 'analyzeduration' (0) and
> 'probesize' (5000000) options
>
>
>
> Input #0, hls, from 'http://127.0.0.1/prog_index.m3u8':
>   Duration: 00:10:34.53, bitrate: 0 kb/s
>   Program 0
>     Metadata:
>       variant_bitrate : 0
>     Stream #0:0: Video: h264 (avc1 / 0x31637661), none(progressive),
> 4000x2250, 245 kb/s, 60k tbr, 60k tbn, 120k tbc (default)
>     Metadata:
>       variant_bitrate : 0
>       major_brand     : iso5
>       minor_version   : 1
>       compatible_brands: isomiso5hlsf
>       creation_time   : 2021-02-01T07:10:21.000000Z
>     Stream #0:1: Audio: ac3 (ac-3 / 0x332D6361), 48000 Hz, 5.1(side),
> fltp, 94 kb/s (default)
>     Metadata:
>       variant_bitrate : 0
>     Side data:
>       audio service type: main
> At least one output file must be specified
> (base) liuqi05:ufbuild liuqi$
>
>
>
>
> 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".
diff mbox series

Patch

diff --git a/libavformat/Makefile b/libavformat/Makefile
index 3a8fbcbe5f..c97930d98b 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -237,7 +237,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..850068736e 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,7 +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 +1098,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 +1252,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 +1268,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 +1288,8 @@  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 = AVERROR(ENOSYS);
+    } else
+        ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts, &is_http);
 
     /* 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 +1958,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);
@@ -1973,6 +1984,18 @@  static int hls_read_header(AVFormatContext *s)
             pls->cur_seq_no = highest_cur_seq_no;
         }
 
+        seg = current_segment(pls);
+
+        if (seg && seg->key_type == KEY_SAMPLE_AES) {
+            pls->crypto_ctx.aes_ctx = av_aes_alloc();
+            if (!pls->crypto_ctx.aes_ctx) {
+                ret = AVERROR(ENOMEM);
+                avformat_free_context(pls->ctx);
+                pls->ctx = NULL;
+                goto fail;
+            }
+        }
+
         pls->read_buffer = av_malloc(INITIAL_BUFFER_SIZE);
         if (!pls->read_buffer){
             ret = AVERROR(ENOMEM);
@@ -1980,8 +2003,40 @@  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
+         */
+        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;          /* error was end of file, nothing read */
+            }
+        }
+
+        /*
+         * 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 == 1 &&
+            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 +2054,8 @@  static int hls_read_header(AVFormatContext *s)
             goto fail;
         }
         av_free(url);
+        }
+
         pls->ctx->pb       = &pls->pb;
         pls->ctx->io_open  = nested_io_open;
         pls->ctx->flags   |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
@@ -2027,7 +2084,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 +2219,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 +2238,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..e51fa25834
--- /dev/null
+++ b/libavformat/hls_sample_aes.c
@@ -0,0 +1,403 @@ 
+/*
+ * 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 is_start_code(const uint8_t *buf, int zeros_in_start_code)
+{
+    for (int i = 0; i < zeros_in_start_code; i++)
+        if(*(buf++) != 0x00)
+            return 0;
+
+    if (*buf != 0x01)
+        return 0;
+
+    return 1;
+}
+
+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 && is_start_code(ctx->buf_ptr, 3))
+        nalu->start_code_length = 4;
+    else if (ctx->buf_end - ctx->buf_ptr >= 3 && is_start_code(ctx->buf_ptr, 2))
+        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 && is_start_code(ctx->buf_ptr, 3))
+            break;
+        else if (ctx->buf_end - ctx->buf_ptr >= 3 && is_start_code(ctx->buf_ptr, 2))
+            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;