Live video on the AT Protocol
1export const QUIET_PROFILE = "audible";
2
3export async function quietReceiver(
4 mediaStream: MediaStream,
5 playerEvent: (time: string, eventType: string, data: any) => void,
6) {
7 let audioTime = 0;
8 let videoTime = 0;
9 let baseline = 0;
10
11 const diff = (a: number, b: number) => {
12 if (audioTime === 0 || videoTime === 0) {
13 return;
14 }
15 if (baseline === 0) {
16 baseline = audioTime - videoTime;
17 console.log("baseline", baseline);
18 }
19 console.log("diff", audioTime - videoTime - baseline);
20 playerEvent(new Date().toISOString(), "av-sync", {
21 diff: audioTime - videoTime - baseline,
22 });
23 };
24
25 const gotVideo = (time: number) => {
26 console.log("video", time);
27 videoTime = time;
28 // diff(audioTime, videoTime);
29 };
30
31 const gotAudio = (time: number) => {
32 console.log("audio", time);
33 audioTime = time;
34 diff(audioTime, videoTime);
35 };
36
37 const Quiet = await import("quietjs-bundle");
38 Quiet.addReadyCallback(() => {
39 const nav = navigator as unknown as any;
40 // quiet doesn't let us pass in a mediaStream so we need to monkeypatch getusermedia
41 const getUserMedia = nav.getUserMedia;
42 nav.getUserMedia = async (constraints, cb) => {
43 cb(mediaStream);
44 console.log("quiet got user media");
45 // we're done, unmonkeypatch
46 nav.getUserMedia = getUserMedia;
47 };
48 const quiet = Quiet.receiver({
49 profile: QUIET_PROFILE,
50 onReceive: (payload) => {
51 try {
52 const str = Quiet.ab2str(payload);
53 const time = parseInt(str);
54 gotAudio(time);
55 } catch (e) {
56 console.error("quiet receiver error", e);
57 }
58 },
59 onCreate: () => {
60 console.log("receiver created");
61 },
62 onCreateFail: (error) => {
63 console.error("receiver failed to create", error);
64 },
65 onReceiveFail: (error) => {
66 console.error("receiver failed to receive", error);
67 },
68 // onReceiverStatsUpdate: (stats) => {
69 // console.log("receiver stats", stats);
70 // },
71 });
72 });
73
74 const zxing = await import("@zxing/browser");
75 const codeReader = new zxing.BrowserQRCodeReader();
76 codeReader.decodeFromStream(mediaStream, undefined, (result, err) => {
77 try {
78 if (result) {
79 const time = parseInt(result.getText());
80 gotVideo(time);
81 }
82 } catch (e) {
83 console.error("zxing error", e);
84 }
85 });
86}