日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android vlc 画面不动,vlc播放rtsp over tcp画面突然卡住问题

發布時間:2024/1/8 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android vlc 画面不动,vlc播放rtsp over tcp画面突然卡住问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

繼完成rtmp服務器開發后,最近也寫完了rtsp服務器,可以將國標ps流以及其他格式協議碼流轉rtsp協議輸出。中間開發過程用了許多播放器測試,最常用的就是vlc。使用vlc測試過程,遇到了許多問題。今天就記錄一個比較奇怪的問題。

使用rtp over udp模式播放時,沒出現問題,但是使用rtp over tcp模式時,vlc播放幾十秒后畫面突然卡住不動了,看了vlc 的debug message沒發現異常。用ffplay,live555,potplayer測了都沒異常。后面換了不同版本vlc測試,更奇怪了,vlc3.0.0以及之前,3.0.5以及之后版本都正常。應該是vlc對rtp over tcp做了特殊處理。此時抓包分析rtsp交互數據,發現出現問題版本的vlc每隔一定時間除了會發送OPTIONS命令,然后還有以'$'開頭的一串特殊字節,發送完這個播放畫面就卡住了。為什么會卡住不播放了呢?只能看vlc源碼查找問題了。

通過閱讀相關源碼,終于定位到了原因。這個是vlc的keep-alive機制造成的。由于vlc使用了live555做rtsp處理,所以對應處理代碼在modules/access/live555.cpp這個文件里。下面結合代碼說下原因。

C++

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" ) )

return;

/* Protect Live555 from us calling their functions simultaneously

with Demux() or Control() */

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

/* If the timer fires while the demuxer owns the lock, and the demuxer

* then torns the session down, the pointers will become NULL. By the time

* this timer callback obtains the callback, either a new session was

* created and the timer is rescheduled, or the pointers are still NULL

* and the timer is descheduled. In the second case, bail out (then wait

* for the timer to be rescheduled or destroyed). In the first case, this

* might send an early refresh - that′s harmless but suboptimal (FIXME). */

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

return;

bool use_get_param = p_sys->b_get_param;

/* Use GET_PARAMETERS if supported. wmserver dialect supports

* it, but does not report this properly. */

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 );

else

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

if( !wait_Live555_response( p_demux ) )

{

msg_Err( p_demux, "keep-alive failed: %s",

p_sys->env->getResultMsg() );

/* Just continue, worst case is we get timed out later */

}

}

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

staticvoidTimeoutPrevention(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"))

return;

/* Protect Live555 from us calling their functions simultaneously

with Demux() or Control() */

vlc::threads::mutex_lockerlocker(p_sys->timeout_mutex);

/* If the timer fires while the demuxer owns the lock, and the demuxer

* then torns the session down, the pointers will become NULL. By the time

* this timer callback obtains the callback, either a new session was

* created and the timer is rescheduled, or the pointers are still NULL

* and the timer is descheduled. In the second case, bail out (then wait

* for the timer to be rescheduled or destroyed). In the first case, this

* might send an early refresh - that′s harmless but suboptimal (FIXME). */

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

return;

booluse_get_param=p_sys->b_get_param;

/* Use GET_PARAMETERS if supported. wmserver dialect supports

* it, but does not report this properly. */

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);

else

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

if(!wait_Live555_response(p_demux))

{

msg_Err(p_demux,"keep-alive failed: %s",

p_sys->env->getResultMsg());

/* Just continue, worst case is we get timed out later */

}

}

如上函數是vlc的rtsp超時處理代碼,出現問題的vlc版本沒有

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

return;

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)。看下這個函數:

C++

/* 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;

}

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 */

staticboolwait_Live555_response(demux_t*p_demux,inti_timeout=0/* ms */)

{

TaskTokentask;

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函數,該函數進入后會:

C++

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

1

vlc::threads::mutex_lockerlocker(p_sys->timeout_mutex);

由于我的服務器沒有回應OPTIONS請求,所以這個鎖會一直阻塞,我們看下這個鎖用在哪個地方:

C++

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

* 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++ )

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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

* Demux:

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

staticintDemux(demux_t*p_demux)

{

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

TaskTokentask;

boolb_send_pcr=true;

inti;

/* Protect Live555 from simultaneous calls in TimeoutPrevention()

during pause */

vlc::threads::mutex_lockerlocker(p_sys->timeout_mutex);

for(i=0;ii_track;i++)

可知由于TimeoutPrevention一直阻塞,所以Demux過程不能執行了,所以播放畫面不動了。

新版vlc已經通過

C++

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

return;

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這么介紹的:

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}

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.12Embedded(Interleaved)BinaryData

Certainfirewalldesignsandothercircumstancesmayforceaserver

tointerleaveRTSPmethodsandstreamdata.Thisinterleavingshould

generallybeavoidedunlessnecessarysinceitcomplicatesclientand

serveroperationandimposesadditionaloverhead.Interleavedbinary

dataSHOULDonlybeusedifRTSPiscarriedoverTCP.

StreamdatasuchasRTPpacketsisencapsulatedbyanASCIIdollar

sign(24hexadecimal),followedbyaone-bytechannelidentifier,

followedbythelengthoftheencapsulatedbinarydataasabinary,

two-byteintegerinnetworkbyteorder.Thestreamdatafollows

immediatelyafterwards,withoutaCRLF,butincludingtheupper-layer

protocolheaders.Each$blockcontainsexactlyoneupper-layer

protocoldataunit,e.g.,oneRTPpacket.

ThechannelidentifierisdefinedintheTransportheaderwiththe

interleavedparameter(Section12.39).

WhenthetransportchoiceisRTP,RTCPmessagesarealsointerleaved

bytheserverovertheTCPconnection.Asadefault,RTCPpacketsare

sentonthefirstavailablechannelhigherthantheRTPchannel.The

clientMAYexplicitlyrequestRTCPpacketsonanotherchannel.This

isdonebyspecifyingtwochannelsintheinterleavedparameterof

theTransportheader(Section12.39).

RTCPisneededforsynchronizationwhentwoormorestreamsare

interleavedinsuchafashion.Also,thisprovidesaconvenientway

totunnelRTP/RTCPpacketsthroughtheTCPcontrolconnectionwhen

requiredbythenetworkconfigurationandtransferthemontoUDP

whenpossible.

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

CSeq:2

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

S->C:RTSP/1.0200OK

CSeq:2

Date:05Jun199718:57:18GMT

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

Schulzrinne,et.al.StandardsTrack[Page40]

RFC2326RealTimeStreamingProtocolApril1998

Session:12345678

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

CSeq:3

Session:12345678

S->C:RTSP/1.0200OK

CSeq:3

Session:12345678

Date:05Jun199718:59:15GMT

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

seq=232433;rtptime=972948234

S->C:$\000{2bytelength}{"length"bytesdata,w/RTPheader}

S->C:$\000{2bytelength}{"length"bytesdata,w/RTPheader}

S->C:$\001{2bytelength}{"length"bytesRTCPpacket}

rtp over tcp模式下,就一個socket端口進行命令控制以及流傳輸,不像rtp over udp,另開udp socket傳輸數據。由于防火墻以及其他外部因素,可能造成rtsp方法與rtp流數據交織混在一起。為了避免這個,才有這個設計。通過:

'$'+信道編號(0或1)+數據

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,因為推流是服務器端,前面命令交互完,服務器就開始推流了,對于客戶端我覺得用處不大。

總結

以上是生活随笔為你收集整理的android vlc 画面不动,vlc播放rtsp over tcp画面突然卡住问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。