rtsp_core.bend checks
raw source on the hub · import 0xd6fc55bf65b187fec4175f80d08c165a/rtsp_core.bend as Rtsp_core
The pure core of the RTSP client (RFC 2326): URLs, requests, what comes back on the connection (replies, and RTP packets interleaved with them), authentication (Basic and Digest, RFC 2617), and the session description (SDP, RFC 4566). Nothing here does IO, so LAWS.bend can state laws about it; rtsp.bend is the dialog.
6 imports
import Base import ./text.bend as T import ./bytes.bend as B import ./b64.bend as E import ./md5.bend as D import ./sha256.bend as H
Types
type Hdr source · line 14 · raw
Data
A header field.
Hdr@name:String -> @value:String -> Hdr
type Url source · line 21 · raw
Data
rtsp://[user[:pass]@]host[:port][/path]; no host means not an RTSP URL.
Url@host:String -> @port:U32 -> @user:String -> @pass:String -> @path:String -> Url
type Resp source · line 168 · raw
Data
A reply: its status code (0 for a request the server sends us), its headers and its body.
Resp@code:U32 -> @headers:List<&2, Hdr> -> @body:List<&2, U32> -> Resp
type Msg source · line 174 · raw
Data
The first whole thing in a buffer, and what follows it: a reply; an interleaved packet ("$", channel, length; RFC 2326 10.12); Need when it is not all there yet; Bad when it can be neither.
NeedMsg
BadMsg
Reply@resp:Resp -> @rest:List<&2, U32> -> Msg
Frame@chan:U32 -> @data:List<&2, U32> -> @rest:List<&2, U32> -> Msg
type Chal source · line 343 · raw
Data
A challenge (WWW-Authenticate): its scheme in lower case ("" for none) and Digest's parameters.
Chal@scheme:String -> @realm:String -> @nonce:String -> @opaque:String -> @qop:String -> @algo:String -> Chal
type Scan source · line 353 · raw
Data
A parameter list cut at its commas, those inside quotes kept: where the scan is (in quotes, after a backslash), the item so far (reversed) and the items (reversed).
Scan@q:Bool -> @esc:Bool -> @cur:String -> @acc:List<&2, String> -> Scan
type Media source · line 545 · raw
Data
A media stream of a description: its kind ("video", "audio"), payload type, codec ("H264", in upper case) and clock rate (from a=rtpmap), control URL (a=control) and format parameters (a=fmtp).
Media@kind:String -> @pt:U32 -> @codec:String -> @clock:U32 -> @control:String -> @fmtp:String -> Media
type Sdp source · line 550 · raw
Data
A description: the session's control URL and its streams.
Sdp@control:String -> @medias:List<&2, Media> -> Sdp
type Sdp.St source · line 558 · raw
Data
The reading so far: the session's control, the streams done (reversed), the stream being read and whether there is one.
St@control:String -> @done:List<&2, Media> -> @cur:Media -> @has:Bool -> Sdp.St
Definitions
def Url.none source · line 24 · raw
Url
def Url.hex source · line 27 · raw
@+c:U32 -> U32
def Url.unpct.put source · line 32 · raw
@ok:Bool -> @+a:U32 -> @+b:U32 -> @rest:String -> String
def Url.unpct source · line 41 · raw
@s:String -> String
Percent-decoding (RFC 3986 2.1), for a user or password with "@" or ":" in it; a "%" not followed by two hex digits stays.
def Url.port source · line 50 · raw
@m:Maybe<&2, U32> -> @dflt:U32 -> U32
def Url.host source · line 58 · raw
@hp:List<&2, String> -> @dflt:U32 -> @user:String -> @pass:String -> @path:String -> Url
"host[:port]"; dflt: the port when absent.
def Url.creds source · line 69 · raw
@up:List<&2, String> -> @hp:List<&2, String> -> @dflt:U32 -> @path:String -> Url
"user[:pass]", percent-decoded.
def Url.auth source · line 80 · raw
@+ps:List<&2, String> -> @dflt:U32 -> @path:String -> Url
"[user[:pass]@]host[:port]"; the last "@" divides them.
def Url.rest source · line 85 · raw
@parts:List<&2, String> -> @dflt:U32 -> Url
def Url.of source · line 96 · raw
@+url:String -> @+scheme:String -> @dflt:U32 -> Url
The URL after "scheme://" (scheme in lower case, with its "://"), on its default port when it names none; anything else gives no host.
def Url.esc source · line 101 · raw
@s:String -> String
def Url.show source · line 112 · raw
@u:Url -> @scheme:String -> String
The URL as it goes in a request: no user or password, spaces escaped, no line end.
def Url.host.of source · line 116 · raw
@u:Url -> String
def Url.port.of source · line 120 · raw
@u:Url -> U32
def Url.user source · line 124 · raw
@u:Url -> String
def Url.pass source · line 128 · raw
@u:Url -> String
def Url.path source · line 132 · raw
@u:Url -> String
def Rtsp.text source · line 142 · raw
@raw:String -> String
A line of a request with no CR or LF in it, whatever went in, and the CRLF that ends it: no method, URL or header value can start another line. LAWS.bend proves it for every string.
def Rtsp.line source · line 145 · raw
@raw:String -> String
def Rtsp.fields source · line 148 · raw
@hs:List<&2, Hdr> -> String
def Rtsp.request source · line 158 · raw
@method:String -> @uri:String -> @cseq:U32 -> @hs:List<&2, Hdr> -> String
A request: the request line, CSeq, User-Agent, the given headers, and the empty line (RFC 2326 6).
def Hdr.trim source · line 180 · raw
@s:String -> String
def Hdr.of source · line 183 · raw
@parts:List<&2, String> -> Hdr
def Hdr.parse source · line 192 · raw
@ls:List<&2, String> -> List<&2, Hdr>
The header lines after the first, each as a field with its name in lower case.
def Hdr.get source · line 200 · raw
@hs:List<&2, Hdr> -> @+name:String -> String
The first value of a header (name in lower case), or "".
def Hdr.keep source · line 208 · raw
@hit:Bool -> @v:String -> @rest:List<&2, String> -> List<&2, String>
def Hdr.all source · line 216 · raw
@hs:List<&2, Hdr> -> @+name:String -> List<&2, String>
Every value of a header, in order.
def Resp.code source · line 224 · raw
@r:Resp -> U32
def Resp.headers source · line 228 · raw
@r:Resp -> List<&2, Hdr>
def Resp.body source · line 232 · raw
@r:Resp -> List<&2, U32>
def Resp.get source · line 236 · raw
@r:Resp -> @name:String -> String
def Rtsp.num source · line 239 · raw
@m:Maybe<&2, U32> -> U32
def Rtsp.word source · line 247 · raw
@ws:List<&2, String> -> @n:Nat -> String
The n-th word of a list, or "".
def Rtsp.status source · line 257 · raw
@+ws:List<&2, String> -> U32
The status of "RTSP/1.0 200 OK"; 0 for any other first line.
def Rtsp.uncr source · line 261 · raw
@s:String -> String
def Rtsp.head source · line 272 · raw
@bs:List<&2, U32> -> @acc:List<&2, U32> -> 0xd6fc55bf65b187fec4175f80d08c165a/bytes.Cut
The end of a message's head: the bytes before the first empty line, and the bytes after it. acc: the head so far, reversed.
def Rtsp.body source · line 281 · raw
@c:0xd6fc55bf65b187fec4175f80d08c165a/bytes.Cut -> @code:U32 -> @hs:List<&2, Hdr> -> Msg
def Rtsp.reply.with source · line 288 · raw
@code:U32 -> @+hs:List<&2, Hdr> -> @rest:List<&2, U32> -> Msg
def Rtsp.reply.lines source · line 292 · raw
@ls:List<&2, String> -> @rest:List<&2, U32> -> Msg
def Rtsp.reply source · line 299 · raw
@c:0xd6fc55bf65b187fec4175f80d08c165a/bytes.Cut -> Msg
def Rtsp.frame source · line 306 · raw
@chan:U32 -> @c:0xd6fc55bf65b187fec4175f80d08c165a/bytes.Cut -> Msg
def Rtsp.demux.text source · line 313 · raw
@letter:Bool -> @buf:List<&2, U32> -> Msg
def Rtsp.demux source · line 323 · raw
@buf:List<&2, U32> -> Msg
The first message in the buffer. A "$" opens an interleaved packet; a letter opens a reply (or a request from the server); the CR and LF some servers leave between messages are skipped.
def Chal.none source · line 347 · raw
Chal
def Chal.scan.ch source · line 356 · raw
@st:Scan -> @+c:U32 -> Scan
def Chal.scan.go source · line 366 · raw
@s:String -> @st:Scan -> Scan
def Chal.scan.fin source · line 373 · raw
@st:Scan -> List<&2, String>
def Chal.items source · line 378 · raw
@s:String -> List<&2, String>
"k=v, k="v, w"" as "k=v" items with the quotes taken off.
def Chal.kv source · line 381 · raw
@parts:List<&2, String> -> Hdr
def Chal.kvs source · line 388 · raw
@xs:List<&2, String> -> List<&2, Hdr>
def Chal.with source · line 395 · raw
@scheme:String -> @+ps:List<&2, Hdr> -> Chal
def Chal.words source · line 400 · raw
@ws:List<&2, String> -> Chal
def Chal.of source · line 408 · raw
@v:String -> Chal
One WWW-Authenticate value.
def Chal.all source · line 411 · raw
@vs:List<&2, String> -> List<&2, Chal>
def Chal.scheme source · line 418 · raw
@c:Chal -> String
def Chal.algo source · line 422 · raw
@c:Chal -> String
def Chal.digest source · line 428 · raw
@+c:Chal -> Bool
Whether this one can be answered: Basic, or Digest with MD5 (the default when no algorithm is named) or SHA-256 (RFC 7616).
def Chal.basic source · line 433 · raw
@c:Chal -> Bool
def Chal.first source · line 436 · raw
@cs:List<&2, Chal> -> @+digest:Bool -> Chal
def Chal.or.at source · line 444 · raw
@none:Bool -> @a:Chal -> @b:Chal -> Chal
def Chal.or source · line 451 · raw
@+a:Chal -> @b:Chal -> Chal
def Chal.pick source · line 458 · raw
@+cs:List<&2, Chal> -> Chal
The challenge to answer among a reply's: the first Digest one this client can answer, if there is one (the password does not travel; a server lists the one it prefers first, RFC 7616 3.7), else Basic, else none.
def Auth.md5 source · line 461 · raw
@s:String -> String
def Auth.sha256 source · line 464 · raw
@s:String -> String
def Auth.hash source · line 468 · raw
@sha:Bool -> @s:String -> String
The hash a Digest challenge names, in hex.
def Auth.esc source · line 475 · raw
@s:String -> String
def Auth.q source · line 487 · raw
@s:String -> String
A quoted string (RFC 2617 1.2), its quotes and backslashes escaped.
def Auth.response source · line 493 · raw
@+user:String -> @+realm:String -> @pass:String -> @method:String -> @+uri:String -> @+nonce:String -> @auth:Bool -> @+cnonce:String -> @+sha:Bool -> String
Digest's response (RFC 2617 3.2.2, RFC 7616 3.4.1): with qop=auth the nonce count and the client nonce enter the hash; without it, the 2069 form. sha: SHA-256 instead of MD5.
def Auth.opt source · line 501 · raw
@+v:String -> @text:String -> String
def Auth.digest source · line 504 · raw
@c:Chal -> @+user:String -> @pass:String -> @method:String -> @+uri:String -> @+cnonce:String -> String
def Auth.basic source · line 516 · raw
@user:String -> @pass:String -> String
def Auth.value.at source · line 519 · raw
@digest:Bool -> @basic:Bool -> @c:Chal -> @user:String -> @pass:String -> @method:String -> @uri:String -> @cnonce:String -> String
def Auth.value source · line 531 · raw
@+c:Chal -> @user:String -> @pass:String -> @method:String -> @uri:String -> @cnonce:String -> String
The Authorization value that answers a challenge for this request, or "" when there is none to answer. cnonce: a fresh random string.
def Auth.headers source · line 536 · raw
@+v:String -> List<&2, Hdr>
The headers that carry it: none when the value is empty.
def Media.none source · line 553 · raw
Media
def Sdp.push source · line 561 · raw
@has:Bool -> @cur:Media -> @done:List<&2, Media> -> List<&2, Media>
def Sdp.m source · line 569 · raw
@+ws:List<&2, String> -> Media
"m=video 0 RTP/AVP 96": the kind and the first payload type.
def Sdp.after source · line 572 · raw
@s:String -> @n:Nat -> String
def Sdp.rtpmap source · line 576 · raw
@+ws:List<&2, String> -> @m:Media -> Media
"96 H264/90000" into the stream, when it is about its payload type.
def Sdp.fmtp source · line 585 · raw
@+v:String -> @m:Media -> Media
"96 a=1; b=2" into the stream, when it is about its payload type.
def Sdp.control source · line 593 · raw
@v:String -> @m:Media -> Media
def Sdp.line source · line 597 · raw
@st:Sdp.St -> @+l:String -> Sdp.St
def Sdp.lines source · line 611 · raw
@ls:List<&2, String> -> @st:Sdp.St -> Sdp.St
def Sdp.fin source · line 618 · raw
@st:Sdp.St -> Sdp
def Sdp.of source · line 623 · raw
@text:String -> Sdp
A description from its text.
def Media.kind source · line 627 · raw
@m:Media -> String
def Media.pt source · line 631 · raw
@m:Media -> U32
def Media.codec source · line 635 · raw
@m:Media -> String
def Media.control source · line 639 · raw
@m:Media -> String
def Media.fmtp source · line 643 · raw
@m:Media -> String
def Sdp.find source · line 647 · raw
@ms:List<&2, Media> -> @+kind:String -> Media
def Sdp.media source · line 655 · raw
@s:Sdp -> @kind:String -> Media
The first stream of a kind (one with no kind when there is none).
def Sdp.session source · line 659 · raw
@s:Sdp -> String
def Sdp.param.of source · line 663 · raw
@parts:List<&2, String> -> Hdr
def Sdp.params source · line 670 · raw
@xs:List<&2, String> -> List<&2, Hdr>
def Sdp.param source · line 678 · raw
@fmtp:String -> @name:String -> String
A format parameter's value ("a=1; b=2" has "b" as "2"), or "".
def Sdp.sets source · line 681 · raw
@xs:List<&2, String> -> List<&2, List<&2, U32>>
def Sdp.some source · line 688 · raw
@s:String -> Bool
def Sdp.sprops source · line 694 · raw
@+fmtp:String -> List<&2, List<&2, U32>>
The parameter sets a stream announces, decoded: H.264's SPS and PPS (sprop-parameter-sets, RFC 6184 8.1), or H.265's VPS, SPS and PPS (sprop-vps, sprop-sps, sprop-pps; RFC 7798 7.1).
def Rtsp.control source · line 703 · raw
@+base:String -> @+control:String -> String
The URL a control attribute names (RFC 2326 C.1.1): itself when absolute, the base for "*" or none, else the base and it.
def Rtsp.session source · line 709 · raw
@v:String -> String
The session id of a Session header ("id;timeout=60").
def Rtsp.timeout.of source · line 712 · raw
@ps:List<&2, String> -> U32
def Rtsp.timeout source · line 723 · raw
@v:String -> U32
The seconds a Session header gives the session without a request ("id;timeout=30"); 60 when it says nothing (RFC 2326 12.37).
def Rtsp.signed.with source · line 726 · raw
@+auth:String -> @method:String -> @uri:String -> @cseq:U32 -> @+session:String -> @hs:List<&2, Hdr> -> String
def Rtsp.signed source · line 735 · raw
@c:Chal -> @user:String -> @pass:String -> @+method:String -> @+uri:String -> @cseq:U32 -> @cnonce:String -> @+session:String -> @hs:List<&2, Hdr> -> String
A request for this session: the given headers, the Session header once there is one, and the Authorization that answers the server's challenge for this method and URL.
def Sdp.problem source · line 742 · raw
@+m:Media -> String
What is wrong with a description for pulling video, or "": there must be a video stream, in H.264 or H.265.