Workaround for GE freeze frame feature

This commit is contained in:
bossiel 2015-06-25 14:56:16 +00:00
parent 740b5a2931
commit 9604956a56
3 changed files with 208 additions and 197 deletions

View File

@ -61,6 +61,7 @@ int tdav_video_frame_put(struct tdav_video_frame_s* self, struct trtp_rtp_packet
const struct trtp_rtp_packet_s* tdav_video_frame_find_by_seq_num(const struct tdav_video_frame_s* self, uint16_t seq_num);
tsk_size_t tdav_video_frame_write(struct tdav_video_frame_s* self, void** buffer_ptr, tsk_size_t* buffer_size);
tsk_bool_t tdav_video_frame_is_complete(const struct tdav_video_frame_s* self, int32_t last_seq_num_with_mark, int32_t* missing_seq_num_start, int32_t* missing_seq_num_count);
#define tdav_video_frame_is_complete_2(self, last_seq_num_with_mark) tdav_video_frame_is_complete((self), (last_seq_num_with_mark), tsk_null, tsk_null)
TDAV_END_DECLS

View File

@ -1,24 +1,24 @@
/*
* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org>
*
* Contact: Mamadou Diop <diopmamadou(at)doubango(DOT)org>
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO 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 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO 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 DOUBANGO.
*
*/
* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org>
*
* Contact: Mamadou Diop <diopmamadou(at)doubango(DOT)org>
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO 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 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO 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 DOUBANGO.
*
*/
/**@file tdav_video_frame.c
* @brief Video Frame
@ -35,128 +35,128 @@
static tsk_object_t* tdav_video_frame_ctor(tsk_object_t * self, va_list * app)
{
tdav_video_frame_t *frame = self;
if(frame){
if(!(frame->pkts = tsk_list_create())){
TSK_DEBUG_ERROR("Faile to list");
return tsk_null;
}
tsk_safeobj_init(frame);
}
return self;
tdav_video_frame_t *frame = self;
if(frame){
if(!(frame->pkts = tsk_list_create())){
TSK_DEBUG_ERROR("Faile to list");
return tsk_null;
}
tsk_safeobj_init(frame);
}
return self;
}
static tsk_object_t* tdav_video_frame_dtor(tsk_object_t * self)
{
tdav_video_frame_t *frame = self;
if(frame){
TSK_OBJECT_SAFE_FREE(frame->pkts);
tsk_safeobj_deinit(frame);
}
return self;
{
tdav_video_frame_t *frame = self;
if(frame){
TSK_OBJECT_SAFE_FREE(frame->pkts);
tsk_safeobj_deinit(frame);
}
return self;
}
static int tdav_video_frame_cmp(const tsk_object_t *_p1, const tsk_object_t *_p2)
{
const tdav_video_frame_t *p1 = _p1;
const tdav_video_frame_t *p2 = _p2;
if(p1 && p2){
return (int)(p1->timestamp - p2->timestamp);
}
else if(!p1 && !p2) return 0;
else return -1;
const tdav_video_frame_t *p1 = _p1;
const tdav_video_frame_t *p2 = _p2;
if(p1 && p2){
return (int)(p1->timestamp - p2->timestamp);
}
else if(!p1 && !p2) return 0;
else return -1;
}
static const tsk_object_def_t tdav_video_frame_def_s =
static const tsk_object_def_t tdav_video_frame_def_s =
{
sizeof(tdav_video_frame_t),
tdav_video_frame_ctor,
tdav_video_frame_dtor,
tdav_video_frame_cmp,
sizeof(tdav_video_frame_t),
tdav_video_frame_ctor,
tdav_video_frame_dtor,
tdav_video_frame_cmp,
};
const tsk_object_def_t *tdav_video_frame_def_t = &tdav_video_frame_def_s;
tdav_video_frame_t* tdav_video_frame_create(trtp_rtp_packet_t* rtp_pkt)
{
tdav_video_frame_t* frame;
if(!rtp_pkt || !rtp_pkt->header){
TSK_DEBUG_ERROR("Invalid parameter");
return tsk_null;
}
if((frame = tsk_object_new(tdav_video_frame_def_t))){
rtp_pkt = tsk_object_ref(rtp_pkt);
frame->payload_type = rtp_pkt->header->payload_type;
frame->timestamp = rtp_pkt->header->timestamp;
frame->highest_seq_num = rtp_pkt->header->seq_num;
frame->ssrc = rtp_pkt->header->ssrc;
tsk_list_push_ascending_data(frame->pkts, (void**)&rtp_pkt);
}
return frame;
tdav_video_frame_t* frame;
if(!rtp_pkt || !rtp_pkt->header){
TSK_DEBUG_ERROR("Invalid parameter");
return tsk_null;
}
if((frame = tsk_object_new(tdav_video_frame_def_t))){
rtp_pkt = tsk_object_ref(rtp_pkt);
frame->payload_type = rtp_pkt->header->payload_type;
frame->timestamp = rtp_pkt->header->timestamp;
frame->highest_seq_num = rtp_pkt->header->seq_num;
frame->ssrc = rtp_pkt->header->ssrc;
tsk_list_push_ascending_data(frame->pkts, (void**)&rtp_pkt);
}
return frame;
}
int tdav_video_frame_put(tdav_video_frame_t* self, trtp_rtp_packet_t* rtp_pkt)
{
if(!self || !rtp_pkt || !rtp_pkt->header){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
if(self->timestamp != rtp_pkt->header->timestamp){
TSK_DEBUG_ERROR("Timestamp mismatch");
return -2;
}
if(self->payload_type != rtp_pkt->header->payload_type){
TSK_DEBUG_ERROR("Payload Type mismatch");
return -2;
}
if(!self || !rtp_pkt || !rtp_pkt->header){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
if(self->timestamp != rtp_pkt->header->timestamp){
TSK_DEBUG_ERROR("Timestamp mismatch");
return -2;
}
if(self->payload_type != rtp_pkt->header->payload_type){
TSK_DEBUG_ERROR("Payload Type mismatch");
return -2;
}
#if 0
if(self->ssrc != rtp_pkt->header->ssrc){
TSK_DEBUG_ERROR("SSRC mismatch");
return -2;
}
if(self->ssrc != rtp_pkt->header->ssrc){
TSK_DEBUG_ERROR("SSRC mismatch");
return -2;
}
#endif
rtp_pkt = tsk_object_ref(rtp_pkt);
self->highest_seq_num = TSK_MAX(self->highest_seq_num, rtp_pkt->header->seq_num);
tsk_list_lock(self->pkts);
if (tdav_video_frame_find_by_seq_num(self, rtp_pkt->header->seq_num)) {
TSK_DEBUG_INFO("JB: Packet with seq_num=%hu duplicated", rtp_pkt->header->seq_num);
}
else {
tsk_list_push_ascending_data(self->pkts, (void**)&rtp_pkt);
}
tsk_list_unlock(self->pkts);
return 0;
rtp_pkt = tsk_object_ref(rtp_pkt);
self->highest_seq_num = TSK_MAX(self->highest_seq_num, rtp_pkt->header->seq_num);
tsk_list_lock(self->pkts);
if (tdav_video_frame_find_by_seq_num(self, rtp_pkt->header->seq_num)) {
TSK_DEBUG_INFO("JB: Packet with seq_num=%hu duplicated", rtp_pkt->header->seq_num);
}
else {
tsk_list_push_ascending_data(self->pkts, (void**)&rtp_pkt);
}
tsk_list_unlock(self->pkts);
return 0;
}
const trtp_rtp_packet_t* tdav_video_frame_find_by_seq_num(const tdav_video_frame_t* self, uint16_t seq_num)
{
const tsk_list_item_t *item;
const trtp_rtp_packet_t* pkt;
const trtp_rtp_packet_t* ret;
if(!self){
TSK_DEBUG_ERROR("Invalid parameter");
return tsk_null;
}
ret = tsk_null;
tsk_list_lock(self->pkts);
tsk_list_foreach(item, self->pkts){
if(!(pkt = item->data) || !pkt->header){
continue;
}
if(pkt->header->seq_num == seq_num){
ret = pkt;
break;
}
}
tsk_list_unlock(self->pkts);
return ret;
const tsk_list_item_t *item;
const trtp_rtp_packet_t* pkt;
const trtp_rtp_packet_t* ret;
if(!self){
TSK_DEBUG_ERROR("Invalid parameter");
return tsk_null;
}
ret = tsk_null;
tsk_list_lock(self->pkts);
tsk_list_foreach(item, self->pkts){
if(!(pkt = item->data) || !pkt->header){
continue;
}
if(pkt->header->seq_num == seq_num){
ret = pkt;
break;
}
}
tsk_list_unlock(self->pkts);
return ret;
}
// @buffer_ptr pointer to the destination buffer
@ -164,80 +164,80 @@ const trtp_rtp_packet_t* tdav_video_frame_find_by_seq_num(const tdav_video_frame
// @retval number of copied bytes
tsk_size_t tdav_video_frame_write(struct tdav_video_frame_s* self, void** buffer_ptr, tsk_size_t* buffer_size)
{
const tsk_list_item_t *item;
const trtp_rtp_packet_t* pkt;
tsk_size_t ret_size = 0;
int32_t last_seq_num = -1; // guard against duplicated packets
if(!self || !buffer_ptr || !buffer_size){
TSK_DEBUG_ERROR("Invalid parameter");
return 0;
}
tsk_list_lock(self->pkts);
tsk_list_foreach(item, self->pkts){
if(!(pkt = item->data) || !pkt->payload.size || !pkt->header || pkt->header->seq_num == last_seq_num){
continue;
}
if((ret_size + pkt->payload.size) > *buffer_size){
if(!(*buffer_ptr = tsk_realloc(*buffer_ptr, (ret_size + pkt->payload.size)))){
TSK_DEBUG_ERROR("Failed to resize the buffer");
*buffer_size = 0;
goto bail;
}
*buffer_size = (ret_size + pkt->payload.size);
}
memcpy(&((uint8_t*)*buffer_ptr)[ret_size], (pkt->payload.data ? pkt->payload.data : pkt->payload.data_const), pkt->payload.size);
ret_size += pkt->payload.size;
last_seq_num = pkt->header->seq_num;
}
const tsk_list_item_t *item;
const trtp_rtp_packet_t* pkt;
tsk_size_t ret_size = 0;
int32_t last_seq_num = -1; // guard against duplicated packets
if(!self || !buffer_ptr || !buffer_size){
TSK_DEBUG_ERROR("Invalid parameter");
return 0;
}
tsk_list_lock(self->pkts);
tsk_list_foreach(item, self->pkts){
if(!(pkt = item->data) || !pkt->payload.size || !pkt->header || pkt->header->seq_num == last_seq_num){
continue;
}
if((ret_size + pkt->payload.size) > *buffer_size){
if(!(*buffer_ptr = tsk_realloc(*buffer_ptr, (ret_size + pkt->payload.size)))){
TSK_DEBUG_ERROR("Failed to resize the buffer");
*buffer_size = 0;
goto bail;
}
*buffer_size = (ret_size + pkt->payload.size);
}
memcpy(&((uint8_t*)*buffer_ptr)[ret_size], (pkt->payload.data ? pkt->payload.data : pkt->payload.data_const), pkt->payload.size);
ret_size += pkt->payload.size;
last_seq_num = pkt->header->seq_num;
}
bail:
tsk_list_unlock(self->pkts);
return ret_size;
tsk_list_unlock(self->pkts);
return ret_size;
}
/**
Checks if the frame is complete (no gap/loss) or not.
IMPORTANT: This function assume that the RTP packets use the marker bit to signal end of sequences.
*@param self The frame with all rtp packets to check
*@param last_seq_num_with_mark The last seq num value of the packet with the mark bit set. Use negative value to ignore.
*@param missing_seq_num A missing seq num if any. This value is set only if the function returns False.
*@return True if the frame is complete and False otherwise. If False is returned then, missing_seq_num is set.
*/
Checks if the frame is complete (no gap/loss) or not.
IMPORTANT: This function assume that the RTP packets use the marker bit to signal end of sequences.
*@param self The frame with all rtp packets to check
*@param last_seq_num_with_mark The last seq num value of the packet with the mark bit set. Use negative value to ignore.
*@param missing_seq_num A missing seq num if any. This value is set only if the function returns False.
*@return True if the frame is complete and False otherwise. If False is returned then, missing_seq_num is set.
*/
tsk_bool_t tdav_video_frame_is_complete(const tdav_video_frame_t* self, int32_t last_seq_num_with_mark, int32_t* missing_seq_num_start, int32_t* missing_seq_num_count)
{
const trtp_rtp_packet_t* pkt;
const tsk_list_item_t *item;
uint16_t i;
tsk_bool_t is_complete = tsk_false;
if(!self || !missing_seq_num_start || !missing_seq_num_count){
TSK_DEBUG_ERROR("Invalid parameter");
return tsk_false;
}
i = 0;
tsk_list_lock(self->pkts);
tsk_list_foreach(item, self->pkts){
if(!(pkt = item->data)){
continue;
}
if(last_seq_num_with_mark >= 0 && pkt->header->seq_num != (last_seq_num_with_mark + ++i)){
*missing_seq_num_start = (last_seq_num_with_mark + i);
*missing_seq_num_count = pkt->header->seq_num - (*missing_seq_num_start);
break;
}
if(item == self->pkts->tail){
if(!(is_complete = (pkt->header->marker))){
*missing_seq_num_start = (pkt->header->seq_num + 1);
*missing_seq_num_count = 1;
}
}
}
tsk_list_unlock(self->pkts);
return is_complete;
const trtp_rtp_packet_t* pkt;
const tsk_list_item_t *item;
uint16_t i;
tsk_bool_t is_complete = tsk_false;
if (!self){
TSK_DEBUG_ERROR("Invalid parameter");
return tsk_false;
}
i = 0;
tsk_list_lock(self->pkts);
tsk_list_foreach (item, self->pkts) {
if (!(pkt = item->data)){
continue;
}
if (last_seq_num_with_mark >= 0 && pkt->header->seq_num != (last_seq_num_with_mark + ++i)) {
if (missing_seq_num_start) *missing_seq_num_start = (last_seq_num_with_mark + i);
if (missing_seq_num_count) *missing_seq_num_count = pkt->header->seq_num - (*missing_seq_num_start);
break;
}
if (item == self->pkts->tail) {
if(!(is_complete = (pkt->header->marker))){
if (missing_seq_num_start) *missing_seq_num_start = (pkt->header->seq_num + 1);
if (missing_seq_num_count) *missing_seq_num_count = 1;
}
}
}
tsk_list_unlock(self->pkts);
return is_complete;
}

View File

@ -427,11 +427,13 @@ static void* TSK_STDCALL _tdav_video_jb_decode_thread_func(void *arg)
int32_t missing_seq_num_count = 0, prev_lasted_missing_seq_num_count = 0;
const tdav_video_frame_t* frame;
tsk_list_item_t* item;
uint64_t next_decode_duration = 0, now, _now;
uint64_t next_decode_duration = 0, now, _now, latency = 0;
//uint64_t x_decode_duration = (1000 / jb->fps); // expected
//uint64_t x_decode_time = tsk_time_now();//expected
tsk_bool_t postpone, cleaning_delay = tsk_false;
#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();
@ -453,21 +455,23 @@ static void* TSK_STDCALL _tdav_video_jb_decode_thread_func(void *arg)
// TSK_DEBUG_INFO("Frames count = %d", jb->frames_count);
if(jb->frames_count >= (int64_t)jb->latency_min){
// 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)) {
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){
if (jb->frames_count < (int64_t)jb->latency_max) {
frame = (const tdav_video_frame_t*)jb->frames->head->data;
if(!tdav_video_frame_is_complete(frame, jb->decode_last_seq_num_with_mark, &missing_seq_num_start, &missing_seq_num_count)){
if (!tdav_video_frame_is_complete(frame, jb->decode_last_seq_num_with_mark, &missing_seq_num_start, &missing_seq_num_count)) {
TSK_DEBUG_INFO("Time to decode frame...but some RTP packets are missing (missing_seq_num_start=%d, missing_seq_num_count=%d, last_seq_num_with_mark=%d). Postpone :(", missing_seq_num_start, missing_seq_num_count, jb->decode_last_seq_num_with_mark);
// 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){
if (jb->callback) {
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;
@ -479,18 +483,19 @@ static void* TSK_STDCALL _tdav_video_jb_decode_thread_func(void *arg)
}
}
}
else{
else {
TSK_DEBUG_INFO("frames_count(%lld)>=latency_max(%u)...decoding video frame even if pkts are missing :(", jb->frames_count, (unsigned)jb->latency_max);
jb->decode_last_seq_num_with_mark = -1; // unset()
// postpone is equal to "tsk_false" which means the pending frame will be displayed in all cases
}
if(!postpone){
if (!postpone) {
item = tsk_list_pop_first_item(jb->frames);
--jb->frames_count;
}
tsk_list_unlock(jb->frames);
tsk_safeobj_unlock(jb);
if(item){
if (item) {
jb->decode_last_timestamp = ((const tdav_video_frame_t*)item->data)->timestamp;
if(jb->callback){
trtp_rtp_packet_t* pkt;
@ -501,7 +506,7 @@ static void* TSK_STDCALL _tdav_video_jb_decode_thread_func(void *arg)
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){
@ -513,6 +518,11 @@ static void* TSK_STDCALL _tdav_video_jb_decode_thread_func(void *arg)
TSK_OBJECT_SAFE_FREE(item);
}
}
else {
if (jb->frames_count > 0) { // there are pending frames but we cannot display them yet -> increase latency
latency++;
}
}
#if 1
if (cleaning_delay || jb->frames_count > (int64_t)jb->latency_max){