~/bend-docscommunity

rec.bend source

rec.bend on the hub · documented module

# Frames into the bytes of a file, in one of three forms:##   raw  the video's access units one after the other, the announced#        parameter sets first (.h264, .h265): what a decoder reads, with#        no times and no audio;#   TS   MPEG-TS: each picture and each run of AAC frames in a PES#        packet with its time (.ts). G.711 has no place in a TS and is#        left out.#   sound  the audio alone, as it comes: AAC with its ADTS headers is an#        .aac file, G.711 samples an .alaw or .ulaw one.import Baseimport ./bytes.bend as Bimport ./ts.bend as Timport ./frame.bend as F# sets: the parameter sets still to go in front of the first picture.# vcc, acc, tcc: the packet counters of the video, the audio and the# tables. n: pictures written.type Rec is Data:  Raw{sets: List<&2, U32>}  Snd{}  Ts{h265: Bool, audio: Bool, vcc: U32, acc: U32, tcc: U32, n: U32, sets: List<&2, U32>}# What a frame gave: bytes to write, and the state after.type Wrote is Data:  Wrote{bytes: List<&2, U32>, r: Rec}def Rec.raw(i: F.Info) -> Rec:  Raw{F.Info.sets(i)}def Rec.ts(+i: F.Info) -> Rec:  Ts{F.Info.h265(i), String.eq(F.Info.audio(i), "AAC"), 0, 0, 0, 0, F.Info.sets(i)}def Rec.ends(+path: String, a: String, b: String, c: String) -> Bool:  String.ends_with(path, a) || String.ends_with(path, b) || String.ends_with(path, c)def Rec.named(+path: String, +i: F.Info) -> Rec:  Bool.pick(Rec, String.ends_with(path, ".ts"), Rec.ts(i),    Bool.pick(Rec, Rec.ends(path, ".aac", ".alaw", ".ulaw"), Snd{}, Rec.raw(i)))# The form a file's name asks for: TS for .ts, the audio alone for# .aac, .alaw and .ulaw, the raw video for the rest.def Rec.for(path: String, i: F.Info) -> Rec:  Rec.named(String.to_lower(path), i)# The access unit delimiter that opens each access unit in a TS.def Rec.aud(h265: Bool) -> List<&2, U32>:  match h265:    case True{}:      [0, 0, 0, 1, 70, 1, 80]    case False{}:      [0, 0, 0, 1, 9, 240]def Rec.tables(yes: Bool, +tcc: U32, h265: Bool, audio: Bool) -> List<&2, U32>:  match yes:    case True{}:      B.Bytes.cat(T.Ts.pat(tcc), T.Ts.pmt(tcc, Bool.pick(U32, h265, 36, 27),        Bool.pick(U32, audio, 15, 0)))    case False{}:      Nil{}# A frame into the file. In a TS: the tables again every 30 pictures,# and everything 0.7 s after the clock the video carries.def Rec.put(r: Rec, f: F.Frame) -> Wrote:  match r f:    case Raw{sets} F.Video{_, _, data}:      Wrote{B.Bytes.cat(sets, data), Raw{Nil{}}}    case Raw{sets} F.Audio{_, _}:      Wrote{Nil{}, Raw{sets}}    case Snd{} F.Video{_, _, _}:      Wrote{Nil{}, Snd{}}    case Snd{} F.Audio{_, data}:      Wrote{data, Snd{}}    case Ts{+h265, +audio, +vcc, acc, +tcc, +n, sets} F.Video{+pts, _, data}:      +pes = T.Pes.of(224, False{}, U32.add(pts, 63000),        B.Bytes.cat(sets, B.Bytes.cat(Rec.aud(h265), data)))      +tables = U32.is_zero(U32.mod(n, 30))      Wrote{B.Bytes.cat(Rec.tables(tables, tcc, h265, audio),          T.Ts.pes(256, vcc, T.Ts.pcr(pts), pes)),        Ts{h265, audio, U32.add(vcc, T.Ts.count(B.Bytes.len(pes), True{})), acc,          U32.add(tcc, Bool.pick(U32, tables, 1, 0)), U32.add(n, 1), Nil{}}}    case Ts{h265, +audio, vcc, +acc, tcc, n, sets} F.Audio{pts, data}:      +pes = T.Pes.of(192, True{}, U32.add(pts, 63000), data)      Wrote{Bool.pick(List<&2, U32>, audio, T.Ts.pes(257, acc, Nil{}, pes), Nil{}),        Ts{h265, audio, vcc, U32.add(acc, Bool.pick(U32, audio,          T.Ts.count(B.Bytes.len(pes), False{}), 0)), tcc, n, sets}}