h264流中有兩個概念,pts,dts: vlc源碼分析--播放速度控制原理,pts,dts
vlc 使用插件的形式開發,讀取rtsp流時,會根據url地址解析,加載 liblive555_plugin.so 插件,作爲demuxer使用,輸入線程 (input.c 中)會調用 demuxer的 demux() 函數,驅動其解複用。
liblive555_plugin.so的源碼文件,在 /modules/access/live555.cpp
使用的是 live555開源項目,所以這裏的初始化函數 open() 即是一個live555 的一個RTSPClient 客戶端,使用方法基本遵循 live555:testRTSPClient.cpp 這個live555提供的例子。
demux() 函數從live555庫讀取到數據後,調用到設置的回調StreamRead( void *p_private, unsigned int i_size, unsigned int i_truncated_bytes, struct timeval pts,unsigned int duration );這裏的參數 timeval pts, 即live555庫從網絡rtp流中解出來的幀的時間戳。如果是一幀 視頻,被切分成多個 網絡包傳輸,那麼這幾個包的pts 都是一樣的值。
由於h264 流的特性,DTS:解碼序列 PTS:顯示序列 兩者可能不一致,而這裏只有從rtp流獲取到的pts, 所以vlc這裏,有一套機制利用這個pts重新計算出了一個dts . 確保 dts的值都是按從小到大有序的。
1.0 dtsgen_Init( &tk->dtsgen );
2.0 dtsgen_AddNextPTS( &tk->dtsgen, i_pts );
3.0 p_block->i_dts = dtsgen_GetDTS( &tk->dtsgen );
利用pts(要顯示的順序)計算出了一個dts(要解碼的順序)
這個計算邏輯,live555_dtsgen.h中官方解釋是,確保 dts的值都是按從小到大有序的。最理想的狀態的是:
比如輸入pts:P0 P2 P3[P1]P5 P7 P8 P6 那麼解析出 dts: [D0]D1 D2 D3 D4 D5 D6 D7 顯然,要獲取到整個的真確順序,需要把後續的亂序的幀都獲取到之後,纔可能完整解析出正常順序。多讀到幾幀再排序這樣又會導致整個視頻流的延遲。
提取出這個計算的邏輯:(ubuntu + gcc 可以單獨運行,測試)
來自 vlc 3.0.6 /modules/access/live555_dtsgen.h
/*****************************************************************************
* 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.
*****************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef long int64_t;
typedef unsigned long uint64_t;
typedef int64_t mtime_t;
//#define VLC_TS_INVALID INT64_C(0)
//#define VLC_TS_0 INT64_C(1)
//#define CLOCK_FREQ INT64_C(1000000)
#define VLC_TS_INVALID (0)
#define VLC_TS_0 (1)
#define CLOCK_FREQ (1000000)
/* __MAX and __MIN: self explanatory */
#ifndef __MAX
# define __MAX(a, b) ( ((a) > (b)) ? (a) : (b) )
#endif
#ifndef __MIN
# define __MIN(a, b) ( ((a) < (b)) ? (a) : (b) )
#endif
#define DTSGEN_REORDER_MAX 4 /* should be enough */
#define DTSGEN_HISTORY_COUNT (DTSGEN_REORDER_MAX + 2)
//#define DTSGEN_DEBUG
struct dtsgen_t
{
mtime_t history[DTSGEN_HISTORY_COUNT];
mtime_t ordereddts[DTSGEN_HISTORY_COUNT];
mtime_t i_startingdts;
mtime_t i_startingdiff;
unsigned reorderdepth;
unsigned count;
};
static int cmpvlctickp(const void *p1, const void *p2)
{
if(*((mtime_t *)p1) >= *((mtime_t *)p2))
return *((mtime_t *)p1) > *((mtime_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,
* substracting 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, mtime_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 mtime_t dtsgen_GetDTS(struct dtsgen_t *d)
{
mtime_t i_dts = VLC_TS_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] - 150000, VLC_TS_0);
d->i_startingdiff = d->history[0] - i_dts;
}
else if(d->count > 1)
{
mtime_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,
mtime_t dts, mtime_t pts)
{
if(pts == VLC_TS_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
int main()
{
struct dtsgen_t dtsgen;
dtsgen_Init( &dtsgen );
dtsgen_Resync( &dtsgen );
int64_t pts[8]={0,2,3,1,5,7,8,6};
//int64_t pts[10]={1,2,3,4,5,7,8,6,9,10};
int i =0 ;
for(i = 0; i<sizeof(pts)/sizeof(pts[0]); i++)
{
dtsgen_AddNextPTS( &dtsgen, pts[i] );
printf("get dts :%ld\n", dtsgen_GetDTS( &dtsgen ));
}
printf("wang hello world [%d%s]\n",__LINE__,__FUNCTION__);
}