diff mbox series

[FFmpeg-devel] Add RTSP range header

Message ID CAEU7NfN6j-aCnfQxT_twmkd5esvH5DObjwkyA-du2VQ7TjenxQ@mail.gmail.com
State New
Headers show
Series [FFmpeg-devel] Add RTSP range header
Related show

Checks

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

Commit Message

Yakov Okshtein Nov. 22, 2020, 4:21 p.m. UTC
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
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(-)

Comments

Yakov Okshtein Nov. 23, 2020, 1:51 a.m. UTC | #1
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);
Andriy Gelman Nov. 26, 2020, 5:09 p.m. UTC | #2
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 mbox series

Patch

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);