[FFmpeg-devel] [PATCH v2] avformat/hlsenc: add hls encrypt options

Steven Liu lingjiujianke at gmail.com
Fri Apr 14 18:37:15 EEST 2017


2017-04-06 23:02 GMT+08:00 Steven Liu <lq at chinaffmpeg.org>:

> refer to:
> https://git.libav.org/?p=libav.git;a=commitdiff;h=
> 0a4b9d0ccd10b3c39105f99bd320f696f69a75a2
> add hls encrypt options looks like libav's libavformat/hlsenc.c
>
> Signed-off-by: Steven Liu <lq at chinaffmpeg.org>
> ---
>  doc/muxers.texi      |  16 +++++++
>  libavformat/hlsenc.c | 122 ++++++++++++++++++++++++++++++
> ++++++++++++++++++---
>  2 files changed, 133 insertions(+), 5 deletions(-)
>
> diff --git a/doc/muxers.texi b/doc/muxers.texi
> index 166c929369..db1c3db999 100644
> --- a/doc/muxers.texi
> +++ b/doc/muxers.texi
> @@ -597,6 +597,22 @@ ffmpeg -f lavfi -re -i testsrc -c:v h264 -hls_flags
> delete_segments \
>    -hls_key_info_file file.keyinfo out.m3u8
>  @end example
>
> + at item -hls_enc @var{enc}
> +Enable (1) or disable (0) the AES128 encryption.
> +When enabled every segment generated is encrypted and the encryption key
> +is saved as @var{playlist name}.key.
> +
> + at item -hls_enc_key @var{key}
> +Hex-coded 16byte key to encrypt the segments, by default it
> +is randomly generated.
> +
> + at item -hls_enc_key_url @var{keyurl}
> +If set, @var{keyurl} is prepended instead of @var{baseurl} to the key
> filename
> +in the playlist.
> +
> + at item -hls_enc_iv @var{iv}
> +Hex-coded 16byte initialization vector for every segment instead
> +of the autogenerated ones.
>
>  @item hls_flags @var{flags}
>  Possible values:
> diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
> index e6c378df2e..f359bb9036 100644
> --- a/libavformat/hlsenc.c
> +++ b/libavformat/hlsenc.c
> @@ -26,10 +26,18 @@
>  #include <unistd.h>
>  #endif
>
> +#if CONFIG_GCRYPT
> +#include <gcrypt.h>
> +#elif CONFIG_OPENSSL
> +#include <openssl/rand.h>
> +#endif
> +
>  #include "libavutil/avassert.h"
>  #include "libavutil/mathematics.h"
>  #include "libavutil/parseutils.h"
>  #include "libavutil/avstring.h"
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/random_seed.h"
>  #include "libavutil/opt.h"
>  #include "libavutil/log.h"
>  #include "libavutil/time_internal.h"
> @@ -139,6 +147,12 @@ typedef struct HLSContext {
>      char *subtitle_filename;
>      AVDictionary *format_options;
>
> +    int encrypt;
> +    char *key;
> +    char *key_url;
> +    char *iv;
> +    char *key_basename;
> +
>      char *key_info_file;
>      char key_file[LINE_BUFFER_SIZE + 1];
>      char key_uri[LINE_BUFFER_SIZE + 1];
> @@ -349,6 +363,89 @@ fail:
>      return ret;
>  }
>
> +static int randomize(uint8_t *buf, int len)
> +{
> +#if CONFIG_GCRYPT
> +    gcry_randomize(buf, len, GCRY_VERY_STRONG_RANDOM);
> +    return 0;
> +#elif CONFIG_OPENSSL
> +    if (RAND_bytes(buf, len))
> +        return 0;
> +#else
> +    return AVERROR(ENOSYS);
> +#endif
> +    return AVERROR(EINVAL);
> +}
> +
> +static int do_encrypt(AVFormatContext *s)
> +{
> +    HLSContext *hls = s->priv_data;
> +    int ret;
> +    int len;
> +    AVIOContext *pb;
> +    uint8_t key[KEYSIZE];
> +
> +    len = strlen(hls->basename) + 4 + 1;
> +    hls->key_basename = av_mallocz(len);
> +    if (!hls->key_basename)
> +        return AVERROR(ENOMEM);
> +
> +    av_strlcpy(hls->key_basename, s->filename, len);
> +    av_strlcat(hls->key_basename, ".key", len);
> +
> +    if (hls->key_url) {
> +        strncpy(hls->key_file, hls->key_url, sizeof(hls->key_file));
> +        strncpy(hls->key_uri, hls->key_url, sizeof(hls->key_uri));
> +    } else {
> +        strncpy(hls->key_file, hls->key_basename, sizeof(hls->key_file));
> +        strncpy(hls->key_uri, hls->key_basename, sizeof(hls->key_uri));
> +    }
> +
> +    if (!*hls->iv_string) {
> +        uint8_t iv[16] = { 0 };
> +        char buf[33];
> +
> +        if (!hls->iv) {
> +            AV_WB64(iv + 8, hls->sequence);
> +        } else {
> +            memcpy(iv, hls->iv, sizeof(iv));
> +        }
> +        ff_data_to_hex(buf, iv, sizeof(iv), 0);
> +        buf[32] = '\0';
> +        memcpy(hls->iv_string, buf, sizeof(hls->iv_string));
> +    }
> +
> +    if (!*hls->key_uri) {
> +        av_log(hls, AV_LOG_ERROR, "no key URI specified in key info
> file\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (!*hls->key_file) {
> +        av_log(hls, AV_LOG_ERROR, "no key file specified in key info
> file\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (!*hls->key_string) {
> +        if (!hls->key) {
> +            if ((ret = randomize(key, sizeof(key))) < 0) {
> +                av_log(s, AV_LOG_ERROR, "Cannot generate a strong random
> key\n");
> +                return ret;
> +            }
> +        } else {
> +            memcpy(key, hls->key, sizeof(key));
> +        }
> +
> +        ff_data_to_hex(hls->key_string, key, sizeof(key), 0);
> +        if ((ret = s->io_open(s, &pb, hls->key_file, AVIO_FLAG_WRITE,
> NULL)) < 0)
> +            return ret;
> +        avio_seek(pb, 0, SEEK_CUR);
> +        avio_write(pb, key, KEYSIZE);
> +        avio_close(pb);
> +    }
> +    return 0;
> +}
> +
> +
>  static int hls_encryption_start(AVFormatContext *s)
>  {
>      HLSContext *hls = s->priv_data;
> @@ -662,7 +759,7 @@ static int hls_append_segment(struct AVFormatContext
> *s, HLSContext *hls, double
>          hls->discontinuity = 0;
>      }
>
> -    if (hls->key_info_file) {
> +    if (hls->key_info_file || hls->encrypt) {
>          av_strlcpy(en->key_uri, hls->key_uri, sizeof(en->key_uri));
>          av_strlcpy(en->iv_string, hls->iv_string, sizeof(en->iv_string));
>      }
> @@ -865,7 +962,7 @@ static int hls_window(AVFormatContext *s, int last)
>          hls->discontinuity_set = 1;
>      }
>      for (en = hls->segments; en; en = en->next) {
> -        if (hls->key_info_file && (!key_uri || strcmp(en->key_uri,
> key_uri) ||
> +        if ((hls->encrypt || hls->key_info_file) && (!key_uri ||
> strcmp(en->key_uri, key_uri) ||
>                                      av_strcasecmp(en->iv_string,
> iv_string))) {
>              avio_printf(out, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"",
> en->key_uri);
>              if (*en->iv_string)
> @@ -1031,9 +1128,18 @@ static int hls_start(AVFormatContext *s)
>          av_strlcat(oc->filename, ".tmp", sizeof(oc->filename));
>      }
>
> -    if (c->key_info_file) {
> -        if ((err = hls_encryption_start(s)) < 0)
> -            goto fail;
> +    if (c->key_info_file || c->encrypt) {
> +        if (c->key_info_file && c->encrypt) {
> +            av_log(s, AV_LOG_WARNING, "Cannot use both -hls_key_info_file
> and -hls_enc,"
> +                  " will use -hls_key_info_file priority\n");
> +        }
> +        if (c->key_info_file) {
> +            if ((err = hls_encryption_start(s)) < 0)
> +                goto fail;
> +        } else {
> +            if ((err = do_encrypt(s)) < 0)
> +                goto fail;
> +        }
>          if ((err = av_dict_set(&options, "encryption_key", c->key_string,
> 0))
>                  < 0)
>              goto fail;
> @@ -1300,6 +1406,7 @@ fail:
>      if (ret < 0) {
>          av_freep(&hls->basename);
>          av_freep(&hls->vtt_basename);
> +        av_freep(&hls->key_basename);
>          if (hls->avf)
>              avformat_free_context(hls->avf);
>          if (hls->vtt_avf)
> @@ -1469,6 +1576,7 @@ static int hls_write_trailer(struct AVFormatContext
> *s)
>          ff_format_io_close(s, &vtt_oc->pb);
>      }
>      av_freep(&hls->basename);
> +    av_freep(&hls->key_basename);
>      avformat_free_context(oc);
>
>      hls->avf = NULL;
> @@ -1503,6 +1611,10 @@ static const AVOption options[] = {
>      {"hls_segment_filename", "filename template for segment files",
> OFFSET(segment_filename),   AV_OPT_TYPE_STRING, {.str = NULL},
> 0,       0,         E},
>      {"hls_segment_size", "maximum size per segment file, (in bytes)",
> OFFSET(max_seg_size),    AV_OPT_TYPE_INT,    {.i64 = 0},               0,
>      INT_MAX,   E},
>      {"hls_key_info_file",    "file with key URI and key file path",
> OFFSET(key_info_file),      AV_OPT_TYPE_STRING, {.str = NULL},
> 0,       0,         E},
> +    {"hls_enc",    "enable AES128 encryption support", OFFSET(encrypt),
>     AV_OPT_TYPE_BOOL, {.i64 = 0},            0,       1,         E},
> +    {"hls_enc_key",    "hex-coded 16 byte key to encrypt the segments",
> OFFSET(key),      AV_OPT_TYPE_STRING, .flags = E},
> +    {"hls_enc_key_url",    "url to access the key to decrypt the
> segments", OFFSET(key_url),      AV_OPT_TYPE_STRING, {.str = NULL},
>     0,       0,         E},
> +    {"hls_enc_iv",    "hex-coded 16 byte initialization vector",
> OFFSET(iv),      AV_OPT_TYPE_STRING, .flags = E},
>      {"hls_subtitle_path",     "set path of hls subtitles",
> OFFSET(subtitle_filename), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,    E},
>      {"hls_flags",     "set flags affecting HLS playlist and media file
> generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E,
> "flags"},
>      {"single_file",   "generate a single media file indexed with byte
> ranges", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SINGLE_FILE }, 0, UINT_MAX,   E,
> "flags"},
> --
> 2.11.0 (Apple Git-81)
>
>
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>

pushed


Thanks


More information about the ffmpeg-devel mailing list