Commit 68bce248 authored by Sylvain Berfini's avatar Sylvain Berfini 🎩

Final version of the video bandwidth estimator

parent 57e12e61
......@@ -52,6 +52,7 @@ struct _OrtpEventData{
OrtpSocketType socket_type;
uint32_t received_rtt_character;
bool_t congestion_detected;
int video_bandwidth_available;
} info;
};
......@@ -80,6 +81,7 @@ ORTP_PUBLIC OrtpEventType ortp_event_get_type(const OrtpEvent *ev);
#define ORTP_EVENT_DTLS_ENCRYPTION_CHANGED 13
#define ORTP_EVENT_RTT_CHARACTER_RECEIVED 15
#define ORTP_EVENT_CONGESTION_STATE_CHANGED 16
#define ORTP_EVENT_NEW_VIDEO_BANDWIDTH_ESTIMATION_AVAILABLE 17
ORTP_PUBLIC OrtpEventData * ortp_event_get_data(OrtpEvent *ev);
ORTP_PUBLIC void ortp_event_destroy(OrtpEvent *ev);
......
......@@ -352,6 +352,7 @@ typedef struct _RtpStream
int ssrc_changed_thres;
jitter_stats_t jitter_stats;
struct _OrtpCongestionDetector *congdetect;
struct _OrtpVideoBandwidthEstimator *video_bw_estimator;
}RtpStream;
typedef struct _RtcpStream
......@@ -445,6 +446,7 @@ struct _RtpSession
bool_t is_spliced;
bool_t congestion_detector_enabled;
bool_t video_bandwidth_estimator_enabled;
};
......@@ -683,6 +685,7 @@ ORTP_PUBLIC float rtp_session_get_round_trip_propagation(RtpSession *session);
ORTP_PUBLIC void rtp_session_enable_network_simulation(RtpSession *session, const OrtpNetworkSimulatorParams *params);
ORTP_PUBLIC void rtp_session_enable_congestion_detection(RtpSession *session, bool_t enabled);
ORTP_PUBLIC void rtp_session_enable_video_bandwidth_estimator(RtpSession *session, bool_t enabled);
ORTP_PUBLIC void rtp_session_rtcp_set_lost_packet_value( RtpSession *session, const int value );
ORTP_PUBLIC void rtp_session_rtcp_set_jitter_value(RtpSession *session, const unsigned int value );
......
......@@ -59,6 +59,7 @@ set(ORTP_SOURCE_FILES_C
sessionset.c
str_utils.c
telephonyevents.c
videobandwidthestimator.c
utils.c
)
set(ORTP_SOURCE_FILES_CXX )
......
......@@ -47,6 +47,7 @@ libortp_la_SOURCES= \
sessionset.c \
str_utils.c \
telephonyevents.c \
videobandwidthestimator.c videobandwidthestimator.h\
utils.c utils.h
libortp_la_LIBADD= $(PTHREAD_LIBS) $(RT_LIBS) -lm $(BCTOOLBOX_LIBS)
......
......@@ -81,7 +81,6 @@ OrtpCongestionDetector * ortp_congestion_detector_new(RtpSession *session) {
OrtpCongestionDetector *cd = (OrtpCongestionDetector*)ortp_malloc0(sizeof(OrtpCongestionDetector));
cd->session = session;
ortp_congestion_detector_reset(cd);
ortp_congestion_detector_setup_video_bandwidth_detector(cd, 5, 100, 95);
return cd;
}
......@@ -215,88 +214,4 @@ bool_t ortp_congestion_detector_record(OrtpCongestionDetector *cd, uint32_t pack
void ortp_congestion_detector_destroy(OrtpCongestionDetector *obj){
ortp_free(obj);
}
void ortp_congestion_detector_setup_video_bandwidth_detector(OrtpCongestionDetector *cd, int count, int size_max, int trust) {
cd->vbd = (OrtpVideoBandwidthDetector*)ortp_malloc0(sizeof(OrtpVideoBandwidthDetector));
cd->vbd->packet_count_min = count;
cd->vbd->packets_size_max = size_max;
cd->vbd->trust_percentage = trust;
cd->vbd->packets = NULL;
}
static void compute_bitrate_add_to_list_and_remove_oldest_value(OrtpVideoBandwidthDetector *vbd, OrtpVideoBandwidthDetectorPacket *packet) {
float difftime = (float)(packet->recv_last_timestamp.tv_sec - packet->recv_first_timestamp.tv_sec)
+ 1e-6*(packet->recv_last_timestamp.tv_usec - packet->recv_first_timestamp.tv_usec);
packet->bitrate = (packet->bytes / difftime) / 1000; // bytes per seconds
ortp_debug("[VBD] Bitrate is %f computed using %f timedif and %u size", packet->bitrate, difftime, packet->bytes);
vbd->packets = bctbx_list_prepend(vbd->packets, packet);
if (bctbx_list_size(vbd->packets) > vbd->packets_size_max) {
void *old_data = bctbx_list_nth_data(vbd->packets, vbd->packets_size_max);
vbd->packets = bctbx_list_remove(vbd->packets, old_data);
}
}
void ortp_video_bandwidth_detector_process_packet(OrtpVideoBandwidthDetector *vbd, uint32_t sent_timestamp, const struct timeval *recv_timestamp, int msgsize, bool_t is_last) {
OrtpVideoBandwidthDetectorPacket *last_packet = vbd->last_packet;
OrtpVideoBandwidthDetectorPacket *current_packet = NULL;
if (last_packet) {
if (last_packet->sent_timestamp == sent_timestamp) {
current_packet = last_packet;
current_packet->count += 1;
current_packet->bytes += msgsize;
current_packet->recv_last_timestamp.tv_sec = recv_timestamp->tv_sec;
current_packet->recv_last_timestamp.tv_usec = recv_timestamp->tv_usec;
if (is_last && current_packet->count >= vbd->packet_count_min) {
compute_bitrate_add_to_list_and_remove_oldest_value(vbd, current_packet);
vbd->last_packet = NULL;
}
} else {
// This can happen even if it's probability is quite low
if (last_packet->count >= vbd->packet_count_min) {
ortp_warning("[VBD] Last packet not complete but enough packet received (%u), add to packets list", last_packet->count);
compute_bitrate_add_to_list_and_remove_oldest_value(vbd, last_packet);
} else {
ortp_free(vbd->last_packet);
}
vbd->last_packet = NULL;
}
}
if (!current_packet) {
current_packet = (OrtpVideoBandwidthDetectorPacket*)ortp_malloc0(sizeof(OrtpVideoBandwidthDetectorPacket));
current_packet->count = 1;
current_packet->bytes = msgsize;
current_packet->sent_timestamp = sent_timestamp;
current_packet->recv_first_timestamp.tv_sec = recv_timestamp->tv_sec;
current_packet->recv_first_timestamp.tv_usec = recv_timestamp->tv_usec;
vbd->last_packet = current_packet;
}
}
static int compare_float(const float *v1, const float *v2) {
if (*v1 == *v2) return 0;
if (*v1 < *v2) return 1;
return -1;
}
int ortp_video_bandwidth_detector_get_estimated_available_bandwidth(OrtpVideoBandwidthDetector *vbd) {
if (bctbx_list_size(vbd->packets) >= vbd->packets_size_max) {
bctbx_list_t *bitrate_sorted = NULL;
bctbx_list_t *elem;
float *result = NULL;
int index = (int)(vbd->trust_percentage * vbd->packets_size_max / 100);
for(elem = vbd->packets; elem != NULL; elem = bctbx_list_next(elem)) {
OrtpVideoBandwidthDetectorPacket *packet = (OrtpVideoBandwidthDetectorPacket *)bctbx_list_get_data(elem);
bitrate_sorted = bctbx_list_insert_sorted(bitrate_sorted, &packet->bitrate, (bctbx_compare_func)compare_float);
}
result = (float *)bctbx_list_nth_data(bitrate_sorted, index);
bctbx_list_free(bitrate_sorted);
return (int)*result;
}
return -1;
}
\ No newline at end of file
......@@ -44,27 +44,8 @@ typedef struct _OrtpCongestionDetector{
bool_t too_much_loss;
OrtpCongestionState state;
struct _RtpSession *session;
struct _OrtpVideoBandwidthDetector *vbd;
}OrtpCongestionDetector;
typedef struct _OrtpVideoBandwidthDetectorPacket{
unsigned int index;
uint32_t sent_timestamp;
struct timeval recv_first_timestamp;
struct timeval recv_last_timestamp;
unsigned int bytes;
unsigned int count;
float bitrate;
}OrtpVideoBandwidthDetectorPacket;
typedef struct _OrtpVideoBandwidthDetector{
unsigned int packet_count_min;
unsigned int packets_size_max;
unsigned int trust_percentage;
OrtpVideoBandwidthDetectorPacket *last_packet;
bctbx_list_t *packets;
}OrtpVideoBandwidthDetector;
OrtpCongestionDetector * ortp_congestion_detector_new(struct _RtpSession *session);
/*
......@@ -76,9 +57,4 @@ void ortp_congestion_detector_destroy(OrtpCongestionDetector *obj);
void ortp_congestion_detector_reset(OrtpCongestionDetector *cd);
void ortp_congestion_detector_setup_video_bandwidth_detector(OrtpCongestionDetector *cd, int count, int size_max, int trust);
void ortp_video_bandwidth_detector_process_packet(OrtpVideoBandwidthDetector *vbd, uint32_t sent_timestamp, const struct timeval *recv_timestamp, int msgsize, bool_t is_last);
int ortp_video_bandwidth_detector_get_estimated_available_bandwidth(OrtpVideoBandwidthDetector *vbd);
#endif
......@@ -27,6 +27,7 @@
#include "utils.h"
#include "rtpsession_priv.h"
#include "congestiondetector.h"
#include "videobandwidthestimator.h"
static bool_t queue_packet(queue_t *q, int maxrqsz, mblk_t *mp, rtp_header_t *rtp, int *discarded, int *duplicate)
{
......@@ -291,7 +292,11 @@ void rtp_session_rtp_parse(RtpSession *session, mblk_t *mp, uint32_t local_str_t
ed->info.congestion_detected = session->rtp.congdetect->state == CongestionStateDetected;
rtp_session_dispatch_event(session,ev);
}
ortp_video_bandwidth_detector_process_packet(session->rtp.congdetect->vbd, rtp->timestamp, &mp->timestamp, msgsize, rtp->markbit == 1);
}
if (session->video_bandwidth_estimator_enabled && session->rtp.video_bw_estimator) {
int overhead = ortp_stream_is_ipv6(&session->rtp.gs) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;
ortp_video_bandwidth_estimator_process_packet(session->rtp.video_bw_estimator, rtp->timestamp, &mp->timestamp, msgsize + overhead, rtp->markbit == 1);
}
update_rtcp_xr_stat_summary(session, mp, local_str_ts);
......
......@@ -30,6 +30,7 @@
#include "utils.h"
#include "rtpsession_priv.h"
#include "congestiondetector.h"
#include "videobandwidthestimator.h"
#if (_WIN32_WINNT >= 0x0600)
#include <delayimp.h>
......@@ -326,6 +327,17 @@ void rtp_session_enable_congestion_detection(RtpSession *session, bool_t enabled
session->congestion_detector_enabled = enabled;
}
void rtp_session_enable_video_bandwidth_estimator(RtpSession *session, bool_t enabled){
if (enabled){
if (!session->rtp.video_bw_estimator){
session->rtp.video_bw_estimator = ortp_video_bandwidth_estimator_new(session);
}else{
if (!session->video_bandwidth_estimator_enabled) ortp_video_bandwidth_estimator_reset(session->rtp.video_bw_estimator);
}
}
session->video_bandwidth_estimator_enabled = enabled;
}
void jb_parameters_init(JBParameters *jbp) {
/* configure jitter buffer with working default parameters */
jbp->min_size=RTP_DEFAULT_JITTER_TIME;
......@@ -1580,6 +1592,10 @@ void rtp_session_uninit (RtpSession * session)
ortp_congestion_detector_destroy(session->rtp.congdetect);
}
if (session->rtp.video_bw_estimator){
ortp_video_bandwidth_estimator_destroy(session->rtp.video_bw_estimator);
}
rtp_session_get_transports(session,&rtp_meta_transport,&rtcp_meta_transport);
if (rtp_meta_transport)
meta_rtp_transport_destroy(rtp_meta_transport);
......@@ -1628,6 +1644,7 @@ void rtp_session_resync(RtpSession *session){
rtp_session_unset_flag(session,RTP_SESSION_FIRST_PACKET_DELIVERED);
rtp_session_init_jitter_buffer(session);
if (session->rtp.congdetect) ortp_congestion_detector_reset(session->rtp.congdetect);
if (session->rtp.video_bw_estimator) ortp_video_bandwidth_estimator_reset(session->rtp.video_bw_estimator);
/* Since multiple streams might share the same session (fixed RTCP port for example),
RTCP values might be erroneous (number of packets received is computed
......
/*
* The oRTP library is an RTP (Realtime Transport Protocol - rfc3550) implementation with additional features.
* Copyright (C) 2017 Belledonne Communications SARL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "videobandwidthestimator.h"
#include <ortp/logging.h>
#include <math.h>
#include <ortp/rtpsession.h>
OrtpVideoBandwidthEstimator * ortp_video_bandwidth_estimator_new(RtpSession *session) {
OrtpVideoBandwidthEstimator *vbe = (OrtpVideoBandwidthEstimator*)ortp_malloc0(sizeof(OrtpVideoBandwidthEstimator));
vbe->session = session;
vbe->packet_count_min = 7;
vbe->packets_size_max = 30;
vbe->trust_percentage = 90;
vbe->nb_packets_computed = 0;
vbe->packets = NULL;
vbe->last_packet = NULL;
return vbe;
}
void ortp_video_bandwidth_estimator_destroy(OrtpVideoBandwidthEstimator *vbe){
ortp_video_bandwidth_estimator_reset(vbe);
ortp_free(vbe);
}
void ortp_video_bandwidth_estimator_reset(OrtpVideoBandwidthEstimator *vbe) {
ortp_free(vbe->last_packet);
vbe->last_packet = NULL;
vbe->nb_packets_computed = 0;
vbe->packets = bctbx_list_free_with_data(vbe->packets, ortp_free);
}
void ortp_video_bandwidth_estimator_set_packets_count_min(OrtpVideoBandwidthEstimator *vbe, unsigned int value) {
vbe->packet_count_min = value;
}
void ortp_video_bandwidth_estimator_set_history_max_size(OrtpVideoBandwidthEstimator *vbe, unsigned int value) {
vbe->packets_size_max = value;
}
void ortp_video_bandwidth_estimator_set_trust(OrtpVideoBandwidthEstimator *vbe, unsigned int value) {
vbe->trust_percentage = value;
}
unsigned int ortp_video_bandwidth_estimator_get_packets_count_min(OrtpVideoBandwidthEstimator *vbe) {
return vbe->packet_count_min;
}
unsigned int ortp_video_bandwidth_estimator_get_history_max_size(OrtpVideoBandwidthEstimator *vbe) {
return vbe->packets_size_max;
}
unsigned int ortp_video_bandwidth_estimator_get_trust(OrtpVideoBandwidthEstimator *vbe) {
return vbe->trust_percentage;
}
static int compare_float(const float *v1, const float *v2) {
if (*v1 == *v2) return 0;
if (*v1 < *v2) return 1;
return -1;
}
int ortp_video_bandwidth_estimator_get_estimated_available_bandwidth(OrtpVideoBandwidthEstimator *vbe) {
bctbx_list_t *bitrate_sorted = NULL;
bctbx_list_t *elem;
float *result = NULL;
int index = (int)(vbe->trust_percentage * vbe->packets_size_max / 100);
for(elem = vbe->packets; elem != NULL; elem = bctbx_list_next(elem)) {
OrtpVideoBandwidthEstimatorPacket *packet = (OrtpVideoBandwidthEstimatorPacket *)bctbx_list_get_data(elem);
bitrate_sorted = bctbx_list_insert_sorted(bitrate_sorted, &packet->bitrate, (bctbx_compare_func)compare_float);
}
result = (float *)bctbx_list_nth_data(bitrate_sorted, index);
bctbx_list_free(bitrate_sorted);
return (int)*result;
}
static void compute_bitrate_add_to_list_and_remove_oldest_value(OrtpVideoBandwidthEstimator *vbe, OrtpVideoBandwidthEstimatorPacket *packet) {
float difftime = (float)(packet->recv_last_timestamp.tv_sec - packet->recv_first_timestamp.tv_sec)
+ 1e-6*(packet->recv_last_timestamp.tv_usec - packet->recv_first_timestamp.tv_usec);
packet->bitrate = (packet->bytes * 8 / difftime);
ortp_debug("[VBE] Bitrate is %f kbits/s computed using %f timedif and %u size", packet->bitrate / 1000, difftime, packet->bytes);
vbe->nb_packets_computed += 1;
vbe->packets = bctbx_list_prepend(vbe->packets, packet);
if (bctbx_list_size(vbe->packets) > vbe->packets_size_max) {
void *old_data = bctbx_list_nth_data(vbe->packets, vbe->packets_size_max);
vbe->packets = bctbx_list_remove(vbe->packets, old_data);
}
if (vbe->nb_packets_computed % vbe->packets_size_max == 0) {
OrtpEvent *ev = ortp_event_new(ORTP_EVENT_NEW_VIDEO_BANDWIDTH_ESTIMATION_AVAILABLE);
OrtpEventData *ed = ortp_event_get_data(ev);
ed->info.video_bandwidth_available = ortp_video_bandwidth_estimator_get_estimated_available_bandwidth(vbe);
ortp_debug("[VBE] Dispatching event ORTP_EVENT_NEW_VIDEO_BANDWIDTH_ESTIMATION_AVAILABLE with value %f kbits/s", ed->info.video_bandwidth_available / 1000);
rtp_session_dispatch_event(vbe->session, ev);
}
}
void ortp_video_bandwidth_estimator_process_packet(OrtpVideoBandwidthEstimator *vbe, uint32_t sent_timestamp, const struct timeval *recv_timestamp, int msgsize, bool_t is_last) {
OrtpVideoBandwidthEstimatorPacket *last_packet = vbe->last_packet;
OrtpVideoBandwidthEstimatorPacket *current_packet = NULL;
if (last_packet) {
if (last_packet->sent_timestamp == sent_timestamp) {
current_packet = last_packet;
current_packet->count += 1;
current_packet->bytes += msgsize;
current_packet->recv_last_timestamp.tv_sec = recv_timestamp->tv_sec;
current_packet->recv_last_timestamp.tv_usec = recv_timestamp->tv_usec;
if (is_last && current_packet->count >= vbe->packet_count_min) {
compute_bitrate_add_to_list_and_remove_oldest_value(vbe, current_packet);
vbe->last_packet = NULL;
}
} else {
// This can happen even if it's probability is quite low
if (last_packet->count >= vbe->packet_count_min) {
ortp_warning("[VBE] Last packet not complete but enough packet received (%u), add to packets list", last_packet->count);
compute_bitrate_add_to_list_and_remove_oldest_value(vbe, last_packet);
} else {
ortp_free(vbe->last_packet);
}
vbe->last_packet = NULL;
}
}
if (!current_packet) {
current_packet = (OrtpVideoBandwidthEstimatorPacket*)ortp_malloc0(sizeof(OrtpVideoBandwidthEstimatorPacket));
current_packet->count = 1;
current_packet->bytes = msgsize;
current_packet->sent_timestamp = sent_timestamp;
current_packet->recv_first_timestamp.tv_sec = recv_timestamp->tv_sec;
current_packet->recv_first_timestamp.tv_usec = recv_timestamp->tv_usec;
vbe->last_packet = current_packet;
}
}
\ No newline at end of file
/*
* The oRTP library is an RTP (Realtime Transport Protocol - rfc3550) implementation with additional features.
* Copyright (C) 2017 Belledonne Communications SARL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef VIDEOBANDWIDTHESTIMATOR_H
#define VIDEOBANDWIDTHESTIMATOR_H
#include <ortp/port.h>
#include <ortp/utils.h>
#include <bctoolbox/list.h>
typedef struct _OrtpVideoBandwidthEstimatorPacket{
uint32_t sent_timestamp;
struct timeval recv_first_timestamp;
struct timeval recv_last_timestamp;
unsigned int bytes;
unsigned int count;
float bitrate;
}OrtpVideoBandwidthEstimatorPacket;
typedef struct _OrtpVideoBandwidthEstimator{
struct _RtpSession *session;
unsigned int packet_count_min;
unsigned int packets_size_max;
unsigned int trust_percentage;
OrtpVideoBandwidthEstimatorPacket *last_packet;
bctbx_list_t *packets;
uint32_t last_computed;
int nb_packets_computed;
}OrtpVideoBandwidthEstimator;
OrtpVideoBandwidthEstimator * ortp_video_bandwidth_estimator_new(struct _RtpSession *session);
void ortp_video_bandwidth_estimator_destroy(OrtpVideoBandwidthEstimator *vbe);
void ortp_video_bandwidth_estimator_reset(OrtpVideoBandwidthEstimator *vbe);
/**
* Sets the minimum number of packets with the same sent timestamp to be processed continuously before being used.
* Default value is 7.
*/
void ortp_video_bandwidth_estimator_set_packets_count_min(OrtpVideoBandwidthEstimator *vbe, unsigned int value);
/**
* Sets the number of packets needed to compute the available video bandwidth.
* Default value is 30.
*/
void ortp_video_bandwidth_estimator_set_history_max_size(OrtpVideoBandwidthEstimator *vbe, unsigned int value);
/**
* Sets the percentage for which the chosen bandwidth value in all available will be inferior.
* Example: for 100 packets with 90% trust, bandwidth value will be the 90th after sorted.
* Default value is 90.
*/
void ortp_video_bandwidth_estimator_set_trust(OrtpVideoBandwidthEstimator *vbe, unsigned int value);
/**
* Gets the minimum number of packets with the same sent timestamp to be processed continuously before being used.
* Default value is 7.
*/
unsigned int ortp_video_bandwidth_estimator_get_packets_count_min(OrtpVideoBandwidthEstimator *vbe);
/**
* Gets the number of packets needed to compute the available video bandwidth.
* Default value is 30.
*/
unsigned int ortp_video_bandwidth_estimator_get_history_max_size(OrtpVideoBandwidthEstimator *vbe);
/**
* Gets the percentage for which the chosen bandwidth value in all available will be inferior.
* Example: for 100 packets with 90% trust, bandwidth value will be the 90th after sorted.
* Default value is 90.
*/
unsigned int ortp_video_bandwidth_estimator_get_trust(OrtpVideoBandwidthEstimator *vbe);
void ortp_video_bandwidth_estimator_process_packet(OrtpVideoBandwidthEstimator *vbe, uint32_t sent_timestamp, const struct timeval *recv_timestamp, int msgsize, bool_t is_last);
int ortp_video_bandwidth_estimator_get_estimated_available_bandwidth(OrtpVideoBandwidthEstimator *vbe);
#endif
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment