avformat/udp: replace packet_gap with bitrate option
authorMarton Balint <cus@passwd.hu>
Fri, 10 Jun 2016 00:32:21 +0000 (02:32 +0200)
committerMarton Balint <cus@passwd.hu>
Sun, 19 Jun 2016 20:55:57 +0000 (22:55 +0200)
We haven't had a stable release since the packet_gap addition, so probably it
is worth reworking the option to something that makes more sense to the end
user. Also add burst_bits option to specify maximum length of bit bursts.

Signed-off-by: Marton Balint <cus@passwd.hu>
doc/protocols.texi
libavformat/udp.c
libavformat/version.h

index a9c9d0c..72b3914 100644 (file)
@@ -1285,8 +1285,13 @@ Set the UDP maximum socket buffer size in bytes. This is used to set either
 the receive or send buffer size, depending on what the socket is used for.
 Default is 64KB.  See also @var{fifo_size}.
 
-@item packet_gap=@var{seconds}
-Delay between packets
+@item bitrate=@var{bitrate}
+If set to nonzero, the output will have the specified constant bitrate if the
+input has enough packets to sustain it.
+
+@item burst_bits=@var{bits}
+When using @var{bitrate} this specifies the maximum number of bits in
+packet bursts.
 
 @item localport=@var{port}
 Override the local UDP port to bind with.
index f2446c6..8699c1c 100644 (file)
@@ -93,7 +93,8 @@ typedef struct UDPContext {
     int circular_buffer_size;
     AVFifoBuffer *fifo;
     int circular_buffer_error;
-    int64_t packet_gap; /* delay between transmitted packets */
+    int64_t bitrate; /* number of bits to send per second */
+    int64_t burst_bits;
     int close_req;
 #if HAVE_PTHREAD_CANCEL
     pthread_t circular_buffer_thread;
@@ -115,7 +116,8 @@ typedef struct UDPContext {
 #define E AV_OPT_FLAG_ENCODING_PARAM
 static const AVOption options[] = {
     { "buffer_size",    "System data size (in bytes)",                     OFFSET(buffer_size),    AV_OPT_TYPE_INT,    { .i64 = -1 },    -1, INT_MAX, .flags = D|E },
-    { "packet_gap",     "Delay between packets",                           OFFSET(packet_gap),     AV_OPT_TYPE_DURATION,    { .i64 = 0  },     0, INT_MAX, .flags = E },
+    { "bitrate",        "Bits to send per second",                         OFFSET(bitrate),        AV_OPT_TYPE_INT64,  { .i64 = 0  },     0, INT64_MAX, .flags = E },
+    { "burst_bits",     "Max length of bursts in bits (when using bitrate)", OFFSET(burst_bits),   AV_OPT_TYPE_INT64,  { .i64 = 0  },     0, INT64_MAX, .flags = E },
     { "localport",      "Local port",                                      OFFSET(local_port),     AV_OPT_TYPE_INT,    { .i64 = -1 },    -1, INT_MAX, D|E },
     { "local_port",     "Local port",                                      OFFSET(local_port),     AV_OPT_TYPE_INT,    { .i64 = -1 },    -1, INT_MAX, .flags = D|E },
     { "localaddr",      "Local address",                                   OFFSET(localaddr),      AV_OPT_TYPE_STRING, { .str = NULL },               .flags = D|E },
@@ -552,7 +554,11 @@ static void *circular_buffer_task_tx( void *_URLContext)
     URLContext *h = _URLContext;
     UDPContext *s = h->priv_data;
     int old_cancelstate;
-    int64_t target_timestamp = 0;
+    int64_t target_timestamp = av_gettime_relative();
+    int64_t start_timestamp = av_gettime_relative();
+    int64_t sent_bits = 0;
+    int64_t burst_interval = s->bitrate ? (s->burst_bits * 1000000 / s->bitrate) : 0;
+    int64_t max_delay = s->bitrate ?  ((int64_t)h->max_packet_size * 8 * 1000000 / s->bitrate + 1) : 0;
 
     pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate);
     pthread_mutex_lock(&s->mutex);
@@ -591,15 +597,24 @@ static void *circular_buffer_task_tx( void *_URLContext)
         pthread_mutex_unlock(&s->mutex);
         pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelstate);
 
-        if (s->packet_gap) {
+        if (s->bitrate) {
             timestamp = av_gettime_relative();
             if (timestamp < target_timestamp) {
-                target_timestamp = FFMIN(target_timestamp, timestamp + s->packet_gap);
-                av_usleep(target_timestamp - timestamp);
+                int64_t delay = target_timestamp - timestamp;
+                if (delay > max_delay) {
+                    delay = max_delay;
+                    start_timestamp = timestamp + delay;
+                    sent_bits = 0;
+                }
+                av_usleep(delay);
             } else {
-                target_timestamp = timestamp;
+                if (timestamp - burst_interval > target_timestamp) {
+                    start_timestamp = timestamp - burst_interval;
+                    sent_bits = 0;
+                }
             }
-            target_timestamp += s->packet_gap;
+            sent_bits += len * 8;
+            target_timestamp = start_timestamp + sent_bits * 1000000 / s->bitrate;
         }
 
         p = s->tmp;
@@ -744,16 +759,16 @@ static int udp_open(URLContext *h, const char *uri, int flags)
                        "'circular_buffer_size' option was set but it is not supported "
                        "on this build (pthread support is required)\n");
         }
