Commit 84465f21 authored by Martin Storsjö's avatar Martin Storsjö

applehttp: Handle AES-128 encrypted streams

This should hopefully fix roundup issue 2586.

This commit only implements it in the demuxer, not in the
protocol handler. If desired, some of the code could be
refactored to be shared by both implementations.
Signed-off-by: 's avatarMartin Storsjö <martin@martin.st>
parent 6e4f70a8
......@@ -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 <unistd.h>
......@@ -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;
}
......
......@@ -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, \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment