RTSP - Stream empfangen und abspeichern

  • Guten Abend,

    ich versuche momentan in AutoIt eine kleine RTSP (Real Time Streaming Protocol) UDF zu erstellen, da sowas anscheinend nicht existiert. Momentan bin ich soweit, dass ich mit einem RTSP-Server etwas kommunizieren kann, aber ich verstehe nicht wirklich, wie ich an den Stream komme.
    Scripte + Dateien die ich vom Server bekomme sind im Anhang vorzufinden.

    Mein 1. Script. Ist zwar sehr einfach gehalten, müsste aber so funktionieren.

    Spoiler anzeigen
    [autoit]

    #include <String.au3>

    [/autoit] [autoit][/autoit] [autoit]

    TCPStartup()

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $sock = TCPConnect(TCPNameToIP('a1979.l12278653874.c122786.e.lm.akamaistream.net'), 554)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;#####################################################///DESCRIBE

    [/autoit] [autoit][/autoit] [autoit]

    $packet = 'DESCRIBE rtsp://a1979.l12278653874.c122786.e.lm.akamaistream.net/D/1979/122786/v0001/reflector:53874 RTSP/1.0' & @CRLF & _
    'CSeq: 1' & @CRLF & @CRLF

    [/autoit] [autoit][/autoit] [autoit]

    TCPSend($sock, $packet)
    _lausche(1)

    [/autoit] [autoit][/autoit] [autoit]

    ;#####################################################///SETUP

    [/autoit] [autoit][/autoit] [autoit]

    $packet = 'SETUP rtsp://a1979.l12278653874.c122786.e.lm.akamaistream.net/Akamai_Live_53874/rtx RTSP/1.0' & @CRLF & _
    'CSeq: 2' & @CRLF & _
    'Transport: RTP/AVP/TCP;unicast;interleaved=0-1;ssrc=a33b5252;mode=PLAY' & @CRLF & @CRLF

    [/autoit] [autoit][/autoit] [autoit]

    TCPSend($sock, $packet)
    _lausche(2)

    [/autoit] [autoit][/autoit] [autoit]

    ;#####################################################///PLAY

    [/autoit] [autoit][/autoit] [autoit]

    $packet = 'PLAY rtsp://a1979.l12278653874.c122786.e.lm.akamaistream.net/Akamai_Live_53874 RTSP/1.0' & @CRLF & _
    'CSeq: 3' & @CRLF & _
    'Range: npt=5-20' & @CRLF & @CRLF

    [/autoit] [autoit][/autoit] [autoit]

    TCPSend($sock, $packet)
    _lausche(3)

    [/autoit] [autoit][/autoit] [autoit]

    Local $session = _readSession(@ScriptDir & "\tmp3.txt") ;Session aus TXT-Datei auslesen

    [/autoit] [autoit][/autoit] [autoit]

    ;#####################################################///TEARDOWN => Session Not Found

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $packet = 'TEARDOWN rtsp://a1979.l12278653874.c122786.e.lm.akamaistream.net/Akamai_Live_53874 RTSP/1.0' & @CRLF & _
    'CSeq: 4' & @CRLF & _
    'Session: ' & $session & @CRLF & @CRLF

    [/autoit] [autoit][/autoit] [autoit]

    TCPSend($sock, $packet)
    _lausche(4)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ConsoleWrite($session & @CRLF)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;Liest nur die Session aus.
    Func _readSession($sPath)
    Local $sFile = FileRead($sPath)
    Local $sStr = _StringBetween($sFile, 'Session:', ';')
    Return StringStripWS($sStr[0], 8)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ;Lauscht dem Server
    Func _lausche($nnn)
    Do
    $rec = TCPRecv($sock, 1024)
    Until $rec <> ""
    Local $x = ''
    While 1
    $x = TCPRecv($sock, 1024)

    [/autoit] [autoit][/autoit] [autoit]

    $rec &= $x
    If $x = "" Then ExitLoop
    WEnd
    FileWrite(FileOpen("tmp" & $nnn & ".txt", 2), $rec)
    EndFunc ;==>_lausche

    [/autoit]

    Auf diesen Seiten wird das einigermaßen erklärt, wie das funktioniert.
    http://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol
    https://autoit.de/www.csee.umbc.…urose-ross.html

    Hier mal die Daten, die der Server mir liefert:

    1. Schritt
    2. Schritt
    3. Schritt
    Code
    RTSP/1.0 200 OK
    Transport: RTP/AVP/TCP;unicast;interleaved=0-1;ssrc=a1dab41b;mode=PLAY
    Date: Sat, 14 Apr 2012 22:42:55 GMT
    CSeq: 2
    Session: 13484517198031398105;timeout=60
    Server: WMServer/9.5.6001.18223
    Supported: com.microsoft.wm.srvppair, com.microsoft.wm.sswitch, com.microsoft.wm.eosmsg, com.microsoft.wm.fastcache, com.microsoft.wm.packetpairssrc, com.microsoft.wm.startupprofile
    Last-Modified: Sat, 30 Dec 1899 00:00:00 GMT
    Cache-Control: x-wms-event-subscription="remote-log", x-wms-stream-type="broadcast", no-cache, no-user-cache, private
    4. Schritt
    Code
    RTSP/1.0 454 Session Not Found
    Date: Sat, 14 Apr 2012 22:42:55 GMT
    CSeq: 3
    Session: 13484517198031398105;timeout=60
    Server: WMServer/9.5.6001.18223
    Supported: com.microsoft.wm.srvppair, com.microsoft.wm.sswitch, com.microsoft.wm.eosmsg, com.microsoft.wm.fastcache, com.microsoft.wm.packetpairssrc, com.microsoft.wm.startupprofile

    Was ich nicht verstehe ist, warum der bei Schritt 4 mir "RTSP/1.0 454 Session Not Found" liefert.. ?(
    Theoretisch gesehen, müsste das so funktionieren:

    Spoiler anzeigen

    Wenn man an den Server Play sendet müsste er doch normalerweise den Stream Packetweise senden. Warum macht der das nicht, wenn ich schon einen PLAY-Request sende?

    Ist zwar sehr kompliziert, aber vielleicht hat jemand schon damit Erfahrungen gemacht und kann mir helfen. Für Tipps oder Hinweise bin ich ebenfalls dankbar.
    Hoffentlich wird das mit der UDF nicht allzu kompliziert *gg*

  • Deine Receive-Funktion bricht zu schnell ab. Eine Antwort ist erst vorbei, wenn du eine Leerzeile empfängst (@CRLF & @CRLF) Dann erhälst du bei SETUP eine Session, die du dann bei PLAY angeben musst.
    Für PLAY kannst du übrigens eine neue Verbindung aufbauen, da du dann die Session bereits hast.
    http://folk.uio.no/meccano/reflector/smallclient.html

  • Okay, das hat mir weitergeholfen. Tatsächlich ist es so, dass ich die Daten vom Server deswegen nur Stückweise hatte. Jetzt sollte das mit TCPRecv gehen. Jetzt habe ich es geschafft, DESCRIBE, SETUP1 und SETUP2 richtig aufzurufen. Problem ist jetzt nur noch PLAY.

    Habe das Script komplett überarbeitet und es sieht schon fast wie eine richtige UDF aus. Allerdings benutze ich dafür AutoItObject.

    Also damit man nicht sofort alles runterladen muss, poste ich erstmal, was ich vom Server bekomme:

    DESCRIBE
    SETUP1
    Code
    RTSP/1.0 200 OK
    Transport: RTP/AVP/TCP;unicast;server_port=5004-5005;client_port=9000-9000;ssrc=b85c9c9c;mode=PLAY
    Date: Sun, 15 Apr 2012 04:49:13 GMT
    CSeq: 2
    Session: 11911824958435848744;timeout=60
    Server: WMServer/9.5.6001.18223
    Supported: com.microsoft.wm.srvppair, com.microsoft.wm.sswitch, com.microsoft.wm.eosmsg, com.microsoft.wm.fastcache, com.microsoft.wm.packetpairssrc, com.microsoft.wm.startupprofile
    Last-Modified: Sat, 30 Dec 1899 00:00:00 GMT
    Cache-Control: x-wms-event-subscription="remote-log", x-wms-stream-type="broadcast", no-cache, no-user-cache, private
    SETUP2
    Code
    RTSP/1.0 200 OK
    Transport: RTP/AVP/TCP;unicast;server_port=5004-5005;client_port=9000-9000;ssrc=b85c9c9c;mode=PLAY
    Date: Sun, 15 Apr 2012 04:49:13 GMT
    CSeq: 3
    Session: 11911824958435848744;timeout=60
    Server: WMServer/9.5.6001.18223
    Supported: com.microsoft.wm.srvppair, com.microsoft.wm.sswitch, com.microsoft.wm.eosmsg, com.microsoft.wm.fastcache, com.microsoft.wm.packetpairssrc, com.microsoft.wm.startupprofile
    Last-Modified: Sat, 30 Dec 1899 00:00:00 GMT
    Cache-Control: x-wms-event-subscription="remote-log", x-wms-stream-type="broadcast", no-cache, no-user-cache, private
    PLAY
    Code
    RTSP/1.0 500 Internal Server Error
    Date: Sun, 15 Apr 2012 04:49:14 GMT
    CSeq: 4
    Session: 11911824958435848744;timeout=60
    Server: WMServer/9.5.6001.18223
    Supported: com.microsoft.wm.srvppair, com.microsoft.wm.sswitch, com.microsoft.wm.eosmsg, com.microsoft.wm.fastcache, com.microsoft.wm.packetpairssrc, com.microsoft.wm.startupprofile

    Habe 2x SETUP benutzt, da das hier (http://folk.uio.no/meccano/reflector/smallclient.html) auch gemacht wurde.

    Und hier die Scripte:

    main.au3
    [autoit]

    #include <class/rtsp.au3>
    #include <string.au3>

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Local $rtsp = _New_RTSP()

    [/autoit] [autoit][/autoit] [autoit]

    $rtsp.setServer('rtsp://a1979.l12278653874.c122786.e.lm.akamaistream.net/D/1979/122786/v0001/reflector:53874')
    $rtsp.connect()

    [/autoit] [autoit][/autoit] [autoit]

    $rtsp.requestDESCRIBE()
    $sRecvData = $rtsp.recvData()
    $rtsp.setData($sRecvData, 'DESCRIBE')

    [/autoit] [autoit][/autoit] [autoit]

    _Debug('DESCRIBE', $sRecvData) ;Schreibt auf, was vom Server wiedergegeben wurde!

    [/autoit] [autoit][/autoit] [autoit]

    $rtsp.requestSETUP1()
    $sRecvData = $rtsp.recvData()
    $rtsp.setData($sRecvData, 'SETUP1')

    [/autoit] [autoit][/autoit] [autoit]

    _Debug('SETUP1', $sRecvData) ;Schreibt auf, was vom Server wiedergegeben wurde!

    [/autoit] [autoit][/autoit] [autoit]

    $rtsp.requestSETUP2()
    $sRecvData = $rtsp.recvData()
    $rtsp.setData($sRecvData, 'SETUP2')

    [/autoit] [autoit][/autoit] [autoit]

    _Debug('SETUP2', $sRecvData) ;Schreibt auf, was vom Server wiedergegeben wurde!

    [/autoit] [autoit][/autoit] [autoit]

    $rtsp.requestPLAY()
    $sRecvData = $rtsp.recvData()
    $rtsp.setData($sRecvData, 'PLAY')

    [/autoit] [autoit][/autoit] [autoit]

    _Debug('PLAY', $sRecvData) ;Schreibt auf, was vom Server wiedergegeben wurde!

    [/autoit] [autoit][/autoit] [autoit]

    ;Ok, INTERNAL SERVER ERROR. Dann neue Verbindung??
    MsgBox(0, 'SERVER ERROR???', $sRecvData)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    #region Versuch2
    $con = TCPConnect($rtsp.serverIp, 9000)
    Local $sPacket = 'PLAY ' & $rtsp.getSETUPurl() & ' RTSP/1.0' & @CRLF & _
    'CSeq: 4' & @CRLF & _
    'Session: ' & $rtsp.getSession() & @CRLF & @CRLF
    TCPSend($con, $sPacket)
    $sRecvData = $rtsp.recvData()

    [/autoit] [autoit][/autoit] [autoit]

    MsgBox(0, 'GAR NICHTS!?', $sRecvData)
    #endregion Versuch 2

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _Debug($sName, $sData)
    Local $sPath = @ScriptDir & '\' & $sName & '.txt'
    FileDelete($sPath)
    FileWrite($sPath, $sData)
    EndFunc

    [/autoit]
    rtsp.au3
    [autoit]

    #include <AutoItObject\AutoItObject.au3>

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _AutoItObject_Startup()

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _New_RTSP()
    Local $oRTSP = _AutoItObject_Create()
    _AutoItObject_AddProperty($oRTSP, 'server')
    _AutoItObject_AddProperty($oRTSP, 'serverPort')
    _AutoItObject_AddProperty($oRTSP, 'serverIp')
    _AutoItObject_AddProperty($oRTSP, 'socket')
    _AutoItObject_AddProperty($oRTSP, 'dataDESCRIBE')
    _AutoItObject_AddProperty($oRTSP, 'dataSETUP1')
    _AutoItObject_AddProperty($oRTSP, 'dataSETUP2')
    _AutoItObject_AddProperty($oRTSP, 'dataPLAY')

    [/autoit] [autoit][/autoit] [autoit]

    _AutoItObject_AddMethod($oRTSP, 'setServer', '_rtspSetServer')
    _AutoItObject_AddMethod($oRTSP, 'getServer', '_rtspGetServer')
    _AutoItObject_AddMethod($oRTSP, 'connect', '_rtspConnect')
    _AutoItObject_AddMethod($oRTSP, 'requestDESCRIBE', '_rtspRequestDESCRIBE')
    _AutoItObject_AddMethod($oRTSP, 'requestSETUP1', '_rtspRequestSETUP1')
    _AutoItObject_AddMethod($oRTSP, 'requestSETUP2', '_rtspRequestSETUP2')
    _AutoItObject_AddMethod($oRTSP, 'requestPLAY', '_rtspRequestPLAY')
    _AutoItObject_AddMethod($oRTSP, 'getHostName', '_rtspGetHostName')
    _AutoItObject_AddMethod($oRTSP, 'recvData', '_rtspRecvData')
    _AutoItObject_AddMethod($oRTSP, 'getSETUPurl', '_rtspGetSETUPurl')
    _AutoItObject_AddMethod($oRTSP, 'getSession', '_rtspGETSession')
    _AutoItObject_AddMethod($oRTSP, 'setData', '_rtspSetData')

    [/autoit] [autoit][/autoit] [autoit]

    Return $oRTSP
    EndFunc ;==>_New_RTSP

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _rtspGETSession($oSelf)
    Local $sData = _StringBetween($oSelf.dataSETUP1, 'Session:', ';')
    If IsArray($sData) Then Return StringStripWS($sData[0], 8)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _rtspSetData($oSelf, $sData, $sType)
    Switch $sType
    Case 'DESCRIBE'
    $oSelf.dataDESCRIBE = $sData
    Case 'SETUP1'
    $oSelf.dataSETUP1 = $sData
    Case 'SETUP2'
    $oSelf.dataSETUP2 = $sData
    Case 'PLAY'
    $oSelf.dataPLAY = $sData
    EndSwitch
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _rtspSetServer($oSelf, $sServer)
    $oSelf.server = $sServer
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _rtspGetServer($oSelf)
    Return $oSelf.server
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _rtspConnect($oSelf)
    TCPStartup()
    $oSelf.serverIp = TCPNameToIP($oSelf.getHostName())
    $oSelf.socket = TCPConnect($oSelf.serverIp, 554)
    Return $oSelf.socket
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _rtspRecvData($oSelf)
    Local $sRecv, $sData, $sN
    While 1
    $sRecv = TCPRecv($oSelf.socket, 2048)
    $sData &= $sRecv
    If $sRecv = '' Then $sN += 1
    If $sN > 10 Then ExitLoop
    Sleep(1)
    WEnd
    Return $sData
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _rtspRequestDESCRIBE($oSelf)
    Local $sPacket = 'DESCRIBE ' & $oSelf.server & ' RTSP/1.0' & @CRLF & _
    'CSeq: 1' & @CRLF & @CRLF
    TCPSend($oSelf.socket, $sPacket)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _rtspRequestSETUP1($oSelf)
    Local $sPacket = 'SETUP ' & $oSelf.getSETUPurl() & 'rtx RTSP/1.0' & @CRLF & _
    'CSeq: 2' & @CRLF & _
    'Transport: RTP/AVP/TCP;unicast;client_port=9000-9001' & @CRLF & @CRLF
    TCPSend($oSelf.socket, $sPacket)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _rtspRequestSETUP2($oSelf)
    Local $sPacket = 'SETUP ' & $oSelf.getSETUPurl() & 'rtx RTSP/1.0' & @CRLF & _
    'CSeq: 3' & @CRLF & _
    'Session: ' & $oSelf.getSession() & @CRLF & _
    'Transport: RTP/AVP/TCP;unicast;client_port=9000-9001' & @CRLF & @CRLF
    TCPSend($oSelf.socket, $sPacket)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _rtspRequestPLAY($oSelf)
    Local $sPacket = 'PLAY ' & $oSelf.getSETUPurl() & ' RTSP/1.0' & @CRLF & _
    'CSeq: 4' & @CRLF & _
    'Session: ' & $oSelf.getSession() & @CRLF & @CRLF
    TCPSend($oSelf.socket, $sPacket)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _rtspGetSETUPurl($oSelf)
    Local $sData = _StringBetween($oSelf.dataDESCRIBE, 'control:', @LF)
    If UBound($sData) > 0 Then Return StringStripWS($sData[1], 8)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _rtspGetHostName($oSelf)
    Local $sTmp1, $sTmp2
    If StringLeft($oSelf.server, 7) == 'rtsp://' Then $sTmp1 = StringTrimLeft($oSelf.server, 7)
    If StringInStr($sTmp1, '/') Then
    $sTmp2 = StringSplit($sTmp1, '/')
    $sTmp1 = $sTmp2[1]
    EndIf
    Return $sTmp1
    EndFunc

    [/autoit]

    Eigentlich müsste das doch gehen. Hab das einmal ohne eine neue Verbindung versucht => geht nicht. Mit einer neuen Verbindung und auf Port 9000, wie mit dem Server "ausgehandelt" => immer noch nicht ..

    Die Fehlermeldung lautet beim ersten "INTERNAL SERVER ERROR" und beim zweiten kommt nichts an. :wacko: