1

我有一个 RTSP 客户端和服务器的基于 live555 的自定义实现。我正在使用 Live555 的 16/08/2013 版本。我正在使用 Interleaved RTP-OVER-TCP 进行流式传输,因为我们将使用的防火墙不允许我们使用 UDP。(也就是说,RTP、RTCP 和 RTSP 数据都将通过 TCP 发送)。当我部署应用程序时,在我的本地网络上,流式传输运行良好。然而,在一个有轻微延迟的 WAN 上,我从服务器收到“405 Method Not Allowed”错误。我已经能够通过限制我的带宽在我的本地主机上模拟这一点,以便数据流动得慢一点。如果我限制网络,我会收到“405 Method Not Allowed”错误,如果我不限制它,我不会收到此错误并且流媒体工作正常。我已经使用网络流量检查器来观察客户端发送和接收的数据,并且我注意到在出现错误的情况下,客户端会在 PLAY 命令之前发送一个以 $ 分隔的数据,然后是一些额外的二进制数据发出。我认为这会使服务器感到困惑,因为服务器可能希望仅在建立会话并发出 PLAY 命令后才能接收此类数据。

我已经手动尝试过滤掉“不需要的”消息,直到发出 PLAY 命令。如果我这样做,流式传输实际上开始并且我能够渲染一两帧,之后视频流似乎“冻结”。然而,在检查网络流量时,似乎流确实像常规视频流一样在后台流动,但是我在 SETUP 期间丢弃了“不合适”的数据片段的事实导致视频流不呈现超过最初的几微秒。

我很清楚以 $ 分隔的消息代表 RTP 或 RTCP 数据包。然而,我没想到客户端会在 RTSP 命令完成设置流会话并开始播放之前开始发送此类数据包。

有人可以帮助向我解释这些“乱序”的 RTP/RTCP 数据包是什么以及为什么客户端会发送它们吗?我怎么解决这个问题?

作为参考,我在下面的日志中包含了显示来自客户端的消息,代表错误:

##Client Sends:##
DESCRIBE rtsp://127.0.0.1:8554/ RTSP/1.0
CSeq: 2
User-Agent: LIVE555 Streaming Media v2013.08.16
Accept: application/sdp

##Client Receives:##
RTSP/1.0 200 OK
CSeq: 2
Date: Thu, Aug 29 2013 06:18:42 GMT
Content-Base: rtsp://127.0.0.1:8554/nurv/
Content-Type: application/sdp
Content-Length: 449

v=0
o=- 1377757120235695 1 IN IP4 192.168.56.1
s=MyVideo Streaming Session
i=nurv
t=0 0
a=tool:LIVE555 Streaming Media v2013.08.16
a=type:broadcast
a=control:*
a=range:npt=0-
a=x-qt-text-nam:MyVideo Streaming Session
a=x-qt-text-inf:nurv
m=video 0 RTP/AVP 96
c=IN IP4 0.0.0.0
b=AS:3750
a=rtpmap:96 H264/90000
a=control:track1
m=audio 0 RTP/AVP 96
c=IN IP4 0.0.0.0
b=AS:84602240
a=rtpmap:96 PCMU/48000/2
a=control:track2

##Client Sends:##
SETUP rtsp://127.0.0.1:8554/nurv/track1 RTSP/1.0
CSeq: 3
User-Agent: LIVE555 Streaming Media v2013.08.16
Transport: RTP/AVP/TCP;unicast;interleaved=0-1

##Client Receives:##
RTSP/1.0 200 OK
CSeq: 3
Date: Thu, Aug 29 2013 06:18:43 GMT
Transport: RTP/AVP/TCP;unicast;destination=127.0.0.1;source=127.0.0.1;interleaved=0-1
Session: 32A854D4

##Client Sends:##
SETUP rtsp://127.0.0.1:8554/nurv/track2 RTSP/1.0
CSeq: 4
User-Agent: LIVE555 Streaming Media v2013.08.16
Transport: RTP/AVP/TCP;unicast;interleaved=2-3
Session: 32A854D4

