vlc讀取rtsp流-源碼分析---時間戳dts的計算

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__);
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章