@pipecat-ai/moq-transport 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2024, Daily
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # MoQ (Media over QUIC) Transport
2
+
3
+ [![Docs](https://img.shields.io/badge/documentation-blue)](https://docs.pipecat.ai/client/js/transports)
4
+ ![NPM Version](https://img.shields.io/npm/v/@pipecat-ai/moq-transport)
5
+ [![Demo](https://img.shields.io/badge/Demo-forestgreen)](https://github.com/pipecat-ai/voice-ui-kit/tree/main/examples/01-console)
6
+
7
+ Media-over-QUIC transport package for use with `@pipecat-ai/client-js`.
8
+
9
+ ## Installation
10
+
11
+ ```bash copy
12
+ npm install \
13
+ @pipecat-ai/client-js \
14
+ @pipecat-ai/moq-transport
15
+ ```
16
+
17
+ ## Overview
18
+
19
+ The `MoqTransport` class connects a `PipecatClient` to a Pipecat MoQ bot, either through a MoQ relay or directly to a bot running in serve mode. It publishes the local microphone as an Opus broadcast and consumes the bot's broadcast — both audio and a bot→client RTVI message track — using catalog discovery, so codec and sample rate are negotiated at connect time rather than pinned in code. RTVI messages also flow client→bot over a matching transcript track on the client's own broadcast, carrying `client-ready`, typed text input, function-call results, and other client→server RTVI traffic.
20
+
21
+ Connection management uses WebTransport with a WebSocket fallback (raced by `@moq/net`), with auto-reconnect via `Connection.Reload`.
22
+
23
+ ## Features
24
+
25
+ - 🎤 Microphone capture and Opus publish (`@moq/publish`)
26
+ - 📡 WebTransport with WebSocket fallback and auto-reconnect (`@moq/net`)
27
+ - 🎧 Catalog-driven bot audio playback with bounded-latency jitter buffering (`@moq/watch`)
28
+ - 💬 Bidirectional RTVI messages over dedicated transcript tracks (`@moq/json`) — bot→client events/transcripts, and client→bot messages like `client-ready`
29
+ - 🔐 `serverCertificateHashes` pinning for self-signed dev relays
30
+
31
+ ## Usage
32
+
33
+ ### Basic Setup
34
+
35
+ ```javascript
36
+ import { PipecatClient } from "@pipecat-ai/client-js";
37
+ import { MoqTransport } from "@pipecat-ai/moq-transport";
38
+
39
+ const transport = new MoqTransport({
40
+ relayUrl: "https://relay.example.com:4080/moq",
41
+ });
42
+
43
+ const pcClient = new PipecatClient({
44
+ transport,
45
+ callbacks: {
46
+ // Event handlers
47
+ },
48
+ });
49
+
50
+ await pcClient.connect();
51
+ ```
52
+
53
+ ### Self-signed dev relay
54
+
55
+ ```javascript
56
+ const transport = new MoqTransport({
57
+ relayUrl: "https://localhost:4080/moq",
58
+ serverCertificateHashes: [
59
+ { algorithm: "sha-256", value: certHashBytes },
60
+ ],
61
+ });
62
+ ```
63
+
64
+ ### Configuration Options
65
+
66
+ ```typescript
67
+ interface MoqTransportOptions {
68
+ relayUrl: string; // Required: full URL of the MoQ peer
69
+ serverCertificateHashes?: WebTransportHash[]; // Optional: pinned cert hashes for self-signed dev setups
70
+ clientId?: string; // Optional: this client's participant id (default "client0")
71
+ botId?: string; // Optional: peer (bot) participant id to consume (default "bot0")
72
+ namespace?: string; // Optional: top-level namespace / room name (default "pipecat")
73
+ transcriptTrack?: string; // Optional: track name for the bidirectional RTVI transcript channel (default "transcript.json.z")
74
+ audioLatencyMs?: number; // Optional: jitter buffer floor latency in ms (default 80)
75
+ audioBufferMaxMs?: number | "real-time"; // Optional: buffered-playback latency ceiling in ms, or "real-time" to collapse to the floor (default 30000)
76
+ audioSampleRate?: number; // Optional: mic publish sample rate in Hz; one of 8000/12000/16000/24000/48000 (default 48000)
77
+ }
78
+ ```
79
+
80
+ Broadcast paths are derived as `<namespace>/<clientId>` (publish) and `<namespace>/<botId>` (subscribe). Audio track names inside each broadcast are discovered from the bot's catalog, so they aren't configured directly.
81
+
82
+ ### Connecting via a bot `/start` endpoint
83
+
84
+ If your `/start` endpoint returns the bot's MoQ config nested under a `moq` key (the shape the bot's `pipecat.transports.moq.transport` returns), pass that response straight to `connect()` / `PipecatClient.startBotAndConnect()` — `MoqTransport` unwraps it (including base64-decoding `certHash` into `serverCertificateHashes`) in `_validateConnectionParams`, no app-side transform needed:
85
+
86
+ ```json
87
+ {
88
+ "moq": {
89
+ "relayUrl": "https://relay.example.com:4080/moq",
90
+ "certHash": "base64-encoded-sha-256-or-null",
91
+ "namespace": "pipecat",
92
+ "clientId": "client0",
93
+ "botId": "bot0",
94
+ "transcriptTrack": "transcript.json.z"
95
+ }
96
+ }
97
+ ```
98
+
99
+ ### Handling Events
100
+
101
+ The transport implements the various [Pipecat event handlers](https://docs.pipecat.ai/client/js/api-reference/callbacks). Check out the docs or samples for more info.
102
+
103
+ ## API Reference
104
+
105
+ ### States
106
+
107
+ The transport can be in one of these states:
108
+ - "disconnected"
109
+ - "initialized"
110
+ - "connecting"
111
+ - "connected"
112
+ - "ready"
113
+ - "disconnecting"
114
+ - "error"
115
+
116
+ ## Error Handling
117
+
118
+ The transport includes error handling for:
119
+ - Microphone acquisition failures (`initDevices`, `_connect`)
120
+ - Invalid `relayUrl`
121
+ - WebTransport / WebSocket connection failures (surfaced via `@moq/net` auto-reconnect)
122
+ - Catalog decode and audio decode errors (logged; the consume loop continues)
123
+
124
+ ## License
125
+ BSD-2 Clause
@@ -0,0 +1,156 @@
1
+ import { PipecatClientOptions, RTVIMessage, Transport, Tracks, TransportState } from "@pipecat-ai/client-js";
2
+ /**
3
+ * Constructor options for the MoQ transport.
4
+ *
5
+ * The browser dials a MOQ peer (relay or bot in serve mode) at
6
+ * ``relayUrl`` and publishes its mic under ``<namespace>/<clientId>``
7
+ * while consuming the bot under ``<namespace>/<botId>``. Audio tracks
8
+ * inside each broadcast are catalog-driven (codec, sample rate, channel
9
+ * count are discovered at connect time), so they aren't pinned here.
10
+ */
11
+ export interface MoqTransportOptions {
12
+ /**
13
+ * Full URL of the MOQ peer (e.g. ``https://relay.example.com:4080/moq``).
14
+ * WebTransport (HTTPS/HTTP3); @moq/net races WebSocket as a fallback
15
+ * if WebTransport isn't reachable.
16
+ */
17
+ relayUrl: string;
18
+ /**
19
+ * Pinned cert hashes for self-signed dev setups. Same shape as the
20
+ * native WebTransport ``serverCertificateHashes`` option.
21
+ */
22
+ serverCertificateHashes?: WebTransportHash[];
23
+ /**
24
+ * This client's participant id. Combined with ``namespace`` it forms
25
+ * the broadcast path the client publishes under: ``<namespace>/<clientId>``.
26
+ */
27
+ clientId?: string;
28
+ /**
29
+ * The peer (bot) participant id to consume. Subscriptions target
30
+ * ``<namespace>/<botId>``; track names come from the bot's catalog.
31
+ */
32
+ botId?: string;
33
+ /**
34
+ * Top-level namespace (analogous to a room name). Defaults to ``"pipecat"``.
35
+ */
36
+ namespace?: string;
37
+ /**
38
+ * Track name for RTVI server→client messages (transcripts, bot-ready,
39
+ * speech events, etc.). The client subscribes at
40
+ * ``<namespace>/<botId>/<transcriptTrack>``. Defaults to
41
+ * ``"transcript.json.z"``.
42
+ *
43
+ * Stays pinned because the transcript is a ``@moq/json`` stream track
44
+ * (an ordered, lossless append-log, wire-compatible with the bot's Rust
45
+ * ``moq_json::stream``), not a catalog rendition — the catalog only
46
+ * describes media tracks (audio/video). The ``.z`` suffix marks that the
47
+ * stream is compressed.
48
+ */
49
+ transcriptTrack?: string;
50
+ /**
51
+ * Latency floor (ms) — the jitter buffer the player keeps before
52
+ * playback. Lower = more interactive, more drops; higher = smoother,
53
+ * more glass-to-glass delay. Maps to the `min` bound of the
54
+ * ``@moq/watch`` ``Sync.latency`` range.
55
+ */
56
+ audioLatencyMs?: number;
57
+ /**
58
+ * Latency ceiling for buffered playback (the `max` bound of the
59
+ * ``@moq/watch`` ``Sync.latency`` range).
60
+ *
61
+ * The bot writes TTS audio faster than real-time with future-dated
62
+ * timestamps; the player buffers it and plays at the encoded pace
63
+ * instead of skipping ahead. This sets how much it's allowed to
64
+ * build up before re-anchoring:
65
+ *
66
+ * - a number (default: 30 s) — cap the buffer at that many ms. The
67
+ * player retains up to this much faster-than-real-time audio before
68
+ * dropping; an interruption (``user-started-speaking``) flushes early
69
+ * via ``reset()``. The bot paces a little under this so it never
70
+ * actually overruns the cap.
71
+ * - ``"real-time"`` — collapse to the floor (minimize latency, the old
72
+ * skip-ahead behavior; only useful with a live, real-time publisher).
73
+ */
74
+ audioBufferMaxMs?: number | "real-time";
75
+ /**
76
+ * Sample rate (Hz) the client publishes its mic audio at. Must be one
77
+ * of Opus's supported rates: 8000, 12000, 16000, 24000, or 48000.
78
+ * Defaults to ``48000`` to match the typical browser mic rate.
79
+ *
80
+ * The bot reads this from the client's catalog and resamples to its
81
+ * own ``audio_in_sample_rate`` after Opus decode, so exact agreement
82
+ * isn't required — pinning a known value just keeps the catalog
83
+ * unambiguous.
84
+ *
85
+ * Bot-side playback sample rate isn't an option here: ``MoqTransport``
86
+ * reads whatever the bot advertises in its catalog and configures its
87
+ * playback ``AudioContext`` accordingly.
88
+ */
89
+ audioSampleRate?: number;
90
+ }
91
+ /**
92
+ * ``MoqTransport`` — Pipecat Client SDK transport plugin for Media-over-QUIC.
93
+ *
94
+ * Built on the official ``moq`` library family:
95
+ *
96
+ * - ``@moq/net`` for connection management (``Connection.Reload``
97
+ * auto-reconnects on drops; races WebTransport + WebSocket).
98
+ * - ``@moq/publish`` for mic capture and Opus encoding via
99
+ * ``Publish.Source.Microphone`` + ``Publish.Broadcast``.
100
+ * - ``@moq/watch`` for bot audio playback: ``Watch.Broadcast`` discovers
101
+ * the catalog, ``Watch.Audio.Source`` picks a rendition, and
102
+ * ``Watch.Audio.Decoder`` + ``Watch.Audio.Emitter`` drive an
103
+ * ``AudioWorklet`` with a ring buffer (loss-tolerant, low-latency).
104
+ * - ``@moq/json`` for the transcript tracks — the ``Stream`` mode: an
105
+ * ordered, lossless append-log (wire-compatible with the bot's Rust
106
+ * ``moq_json::stream``), so no RTVI event is dropped. Not the snapshot
107
+ * mode, which collapses to the latest value.
108
+ * - ``@moq/signals`` for reactive plumbing.
109
+ *
110
+ * Two transcript tracks flow in opposite directions, both named
111
+ * ``transcript.json.z`` (or whatever the shared ``transcriptTrack`` option
112
+ * is set to) on their respective broadcasts:
113
+ *
114
+ * - Bot → client: bot publishes RTVI events (``bot-output``,
115
+ * ``bot-transcription``, ``bot-started-speaking``, ``metrics``, …);
116
+ * client subscribes and delivers to ``PipecatClient`` via ``_onMessage``.
117
+ * - Client → bot: ``sendMessage`` writes each RTVI message on the client's
118
+ * own transcript track; the bot's Python transport
119
+ * (``pipecat.transports.moq.transport``) subscribes and dispatches them
120
+ * through ``RTVIProcessor`` the same way any other transport does. This
121
+ * is what carries ``client-ready`` for protocol-version negotiation,
122
+ * typed text input, function-call results, and any other client→server
123
+ * RTVI traffic.
124
+ */
125
+ export class MoqTransport extends Transport {
126
+ static SERVICE_NAME: string;
127
+ protected _state: TransportState;
128
+ constructor(options: MoqTransportOptions);
129
+ initialize(options: PipecatClientOptions, messageHandler: (ev: RTVIMessage) => void): void;
130
+ initDevices(): Promise<void>;
131
+ _validateConnectionParams(connectParams?: unknown): MoqTransportOptions;
132
+ _connect(connectParams?: MoqTransportOptions): Promise<void>;
133
+ _disconnect(): Promise<void>;
134
+ sendReadyMessage(): void;
135
+ get state(): TransportState;
136
+ set state(next: TransportState);
137
+ getAllMics(): Promise<MediaDeviceInfo[]>;
138
+ getAllCams(): Promise<MediaDeviceInfo[]>;
139
+ getAllSpeakers(): Promise<MediaDeviceInfo[]>;
140
+ updateMic(micId: string): void;
141
+ updateCam(_camId: string): void;
142
+ updateSpeaker(_speakerId: string): void;
143
+ get selectedMic(): MediaDeviceInfo | Record<string, never>;
144
+ get selectedCam(): MediaDeviceInfo | Record<string, never>;
145
+ get selectedSpeaker(): MediaDeviceInfo | Record<string, never>;
146
+ enableMic(enable: boolean): void;
147
+ enableCam(_enable: boolean): void;
148
+ enableScreenShare(_enable: boolean): void;
149
+ get isCamEnabled(): boolean;
150
+ get isMicEnabled(): boolean;
151
+ get isSharingScreen(): boolean;
152
+ sendMessage(message: RTVIMessage): void;
153
+ tracks(): Tracks;
154
+ }
155
+
156
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"mappings":";AA4CA;;;;;;;;GAQG;AACH;IACE;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,uBAAuB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAE7C;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;;;;;;;;;;;;;OAgBG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAExC;;;;;;;;;;;;;OAaG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AA2DD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,yBAA0B,SAAQ,SAAS;IACzC,OAAc,YAAY,SAAmB;IAmD7C,UAAkB,MAAM,EAAE,cAAc,CAAC;gBAE7B,OAAO,EAAE,mBAAmB;IAUxC,UAAU,CACR,OAAO,EAAE,oBAAoB,EAC7B,cAAc,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,IAAI,GACxC,IAAI;IAOD,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAelC,yBAAyB,CAAC,aAAa,CAAC,EAAE,OAAO,GAAG,mBAAmB;IAsCjE,QAAQ,CAAC,aAAa,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAwP5D,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IA2ClC,gBAAgB,IAAI,IAAI;IAaxB,IAAI,KAAK,IAAI,cAAc,CAE1B;IAED,IAAI,KAAK,CAAC,IAAI,EAAE,cAAc,EAI7B;IAMK,UAAU,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAIxC,UAAU,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAIxC,cAAc,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAIlD,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI9B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAE/B,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAEvC,IAAI,WAAW,IAAI,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAOzD;IAED,IAAI,WAAW,IAAI,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAEzD;IAED,IAAI,eAAe,IAAI,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAE7D;IAED,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAIhC,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAEjC,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAEzC,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,IAAI,eAAe,IAAI,OAAO,CAE7B;IAMD,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAevC,MAAM,IAAI,MAAM;CA4DjB","sources":["transports/moq-transport/src/src/moqTransport.ts","transports/moq-transport/src/src/index.ts","transports/moq-transport/src/index.ts"],"sourcesContent":[null,null,"export * from \"./moqTransport\";\n"],"names":[],"version":3,"file":"index.d.ts.map"}