[FFmpeg-devel] [PATCH] avformat/hls: Re-use crypto URLContext
Luis Scheurenbrand
luis at scheurenbrand.me
Fri Sep 29 18:10:18 EEST 2023
Reset crypto state and keep nested http context alive.
Previously, an unencrypted file, followed by an encrypted file would
result in a corrupted stream, as the stream would not be closed and
an encrypted file would be forwarded to the demuxer.
fixes: https://trac.ffmpeg.org/ticket/10599
fixes: https://trac.ffmpeg.org/ticket/8490
Signed-off-by: Luis Scheurenbrand <luis at scheurenbrand.me>
---
libavformat/crypto.c | 55 ++++++++++++++++++++++++++++++++++++++++++++
libavformat/crypto.h | 38 ++++++++++++++++++++++++++++++
libavformat/hls.c | 32 ++++++++++++++++++++++----
3 files changed, 120 insertions(+), 5 deletions(-)
create mode 100644 libavformat/crypto.h
diff --git a/libavformat/crypto.c b/libavformat/crypto.c
index 75b00020bc..543300c7c0 100644
--- a/libavformat/crypto.c
+++ b/libavformat/crypto.c
@@ -20,8 +20,10 @@
*/
#include "libavutil/aes.h"
+#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/opt.h"
+#include "crypto.h"
#include "url.h"
// encourage reads of 4096 bytes - 1 block is always retained.
@@ -105,6 +107,59 @@ static int set_aes_arg(URLContext *h, uint8_t
**buf, int *buf_len,
return 0;
}
+URLContext *ff_crypto_get_inner(URLContext *h)
+{
+ CryptoContext *c = h->priv_data;
+ return c->hd;
+}
+
+int ff_crypto_reset(URLContext *h)
+{
+ int ret = 0;
+ CryptoContext *c = h->priv_data;
+ int flags = c->flags;
+
+ if (flags & AVIO_FLAG_READ) {
+ av_assert0(c->decrypt_key);
+ av_assert0(c->decrypt_iv);
+ av_assert0(c->aes_decrypt);
+
+ memcpy(c->decrypt_key, c->key, c->keylen);
+ memcpy(c->decrypt_iv, c->iv, c->ivlen);
+
+ ret = av_aes_init(c->aes_decrypt, c->decrypt_key, BLOCKSIZE *
8, 1);
+ if (ret < 0)
+ goto err;
+ }
+
+ if (flags & AVIO_FLAG_WRITE) {
+ av_assert0(c->encrypt_key);
+ av_assert0(c->encrypt_iv);
+ av_assert0(c->aes_encrypt);
+
+ memcpy(c->encrypt_key, c->key, c->keylen);
+ memcpy(c->encrypt_iv, c->iv, c->ivlen);
+
+ ret = av_aes_init(c->aes_encrypt, c->encrypt_key, BLOCKSIZE *
8, 0);
+ if (ret < 0)
+ goto err;
+ }
+
+ c->indata = c->indata_used = c->outdata = 0;
+ c->position = 0;
+ c->eof = 0;
+ c->outdata = 0;
+ c->outptr = 0;
+
+ av_assert0(c->outbuffer);
+ av_assert0(c->inbuffer);
+ memset(c->outbuffer, 0, sizeof(c->outbuffer));
+ memset(c->inbuffer, 0, sizeof(c->inbuffer));
+
+err:
+ return ret;
+}
+
static int crypto_open2(URLContext *h, const char *uri, int flags,
AVDictionary **options)
{
const char *nested_url;
diff --git a/libavformat/crypto.h b/libavformat/crypto.h
new file mode 100644
index 0000000000..7e6d7f0279
--- /dev/null
+++ b/libavformat/crypto.h
@@ -0,0 +1,38 @@
+/*
+ * HTTP definitions
+ * Copyright (c) 2010 Josh Allmann
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+#ifndef AVFORMAT_CRYPTO_H
+#define AVFORMAT_CRYPTO_H
+
+#include "url.h"
+
+URLContext *ff_crypto_get_inner(URLContext *h);
+
+/**
+ * Resets the crypto context.
+ *
+ * @param h pointer to the resource
+ * @return a negative value if an error condition occurred, 0
+ * otherwise
+ */
+int ff_crypto_reset(URLContext *h);
+
+#endif /* AVFORMAT_CRYPTO_H */
diff --git a/libavformat/hls.c b/libavformat/hls.c
index 332a5a15bb..7ad0ec9aec 100644
--- a/libavformat/hls.c
+++ b/libavformat/hls.c
@@ -44,6 +44,7 @@
#include "avio_internal.h"
#include "id3v2.h"
#include "url.h"
+#include "crypto.h"
#include "hls_sample_encryption.h"
@@ -620,17 +621,38 @@ static int ensure_playlist(HLSContext *c, struct
playlist **pls, const char *url
return 0;
}
+/* compares two segments to ensure the encryption is the same and
+ * we can reuse the crypto and/or http context. */
+static int crypto_matches(const struct segment *lhs, const struct
segment *rhs) {
+ if (!lhs || !rhs)
+ return 0;
+ if (lhs->key_type != rhs->key_type)
+ return 0;
+ if (lhs->key_type == KEY_NONE)
+ return 1;
+ + return
+ strcmp(lhs->key, rhs->key) == 0 &&
+ memcmp(lhs->iv, rhs->iv, sizeof(lhs->iv)) == 0;
+}
+
static int open_url_keepalive(AVFormatContext *s, AVIOContext **pb,
const char *url, AVDictionary **options)
{
#if !CONFIG_HTTP_PROTOCOL
return AVERROR_PROTOCOL_NOT_FOUND;
#else
- int ret;
+ int ret=0;
URLContext *uc = ffio_geturlcontext(*pb);
av_assert0(uc);
(*pb)->eof_reached = 0;
- ret = ff_http_do_new_request2(uc, url, options);
+
+ if (av_strstart(url, "crypto+", &url)) {
+ ret = ff_crypto_reset(uc);
+ uc = ff_crypto_get_inner(uc);
+ }
+
+ if (ret == 0) ret = ff_http_do_new_request2(uc, url, options);
if (ret < 0) {
ff_format_io_close(s, pb);
}
@@ -1592,7 +1614,7 @@ reload:
seg = next_segment(v);
if (c->http_multiple == 1 && !v->input_next_requested &&
- seg && seg->key_type == KEY_NONE && av_strstart(seg->url,
"http", NULL)) {
+ seg && av_strstart(seg->url, "http", NULL)) {
ret = open_input(c, v, seg, &v->input_next);
if (ret < 0) {
if (ff_check_interrupt(c->interrupt_callback))
@@ -1624,8 +1646,8 @@ reload:
return ret;
}
- if (c->http_persistent &&
- seg->key_type == KEY_NONE && av_strstart(seg->url, "http", NULL)) {
+ if (c->http_persistent && crypto_matches(seg, next_segment(v)) &&
+ av_strstart(seg->url, "http", NULL)) {
v->input_read_done = 1;
} else {
ff_format_io_close(v->parent, &v->input);
--
2.42.0
More information about the ffmpeg-devel
mailing list