You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

184 lines
5.9 KiB

/*****************************************************************************
* live555_dtsgen.h : DTS rebuilder for pts only streams
*****************************************************************************
* Copyright (C) 2018 VideoLabs, VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#define DTSGEN_REORDER_MAX 4 /* should be enough */
#define DTSGEN_HISTORY_COUNT (DTSGEN_REORDER_MAX + 2)
//#define DTSGEN_DEBUG
struct dtsgen_t
{
vlc_tick_t history[DTSGEN_HISTORY_COUNT];
vlc_tick_t ordereddts[DTSGEN_HISTORY_COUNT];
vlc_tick_t i_startingdts;
vlc_tick_t i_startingdiff;
unsigned reorderdepth;
unsigned count;
};
static int cmpvlctickp(const void *p1, const void *p2)
{
if(*((vlc_tick_t *)p1) >= *((vlc_tick_t *)p2))
return *((vlc_tick_t *)p1) > *((vlc_tick_t *)p2) ? 1 : 0;
else
return -1;
}
static void dtsgen_Init(struct dtsgen_t *d)
{
d->count = 0;
d->reorderdepth = 0;
}
static void dtsgen_Resync(struct dtsgen_t *d)
{
d->count = 0;
d->reorderdepth = 0;
}
#define dtsgen_Clean(d)
/*
* RTSP sends in decode order, but only provides PTS as timestamp
* P0 P2 P3 P1 P5 P7 P8 P6
* D0 D2 D3 D1 D5 D7 D8 D6 <- wrong !
* creating a non monotonical sequence when used as DTS, then PCR
*
* We need to have a suitable DTS for proper PCR and transcoding
* with the usual requirements DTS0 < DTS1 and DTSN < PTSN
*
* So we want to find the closest DTS matching those conditions
* P0 P2 P3[P1]P5 P7 P8 P6
* [D0]D1 D2 D3 D4 D5 D6 D7
*
* Which means that within a reorder window,
* we want the PTS time index after reorder as DTS
* [P0 P2 P3 P1]P5 P7 P8 P6
* [P0 P1 P2 P3] reordered
* [D0 D1 D2 D3]D4 D5 D6 D7
* we need to pick then N frames before in reordered order (== reorder depth)
* P0 P2 P3[P1]P5 P7 P8 P6
* [D0]D1 D2 D3 D4 D5 D6 D7
* so D0 < P1 (we can also pick D1 if we want DTSN <= PTSN)
*
* Since it would create big delays with low fps streams we need
* - to avoid buffering packets
* - to detect real reorder depth (low fps usually have no reorder)
*
* We need then to:
* - Detect reorder depth
* - Keep track of last of N past timestamps, > maximum possible reorder
* - Make sure a suitable dts is still created while detecting reorder depth
*
* While receiving the N first packets (N>max reorder):
* - check if it needs reorder, or increase depth
* - create slow increments in DTS while taking any frame as a start,
* subtracting the total difference between first and last packet,
* and removing the possible offset after reorder,
* divided by max possible frames.
*
* Once reorder depth is fully known,
* - use N previous frames reordered PTS as DTS for current PTS.
* (with mandatory gap/increase in DTS caused by previous step)
*/
static void dtsgen_AddNextPTS(struct dtsgen_t *d, vlc_tick_t i_pts)
{
/* Check saved pts in reception order to find reordering depth */
if(d->count > 0 && d->count < DTSGEN_HISTORY_COUNT)
{
unsigned i;
if(d->count > (1 + d->reorderdepth))
i = d->count - (1 + d->reorderdepth);
else
i = 0;
for(; i < d->count; i++)
{
if(d->history[i] > i_pts)
{
if(d->reorderdepth < DTSGEN_REORDER_MAX)
d->reorderdepth++;
}
break;
}
}
/* insert current */
if(d->count == DTSGEN_HISTORY_COUNT)
{
d->ordereddts[0] = i_pts; /* remove lowest */
memmove(d->history, &d->history[1],
sizeof(d->history[0]) * (d->count - 1));
}
else
{
d->history[d->count] = i_pts;
d->ordereddts[d->count++] = i_pts;
}
/* order pts in second list, will be used as dts */
qsort(&d->ordereddts, d->count, sizeof(d->ordereddts[0]), cmpvlctickp);
}
static vlc_tick_t dtsgen_GetDTS(struct dtsgen_t *d)
{
vlc_tick_t i_dts = VLC_TICK_INVALID;
/* When we have inspected enough packets,
* use the reorderdepth th packet as dts offset */
if(d->count > DTSGEN_REORDER_MAX)
{
i_dts = d->ordereddts[d->count - d->reorderdepth - 1];
}
/* When starting, we craft a slow incrementing DTS to ensure
we can't go backward due to reorder need */
else if(d->count == 1)
{
d->i_startingdts =
i_dts = __MAX(d->history[0] - VLC_TICK_FROM_MS(150), VLC_TICK_0);
d->i_startingdiff = d->history[0] - i_dts;
}
else if(d->count > 1)
{
vlc_tick_t i_diff = d->ordereddts[d->count - 1] -
d->ordereddts[0];
i_diff = __MIN(d->i_startingdiff, i_diff);
d->i_startingdts += i_diff / DTSGEN_REORDER_MAX;
i_dts = d->i_startingdts;
}
return i_dts;
}
#ifdef DTSGEN_DEBUG
static void dtsgen_Debug(vlc_object_t *p_demux, struct dtsgen_t *d,
vlc_tick_t dts, vlc_tick_t pts)
{
if(pts == VLC_TICK_INVALID)
return;
msg_Dbg(p_demux, "dtsgen %" PRId64 " / pts %" PRId64 " diff %" PRId64 ", "
"pkt count %u, reorder %u",
dts % (10 * CLOCK_FREQ),
pts % (10 * CLOCK_FREQ),
(pts - dts) % (10 * CLOCK_FREQ),
d->count, d->reorderdepth);
}
#else
#define dtsgen_Debug(a,b,c,d)
#endif