blob: 784feef3ff25c8a64e6f16b73dc8daf4f512c111 [file] [log] [blame]
/*
* Copyright (c) 2010-2012, Freescale Semiconductor, Inc. All rights reserved.
*
*/
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Module Name: TimeStamp.c
*
* Description: include TimeStamp stratege for VPU / SW video decoder plugin
*
* Portability: This code is written for Linux OS and Gstreamer
*/
/*
* Changelog:
11/2/2010 draft version Lyon Wang
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mfw_gst_ts.h"
const char *debug_env = "ME_DEBUG";
char *debug = NULL;
int debug_level = 0;
enum
{
DEBUG_LEVEL_ERROR = 1,
DEBUG_LEVEL_WARNING,
DEBUG_LEVEL_LOG,
DEBUG_LEVEL_VERBOSE,
};
#define TSM_MESSAGE(level, fmt, ...)\
do{\
if (debug_level>=(level)){\
printf("TSM:"fmt, ##__VA_ARGS__);\
}\
}while(0)
#define TSM_ERROR(...) TSM_MESSAGE(DEBUG_LEVEL_ERROR, ##__VA_ARGS__)
#define TSM_WARNING(...) TSM_MESSAGE(DEBUG_LEVEL_WARNING, ##__VA_ARGS__)
#define TSM_LOG(...) TSM_MESSAGE(DEBUG_LEVEL_LOG, ##__VA_ARGS__)
#define TSM_VERBOSE(...) TSM_MESSAGE(DEBUG_LEVEL_VERBOSE, ##__VA_ARGS__)
#define TSM_HISTORY_POWER 5
#define TSM_HISTORY_SIZE (1<<TSM_HISTORY_POWER)
#define TSM_ADAPTIVE_INTERVAL(tsm) \
(tsm->dur_history_total>>TSM_HISTORY_POWER)
#define TSM_SECOND ((TSM_TIMESTAMP)1000000000)
#define TSM_DEFAULT_INTERVAL (TSM_SECOND/30)
#define TSM_DEFAULT_TS_BUFFER_SIZE (128)
#define TSM_TS_IS_VALID(ts) \
((ts) != TSM_TIMESTAMP_NONE)
#define TSM_KEY_IS_VALID(key) \
((key) != TSM_KEY_NONE)
#define TSM_DISTANCE(tsm)\
(((tsm->rx)>=(tsm->tx))?((tsm->rx)-(tsm->tx)):(tsm->ts_buf_size-(tsm->tx)+(tsm->rx)))
#define TSM_PLUS_AGE(tsm)\
(TSM_DISTANCE(tsm)+tsm->invalid_ts_count+2)
#define TSM_ABS(ts0, ts1)\
(((ts0)>(ts1))?((ts0)-(ts1)):((ts1)-(ts0)))
#define TSM_TIME_FORMAT "u:%02u:%02u.%09u"
#define TSM_TIME_ARGS(t) \
TSM_TS_IS_VALID (t) ? \
(unsigned int) (((TSM_TIMESTAMP)(t)) / (TSM_SECOND * 60 * 60)) : 99, \
TSM_TS_IS_VALID (t) ? \
(unsigned int) ((((TSM_TIMESTAMP)(t)) / (TSM_SECOND * 60)) % 60) : 99, \
TSM_TS_IS_VALID (t) ? \
(unsigned int) ((((TSM_TIMESTAMP)(t)) / TSM_SECOND) % 60) : 99, \
TSM_TS_IS_VALID (t) ? \
(unsigned int) (((TSM_TIMESTAMP)(t)) % TSM_SECOND) : 999999999
#define TSM_BUFFER_SET(buf, value, size) \
do {\
int i;\
for (i=0;i<(size);i++){\
(buf)[i] = (value);\
}\
}while(0)
#define TSM_RECEIVED_NUNBER 512
typedef struct
{
TSM_TIMESTAMP ts;
unsigned long long age;
void *key;
} TSMControl;
typedef struct _TSMReceivedEntry
{
TSM_TIMESTAMP ts;
struct _TSMReceivedEntry *next;
unsigned int used:1;
unsigned int subentry:1;
int size;
} TSMReceivedEntry;
typedef struct _TSMReceivedEntryMemory
{
struct _TSMReceivedEntryMemory *next;
TSMReceivedEntry entrys[TSM_RECEIVED_NUNBER];
} TSMReceivedEntryMemory;
typedef struct
{
TSMReceivedEntry *head;
TSMReceivedEntry *tail;
TSMReceivedEntry *free;
TSMReceivedEntryMemory *memory;
int cnt;
} TSMRecivedCtl;
typedef struct _TSManager
{
int first_tx;
int first_rx;
int rx; //timestamps received
int tx; //timestamps transfered
TSM_TIMESTAMP last_ts_sent; //last time stamp sent
TSM_TIMESTAMP last_ts_received;
TSM_TIMESTAMP suspicious_ts;
TSM_TIMESTAMP discont_threshold;
unsigned int invalid_ts_count;
TSMGR_MODE mode;
int ts_buf_size;
int dur_history_tx;
TSM_TIMESTAMP dur_history_total;
TSM_TIMESTAMP dur_history_buf[TSM_HISTORY_SIZE];
TSMControl *ts_buf;
unsigned long long age;
int tx_cnt;
int rx_cnt;
int cnt;
int valid_ts_received:1;
int big_cnt;
TSMRecivedCtl rctl;
} TSManager;
static void
tsm_free_received_entry (TSMRecivedCtl * rctl, TSMReceivedEntry * entry)
{
entry->next = rctl->free;
rctl->free = entry;
}
static TSMReceivedEntry *
tsm_new_received_entry (TSMRecivedCtl * rctl)
{
TSMReceivedEntry *ret = NULL;
if (rctl->free) {
ret = rctl->free;
rctl->free = ret->next;
} else {
TSMReceivedEntryMemory *p = malloc (sizeof (TSMReceivedEntryMemory));
if (p) {
int i;
for (i = 1; i < TSM_RECEIVED_NUNBER; i++) {
TSMReceivedEntry *e = &p->entrys[i];
tsm_free_received_entry (rctl, e);
};
p->next = rctl->memory;
rctl->memory = p;
ret = p->entrys;
}
}
return ret;
}
void
TSManagerReceive2 (void *handle, TSM_TIMESTAMP timestamp, int size)
{
#define CLEAR_TSM_RENTRY(entry)\
do { \
(entry)->used = 0; \
(entry)->subentry = 0; \
(entry)->next = NULL; \
} while (0)
TSManager *tsm = (TSManager *) handle;
TSM_VERBOSE ("receive2 %" TSM_TIME_FORMAT " size %d\n",
TSM_TIME_ARGS (timestamp), size);
if (tsm) {
if (size > 0) {
TSMRecivedCtl *rctl = &tsm->rctl;
TSMReceivedEntry *e = tsm_new_received_entry (rctl);
if (e) {
CLEAR_TSM_RENTRY (e);
if ((rctl->tail) && (rctl->tail->ts == timestamp)) {
e->subentry = 1;
}
e->ts = timestamp;
e->size = size;
if (rctl->tail) {
rctl->tail->next = e;
rctl->tail = e;
} else {
rctl->head = rctl->tail = e;
}
}
rctl->cnt++;
} else {
TSManagerReceive (handle, timestamp);
}
}
}
static TSM_TIMESTAMP
TSManagerGetLastTimeStamp (TSMRecivedCtl * rctl, int size, int use)
{
TSM_TIMESTAMP ts = TSM_TIMESTAMP_NONE;
TSMReceivedEntry *e;
while ((size > 0) && (e = rctl->head)) {
ts = ((e->used) ? (TSM_TIMESTAMP_NONE) : (e->ts));
TSM_VERBOSE ("ts get: %" TSM_TIME_FORMAT "\n",
TSM_TIME_ARGS (ts));
if (use)
e->used = 1;
if (size >= e->size) {
rctl->head = e->next;
if (rctl->head == NULL) {
rctl->tail = NULL;
} else {
#if 0
//removed for rtp/rtsp streaming fix,
//this will make same timestamp buffers output timestamp to -1.
if (rctl->head->subentry) {
rctl->head->used = e->used;
}
#endif
}
size -= e->size;
rctl->cnt--;
tsm_free_received_entry (rctl, e);
} else {
e->size -= size;
size = 0;
}
}
return ts;
}
void
TSManagerFlush2 (void *handle, int size)
{
TSManager *tsm = (TSManager *) handle;
if (tsm) {
TSManagerGetLastTimeStamp (&tsm->rctl, size, 0);
}
}
/*======================================================================================
FUNCTION: mfw_gst_receive_ts
DESCRIPTION: Check timestamp and do frame dropping if enabled
ARGUMENTS PASSED: pTimeStamp_Object - TimeStamp Manager to handle related timestamp
timestamp - time stamp of the input buffer which has video data.
RETURN VALUE: None
PRE-CONDITIONS: None
POST-CONDITIONS: None
IMPORTANT NOTES: None
=======================================================================================*/
static void
_TSManagerReceive (void *handle, TSM_TIMESTAMP timestamp, void *key)
{
TSManager *tsm = (TSManager *) handle;
if (tsm) {
if (TSM_TS_IS_VALID (timestamp) && (tsm->rx_cnt))
tsm->valid_ts_received = 1;
tsm->rx_cnt++;
if (tsm->cnt < tsm->ts_buf_size - 1) {
tsm->cnt++;
if (tsm->mode == MODE_AI) {
if (TSM_TS_IS_VALID (timestamp)) {
if (tsm->first_rx) {
tsm->last_ts_received = timestamp;
tsm->first_rx = 0;
} else {
if (tsm->suspicious_ts) {
if (timestamp >= tsm->suspicious_ts) {
tsm->last_ts_received = timestamp;
}
tsm->suspicious_ts = 0;
}
if ((timestamp > tsm->last_ts_received)
&& (timestamp - tsm->last_ts_received > tsm->discont_threshold)) {
tsm->suspicious_ts = timestamp;
timestamp = TSM_TIMESTAMP_NONE;
}
}
}
if (TSM_TS_IS_VALID (timestamp)) // && (TSM_ABS(timestamp, tsm->last_ts_sent)<TSM_SECOND*10))
{
tsm->ts_buf[tsm->rx].ts = timestamp;
tsm->ts_buf[tsm->rx].age = tsm->age + TSM_PLUS_AGE (tsm);
tsm->ts_buf[tsm->rx].key = key;
tsm->last_ts_received = timestamp;
#ifdef DEBUG
//printf("age should %lld %lld\n", tsm->age, tsm->ts_buf[tsm->rx].age);
//printf("++++++ distance = %d tx=%d, rx=%d, invalid count=%d\n", TSM_DISTANCE(tsm), tsm->tx, tsm->rx,tsm->invalid_ts_count);
#endif
tsm->rx = ((tsm->rx + 1) % tsm->ts_buf_size);
} else {
tsm->invalid_ts_count++;
}
} else if (tsm->mode == MODE_FIFO) {
tsm->ts_buf[tsm->rx].ts = timestamp;
tsm->rx = ((tsm->rx + 1) % tsm->ts_buf_size);
}
TSM_LOG ("++Receive %d:%" TSM_TIME_FORMAT
", invalid:%d, size:%d key %p\n", tsm->rx_cnt,
TSM_TIME_ARGS (timestamp), tsm->invalid_ts_count, tsm->cnt, key);
} else {
TSM_ERROR ("Too many timestamps recieved!! (cnt=%d)\n", tsm->cnt);
}
}
}
void
TSManagerValid2 (void *handle, int size, void *key)
{
TSManager *tsm = (TSManager *) handle;
TSM_VERBOSE ("valid2 size %d\n", size);
if (tsm) {
TSM_TIMESTAMP ts;
ts = TSManagerGetLastTimeStamp (&tsm->rctl, size, 1);
TSM_VERBOSE ("TSManagerGetLastTimeStamp: %" TSM_TIME_FORMAT "\n",
TSM_TIME_ARGS (ts));
_TSManagerReceive (tsm, ts, key);
}
}
void
TSManagerReceive (void *handle, TSM_TIMESTAMP timestamp)
{
_TSManagerReceive (handle, timestamp, TSM_KEY_NONE);
}
/*======================================================================================
FUNCTION: TSManagerSend
DESCRIPTION: Check timestamp and do frame dropping if enabled
ARGUMENTS PASSED: pTimeStamp_Object - TimeStamp Manager to handle related timestamp
ptimestamp - returned timestamp to use at render
RETURN VALUE: None
PRE-CONDITIONS: None
POST-CONDITIONS: None
IMPORTANT NOTES: None
=======================================================================================*/
static TSM_TIMESTAMP
_TSManagerSend2 (void *handle, void *key, int send)
{
TSManager *tsm = (TSManager *) handle;
int i;
int index = -1;
TSM_TIMESTAMP ts0 = 0, tstmp = TSM_TIMESTAMP_NONE;
unsigned long long age = 0;
TSM_TIMESTAMP half_interval;
if (tsm) {
i = tsm->tx;
half_interval = TSM_ADAPTIVE_INTERVAL (tsm) >> 1;
if (send) {
tsm->tx_cnt++;
} else {
tsm->cnt++;
tsm->invalid_ts_count++;
}
if (tsm->cnt > 0) {
if (send) {
tsm->cnt--;
}
if (tsm->mode == MODE_AI) {
if (tsm->first_tx == 0) {
tstmp = tsm->last_ts_sent + TSM_ADAPTIVE_INTERVAL (tsm);
} else {
tstmp = tsm->last_ts_sent;
}
while (i != tsm->rx) {
if (index >= 0) {
if (tsm->ts_buf[i].ts < ts0) {
ts0 = tsm->ts_buf[i].ts;
age = tsm->ts_buf[i].age;
index = i;
}
} else {
ts0 = tsm->ts_buf[i].ts;
age = tsm->ts_buf[i].age;
index = i;
}
if ((TSM_KEY_IS_VALID (key)) && (key == tsm->ts_buf[i].key))
break;
i = ((i + 1) % tsm->ts_buf_size);
}
if (index >= 0) {
if ((tsm->invalid_ts_count) && (ts0 >= ((tstmp) + half_interval))
&& (age > tsm->age)) {
/* use calculated ts0 */
if (send) {
tsm->invalid_ts_count--;
}
} else {
if (send) {
if (index != tsm->tx) {
tsm->ts_buf[index] = tsm->ts_buf[tsm->tx];
}
tsm->tx = ((tsm->tx + 1) % tsm->ts_buf_size);
}
#if 0
if (ts0 >= ((tstmp) + half_interval))
tstmp = tstmp;
else
tstmp = ts0;
#else
tstmp = ts0;
#endif
}
} else {
if (send) {
tsm->invalid_ts_count--;
}
}
if (tsm->first_tx == 0) {
if (tstmp > tsm->last_ts_sent) {
ts0 = (tstmp - tsm->last_ts_sent);
} else {
ts0 = 0;
tstmp = tsm->last_ts_sent;
}
if (ts0 > TSM_ADAPTIVE_INTERVAL (tsm) * 3 / 2) {
TSM_WARNING ("Jitter1:%" TSM_TIME_FORMAT " %" TSM_TIME_FORMAT "\n",
TSM_TIME_ARGS (ts0),
TSM_TIME_ARGS (TSM_ADAPTIVE_INTERVAL (tsm) * 3 / 2));
} else if (ts0 == 0) {
TSM_WARNING ("Jitter:%" TSM_TIME_FORMAT "\n", TSM_TIME_ARGS (ts0));
}
if (send) {
if ((ts0 < TSM_ADAPTIVE_INTERVAL (tsm) * 5) || (tsm->big_cnt > 3)) {
tsm->big_cnt = 0;
tsm->dur_history_total -=
tsm->dur_history_buf[tsm->dur_history_tx];
tsm->dur_history_buf[tsm->dur_history_tx] = ts0;
tsm->dur_history_tx =
((tsm->dur_history_tx + 1) % TSM_HISTORY_SIZE);
tsm->dur_history_total += ts0;
} else {
tsm->big_cnt++;
}
}
}
if (send) {
tsm->last_ts_sent = tstmp;
tsm->age++;
tsm->first_tx = 0;
}
} else if (tsm->mode == MODE_FIFO) {
tstmp = tsm->ts_buf[tsm->tx].ts;
if (send) {
tsm->tx = ((tsm->tx + 1) % tsm->ts_buf_size);
}
ts0 = tstmp - tsm->last_ts_sent;
if (send) {
tsm->last_ts_sent = tstmp;
}
}
if (send) {
TSM_LOG ("--Send %d:%" TSM_TIME_FORMAT ", int:%" TSM_TIME_FORMAT
", avg:%" TSM_TIME_FORMAT " inkey %p\n", tsm->tx_cnt,
TSM_TIME_ARGS (tstmp), TSM_TIME_ARGS (ts0),
TSM_TIME_ARGS (TSM_ADAPTIVE_INTERVAL (tsm)), key);
}
} else {
if (tsm->valid_ts_received == 0) {
if (tsm->first_tx) {
tstmp = tsm->last_ts_sent;
} else {
tstmp = tsm->last_ts_sent + TSM_ADAPTIVE_INTERVAL (tsm);
}
if (send) {
tsm->first_tx = 0;
tsm->last_ts_sent = tstmp;
}
}
TSM_ERROR ("Too many timestamps send!!\n");
}
if (send == 0) {
tsm->cnt--;
tsm->invalid_ts_count--;
}
}
return tstmp;
}
TSM_TIMESTAMP
TSManagerSend2 (void *handle, void *key)
{
return _TSManagerSend2 (handle, key, 1);
}
TSM_TIMESTAMP
TSManagerQuery2 (void *handle, void *key)
{
return _TSManagerSend2 (handle, key, 0);
}
TSM_TIMESTAMP
TSManagerSend (void *handle)
{
return TSManagerSend2 (handle, TSM_KEY_NONE);
}
TSM_TIMESTAMP
TSManagerQuery (void *handle)
{
return TSManagerQuery2 (handle, TSM_KEY_NONE);
}
void
resyncTSManager (void *handle, TSM_TIMESTAMP synctime, TSMGR_MODE mode)
{
TSManager *tsm = (TSManager *) handle;
if (tsm) {
TSMRecivedCtl *rctl = &tsm->rctl;
TSMReceivedEntry *e = rctl->head;
while ((e = rctl->head)) {
rctl->head = e->next;
tsm_free_received_entry (rctl, e);
};
rctl->cnt = 0;
rctl->tail = NULL;
tsm->first_tx = 1;
tsm->first_rx = 1;
tsm->suspicious_ts = 0;
if (TSM_TS_IS_VALID (synctime))
tsm->last_ts_sent = synctime;
tsm->tx = tsm->rx = 0;
tsm->invalid_ts_count = 0;
tsm->mode = mode;
tsm->age = 0;
tsm->rx_cnt = tsm->tx_cnt = tsm->cnt = 0;
tsm->valid_ts_received = 0;
tsm->big_cnt = 0;
}
}
/*======================================================================================
FUNCTION: mfw_gst_init_ts
DESCRIPTION: malloc and initialize timestamp strcture
ARGUMENTS PASSED: ppTimeStamp_Object - pointer of TimeStamp Manager to handle related timestamp
RETURN VALUE: TimeStamp structure pointer
PRE-CONDITIONS: None
POST-CONDITIONS: None
IMPORTANT NOTES: None
=======================================================================================*/
void *
createTSManager (int ts_buf_size)
{
TSManager *tsm = (TSManager *) malloc (sizeof (TSManager));
debug = getenv (debug_env);
if (debug) {
debug_level = atoi (debug);
}
// printf("debug = %s \n ++++++++++++++++++++++++++++",debug);
if (tsm) {
memset (tsm, 0, sizeof (TSManager));
if (ts_buf_size <= 0) {
ts_buf_size = TSM_DEFAULT_TS_BUFFER_SIZE;
}
tsm->ts_buf_size = ts_buf_size;
tsm->ts_buf = malloc (sizeof (TSMControl) * ts_buf_size);
if (tsm->ts_buf == NULL) {
goto fail;
}
resyncTSManager (tsm, (TSM_TIMESTAMP) 0, MODE_AI);
tsm->dur_history_tx = 0;
TSM_BUFFER_SET (tsm->dur_history_buf, TSM_DEFAULT_INTERVAL,
TSM_HISTORY_SIZE);
tsm->dur_history_total = TSM_DEFAULT_INTERVAL << TSM_HISTORY_POWER;
tsm->discont_threshold = 10000000000LL; // 10s
}
return tsm;
fail:
if (tsm) {
if (tsm->ts_buf) {
free (tsm->ts_buf);
}
free (tsm);
tsm = NULL;
}
return tsm;
}
void
destroyTSManager (void *handle)
{
TSManager *tsm = (TSManager *) handle;
if (tsm) {
TSMRecivedCtl *rctl = &tsm->rctl;
TSMReceivedEntryMemory *rmem;
if (tsm->ts_buf) {
free (tsm->ts_buf);
}
while ((rmem = rctl->memory)) {
rctl->memory = rmem->next;
free (rmem);
}
free (tsm);
tsm = NULL;
}
}
void
setTSManagerFrameRate (void *handle, int fps_n, int fps_d)
//void setTSManagerFrameRate(void * handle, float framerate)
{
TSManager *tsm = (TSManager *) handle;
TSM_TIMESTAMP ts;
if ((fps_n > 0) && (fps_d > 0) && (fps_n / fps_d <= 80))
ts = TSM_SECOND * fps_d / fps_n;
else
ts = TSM_DEFAULT_INTERVAL;
// TSM_TIMESTAMP ts = TSM_SECOND / framerate;
if (tsm) {
TSM_BUFFER_SET (tsm->dur_history_buf, ts, TSM_HISTORY_SIZE);
tsm->dur_history_total = (ts << TSM_HISTORY_POWER);
if (debug)
TSM_LOG ("Set frame intrval:%" TSM_TIME_FORMAT "\n", TSM_TIME_ARGS (ts));
}
}
TSM_TIMESTAMP
getTSManagerFrameInterval (void *handle)
{
TSManager *tsm = (TSManager *) handle;
TSM_TIMESTAMP ts = 0;
if (tsm) {
ts = TSM_ADAPTIVE_INTERVAL (tsm);
}
return ts;
}
TSM_TIMESTAMP
getTSManagerPosition (void *handle)
{
TSManager *tsm = (TSManager *) handle;
TSM_TIMESTAMP ts = 0;
if (tsm) {
ts = tsm->last_ts_sent;
}
return ts;
}
int
getTSManagerPreBufferCnt (void *handle)
{
int i = 0;
TSManager *tsm = (TSManager *) handle;
if (tsm) {
i = tsm->rctl.cnt;
}
return i;
}