Forum Discussion

albert_forster_'s avatar
albert_forster_
Icon for Nimbostratus rankNimbostratus
Feb 02, 2005

persistence with rtsp

we are trying to enable persistence based on a universal persistence profile using an irule for rtsp connections.

 

 

we have multiple helix servers installed and when accessing video streams via the real player we want connections to persist to one of the helix servers as long as the chanel-set-id specified in the rtsp uri stays the same.

 

 

in the real player we are using for instance this uri:

 

 

rtsp://158.226.219.222:554/africag2/giraffe-2sm.jpg?CHSETID=45&xyz=martin

 

 

... and the irule we defined looks like this:

 

 

when HTTP_REQUEST {

 

set rtsp_chsid [findstr [HTTP::query] "CHSETID=" 0 "&"]

 

set rtsp_uri [HTTP::uri]

 

 

log "Found ChanelSetID $rtsp_chsid in request!!!"

 

log "Found RTSP uri from query = $rtsp_uri"

 

 

findstr [HTTP::query] "CHSETID=" 0 "&"

 

}

 

 

the log entries show, that the irule sometimes finds the chanel-set-id in the rtsp-request, but that there are also rtsp-requests belonging to this session, which do not include the chanel-set-id:

 

 

die durch diese irule erzeugten log-einträge:

 

ltm:Feb 1 17:27:46 tmm tmm[357]: 01220002:6: Rule _rtsp_persist_on_chanel_set_id : Found ChanelSetID in request!!!

 

ltm:Feb 1 17:27:47 tmm tmm[357]: 01220002:6: Rule _rtsp_persist_on_chanel_set_id : Found RTSP uri from query = /SmpDsBhgRl

 

 

ltm:Feb 1 17:27:47 tmm tmm[357]: 01220002:6: Rule _rtsp_persist_on_chanel_set_id : Found ChanelSetID CHSETID=45 in request!!!

 

ltm:Feb 1 17:27:47 tmm tmm[357]: 01220002:6: Rule _rtsp_persist_on_chanel_set_id : Found RTSP uri from query = /SmpDsBhgRlce6cdda9-8a50-4c26-a2f3-83b059a0fcde

 

 

ltm:Feb 1 17:28:06 tmm tmm[357]: 01220002:6: Rule _rtsp_persist_on_chanel_set_id : Found ChanelSetID in request!!!

 

ltm:Feb 1 17:28:06 tmm tmm[357]: 01220002:6: Rule _rtsp_persist_on_chanel_set_id : Found RTSP uri from query = rtsp://158.226.219.222:554

 

 

ltm:Feb 1 17:31:04 tmm tmm[357]: 01220002:6: Rule _rtsp_persist_on_chanel_set_id : Found ChanelSetID in request!!!

 

ltm:Feb 1 17:31:04 tmm tmm[357]: 01220002:6: Rule _rtsp_persist_on_chanel_set_id : Found RTSP uri from query = rtsp://158.226.219.222:554

 

 

ltm:Feb 1 17:31:24 tmm tmm[357]: 01220002:6: Rule _rtsp_persist_on_chanel_set_id : Found ChanelSetID in request!!!

 

ltm:Feb 1 17:31:24 tmm tmm[357]: 01220002:6: Rule _rtsp_persist_on_chanel_set_id : Found RTSP uri from query = /SmpDsBhgRl

 

 

ltm:Feb 1 17:31:24 tmm tmm[357]: 01220002:6: Rule _rtsp_persist_on_chanel_set_id : Found ChanelSetID CHSETID=45 in request!!!

 

ltm:Feb 1 17:31:24 tmm tmm[357]: 01220002:6: Rule _rtsp_persist_on_chanel_set_id : Found RTSP uri from query = /SmpDsBhgRl1ff70c47-f05c-4c83-b73f-fd914257b080

 

 

... therefore requests from this single rtsp session are distributed to multiple helix servers (as seen in the pool statistics), and the real player fails to play the video stream.

 

 

so finally our question is: is this kind of rtsp-session-persistence possible with irules and how can it be configured?

 

 

additional info: we traced the rtsp stream on the helix server with and without the loadbalancer and the rtsp packages seem to be modified by the loadbalancer (no rtsp methods are visible and the video stream file which should be played is not contained in the rtsp uri, but in an rtsp continuation package).

 

is there any possibility to disable this modification of the rtsp packages by the loadbalancer?

 

 

kind regards,

 

