Message ID | CAEU7NfN6j-aCnfQxT_twmkd5esvH5DObjwkyA-du2VQ7TjenxQ@mail.gmail.com |
---|---|
State | New |
Headers | show |
Series | [FFmpeg-devel] Add RTSP range header | expand |
Context | Check | Description |
---|---|---|
andriy/x86_make | success | Make finished |
andriy/x86_make_fate | success | Make fate finished |
andriy/PPC64_make | success | Make finished |
andriy/PPC64_make_fate | success | Make fate finished |
Whoops, looks like Gmail sent the .patch file attachment as application/octet-stream ... I must say, the ffmpeg binary is much more lenient in its input than the ffmpeg-devel mailing list :) Let me try again with Thunderbird, and a .txt file. Thanks all, Yakov On 11/22/20 11:21 AM, Yakov Okshtein wrote: > Hi all, > > While working to get historical video off a DVR, I found the lack of a > customizable Range: header for RTSP video frustrating (as, apparently, > have others on StackOverflow and the Trac wiki, #6659). One user > mentionedhaving made a custom patch, but didn't post it; so, I decided > to make one. > > It's my first submission to ffmpeg. I read the submission guidelines > and hope I didn't miss anything. > > Thanks all! > > Yakov From 1c0a5ce96eef83bb3ed55a4dfa6e825668055cdb Mon Sep 17 00:00:00 2001 From: Yakov Okshtein <flyashi@gmail.com> Date: Sun, 22 Nov 2020 16:06:46 +0000 Subject: [PATCH] Add optional Range and Rate-Control header to RTSP PLAY command Some DVR/NVRs send historical video specified by the Range: header, and can send files faster than real-time by the presence of the Rate-Control: no header. This patch adds optional -range and -rate_control CLI arguments, and if present, uses those values to override the defaults. Closes #6659 --- libavformat/rtsp.c | 2 ++ libavformat/rtsp.h | 10 ++++++++++ libavformat/rtspdec.c | 15 +++++++++++---- libavformat/rtspenc.c | 9 +++++++-- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 0be405aba1..ca0b92251c 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -101,6 +101,8 @@ const AVOption ff_rtsp_options[] = { #endif COMMON_OPTS(), { "user_agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC }, + { "range", "override Range header", OFFSET(range), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC }, + { "rate_control", "override Rate-Control header", OFFSET(rate_control), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC }, #if FF_API_OLD_RTSP_OPTIONS { "user-agent", "override User-Agent header (deprecated, use user_agent)", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC }, #endif diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h index 251ed86d19..0778515045 100644 --- a/libavformat/rtsp.h +++ b/libavformat/rtsp.h @@ -409,6 +409,16 @@ typedef struct RTSPState { */ char *user_agent; + /** + * Range header override; string. + */ + char *range; + + /** + * Rate-Control header; optional string. + */ + char *rate_control; + char default_lang[4]; int buffer_size; int pkt_size; diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c index 28b35d1993..0a0de21b82 100644 --- a/libavformat/rtspdec.c +++ b/libavformat/rtspdec.c @@ -541,11 +541,18 @@ static int rtsp_read_play(AVFormatContext *s) if (rt->state == RTSP_STATE_PAUSED) { cmd[0] = 0; } else { - snprintf(cmd, sizeof(cmd), - "Range: npt=%"PRId64".%03"PRId64"-\r\n", - rt->seek_timestamp / AV_TIME_BASE, - rt->seek_timestamp / (AV_TIME_BASE / 1000) % 1000); + if (rt->range != NULL) { + snprintf(cmd, sizeof(cmd), "Range: %s\r\n", rt->range); + } else { + snprintf(cmd, sizeof(cmd), + "Range: npt=%"PRId64".%03"PRId64"-\r\n", + rt->seek_timestamp / AV_TIME_BASE, + rt->seek_timestamp / (AV_TIME_BASE / 1000) % 1000); + } } + if (rt->rate_control != NULL) { + snprintf(cmd, sizeof(cmd), "Rate-Control: %s\r\n", rt->rate_control); + } ff_rtsp_send_cmd(s, "PLAY", rt->control_uri, cmd, reply, NULL); if (reply->status_code != RTSP_STATUS_OK) { return ff_rtsp_averror(reply->status_code, -1); diff --git a/libavformat/rtspenc.c b/libavformat/rtspenc.c index d50544456d..1b7281cc67 100644 --- a/libavformat/rtspenc.c +++ b/libavformat/rtspenc.c @@ -113,8 +113,13 @@ static int rtsp_write_record(AVFormatContext *s) RTSPMessageHeader reply1, *reply = &reply1; char cmd[1024]; - snprintf(cmd, sizeof(cmd), - "Range: npt=0.000-\r\n"); + if (rt->range != NULL) { + snprintf(cmd, sizeof(cmd), + "Range: %s\r\n", rt->range); + } else { + snprintf(cmd, sizeof(cmd), + "Range: npt=0.0000-\r\n"); + } ff_rtsp_send_cmd(s, "RECORD", rt->control_uri, cmd, reply, NULL); if (reply->status_code != RTSP_STATUS_OK) return ff_rtsp_averror(reply->status_code, -1);
Hi, On Sun, 22. Nov 11:21, Yakov Okshtein wrote: > Hi all, > > While working to get historical video off a DVR, I found the lack of a > customizable Range: header for RTSP video frustrating (as, apparently, have > others on StackOverflow and the Trac wiki, #6659). One user mentionedhaving > made a custom patch, but didn't post it; so, I decided to make one. When fetching the stream, have you tried to use -ss for seeking? Is the rate-control option only supported by the onvif cameras? https://www.onvif.org/specs/stream/ONVIF-Streaming-Spec.pdf If you want to add both options, they should be separate patches. And you'll need to add documentation in doc/protocols.texi > diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c > index 0be405aba1..ca0b92251c 100644 > --- a/libavformat/rtsp.c > +++ b/libavformat/rtsp.c > @@ -101,6 +101,8 @@ const AVOption ff_rtsp_options[] = { > #endif > COMMON_OPTS(), > { "user_agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC }, > + { "range", "override Range header", OFFSET(range), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC }, I don't think you meant LIBAVFORMAT_IDENT to be the default (same for rate_control). This would break seeking for regular streams. > + { "rate_control", "override Rate-Control header", OFFSET(rate_control), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC }, > #if FF_API_OLD_RTSP_OPTIONS > { "user-agent", "override User-Agent header (deprecated, use user_agent)", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC }, > #endif > diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h > index 251ed86d19..0778515045 100644 > --- a/libavformat/rtsp.h > +++ b/libavformat/rtsp.h > @@ -409,6 +409,16 @@ typedef struct RTSPState { > */ > char *user_agent; > > + /** > + * Range header override; string. > + */ > + char *range; > + > + /** > + * Rate-Control header; optional string. > + */ > + char *rate_control; > + > char default_lang[4]; > int buffer_size; > int pkt_size; > diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c > index 28b35d1993..0a0de21b82 100644 > --- a/libavformat/rtspdec.c > +++ b/libavformat/rtspdec.c > @@ -541,11 +541,18 @@ static int rtsp_read_play(AVFormatContext *s) > if (rt->state == RTSP_STATE_PAUSED) { > cmd[0] = 0; > } else { > - snprintf(cmd, sizeof(cmd), > - "Range: npt=%"PRId64".%03"PRId64"-\r\n", > - rt->seek_timestamp / AV_TIME_BASE, > - rt->seek_timestamp / (AV_TIME_BASE / 1000) % 1000); > + if (rt->range != NULL) { > + snprintf(cmd, sizeof(cmd), "Range: %s\r\n", rt->range); > + } else { > + snprintf(cmd, sizeof(cmd), > + "Range: npt=%"PRId64".%03"PRId64"-\r\n", > + rt->seek_timestamp / AV_TIME_BASE, > + rt->seek_timestamp / (AV_TIME_BASE / 1000) % 1000); > + } > } > + if (rt->rate_control != NULL) { > + snprintf(cmd, sizeof(cmd), "Rate-Control: %s\r\n", rt->rate_control); > + } Your rate-control header is just going to overwrite your range header. > ff_rtsp_send_cmd(s, "PLAY", rt->control_uri, cmd, reply, NULL); > if (reply->status_code != RTSP_STATUS_OK) { > return ff_rtsp_averror(reply->status_code, -1); > diff --git a/libavformat/rtspenc.c b/libavformat/rtspenc.c > index d50544456d..1b7281cc67 100644 > --- a/libavformat/rtspenc.c > +++ b/libavformat/rtspenc.c > @@ -113,8 +113,13 @@ static int rtsp_write_record(AVFormatContext *s) > RTSPMessageHeader reply1, *reply = &reply1; > char cmd[1024]; > > - snprintf(cmd, sizeof(cmd), > - "Range: npt=0.000-\r\n"); > + if (rt->range != NULL) { > + snprintf(cmd, sizeof(cmd), > + "Range: %s\r\n", rt->range); > + } else { > + snprintf(cmd, sizeof(cmd), > + "Range: npt=0.0000-\r\n"); > + } > ff_rtsp_send_cmd(s, "RECORD", rt->control_uri, cmd, reply, NULL); > if (reply->status_code != RTSP_STATUS_OK) > return ff_rtsp_averror(reply->status_code, -1);
diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 0be405aba1..ca0b92251c 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -101,6 +101,8 @@ const AVOption ff_rtsp_options[] = { #endif COMMON_OPTS(), { "user_agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC }, + { "range", "override Range header", OFFSET(range), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC }, + { "rate_control", "override Rate-Control header", OFFSET(rate_control), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC }, #if FF_API_OLD_RTSP_OPTIONS { "user-agent", "override User-Agent header (deprecated, use user_agent)", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC }, #endif diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h index 251ed86d19..0778515045 100644 --- a/libavformat/rtsp.h +++ b/libavformat/rtsp.h @@ -409,6 +409,16 @@ typedef struct RTSPState { */ char *user_agent; + /** + * Range header override; string. + */ + char *range; + + /** + * Rate-Control header; optional string. + */ + char *rate_control; + char default_lang[4]; int buffer_size; int pkt_size; diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c index 28b35d1993..0a0de21b82 100644 --- a/libavformat/rtspdec.c +++ b/libavformat/rtspdec.c @@ -541,11 +541,18 @@ static int rtsp_read_play(AVFormatContext *s) if (rt->state == RTSP_STATE_PAUSED) { cmd[0] = 0; } else { - snprintf(cmd, sizeof(cmd), - "Range: npt=%"PRId64".%03"PRId64"-\r\n", - rt->seek_timestamp / AV_TIME_BASE, - rt->seek_timestamp / (AV_TIME_BASE / 1000) % 1000); + if (rt->range != NULL) { + snprintf(cmd, sizeof(cmd), "Range: %s\r\n", rt->range); + } else { + snprintf(cmd, sizeof(cmd), + "Range: npt=%"PRId64".%03"PRId64"-\r\n", + rt->seek_timestamp / AV_TIME_BASE, + rt->seek_timestamp / (AV_TIME_BASE / 1000) % 1000); + } } + if (rt->rate_control != NULL) { + snprintf(cmd, sizeof(cmd), "Rate-Control: %s\r\n", rt->rate_control); + } ff_rtsp_send_cmd(s, "PLAY", rt->control_uri, cmd, reply, NULL); if (reply->status_code != RTSP_STATUS_OK) { return ff_rtsp_averror(reply->status_code, -1); diff --git a/libavformat/rtspenc.c b/libavformat/rtspenc.c index d50544456d..1b7281cc67 100644 --- a/libavformat/rtspenc.c +++ b/libavformat/rtspenc.c @@ -113,8 +113,13 @@ static int rtsp_write_record(AVFormatContext *s) RTSPMessageHeader reply1, *reply = &reply1; char cmd[1024]; - snprintf(cmd, sizeof(cmd), - "Range: npt=0.000-\r\n"); + if (rt->range != NULL) { + snprintf(cmd, sizeof(cmd), + "Range: %s\r\n", rt->range); + } else { + snprintf(cmd, sizeof(cmd), + "Range: npt=0.0000-\r\n"); + } ff_rtsp_send_cmd(s, "RECORD", rt->control_uri, cmd, reply, NULL); if (reply->status_code != RTSP_STATUS_OK) return ff_rtsp_averror(reply->status_code, -1);