diff --git a/branches/2.0/doubango/tinyDAV/include/tinydav/video/jb/tdav_video_frame.h b/branches/2.0/doubango/tinyDAV/include/tinydav/video/jb/tdav_video_frame.h index 0795a3ec..798f8486 100644 --- a/branches/2.0/doubango/tinyDAV/include/tinydav/video/jb/tdav_video_frame.h +++ b/branches/2.0/doubango/tinyDAV/include/tinydav/video/jb/tdav_video_frame.h @@ -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 diff --git a/branches/2.0/doubango/tinyDAV/src/video/jb/tdav_video_frame.c b/branches/2.0/doubango/tinyDAV/src/video/jb/tdav_video_frame.c index 0cc178a0..fc7cbc32 100644 --- a/branches/2.0/doubango/tinyDAV/src/video/jb/tdav_video_frame.c +++ b/branches/2.0/doubango/tinyDAV/src/video/jb/tdav_video_frame.c @@ -1,24 +1,24 @@ /* -* Copyright (C) 2012 Doubango Telecom -* -* Contact: Mamadou Diop -* -* 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 + * + * Contact: Mamadou Diop + * + * 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; } diff --git a/branches/2.0/doubango/tinyDAV/src/video/jb/tdav_video_jb.c b/branches/2.0/doubango/tinyDAV/src/video/jb/tdav_video_jb.c index 24c307a2..28c83ed0 100644 --- a/branches/2.0/doubango/tinyDAV/src/video/jb/tdav_video_jb.c +++ b/branches/2.0/doubango/tinyDAV/src/video/jb/tdav_video_jb.c @@ -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){