RTP Timestamp to presentationTime (live555)

RTSP流中实际传输音频或视频数据为一个个RTP包,每个RTP包的Header的第5个到第8个字节为RTP Timestamp(时间戳),是个32bit的整数。

RTP Header

而live555中类似H264or5VideoFileSink::afterGettingFrame(接收每帧数据)函数中:

void H264or5VideoFileSink::afterGettingFrame(unsigned frameSize, 
                                             unsigned numTruncatedBytes, 
                                             struct timeval presentationTime) 
{
    ...
}

实际接收到的时间戳为 struct timeval presentationTime,
其中:

struct timeval {
        long    tv_sec;         /* seconds */
        long    tv_usec;        /* and microseconds */
};

举例示意如下:

转换前:
rtpTimestamp:439803124

转换后:
presentationTime.tv_sec: 1482476415
presentationTime.tv_usec:183008 

在live555中,两者是如何实现转换的呢?

liveMedia\RTPSource.cppRTPReceptionStats::noteIncomingPacket函数中实现这一转换。

RTPReceptionStats::noteIncomingPacket
void RTPReceptionStats::noteIncomingPacket(u_int16_t seqNum, 
                                           u_int32_t rtpTimestamp,
                                           unsigned timestampFrequency,
                                           Boolean useForJitterCalculation,
                                           struct timeval& resultPresentationTime,
                                           Boolean& resultHasBeenSyncedUsingRTCP,
                                           unsigned packetSize) 
{
    ...

    // Record the inter-packet delay
    struct timeval timeNow;
    gettimeofday(&timeNow, NULL);

    ...

    fLastPacketReceptionTime = timeNow;

    ... 

    // Return the 'presentation time' that corresponds to "rtpTimestamp":
    if (fSyncTime.tv_sec == 0 && fSyncTime.tv_usec == 0) 
    {
        // 第一个时间戳
        // 用当前系统时间作为同步时刻
        // 后续将会根据接收到的RTCP SRs进行校正.
        fSyncTimestamp = rtpTimestamp;
        fSyncTime      = timeNow;
    }

    int timestampDiff = rtpTimestamp - fSyncTimestamp;

    // Note: This works even if the timestamp wraps around
    // (as long as "int" is 32 bits)

    // Divide this by the timestamp frequency to get real time:
    double timeDiff = timestampDiff/(double)timestampFrequency;

    // Add this to the 'sync time' to get our result:
    unsigned const million = 1000000;
    unsigned seconds, uSeconds;

    if (timeDiff >= 0.0) 
    {
        // 核心算法
        seconds  = fSyncTime.tv_sec  + (unsigned)(timeDiff);
        uSeconds = fSyncTime.tv_usec + (unsigned)((timeDiff - (unsigned)timeDiff)*million);

        if (uSeconds >= million) 
        {
            uSeconds -= million;
            ++seconds;
        }
    } 
    else 
    {
        timeDiff = -timeDiff;
        seconds  = fSyncTime.tv_sec  - (unsigned)(timeDiff);
        uSeconds = fSyncTime.tv_usec - (unsigned)((timeDiff - (unsigned)timeDiff)*million);
        if ((int)uSeconds < 0) 
        {
            uSeconds += million;
            --seconds;
        }
    }

    resultPresentationTime.tv_sec  = seconds;
    resultPresentationTime.tv_usec = uSeconds;
    resultHasBeenSyncedUsingRTCP   = fHasBeenSynchronized;

    // Save these as the new synchronization timestamp & time:
    fSyncTimestamp = rtpTimestamp;
    fSyncTime      = resultPresentationTime;

    fPreviousPacketRTPTimestamp = rtpTimestamp;
}

输入rtpTimestamp等,输出struct timeval& resultPresentationTime

RTPReceptionStats::noteIncomingPacket() 的调用堆栈
     RTPReceptionStats::noteIncomingPacket()  
    RTPReceptionStatsDB::noteIncomingPacket()  
    MultiFramedRTPSource::networkReadHandler1()  
    MultiFramedRTPSource::networkReadHandler()  
    SocketDescriptor::tcpReadHandler1(int mask, bool callAgain)  
    SocketDescriptor::tcpReadHandler()  
    BasicTaskScheduler::SingleStep(unsigned int maxDelayTime) 
    BasicTaskScheduler0::doEventLoop(volatile char * watchVariable)  
MultiFramedRTPSource
MultiFramedRTPSource
class FramedSource: public MediaSource {
  ...
  struct timeval fPresentationTime; // out
  ...
};

class RTPSource: public FramedSource {
  ...
}

class MultiFramedRTPSource: public RTPSource {

  ...
  static void networkReadHandler(MultiFramedRTPSource* source, int /*mask*/);
  void networkReadHandler1();
};

class BufferedPacket {
  ...
  struct timeval fPresentationTime; // corresponding to "fRTPTimestamp"
  ...
};

