[FFmpeg-devel] Patch for libavformat/crypto to add seeking on read
Michael Niedermayer
michael at niedermayer.cc
Tue Aug 30 03:10:20 EEST 2016
On Mon, Aug 29, 2016 at 09:56:59PM +0100, Simon H wrote:
> thanks Michael,
>
> try the attached file. I assume the corruption came from email word
> wrapping?
likely
> or was there something else wrong?
[...]
> example:
> take 25.mp4 created with:
> ffmpeg -f lavfi -i sine=frequency=1000:beep_factor=2:r=48000:duration=720.0 -f lavfi -i testsrc=duration=720.0:rate=25 -vcodec libx264 -cmp 22 -timecode 10:00:00:00 -r 25 -y out\25.mp4
>
> encrypt with:
> openssl enc -aes-128-cbc -K 12345678901234567890123456789012 -iv 12345678901234567890123456789012 -in 25.mp4 -out 25.enc
> then to transcode in ffmpeg:
> ffmpeg -key 12345678901234567890123456789012 -iv 12345678901234567890123456789012 -i crypto:25.enc -vcodec mpeg4 -r 25 -y 25dec.mp4
>
> prior to this modification, the transcode would fail.
>
> Note also: crypto previously marked both reads and writes as streamed, which caused the whole file
> to be read before the transcode started. Now, for read only, if the underlying layer is not marked as streamed,
> then crypto is not. This should enable efficient reading of encrypted containers which require seeking.
> ---
> libavformat/crypto.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 112 insertions(+), 3 deletions(-)
>
> diff --git a/libavformat/crypto.c b/libavformat/crypto.c
> index 2999f50..23bc0a6 100644
> --- a/libavformat/crypto.c
> +++ b/libavformat/crypto.c
> @@ -26,7 +26,8 @@
> #include "internal.h"
> #include "url.h"
>
> -#define MAX_BUFFER_BLOCKS 150
> +// encourage reads of 4096 bytes - 1 block is always retained.
> +#define MAX_BUFFER_BLOCKS 257
> #define BLOCKSIZE 16
>
> typedef struct CryptoContext {
this should be in a separate patch
> @@ -36,6 +37,8 @@ typedef struct CryptoContext {
> outbuffer[BLOCKSIZE*MAX_BUFFER_BLOCKS];
> uint8_t *outptr;
> int indata, indata_used, outdata;
> + int64_t position; // position in file - used in seek
> + int flags;
> int eof;
> uint8_t *key;
> int keylen;
> @@ -109,6 +112,7 @@ static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary
> const char *nested_url;
> int ret = 0;
> CryptoContext *c = h->priv_data;
> + c->flags = flags;
>
> if (!av_strstart(uri, "crypto+", &nested_url) &&
> !av_strstart(uri, "crypto:", &nested_url)) {
> @@ -117,6 +121,8 @@ static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary
> goto err;
> }
>
> + c->position = 0L;
> +
> if (flags & AVIO_FLAG_READ) {
> if ((ret = set_aes_arg(c, &c->decrypt_key, &c->decrypt_keylen,
> c->key, c->keylen, "decryption key")) < 0)
> @@ -152,6 +158,10 @@ static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary
> ret = av_aes_init(c->aes_decrypt, c->decrypt_key, BLOCKSIZE*8, 1);
> if (ret < 0)
> goto err;
> +
> + // pass back information about the context we openned
> + if (c->hd->is_streamed)
> + h->is_streamed = c->hd->is_streamed;
> }
>
> if (flags & AVIO_FLAG_WRITE) {
> @@ -163,12 +173,13 @@ static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary
> ret = av_aes_init(c->aes_encrypt, c->encrypt_key, BLOCKSIZE*8, 0);
> if (ret < 0)
> goto err;
> + // for write, we must be streamed
> + // - linear write only for crytpo aes-128-cbc
> + h->is_streamed = 1;
> }
>
> c->pad_len = 0;
>
> - h->is_streamed = 1;
> -
> err:
> return ret;
> }
> @@ -183,6 +194,7 @@ retry:
> memcpy(buf, c->outptr, size);
> c->outptr += size;
> c->outdata -= size;
> + c->position = c->position + size;
> return size;
> }
> // We avoid using the last block until we've found EOF,
> @@ -222,6 +234,102 @@ retry:
> goto retry;
> }
>
> +static int64_t crypto_seek(URLContext *h, int64_t pos, int whence)
> +{
> + CryptoContext *c = h->priv_data;
> + int64_t block;
> + int64_t newpos;
> +
> + if (c->flags & AVIO_FLAG_WRITE) {
> + av_log(h, AV_LOG_ERROR,
> + "Crypto: seek not supported for write\r\n");
> + return -1L;
should be a AVERROR* code
> + }
> +
> + // reset eof, else we won't read it correctly if we already hit eof.
> + c->eof = 0;
> +
> + switch (whence) {
> + case SEEK_SET:
> + break;
> + case SEEK_CUR:
> + pos = pos + c->position;
> + break;
> + case SEEK_END: {
> + int64_t newpos = ffurl_seek( c->hd, pos, AVSEEK_SIZE );
> + if (newpos < 0) {
> + av_log(h, AV_LOG_ERROR,
> + "Crypto: seek_end - can't get file size (pos=%lld)\r\n", pos);
libavformat/crypto.c: In function ‘crypto_seek’:
libavformat/crypto.c:262:17: warning: format ‘%lld’ expects argument of type ‘long long int’, but argument 4 has type ‘int64_t’ [-Wformat]
> + return newpos;
> + }
> + pos = newpos - pos;
> + }
> + break;
> + case AVSEEK_SIZE:
> + return ffurl_seek( c->hd, pos, AVSEEK_SIZE );
please use the same formating as the surrounding code
> + default:
> + av_log(h, AV_LOG_ERROR,
> + "Crypto: no support for seek where 'whence' is %d\r\n", whence);
> + return -1L;
> + }
> +
> + c->outdata = 0;
> + c->indata = 0;
> + c->indata_used = 0;
> + c->outptr = c->outbuffer;
> +
> + // identify the block containing the IV for the
> + // next block we will decrypt
> + block = pos/16L;
many of the L suffixes are unneeded
thx
[...]
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
Let us carefully observe those good qualities wherein our enemies excel us
and endeavor to excel them, by avoiding what is faulty, and imitating what
is excellent in them. -- Plutarch
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: Digital signature
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20160830/0852f377/attachment.sig>
More information about the ffmpeg-devel
mailing list