RTSP客户端在会话完全设置(发出PLAY命令)之前发送以$分隔的数据,导致405方法不允许错误



我有一个自定义的基于live555的RTSP客户端和服务器实现。我使用的是2013年8月16日版本的Live555。我正在使用交错RTP-OVER-TCP进行流式传输,因为我们将使用的防火墙不允许我们使用UDP。(换句话说,RTP、RTCP和RTSP数据都将通过TCP发送)。当我部署应用程序时,在我的本地网络上,流媒体可以完美地工作。然而,在有轻微延迟的WAN上,我从服务器收到了"405方法不允许"的错误。我已经能够在本地主机上通过限制带宽来模拟这一点,这样数据流就会稍微慢一点。如果我节流网络,我会得到"405方法不允许"错误,如果我不节流,我就不会得到这个错误,流媒体也能正常工作。我使用了一个网络流量检查器来观察客户端发送和接收的数据,我注意到在出现错误的情况下,客户端会在发出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

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

然而,在实际执行发送的代码中,有一个条件,即在发送"RR"报告之前,不检查此标志。这就是我发现的bug。这个错误报告已经连同修复程序一起提交到Live555项目,但我也将修复程序放在这里,供在此期间遇到它的人使用:

livemedia/include/RTSSource.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; } 
...

在直播媒体/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;
}
...

相关内容

  • 没有找到相关文章

最新更新