MultiFramedRTPSource::networkReadHandler1()
void MultiFramedRTPSource::networkReadHandler1() {
    BufferedPacket* bPacket = fPacketReadInProgress;
    if (bPacket == NULL) {
        // Normal case: Get a free BufferedPacket descriptor to hold the new network packet:
        bPacket = fReorderingBuffer->getFreePacket(this);
    }

    ...

    struct timeval presentationTime; // computed by:
    Boolean hasBeenSyncedUsingRTCP; // computed by:

        // 此函数中调用RTPReceptionStats::noteIncomingPacket()
        // 生成的时间保存在presentationTime
        receptionStatsDB().noteIncomingPacket(rtpSSRC, 
                                              rtpSeqNo, 
                                              rtpTimestamp,
                                              timestampFrequency(),
                                              usableInJitterCalculation, 
                                              presentationTime,
                                              hasBeenSyncedUsingRTCP, bPacket->dataSize());

    // Fill in the rest of the packet descriptor, and store it:
    struct timeval timeNow;
    gettimeofday(&timeNow, NULL);

    // 将presentationTime保存在BufferedPacket的fPresentationTime中
    bPacket->assignMiscParams(rtpSeqNo, 
                              rtpTimestamp, 
                              presentationTime,
                              hasBeenSyncedUsingRTCP, 
                              rtpMarkerBit,
                              timeNow);

    ...
    
    // doGetNextFrame1中调用BufferedPacket::use将保存在BufferedPacket中的fPresentationTime
    // 赋值给FramedSource的fPresentationTime
    doGetNextFrame1();
    // If we didn't get proper data this time, we'll get another chance
}
  • S1: RTPReceptionStats::noteIncomingPacket()
    获取resultPresentationTime

  • S2: BufferedPacket::assignMiscParams()
    resultPresentationTime赋值给BufferedPacketfPresentationTime

void BufferedPacket::assignMiscParams(unsigned short rtpSeqNo, 
                                      unsigned rtpTimestamp,
                                      struct timeval presentationTime,
                                      Boolean hasBeenSyncedUsingRTCP, 
                                      Boolean rtpMarkerBit,
                                      struct timeval timeReceived) 
{
    fRTPSeqNo               = rtpSeqNo;
    fRTPTimestamp           = rtpTimestamp;
    fPresentationTime       = presentationTime;
    fHasBeenSyncedUsingRTCP = hasBeenSyncedUsingRTCP;
    fRTPMarkerBit           = rtpMarkerBit;
    fTimeReceived           = timeReceived;
}
  • S3: doGetNextFrame1()
    将保存在BufferedPacket中的fPresentationTime赋值给FramedSourcefPresentationTime
void MultiFramedRTPSource::doGetNextFrame1() 
{
    while (fNeedDelivery) 
    {
        // If we already have packet data available, then deliver it now.
        Boolean packetLossPrecededThis;
        BufferedPacket* nextPacket
            = fReorderingBuffer->getNextCompletedPacket(packetLossPrecededThis);

        if (nextPacket == NULL) break;

        ...

        // The packet is usable. Deliver all or part of it to our caller:
        unsigned frameSize;
        nextPacket->use(fTo, 
                        fMaxSize, 
                        frameSize, 
                        fNumTruncatedBytes,
                        fCurPacketRTPSeqNum, 
                        fCurPacketRTPTimestamp,
                        fPresentationTime,  // 时间戳
                        fCurPacketHasBeenSynchronizedUsingRTCP,
                        fCurPacketMarkerBit);
        ...
     }
}
void BufferedPacket::use(unsigned char* to, 
                         unsigned toSize,
                         unsigned& bytesUsed, 
                         unsigned& bytesTruncated,
                         unsigned short& rtpSeqNo, 
                         unsigned& rtpTimestamp,
                         struct timeval& presentationTime, // out
                         Boolean& hasBeenSyncedUsingRTCP,
                         Boolean& rtpMarkerBit) 
{
        ...
        rtpTimestamp = fRTPTimestamp;
        presentationTime = fPresentationTime; // 赋值
        ...
}
H264or5VideoFileSink::afterGettingFrame调用堆栈
H264or5VideoFileSink::afterGettingFrame(unsigned int frameSize, unsigned int numTruncatedBytes, timeval presentationTime) 
FileSink::afterGettingFrame(void * clientData, unsigned int frameSize, unsigned int numTruncatedBytes, timeval presentationTime, unsigned int __formal)  
FramedSource::afterGetting(FramedSource * source)  
MultiFramedRTPSource::doGetNextFrame1()  
MultiFramedRTPSource::networkReadHandler1() 
MultiFramedRTPSource::networkReadHandler(MultiFramedRTPSource * source, int __formal)  
SocketDescriptor::tcpReadHandler1(int mask) 
SocketDescriptor::tcpReadHandler(SocketDescriptor * socketDescriptor, int mask)  
BasicTaskScheduler::SingleStep(unsigned int maxDelayTime)  
BasicTaskScheduler0::doEventLoop(volatile char * watchVariable)  
FramedSource::afterGetting()

此函数中将FramedSourcefPresentationTime传给FileSink::afterGettingFrame
将Source(生产者)的视音频数据的buffer、数据大小、时间戳等传给Sink(消费者)

void FramedSource::afterGetting(FramedSource* source) {
  source->fIsCurrentlyAwaitingData = False;
      // indicates that we can be read again
      // Note that this needs to be done here, in case the "fAfterFunc"
      // called below tries to read another frame (which it usually will)

  // source->fPresentationTime即是FramedSource的fPresentationTime
  // fPresentationTime由此传入 

  if (source->fAfterGettingFunc != NULL) {
    (*(source->fAfterGettingFunc))(source->fAfterGettingClientData,
                   source->fFrameSize, source->fNumTruncatedBytes,
                   source->fPresentationTime, 
                   source->fDurationInMicroseconds);
  }
}
FileSink::afterGettingFrame()
void FileSink::afterGettingFrame(void* clientData, 
                                 unsigned frameSize,
                                 unsigned numTruncatedBytes,
                                 struct timeval presentationTime,
                                 unsigned /*durationInMicroseconds*/) 
{
        FileSink* sink = (FileSink*)clientData;

        sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容