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