##THIS IS THE "OUT-OF-SEQUENCE DATA THAT CAUSES THE PROBLEM. THIS SECTION IS ONLY SENT WHEN NETWORK HAS SOME LATENCY AND DOES NOT APPEAR WHEN STREAMING WORKS.##
##Client Sends:##
00000000  24 01 00 20                                        $..             

00000000  80 C9 00 01 23 7A EB 1D 81 CA 00 05 23 7A EB 1D    ....#z......#z..
00000010  6C 61 70 74 6F 70 6E 61 6D 65 0D 0A 00 00 00 00    ..LaptopName....
##END OF SECTION THAT SHOWS THE "OUT-OF-SEQUENCE" DATA.##

##Client Receives:##
RTSP/1.0 200 OK
CSeq: 4
Date: Thu, Aug 29 2013 06:18:47 GMT
Transport: RTP/AVP/TCP;unicast;destination=127.0.0.1;source=127.0.0.1;interleaved=2-3
Session: 32A854D4

##Client Sends:##
PLAY rtsp://127.0.0.1:8554/nurv/ RTSP/1.0
CSeq: 5
User-Agent: LIVE555 Streaming Media v2013.08.16
Session: 32A854D4
Range: npt=0.000-

##THIS IS THE ERROR RECEIVED FROM THE SERVER. IN CASES WHERE STREAMING WORKS, THIS ERROR IS NOT RECEIVED BUT WE RATHER START RECEIVING RTP AND RTCP PACKETS (DATA) FROM THE SERVER.##
##Client Receives:##
RTSP/1.0 405 Method Not Allowed
CSeq: 5
Date: Thu, Aug 29 2013 06:18:48 GMT
Allow: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER

##Client Sends:##
TEARDOWN rtsp://127.0.0.1:8554/nurv/ RTSP/1.0
CSeq: 6
User-Agent: LIVE555 Streaming Media v2013.08.16
Session: 32A854D4
4

1 回答 1

0

在对 Live555 本身做了一些深入调试后,我发现它包含一个错误。$-delimited 数据基本上是在 PLAY 命令发出之前错误地发送的 RTCP“RR”数据包(它应该只发生在 PLAY 发出之后)。live555 的 2012/10/04 版本似乎包含一个“EnableRTCPReports”标志来指示是否可以继续发送“RR”,并且该值设置为仅在 PLAY 发布后才允许发送这些报告。

但是,在实际执行发送的代码中,有一种情况是在发送“RR”报告之前未检查此标志。这是我发现的错误。此错误报告已与修复程序一起提交给 Live555 项目,但我也将修复程序放在这里以供同时遇到它的任何人使用:

livemedia/include/RTSPSource.hh我们有 enableRTCPReports 函数来返回 fEnableRTCPReports。但是,这不能由 const 对象调用(这是我们需要做的),所以只需声明一个非常相似的函数,但使其成为 const 且安全:

...
Boolean& enableRTCPReports() { return fEnableRTCPReports; }
//****The following function is part of the fix. The same as enableRTCPReports except that it is "const" and "safe" for const object references to call, which means we can call this from RTCPInstance using the fSource object there.
Boolean constAccessibleEnableRTCPReports() const { return fEnableRTCPReports; } 
...

在 livemedia/RTCP.cpp 中:

...
Boolean RTCPInstance::addReport(Boolean alwaysAdd) {
  // Include a SR or a RR, depending on whether we have an associated sink or source:
  if (fSink != NULL) {
    if (!alwaysAdd) {
      if (!fSink->enableRTCPReports()) return False;

      // Hack: Don't send a SR during those (brief) times when the timestamp of the
      // next outgoing RTP packet has been preset, to ensure that that timestamp gets
      // used for that outgoing packet. (David Bertrand, 2006.07.18)
      if (fSink->nextTimestampHasBeenPreset()) return False;
    }

    addSR();
  } else if (fSource != NULL) {
     //****The following IF-statement is the fix. As in the case of the Sink Node above (but using our "const-accessible" function), we first check the value of EnableRTCPReports before we do addRR().
    if (!fSource->constAccessibleEnableRTCPReports()) return false;
    addRR();
  }

  return True;
}
...
于 2013-09-11T07:40:26.440 回答