rtmp: drop support

- librtmp has no test cases, makes no proper releases and has not had a
  single commit within the last year

- librtmp parses the URL itself and requires non-compliant URLs for this

- we have no RTMP tests

- RTMP was used by 2.2% of curl users (self-identified in the 2025
  survey)

Closes #20673
This commit is contained in:
Daniel Stenberg 2026-03-21 12:14:21 +01:00
parent ff28f67970
commit ceae02db04
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
37 changed files with 82 additions and 740 deletions

View file

@ -171,7 +171,6 @@ LIB_CFILES = \
curl_memrchr.c \
curl_ntlm_core.c \
curl_range.c \
curl_rtmp.c \
curl_sasl.c \
curl_sha512_256.c \
curl_share.c \
@ -305,7 +304,6 @@ LIB_HFILES = \
curl_ntlm_core.h \
curl_printf.h \
curl_range.h \
curl_rtmp.h \
curl_sasl.h \
curl_setup.h \
curl_sha256.h \

View file

@ -706,9 +706,6 @@ ${SIZEOF_TIME_T_CODE}
/* if AmiSSL is in use */
#cmakedefine USE_AMISSL 1
/* if librtmp/rtmpdump is in use */
#cmakedefine USE_LIBRTMP 1
/* if GSASL is in use */
#cmakedefine USE_GSASL 1

View file

