doubango/tinyDAV/src/video/jb/tdav_video_frame.c
c732d49e
 #if HAVE_CRT
 #define _CRTDBG_MAP_ALLOC 
 #include <stdlib.h> 
 #include <crtdbg.h>
 #endif //HAVE_CRT
 /*
74ca6d11
 * Copyright (C) 2020, University of the Basque Country (UPV/EHU)
c732d49e
 * Contact for licensing options: <licensing-mcpttclient(at)mcopenplatform(dot)com>
 *
 * The original file was part of Open Source Doubango Framework
 * Copyright (C) 2010-2011 Mamadou Diop.
 * Copyright (C) 2012 Doubango Telecom <http://doubango.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
  *
  * @author Mamadou Diop <diopmamadou(at)doubango(DOT)org>
  */
 #include "tinydav/video/jb/tdav_video_frame.h"
 
 #include "tsk_memory.h"
 #include "tsk_debug.h"
 
 #include <string.h>
 #include <stdlib.h>
 
 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;
 }
 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;
 }
 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;
 }
 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, 
 };
 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;
 }
 
 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 0
 	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;
 }
 
 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;
 }
 
 // @buffer_ptr pointer to the destination buffer
 // @buffer_size the actual buffer size. Could be enlarged if too small to fit
 // @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;
 	}
 
 bail:
 	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.
 */
 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;
 }