albert
  • Mark_Crosland_1's avatar
    Mark_Crosland_1
    Historic F5 Account
    Real-Networks can tunnel RTSP through HTTP. They use the URI /SmpDsBhgRl to

    do so. There are two scenarios.

    1) There is one POST and one GET.

    2) There are multiple POSTs and one GET.

    The player will do one or the other based on whether a timely response is

    received to the first POST. The following rule will work in either case.

    If you want to load balance based on the URI in the RTSP URI you need to

    force the second scenario. In the first scenario they send a bogus

    content length, 32767, and trying to HTTP::collect it all will cause

    enough of a delay (really imperceptible to the user) to force the second

    scenario where each POST contains a content length that matches what

    they send. The 32767 content-length isn't really bogus, it is just that

    they never actually send that much data.

    The HTTP POST commands are used by the client to send base64 encoded RTSP

    commands. That is why you don't see the RTSP methods, they have been

    encoded.

    The single HTTP GET command is used as a virtual channel to stream the

    audio/video back to the client.

    The first 36 bytes of content in each POST contain a session identifier. If

    you see /SmpDsBhgRl in a POST URI, collect the 36 bytes of data. When the

    36 bytes of data arrives the HTTP_REQUEST_DATA event is fired. Use

    HTTP::payload to get the session ID and then persist it.

    The rule below will also decode the tunnelled RTSP requests and find the

    URI in the DESCRIBE request (typically the first request that contains

    a URI). You can then use the uri variable to make further load balancing

    decisions.

        when HTTP_REQUEST { 
                      
         set decode_rtsp 0 
               
             if { [HTTP::method] eq "POST" && [HTTP::uri] eq "/SmpDsBhgRl" } { 
      
                 if { [HTTP::header exists "Content-Length"] } { 
      
                     set clen [HTTP::header value "Content-Length"] 
              
                     if { $clen == 32767 } { 
      
                          force multi-post mode, 32767 bytes are never sent 
                         HTTP::collect $clen 
              
                          or just collect the session ID, however, the player 
                          may choose to go into multi-post mode anyway 
                         HTTP::collect 36 
                  
                     } else { 
                      
                         set decode_rtsp 1 
                         HTTP::collect $clen 
                      
                     } 
                 } 
      
             } elseif { [HTTP::method] eq "GET" } { 
               
                 set uri [HTTP::uri] 
              
                 if { [string compare -length 11 "/SmpDsBhgRl" $uri] == 0 } { 
                     set session_id [string range $uri 11 end] 
                     persist universal $session_id 
                 } 
             } 
         } 
      
         when HTTP_REQUEST_DATA { 
      
             set content [HTTP::payload] 
             set session_id [string range $content 0 35] 
             set decode_idx [expr $clen - 2] 
      
             persist universal $session_id 
      
             if { $decode_rtsp } { 
      
                 set rtsp_request [b64decode [string range $content 38 $decode_idx]] 
      
                 if { [string compare -length 8 $rtsp_request "DESCRIBE"] == 0 } { 
      
                     set end_of_uri [string first " RTSP/1.0" $rtsp_request] 
                     set uri [string range $rtsp_request 9 $end_of_uri] 
      
                      now you can dig into the "real" URI to load balance to 
                      specific nodes based on the presentation or to persist 
                 } 
      
                 set decode_rtsp 0 
             } 
      
             HTTP::release 
         } 
     
  • hello,

     

     

    thank you for your reply. but we are trying to force all components to use pure rtsp - we don't want to handle rtsp-tunneling via the loadbalancer.

     

     

    so we configured the real player not to use http-tunneling and traced on the client node and on the helix-server node with ethereal.

     

     

    when we start a streaming download of a *.wmv-file, then we can access the stream and we see rtsp tunneled via http sent to the rtsp-port.

     

     

    but when we start a streaming download of a *.rm-file, then the real player tries to use pure rtsp. in this case the rtsp-options request is received on the helix-server-node, but we can NOT see the following rtsp-describe request sent by the client on the helix-server node, although the client receives an answer to this request (and the stream can not be downloaded).

     

    so we think that the answer must be produced by the loadbalancer itself (see the attached ethereal-traces).

     

     

    is this observed behaviour part of the loadbalancer configuration (if that's the case: how can we disable this?)

     

     

    can the real player be forced to always use pure rtsp (not tunneled in any way)? and is there a difference in the protocol choosen by the real player dependent on the streamed media type (rm, wmv, ...)?

     

     

    thanks,

     

    albert

     

     

    =======================================================================

     

    this is an ethereal trace taken on the client-side, where

     

    we tried to download a media stream (*.rm) via pure rtsp, but failed

     

    because the rtsp-describe never reached the helix-server (and we

     

    think the loadbalancer answered instead of the helix-server).

     

    =======================================================================

     

     

    OPTIONS rtsp://158.226.219.222:554 RTSP/1.0

     

    CSeq: 1

     

    User-Agent: RealMedia Player (HelixDNAClient)/10.0.0.0 (win32)

     

    ClientChallenge: 1f86e2c643592f625a33fda9efa4fec3

     

    ClientID: WinNT_5.1_6.0.12.1056_RealPlayer_R30DED_de-at_686

     

    CompanyID: /0xfnpVYGBwuXMQwGzpM+g==

     

    GUID: 00000000-0000-0000-0000-000000000000

     

    PlayerStarttime: [04/02/2005:14:38:11 01:00]

     

    Pragma: initiate-session

     

    RegionData: 1140

     

     

    RTSP/1.0 200 OK

     

    CSeq: 1

     

    Date: Fri, 04 Feb 2005 13:45:51 GMT

     

    Session: 23478-1

     

    Server: Helix Server Version 9.0.5.1159 (sunos-5.8-sparc-server) (RealServer compatible)

     

    Public: OPTIONS, DESCRIBE, ANNOUNCE, PLAY, SETUP, GET_PARAMETER, SET_PARAMETER, TEARDOWN

     

    RealChallenge1: 743343e19df49991f747138ef6b10f4e

     

    StatsMask: 3

     

     

    DESCRIBE rtsp://158.226.219.222:554/real9video.rm?CHSETID=41&xyz=martin RTSP/1.0

     

    CSeq: 2

     

    User-Agent: RealMedia Player (HelixDNAClient)/10.0.0.0 (win32)

     

    Accept: application/sdp

     

    Session: 23478-1

     

    Bandwidth: 1544000

     

    ClientID: WinNT_5.1_6.0.12.1056_RealPlayer_R30DED_de-at_686

     

    Cookie: cbid=rfqgjhcidjfkhldmeoloqphqerjrktlufkrgkieliggkilplmsnrqugqlorndtdudfjjdhci

     

    GUID: 00000000-0000-0000-0000-000000000000

     

    Language: de-at

     

    RegionData: 1140

     

    Require: com.real.retain-entity-for-setup

     

    SupportsMaximumASMBandwidth: 1

     

     

    =======================================================================

     

    this is an ethereal trace taken on the client-side, where

     

    we tried to download a media stream (*.wmv) via pure rtsp, but we

     

    saw a rtsp request tunneled via http and sent to the rtsp port:

     

    (and we could access the data stream)

     

    =======================================================================

     

     

    GET /wmvideo.wmv?CHSETID=36&xyz=mp HTTP/1.1

     

    Accept: */*

     

    User-Agent: NSPlayer/9.0.0.2980

     

    Host: 158.226.219.222

     

    Cookie: cbid=rfqgjhcidjfkhldmeoloqphqerjrktlufkrgkieliggkilplmsnrqugqlorndtdudfjjdhci

     

    X-Accept-Authentication: Negotiate, NTLM, Digest, Basic

     

    Pragma: version11-enabled=1

     

    Pragma: no-cache,rate=1.000,stream-time=0,stream-offset=0:0,packet-num=4294967295,max-duration=0

     

    Pragma: packet-pair-experiment=1

     

    Pragma: pipeline-experiment=1

     

    Supported: com.microsoft.wm.srvppair, com.microsoft.wm.sswitch, com.microsoft.wm.predstrm

     

    Pragma: xClientGUID={3300AD50-2C39-46c0-AE0A-CC44945DC0BE}

     

    Accept-Language: de-DE, *;q=0.1

     

     

    HTTP/1.0 200 OK

     

    Content-Type: application/octet-stream

     

    Server: Cougar 4.1.0.3917

     

    Cache-Control: no-cache

     

    Pragma: no-cache

     

    Pragma: client-id="5483"

     

    Pragma: features="seekable,stridable"

     

     

    $H.....<----- snip ---->
  • Mark_Crosland_1's avatar
    Mark_Crosland_1
    Historic F5 Account
    Ideally you would like the transport to be UDP based. The client advertises one or more ports to the server and the server selects its own port set. The RTSP control channel remains for the duration of the presentation, but the audio/video is streamed over UDP using the ports selected by the client and server. Port holes need to be opened in the BigIP for this to work. Depending on where you clients are you may encounter firewall issues with this configuration. The BigIP does not currently support this. Native support for RTSP was completed recently but has not been released yet.

     

     

    The next best option is to have the audio/video sent over the RTSP control connection. This is similar to HTTP tunneling but with less overhead. The audio/video is sent in binary form, no base64 encode/decode step involved and there are no HTTP headers to deal with. You can force this in real player by selecting the TCP option (and un-selecting the other transports) in the manual RTSP settings under tools>preferences>network transport.

     

    The TCP option works with a simple virtual such as the following.

     

     

    virtual helix {

     

    destination 10.1.30.2:rtsp

     

    ip protocol tcp

     

    profile fastL4

     

    pool helix

     

    }

     

     

    Hope this helps.