applehttp: Handle AES-128 encrypted streams
authorMartin Storsjö <martin@martin.st>
Sun, 23 Jan 2011 21:42:18 +0000 (23:42 +0200)
committerMartin Storsjö <martin@martin.st>
Fri, 22 Apr 2011 21:27:21 +0000 (00:27 +0300)
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: Martin Storsjö <martin@martin.st>
libavformat/applehttp.c
libavformat/version.h

index 90b86a8..2e0e8a1 100644 (file)
@@ -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>
  * 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;
     }
index 04c5d73..9041f92 100644 (file)
@@ -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, \