[2/2] applehttp: Handle AES-128 encrypted streams

Message ID 1303306079-15477-2-git-send-email-martin@martin.st
State Committed
Headers show

Commit Message

Martin Storsjö April 20, 2011, 1:27 p.m.
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.

---
This was OKd by Ronald before, but the crypto protocol
patch wasn't OKd yet.

 libavformat/applehttp.c |  108 +++++++++++++++++++++++++++++++++++++++++++++-
 libavformat/version.h   |    2 +-
 2 files changed, 106 insertions(+), 4 deletions(-)

Patch

diff --git a/libavformat/applehttp.c b/libavformat/applehttp.c
index df4494a..4484903 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 <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;
     }
diff --git a/libavformat/version.h b/libavformat/version.h
index 0173018..04c5d73 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  0
+#define LIBAVFORMAT_VERSION_MICRO  1
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \