pull.bend relies on unsafe/foreign
raw source on the hub · import 0x2355f41b6f5561f50104a75ccca27cf7/pull.bend as Pull
Record a camera or a stream into a file.
bend pull.bend -o pull ./pull rtsp://user:pass@camera.example:554/stream out.ts 10 ./pull rtmp://192.168.0.20/live/cam out.flv 10
The file's name picks the form: .ts is MPEG-TS, with the audio and each frame's time; .flv (RTMP only) is the stream's tags as they come; anything else is the raw video (.h264, .h265). The last argument is how many seconds to record (10 when left out).
It is also the example of the library: open a session, take frames until the time is up, close it. It runs as a native binary only (the sockets are in net.c); rtsps:// and rtmps:// are the same under TLS, and STREAM_CAFILE names a PEM file to trust instead of the system's.
11 imports
import Base import ./bytes.bend as B import ./conn.bend as N import ./opts.bend as E import ./frame.bend as F import ./rec.bend as W import ./rtsp.bend as S import ./rtsp_core.bend as C import ./rtmp.bend as M import ./rtmp_core.bend as K import ./flv.bend as V
Types
type Stat source · line 33 · raw
Data
What was written: frames (or tags) and bytes; the last frame's time and what is added to a frame's time (a stream that comes back starts its times over); how many times the stream was lost and found; and whether the next tag is the first after one of those (RTMP, whose times start anywhere).
Stat@frames:U32 -> @bytes:U32 -> @last:U32 -> @off:U32 -> @lost:U32 -> @fresh:Bool -> Stat
type Sink source · line 227 · raw
Data
Where an RTMP session's tags go: into an FLV as they are (first: the file's header is still to write), or, their frames taken out, into a recorder that is made at the first frame, once the stream is known.
Tags@first:Bool -> Sink
Frames@v:0x2355f41b6f5561f50104a75ccca27cf7/flv.Flv -> @rec:Maybe<&2, 0x2355f41b6f5561f50104a75ccca27cf7/rec.Rec> -> @path:String -> Sink
type Sunk source · line 231 · raw
Data
Sunk@bytes:List<&2, U32> -> @k:Sink -> Sunk
Definitions
def Stat.add source · line 36 · raw
@st:Stat -> @bytes:U32 -> Stat
def Stat.place source · line 41 · raw
@st:Stat -> @f:0x2355f41b6f5561f50104a75ccca27cf7/frame.Frame -> Pair(Stat, 0x2355f41b6f5561f50104a75ccca27cf7/frame.Frame)
The frame at its place in the file, and its time noted.
def Stat.tag source · line 49 · raw
@st:Stat -> @t:0x2355f41b6f5561f50104a75ccca27cf7/rtmp_core.Tag -> Pair(Stat, 0x2355f41b6f5561f50104a75ccca27cf7/rtmp_core.Tag)
The tag at its place in the file: the first after the stream came back goes a tenth of a second after the last one written, and the rest keep their distance from it.
def Stat.found source · line 58 · raw
@st:Stat -> Stat
The stream was lost and found: what comes goes a tenth of a second after the last frame written.
def Stat.show source · line 62 · raw
@st:Stat -> @kind:String -> String
def Pull.error source · line 68 · raw
@e:0x2355f41b6f5561f50104a75ccca27cf7/opts.Err -> @st:Stat -> String
def Wrote source · line 76 · raw
Type
def Pull.save.end source · line 79 · raw
@m:Wrote -> File
def Pull.save source · line 84 · raw
@bs:List<&2, U32> -> @f:File -> IO(File)
Bytes to the file (nothing to do for none).
def Pull.end source · line 94 · raw
@-X:Type -> @x:X -> @f:File -> @stop:(@_:X -> IO(Unit)) -> @words:String -> IO(String)
The session x of any kind ended and the file closed, with these words.
def Pull.timed source · line 100 · raw
@up:Bool -> @-X:Type -> @x:X -> @f:File -> @st:Stat -> @kind:String -> @stop:(@_:X -> IO(Unit)) -> @again:(@_:X -> @_:File -> @_:Stat -> IO(String)) -> IO(String)
def Pull.write source · line 109 · raw
@-X:Type -> @+bs:List<&2, U32> -> @x:X -> @f:File -> @until:Nat -> @st:Stat -> @kind:String -> @stop:(@_:X -> IO(Unit)) -> @again:(@_:X -> @_:File -> @_:Stat -> IO(String)) -> IO(String)
Bytes written, then: stop if the time is up, else around again.
def Pull.rtsp.put source · line 121 · raw
@w:0x2355f41b6f5561f50104a75ccca27cf7/rec.Wrote -> @s:0x2355f41b6f5561f50104a75ccca27cf7/rtsp.Sess -> @f:File -> @until:Nat -> @st:Stat -> @kind:String -> @again:(@_:0x2355f41b6f5561f50104a75ccca27cf7/rtsp.Sess -> @_:File -> @_:0x2355f41b6f5561f50104a75ccca27cf7/rec.Rec -> @_:Stat -> IO(String)) -> IO(String)
def Pull.rtsp.placed source · line 127 · raw
@sf:Pair(Stat, 0x2355f41b6f5561f50104a75ccca27cf7/frame.Frame) -> @rec:0x2355f41b6f5561f50104a75ccca27cf7/rec.Rec -> @s:0x2355f41b6f5561f50104a75ccca27cf7/rtsp.Sess -> @f:File -> @until:Nat -> @kind:String -> @again:(@_:0x2355f41b6f5561f50104a75ccca27cf7/rtsp.Sess -> @_:File -> @_:0x2355f41b6f5561f50104a75ccca27cf7/rec.Rec -> @_:Stat -> IO(String)) -> IO(String)
def Pull.rtsp.found source · line 134 · raw
@r:0x2355f41b6f5561f50104a75ccca27cf7/rtsp.Opened -> @f:File -> @rec:0x2355f41b6f5561f50104a75ccca27cf7/rec.Rec -> @st:Stat -> @+kind:String -> @+e:0x2355f41b6f5561f50104a75ccca27cf7/opts.Err -> @again:(@_:0x2355f41b6f5561f50104a75ccca27cf7/rtsp.Sess -> @_:File -> @_:0x2355f41b6f5561f50104a75ccca27cf7/rec.Rec -> @_:Stat -> IO(String)) -> IO(String)
The session is open again, or it will not be: what was recorded is kept either way, and the file says so.
def Pull.rtsp.lost source · line 146 · raw
@retry:Bool -> @+e:0x2355f41b6f5561f50104a75ccca27cf7/opts.Err -> @f:File -> @rec:0x2355f41b6f5561f50104a75ccca27cf7/rec.Rec -> @+until:Nat -> @st:Stat -> @kind:String -> @o:0x2355f41b6f5561f50104a75ccca27cf7/opts.Opts -> @again:(@_:0x2355f41b6f5561f50104a75ccca27cf7/rtsp.Sess -> @_:File -> @_:0x2355f41b6f5561f50104a75ccca27cf7/rec.Rec -> @_:Stat -> IO(String)) -> IO(String)
The stream was lost. If it had been flowing and the error is not one that lasts, open it again and go on in the same file.
def Stat.any source · line 159 · raw
@st:Stat -> Bool
def Pull.rtsp.got source · line 163 · raw
@r:0x2355f41b6f5561f50104a75ccca27cf7/rtsp.Read -> @f:File -> @rec:0x2355f41b6f5561f50104a75ccca27cf7/rec.Rec -> @until:Nat -> @+st:Stat -> @kind:String -> @o:0x2355f41b6f5561f50104a75ccca27cf7/opts.Opts -> @again:(@_:0x2355f41b6f5561f50104a75ccca27cf7/rtsp.Sess -> @_:File -> @_:0x2355f41b6f5561f50104a75ccca27cf7/rec.Rec -> @_:Stat -> IO(String)) -> IO(String)
def Pull.rtsp.loop source · line 174 · raw
@fuel:Nat -> @s:0x2355f41b6f5561f50104a75ccca27cf7/rtsp.Sess -> @f:File -> @rec:0x2355f41b6f5561f50104a75ccca27cf7/rec.Rec -> @+until:Nat -> @st:Stat -> @+kind:String -> @+o:0x2355f41b6f5561f50104a75ccca27cf7/opts.Opts -> IO(String)
Frame after frame into the file, until the time is up.
def Pull.rtsp.file source · line 185 · raw
@r:Result<&1, &1, Pair(U32, String), File> -> @s:0x2355f41b6f5561f50104a75ccca27cf7/rtsp.Sess -> @rec:0x2355f41b6f5561f50104a75ccca27cf7/rec.Rec -> @until:Nat -> @kind:String -> @o:0x2355f41b6f5561f50104a75ccca27cf7/opts.Opts -> IO(String)
def Pull.kind source · line 197 · raw
@+i:0x2355f41b6f5561f50104a75ccca27cf7/frame.Info -> String
What is being recorded: "H264", or "H264+PCMA" with audio.
def Pull.rtsp.at source · line 201 · raw
@sa:Pair(0x2355f41b6f5561f50104a75ccca27cf7/rtsp.Sess, 0x2355f41b6f5561f50104a75ccca27cf7/frame.Info) -> @+out:String -> @ms:U32 -> @o:0x2355f41b6f5561f50104a75ccca27cf7/opts.Opts -> IO(String)
def Pull.rtsp.opened source · line 209 · raw
@r:0x2355f41b6f5561f50104a75ccca27cf7/rtsp.Opened -> @out:String -> @ms:U32 -> @o:0x2355f41b6f5561f50104a75ccca27cf7/opts.Opts -> IO(String)
def Pull.rtsp source · line 216 · raw
@+o:0x2355f41b6f5561f50104a75ccca27cf7/opts.Opts -> @out:String -> @ms:U32 -> IO(String)
def Sink.for source · line 234 · raw
@+path:String -> Sink
def Sink.rec source · line 238 · raw
@m:Maybe<&2, 0x2355f41b6f5561f50104a75ccca27cf7/rec.Rec> -> @path:String -> @info:0x2355f41b6f5561f50104a75ccca27cf7/frame.Info -> 0x2355f41b6f5561f50104a75ccca27cf7/rec.Rec
def Sink.wrote source · line 245 · raw
@w:0x2355f41b6f5561f50104a75ccca27cf7/rec.Wrote -> @v:0x2355f41b6f5561f50104a75ccca27cf7/flv.Flv -> @path:String -> Sunk
def Sink.frames source · line 249 · raw
@o:0x2355f41b6f5561f50104a75ccca27cf7/flv.Out -> @rec:Maybe<&2, 0x2355f41b6f5561f50104a75ccca27cf7/rec.Rec> -> @+path:String -> Sunk
def Sink.put source · line 256 · raw
@k:Sink -> @t:0x2355f41b6f5561f50104a75ccca27cf7/rtmp_core.Tag -> Sunk
def Pull.rtmp.put source · line 264 · raw
@u:Sunk -> @s:0x2355f41b6f5561f50104a75ccca27cf7/rtmp.Sess -> @f:File -> @until:Nat -> @st:Stat -> @again:(@_:0x2355f41b6f5561f50104a75ccca27cf7/rtmp.Sess -> @_:File -> @_:Sink -> @_:Stat -> IO(String)) -> IO(String)
def Pull.rtmp.placed source · line 270 · raw
@sf:Pair(Stat, 0x2355f41b6f5561f50104a75ccca27cf7/rtmp_core.Tag) -> @k:Sink -> @s:0x2355f41b6f5561f50104a75ccca27cf7/rtmp.Sess -> @f:File -> @until:Nat -> @again:(@_:0x2355f41b6f5561f50104a75ccca27cf7/rtmp.Sess -> @_:File -> @_:Sink -> @_:Stat -> IO(String)) -> IO(String)
def Pull.rtmp.found source · line 275 · raw
@r:0x2355f41b6f5561f50104a75ccca27cf7/rtmp.Opened -> @f:File -> @k:Sink -> @st:Stat -> @+e:0x2355f41b6f5561f50104a75ccca27cf7/opts.Err -> @again:(@_:0x2355f41b6f5561f50104a75ccca27cf7/rtmp.Sess -> @_:File -> @_:Sink -> @_:Stat -> IO(String)) -> IO(String)
def Pull.rtmp.lost source · line 288 · raw
@retry:Bool -> @+e:0x2355f41b6f5561f50104a75ccca27cf7/opts.Err -> @f:File -> @k:Sink -> @+until:Nat -> @st:Stat -> @o:0x2355f41b6f5561f50104a75ccca27cf7/opts.Opts -> @again:(@_:0x2355f41b6f5561f50104a75ccca27cf7/rtmp.Sess -> @_:File -> @_:Sink -> @_:Stat -> IO(String)) -> IO(String)
The stream was lost (or its publisher stopped). If it had been flowing and the error is not one that lasts, open it again and go on in the same file.
def Pull.rtmp.got source · line 300 · raw
@r:0x2355f41b6f5561f50104a75ccca27cf7/rtmp.Read -> @f:File -> @k:Sink -> @until:Nat -> @+st:Stat -> @o:0x2355f41b6f5561f50104a75ccca27cf7/opts.Opts -> @again:(@_:0x2355f41b6f5561f50104a75ccca27cf7/rtmp.Sess -> @_:File -> @_:Sink -> @_:Stat -> IO(String)) -> IO(String)
def Pull.rtmp.loop source · line 311 · raw
@fuel:Nat -> @s:0x2355f41b6f5561f50104a75ccca27cf7/rtmp.Sess -> @f:File -> @k:Sink -> @+until:Nat -> @st:Stat -> @+o:0x2355f41b6f5561f50104a75ccca27cf7/opts.Opts -> IO(String)
Tag after tag into the file, until the time is up.
def Pull.rtmp.file source · line 322 · raw
@r:Result<&1, &1, Pair(U32, String), File> -> @s:0x2355f41b6f5561f50104a75ccca27cf7/rtmp.Sess -> @k:Sink -> @until:Nat -> @o:0x2355f41b6f5561f50104a75ccca27cf7/opts.Opts -> IO(String)
def Pull.rtmp.opened source · line 333 · raw
@r:0x2355f41b6f5561f50104a75ccca27cf7/rtmp.Opened -> @+out:String -> @ms:U32 -> @o:0x2355f41b6f5561f50104a75ccca27cf7/opts.Opts -> IO(String)
def Pull.rtmp source · line 343 · raw
@+o:0x2355f41b6f5561f50104a75ccca27cf7/opts.Opts -> @out:String -> @ms:U32 -> IO(String)
def Pull.secs source · line 351 · raw
@args:List<&2, String> -> U32
def Pull.copy source · line 358 · raw
@xs:List<&1, String> -> List<&2, String>
def Pull.ca source · line 365 · raw
@r:Result<&1, &1, Pair(U32, String), String> -> String
def Pull.by source · line 372 · raw
@rtmp:Bool -> @o:0x2355f41b6f5561f50104a75ccca27cf7/opts.Opts -> @out:String -> @ms:U32 -> IO(String)
def Pull.run source · line 379 · raw
@args:List<&2, String> -> IO(Unit)
def main source · line 391 · raw
IO(Unit)