avformat/udp: replace packet_gap with bitrate option

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>
This commit is contained in:
Marton Balint 2016-06-10 02:32:21 +02:00
parent 0a06569487
commit cc16229d91
3 changed files with 41 additions and 21 deletions

View 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.

View 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 */

View 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, \