This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit b63f69fccfab672fbabdab47ad21b1805aab0d1d Author: Marvin Scholz <[email protected]> AuthorDate: Fri Oct 10 17:53:23 2025 +0200 Commit: Marvin Scholz <[email protected]> CommitDate: Thu Feb 19 17:18:12 2026 +0100 avformat: rtsp: add functions to allow stored replies This adds the ability to have a stored reply, needed for the SET_PARAMETERS command feature to reliably report the reply even while intermediate packets are read. --- libavformat/rtsp.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++------- libavformat/rtsp.h | 65 +++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 15 deletions(-) diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 94a2f34141..1362cb683b 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -801,6 +801,7 @@ void ff_rtsp_undo_setup(AVFormatContext *s, int send_packets) RTSPState *rt = s->priv_data; int i; + rt->stored_msg.expected_seq = -1; for (i = 0; i < rt->nb_rtsp_streams; i++) { RTSPStream *rtsp_st = rt->rtsp_streams[i]; if (!rtsp_st) @@ -1222,9 +1223,11 @@ int ff_rtsp_skip_packet(AVFormatContext *s) return 0; } -int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, - unsigned char **content_ptr, - int return_on_interleaved_data, const char *method) +static int ff_rtsp_read_reply_internal(AVFormatContext *s, + RTSPMessageHeader *reply, + unsigned char **content_ptr, + int return_on_interleaved_data, + const char *method) { RTSPState *rt = s->priv_data; char buf[MAX_URL_SIZE], buf1[MAX_URL_SIZE], *q; @@ -1233,18 +1236,6 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, int ret, content_length, line_count, request; unsigned char *content; - // If we returned on pending packet last time, - // do not try to read again, as it would corrupt - // the state due to the already consumed '$'. - if (rt->pending_packet) { - if (return_on_interleaved_data) - return 1; - - ret = ff_rtsp_skip_packet(s); - if (ret < 0) - return ret; - } - start: line_count = 0; request = 0; @@ -1386,6 +1377,56 @@ start: return 0; } +int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, + unsigned char **content_ptr, + int return_on_interleaved_data, const char *method) +{ + int ret; + RTSPState *rt = s->priv_data; + + // If we returned on pending packet last time, + // do not try to read again, as it would corrupt + // the state due to the already consumed '$'. + if (rt->pending_packet) { + if (return_on_interleaved_data) + return 1; + + ret = ff_rtsp_skip_packet(s); + if (ret < 0) + return ret; + } + + if (rt->stored_msg.expected_seq != -1) { + RTSPMessageHeader header; + + ret = ff_rtsp_read_reply_internal(s, &header, + &rt->stored_msg.body, return_on_interleaved_data, NULL); + if (ret != 0) + return ret; + + if (rt->stored_msg.expected_seq == header.seq) { + // Got the expected reply, store it for later + rt->stored_msg.expected_seq = -1; + rt->stored_msg.header = av_calloc(1, sizeof(*rt->stored_msg.header)); + if (!rt->stored_msg.header) { + av_freep(&rt->stored_msg.body); + return AVERROR(ENOMEM); + } + memcpy(rt->stored_msg.header, &header, sizeof(header)); + } else { + av_log(s, AV_LOG_WARNING, "Unexpected reply with seq %d, expected %d\n", + rt->stored_msg.header->seq, rt->stored_msg.expected_seq); + av_freep(&rt->stored_msg.body); + } + + // Do not return here as we still need to read the reply + // the caller was actually wanting to retrieve. + } + + return ff_rtsp_read_reply_internal(s, reply, content_ptr, + return_on_interleaved_data, method); +} + /** * Send a command to the RTSP server without waiting for the reply. * @@ -1456,6 +1497,24 @@ int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s, return 0; } +int ff_rtsp_send_cmd_with_content_async_stored(AVFormatContext *s, + const char *method, const char *url, + const char *headers, + const unsigned char *send_content, + int send_content_length) +{ + RTSPState *rt = s->priv_data; + int ret = ff_rtsp_send_cmd_with_content_async(s, method, url, headers, + send_content, send_content_length); + if (ret < 0) + return ret; + + rt->stored_msg.expected_seq = rt->seq; + av_freep(&rt->stored_msg.header); + av_freep(&rt->stored_msg.body); + return 0; +} + int ff_rtsp_send_cmd_async(AVFormatContext *s, const char *method, const char *url, const char *headers) { @@ -1509,6 +1568,33 @@ retry: return 0; } +int ff_rtsp_read_reply_async_stored(AVFormatContext *s, RTSPMessageHeader **reply, + unsigned char **content_ptr) +{ + RTSPState *rt = s->priv_data; + if (rt->stored_msg.header == NULL) { + if (rt->stored_msg.expected_seq == -1) + return AVERROR(EINVAL); // Reply to be stored was never requested + + // Reply pending, tell caller to try again later + return AVERROR(EAGAIN); + } + + if (reply) + *reply = rt->stored_msg.header; + else + av_free(rt->stored_msg.header); + + if (content_ptr) + *content_ptr = rt->stored_msg.body; + else + av_free(rt->stored_msg.body); + + rt->stored_msg.header = NULL; + rt->stored_msg.body = NULL; + return 0; +} + int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, int lower_transport, const char *real_challenge) { @@ -1796,6 +1882,7 @@ int ff_rtsp_connect(AVFormatContext *s) struct sockaddr_storage peer; socklen_t peer_len = sizeof(peer); + rt->stored_msg.expected_seq = -1; if (rt->rtp_port_max < rt->rtp_port_min) { av_log(s, AV_LOG_ERROR, "Invalid UDP port range, max port %d less " "than min port %d\n", rt->rtp_port_max, diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h index 9b2ceff996..3c9c2c842d 100644 --- a/libavformat/rtsp.h +++ b/libavformat/rtsp.h @@ -286,6 +286,25 @@ typedef struct RTSPState { /** The last reply of the server to a RTSP command */ char last_reply[2048]; /* XXX: allocate ? */ + /** + * Stored message context + * This is used to store the last reply marked to be + * stored with ::ff_rtsp_send_cmd_with_content_async_stored + * as well as accompanying state to know when to store + * a reply and if a reply has been stored yet. + */ + struct { + /** + * Sequence number of the reply to be stored + * -1 if we are not waiting to store any message + */ + int expected_seq; + /** Last stored reply message from the RTSP server */ + RTSPMessageHeader *header; + /** Last stored reply message body from the RTSP server */ + unsigned char *body; + } stored_msg; + /** Indicates if a packet is pending to be read (useful for interleaved reads) */ int pending_packet; @@ -530,6 +549,30 @@ int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s, const unsigned char *send_content, int send_content_length); +/** + * Send a command to the RTSP server, storing the reply on future reads + * + * Sends a command to the server, without waiting for the reply and + * marking the request as awaiting a response, which will be stored + * when it is encountered during future read operations and should + * be retrieved with ::ff_rtsp_read_reply_async_stored. + * + * @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_stored(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. * @@ -590,6 +633,28 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, unsigned char **content_ptr, int return_on_interleaved_data, const char *method); +/** + * Retrieve a previously stored RTSP reply message from the server. + * + * Retrieves a reply for a message sent with + * ::ff_rtsp_send_cmd_with_content_async_stored previously. + * If more than one message was received, this function will only + * return the last one and intermediate messages are discarded. + * + * Both reply and content must be ::av_free'd by the caller. + * + * @param s RTSP (de)muxer context + * @param reply Pointer where the RTSP message header will be stored + * @param content_ptr Pointer where the RTSP message body, if any, will + * be stored (length is in reply) + * + * @return 0 on success, AVERROR(EAGAIN) if no reply was received yet, + * other AVERROR for any other errors. + */ +int ff_rtsp_read_reply_async_stored(AVFormatContext *s, RTSPMessageHeader **reply, + unsigned char **content_ptr); + + /** * Skip a RTP/TCP interleaved packet. * _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
