[ターン] tcp画面上でrtspを再生しているVlcが突然スタックしました

この記事はhttps://blog.jianchihu.net/video-freeze-rtsp-over-tcp-vlc.htmlから複製されています

原作者の無私無欲な共有に感謝します。

 

rtmpサーバーの開発が完了した後、rtspサーバーが最近作成されました。これにより、国内標準のpsストリームおよびその他の形式のプロトコルコードストリームをrtspプロトコル出力に変換できます。多くのプレーヤーテストが中間開発プロセスで使用されましたが、最も一般的に使用されるのはvlcです。vlcテストプロセスを使用して、私は多くの問題に遭遇しました。今日は奇妙な問題を記録します。

rtp over udpモードで再生する場合は問題ありませんが、rtp over tcpモードで再生する場合、vlcを再生してから数十秒後に突然画像がフリーズします。vlcのデバッグメッセージを読みましたが、異常はありませんでした。ffplay、live555、potplayerでテストされており、例外はありません。後で、テスト用に別のバージョンのvlcを変更しましたが、それはさらに奇妙で、vlc3.0.0以前、3.0.5以降のバージョンはすべて正常です。vlcがtcpよりもrtpに対して特別な処理を行っているはずです。この時点で、パケットがキャプチャされ、rtspインタラクティブデータが分析され、問題のあるバージョンのvlcがOPTIONSコマンドを定期的に送信するだけでなく、「$」で始まる特別なバイトの文字列も送信することがわかりました。送信後この再生画面、スタックしました。なぜ動かなくなって遊んでいないのですか?問題を見つけるためにvlcソースコードを見ることができるだけです。

関連するソースコードを読んだ後、私はついに原因を突き止めました。これは、vlcのキープアライブメカニズムが原因で発生します。vlcはrtsp処理にlive555を使用するため、対応する処理コードはファイルmodules / access /live555.cppにあります。以下のコードと組み合わせて理由を話しましょう。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

static void TimeoutPrevention(void * p_data)

{{

    demux_t * p_demux =(demux_t *)p_data;

    demux_sys_t * p_sys =(demux_sys_t *)p_demux-> p_sys;

    char * bye = NULL;

 

    if(var_GetBool(p_demux、 "rtsp-tcp"))

        戻る;

 

    / * Live555を関数を同時に呼び出すことから保護します

        Demux()またはControl()を使用* /

    vlc :: threads :: mutex_locker locker(p_sys-> timeout_mutex);

 

    / *デマルチプレクサがロックを所有しているときにタイマーが起動し、デマルチプレクサがロックを所有している場合

     *その後、セッションを破棄すると、ポインタはNULLになります。その時には

     *このタイマーコールバックはコールバックを取得します。新しいセッションは

     *作成され、タイマーが再スケジュールされるか、ポインターがまだNULL

     *そしてタイマーはスケジュールが解除されます。2番目のケースでは、保釈します(その後待機します

     *タイマーが再スケジュールまたは破棄される場合)。最初のケースでは、これ

     *早期更新を送信する可能性があります-これは無害ですが最適ではありません(FIXME)。* /

    if(p_sys-> rtsp == NULL || p_sys-> ms == NULL)

        戻る;

 

    bool use_get_param = p_sys-> b_get_param;

 

    / *サポートされている場合は、GET_PARAMETERSを使用します。wmserver方言はサポートします

     *それですが、これを適切に報告しません。* /

    if(var_GetBool(p_demux、 "rtsp-wmserver"))

        use_get_param = true;

 

    if(use_get_param)

        p_sys-> rtsp-> sendGetParameterCommand(* p_sys-> ms、

                                              default_live555_callback、bye);

    そうしないと

        p_sys-> rtsp-> sendOptionsCommand(default_live555_callback、NULL);

 

    if(!wait_Live555_response(p_demux))

    {{

        msg_Err(p_demux、 "キープアライブに失敗しました:%s"、

                 p_sys-> env-> getResultMsg());

        / *続行します。最悪の場合、後でタイムアウトになります* /

    }

}

上記の関数はvlcのrtspタイムアウト処理コードですが、問題のあるvlcバージョンはそうではありません

1

2

if( var_GetBool( p_demux, "rtsp-tcp" ) )

        return;

这两行代码,我们先把这两行代码注释,分析下为什么会出现播放画面突然不动的现象。
1)rtsp交互开始vlc客户端会发送OPTIONS请求,我们服务器需要回应支持的方法。如果我们服务器回应包括GET_PARAMETER方法(可选),use_get_param就为true,然后keep-alive机制就会定时sendGetParameterCommand,否则sendOptionsCommand,我这边服务没去做GET_PARAMETER方法的支持,所以会定时收到vlc发的OPTIONS命令请求。vlc发送完OPTIONS请求命令后,开始wait_Live555_response(p_demux)。看下这个函数:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

/* return true if the RTSP command succeeded */

static bool wait_Live555_response( demux_t *p_demux, int i_timeout = 0 /* ms */ )

{

    TaskToken task;

    demux_sys_t * p_sys = (demux_sys_t *)p_demux->p_sys;

    p_sys->event_rtsp = 0;

    if( i_timeout > 0 )

    {

        /* Create a task that will be called if we wait more than timeout ms */

        task = p_sys->scheduler->scheduleDelayedTask( i_timeout*1000,

                                                      TaskInterruptRTSP,

                                                      p_demux );

    }

    p_sys->event_rtsp = 0;

    p_sys->b_error = true;

    p_sys->i_live555_ret = 0;

    p_sys->scheduler->doEventLoop( &p_sys->event_rtsp );

    //here, if b_error is true and i_live555_ret = 0 we didn't receive a response

    if( i_timeout > 0 )

    {

        /* remove the task */

        p_sys->scheduler->unscheduleDelayedTask( task );

    }

    return !p_sys->b_error;

}

传入的参数中i_timeout为默认值0,所以没有超时时间,会一直等服务器响应请求。
2)我这边服务器有个命令解析类,只处理标准的命令(OPTIONS,DESCRIBE,PLAY等)。由于vlc会定时发送’$’开头数据,跟OPTIONS请求数据混在一起送到我的命令解析里,导致我这边没能正确解析,所以也没有回应vlc keep-alive机制的OPTIONS请求。我们再看下TimeoutPrevention函数,该函数进入后会:

