summaryrefslogtreecommitdiffstats
path: root/tinyDAV/src/video/jb/tdav_video_jb.c
diff options
context:
space:
mode:
Diffstat (limited to 'tinyDAV/src/video/jb/tdav_video_jb.c')
-rwxr-xr-xtinyDAV/src/video/jb/tdav_video_jb.c260
1 files changed, 141 insertions, 119 deletions
diff --git a/tinyDAV/src/video/jb/tdav_video_jb.c b/tinyDAV/src/video/jb/tdav_video_jb.c
index ec9b53b..ae24a2a 100755
--- a/tinyDAV/src/video/jb/tdav_video_jb.c
+++ b/tinyDAV/src/video/jb/tdav_video_jb.c
@@ -65,10 +65,9 @@ static int _tdav_video_jb_set_defaults(struct tdav_video_jb_s* self);
static const tdav_video_frame_t* _tdav_video_jb_get_frame(struct tdav_video_jb_s* self, uint32_t timestamp, uint8_t pt, tsk_bool_t *pt_matched);
static void* TSK_STDCALL _tdav_video_jb_decode_thread_func(void *arg);
-typedef struct tdav_video_jb_s
-{
+typedef struct tdav_video_jb_s {
TSK_DECLARE_OBJECT;
-
+
tsk_bool_t started;
int32_t fps;
int32_t fps_prob;
@@ -79,30 +78,30 @@ typedef struct tdav_video_jb_s
int32_t tail_max;
tdav_video_frames_L_t *frames;
int64_t frames_count;
-
+
tsk_size_t latency_min;
tsk_size_t latency_max;
-
+
uint32_t decode_last_timestamp;
int32_t decode_last_seq_num_with_mark; // -1 = unset
uint64_t decode_last_time;
tsk_thread_handle_t* decode_thread[1];
tsk_condwait_handle_t* decode_thread_cond;
-
+
uint16_t seq_nums[0xFF];
tdav_video_jb_cb_f callback;
const void* callback_data;
-
+
// to avoid locking use different cb_data
tdav_video_jb_cb_data_xt cb_data_rtp;
tdav_video_jb_cb_data_xt cb_data_fdd;
tdav_video_jb_cb_data_xt cb_data_any;
-
- struct{
+
+ struct {
void* ptr;
tsk_size_t size;
} buffer;
-
+
TSK_DECLARE_SAFEOBJ;
}
tdav_video_jb_t;
@@ -111,18 +110,18 @@ tdav_video_jb_t;
static tsk_object_t* tdav_video_jb_ctor(tsk_object_t * self, va_list * app)
{
tdav_video_jb_t *jb = self;
- if(jb){
- if(!(jb->frames = tsk_list_create())){
+ if(jb) {
+ if(!(jb->frames = tsk_list_create())) {
TSK_DEBUG_ERROR("Failed to create list");
return tsk_null;
}
- if(!(jb->decode_thread_cond = tsk_condwait_create())){
+ if(!(jb->decode_thread_cond = tsk_condwait_create())) {
TSK_DEBUG_ERROR("Failed to create condition var");
return tsk_null;
}
jb->cb_data_fdd.type = tdav_video_jb_cb_data_type_fdd;
jb->cb_data_rtp.type = tdav_video_jb_cb_data_type_rtp;
-
+
tsk_safeobj_init(jb);
}
return self;
@@ -130,22 +129,21 @@ static tsk_object_t* tdav_video_jb_ctor(tsk_object_t * self, va_list * app)
static tsk_object_t* tdav_video_jb_dtor(tsk_object_t * self)
{
tdav_video_jb_t *jb = self;
- if(jb){
- if(jb->started){
+ if(jb) {
+ if(jb->started) {
tdav_video_jb_stop(jb);
}
TSK_OBJECT_SAFE_FREE(jb->frames);
- if(jb->decode_thread_cond){
+ if(jb->decode_thread_cond) {
tsk_condwait_destroy(&jb->decode_thread_cond);
}
TSK_SAFE_FREE(jb->buffer.ptr);
tsk_safeobj_deinit(jb);
}
-
+
return self;
}
-static const tsk_object_def_t tdav_video_jb_def_s =
-{
+static const tsk_object_def_t tdav_video_jb_def_s = {
sizeof(tdav_video_jb_t),
tdav_video_jb_ctor,
tdav_video_jb_dtor,
@@ -155,7 +153,7 @@ static const tsk_object_def_t tdav_video_jb_def_s =
tdav_video_jb_t* tdav_video_jb_create()
{
tdav_video_jb_t* jb;
-
+
if ((jb = tsk_object_new(&tdav_video_jb_def_s))) {
if (_tdav_video_jb_set_defaults(jb) != 0) {
TSK_OBJECT_SAFE_FREE(jb);
@@ -176,7 +174,7 @@ tdav_video_jb_t* tdav_video_jb_create()
int tdav_video_jb_set_callback(tdav_video_jb_t* self, tdav_video_jb_cb_f callback, const void* usr_data)
{
- if(!self){
+ if(!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
@@ -187,27 +185,49 @@ int tdav_video_jb_set_callback(tdav_video_jb_t* self, tdav_video_jb_cb_f callbac
return 0;
}
+// Congestion quality metrics based
+int tdav_video_jb_get_qcong(tdav_video_jb_t* self, float* q)
+{
+ float lm;
+ if (!self || !q) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ lm = (float)self->latency_max;
+ if (lm <= 0.f) { // must never happen...but used as a guard against div(0)
+ *q = 1.f;
+ }
+ else {
+ // when "frames_count" is > "latency_max" q is < 0 but it'll be clipped to 0.f
+ *q = 1.f - (self->frames_count / lm);
+ }
+ // 0.0001f instead of zero which could be interpreted as "no data available"
+ // 0.0001f encoded to 1-byte in RTCP-RR-JCNG will be coded as (0.0001f * 255.f) = zero
+ *q = TSK_CLAMP(0.0001f, *q, 1.f);
+ return 0;
+}
+
int tdav_video_jb_start(tdav_video_jb_t* self)
{
int ret = 0;
- if(!self){
+ if(!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
- if(self->started){
+ if(self->started) {
return 0;
}
-
+
self->started = tsk_true;
-
- if(!self->decode_thread[0]){
+
+ if(!self->decode_thread[0]) {
ret = tsk_thread_create(&self->decode_thread[0], _tdav_video_jb_decode_thread_func, self);
- if(ret != 0 || !self->decode_thread[0]){
+ if(ret != 0 || !self->decode_thread[0]) {
TSK_DEBUG_ERROR("Failed to create new thread");
}
ret = tsk_thread_set_priority(self->decode_thread[0], TSK_THREAD_PRIORITY_TIME_CRITICAL);
}
-
+
return ret;
}
@@ -220,43 +240,43 @@ int tdav_video_jb_put(tdav_video_jb_t* self, trtp_rtp_packet_t* rtp_pkt)
const tdav_video_frame_t* old_frame;
tsk_bool_t pt_matched = tsk_false, is_frame_late_or_dup = tsk_false, is_restarted = tsk_false;
uint16_t* seq_num;
-
- if(!self || !rtp_pkt || !rtp_pkt->header){
+
+ if(!self || !rtp_pkt || !rtp_pkt->header) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
-
- if(!self->started){
+
+ if(!self->started) {
TSK_DEBUG_INFO("Video jitter buffer not started");
return 0;
}
-
+
seq_num = &self->seq_nums[rtp_pkt->header->payload_type];
-
+
tsk_safeobj_lock(self);
-
+
//TSK_DEBUG_INFO("receive seqnum=%u", rtp_pkt->header->seq_num);
-
- if(self->decode_last_timestamp && (self->decode_last_timestamp > rtp_pkt->header->timestamp)){
- if((self->decode_last_timestamp - rtp_pkt->header->timestamp) < TDAV_VIDEO_JB_MAX_DROPOUT){
+
+ if(self->decode_last_timestamp && (self->decode_last_timestamp > rtp_pkt->header->timestamp)) {
+ if((self->decode_last_timestamp - rtp_pkt->header->timestamp) < TDAV_VIDEO_JB_MAX_DROPOUT) {
TSK_DEBUG_INFO("--------Frame already Decoded [seqnum=%u]------------", rtp_pkt->header->seq_num);
tsk_safeobj_unlock(self);
return 0;
}
}
-
+
old_frame = _tdav_video_jb_get_frame(self, rtp_pkt->header->timestamp, rtp_pkt->header->payload_type, &pt_matched);
-
- if((*seq_num && *seq_num != 0xFFFF) && (*seq_num + 1) != rtp_pkt->header->seq_num){
+
+ if((*seq_num && *seq_num != 0xFFFF) && (*seq_num + 1) != rtp_pkt->header->seq_num) {
int32_t diff = ((int32_t)rtp_pkt->header->seq_num - (int32_t)*seq_num);
tsk_bool_t is_frame_loss = (diff > 0);
is_restarted = (TSK_ABS(diff) > TDAV_VIDEO_JB_MAX_DROPOUT);
is_frame_late_or_dup = !is_frame_loss;
tdav_video_jb_reset_fps_prob(self);
TSK_DEBUG_INFO("Packet %s (from JB) [%hu - %hu]", is_frame_loss ? "loss" : "late/duplicated/nack", *seq_num, rtp_pkt->header->seq_num);
-
- if(is_frame_loss && !is_restarted){
- if(self->callback){
+
+ if(is_frame_loss && !is_restarted) {
+ if(self->callback) {
self->cb_data_any.type = tdav_video_jb_cb_data_type_fl;
self->cb_data_any.ssrc = rtp_pkt->header->ssrc;
self->cb_data_any.fl.seq_num = (*seq_num + 1);
@@ -265,48 +285,48 @@ int tdav_video_jb_put(tdav_video_jb_t* self, trtp_rtp_packet_t* rtp_pkt)
}
}
}
-
- if(!old_frame){
+
+ if(!old_frame) {
tdav_video_frame_t* new_frame;
- if(pt_matched){
+ if(pt_matched) {
// if we have a frame with the same payload type but without this timestamp this means that we moved to a new frame
// this happens if the frame is waiting to be decoded or the marker is lost
}
- if((new_frame = tdav_video_frame_create(rtp_pkt))){
+ if((new_frame = tdav_video_frame_create(rtp_pkt))) {
// compute avg frame duration
- if(self->last_timestamp && self->last_timestamp < rtp_pkt->header->timestamp){
+ if(self->last_timestamp && self->last_timestamp < rtp_pkt->header->timestamp) {
uint32_t duration = (rtp_pkt->header->timestamp - self->last_timestamp)/self->rate;
self->avg_duration = self->avg_duration ? ((self->avg_duration + duration) >> 1) : duration;
--self->fps_prob;
}
self->last_timestamp = rtp_pkt->header->timestamp;
-
+
tsk_list_lock(self->frames);
- if(self->frames_count >= self->tail_max){
- if(++self->conseq_frame_drop >= self->tail_max){
+ if(self->frames_count >= self->tail_max) {
+ if(++self->conseq_frame_drop >= self->tail_max) {
TSK_DEBUG_ERROR("Too many frames dropped and fps=%d", self->fps);
tsk_list_clear_items(self->frames);
self->conseq_frame_drop = 0;
self->frames_count = 1;
- if(self->callback){
+ if(self->callback) {
self->cb_data_any.type = tdav_video_jb_cb_data_type_tmfr;
self->cb_data_any.ssrc = rtp_pkt->header->ssrc;
self->callback(&self->cb_data_any);
}
}
- else{
+ else {
TSK_DEBUG_INFO("Dropping video frame because frames_count(%lld)>=tail_max(%d)", self->frames_count, self->tail_max);
tsk_list_remove_first_item(self->frames);
}
tdav_video_jb_reset_fps_prob(self);
}
- else{
+ else {
++self->frames_count;
}
tsk_list_push_ascending_data(self->frames, (void**)&new_frame);
tsk_list_unlock(self->frames);
}
- if(self->fps_prob <= 0 && self->avg_duration){
+ if(self->fps_prob <= 0 && self->avg_duration) {
// compute FPS using timestamp values
int32_t fps_new = (1000 / self->avg_duration);
int32_t fps_old = self->fps;
@@ -315,7 +335,7 @@ int tdav_video_jb_put(tdav_video_jb_t* self, trtp_rtp_packet_t* rtp_pkt)
self->latency_max = self->fps; // maximum = 1 second
TSK_DEBUG_INFO("According to rtp-timestamps ...FPS = %d (clipped to %d) tail_max=%d, latency_max=%u", fps_new, self->fps, self->tail_max, (unsigned)self->latency_max);
tdav_video_jb_reset_fps_prob(self);
- if(self->callback && (fps_old != self->fps)){
+ if(self->callback && (fps_old != self->fps)) {
self->cb_data_any.type = tdav_video_jb_cb_data_type_fps_changed;
self->cb_data_any.ssrc = rtp_pkt->header->ssrc;
self->cb_data_any.fps.new = self->fps; // clipped value
@@ -324,51 +344,51 @@ int tdav_video_jb_put(tdav_video_jb_t* self, trtp_rtp_packet_t* rtp_pkt)
}
}
}
- else{
+ else {
tdav_video_frame_put((tdav_video_frame_t*)old_frame, rtp_pkt);
}
-
+
tsk_safeobj_unlock(self);
-
- if(!is_frame_late_or_dup || is_restarted){
+
+ if(!is_frame_late_or_dup || is_restarted) {
*seq_num = rtp_pkt->header->seq_num;
}
#endif
-
+
return 0;
}
int tdav_video_jb_stop(tdav_video_jb_t* self)
{
int ret;
- if(!self){
+ if(!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
- if(!self->started){
+ if(!self->started) {
return 0;
}
-
+
TSK_DEBUG_INFO("tdav_video_jb_stop()");
-
+
self->started = tsk_false;
-
+
ret = tsk_condwait_broadcast(self->decode_thread_cond);
-
+
if (self->decode_thread[0]) {
ret = tsk_thread_join(&self->decode_thread[0]);
}
-
+
// clear pending frames
tsk_list_lock(self->frames);
tsk_list_clear_items(self->frames);
self->frames_count = 0;
tsk_list_unlock(self->frames);
-
+
// reset default values to make sure next start will be called with right defaults
// do not call this function in start to avoid overriding values defined between prepare() and start()
_tdav_video_jb_set_defaults(self);
-
+
return ret;
}
@@ -378,7 +398,7 @@ static int _tdav_video_jb_set_defaults(struct tdav_video_jb_s* self)
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
-
+
self->fps = TDAV_VIDEO_JB_FPS;
self->fps_prob = TDAV_VIDEO_JB_FPS_PROB;
self->tail_max = TDAV_VIDEO_JB_TAIL_MAX;
@@ -389,10 +409,10 @@ static int _tdav_video_jb_set_defaults(struct tdav_video_jb_s* self)
self->decode_last_timestamp = 0;
self->decode_last_seq_num_with_mark = -1;
self->decode_last_time = 0;
-
+
self->latency_min = TDAV_VIDEO_JB_LATENCY_MIN;
self->latency_max = TDAV_VIDEO_JB_LATENCY_MAX;
-
+
return 0;
}
@@ -400,22 +420,24 @@ static const tdav_video_frame_t* _tdav_video_jb_get_frame(tdav_video_jb_t* self,
{
const tdav_video_frame_t* ret = tsk_null;
const tsk_list_item_t *item;
-
+
*pt_matched =tsk_false;
-
+
tsk_list_lock(self->frames);
- tsk_list_foreach(item, self->frames){
- if(TDAV_VIDEO_FRAME(item->data)->payload_type == pt){
- if(!(*pt_matched)) *pt_matched = tsk_true;
- if(TDAV_VIDEO_FRAME(item->data)->timestamp == timestamp){
+ tsk_list_foreach(item, self->frames) {
+ if(TDAV_VIDEO_FRAME(item->data)->payload_type == pt) {
+ if(!(*pt_matched)) {
+ *pt_matched = tsk_true;
+ }
+ if(TDAV_VIDEO_FRAME(item->data)->timestamp == timestamp) {
ret = item->data;
break;
}
}
-
+
}
tsk_list_unlock(self->frames);
-
+
return ret;
}
@@ -433,36 +455,36 @@ static void* TSK_STDCALL _tdav_video_jb_decode_thread_func(void *arg)
#if 0
static const uint64_t __toomuch_delay_to_be_valid = 10000; // guard against systems with buggy "tsk_time_now()" -Won't say Windows ...but :)-
#endif
-
+
jb->decode_last_seq_num_with_mark = -1; // -1 -> unset
jb->decode_last_time = tsk_time_now();
-
+
(void)(now);
//(void)(delay);
-
+
TSK_DEBUG_INFO("Video jitter buffer thread - ENTER");
-
- while(jb->started){
+
+ while(jb->started) {
now = tsk_time_now();
if (next_decode_duration > 0) {
tsk_condwait_timedwait(jb->decode_thread_cond, next_decode_duration);
}
-
- if(!jb->started){
+
+ if(!jb->started) {
break;
}
-
+
// TSK_DEBUG_INFO("Frames count = %d", jb->frames_count);
-
+
// the second condition (jb->frames_count > 0 && latency >= jb->latency_max) is required to make sure we'll process the pending pkts even if the remote party stops sending frames. GE issue: device stops sending frames when it enters in "frame freeze" mode which means #"latency_min" frames won't be displayed.
if (jb->frames_count >= (int64_t)jb->latency_min || (jb->frames_count > 0 && latency >= jb->latency_max)) {
- tsk_list_item_t *item = tsk_null;
+ tsk_list_item_t *item = tsk_null;
postpone = tsk_false;
latency = 0;
-
+
tsk_safeobj_lock(jb); // against get_frame()
tsk_list_lock(jb->frames); // against put()
-
+
// is it still acceptable to wait for missing packets?
if (jb->frames_count < (int64_t)jb->latency_max) {
frame = (const tdav_video_frame_t*)jb->frames->head->data;
@@ -471,7 +493,7 @@ static void* TSK_STDCALL _tdav_video_jb_decode_thread_func(void *arg)
// signal to the session that a sequence number is missing (will send a NACK)
// the missing seqnum has been already requested in jb_put() and here we request it again only ONE time
if (jb->callback && frame) {
- if(prev_missing_seq_num_start != missing_seq_num_start || prev_lasted_missing_seq_num_count != missing_seq_num_count){ // guard to request it only once
+ if(prev_missing_seq_num_start != missing_seq_num_start || prev_lasted_missing_seq_num_count != missing_seq_num_count) { // guard to request it only once
jb->cb_data_any.type = tdav_video_jb_cb_data_type_fl;
jb->cb_data_any.ssrc = frame->ssrc;
jb->cb_data_any.fl.seq_num = prev_missing_seq_num_start = missing_seq_num_start;
@@ -488,34 +510,34 @@ static void* TSK_STDCALL _tdav_video_jb_decode_thread_func(void *arg)
// postpone is equal to "tsk_false" which means the pending frame will be displayed in all cases
}
if (!postpone) {
- if ((item = tsk_list_pop_first_item(jb->frames))) { // always true (jb->frames_count > 0)
- --jb->frames_count;
- // Update the latest decoded timestamp here while we have the lock on the frames
- jb->decode_last_timestamp = ((const tdav_video_frame_t*)item->data)->timestamp;
- }
+ if ((item = tsk_list_pop_first_item(jb->frames))) { // always true (jb->frames_count > 0)
+ --jb->frames_count;
+ // Update the latest decoded timestamp here while we have the lock on the frames
+ jb->decode_last_timestamp = ((const tdav_video_frame_t*)item->data)->timestamp;
+ }
}
tsk_list_unlock(jb->frames);
tsk_safeobj_unlock(jb);
-
+
if (item) {
- if(jb->callback){
+ if(jb->callback) {
trtp_rtp_packet_t* pkt;
const tsk_list_item_t* _item = item; // save memory address as "tsk_list_foreach() will change it for each loop"
int32_t last_seq_num = -1; // guard against duplicated packets
frame = _item->data;
- tsk_list_foreach(_item, frame->pkts){
- if(!(pkt = _item->data) || !pkt->payload.size || !pkt->header || pkt->header->seq_num == last_seq_num || !jb->started){
+ tsk_list_foreach(_item, frame->pkts) {
+ if(!(pkt = _item->data) || !pkt->payload.size || !pkt->header || pkt->header->seq_num == last_seq_num || !jb->started) {
TSK_DEBUG_ERROR("Skipping invalid rtp packet (do not decode!)");
continue;
}
jb->cb_data_rtp.rtp.pkt = pkt;
jb->callback(&jb->cb_data_rtp);
- if(pkt->header->marker){
+ if(pkt->header->marker) {
jb->decode_last_seq_num_with_mark = pkt->header->seq_num;
}
}
}
-
+
TSK_OBJECT_SAFE_FREE(item);
}
}
@@ -524,18 +546,18 @@ static void* TSK_STDCALL _tdav_video_jb_decode_thread_func(void *arg)
latency++;
}
}
-
+
#if 1
- if (cleaning_delay || jb->frames_count > (int64_t)jb->latency_max){
+ if (cleaning_delay || jb->frames_count > (int64_t)jb->latency_max) {
//x_decode_time = now;
next_decode_duration = 0;
cleaning_delay = ((jb->frames_count << 1) > (int64_t)jb->latency_max); // cleanup up2 half
}
- else{
+ else {
next_decode_duration = (1000 / jb->fps);
_now = tsk_time_now();
if (_now > now) {
- if ((_now - now) > next_decode_duration){
+ if ((_now - now) > next_decode_duration) {
next_decode_duration = 0;
}
else {
@@ -556,19 +578,19 @@ static void* TSK_STDCALL _tdav_video_jb_decode_thread_func(void *arg)
next_decode_duration = 0;
}
else*/{
- //next_decode_duration = (delay > x_decode_duration) ? 0 : (x_decode_duration - delay);
- //x_decode_duration = (1000 / jb->fps);
- //x_decode_time += x_decode_duration;
- }
-
-
+ //next_decode_duration = (delay > x_decode_duration) ? 0 : (x_decode_duration - delay);
+ //x_decode_duration = (1000 / jb->fps);
+ //x_decode_time += x_decode_duration;
+ }
+
+
//TSK_DEBUG_INFO("next_decode_timeout=%llu, delay = %llu", next_decode_duration, delay);
#else
next_decode_duration = (1000 / jb->fps);
#endif
}
-
+
TSK_DEBUG_INFO("Video jitter buffer thread - EXIT");
-
+
return tsk_null;
}
OpenPOWER on IntegriCloud