PR #20349 opened by Marvin Scholz (ePirat)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20349
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20349.patch

This PR is a proposal for a new API in `libavformat`, which I call the command 
API, it adds the ability to send commands to demuxers and receive replies for 
these commands as well. Depending on the demuxer and the command, it could also 
be possible to just receive a command reply or just send a command without 
getting a reply, this would be up to the command id documentation to specify.

It adds two new APIs to libavformat:

- `avformat_send_command`
- `avformat_receive_command_reply`

Both functions need to be passed the command ID which identifies the command to 
send or for which to receive a reply. It also uses void pointers for the data 
payloads, for lack of any better way of generic data handling in C.

To illustrate how this would be used I added a simple example for RTSP's 
`SET_PARAMETER` command, note that this implementation right now is not fully 
done in this PR and should not yet be the focus of reviews.


>From a99a5e3fcde0758a57ad2fd0bcfad77d2686439c Mon Sep 17 00:00:00 2001
From: Marvin Scholz <epira...@gmail.com>
Date: Wed, 27 Aug 2025 03:07:00 +0200
Subject: [PATCH 1/3] avformat: Add new demuxer command API

This new API adds the ability to send commands to the demuxer and also
receive replies to these commands.

It is useful in cases like the RTSP demuxer, where it might be desirable
for the API user to send commands like SET_PARAMETER or PLAY and PAUSE.

Sending and receiving are decoupled, as to not require waiting for a
reply when sending, as with some demuxers and depending on the command
it could be necessary to process further packets in the meantime
before requesting a reply.
---
 libavformat/avformat.h    | 29 +++++++++++++++++++++++++++++
 libavformat/demux.h       | 18 ++++++++++++++++++
 libavformat/demux_utils.c | 14 ++++++++++++++
 3 files changed, 61 insertions(+)

diff --git a/libavformat/avformat.h b/libavformat/avformat.h
index be6e532387..3ba4c88ca6 100644
--- a/libavformat/avformat.h
+++ b/libavformat/avformat.h
@@ -2364,6 +2364,35 @@ int av_read_play(AVFormatContext *s);
  */
 int av_read_pause(AVFormatContext *s);
 
+enum AVFormatCommandID {
+    /**
+     * Sent a RTSP SET_PARAMETER request to the server
+     *
+     * Sends an SET_PARAMETER RTSP command to the server,
+     * with a data payload of type ::AVRTSPCommandRequest,
+     * ownership of it and its data remains with the caller.
+     *
+     * A reply retrieved is of type ::AVRTSPResponse and it
+     * and its contents must be freed by the caller.
+     */
+    AVFORMAT_COMMAND_RTSP_SET_PARAMETER,
+};
+
+/**
+ * Send a command to the demuxer
+ *
+ * Sends the specified command and (depending on the command)
+ * optionally a command-specific payload to the demuxer to handle.
+ */
+int avformat_send_command(AVFormatContext *s, enum AVFormatCommandID id, void 
*data);
+
+/**
+ * Receive a command reply from the demuxer
+ *
+ * Retrieves a reply for a previously sent command from the muxer.
+ */
+int avformat_receive_command_reply(AVFormatContext *s, enum AVFormatCommandID 
id, void **data_out);
+
 /**
  * Close an opened input AVFormatContext. Free it and all its contents
  * and set *s to NULL.
diff --git a/libavformat/demux.h b/libavformat/demux.h
index 7c22c870e2..87774145fd 100644
--- a/libavformat/demux.h
+++ b/libavformat/demux.h
@@ -133,6 +133,24 @@ typedef struct FFInputFormat {
      * @see avdevice_list_devices() for more details.
      */
     int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList 
*device_list);
+
+    /**
+     * Handle demuxer commands
+     * Demuxers implementing this must return AVERROR(ENOTSUP)
+     * if the provided \p id is not handled by the demuxer.
+     *
+     * @see avformat_send_command()
+     */
+    int (*handle_command)(struct AVFormatContext *, enum AVFormatCommandID id, 
void *data);
+
+    /**
+     * Retrieve the reply for a previously submitted command, if any.
+     * Submitting another command before reading a previous reply, discards it.
+     * @return 0 on success, < 0 on error
+     *
+     * @see avformat_receive_command_reply()
+     */
+    int (*read_command_reply)(struct AVFormatContext *, enum AVFormatCommandID 
id, void **data_out);
 } FFInputFormat;
 
 static inline const FFInputFormat *ffifmt(const AVInputFormat *fmt)
diff --git a/libavformat/demux_utils.c b/libavformat/demux_utils.c
index b632277460..c871abc84e 100644
--- a/libavformat/demux_utils.c
+++ b/libavformat/demux_utils.c
@@ -188,6 +188,20 @@ int av_read_pause(AVFormatContext *s)
     return AVERROR(ENOSYS);
 }
 
+int avformat_send_command(AVFormatContext *s, enum AVFormatCommandID id, void 
*data)
+{
+    if (ffifmt(s->iformat)->handle_command)
+        return ffifmt(s->iformat)->handle_command(s, id, data);
+    return AVERROR(ENOSYS);
+}
+
+int avformat_receive_command_reply(AVFormatContext *s, enum AVFormatCommandID 
id, void **data_out)
+{
+    if (ffifmt(s->iformat)->read_command_reply)
+        return ffifmt(s->iformat)->read_command_reply(s, id, data_out);
+    return AVERROR(ENOSYS);
+}
+
 int ff_generate_avci_extradata(AVStream *st)
 {
     static const uint8_t avci100_1080p_extradata[] = {
-- 
2.49.1


>From 648d06b5b5fe38eaa56d1c8ab31bf8dc66ecc03e Mon Sep 17 00:00:00 2001
From: Marvin Scholz <epira...@gmail.com>
Date: Tue, 26 Aug 2025 23:31:57 +0200
Subject: [PATCH 2/3] rtsp: expose rtsp_send_cmd_with_content_async

Add the ff_ prefix and move the declaration to the header, so it can
be used in other places like rtspdec in a future commit.
---
 libavformat/rtsp.c |  6 +++---
 libavformat/rtsp.h | 19 +++++++++++++++++++
 2 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c
index 10355b89b8..85d974f61b 100644
--- a/libavformat/rtsp.c
+++ b/libavformat/rtsp.c
@@ -1376,7 +1376,7 @@ start:
  *
  * @return zero if success, nonzero otherwise
  */
-static int rtsp_send_cmd_with_content_async(AVFormatContext *s,
+int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s,
                                             const char *method, const char 
*url,
                                             const char *headers,
                                             const unsigned char *send_content,
@@ -1436,7 +1436,7 @@ static int 
rtsp_send_cmd_with_content_async(AVFormatContext *s,
 int ff_rtsp_send_cmd_async(AVFormatContext *s, const char *method,
                            const char *url, const char *headers)
 {
-    return rtsp_send_cmd_with_content_async(s, method, url, headers, NULL, 0);
+    return ff_rtsp_send_cmd_with_content_async(s, method, url, headers, NULL, 
0);
 }
 
 int ff_rtsp_send_cmd(AVFormatContext *s, const char *method, const char *url,
@@ -1461,7 +1461,7 @@ int ff_rtsp_send_cmd_with_content(AVFormatContext *s,
 
 retry:
     cur_auth_type = rt->auth_state.auth_type;
-    if ((ret = rtsp_send_cmd_with_content_async(s, method, url, header,
+    if ((ret = ff_rtsp_send_cmd_with_content_async(s, method, url, header,
                                                 send_content,
                                                 send_content_length)) < 0)
         return ret;
diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h
index ca278acd43..c27cbe1e03 100644
--- a/libavformat/rtsp.h
+++ b/libavformat/rtsp.h
@@ -508,6 +508,25 @@ void ff_rtsp_parse_line(AVFormatContext *s,
 int ff_rtsp_send_cmd_async(AVFormatContext *s, const char *method,
                            const char *url, const char *headers);
 
+/**
+ * Send a command to the RTSP server without waiting for the reply.
+ *
+ * @param s RTSP (de)muxer context
+ * @param method the method for the request
+ * @param url the target url for the request
+ * @param headers extra header lines to include in the request
+ * @param send_content if non-null, the data to send as request body content
+ * @param send_content_length the length of the send_content data, or 0 if
+ *                            send_content is null
+ *
+ * @return zero if success, nonzero otherwise
+ */
+int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s,
+                                        const char *method, const char *url,
+                                        const char *headers,
+                                        const unsigned char *send_content,
+                                        int send_content_length);
+
 /**
  * Send a command to the RTSP server and wait for the reply.
  *
-- 
2.49.1


>From 7b48d5ae326ee7656f64e17fd84f9a7424e45ce4 Mon Sep 17 00:00:00 2001
From: Marvin Scholz <epira...@gmail.com>
Date: Wed, 27 Aug 2025 03:10:45 +0200
Subject: [PATCH 3/3] WIP: avformat/rtspdec: Add SET_PARAMETER command support

Initial SET_PARAMETER support, this is just a very basic and not complete
implementation to illustrate the possible usage.
---
 libavformat/avformat.h | 40 +++++++++++++++++++++++++++++++++++
 libavformat/rtspdec.c  | 48 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 88 insertions(+)

diff --git a/libavformat/avformat.h b/libavformat/avformat.h
index 3ba4c88ca6..44a1539c79 100644
--- a/libavformat/avformat.h
+++ b/libavformat/avformat.h
@@ -2378,6 +2378,46 @@ enum AVFormatCommandID {
     AVFORMAT_COMMAND_RTSP_SET_PARAMETER,
 };
 
+typedef struct AVRTSPCommandRequest {
+    /**
+     * Headers sent in the request to the server
+     */
+    AVDictionary *headers;
+
+    /**
+     * Body payload size
+     */
+    size_t body_len;
+
+    /**
+     * Body payload
+     */
+    char *body;
+} AVRTSPCommandRequest;
+
+typedef struct AVRTSPResponse {
+    /**
+     * Response status code from server
+     */
+    int status_code;
+
+    /**
+     * Reason phrase from the server, describing the
+     * status in a human-readable way.
+     */
+    char *reason;
+
+    /**
+     * Body payload size
+     */
+    size_t body_len;
+
+    /**
+     * Body payload
+     */
+    unsigned char *body;
+} AVRTSPResponse;
+
 /**
  * Send a command to the demuxer
  *
diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c
index 88a57b01ef..4b8ff4c570 100644
--- a/libavformat/rtspdec.c
+++ b/libavformat/rtspdec.c
@@ -611,6 +611,52 @@ static int rtsp_read_pause(AVFormatContext *s)
     return 0;
 }
 
+static int rtsp_handle_command(struct AVFormatContext *s, enum 
AVFormatCommandID id, void *data)
+{
+    // TODO: Actually handle the data payload...
+    if (id != AVFORMAT_COMMAND_RTSP_SET_PARAMETER)
+        return AVERROR(ENOTSUP);
+
+    RTSPState *rt = s->priv_data;
+    av_log(s, AV_LOG_INFO, "Sending SET_PARAMETER command to %s\n", 
rt->control_uri);
+
+    int ret = ff_rtsp_send_cmd_with_content_async(s, "SET_PARAMETER", 
rt->control_uri, NULL, NULL, 0);
+    if (ret != 0)
+        av_log(s, AV_LOG_ERROR, "Failure sending SET_PARAMETER command: %s\n", 
av_err2str(ret));
+
+    return ret;
+}
+
+static int rtsp_read_command_reply(AVFormatContext *s, enum AVFormatCommandID 
id, void **data_out)
+{
+    if (id != AVFORMAT_COMMAND_RTSP_SET_PARAMETER)
+        return AVERROR(ENOTSUP);
+
+    if (!data_out)
+        return AVERROR(EINVAL);
+
+    AVRTSPResponse *res = av_malloc(sizeof(*res));
+    if (!res)
+        return AVERROR(ENOMEM);
+
+    RTSPMessageHeader reply;
+    int ret = ff_rtsp_read_reply(s, &reply, &res->body, 0, "SET_PARAMETER");
+    if (ret < 0)
+        return ret;
+
+    res->status_code = reply.status_code;
+    res->body_len = reply.content_length;
+
+    res->reason = av_strdup(reply.reason);
+    if (!res->reason) {
+        av_free(res);
+        return AVERROR(ENOMEM);
+    }
+
+    *data_out = res;
+    return 0;
+}
+
 int ff_rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply)
 {
     RTSPState *rt = s->priv_data;
@@ -1007,4 +1053,6 @@ const FFInputFormat ff_rtsp_demuxer = {
     .read_seek      = rtsp_read_seek,
     .read_play      = rtsp_read_play,
     .read_pause     = rtsp_read_pause,
+    .handle_command = rtsp_handle_command,
+    .read_command_reply = rtsp_read_command_reply,
 };
-- 
2.49.1

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-le...@ffmpeg.org

Reply via email to