Browse code

avformat/hlsenc: add hls encrypt options

refer to:
https://git.libav.org/?p=libav.git;a=commitdiff;h=0a4b9d0ccd10b3c39105f99bd320f696f69a75a2
add hls encrypt options looks like libav's libavformat/hlsenc.c

Reviewed-by: Moritz Barsnick <barsnick@gmx.net>
Signed-off-by: Steven Liu <lq@chinaffmpeg.org>

Steven Liu authored on 2017/04/15 00:30:29
Showing 2 changed files
... ...
@@ -597,6 +597,22 @@ ffmpeg -f lavfi -re -i testsrc -c:v h264 -hls_flags delete_segments \
597 597
   -hls_key_info_file file.keyinfo out.m3u8
598 598
 @end example
599 599
 
600
+@item -hls_enc @var{enc}
601
+Enable (1) or disable (0) the AES128 encryption.
602
+When enabled every segment generated is encrypted and the encryption key
603
+is saved as @var{playlist name}.key.
604
+
605
+@item -hls_enc_key @var{key}
606
+Hex-coded 16byte key to encrypt the segments, by default it
607
+is randomly generated.
608
+
609
+@item -hls_enc_key_url @var{keyurl}
610
+If set, @var{keyurl} is prepended instead of @var{baseurl} to the key filename
611
+in the playlist.
612
+
613
+@item -hls_enc_iv @var{iv}
614
+Hex-coded 16byte initialization vector for every segment instead
615
+of the autogenerated ones.
600 616
 
601 617
 @item hls_flags @var{flags}
602 618
 Possible values:
... ...
@@ -26,10 +26,18 @@
26 26
 #include <unistd.h>
27 27
 #endif
28 28
 
29
+#if CONFIG_GCRYPT
30
+#include <gcrypt.h>
31
+#elif CONFIG_OPENSSL
32
+#include <openssl/rand.h>
33
+#endif
34
+
29 35
 #include "libavutil/avassert.h"
30 36
 #include "libavutil/mathematics.h"
31 37
 #include "libavutil/parseutils.h"
32 38
 #include "libavutil/avstring.h"
39
+#include "libavutil/intreadwrite.h"
40
+#include "libavutil/random_seed.h"
33 41
 #include "libavutil/opt.h"
34 42
 #include "libavutil/log.h"
35 43
 #include "libavutil/time_internal.h"
... ...
@@ -139,6 +147,12 @@ typedef struct HLSContext {
139 139
     char *subtitle_filename;
140 140
     AVDictionary *format_options;
141 141
 
142
+    int encrypt;
143
+    char *key;
144
+    char *key_url;
145
+    char *iv;
146
+    char *key_basename;
147
+
142 148
     char *key_info_file;
143 149
     char key_file[LINE_BUFFER_SIZE + 1];
144 150
     char key_uri[LINE_BUFFER_SIZE + 1];
... ...
@@ -349,6 +363,89 @@ fail:
349 349
     return ret;
350 350
 }
351 351
 
352
+static int randomize(uint8_t *buf, int len)
353
+{
354
+#if CONFIG_GCRYPT
355
+    gcry_randomize(buf, len, GCRY_VERY_STRONG_RANDOM);
356
+    return 0;
357
+#elif CONFIG_OPENSSL
358
+    if (RAND_bytes(buf, len))
359
+        return 0;
360
+#else
361
+    return AVERROR(ENOSYS);
362
+#endif
363
+    return AVERROR(EINVAL);
364
+}
365
+
366
+static int do_encrypt(AVFormatContext *s)
367
+{
368
+    HLSContext *hls = s->priv_data;
369
+    int ret;
370
+    int len;
371
+    AVIOContext *pb;
372
+    uint8_t key[KEYSIZE];
373
+
374
+    len = strlen(hls->basename) + 4 + 1;
375
+    hls->key_basename = av_mallocz(len);
376
+    if (!hls->key_basename)
377
+        return AVERROR(ENOMEM);
378
+
379
+    av_strlcpy(hls->key_basename, s->filename, len);
380
+    av_strlcat(hls->key_basename, ".key", len);
381
+
382
+    if (hls->key_url) {
383
+        strncpy(hls->key_file, hls->key_url, sizeof(hls->key_file));
384
+        strncpy(hls->key_uri, hls->key_url, sizeof(hls->key_uri));
385
+    } else {
386
+        strncpy(hls->key_file, hls->key_basename, sizeof(hls->key_file));
387
+        strncpy(hls->key_uri, hls->key_basename, sizeof(hls->key_uri));
388
+    }
389
+
390
+    if (!*hls->iv_string) {
391
+        uint8_t iv[16] = { 0 };
392
+        char buf[33];
393
+
394
+        if (!hls->iv) {
395
+            AV_WB64(iv + 8, hls->sequence);
396
+        } else {
397
+            memcpy(iv, hls->iv, sizeof(iv));
398
+        }
399
+        ff_data_to_hex(buf, iv, sizeof(iv), 0);
400
+        buf[32] = '\0';
401
+        memcpy(hls->iv_string, buf, sizeof(hls->iv_string));
402
+    }
403
+
404
+    if (!*hls->key_uri) {
405
+        av_log(hls, AV_LOG_ERROR, "no key URI specified in key info file\n");
406
+        return AVERROR(EINVAL);
407
+    }
408
+
409
+    if (!*hls->key_file) {
410
+        av_log(hls, AV_LOG_ERROR, "no key file specified in key info file\n");
411
+        return AVERROR(EINVAL);
412
+    }
413
+
414
+    if (!*hls->key_string) {
415
+        if (!hls->key) {
416
+            if ((ret = randomize(key, sizeof(key))) < 0) {
417
+                av_log(s, AV_LOG_ERROR, "Cannot generate a strong random key\n");
418
+                return ret;
419
+            }
420
+        } else {
421
+            memcpy(key, hls->key, sizeof(key));
422
+        }
423
+
424
+        ff_data_to_hex(hls->key_string, key, sizeof(key), 0);
425
+        if ((ret = s->io_open(s, &pb, hls->key_file, AVIO_FLAG_WRITE, NULL)) < 0)
426
+            return ret;
427
+        avio_seek(pb, 0, SEEK_CUR);
428
+        avio_write(pb, key, KEYSIZE);
429
+        avio_close(pb);
430
+    }
431
+    return 0;
432
+}
433
+
434
+
352 435
 static int hls_encryption_start(AVFormatContext *s)
