~/bend-docscommunity

rtsp.bend relies on unsafe/foreign

raw source on the hub · import 0xd6fc55bf65b187fec4175f80d08c165a/rtsp.bend as Rtsp

RTSP client (RFC 2326): a session that gives a camera's frames.

s <- Rtsp.open(E.Opts.new("rtsp://user:pass@camera/stream")) (s, frame) <- Rtsp.next(s) as many times as wanted Rtsp.close(s)

What goes on the wire:

C: DESCRIBE url S: 401, WWW-Authenticate (if it wants a login) C: DESCRIBE url, Authorization S: 200, the SDP C: SETUP video, Transport: RTP/AVP/TCP;interleaved=0-1 S: 200, Session C: SETUP audio, interleaved=2-3 (when there is AAC and it is wanted) C: PLAY url, Session S: 200 S: $ 0 <len> <RTP packet> ... (on the same connection) C: GET_PARAMETER url, Session (at half the session's timeout) C: TEARDOWN url, Session

A frame is a whole picture (its NAL units in Annex B form) or a run of AAC frames, with its time; frame.bend says what is in one, rec.bend turns them into a file. This file is the IO; what to say and what a reply means is in rtsp_core.bend, rtp.bend and depay.bend, pure.

9 imports
import Base
import ./bytes.bend as B
import ./rtsp_core.bend as C
import ./rtp.bend as R
import ./conn.bend as N
import ./opts.bend as E
import ./depay.bend as D
import ./audio.bend as U
import ./frame.bend as F

Types

type Cfg source · line 38 · raw

Data

What a request needs: the video's payload type, the session's URL and id, the server's challenge and the login that answers it, how often to say we are alive (ms), and how long to wait for the server (ms).

type Sess source · line 45 · raw

Type

An open session: the connection and what was read past the last frame, the frames being put together and those ready, when the next keep-alive is due and the next request's number.

type End source · line 50 · raw

Type

How the steps inside end: a session opened, or a frame read.

Definitions

def Fin source · line 54 · raw

Type

def Cfg.every source · line 57 · raw

@c:Cfg -> Nat

def Cfg.wait source · line 61 · raw

@c:Cfg -> U32

def Cfg.request source · line 66 · raw

@c:Cfg -> @method:String -> @cseq:U32 -> String

A request of this session, signed.

def Rtsp.put source · line 72 · raw

@rtp:Bool -> @sound:Bool -> @data:List<&2, U32> -> @pt:U32 -> @st:0xd6fc55bf65b187fec4175f80d08c165a/depay.Asm -> 0xd6fc55bf65b187fec4175f80d08c165a/depay.Got

What a channel's packet gives: channel 0 is the video's RTP, channel 2 the audio's; 1 and 3 are their RTCP, which is not read.

def Cfg.put source · line 79 · raw

@c:Cfg -> @+chan:U32 -> @data:List<&2, U32> -> @st:0xd6fc55bf65b187fec4175f80d08c165a/depay.Asm -> 0xd6fc55bf65b187fec4175f80d08c165a/depay.Got

def Rtsp.reply source · line 88 · raw

@fuel:Nat -> @m:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Msg -> @buf:List<&2, U32> -> @s:Socket -> @+wait:U32 -> @k:(@_:Socket -> @_:List<&2, U32> -> @_:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Resp -> IO(Fin)) -> IO(Fin)

The next reply, the interleaved packets before it skipped. Each turn takes a message off the buffer or reads more, so the fuel bounds it.

def Rtsp.ask source · line 102 · raw

@s:Socket -> @+buf:List<&2, U32> -> @req:String -> @wait:U32 -> @k:(@_:Socket -> @_:List<&2, U32> -> @_:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Resp -> IO(Fin)) -> IO(Fin)

Send a request and read its reply.

def Rtsp.emit source · line 111 · raw

@g:0xd6fc55bf65b187fec4175f80d08c165a/depay.Got -> @s:Socket -> @rest:List<&2, U32> -> @c:Cfg -> @ka:Nat -> @cseq:U32 -> @info:0xd6fc55bf65b187fec4175f80d08c165a/frame.Info -> @on:(@_:Socket -> @_:List<&2, U32> -> @_:0xd6fc55bf65b187fec4175f80d08c165a/depay.Asm -> @_:0xd6fc55bf65b187fec4175f80d08c165a/frame.Info -> IO(Fin)) -> IO(Fin)

The frames a packet completed: the first is given with a session that holds the rest; none, and the reading goes on.

def Rtsp.wait source · line 121 · raw

@due:Bool -> @+now:Nat -> @buf:List<&2, U32> -> @s:Socket -> @+c:Cfg -> @ka:Nat -> @+cseq:U32 -> @again:(@_:Socket -> @_:List<&2, U32> -> @_:Nat -> @_:U32 -> IO(Fin)) -> IO(Fin)

With nothing whole in the buffer: a keep-alive when one is due, and more bytes.

def Rtsp.due source · line 131 · raw

@+now:Nat -> @buf:List<&2, U32> -> @s:Socket -> @c:Cfg -> @+ka:Nat -> @cseq:U32 -> @again:(@_:Socket -> @_:List<&2, U32> -> @_:Nat -> @_:U32 -> IO(Fin)) -> IO(Fin)

def Rtsp.loop source · line 138 · raw

@fuel:Nat -> @m:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Msg -> @buf:List<&2, U32> -> @s:Socket -> @asm:0xd6fc55bf65b187fec4175f80d08c165a/depay.Asm -> @+c:Cfg -> @+ka:Nat -> @+cseq:U32 -> @info:0xd6fc55bf65b187fec4175f80d08c165a/frame.Info -> IO(Fin)

Until a frame: a packet off the buffer into the frames being put together, a reply (to a keep-alive) dropped, and more bytes when the buffer has no whole message.

def Rtsp.pop source · line 158 · raw

@queue:List<&2, 0xd6fc55bf65b187fec4175f80d08c165a/frame.Frame> -> @s:Socket -> @+buf:List<&2, U32> -> @asm:0xd6fc55bf65b187fec4175f80d08c165a/depay.Asm -> @c:Cfg -> @ka:Nat -> @cseq:U32 -> @info:0xd6fc55bf65b187fec4175f80d08c165a/frame.Info -> IO(Fin)

def Rtsp.play.got source · line 169 · raw

@ok:Bool -> @r:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Resp -> @s:Socket -> @buf:List<&2, U32> -> @+c:Cfg -> @now:Nat -> @next:U32 -> @asm:0xd6fc55bf65b187fec4175f80d08c165a/depay.Asm -> @info:0xd6fc55bf65b187fec4175f80d08c165a/frame.Info -> IO(Fin)

def Rtsp.play source · line 179 · raw

@s:Socket -> @buf:List<&2, U32> -> @+c:Cfg -> @+next:U32 -> @asm:0xd6fc55bf65b187fec4175f80d08c165a/depay.Asm -> @info:0xd6fc55bf65b187fec4175f80d08c165a/frame.Info -> IO(Fin)

PLAY, and the session is open.

def Rtsp.info source · line 187 · raw

@+m:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Media -> @+aud:0xd6fc55bf65b187fec4175f80d08c165a/audio.Aud -> 0xd6fc55bf65b187fec4175f80d08c165a/frame.Info

def Rtsp.audio.on source · line 191 · raw

@s:Socket -> @buf:List<&2, U32> -> @c:Cfg -> @+m:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Media -> @+aud:0xd6fc55bf65b187fec4175f80d08c165a/audio.Aud -> IO(Fin)

def Rtsp.audio.got source · line 196 · raw

@ok:Bool -> @s:Socket -> @buf:List<&2, U32> -> @c:Cfg -> @+m:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Media -> @aud:0xd6fc55bf65b187fec4175f80d08c165a/audio.Aud -> IO(Fin)

def Rtsp.audio source · line 207 · raw

@want:Bool -> @s:Socket -> @buf:List<&2, U32> -> @+c:Cfg -> @+m:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Media -> @aud:0xd6fc55bf65b187fec4175f80d08c165a/audio.Aud -> @req:String -> IO(Fin)

The audio's track too, on channels 2 and 3, when it is wanted and it is AAC or G.711; a server that refuses it gives the video alone.

def Rtsp.setup.got source · line 217 · raw

@ok:Bool -> @+r:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Resp -> @s:Socket -> @buf:List<&2, U32> -> @+m:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Media -> @uri:String -> @+chal:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Chal -> @+user:String -> @+pass:String -> @+cnonce:String -> @a:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Media -> @aurl:String -> @+o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> IO(Fin)

def Rtsp.setup source · line 236 · raw

@problem:String -> @s:Socket -> @buf:List<&2, U32> -> @+m:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Media -> @+base:String -> @+sdp:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Sdp -> @+chal:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Chal -> @+user:String -> @+pass:String -> @+cnonce:String -> @+a:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Media -> @+o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> IO(Fin)

SETUP for the video's track, RTP interleaved on channels 0 and 1.

def Rtsp.described source · line 252 · raw

@+r:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Resp -> @s:Socket -> @buf:List<&2, U32> -> @+uri:String -> @chal:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Chal -> @user:String -> @pass:String -> @cnonce:String -> @o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> IO(Fin)

The description is here: its video stream, and the URL its control attributes are relative to (Content-Base, else the request's).

def Rtsp.describe.req source · line 260 · raw

@chal:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Chal -> @user:String -> @pass:String -> @+uri:String -> @cseq:U32 -> @cnonce:String -> String

def Rtsp.why source · line 267 · raw

@+code:U32 -> 0xd6fc55bf65b187fec4175f80d08c165a/opts.Why

Why a DESCRIBE was refused: a login (401, 403), no such stream (404), or something else.

def Rtsp.words source · line 271 · raw

@+code:U32 -> String

def Rtsp.refused source · line 277 · raw

@s:Socket -> @+code:U32 -> IO(Fin)

def Rtsp.other source · line 283 · raw

@moved:Bool -> @s:Socket -> @code:U32 -> @to:String -> IO(Fin)

A reply that is neither the description nor a challenge: a redirect (a 3xx with a Location) ends as Moved, its words the new address, for open to follow; anything else is a refusal.

def Rtsp.other.of source · line 290 · raw

@s:Socket -> @+code:U32 -> @+to:String -> IO(Fin)

def Rtsp.again.got source · line 294 · raw

@ok:Bool -> @+r:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Resp -> @s:Socket -> @buf:List<&2, U32> -> @uri:String -> @chal:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Chal -> @user:String -> @pass:String -> @cnonce:String -> @o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> IO(Fin)

def Rtsp.again source · line 304 · raw

@none:Bool -> @s:Socket -> @buf:List<&2, U32> -> @+uri:String -> @+chal:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Chal -> @+user:String -> @+pass:String -> @+cnonce:String -> @+o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> IO(Fin)

DESCRIBE again, answering the challenge; a server with none we can answer ends it.

def Rtsp.again.with source · line 316 · raw

@+chal:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Chal -> @s:Socket -> @buf:List<&2, U32> -> @uri:String -> @user:String -> @pass:String -> @cnonce:String -> @o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> IO(Fin)

def Rtsp.first.got source · line 321 · raw

@ok:Bool -> @login:Bool -> @+r:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Resp -> @s:Socket -> @buf:List<&2, U32> -> @uri:String -> @user:String -> @pass:String -> @cnonce:String -> @o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> IO(Fin)

def Rtsp.describe source · line 333 · raw

@s:Socket -> @+uri:String -> @+user:String -> @+pass:String -> @+cnonce:String -> @+o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> IO(Fin)

DESCRIBE with no login first: a 401 names the challenge to answer.

def Rtsp.pick source · line 340 · raw

@mine:String -> @theirs:String -> String

The options' login, or the URL's when they have none.

def Rtsp.go source · line 347 · raw

@bad:Bool -> @+u:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Url -> @+tls:Bool -> @+o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> IO(Fin)

def Rtsp.start.url source · line 362 · raw

@+u:0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.Url -> @tls:Bool -> @o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> IO(Fin)

def Rtsp.start.tls source · line 365 · raw

@+tls:Bool -> @+url:String -> @o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> IO(Fin)

def Rtsp.start source · line 369 · raw

@+o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> IO(Fin)

def Opened source · line 376 · raw

Type

def Read source · line 379 · raw

Type

def Rtsp.open.end source · line 382 · raw

@r:Fin -> Opened

def Rtsp.next.end source · line 391 · raw

@r:Fin -> Read

def Rtsp.open.moved source · line 400 · raw

@moved:Bool -> @+e:0xd6fc55bf65b187fec4175f80d08c165a/opts.Err -> @o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> @again:(@_:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> IO(Opened)) -> IO(Opened)

def Rtsp.open.got source · line 409 · raw

@r:Opened -> @o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> @again:(@_:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> IO(Opened)) -> IO(Opened)

def Rtsp.open.at source · line 417 · raw

@hops:Nat -> @+o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> IO(Opened)

A server may send the client elsewhere (301, 302): up to hops times.

def Rtsp.open source · line 429 · raw

@o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> IO(Opened)

Open a session: connect, log in, set the streams up and play, following a redirect if the server answers with one. On a failure the connection is already closed.

def Rtsp.reopen.end source · line 432 · raw

@r:Opened -> @rest:IO(Opened) -> IO(Opened)

def Rtsp.reopen.go source · line 439 · raw

@late:Bool -> @o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> @rest:IO(Opened) -> IO(Opened)

def Rtsp.reopen.at source · line 448 · raw

@tries:Nat -> @+o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> @+until:Nat -> @+delay:U32 -> IO(Opened)

def Rtsp.reopen source · line 463 · raw

@o:0xd6fc55bf65b187fec4175f80d08c165a/opts.Opts -> @until:Nat -> IO(Opened)

Open again a session that was lost: it waits half a second, tries, and doubles the wait (up to 15 s) after each failure, until the session opens, an error says it never will (E.Err.lasting), or the clock (IO.now's, ms) reaches until.

def Rtsp.next source · line 469 · raw

@x:Sess -> IO(Read)

The next frame, and the session to go on with. It waits for the server at most the options' wait; on a failure the connection is already closed and the session is over.

def Rtsp.about source · line 476 · raw

@x:Sess -> Pair(Sess, 0xd6fc55bf65b187fec4175f80d08c165a/frame.Info)

What the session's stream is, and the session back.

def Rtsp.close source · line 482 · raw

@x:Sess -> IO(Unit)

End the session: TEARDOWN, and the connection closed once the server has answered or gone quiet for a fifth of a second.