1

vlc::threads::mutex_locker locker( p_sys->timeout_mutex );

由于我的服务器没有回应OPTIONS请求,所以这个锁会一直阻塞,我们看下这个锁用在哪个地方:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

/*****************************************************************************

* Demux:

*****************************************************************************/

static int Demux( demux_t *p_demux )

{

    demux_sys_t *p_sys = (demux_sys_t *)p_demux->p_sys;

    TaskToken      task;

 

    bool            b_send_pcr = true;

    int             i;

 

    /* Protect Live555 from simultaneous calls in TimeoutPrevention()

       during pause */

    vlc::threads::mutex_locker locker( p_sys->timeout_mutex );

 

    for( i = 0; i < p_sys->i_track; i++ )

可知由于TimeoutPrevention一直阻塞,所以Demux过程不能执行了,所以播放画面不动了。

新版vlc已经通过

1

2

if( var_GetBool( p_demux, "rtsp-tcp" ) )

        return;

取消了rtp over tcp的keep-alive机制,所以3.0.5以及之后版本没有出现问题。我的rtsp服务器后面也针对’$’开头数据做了处理,测了下,一切都正常了。

‘$’开头数据是做什么的呢?在我服务器发RTCP数据时才用到,没想到客户端也有类似机制。在rfc2326中,’$'(0x24)开头数据叫做:Embedded (Interleaved) Binary Data,称为嵌入式二进制数据。测试的那么多播放器,只有vlc实现了这个。而且这个Embedded (Interleaved) Binary Data只工作在rtp over tcp下。这个数据有什么作用呢?rfx2326 10.12这么介绍的:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

10.12 Embedded (Interleaved) Binary Data

 

   Certain firewall designs and other circumstances may force a server

   to interleave RTSP methods and stream data. This interleaving should

   generally be avoided unless necessary since it complicates client and

   server operation and imposes additional overhead. Interleaved binary

   data SHOULD only be used if RTSP is carried over TCP.

 

   Stream data such as RTP packets is encapsulated by an ASCII dollar

   sign (24 hexadecimal), followed by a one-byte channel identifier,

   followed by the length of the encapsulated binary data as a binary,

   two-byte integer in network byte order. The stream data follows

   immediately afterwards, without a CRLF, but including the upper-layer

   protocol headers. Each $ block contains exactly one upper-layer

   protocol data unit, e.g., one RTP packet.

 

   The channel identifier is defined in the Transport header with the

   interleaved parameter(Section 12.39).

 

   When the transport choice is RTP, RTCP messages are also interleaved

   by the server over the TCP connection. As a default, RTCP packets are

   sent on the first available channel higher than the RTP channel. The

   client MAY explicitly request RTCP packets on another channel. This

   is done by specifying two channels in the interleaved parameter of

   the Transport header(Section 12.39).

 

     RTCP is needed for synchronization when two or more streams are

     interleaved in such a fashion. Also, this provides a convenient way

     to tunnel RTP/RTCP packets through the TCP control connection when

     required by the network configuration and transfer them onto UDP

     when possible.

 

     C->S: SETUP rtsp://foo.com/bar.file RTSP/1.0

           CSeq: 2

           Transport: RTP/AVP/TCP;interleaved=0-1

 

     S->C: RTSP/1.0 200 OK

           CSeq: 2

           Date: 05 Jun 1997 18:57:18 GMT

           Transport: RTP/AVP/TCP;interleaved=0-1

 

 

 

Schulzrinne, et. al.        Standards Track                    [Page 40]

RFC 2326              Real Time Streaming Protocol            April 1998

 

 

           Session: 12345678

 

     C->S: PLAY rtsp://foo.com/bar.file RTSP/1.0

           CSeq: 3

           Session: 12345678

 

     S->C: RTSP/1.0 200 OK

           CSeq: 3

           Session: 12345678

           Date: 05 Jun 1997 18:59:15 GMT

           RTP-Info: url=rtsp://foo.com/bar.file;

             seq=232433;rtptime=972948234

 

     S->C: $\000{2 byte length}{"length" bytes data, w/RTP header}

     S->C: $\000{2 byte length}{"length" bytes data, w/RTP header}

     S->C: $\001{2 byte length}{"length" bytes  RTCP packet}

rtp over tcp模式下,就一个socket端口进行命令控制以及流传输,不像rtp over udp,另开udp socket传输数据。由于防火墙以及其他外部因素,可能造成rtsp方法与rtp流数据交织混在一起。为了避免这个,才有这个设计。通过:

1

'$'+信道编号(0或1)+数据

对控制信息以及流数据进行区分。具体介绍可以参考:
RTP over RTSP包混合发送的解决办法:https://blog.csdn.net/myslq/article/details/79819179

由于Embedded (Interleaved) Binary Data是在是在服务器回应PLAY推流后vlc才这样处理的,我这边没注意到,所以导致解析出现错误。不过除了vlc,其他播放器都没支持Embedded (Interleaved) Binary Data,因为推流是服务器端,前面命令交互完,服务器就开始推流了,对于客户端我觉得用处不大。

おすすめ

転載: blog.csdn.net/gouguofei/article/details/102914288