@ -1,248 +0,0 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) Howard Chu, <hyc@highlandsun.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "curl_rtmp.h"
#include "urldata.h"
#ifdef USE_LIBRTMP
#include "url.h"
#include "curlx/nonblock.h"
#include "progress.h" /* for Curl_pgrsSetUploadSize */
#include "transfer.h"
#include "bufref.h"
#include <librtmp/rtmp.h>
#if defined(USE_WINSOCK) || defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD)
#define SET_RCVTIMEO(tv, s) int tv = s * 1000
#else
#define SET_RCVTIMEO(tv, s) struct timeval tv = { s, 0 }
#endif
#define DEF_BUFTIME (2 * 60 * 60 * 1000) /* 2 hours */
/* meta key for storing RTMP* at connection */
#define CURL_META_RTMP_CONN "meta:proto:rtmp:conn"
static Curl_recv rtmp_recv;
static Curl_send rtmp_send;
static void rtmp_conn_dtor(void *key, size_t klen, void *entry)
{
RTMP *r = entry;
(void)key;
(void)klen;
RTMP_Close(r);
RTMP_Free(r);
}
static CURLcode rtmp_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
RTMP *r = RTMP_Alloc();
if(!r ||
Curl_conn_meta_set(conn, CURL_META_RTMP_CONN, r, rtmp_conn_dtor))
return CURLE_OUT_OF_MEMORY;
RTMP_Init(r);
RTMP_SetBufferMS(r, DEF_BUFTIME);
if(!RTMP_SetupURL(r, CURL_UNCONST(Curl_bufref_ptr(&data->state.url))))
/* rtmp_conn_dtor() performs the cleanup */
return CURLE_URL_MALFORMAT;
return CURLE_OK;
}
static CURLcode rtmp_connect(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
SET_RCVTIMEO(tv, 10);
if(!r)
return CURLE_FAILED_INIT;
if(conn->sock[FIRSTSOCKET] > INT_MAX) {
/* The socket value is invalid for rtmp. */
return CURLE_FAILED_INIT;
}
r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET];
/* We have to know if it is a write before we send the
* connect request packet
*/
if(data->state.upload)
r->Link.protocol |= RTMP_FEATURE_WRITE;
/* For plain streams, use the buffer toggle trick to keep data flowing */
if(!(r->Link.lFlags & RTMP_LF_LIVE) &&
!(r->Link.protocol & RTMP_FEATURE_HTTP))
r->Link.lFlags |= RTMP_LF_BUFX;
(void)curlx_nonblock(r->m_sb.sb_socket, FALSE);
setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO,
(char *)&tv, sizeof(tv));
if(!RTMP_Connect1(r, NULL))
return CURLE_FAILED_INIT;
/* Clients must send a periodic BytesReceived report to the server */
r->m_bSendCounter = TRUE;
*done = TRUE;
conn->recv[FIRSTSOCKET] = rtmp_recv;
conn->send[FIRSTSOCKET] = rtmp_send;
return CURLE_OK;
}
static CURLcode rtmp_do(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
if(!r || !RTMP_ConnectStream(r, 0))
return CURLE_FAILED_INIT;
if(data->state.upload) {
Curl_pgrsSetUploadSize(data, data->state.infilesize);
Curl_xfer_setup_send(data, FIRSTSOCKET);
}
else
Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
*done = TRUE;
return CURLE_OK;
}
static CURLcode rtmp_done(struct Curl_easy *data, CURLcode status,
bool premature)
{
(void)data;
(void)status;
(void)premature;
return CURLE_OK;
}
static CURLcode rtmp_disconnect(struct Curl_easy *data,
struct connectdata *conn,
bool dead_connection)
{
RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
(void)data;
(void)dead_connection;
if(r)
Curl_conn_meta_remove(conn, CURL_META_RTMP_CONN);
return CURLE_OK;
}
static CURLcode rtmp_recv(struct Curl_easy *data, int sockindex, char *buf,
size_t len, size_t *pnread)
{
struct connectdata *conn = data->conn;
RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
CURLcode result = CURLE_OK;
ssize_t rv;
(void)sockindex;
*pnread = 0;
if(!r)
return CURLE_FAILED_INIT;
rv = RTMP_Read(r, buf, curlx_uztosi(len));
if(!curlx_sztouz(rv, pnread)) {
if(r->m_read.status == RTMP_READ_COMPLETE ||
r->m_read.status == RTMP_READ_EOF) {
data->req.size = data->req.bytecount;
}
else
result = CURLE_RECV_ERROR;
}
return result;
}
static CURLcode rtmp_send(struct Curl_easy *data, int sockindex,
const uint8_t *buf, size_t len, bool eos,
size_t *pnwritten)
{
struct connectdata *conn = data->conn;
RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
ssize_t rv;
(void)sockindex;
(void)eos;
*pnwritten = 0;
if(!r)
return CURLE_FAILED_INIT;
rv = RTMP_Write(r, (const char *)buf, curlx_uztosi(len));
if(!curlx_sztouz(rv, pnwritten))
return CURLE_SEND_ERROR;
return CURLE_OK;
}
void Curl_rtmp_version(char *version, size_t len)
{
char suff[2];
if(RTMP_LIB_VERSION & 0xff) {
suff[0] = (RTMP_LIB_VERSION & 0xff) + 'a' - 1;
suff[1] = '\0';
}
else
suff[0] = '\0';
curl_msnprintf(version, len, "librtmp/%d.%d%s",
RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff,
suff);
}
/*
* RTMP protocol handler.h, based on https://rtmpdump.mplayerhq.hu/
*/
const struct Curl_protocol Curl_protocol_rtmp = {
rtmp_setup_connection, /* setup_connection */
rtmp_do, /* do_it */
rtmp_done, /* done */
ZERO_NULL, /* do_more */
rtmp_connect, /* connect_it */
ZERO_NULL, /* connecting */
ZERO_NULL, /* doing */
ZERO_NULL, /* proto_pollset */
ZERO_NULL, /* doing_pollset */
ZERO_NULL, /* domore_pollset */
ZERO_NULL, /* perform_pollset */
rtmp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_is_dead */
ZERO_NULL, /* attach connection */
ZERO_NULL, /* follow */
};
#endif /* USE_LIBRTMP */

View file

@ -1,31 +0,0 @@
#ifndef HEADER_CURL_RTMP_H
#define HEADER_CURL_RTMP_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Howard Chu, <hyc@highlandsun.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#ifdef USE_LIBRTMP
extern const struct Curl_protocol Curl_protocol_rtmp;
void Curl_rtmp_version(char *version, size_t len);
#endif
#endif /* HEADER_CURL_RTMP_H */

View file

