[FFmpeg-devel] [PATCH] avformat/tls_openssl: cleanup code and add doc for dtls (PR #20193)

Jack Lau code at ffmpeg.org
Sat Aug 9 12:18:09 EEST 2025


PR #20193 opened by Jack Lau (JackLau)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20193
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20193.patch

These patches were split from https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20030 to make the patchset smaller and easier to review.


From 0922698158d4b8a8bf778b12b872c58ac28a2c27 Mon Sep 17 00:00:00 2001
From: Jack Lau <jacklau1222 at qq.com>
Date: Tue, 22 Jul 2025 12:53:28 +0800
Subject: [PATCH 1/5] avformat/tls_openssl: cleanup the pointer name of
 TLSContext and TLSShared

Signed-off-by: Jack Lau <jacklau1222 at qq.com>
---
 libavformat/tls_openssl.c | 231 +++++++++++++++++++-------------------
 1 file changed, 116 insertions(+), 115 deletions(-)

diff --git a/libavformat/tls_openssl.c b/libavformat/tls_openssl.c
index 9f7b46c3ca..d6d8afd96b 100644
--- a/libavformat/tls_openssl.c
+++ b/libavformat/tls_openssl.c
@@ -500,16 +500,16 @@ typedef struct TLSContext {
  * to a human-readable string, and stores it in the TLSContext's error_message field.
  * The error queue is then cleared using ERR_clear_error().
  */
-static const char* openssl_get_error(TLSContext *ctx)
+static const char* openssl_get_error(TLSContext *c)
 {
     int r2 = ERR_get_error();
     if (r2) {
-        ERR_error_string_n(r2, ctx->error_message, sizeof(ctx->error_message));
+        ERR_error_string_n(r2, c->error_message, sizeof(c->error_message));
     } else
-        ctx->error_message[0] = '\0';
+        c->error_message[0] = '\0';
 
     ERR_clear_error();
-    return ctx->error_message;
+    return c->error_message;
 }
 
 int ff_tls_set_external_socket(URLContext *h, URLContext *sock)
@@ -661,24 +661,24 @@ static int url_bio_bputs(BIO *b, const char *str)
 
 static av_cold void init_bio_method(URLContext *h)
 {
-    TLSContext *p = h->priv_data;
+    TLSContext *c = h->priv_data;
     BIO *bio;
-    p->url_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK, "urlprotocol bio");
-    BIO_meth_set_write(p->url_bio_method, url_bio_bwrite);
-    BIO_meth_set_read(p->url_bio_method, url_bio_bread);
-    BIO_meth_set_puts(p->url_bio_method, url_bio_bputs);
-    BIO_meth_set_ctrl(p->url_bio_method, url_bio_ctrl);
-    BIO_meth_set_create(p->url_bio_method, url_bio_create);
-    BIO_meth_set_destroy(p->url_bio_method, url_bio_destroy);
-    bio = BIO_new(p->url_bio_method);
-    BIO_set_data(bio, p);
+    c->url_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK, "urlprotocol bio");
+    BIO_meth_set_write(c->url_bio_method, url_bio_bwrite);
+    BIO_meth_set_read(c->url_bio_method, url_bio_bread);
+    BIO_meth_set_puts(c->url_bio_method, url_bio_bputs);
+    BIO_meth_set_ctrl(c->url_bio_method, url_bio_ctrl);
+    BIO_meth_set_create(c->url_bio_method, url_bio_create);
+    BIO_meth_set_destroy(c->url_bio_method, url_bio_destroy);
+    bio = BIO_new(c->url_bio_method);
+    BIO_set_data(bio, c);
 
-    SSL_set_bio(p->ssl, bio, bio);
+    SSL_set_bio(c->ssl, bio, bio);
 }
 
 static void openssl_info_callback(const SSL *ssl, int where, int ret) {
     const char *method = "undefined";
-    TLSContext *ctx = (TLSContext*)SSL_get_ex_data(ssl, 0);
+    TLSContext *c = (TLSContext*)SSL_get_ex_data(ssl, 0);
 
     if (where & SSL_ST_CONNECT) {
         method = "SSL_connect";
@@ -686,11 +686,11 @@ static void openssl_info_callback(const SSL *ssl, int where, int ret) {
         method = "SSL_accept";
 
     if (where & SSL_CB_LOOP) {
-        av_log(ctx, AV_LOG_DEBUG, "Info method=%s state=%s(%s), where=%d, ret=%d\n",
+        av_log(c, AV_LOG_DEBUG, "Info method=%s state=%s(%s), where=%d, ret=%d\n",
                method, SSL_state_string(ssl), SSL_state_string_long(ssl), where, ret);
     } else if (where & SSL_CB_ALERT) {
         method = (where & SSL_CB_READ) ? "read":"write";
-        av_log(ctx, AV_LOG_DEBUG, "Alert method=%s state=%s(%s), where=%d, ret=%d\n",
+        av_log(c, AV_LOG_DEBUG, "Alert method=%s state=%s(%s), where=%d, ret=%d\n",
                method, SSL_state_string(ssl), SSL_state_string_long(ssl), where, ret);
     }
 }
@@ -698,29 +698,29 @@ static void openssl_info_callback(const SSL *ssl, int where, int ret) {
 static int dtls_handshake(URLContext *h)
 {
     int ret = 1, r0, r1;
-    TLSContext *p = h->priv_data;
+    TLSContext *c = h->priv_data;
 
-    p->tls_shared.udp->flags &= ~AVIO_FLAG_NONBLOCK;
+    c->tls_shared.udp->flags &= ~AVIO_FLAG_NONBLOCK;
 
-    r0 = SSL_do_handshake(p->ssl);
+    r0 = SSL_do_handshake(c->ssl);
     if (r0 <= 0) {
-        r1 = SSL_get_error(p->ssl, r0);
+        r1 = SSL_get_error(c->ssl, r0);
 
         if (r1 != SSL_ERROR_WANT_READ && r1 != SSL_ERROR_WANT_WRITE && r1 != SSL_ERROR_ZERO_RETURN) {
-            av_log(p, AV_LOG_ERROR, "Handshake failed, r0=%d, r1=%d\n", r0, r1);
+            av_log(c, AV_LOG_ERROR, "Handshake failed, r0=%d, r1=%d\n", r0, r1);
             ret = print_ssl_error(h, r0);
             goto end;
         }
     } else {
-        av_log(p, AV_LOG_TRACE, "Handshake success, r0=%d\n", r0);
+        av_log(c, AV_LOG_TRACE, "Handshake success, r0=%d\n", r0);
     }
 
     /* Check whether the handshake is completed. */
-    if (SSL_is_init_finished(p->ssl) != TLS_ST_OK)
+    if (SSL_is_init_finished(c->ssl) != TLS_ST_OK)
         goto end;
 
     ret = 0;
-    p->tls_shared.state = DTLS_STATE_FINISHED;
+    c->tls_shared.state = DTLS_STATE_FINISHED;
 end:
     return ret;
 }
@@ -728,57 +728,57 @@ end:
 static av_cold int openssl_init_ca_key_cert(URLContext *h)
 {
     int ret;
-    TLSContext *p = h->priv_data;
-    TLSShared *c = &p->tls_shared;
+    TLSContext *c = h->priv_data;
+    TLSShared *s = &c->tls_shared;
     EVP_PKEY *pkey = NULL;
     X509 *cert = NULL;
     /* setup ca, private key, certificate */
-    if (c->ca_file) {
-        if (!SSL_CTX_load_verify_locations(p->ctx, c->ca_file, NULL))
-            av_log(h, AV_LOG_ERROR, "SSL_CTX_load_verify_locations %s\n", openssl_get_error(p));
+    if (s->ca_file) {
+        if (!SSL_CTX_load_verify_locations(c->ctx, s->ca_file, NULL))
+            av_log(h, AV_LOG_ERROR, "SSL_CTX_load_verify_locations %s\n", openssl_get_error(c));
     } else {
-        if (!SSL_CTX_set_default_verify_paths(p->ctx)) {
+        if (!SSL_CTX_set_default_verify_paths(c->ctx)) {
             // Only log the failure but do not error out, as this is not fatal
             av_log(h, AV_LOG_WARNING, "Failure setting default verify locations: %s\n",
-                openssl_get_error(p));
+                openssl_get_error(c));
         }
     }
 
-    if (c->cert_file) {
-        ret = SSL_CTX_use_certificate_chain_file(p->ctx, c->cert_file);
+    if (s->cert_file) {
+        ret = SSL_CTX_use_certificate_chain_file(c->ctx, s->cert_file);
         if (ret <= 0) {
             av_log(h, AV_LOG_ERROR, "Unable to load cert file %s: %s\n",
-               c->cert_file, openssl_get_error(p));
+               s->cert_file, openssl_get_error(c));
             ret = AVERROR(EIO);
             goto fail;
         }
-    } else if (c->cert_buf) {
-        cert = cert_from_pem_string(c->cert_buf);
-        if (SSL_CTX_use_certificate(p->ctx, cert) != 1) {
-            av_log(p, AV_LOG_ERROR, "SSL: Init SSL_CTX_use_certificate failed, %s\n", openssl_get_error(p));
+    } else if (s->cert_buf) {
+        cert = cert_from_pem_string(s->cert_buf);
+        if (SSL_CTX_use_certificate(c->ctx, cert) != 1) {
+            av_log(c, AV_LOG_ERROR, "SSL: Init SSL_CTX_use_certificate failed, %s\n", openssl_get_error(c));
             ret = AVERROR(EINVAL);
             goto fail;
         }
     }
 
-    if (c->key_file) {
-        ret = SSL_CTX_use_PrivateKey_file(p->ctx, c->key_file, SSL_FILETYPE_PEM);
+    if (s->key_file) {
+        ret = SSL_CTX_use_PrivateKey_file(c->ctx, s->key_file, SSL_FILETYPE_PEM);
         if (ret <= 0) {
             av_log(h, AV_LOG_ERROR, "Unable to load key file %s: %s\n",
-                c->key_file, openssl_get_error(p));
+                s->key_file, openssl_get_error(c));
             ret = AVERROR(EIO);
             goto fail;
         }
-    } else if (c->key_buf) {
-        pkey = pkey_from_pem_string(c->key_buf, 1);
-        if (SSL_CTX_use_PrivateKey(p->ctx, pkey) != 1) {
-            av_log(p, AV_LOG_ERROR, "Init SSL_CTX_use_PrivateKey failed, %s\n", openssl_get_error(p));
+    } else if (s->key_buf) {
+        pkey = pkey_from_pem_string(s->key_buf, 1);
+        if (SSL_CTX_use_PrivateKey(c->ctx, pkey) != 1) {
+            av_log(c, AV_LOG_ERROR, "Init SSL_CTX_use_PrivateKey failed, %s\n", openssl_get_error(c));
             ret = AVERROR(EINVAL);
             goto fail;
         }
     }
 
-    if (c->listen && !c->cert_file && !c->cert_buf && !c->key_file && !c->key_buf) {
+    if (s->listen && !s->cert_file && !s->cert_buf && !s->key_file && !s->key_buf) {
         av_log(h, AV_LOG_VERBOSE, "No server certificate provided, using self-signed\n");
 
         ret = openssl_gen_private_key(&pkey);
@@ -789,14 +789,14 @@ static av_cold int openssl_init_ca_key_cert(URLContext *h)
         if (ret < 0)
             goto fail;
 
-        if (SSL_CTX_use_certificate(p->ctx, cert) != 1) {
-            av_log(p, AV_LOG_ERROR, "SSL_CTX_use_certificate failed for self-signed cert, %s\n", openssl_get_error(p));
+        if (SSL_CTX_use_certificate(c->ctx, cert) != 1) {
+            av_log(c, AV_LOG_ERROR, "SSL_CTX_use_certificate failed for self-signed cert, %s\n", openssl_get_error(c));
             ret = AVERROR(EINVAL);
             goto fail;
         }
 
-        if (SSL_CTX_use_PrivateKey(p->ctx, pkey) != 1) {
-            av_log(p, AV_LOG_ERROR, "SSL_CTX_use_PrivateKey failed for self-signed cert, %s\n", openssl_get_error(p));
+        if (SSL_CTX_use_PrivateKey(c->ctx, pkey) != 1) {
+            av_log(c, AV_LOG_ERROR, "SSL_CTX_use_PrivateKey failed for self-signed cert, %s\n", openssl_get_error(c));
             ret = AVERROR(EINVAL);
             goto fail;
         }
@@ -815,10 +815,10 @@ fail:
  */
 static int dtls_start(URLContext *h, const char *url, int flags, AVDictionary **options)
 {
-    TLSContext *p = h->priv_data;
-    TLSShared *c = &p->tls_shared;
+    TLSContext *c = h->priv_data;
+    TLSShared *s = &c->tls_shared;
     int ret = 0;
-    c->is_dtls = 1;
+    s->is_dtls = 1;
 
     /**
      * The profile for OpenSSL's SRTP is SRTP_AES128_CM_SHA1_80, see ssl/d1_srtp.c.
@@ -826,8 +826,8 @@ static int dtls_start(URLContext *h, const char *url, int flags, AVDictionary **
      */
     const char* profiles = "SRTP_AES128_CM_SHA1_80";
 
-    p->ctx = SSL_CTX_new(c->listen ? DTLS_server_method() : DTLS_client_method());
-    if (!p->ctx) {
+    c->ctx = SSL_CTX_new(s->listen ? DTLS_server_method() : DTLS_client_method());
+    if (!c->ctx) {
         ret = AVERROR(ENOMEM);
         goto fail;
     }
@@ -836,53 +836,54 @@ static int dtls_start(URLContext *h, const char *url, int flags, AVDictionary **
     if (ret < 0) goto fail;
 
     /* Note, this doesn't check that the peer certificate actually matches the requested hostname. */
-    if (c->verify)
-        SSL_CTX_set_verify(p->ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
+    if (s->verify)
+        SSL_CTX_set_verify(c->ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
 
     /* Setup the SRTP context */
-    if (SSL_CTX_set_tlsext_use_srtp(p->ctx, profiles)) {
-        av_log(p, AV_LOG_ERROR, "Init SSL_CTX_set_tlsext_use_srtp failed, profiles=%s, %s\n",
-            profiles, openssl_get_error(p));
+    if (SSL_CTX_set_tlsext_use_srtp(c->ctx, profiles)) {
+        av_log(c, AV_LOG_ERROR, "Init SSL_CTX_set_tlsext_use_srtp failed, profiles=%s, %s\n",
+            profiles, openssl_get_error(c));
         ret = AVERROR(EINVAL);
         return ret;
     }
 
     /* The ssl should not be created unless the ctx has been initialized. */
-    p->ssl = SSL_new(p->ctx);
-    if (!p->ssl) {
+    c->ssl = SSL_new(c->ctx);
+    if (!c->ssl) {
         ret = AVERROR(ENOMEM);
         goto fail;
     }
 
-    if (!c->listen && !c->numerichost)
-        SSL_set_tlsext_host_name(p->ssl, c->host);
+    if (!s->listen && !s->numerichost)
+        SSL_set_tlsext_host_name(c->ssl, s->host);
 
     /* Setup the callback for logging. */
-    SSL_set_ex_data(p->ssl, 0, p);
-    SSL_CTX_set_info_callback(p->ctx, openssl_info_callback);
+    SSL_set_ex_data(c->ssl, 0, c);
+    SSL_CTX_set_info_callback(c->ctx, openssl_info_callback);
 
     /**
      * We have set the MTU to fragment the DTLS packet. It is important to note that the
      * packet is split to ensure that each handshake packet is smaller than the MTU.
      */
-    if (c->mtu <= 0)
-        c->mtu = 1096;
-    SSL_set_options(p->ssl, SSL_OP_NO_QUERY_MTU);
-    SSL_set_mtu(p->ssl, c->mtu);
-    DTLS_set_link_mtu(p->ssl, c->mtu);
+    if (s->mtu <= 0)
+        s->mtu = 1096;
+    SSL_set_options(c->ssl, SSL_OP_NO_QUERY_MTU);
+    SSL_set_mtu(c->ssl, s->mtu);
+    DTLS_set_link_mtu(c->ssl, s->mtu);
     init_bio_method(h);
-    if (p->tls_shared.external_sock != 1) {
-        if ((ret = ff_tls_open_underlying(&p->tls_shared, h, url, options)) < 0) {
-            av_log(p, AV_LOG_ERROR, "Failed to connect %s\n", url);
+
+    if (c->tls_shared.external_sock != 1) {
+        if ((ret = ff_tls_open_underlying(&c->tls_shared, h, url, options)) < 0) {
+            av_log(c, AV_LOG_ERROR, "Failed to connect %s\n", url);
             return ret;
         }
     }
 
     /* This seems to be necessary despite explicitly setting client/server method above. */
-    if (c->listen)
-        SSL_set_accept_state(p->ssl);
+    if (s->listen)
+        SSL_set_accept_state(c->ssl);
     else
-        SSL_set_connect_state(p->ssl);
+        SSL_set_connect_state(c->ssl);
 
     /**
      * During initialization, we only need to call SSL_do_handshake once because SSL_read consumes
@@ -895,16 +896,16 @@ static int dtls_start(URLContext *h, const char *url, int flags, AVDictionary **
      *
      * The SSL_do_handshake can't be called if DTLS hasn't prepare for udp.
      */
-    if (p->tls_shared.external_sock != 1) {
+    if (c->tls_shared.external_sock != 1) {
         ret = dtls_handshake(h);
         // Fatal SSL error, for example, no available suite when peer is DTLS 1.0 while we are DTLS 1.2.
         if (ret < 0) {
-            av_log(p, AV_LOG_ERROR, "Failed to drive SSL context, ret=%d\n", ret);
+            av_log(c, AV_LOG_ERROR, "Failed to drive SSL context, ret=%d\n", ret);
             return AVERROR(EIO);
         }
     }
 
-    av_log(p, AV_LOG_VERBOSE, "Setup ok, MTU=%d\n", p->tls_shared.mtu);
+    av_log(c, AV_LOG_VERBOSE, "Setup ok, MTU=%d\n", c->tls_shared.mtu);
 
     ret = 0;
 fail:
@@ -913,24 +914,24 @@ fail:
 
 static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
 {
-    TLSContext *p = h->priv_data;
-    TLSShared *c = &p->tls_shared;
+    TLSContext *c = h->priv_data;
+    TLSShared *s = &c->tls_shared;
     int ret;
 
-    if ((ret = ff_tls_open_underlying(c, h, uri, options)) < 0)
+    if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0)
         goto fail;
 
     // We want to support all versions of TLS >= 1.0, but not the deprecated
     // and insecure SSLv2 and SSLv3.  Despite the name, TLS_*_method()
     // enables support for all versions of SSL and TLS, and we then disable
     // support for the old protocols immediately after creating the context.
-    p->ctx = SSL_CTX_new(c->listen ? TLS_server_method() : TLS_client_method());
-    if (!p->ctx) {
-        av_log(h, AV_LOG_ERROR, "%s\n", openssl_get_error(p));
+    c->ctx = SSL_CTX_new(s->listen ? TLS_server_method() : TLS_client_method());
+    if (!c->ctx) {
+        av_log(h, AV_LOG_ERROR, "%s\n", openssl_get_error(c));
         ret = AVERROR(EIO);
         goto fail;
     }
-    if (!SSL_CTX_set_min_proto_version(p->ctx, TLS1_VERSION)) {
+    if (!SSL_CTX_set_min_proto_version(c->ctx, TLS1_VERSION)) {
         av_log(h, AV_LOG_ERROR, "Failed to set minimum TLS version to TLSv1\n");
         ret = AVERROR_EXTERNAL;
         goto fail;
@@ -938,33 +939,33 @@ static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **op
     ret = openssl_init_ca_key_cert(h);
     if (ret < 0) goto fail;
 
-    if (c->verify)
-        SSL_CTX_set_verify(p->ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
-    p->ssl = SSL_new(p->ctx);
-    if (!p->ssl) {
-        av_log(h, AV_LOG_ERROR, "%s\n", openssl_get_error(p));
+    if (s->verify)
+        SSL_CTX_set_verify(c->ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
+    c->ssl = SSL_new(c->ctx);
+    if (!c->ssl) {
+        av_log(h, AV_LOG_ERROR, "%s\n", openssl_get_error(c));
         ret = AVERROR(EIO);
         goto fail;
     }
-    SSL_set_ex_data(p->ssl, 0, p);
-    SSL_CTX_set_info_callback(p->ctx, openssl_info_callback);
+    SSL_set_ex_data(c->ssl, 0, c);
+    SSL_CTX_set_info_callback(c->ctx, openssl_info_callback);
     init_bio_method(h);
-    if (!c->listen && !c->numerichost) {
+    if (!s->listen && !s->numerichost) {
         // By default OpenSSL does too lax wildcard matching
-        SSL_set_hostflags(p->ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
-        if (!SSL_set1_host(p->ssl, c->host)) {
+        SSL_set_hostflags(c->ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+        if (!SSL_set1_host(c->ssl, s->host)) {
             av_log(h, AV_LOG_ERROR, "Failed to set hostname for TLS/SSL verification: %s\n",
-                openssl_get_error(p));
+                openssl_get_error(c));
             ret = AVERROR_EXTERNAL;
             goto fail;
         }
-        if (!SSL_set_tlsext_host_name(p->ssl, c->host)) {
-            av_log(h, AV_LOG_ERROR, "Failed to set hostname for SNI: %s\n", openssl_get_error(p));
+        if (!SSL_set_tlsext_host_name(c->ssl, s->host)) {
+            av_log(h, AV_LOG_ERROR, "Failed to set hostname for SNI: %s\n", openssl_get_error(c));
             ret = AVERROR_EXTERNAL;
             goto fail;
         }
     }
-    ret = c->listen ? SSL_accept(p->ssl) : SSL_connect(p->ssl);
+    ret = s->listen ? SSL_accept(c->ssl) : SSL_connect(c->ssl);
     if (ret == 0) {
         av_log(h, AV_LOG_ERROR, "Unable to negotiate TLS/SSL session\n");
         ret = AVERROR(EIO);
@@ -983,10 +984,10 @@ fail:
 static int tls_read(URLContext *h, uint8_t *buf, int size)
 {
     TLSContext *c = h->priv_data;
-    URLContext *uc = c->tls_shared.is_dtls ? c->tls_shared.udp
-                                           : c->tls_shared.tcp;
+    TLSShared *s = &c->tls_shared;
+    URLContext *uc = s->is_dtls ? s->udp : s->tcp;
     int ret;
-    // Set or clear the AVIO_FLAG_NONBLOCK on c->tls_shared.tcp
+    // Set or clear the AVIO_FLAG_NONBLOCK on the underlying socket
     uc->flags &= ~AVIO_FLAG_NONBLOCK;
     uc->flags |= h->flags & AVIO_FLAG_NONBLOCK;
     ret = SSL_read(c->ssl, buf, size);
@@ -1000,15 +1001,15 @@ static int tls_read(URLContext *h, uint8_t *buf, int size)
 static int tls_write(URLContext *h, const uint8_t *buf, int size)
 {
     TLSContext *c = h->priv_data;
-    URLContext *uc = c->tls_shared.is_dtls ? c->tls_shared.udp
-                                           : c->tls_shared.tcp;
+    TLSShared *s = &c->tls_shared;
+    URLContext *uc = s->is_dtls ? s->udp : s->tcp;
     int ret;
 
     // Set or clear the AVIO_FLAG_NONBLOCK on c->tls_shared.tcp
     uc->flags &= ~AVIO_FLAG_NONBLOCK;
     uc->flags |= h->flags & AVIO_FLAG_NONBLOCK;
 
-    if (c->tls_shared.is_dtls)
+    if (s->is_dtls)
         size = FFMIN(size, DTLS_get_data_mtu(c->ssl));
 
     ret = SSL_write(c->ssl, buf, size);
@@ -1021,16 +1022,16 @@ static int tls_write(URLContext *h, const uint8_t *buf, int size)
 
 static int tls_get_file_handle(URLContext *h)
 {
-    TLSContext *p = h->priv_data;
-    TLSShared *c = &p->tls_shared;
-    return ffurl_get_file_handle(c->is_dtls ? c->udp : c->tcp);
+    TLSContext *c = h->priv_data;
+    TLSShared *s = &c->tls_shared;
+    return ffurl_get_file_handle(s->is_dtls ? s->udp : s->tcp);
 }
 
 static int tls_get_short_seek(URLContext *h)
 {
-    TLSContext *p = h->priv_data;
-    TLSShared *c = &p->tls_shared;
-    return ffurl_get_short_seek(c->is_dtls ? c->udp : c->tcp);
+    TLSContext *c = h->priv_data;
+    TLSShared *s = &c->tls_shared;
+    return ffurl_get_short_seek(s->is_dtls ? s->udp : s->tcp);
 }
 
 static const AVOption options[] = {
-- 
2.49.1


From 1f84ec51ffc2e12ca726bb5c9ee69af71c61d510 Mon Sep 17 00:00:00 2001
From: Jack Lau <jacklau1222 at qq.com>
Date: Mon, 4 Aug 2025 15:49:11 +0800
Subject: [PATCH 2/5] avformat/tls_openssl: simplify the external_sock check

Signed-off-by: Jack Lau <jacklau1222 at qq.com>
---
 libavformat/tls_openssl.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libavformat/tls_openssl.c b/libavformat/tls_openssl.c
index d6d8afd96b..3df6e3f0a1 100644
--- a/libavformat/tls_openssl.c
+++ b/libavformat/tls_openssl.c
@@ -872,7 +872,7 @@ static int dtls_start(URLContext *h, const char *url, int flags, AVDictionary **
     DTLS_set_link_mtu(c->ssl, s->mtu);
     init_bio_method(h);
 
-    if (c->tls_shared.external_sock != 1) {
+    if (!c->tls_shared.external_sock) {
         if ((ret = ff_tls_open_underlying(&c->tls_shared, h, url, options)) < 0) {
             av_log(c, AV_LOG_ERROR, "Failed to connect %s\n", url);
             return ret;
@@ -896,7 +896,7 @@ static int dtls_start(URLContext *h, const char *url, int flags, AVDictionary **
      *
      * The SSL_do_handshake can't be called if DTLS hasn't prepare for udp.
      */
-    if (c->tls_shared.external_sock != 1) {
+    if (!c->tls_shared.external_sock) {
         ret = dtls_handshake(h);
         // Fatal SSL error, for example, no available suite when peer is DTLS 1.0 while we are DTLS 1.2.
         if (ret < 0) {
-- 
2.49.1


From d4047873dc06300a649071ff64a8695bde80cc3d Mon Sep 17 00:00:00 2001
From: Jack Lau <jacklau1222 at qq.com>
Date: Thu, 7 Aug 2025 10:51:01 +0800
Subject: [PATCH 3/5] avformat/tls_openssl: add check to avoid tls_shared is
 null

Signed-off-by: Jack Lau <jacklau1222 at qq.com>
---
 libavformat/tls_openssl.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libavformat/tls_openssl.c b/libavformat/tls_openssl.c
index 3df6e3f0a1..554dff711a 100644
--- a/libavformat/tls_openssl.c
+++ b/libavformat/tls_openssl.c
@@ -818,6 +818,9 @@ static int dtls_start(URLContext *h, const char *url, int flags, AVDictionary **
     TLSContext *c = h->priv_data;
     TLSShared *s = &c->tls_shared;
     int ret = 0;
+
+    if (!s)
+        return AVERROR(EINVAL);
     s->is_dtls = 1;
 
     /**
@@ -918,6 +921,9 @@ static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **op
     TLSShared *s = &c->tls_shared;
     int ret;
 
+    if (!s)
+        return AVERROR(EINVAL);
+
     if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0)
         goto fail;
 
-- 
2.49.1


From 7c89bcce9d7c883f2797e0ffb638bd54fcf2bea0 Mon Sep 17 00:00:00 2001
From: Jack Lau <jacklau1222 at qq.com>
Date: Sat, 9 Aug 2025 16:52:46 +0800
Subject: [PATCH 4/5] avformat/tls: add new option use_srtp to control whether
 enable it

Signed-off-by: Jack Lau <jacklau1222 at qq.com>
---
 libavformat/tls.h         |  2 ++
 libavformat/tls_openssl.c | 24 ++++++++++++------------
 libavformat/whip.c        |  1 +
 3 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/libavformat/tls.h b/libavformat/tls.h
index 0c02a4ab27..5f09357d8a 100644
--- a/libavformat/tls.h
+++ b/libavformat/tls.h
@@ -62,6 +62,7 @@ typedef struct TLSShared {
     URLContext *tcp;
 
     int is_dtls;
+    int use_srtp;
 
     enum DTLSState state;
 
@@ -90,6 +91,7 @@ typedef struct TLSShared {
     {"listen",     "Listen for incoming connections",     offsetof(pstruct, options_field . listen),    AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = TLS_OPTFL }, \
     {"http_proxy", "Set proxy to tunnel through",         offsetof(pstruct, options_field . http_proxy), AV_OPT_TYPE_STRING, .flags = TLS_OPTFL }, \
     {"external_sock", "Use external socket",              offsetof(pstruct, options_field . external_sock), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = TLS_OPTFL }, \
+    {"use_srtp", "Enable use_srtp DTLS extension",        offsetof(pstruct, options_field . use_srtp), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = TLS_OPTFL }, \
     {"mtu", "Maximum Transmission Unit", offsetof(pstruct, options_field . mtu), AV_OPT_TYPE_INT,  { .i64 = 0 }, 0, INT_MAX, .flags = TLS_OPTFL}, \
     {"cert_pem",   "Certificate PEM string",              offsetof(pstruct, options_field . cert_buf),  AV_OPT_TYPE_STRING, .flags = TLS_OPTFL }, \
     {"key_pem",    "Private key PEM string",              offsetof(pstruct, options_field . key_buf),   AV_OPT_TYPE_STRING, .flags = TLS_OPTFL }, \
diff --git a/libavformat/tls_openssl.c b/libavformat/tls_openssl.c
index 554dff711a..67d8b33d5c 100644
--- a/libavformat/tls_openssl.c
+++ b/libavformat/tls_openssl.c
@@ -823,12 +823,6 @@ static int dtls_start(URLContext *h, const char *url, int flags, AVDictionary **
         return AVERROR(EINVAL);
     s->is_dtls = 1;
 
-    /**
-     * The profile for OpenSSL's SRTP is SRTP_AES128_CM_SHA1_80, see ssl/d1_srtp.c.
-     * The profile for FFmpeg's SRTP is SRTP_AES128_CM_HMAC_SHA1_80, see libavformat/srtp.c.
-     */
-    const char* profiles = "SRTP_AES128_CM_SHA1_80";
-
     c->ctx = SSL_CTX_new(s->listen ? DTLS_server_method() : DTLS_client_method());
     if (!c->ctx) {
         ret = AVERROR(ENOMEM);
@@ -842,12 +836,18 @@ static int dtls_start(URLContext *h, const char *url, int flags, AVDictionary **
     if (s->verify)
         SSL_CTX_set_verify(c->ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
 
-    /* Setup the SRTP context */
-    if (SSL_CTX_set_tlsext_use_srtp(c->ctx, profiles)) {
-        av_log(c, AV_LOG_ERROR, "Init SSL_CTX_set_tlsext_use_srtp failed, profiles=%s, %s\n",
-            profiles, openssl_get_error(c));
-        ret = AVERROR(EINVAL);
-        return ret;
+    if (s->use_srtp) {
+        /**
+         * The profile for OpenSSL's SRTP is SRTP_AES128_CM_SHA1_80, see ssl/d1_srtp.c.
+         * The profile for FFmpeg's SRTP is SRTP_AES128_CM_HMAC_SHA1_80, see libavformat/srtp.c.
+         */
+        const char* profiles = "SRTP_AES128_CM_SHA1_80";
+        if (SSL_CTX_set_tlsext_use_srtp(c->ctx, profiles)) {
+            av_log(c, AV_LOG_ERROR, "Init SSL_CTX_set_tlsext_use_srtp failed, profiles=%s, %s\n",
+                profiles, openssl_get_error(c));
+            ret = AVERROR(EINVAL);
+            goto fail;
+        }
     }
 
     /* The ssl should not be created unless the ctx has been initialized. */
diff --git a/libavformat/whip.c b/libavformat/whip.c
index 256ea14d2c..65fd3b39b2 100644
--- a/libavformat/whip.c
+++ b/libavformat/whip.c
@@ -1303,6 +1303,7 @@ next_packet:
                 } else
                     av_dict_set(&opts, "key_pem", whip->key_buf, 0);
                 av_dict_set_int(&opts, "external_sock", 1, 0);
+                av_dict_set_int(&opts, "use_srtp", 1, 0);
                 av_dict_set_int(&opts, "listen", 1, 0);
                 /* If got the first binding response, start DTLS handshake. */
                 ret = ffurl_open_whitelist(&whip->dtls_uc, buf, AVIO_FLAG_READ_WRITE, &s->interrupt_callback,
-- 
2.49.1


From a1f9d7631bcf6c5f56968ee591db8ff11062f9d2 Mon Sep 17 00:00:00 2001
From: Jack Lau <jacklau1222 at qq.com>
Date: Sat, 9 Aug 2025 16:55:45 +0800
Subject: [PATCH 5/5] doc/protocols: add doc for dtls

Signed-off-by: Jack Lau <jacklau1222 at qq.com>
---
 doc/protocols.texi | 78 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/doc/protocols.texi b/doc/protocols.texi
index 089f917dcc..4f164983c7 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -2028,6 +2028,84 @@ To play back a stream from the TLS/SSL server using @command{ffplay}:
 ffplay tls://@var{hostname}:@var{port}
 @end example
 
+ at section dtls
+
+Datagram Transport Layer Security (DTLS)
+
+The required syntax for a DTLS URL is:
+ at example
+dtls://@var{hostname}:@var{port}
+ at end example
+
+DTLS shares most options with TLS, but operates over UDP instead of TCP.
+The following parameters can be set via command line options
+(or in code via @code{AVOption}s):
+
+ at table @option
+
+ at item ca_file, cafile=@var{filename}
+A file containing certificate authority (CA) root certificates to treat
+as trusted. If the linked TLS library contains a default this might not
+need to be specified for verification to work, but not all libraries and
+setups have defaults built in.
+The file must be in OpenSSL PEM format.
+
+ at item tls_verify=@var{1|0}
+If enabled, try to verify the peer that we are communicating with.
+Note, if using OpenSSL, this currently only makes sure that the
+peer certificate is signed by one of the root certificates in the CA
+database, but it does not validate that the certificate actually
+matches the host name we are trying to connect to.
+
+This is disabled by default since it requires a CA database to be
+provided by the caller in many cases.
+
+ at item cert_file, cert=@var{filename}
+A file containing a certificate to use in the handshake with the peer.
+(When operating as server, in listen mode, this is more often required
+by the peer, while client certificates only are mandated in certain
+setups.)
+
+ at item key_file, key=@var{filename}
+A file containing the private key for the certificate.
+
+ at item cert_pem=@var{string}
+Certificate PEM string
+
+ at item key_pem=@var{string}
+Private key PEM string
+
+ at item listen=@var{1|0}
+If enabled, listen for connections on the provided port, and assume
+the server role in the handshake instead of the client role.
+
+ at item mtu=@var{size}
+Set the Maximum Transmission Unit (MTU) for DTLS packets.
+
+ at item use_srtp=@var{1|0}
+Enable the use_srtp DTLS extension.
+This is used in WebRTC applications to establish SRTP encryption keys
+through the DTLS handshake. Default is disabled.
+
+ at item external_sock=@var{1|0}
+Use an external socket instead of creating a new one. Default is disabled.
+
+ at end table
+
+Example command lines:
+
+To create a DTLS server:
+
+ at example
+ffmpeg -listen 1 -i dtls://@var{hostname}:@var{port} @var{output}
+ at end example
+
+To create a DTLS client and send data to server:
+
+ at example
+ffmpeg -i @var{input} -f @var{format} dtls://@var{hostname}:@var{port}
+ at end example
+
 @section udp
 
 User Datagram Protocol.
-- 
2.49.1



More information about the ffmpeg-devel mailing list