353 436
 {
354 437
     HLSContext *hls = s->priv_data;
... ...
@@ -662,7 +759,7 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, double
662 662
         hls->discontinuity = 0;
663 663
     }
664 664
 
665
-    if (hls->key_info_file) {
665
+    if (hls->key_info_file || hls->encrypt) {
666 666
         av_strlcpy(en->key_uri, hls->key_uri, sizeof(en->key_uri));
667 667
         av_strlcpy(en->iv_string, hls->iv_string, sizeof(en->iv_string));
668 668
     }
... ...
@@ -865,7 +962,7 @@ static int hls_window(AVFormatContext *s, int last)
865 865
         hls->discontinuity_set = 1;
866 866
     }
867 867
     for (en = hls->segments; en; en = en->next) {
868
-        if (hls->key_info_file && (!key_uri || strcmp(en->key_uri, key_uri) ||
868
+        if ((hls->encrypt || hls->key_info_file) && (!key_uri || strcmp(en->key_uri, key_uri) ||
869 869
                                     av_strcasecmp(en->iv_string, iv_string))) {
870 870
             avio_printf(out, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"", en->key_uri);
871 871
             if (*en->iv_string)
... ...
@@ -1031,9 +1128,18 @@ static int hls_start(AVFormatContext *s)
1031 1031
         av_strlcat(oc->filename, ".tmp", sizeof(oc->filename));
1032 1032
     }
1033 1033
 
1034
-    if (c->key_info_file) {
1035
-        if ((err = hls_encryption_start(s)) < 0)
1036
-            goto fail;
1034
+    if (c->key_info_file || c->encrypt) {
1035
+        if (c->key_info_file && c->encrypt) {
1036
+            av_log(s, AV_LOG_WARNING, "Cannot use both -hls_key_info_file and -hls_enc,"
1037
+                  " will use -hls_key_info_file priority\n");
1038
+        }
1039
+        if (c->key_info_file) {
1040
+            if ((err = hls_encryption_start(s)) < 0)
1041
+                goto fail;
1042
+        } else {
1043
+            if ((err = do_encrypt(s)) < 0)
1044
+                goto fail;
1045
+        }
1037 1046
         if ((err = av_dict_set(&options, "encryption_key", c->key_string, 0))
1038 1047
                 < 0)
1039 1048
             goto fail;
... ...
@@ -1300,6 +1406,7 @@ fail:
1300 1300
     if (ret < 0) {
1301 1301
         av_freep(&hls->basename);
1302 1302
         av_freep(&hls->vtt_basename);
1303
+        av_freep(&hls->key_basename);
1303 1304
         if (hls->avf)
1304 1305
             avformat_free_context(hls->avf);
1305 1306
         if (hls->vtt_avf)
... ...
@@ -1469,6 +1576,7 @@ static int hls_write_trailer(struct AVFormatContext *s)
1469 1469
         ff_format_io_close(s, &vtt_oc->pb);
1470 1470
     }
1471 1471
     av_freep(&hls->basename);
1472
+    av_freep(&hls->key_basename);
1472 1473
     avformat_free_context(oc);
1473 1474
 
1474 1475
     hls->avf = NULL;
... ...
@@ -1503,6 +1611,10 @@ static const AVOption options[] = {
1503 1503
     {"hls_segment_filename", "filename template for segment files", OFFSET(segment_filename),   AV_OPT_TYPE_STRING, {.str = NULL},            0,       0,         E},
1504 1504
     {"hls_segment_size", "maximum size per segment file, (in bytes)",  OFFSET(max_seg_size),    AV_OPT_TYPE_INT,    {.i64 = 0},               0,       INT_MAX,   E},
1505 1505
     {"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},
1506
+    {"hls_enc",    "enable AES128 encryption support", OFFSET(encrypt),      AV_OPT_TYPE_BOOL, {.i64 = 0},            0,       1,         E},
1507
+    {"hls_enc_key",    "hex-coded 16 byte key to encrypt the segments", OFFSET(key),      AV_OPT_TYPE_STRING, .flags = E},
1508
+    {"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},
1509
+    {"hls_enc_iv",    "hex-coded 16 byte initialization vector", OFFSET(iv),      AV_OPT_TYPE_STRING, .flags = E},
1506 1510
     {"hls_subtitle_path",     "set path of hls subtitles", OFFSET(subtitle_filename), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,    E},
1507 1511
     {"hls_flags",     "set flags affecting HLS playlist and media file generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, "flags"},
1508 1512
     {"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"},