-        if (av_find_info_tag(buf, sizeof(buf), "packet_gap", p)) {
-            if (av_parse_time(&s->packet_gap, buf, 1)<0) {
-                av_log(h, AV_LOG_ERROR, "Can't parse 'packet_gap'");
-                goto fail;
-            }
+        if (av_find_info_tag(buf, sizeof(buf), "bitrate", p)) {
+            s->bitrate = strtoll(buf, NULL, 10);
             if (!HAVE_PTHREAD_CANCEL)
                 av_log(h, AV_LOG_WARNING,
-                       "'packet_gap' option was set but it is not supported "
+                       "'bitrate' option was set but it is not supported "
                        "on this build (pthread support is required)\n");
         }
+        if (av_find_info_tag(buf, sizeof(buf), "burst_bits", p)) {
+            s->burst_bits = strtoll(buf, NULL, 10);
+        }
         if (av_find_info_tag(buf, sizeof(buf), "localaddr", p)) {
             av_strlcpy(localaddr, buf, sizeof(localaddr));
         }
@@ -936,15 +951,15 @@ static int udp_open(URLContext *h, const char *uri, int flags)
     /*
       Create thread in case of:
       1. Input and circular_buffer_size is set
-      2. Output and packet_gap and circular_buffer_size is set
+      2. Output and bitrate and circular_buffer_size is set
     */
 
-    if (is_output && s->packet_gap && !s->circular_buffer_size) {
+    if (is_output && s->bitrate && !s->circular_buffer_size) {
         /* Warn user in case of 'circular_buffer_size' is not set */
-        av_log(h, AV_LOG_WARNING,"'packet_gap' option was set but 'circular_buffer_size' is not, but required\n");
+        av_log(h, AV_LOG_WARNING,"'bitrate' option was set but 'circular_buffer_size' is not, but required\n");
     }
 
-    if ((!is_output && s->circular_buffer_size) || (is_output && s->packet_gap && s->circular_buffer_size)) {
+    if ((!is_output && s->circular_buffer_size) || (is_output && s->bitrate && s->circular_buffer_size)) {
         int ret;
 
         /* start the task going */
index 3dc79ff..ce650d5 100644 (file)
@@ -33,7 +33,7 @@
 // Also please add any ticket numbers that you belive might regress here
 #define LIBAVFORMAT_VERSION_MAJOR  57
 #define LIBAVFORMAT_VERSION_MINOR  38
-#define LIBAVFORMAT_VERSION_MICRO 101
+#define LIBAVFORMAT_VERSION_MICRO 102
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \