RTP缓冲区副作用

时间:2011-11-03 17:26:30

标签: timestamp buffer directshow rtsp rtp

在我的RTSP Source Filter的先前版本中,我立即打包RTP数据包以便接收它们。没有缓冲区,按序列号排序,丢失丢失的帧等等,但结果还不错,因为我在局域网上进行了测试。我决定添加一个RTP缓冲区,排序机制等。实际上,核心代码是成功的。但是现在我在发送具有正确时序的帧时遇到了一些问题。

我有一个调试机制。我能够将所有帧存储到单独的文件中,如frame0.bin,frame1.bin等。我有一个能够读取这些文件并将其发送到h.264解码器的工具。当我播放这些帧时,结果是完美的。我相信这证明我的RTP和帧缓冲区工作正常。

当我在创建一个帧时尝试将帧传递给FillBuffer函数时(实际上当我的帧队列有超过1帧时),结果真的很糟糕(我得到图像但有延迟和损坏的帧)。这可能是由于立即将帧传递给FillBuffer函数引起的。我的DoBufferProcessingLoop函数如下所示(我从代码中删除了大部分错误检查);

HRESULT RtspSourceFilterOutputPin::DoBufferProcessingLoop() {

    Command com;
    REFERENCE_TIME rtNow = 0L;
    REFERENCE_TIME rtAdvise = 0L;

    OnThreadStartPlay();

    do {
        while (!CheckRequest(&com)) {
            if(streamReader->frames.size() > 1) {
                IMediaSample *pSample;
                GetDeliveryBuffer(&pSample,NULL,NULL,FALSE);

                hr = FillBuffer(pSample);

                if (hr == S_OK) {
                    HRESULT result = Deliver(pSample);
                } else if (hr == S_FALSE) {
                    pSample->Release();
                    DeliverEndOfStream();
                    return S_OK;
                } else {
                    //error
                }
                pSample->Release();
            }
        }

        if (com == CMD_RUN || com == CMD_PAUSE) {
            com = GetRequest();
        } else if (com != CMD_STOP) {
            //error
        }
    } while (com != CMD_STOP);

    return S_OK;
}

由于流式传输是实时的,因此我从SetTime函数中删除了所有时间戳(SetMediaTimeFillBuffer等)。 FillBuffer没有什么特别之处。它只是从帧队列中弹出一帧并将其传递给解码器。如果删除我的缓冲机制,它就可以工作。

最后,问题是......

我应该在发送帧之前等待一段时间,比如“接收时间+缓冲时间”,而不是在创建帧后立即将其传递给FillBuffer吗?或者我应该在SetTime函数中使用FillBuffer吗?我已经尝试过但失败了。我还尝试在添加缓冲时间的情况下给出开始/停止时间值但是没有更好的效果。

当我将帧传递给解码器时,为什么结果很糟糕但是如果我将这些帧保存到二进制文件并从这些文件播放,结果是否正常?我在这里缺少什么?

1 个答案:

答案 0 :(得分:2)

只要您的样品有正确的时间戳,您缓冲的事实就不会有任何区别。如果您没有为样本添加时间戳(例如,如

中那样使用NULL调用SetTime
pSample->SetTime(NULL, NULL);

渲染器将尽可能快地渲染样本。如果设置了时间戳,渲染器将相应地渲染帧(即当前流时间)。也许你的缓冲导致样品迟到?在任何情况下,将时间戳设置为NULL是测试是否接收到所有媒体并且是否有序的好方法。

所以要回答你的问题,是的,你需要在样本上设置时间戳:实时并不意味着你可以删除时间戳。如果要正确查看流,网络抖动将导致样本的到达时间不同,并且必须正确设置时间戳。在将样本传递给FillBuffer之前,您无需等待任何时间,只要设置了时间戳,渲染器就会正确播放样本。

我的方法运作得相当好,如下: 在RTSP源过滤器中执行所需的缓冲。然后,填充缓冲区后,计算将用于每个样本的偏移量。 您需要在开始播放样本的时间点考虑接收到的第一个RTCP同步样本(ts_0)和DirectShow StreamTime(st_0)的RTP时间戳。当您开始通过管道传递样本时,流时间可能不再为零....

然后将每个样本的DirectShow时间戳ts_new计算为

ts_new_x = ts_x - ts_0 + st_0 + 50ms

50ms对于将样本的时间戳设置为将来很有用。这样做的原因是为了避免样品迟到到渲染器。 样本的结束时间可以设置为ts_new_x + 1.无论如何,IIRC会被忽略,但不要引用我。

如果您还没有,我建议您多次阅读http://msdn.microsoft.com/en-us/library/windows/desktop/dd407202(v=vs.85).aspx中的所有部分。我发现这在实施过程中非常有用。

另外,请随时查看我写的open source version of an RTSP source filter。它的主要目的是强调编写实时RTSP DirectShow源过滤器的一些方面,并支持PCM,AMR和MP3。我确实为它添加了一些次要的H.264支持,但从未完成该部分的完成(IIRC它能够播放live555样本视频流)。