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>
... | ... |
@@ -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"}, |