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 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel