diff --git a/libavformat/applehttp.c b/libavformat/applehttp.c index 90b86a8733..2e0e8a1b3a 100644 --- a/libavformat/applehttp.c +++ b/libavformat/applehttp.c @@ -27,6 +27,8 @@ #define _XOPEN_SOURCE 600 #include "libavutil/avstring.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" #include "avformat.h" #include "internal.h" #include @@ -47,9 +49,17 @@ * one anonymous toplevel variant for this, to maintain the structure. */ +enum KeyType { + KEY_NONE, + KEY_AES_128, +}; + struct segment { int duration; char url[MAX_URL_SIZE]; + char key[MAX_URL_SIZE]; + enum KeyType key_type; + uint8_t iv[16]; }; /* @@ -77,6 +87,9 @@ struct variant { int needed, cur_needed; int cur_seq_no; int64_t last_load_time; + + char key_url[MAX_URL_SIZE]; + uint8_t key[16]; }; typedef struct AppleHTTPContext { @@ -160,10 +173,35 @@ static void handle_variant_args(struct variant_info *info, const char *key, } } +struct key_info { + char uri[MAX_URL_SIZE]; + char method[10]; + char iv[35]; +}; + +static void handle_key_args(struct key_info *info, const char *key, + int key_len, char **dest, int *dest_len) +{ + if (!strncmp(key, "METHOD=", key_len)) { + *dest = info->method; + *dest_len = sizeof(info->method); + } else if (!strncmp(key, "URI=", key_len)) { + *dest = info->uri; + *dest_len = sizeof(info->uri); + } else if (!strncmp(key, "IV=", key_len)) { + *dest = info->iv; + *dest_len = sizeof(info->iv); + } +} + static int parse_playlist(AppleHTTPContext *c, const char *url, struct variant *var, AVIOContext *in) { int ret = 0, duration = 0, is_segment = 0, is_variant = 0, bandwidth = 0; + enum KeyType key_type = KEY_NONE; + uint8_t iv[16] = ""; + int has_iv = 0; + char key[MAX_URL_SIZE]; char line[1024]; const char *ptr; int close_in = 0; @@ -192,6 +230,19 @@ static int parse_playlist(AppleHTTPContext *c, const char *url, ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_variant_args, &info); bandwidth = atoi(info.bandwidth); + } else if (av_strstart(line, "#EXT-X-KEY:", &ptr)) { + struct key_info info = {{0}}; + ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_key_args, + &info); + key_type = KEY_NONE; + has_iv = 0; + if (!strcmp(info.method, "AES-128")) + key_type = KEY_AES_128; + if (!strncmp(info.iv, "0x", 2) || !strncmp(info.iv, "0X", 2)) { + ff_hex_to_data(iv, info.iv + 2); + has_iv = 1; + } + av_strlcpy(key, info.uri, sizeof(key)); } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) { if (!var) { var = new_variant(c, 0, url, NULL); @@ -242,6 +293,15 @@ static int parse_playlist(AppleHTTPContext *c, const char *url, goto fail; } seg->duration = duration; + seg->key_type = key_type; + if (has_iv) { + memcpy(seg->iv, iv, sizeof(iv)); + } else { + int seq = var->start_seq_no + var->n_segments; + memset(seg->iv, 0, sizeof(seg->iv)); + AV_WB32(seg->iv + 12, seq); + } + ff_make_absolute_url(seg->key, sizeof(seg->key), url, key); ff_make_absolute_url(seg->url, sizeof(seg->url), url, line); dynarray_add(&var->segments, &var->n_segments, seg); is_segment = 0; @@ -257,6 +317,50 @@ fail: return ret; } +static int open_input(struct variant *var) +{ + struct segment *seg = var->segments[var->cur_seq_no - var->start_seq_no]; + if (seg->key_type == KEY_NONE) { + return ffurl_open(&var->input, seg->url, AVIO_FLAG_READ); + } else if (seg->key_type == KEY_AES_128) { + char iv[33], key[33], url[MAX_URL_SIZE]; + int ret; + if (strcmp(seg->key, var->key_url)) { + URLContext *uc; + if (ffurl_open(&uc, seg->key, AVIO_FLAG_READ) == 0) { + if (ffurl_read_complete(uc, var->key, sizeof(var->key)) + != sizeof(var->key)) { + av_log(NULL, AV_LOG_ERROR, "Unable to read key file %s\n", + seg->key); + } + ffurl_close(uc); + } else { + av_log(NULL, AV_LOG_ERROR, "Unable to open key file %s\n", + seg->key); + } + av_strlcpy(var->key_url, seg->key, sizeof(var->key_url)); + } + ff_data_to_hex(iv, seg->iv, sizeof(seg->iv), 0); + ff_data_to_hex(key, var->key, sizeof(var->key), 0); + iv[32] = key[32] = '\0'; + if (strstr(seg->url, "://")) + snprintf(url, sizeof(url), "crypto+%s", seg->url); + else + snprintf(url, sizeof(url), "crypto:%s", seg->url); + if ((ret = ffurl_alloc(&var->input, url, AVIO_FLAG_READ)) < 0) + return ret; + av_set_string3(var->input->priv_data, "key", key, 0, NULL); + av_set_string3(var->input->priv_data, "iv", iv, 0, NULL); + if ((ret = ffurl_connect(var->input)) < 0) { + ffurl_close(var->input); + var->input = NULL; + return ret; + } + return 0; + } + return AVERROR(ENOSYS); +} + static int read_data(void *opaque, uint8_t *buf, int buf_size) { struct variant *v = opaque; @@ -291,9 +395,7 @@ reload: goto reload; } - ret = ffurl_open(&v->input, - v->segments[v->cur_seq_no - v->start_seq_no]->url, - AVIO_FLAG_READ); + ret = open_input(v); if (ret < 0) return ret; } diff --git a/libavformat/version.h b/libavformat/version.h index 04c5d73f50..9041f92626 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -25,7 +25,7 @@ #define LIBAVFORMAT_VERSION_MAJOR 53 #define LIBAVFORMAT_VERSION_MINOR 0 -#define LIBAVFORMAT_VERSION_MICRO 1 +#define LIBAVFORMAT_VERSION_MICRO 2 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \