From patchwork Wed Apr 5 14:04:01 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Liu Steven X-Patchwork-Id: 3301 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.44.195 with SMTP id s186csp206969vss; Wed, 5 Apr 2017 07:04:51 -0700 (PDT) X-Received: by 10.28.103.138 with SMTP id b132mr19297859wmc.79.1491401090959; Wed, 05 Apr 2017 07:04:50 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id f71si8897757wmh.127.2017.04.05.07.04.50; Wed, 05 Apr 2017 07:04:50 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 636DC6882B4; Wed, 5 Apr 2017 17:04:44 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from smtpbg65.qq.com (smtpbg65.qq.com [103.7.28.233]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 561F0688268 for ; Wed, 5 Apr 2017 17:04:37 +0300 (EEST) X-QQ-mid: bizesmtp14t1491401048tkcvpsor Received: from localhost (unknown [221.222.200.46]) by esmtp4.qq.com (ESMTP) with id ; Wed, 05 Apr 2017 22:04:07 +0800 (CST) X-QQ-SSF: 01100000000000F0FF30B00A0000000 X-QQ-FEAT: Ntz8agaBy+EIxz7XSEO1WISlKQ8S3fLTzCNe6MuVkIzQ/yZQS+TVAvHhf4nyi svij/2Cd/tZq7j3U4mBRoSHeC9RF6KTzV1LaQgkYJm25QiW/OwfczmpIE0g6J2PTMWVuwkk DWO0pH4g9WSvGo3zfB44f5vmu1RSb6X+zNA1NHs3aHrVYp6PPxWTkwVz1IHijByxeVYTucQ S5HcvG/Qb3Mb1jAOJ5+8Pu/kG3PNzLwPbzT1eqhBLoFzuQNG1IMkUNBMh7h8vj0THT8p5ze CYn9G5/xHcmIGC X-QQ-GoodBg: 0 From: Steven Liu To: ffmpeg-devel@ffmpeg.org Date: Wed, 5 Apr 2017 22:04:01 +0800 Message-Id: <20170405140401.1829-1-lq@chinaffmpeg.org> X-Mailer: git-send-email 2.11.0 (Apple Git-81) X-QQ-SENDSIZE: 520 X-QQ-Bgrelay: 1 Subject: [FFmpeg-devel] [PATCH] avformat/hlsenc: add hls encrypt options X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Steven Liu MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" add hls encrypt options looks like libav's libavformat/hlsenc.c Signed-off-by: Steven Liu --- libavformat/hlsenc.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 113 insertions(+), 5 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index e6c378df2e..4583d75a98 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -26,10 +26,18 @@ #include #endif +#if CONFIG_GCRYPT +#include +#elif CONFIG_OPENSSL +#include +#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,14 @@ 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) { + 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 +1402,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 +1572,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 +1607,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", "AES128 encryption support", OFFSET(encrypt), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E}, + {"hls_enc_key", "use the specified hex-coded 16byte 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", "use the specified hex-coded 16byte 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"},