@ -35,7 +35,6 @@
#include "curl_ldap.h"
#include "mqtt.h"
#include "pop3.h"
#include "curl_rtmp.h"
#include "rtsp.h"
#include "smb.h"
#include "smtp.h"
@ -267,84 +266,6 @@ const struct Curl_scheme Curl_scheme_pop3s = {
PORT_POP3S, /* defport */
};
const struct Curl_scheme Curl_scheme_rtmp = {
"rtmp", /* scheme */
#ifndef USE_LIBRTMP
ZERO_NULL,
#else
&Curl_protocol_rtmp,
#endif
CURLPROTO_RTMP, /* protocol */
CURLPROTO_RTMP, /* family */
PROTOPT_NONE, /* flags */
PORT_RTMP, /* defport */
};
const struct Curl_scheme Curl_scheme_rtmpt = {
"rtmpt", /* scheme */
#ifndef USE_LIBRTMP
ZERO_NULL,
#else
&Curl_protocol_rtmp,
#endif
CURLPROTO_RTMPT, /* protocol */
CURLPROTO_RTMPT, /* family */
PROTOPT_NONE, /* flags */
PORT_RTMPT, /* defport */
};
const struct Curl_scheme Curl_scheme_rtmpe = {
"rtmpe", /* scheme */
#ifndef USE_LIBRTMP
ZERO_NULL,
#else
&Curl_protocol_rtmp,
#endif
CURLPROTO_RTMPE, /* protocol */
CURLPROTO_RTMPE, /* family */
PROTOPT_NONE, /* flags */
PORT_RTMP, /* defport */
};
const struct Curl_scheme Curl_scheme_rtmpte = {
"rtmpte", /* scheme */
#ifndef USE_LIBRTMP
ZERO_NULL,
#else
&Curl_protocol_rtmp,
#endif
CURLPROTO_RTMPTE, /* protocol */
CURLPROTO_RTMPTE, /* family */
PROTOPT_NONE, /* flags */
PORT_RTMPT, /* defport */
};
const struct Curl_scheme Curl_scheme_rtmps = {
"rtmps", /* scheme */
#ifndef USE_LIBRTMP
ZERO_NULL,
#else
&Curl_protocol_rtmp,
#endif
CURLPROTO_RTMPS, /* protocol */
CURLPROTO_RTMP, /* family */
PROTOPT_NONE, /* flags */
PORT_RTMPS, /* defport */
};
const struct Curl_scheme Curl_scheme_rtmpts = {
"rtmpts", /* scheme */
#ifndef USE_LIBRTMP
ZERO_NULL,
#else
&Curl_protocol_rtmp,
#endif
CURLPROTO_RTMPTS, /* protocol */
CURLPROTO_RTMPT, /* family */
PROTOPT_NONE, /* flags */
PORT_RTMPS, /* defport */
};
const struct Curl_scheme Curl_scheme_rtsp = {
"rtsp", /* scheme */
#ifdef CURL_DISABLE_RTSP
@ -510,55 +431,49 @@ const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len)
6. make sure this function uses the same hash function that worked for
schemetable.c
*/
static const struct Curl_scheme * const all_schemes[67] = {
&Curl_scheme_file,
&Curl_scheme_mqtts, NULL,
&Curl_scheme_gophers, NULL,
&Curl_scheme_rtmpe,
&Curl_scheme_smtp,
&Curl_scheme_sftp,
&Curl_scheme_smb,
&Curl_scheme_smtps,
&Curl_scheme_telnet,
&Curl_scheme_gopher,
&Curl_scheme_tftp, NULL, NULL, NULL,
&Curl_scheme_ftps,
&Curl_scheme_http,
&Curl_scheme_imap,
&Curl_scheme_rtmps,
&Curl_scheme_rtmpt, NULL, NULL, NULL,
&Curl_scheme_ldaps,
&Curl_scheme_wss,
&Curl_scheme_https, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&Curl_scheme_rtsp,
&Curl_scheme_smbs,
&Curl_scheme_scp, NULL, NULL, NULL,
&Curl_scheme_pop3, NULL, NULL,
&Curl_scheme_rtmp, NULL, NULL, NULL,
&Curl_scheme_rtmpte, NULL, NULL, NULL,
&Curl_scheme_dict, NULL, NULL, NULL,
static const struct Curl_scheme * const all_schemes[47] = {
&Curl_scheme_mqtt,
&Curl_scheme_smtp,
&Curl_scheme_tftp,
&Curl_scheme_imap, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&Curl_scheme_ldaps,
&Curl_scheme_dict, NULL,
&Curl_scheme_file, NULL,
&Curl_scheme_pop3s,
&Curl_scheme_imaps, NULL,
&Curl_scheme_ws, NULL,
&Curl_scheme_rtmpts,
&Curl_scheme_ldap, NULL, NULL,
&Curl_scheme_ftp,
&Curl_scheme_scp,
&Curl_scheme_mqtts,
&Curl_scheme_imaps,
&Curl_scheme_ldap,
&Curl_scheme_http,
&Curl_scheme_smb, NULL, NULL,
&Curl_scheme_telnet,
&Curl_scheme_https,
&Curl_scheme_gopher,
&Curl_scheme_rtsp, NULL, NULL,
&Curl_scheme_wss, NULL,
&Curl_scheme_gophers,
&Curl_scheme_smtps,
&Curl_scheme_pop3,
&Curl_scheme_ws, NULL, NULL,
&Curl_scheme_sftp,
&Curl_scheme_ftps, NULL,
&Curl_scheme_smbs, NULL,
};
if(len && (len <= 7)) {
const char *s = scheme;
size_t l = len;
const struct Curl_scheme *h;
unsigned int c = 978;
unsigned int c = 792;
while(l) {
c <<= 5;
c <<= 4;
c += (unsigned int)Curl_raw_tolower(*s);
s++;
l--;
}
h = all_schemes[c % 67];
h = all_schemes[c % 47];
if(h && curl_strnequal(scheme, h->name, len) && !h->name[len])
return h;
}

View file

@ -50,9 +50,6 @@ struct easy_pollset;
#define PORT_SMTP 25
#define PORT_SMTPS 465 /* sometimes called SSMTP */
#define PORT_RTSP 554
#define PORT_RTMP 1935
#define PORT_RTMPT PORT_HTTP
#define PORT_RTMPS PORT_HTTPS
#define PORT_GOPHER 70
#define PORT_MQTT 1883
#define PORT_MQTTS 8883
@ -264,12 +261,6 @@ extern const struct Curl_scheme Curl_scheme_mqtt;
extern const struct Curl_scheme Curl_scheme_mqtts;
extern const struct Curl_scheme Curl_scheme_pop3;
extern const struct Curl_scheme Curl_scheme_pop3s;
extern const struct Curl_scheme Curl_scheme_rtmp;
extern const struct Curl_scheme Curl_scheme_rtmpe;
extern const struct Curl_scheme Curl_scheme_rtmpt;
extern const struct Curl_scheme Curl_scheme_rtmpte;
extern const struct Curl_scheme Curl_scheme_rtmps;
extern const struct Curl_scheme Curl_scheme_rtmpts;
extern const struct Curl_scheme Curl_scheme_rtsp;
extern const struct Curl_scheme Curl_scheme_scp;
extern const struct Curl_scheme Curl_scheme_sftp;

View file

@ -112,7 +112,6 @@
#include "imap.h"
#include "url.h"
#include "connect.h"
#include "curl_rtmp.h"
#include "gopher.h"
#include "mqtt.h"
#include "rtsp.h"

View file

@ -46,11 +46,6 @@
#include <libpsl.h>
#endif
#ifdef USE_LIBRTMP
#include <librtmp/rtmp.h>
#include "curl_rtmp.h"
#endif
#ifdef HAVE_LIBZ
#include <zlib.h>
#endif
@ -178,9 +173,6 @@ char *curl_version(void)
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3)
char h3_version[30];
#endif
#ifdef USE_LIBRTMP
char rtmp_version[30];
#endif
#ifdef USE_GSASL
char gsasl_buf[30];
#endif
@ -244,10 +236,6 @@ char *curl_version(void)
Curl_quic_ver(h3_version, sizeof(h3_version));
src[i++] = h3_version;
#endif
#ifdef USE_LIBRTMP
Curl_rtmp_version(rtmp_version, sizeof(rtmp_version));
src[i++] = rtmp_version;
#endif
#ifdef USE_GSASL
curl_msnprintf(gsasl_buf, sizeof(gsasl_buf), "libgsasl/%s",
gsasl_check_version(NULL));
@ -350,14 +338,6 @@ static const char * const supported_protocols[] = {
#if defined(USE_SSL) && !defined(CURL_DISABLE_POP3)
"pop3s",
#endif
#ifdef USE_LIBRTMP
"rtmp",
"rtmpe",
"rtmps",
"rtmpt",
"rtmpte",
"rtmpts",
#endif
#ifndef CURL_DISABLE_RTSP
"rtsp",
#endif
@ -694,13 +674,5 @@ curl_version_info_data *curl_version_info(CURLversion stamp)
feature_names[n] = NULL; /* Terminate array. */
version_info.features = features;
#ifdef USE_LIBRTMP
{
static char rtmp_version[30];
Curl_rtmp_version(rtmp_version, sizeof(rtmp_version));
version_info.rtmp_version = rtmp_version;
}
#endif
return &version_info;
}