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;
#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 },
URLContext *h = _URLContext;
UDPContext *s = h->priv_data;
int old_cancelstate;
+ 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);
int len;
const uint8_t *p;
uint8_t tmp[4];
+ int64_t timestamp;
len=av_fifo_size(s->fifo);
pthread_mutex_unlock(&s->mutex);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelstate);
+ if (s->bitrate) {
+ timestamp = av_gettime_relative();
+ if (timestamp < target_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 {
+ if (timestamp - burst_interval > target_timestamp) {
+ start_timestamp = timestamp - burst_interval;
+ sent_bits = 0;
+ }
+ }
+ sent_bits += len * 8;
+ target_timestamp = start_timestamp + sent_bits * 1000000 / s->bitrate;
+ }
+
p = s->tmp;
while (len) {
int ret;
}
}
- av_usleep(s->packet_gap);
-
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate);
pthread_mutex_lock(&s->mutex);
}
"'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));
}
/*
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 */