[FFmpeg-devel] [PATCH 2/2] lavc/samidec: support Hulu subtitle encryption

Rodger Combs rodger.combs at gmail.com
Sun Oct 11 06:05:50 CEST 2015


hex_to_data should probably move to lavu before this is merged.

This is probably a good case for sub_charenc to run _after_ the decoder.

I could see an argument that this should go in the demuxer instead. Thoughts?
---
 libavcodec/samidec.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/libavcodec/samidec.c b/libavcodec/samidec.c
index 8dd2749..f279f8a 100644
--- a/libavcodec/samidec.c
+++ b/libavcodec/samidec.c
@@ -27,16 +27,53 @@
 #include "ass.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
+#include "libavutil/aes.h"
+#include "libavutil/opt.h"
+#include "libavformat/internal.h"
 #include "htmlsubtitles.h"
 
 typedef struct {
+    AVClass  *class;
     AVBPrint source;
     AVBPrint content;
     AVBPrint encoded_source;
     AVBPrint encoded_content;
     AVBPrint full;
+    uint8_t *key;
+    int key_size;
+    uint8_t *iv;
+    int iv_size;
+    struct AVAES *aes;
 } SAMIContext;
 
+static int hex_to_data(uint8_t *data, const char *p)
+{
+    int c, len, v;
+
+    len = 0;
+    v   = 1;
+    for (;;) {
+        p += strspn(p, SPACE_CHARS);
+        if (*p == '\0')
+            break;
+        c = av_toupper((unsigned char) *p++);
+        if (c >= '0' && c <= '9')
+            c = c - '0';
+        else if (c >= 'A' && c <= 'F')
+            c = c - 'A' + 10;
+        else
+            break;
+        v = (v << 4) | c;
+        if (v & 0x100) {
+            if (data)
+                data[len] = v;
+            len++;
+            v = 1;
+        }
+    }
+    return len;
+}
+
 static int sami_paragraph_to_ass(AVCodecContext *avctx, const char *src)
 {
     SAMIContext *sami = avctx->priv_data;
@@ -51,6 +88,18 @@ static int sami_paragraph_to_ass(AVCodecContext *avctx, const char *src)
     av_bprint_clear(&sami->encoded_content);
     av_bprint_clear(&sami->content);
     av_bprint_clear(&sami->encoded_source);
+
+    p = av_stristr(p, "<SYNC");
+    tag = av_strtok(p, ">", &p);
+    if (sami->aes &&
+        av_stristr(tag, "Encrypted=true") || av_stristr(tag, "Encrypted=\"true\"")) {
+        char iv[16];
+        int len = hex_to_data(p, p);
+        memcpy(iv, sami->iv, FFMIN(sizeof(iv), sami->iv_size));
+        av_aes_crypt(sami->aes, p, p, len / 16, iv, 1);
+        *(p + len) = 0;
+    }
+
     for (;;) {
         char *saveptr = NULL;
         int prev_chr_is_space = 0;
@@ -158,6 +207,18 @@ static av_cold int sami_init(AVCodecContext *avctx)
     av_bprint_init(&sami->encoded_source,  0, 2048);
     av_bprint_init(&sami->encoded_content, 0, 2048);
     av_bprint_init(&sami->full,    0, 2048);
+
+    if (sami->key && sami->iv && *sami->iv && !sami->iv_size)
+        sami->iv_size = strlen(sami->iv);
+
+    if (sami->key_size && sami->iv_size) {
+        sami->aes = av_aes_alloc();
+        if (!sami->aes)
+            return ENOMEM;
+
+        av_aes_init(sami->aes, sami->key, 256, 1);
+    }
+
     return ff_ass_subtitle_header_default(avctx);
 }
 
@@ -172,6 +233,22 @@ static av_cold int sami_close(AVCodecContext *avctx)
     return 0;
 }
 
+#define OFFSET(x) offsetof(SAMIContext, x)
+#define FLAGS AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
+static const AVOption options[] = {
+    { "key", "Decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, { .str = NULL }, 0, 0, FLAGS},
+    { "iv", "Decryption IV", OFFSET(iv), AV_OPT_TYPE_BINARY, { .str = NULL }, 0, 0, FLAGS},
+    { "iv_str", "Decryption IV", OFFSET(iv), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS},
+    { NULL },
+};
+
+static const AVClass sami_class = {
+    .class_name = "sami",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
 AVCodec ff_sami_decoder = {
     .name           = "sami",
     .long_name      = NULL_IF_CONFIG_SMALL("SAMI subtitle"),
@@ -181,4 +258,5 @@ AVCodec ff_sami_decoder = {
     .init           = sami_init,
     .close          = sami_close,
     .decode         = sami_decode_frame,
+    .priv_class     = &sami_class,
 };
-- 
2.6.0



More information about the ffmpeg-devel mailing list