[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