Workaround for GE freeze frame feature
This commit is contained in:
parent
740b5a2931
commit
9604956a56
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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){
|
||||
|
|
Loading…
Reference in New Issue