reactor-effect-browser 0.7.0 → 0.8.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/README.md +26 -21
- package/dist/BrowserMedia.d.ts +28 -0
- package/dist/BrowserMedia.d.ts.map +1 -0
- package/dist/BrowserMedia.js +56 -0
- package/dist/BrowserMedia.js.map +1 -0
- package/dist/BrowserPeer.d.ts +23 -0
- package/dist/BrowserPeer.d.ts.map +1 -0
- package/dist/BrowserPeer.js +298 -0
- package/dist/BrowserPeer.js.map +1 -0
- package/dist/index.d.ts +2 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -21
- package/dist/index.js.map +1 -1
- package/package.json +12 -11
- package/dist/_internal/audio-packet.d.ts +0 -13
- package/dist/_internal/audio-packet.d.ts.map +0 -1
- package/dist/_internal/audio-packet.js +0 -43
- package/dist/_internal/audio-packet.js.map +0 -1
- package/dist/_internal/audio.d.ts +0 -48
- package/dist/_internal/audio.d.ts.map +0 -1
- package/dist/_internal/audio.js +0 -242
- package/dist/_internal/audio.js.map +0 -1
- package/dist/_internal/media.d.ts +0 -78
- package/dist/_internal/media.d.ts.map +0 -1
- package/dist/_internal/media.js +0 -414
- package/dist/_internal/media.js.map +0 -1
- package/dist/_internal/pcm-worklet.d.ts +0 -2
- package/dist/_internal/pcm-worklet.d.ts.map +0 -1
- package/dist/_internal/pcm-worklet.js +0 -51
- package/dist/_internal/pcm-worklet.js.map +0 -1
- package/dist/_internal/peer.d.ts +0 -30
- package/dist/_internal/peer.d.ts.map +0 -1
- package/dist/_internal/peer.js +0 -294
- package/dist/_internal/peer.js.map +0 -1
- package/dist/_internal/recording.d.ts +0 -15
- package/dist/_internal/recording.d.ts.map +0 -1
- package/dist/_internal/recording.js +0 -9
- package/dist/_internal/recording.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# reactor-effect-browser
|
|
2
2
|
|
|
3
|
-
Browser WebRTC host for [`reactor-effect-client`](https://www.npmjs.com/package/reactor-effect-client).
|
|
3
|
+
Browser WebRTC host for [`reactor-effect-client`](https://www.npmjs.com/package/reactor-effect-client). `BrowserPeer` binds the session's `Peer` port to the built-in `RTCPeerConnection`, and `BrowserMedia` reads a connected session's tracks as the browser's own `MediaStreamTrack`s and plays them.
|
|
4
4
|
|
|
5
5
|
This is not an official Reactor SDK.
|
|
6
6
|
|
|
@@ -10,38 +10,43 @@ This is not an official Reactor SDK.
|
|
|
10
10
|
npm install reactor-effect-client reactor-effect-browser effect@4.0.0-rc.117
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
`reactor-effect-client` and Effect `4.0.0-rc.117` are peer dependencies
|
|
13
|
+
`reactor-effect-client` and Effect `4.0.0-rc.117` are peer dependencies, both exact: a later Effect rc needs a new SDK release. The package needs a browser with WebRTC. It has no Node dependency: its declarations compile with DOM types and without `@types/node`, and the workspace's installed-package check bundles it for the browser and runs that bundle without the Node `Buffer` global.
|
|
14
14
|
|
|
15
15
|
## Usage
|
|
16
16
|
|
|
17
|
-
`
|
|
17
|
+
`BrowserPeer.layer` supplies the `PeerFactory` for `Reactor.layer()`. Building it checks for `RTCPeerConnection` and `MediaStream`, and fails with `UnsupportedHost` without them, before any session is allocated. The HTTP client stays explicit. The page never holds the API key: its `Coordinator.Tokens` ask the application's server for a token that creates the session and then, before each expires, for one bound to it.
|
|
18
18
|
|
|
19
19
|
```ts
|
|
20
20
|
import { Effect, Layer } from "effect";
|
|
21
|
-
import * as
|
|
22
|
-
import * as
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
|
|
22
|
+
import * as Coordinator from "reactor-effect-client/Coordinator";
|
|
23
|
+
import * as H3 from "reactor-effect-client/H3";
|
|
24
|
+
import * as Reactor from "reactor-effect-client/Reactor";
|
|
25
|
+
import { BrowserMedia, BrowserPeer } from "reactor-effect-browser";
|
|
26
|
+
|
|
27
|
+
const reactorLayer = Reactor.layer().pipe(
|
|
28
|
+
Layer.provide(
|
|
29
|
+
Layer.mergeAll(Coordinator.layer({ apiUrl: "https://api.reactor.inc" }), BrowserPeer.layer),
|
|
30
|
+
),
|
|
31
|
+
Layer.provide(FetchHttpClient.layer),
|
|
26
32
|
);
|
|
27
33
|
|
|
28
|
-
|
|
34
|
+
// The tokens come from the application's server, which holds the API key.
|
|
35
|
+
const watch = (element: HTMLVideoElement, tokens: Coordinator.Tokens) =>
|
|
29
36
|
Effect.gen(function* () {
|
|
30
|
-
const
|
|
31
|
-
const session = yield*
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
);
|
|
37
|
+
const reactor = yield* Reactor.Reactor;
|
|
38
|
+
const session = yield* reactor.create({ model: H3.modelName, tokens });
|
|
39
|
+
const tracks = yield* BrowserMedia.tracks(session);
|
|
40
|
+
yield* BrowserMedia.play(yield* tracks.track("main_video"), element);
|
|
41
|
+
return yield* Effect.never;
|
|
42
|
+
}).pipe(Effect.scoped);
|
|
36
43
|
```
|
|
37
44
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
`Browser.media(session)` obtains the negotiated generation; `media.track(name)` acquires a scoped `MediaStreamTrack`, and `media.publish(name, track)` publishes through that generation. Media values stay bound to the generation that negotiated them. A reconnect creates a new generation; existing readers end or fail with their source, and applications obtain the new generation explicitly.
|
|
45
|
+
`BrowserMedia.tracks(session)` returns the session's current generation. `tracks.track(name)` acquires a clone of a received track that stops when its scope closes; `publish`, `unpublish`, `setTrackActive` and `setMaxBitrate` act on that generation, and a reconnect makes a new one that the application obtains again. The session reconnects a dropped connection on its own, so an application holding tracks follows `session.changes` to the next ready generation and obtains them again.
|
|
41
46
|
|
|
42
|
-
|
|
47
|
+
`BrowserMedia.play(track, element, { playTimeout })` plays a clone of `track` in `element` until the scope closes, which stops the clone and detaches it. It refuses a track that is not live and an element that already has a source, and fails when starting takes longer than `playTimeout`, 10 seconds by default. Starting playback does not establish that anyone saw it.
|
|
43
48
|
|
|
44
|
-
|
|
49
|
+
The peer opens the control channel before the data channel. It fails the connection with `Overflow` when a message exceeds the bound SCTP negotiated (at most 256 KiB), and refuses a `send`, before submitting it, while more than 1 MiB waits in the channel's buffer. Closing the peer closes both channels and the connection and stops every track it received or leased.
|
|
45
50
|
|
|
46
51
|
## Example
|
|
47
52
|
|
|
@@ -49,7 +54,7 @@ If the host lacks `RTCPeerConnection` or `MediaStream`, building `Browser.layer`
|
|
|
49
54
|
|
|
50
55
|
## Development
|
|
51
56
|
|
|
52
|
-
This package is built and tested from the workspace root; see the repository [CONTRIBUTING](https://github.com/mannyc2/reactor-effect-client/blob/main/CONTRIBUTING.md). Its tests
|
|
57
|
+
This package is built and tested from the workspace root; see the repository [CONTRIBUTING](https://github.com/mannyc2/reactor-effect-client/blob/main/CONTRIBUTING.md). Its tests run the peer and playback against DOM fakes on Node and Bun: they check ordering, bounds and ownership, not browser WebRTC or codec support. Real Chrome interoperability with the native host is exercised by the workspace's `integration` project.
|
|
53
58
|
|
|
54
59
|
## License
|
|
55
60
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A connected session's media as the browser's own tracks, and local playback
|
|
3
|
+
* of them. Starting playback does not establish that an audience saw anything.
|
|
4
|
+
*/
|
|
5
|
+
import * as Duration from "effect/Duration";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import type * as Scope from "effect/Scope";
|
|
8
|
+
import type { TrackMedia } from "reactor-effect-client/Media";
|
|
9
|
+
import { ReactorError } from "reactor-effect-client/ReactorError";
|
|
10
|
+
import type { Session } from "reactor-effect-client/Session";
|
|
11
|
+
/** A generation's tracks, typed as the DOM tracks the browser peer produces. */
|
|
12
|
+
export interface Tracks extends Omit<TrackMedia, "track" | "publish"> {
|
|
13
|
+
/** A clone of a received track, stopped when the scope closes. */
|
|
14
|
+
readonly track: (name: string) => Effect.Effect<MediaStreamTrack, ReactorError, Scope.Scope>;
|
|
15
|
+
readonly publish: (name: string, source: MediaStreamTrack) => Effect.Effect<void, ReactorError>;
|
|
16
|
+
}
|
|
17
|
+
/** The session's current tracks. A reconnect produces new ones. */
|
|
18
|
+
export declare const tracks: (session: Session) => Effect.Effect<Tracks, ReactorError>;
|
|
19
|
+
export interface PlayOptions {
|
|
20
|
+
/** How long starting playback may take; 10 seconds by default. */
|
|
21
|
+
readonly playTimeout?: Duration.Input | undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Plays a clone of `track` in `element` until the scope closes, which stops the
|
|
25
|
+
* clone and detaches it. Fails when starting takes longer than `playTimeout`.
|
|
26
|
+
*/
|
|
27
|
+
export declare const play: (track: MediaStreamTrack, element: Pick<HTMLMediaElement, "getAttribute" | "pause" | "play" | "srcObject">, options?: PlayOptions | undefined) => Effect.Effect<void, ReactorError, Scope.Scope>;
|
|
28
|
+
//# sourceMappingURL=BrowserMedia.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BrowserMedia.d.ts","sourceRoot":"","sources":["../src/BrowserMedia.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAE7D,gFAAgF;AAChF,MAAM,WAAW,MAAO,SAAQ,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,SAAS,CAAC;IACnE,kEAAkE;IAClE,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,YAAY,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAC7F,QAAQ,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;CACjG;AAED,mEAAmE;AACnE,eAAO,MAAM,MAAM,YAAa,OAAO,KAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAKvE,CAAC;AAEN,MAAM,WAAW,WAAW;IAC1B,kEAAkE;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,KAAK,GAAG,SAAS,CAAC;CACnD;AAED;;;GAGG;AACH,eAAO,MAAM,IAAI,kMA0Df,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A connected session's media as the browser's own tracks, and local playback
|
|
3
|
+
* of them. Starting playback does not establish that an audience saw anything.
|
|
4
|
+
*/
|
|
5
|
+
import * as Duration from "effect/Duration";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import { ReactorError } from "reactor-effect-client/ReactorError";
|
|
8
|
+
/** The session's current tracks. A reconnect produces new ones. */
|
|
9
|
+
export const tracks = (session) => Effect.map(session.tracks, (generation) => ({
|
|
10
|
+
...generation,
|
|
11
|
+
// The browser peer is the only producer of this session's tracks.
|
|
12
|
+
track: (name) => Effect.map(generation.track(name), (track) => track),
|
|
13
|
+
}));
|
|
14
|
+
/**
|
|
15
|
+
* Plays a clone of `track` in `element` until the scope closes, which stops the
|
|
16
|
+
* clone and detaches it. Fails when starting takes longer than `playTimeout`.
|
|
17
|
+
*/
|
|
18
|
+
export const play = Effect.fnUntraced(function* (track, element, options = {}) {
|
|
19
|
+
const timeout = yield* Effect.fromOption(Duration.fromInput(options.playTimeout ?? "10 seconds")).pipe(Effect.mapError(() => ReactorError.fromCode("InvalidInput", "playTimeout is not a duration", {
|
|
20
|
+
outcome: "not-submitted",
|
|
21
|
+
})));
|
|
22
|
+
if (track.readyState !== "live")
|
|
23
|
+
return yield* ReactorError.fromCode("InvalidState", "playback needs a live track");
|
|
24
|
+
if (element.srcObject !== null || (element.getAttribute("src") ?? "") !== "")
|
|
25
|
+
return yield* ReactorError.fromCode("InvalidState", "the element already has a source");
|
|
26
|
+
const owned = yield* Effect.acquireRelease(Effect.sync(() => track.clone()), (clone) => Effect.sync(() => clone.stop()));
|
|
27
|
+
yield* Effect.acquireRelease(Effect.try({
|
|
28
|
+
try: () => {
|
|
29
|
+
const stream = new MediaStream([owned]);
|
|
30
|
+
element.srcObject = stream;
|
|
31
|
+
return stream;
|
|
32
|
+
},
|
|
33
|
+
catch: (cause) => ReactorError.fromCode("UnsupportedCapability", "attaching the media element failed", {
|
|
34
|
+
operation: "play",
|
|
35
|
+
detail: cause,
|
|
36
|
+
}),
|
|
37
|
+
}), (stream) => Effect.sync(() => {
|
|
38
|
+
if (element.srcObject === stream) {
|
|
39
|
+
element.pause();
|
|
40
|
+
element.srcObject = null;
|
|
41
|
+
}
|
|
42
|
+
}));
|
|
43
|
+
yield* Effect.tryPromise({
|
|
44
|
+
try: () => element.play(),
|
|
45
|
+
catch: (cause) => ReactorError.fromCode("UnsupportedCapability", "HTMLMediaElement.play failed", {
|
|
46
|
+
operation: "play",
|
|
47
|
+
detail: cause,
|
|
48
|
+
}),
|
|
49
|
+
}).pipe(Effect.timeoutOrElse({
|
|
50
|
+
duration: timeout,
|
|
51
|
+
orElse: () => Effect.fail(ReactorError.fromCode("Timeout", "playback did not start in time")),
|
|
52
|
+
}));
|
|
53
|
+
if (owned.readyState !== "live")
|
|
54
|
+
return yield* ReactorError.fromCode("Disconnected", "the track ended while playback started");
|
|
55
|
+
});
|
|
56
|
+
//# sourceMappingURL=BrowserMedia.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BrowserMedia.js","sourceRoot":"","sources":["../src/BrowserMedia.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAUlE,mEAAmE;AACnE,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,OAAgB,EAAuC,EAAE,CAC9E,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC1C,GAAG,UAAU;IACb,kEAAkE;IAClE,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAyB,CAAC;CAC1F,CAAC,CAAC,CAAC;AAON;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAC7C,KAAuB,EACvB,OAAgF,EAChF,OAAO,GAAgB,EAAE;IAEzB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CACtC,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,WAAW,IAAI,YAAY,CAAC,CACxD,CAAC,IAAI,CACJ,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CACnB,YAAY,CAAC,QAAQ,CAAC,cAAc,EAAE,+BAA+B,EAAE;QACrE,OAAO,EAAE,eAAe;KACzB,CAAC,CACH,CACF,CAAC;IACF,IAAI,KAAK,CAAC,UAAU,KAAK,MAAM;QAC7B,OAAO,KAAK,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,cAAc,EAAE,6BAA6B,CAAC,CAAC;IACrF,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE;QAC1E,OAAO,KAAK,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,cAAc,EAAE,kCAAkC,CAAC,CAAC;IAC1F,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,cAAc,CACxC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,EAChC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAC3C,CAAC;IACF,KAAK,CAAC,CAAC,MAAM,CAAC,cAAc,CAC1B,MAAM,CAAC,GAAG,CAAC;QACT,GAAG,EAAE,GAAG,EAAE;YACR,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;YACxC,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC;YAC3B,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,YAAY,CAAC,QAAQ,CAAC,uBAAuB,EAAE,oCAAoC,EAAE;YACnF,SAAS,EAAE,MAAM;YACjB,MAAM,EAAE,KAAK;SACd,CAAC;KACL,CAAC,EACF,CAAC,MAAM,EAAE,EAAE,CACT,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;QACf,IAAI,OAAO,CAAC,SAAS,KAAK,MAAM,EAAE,CAAC;YACjC,OAAO,CAAC,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;QAC3B,CAAC;IACH,CAAC,CAAC,CACL,CAAC;IACF,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;QACvB,GAAG,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE;QACzB,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,YAAY,CAAC,QAAQ,CAAC,uBAAuB,EAAE,8BAA8B,EAAE;YAC7E,SAAS,EAAE,MAAM;YACjB,MAAM,EAAE,KAAK;SACd,CAAC;KACL,CAAC,CAAC,IAAI,CACL,MAAM,CAAC,aAAa,CAAC;QACnB,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,SAAS,EAAE,gCAAgC,CAAC,CAAC;KAC9F,CAAC,CACH,CAAC;IACF,IAAI,KAAK,CAAC,UAAU,KAAK,MAAM;QAC7B,OAAO,KAAK,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,cAAc,EAAE,wCAAwC,CAAC,CAAC;AAClG,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The browser's peer: the client's `Peer` port on `RTCPeerConnection`, with the
|
|
3
|
+
* browser's own media tracks. Session allocation, commands and correlation stay
|
|
4
|
+
* in the client; this module carries bytes and tracks.
|
|
5
|
+
*/
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Layer from "effect/Layer";
|
|
8
|
+
import type * as Scope from "effect/Scope";
|
|
9
|
+
import { PeerFactory } from "reactor-effect-client/Peer";
|
|
10
|
+
import type { Peer } from "reactor-effect-client/Peer";
|
|
11
|
+
import { ReactorError } from "reactor-effect-client/ReactorError";
|
|
12
|
+
/**
|
|
13
|
+
* A peer for one connection generation. Closing it, or its scope, closes the
|
|
14
|
+
* connection and stops every received track and lease.
|
|
15
|
+
*/
|
|
16
|
+
export declare const make: Effect.Effect<Peer, never, Scope.Scope>;
|
|
17
|
+
/**
|
|
18
|
+
* The browser's `PeerFactory`. Building it checks for WebRTC, so a browser
|
|
19
|
+
* without it fails there, before any session is allocated:
|
|
20
|
+
* `Reactor.layer().pipe(Layer.provide(BrowserPeer.layer))`.
|
|
21
|
+
*/
|
|
22
|
+
export declare const layer: Layer.Layer<PeerFactory, ReactorError>;
|
|
23
|
+
//# sourceMappingURL=BrowserPeer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BrowserPeer.d.ts","sourceRoot":"","sources":["../src/BrowserPeer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAC;AAE3C,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,KAAK,EAGV,IAAI,EAGL,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AA2ClE;;;GAGG;AACH,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CA+SvD,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,YAAY,CAYxD,CAAC"}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The browser's peer: the client's `Peer` port on `RTCPeerConnection`, with the
|
|
3
|
+
* browser's own media tracks. Session allocation, commands and correlation stay
|
|
4
|
+
* in the client; this module carries bytes and tracks.
|
|
5
|
+
*/
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Layer from "effect/Layer";
|
|
8
|
+
import { PeerFactory } from "reactor-effect-client/Peer";
|
|
9
|
+
import { ReactorError } from "reactor-effect-client/ReactorError";
|
|
10
|
+
/** SCTP's usual message bound, lowered by the answer when the remote negotiates less. */
|
|
11
|
+
const messageBound = 262_144;
|
|
12
|
+
/** How much may wait in a data channel's send buffer before `send` refuses. */
|
|
13
|
+
const bufferedBound = 1_048_576;
|
|
14
|
+
/** A browser API's expected exception as a failure of `code`; the exception stays in `detail`. */
|
|
15
|
+
const platformError = (code, operation) => (cause) => ReactorError.is(cause)
|
|
16
|
+
? cause
|
|
17
|
+
: ReactorError.fromCode(code, `${operation} failed`, { operation, detail: cause });
|
|
18
|
+
const refused = (code, message) => ReactorError.fromCode(code, message, { outcome: "not-submitted" });
|
|
19
|
+
const iceServers = (servers) => servers.map((server) => ({
|
|
20
|
+
urls: [...server.urls],
|
|
21
|
+
...(server.username === undefined ? {} : { username: server.username }),
|
|
22
|
+
...(server.credential === undefined ? {} : { credential: server.credential }),
|
|
23
|
+
}));
|
|
24
|
+
/**
|
|
25
|
+
* A peer for one connection generation. Closing it, or its scope, closes the
|
|
26
|
+
* connection and stops every received track and lease.
|
|
27
|
+
*/
|
|
28
|
+
export const make = Effect.gen(function* () {
|
|
29
|
+
let connection;
|
|
30
|
+
const leases = new Set();
|
|
31
|
+
const close = () => {
|
|
32
|
+
const closing = connection;
|
|
33
|
+
connection = undefined;
|
|
34
|
+
if (closing !== undefined) {
|
|
35
|
+
for (const channel of [closing.channels.control, closing.channels.data]) {
|
|
36
|
+
channel.onopen = null;
|
|
37
|
+
channel.onclose = null;
|
|
38
|
+
channel.onerror = null;
|
|
39
|
+
channel.onmessage = null;
|
|
40
|
+
channel.close();
|
|
41
|
+
}
|
|
42
|
+
closing.pc.onconnectionstatechange = null;
|
|
43
|
+
closing.pc.onicecandidate = null;
|
|
44
|
+
closing.pc.ontrack = null;
|
|
45
|
+
closing.pc.close();
|
|
46
|
+
for (const track of closing.received.values())
|
|
47
|
+
track.stop();
|
|
48
|
+
}
|
|
49
|
+
for (const track of leases)
|
|
50
|
+
track.stop();
|
|
51
|
+
leases.clear();
|
|
52
|
+
};
|
|
53
|
+
yield* Effect.addFinalizer(() => Effect.sync(close));
|
|
54
|
+
const current = Effect.suspend(() => connection === undefined
|
|
55
|
+
? Effect.fail(refused("InvalidState", "peer is closed"))
|
|
56
|
+
: Effect.succeed(connection));
|
|
57
|
+
/** The connection is still the one `opened`; a close or a newer prepare replaced it otherwise. */
|
|
58
|
+
const still = (opened, operation) => Effect.suspend(() => connection === opened
|
|
59
|
+
? Effect.void
|
|
60
|
+
: Effect.fail(ReactorError.fromCode("Disconnected", `peer closed during ${operation}`)));
|
|
61
|
+
const section = (name) => Effect.flatMap(current, (opened) => {
|
|
62
|
+
const found = opened.sections.get(name);
|
|
63
|
+
return found === undefined
|
|
64
|
+
? Effect.fail(refused("InvalidState", `unknown track: ${name}`))
|
|
65
|
+
: Effect.succeed(found);
|
|
66
|
+
});
|
|
67
|
+
const open = (servers, tracks, emit) => {
|
|
68
|
+
close();
|
|
69
|
+
const pc = new RTCPeerConnection({ iceServers: iceServers(servers) });
|
|
70
|
+
// Reliable and ordered by default; Reactor expects control before data.
|
|
71
|
+
const channels = {
|
|
72
|
+
control: pc.createDataChannel("control"),
|
|
73
|
+
data: pc.createDataChannel("data"),
|
|
74
|
+
};
|
|
75
|
+
const sections = new Map();
|
|
76
|
+
for (const declared of tracks)
|
|
77
|
+
sections.set(declared.name, {
|
|
78
|
+
declared,
|
|
79
|
+
transceiver: pc.addTransceiver(declared.kind, { direction: declared.direction }),
|
|
80
|
+
});
|
|
81
|
+
const opened = {
|
|
82
|
+
pc,
|
|
83
|
+
channels,
|
|
84
|
+
sections,
|
|
85
|
+
received: new Map(),
|
|
86
|
+
messageLimit: messageBound,
|
|
87
|
+
};
|
|
88
|
+
const live = () => connection === opened;
|
|
89
|
+
for (const kind of ["control", "data"]) {
|
|
90
|
+
const channel = channels[kind];
|
|
91
|
+
channel.binaryType = "arraybuffer";
|
|
92
|
+
channel.onopen = () => {
|
|
93
|
+
if (live())
|
|
94
|
+
emit({ type: "channel", channel: kind, open: true });
|
|
95
|
+
};
|
|
96
|
+
channel.onclose = () => {
|
|
97
|
+
if (live())
|
|
98
|
+
emit({ type: "channel", channel: kind, open: false });
|
|
99
|
+
};
|
|
100
|
+
channel.onerror = () => {
|
|
101
|
+
if (live())
|
|
102
|
+
emit({
|
|
103
|
+
type: "error",
|
|
104
|
+
error: ReactorError.fromCode("Disconnected", `${kind} channel error`),
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
channel.onmessage = (event) => {
|
|
108
|
+
if (!live())
|
|
109
|
+
return;
|
|
110
|
+
if (!(event.data instanceof ArrayBuffer))
|
|
111
|
+
emit({
|
|
112
|
+
type: "error",
|
|
113
|
+
error: ReactorError.fromCode("Protocol", "nonbinary channel message"),
|
|
114
|
+
});
|
|
115
|
+
else if (event.data.byteLength > opened.messageLimit)
|
|
116
|
+
emit({
|
|
117
|
+
type: "error",
|
|
118
|
+
error: ReactorError.fromCode("Overflow", "received message exceeds its bound"),
|
|
119
|
+
});
|
|
120
|
+
else
|
|
121
|
+
emit({ type: "message", channel: kind, bytes: new Uint8Array(event.data) });
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
pc.onconnectionstatechange = () => {
|
|
125
|
+
if (live())
|
|
126
|
+
emit({ type: "state", state: pc.connectionState });
|
|
127
|
+
};
|
|
128
|
+
pc.onicecandidate = (event) => {
|
|
129
|
+
if (!live())
|
|
130
|
+
return;
|
|
131
|
+
const candidate = event.candidate;
|
|
132
|
+
if (candidate === null) {
|
|
133
|
+
emit({ type: "ice" });
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
emit({
|
|
137
|
+
type: "ice",
|
|
138
|
+
candidate: {
|
|
139
|
+
candidate: candidate.candidate,
|
|
140
|
+
...(candidate.sdpMid === null ? {} : { sdp_mid: candidate.sdpMid }),
|
|
141
|
+
...(candidate.sdpMLineIndex === null ? {} : { sdp_mline_index: candidate.sdpMLineIndex }),
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
pc.ontrack = (event) => {
|
|
146
|
+
if (!live()) {
|
|
147
|
+
event.track.stop();
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const name = [...sections].find(([, entry]) => entry.transceiver === event.transceiver)?.[0];
|
|
151
|
+
const mid = event.transceiver.mid;
|
|
152
|
+
if (name === undefined || mid === null) {
|
|
153
|
+
event.track.stop();
|
|
154
|
+
emit({
|
|
155
|
+
type: "error",
|
|
156
|
+
error: ReactorError.fromCode("Protocol", "received track has no declared mapping"),
|
|
157
|
+
});
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
opened.received.get(name)?.stop();
|
|
161
|
+
opened.received.set(name, event.track);
|
|
162
|
+
emit({ type: "track", name, mid });
|
|
163
|
+
};
|
|
164
|
+
connection = opened;
|
|
165
|
+
return opened;
|
|
166
|
+
};
|
|
167
|
+
const prepare = (servers, tracks, emit) => Effect.gen(function* () {
|
|
168
|
+
const opened = yield* Effect.try({
|
|
169
|
+
try: () => open(servers, tracks, emit),
|
|
170
|
+
catch: platformError("Disconnected", "create peer connection"),
|
|
171
|
+
});
|
|
172
|
+
const offer = yield* Effect.tryPromise({
|
|
173
|
+
try: () => opened.pc.createOffer(),
|
|
174
|
+
catch: platformError("Disconnected", "create offer"),
|
|
175
|
+
}).pipe(Effect.tap(() => still(opened, "prepare")));
|
|
176
|
+
const sdp = offer.sdp;
|
|
177
|
+
if (sdp === undefined || sdp.length === 0)
|
|
178
|
+
return yield* ReactorError.fromCode("Protocol", "createOffer returned no SDP");
|
|
179
|
+
yield* Effect.tryPromise({
|
|
180
|
+
try: () => opened.pc.setLocalDescription(offer),
|
|
181
|
+
catch: platformError("Disconnected", "set local description"),
|
|
182
|
+
});
|
|
183
|
+
yield* still(opened, "prepare");
|
|
184
|
+
const mapping = [];
|
|
185
|
+
for (const track of tracks) {
|
|
186
|
+
const mid = opened.sections.get(track.name)?.transceiver.mid;
|
|
187
|
+
if (mid === null || mid === undefined)
|
|
188
|
+
return yield* ReactorError.fromCode("Protocol", `missing mid after setLocalDescription: ${track.name}`);
|
|
189
|
+
mapping.push({ ...track, mid });
|
|
190
|
+
}
|
|
191
|
+
return { sdp, mapping };
|
|
192
|
+
}).pipe(
|
|
193
|
+
// An interrupted preparation leaves no half-made connection behind.
|
|
194
|
+
Effect.onInterrupt(() => Effect.sync(close)));
|
|
195
|
+
const answer = (sdp) => Effect.gen(function* () {
|
|
196
|
+
const opened = yield* current;
|
|
197
|
+
yield* Effect.tryPromise({
|
|
198
|
+
try: () => opened.pc.setRemoteDescription({ type: "answer", sdp }),
|
|
199
|
+
catch: platformError("Disconnected", "apply SDP answer"),
|
|
200
|
+
});
|
|
201
|
+
yield* still(opened, "apply SDP answer");
|
|
202
|
+
const negotiated = opened.pc.sctp?.maxMessageSize;
|
|
203
|
+
opened.messageLimit =
|
|
204
|
+
negotiated !== undefined && Number.isFinite(negotiated) && negotiated >= 1
|
|
205
|
+
? Math.min(Math.floor(negotiated), messageBound)
|
|
206
|
+
: messageBound;
|
|
207
|
+
});
|
|
208
|
+
const send = (kind, bytes) => Effect.gen(function* () {
|
|
209
|
+
const opened = yield* current;
|
|
210
|
+
const channel = opened.channels[kind];
|
|
211
|
+
if (channel.readyState !== "open")
|
|
212
|
+
return yield* refused("Disconnected", `${kind} channel is not open`);
|
|
213
|
+
if (bytes.byteLength > opened.messageLimit ||
|
|
214
|
+
channel.bufferedAmount + bytes.byteLength > bufferedBound)
|
|
215
|
+
return yield* refused("Overflow", `${kind} message or send buffer bound exceeded`);
|
|
216
|
+
yield* Effect.try({
|
|
217
|
+
try: () => channel.send(bytes),
|
|
218
|
+
catch: platformError("Disconnected", `send ${kind}`),
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
const lease = (name) => Effect.acquireRelease(Effect.flatMap(current, (opened) => {
|
|
222
|
+
const track = opened.received.get(name);
|
|
223
|
+
if (track?.readyState !== "live")
|
|
224
|
+
return Effect.fail(refused("InvalidState", `no live received track: ${name}`));
|
|
225
|
+
const clone = track.clone();
|
|
226
|
+
leases.add(clone);
|
|
227
|
+
return Effect.succeed(clone);
|
|
228
|
+
}), (clone) => Effect.sync(() => {
|
|
229
|
+
leases.delete(clone);
|
|
230
|
+
clone.stop();
|
|
231
|
+
}));
|
|
232
|
+
const replace = (name, track) => Effect.gen(function* () {
|
|
233
|
+
const opened = yield* current;
|
|
234
|
+
const entry = yield* section(name);
|
|
235
|
+
if (entry.declared.direction !== "sendonly")
|
|
236
|
+
return yield* refused("InvalidState", `${name} is not an outgoing track`);
|
|
237
|
+
if (track !== null && (track.kind !== entry.declared.kind || track.readyState !== "live"))
|
|
238
|
+
return yield* refused("InvalidState", "outgoing track kind or liveness mismatch");
|
|
239
|
+
yield* Effect.tryPromise({
|
|
240
|
+
// The session hands this host clones of the application's MediaStreamTracks.
|
|
241
|
+
try: () => entry.transceiver.sender.replaceTrack(track),
|
|
242
|
+
catch: platformError("InvalidState", "replace track"),
|
|
243
|
+
});
|
|
244
|
+
yield* still(opened, "replace track");
|
|
245
|
+
});
|
|
246
|
+
const direction = (name, active) => Effect.map(section(name), (entry) => {
|
|
247
|
+
entry.transceiver.direction = active ? entry.declared.direction : "inactive";
|
|
248
|
+
});
|
|
249
|
+
const maxBitrate = (name, bitsPerSecond) => Effect.gen(function* () {
|
|
250
|
+
const entry = yield* section(name);
|
|
251
|
+
if (entry.declared.direction !== "sendonly" ||
|
|
252
|
+
!Number.isSafeInteger(bitsPerSecond) ||
|
|
253
|
+
bitsPerSecond < 1)
|
|
254
|
+
return yield* refused("InvalidInput", "maxBitrate needs an outgoing track and a positive integral bit rate");
|
|
255
|
+
const sender = entry.transceiver.sender;
|
|
256
|
+
const parameters = sender.getParameters();
|
|
257
|
+
if (parameters.encodings.length === 0)
|
|
258
|
+
return yield* refused("UnsupportedCapability", "sender has no encodings yet");
|
|
259
|
+
for (const encoding of parameters.encodings)
|
|
260
|
+
encoding.maxBitrate = bitsPerSecond;
|
|
261
|
+
yield* Effect.tryPromise({
|
|
262
|
+
try: () => sender.setParameters(parameters),
|
|
263
|
+
catch: platformError("UnsupportedCapability", "sender bitrate"),
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
const stats = Effect.gen(function* () {
|
|
267
|
+
const opened = yield* current;
|
|
268
|
+
const report = yield* Effect.tryPromise({
|
|
269
|
+
try: () => opened.pc.getStats(),
|
|
270
|
+
catch: platformError("UnsupportedCapability", "WebRTC statistics"),
|
|
271
|
+
});
|
|
272
|
+
yield* still(opened, "statistics");
|
|
273
|
+
const entries = [];
|
|
274
|
+
report.forEach((entry) => {
|
|
275
|
+
entries.push(entry);
|
|
276
|
+
});
|
|
277
|
+
return entries;
|
|
278
|
+
});
|
|
279
|
+
return {
|
|
280
|
+
media: { _tag: "Tracks", lease, replace },
|
|
281
|
+
prepare,
|
|
282
|
+
answer,
|
|
283
|
+
send,
|
|
284
|
+
direction,
|
|
285
|
+
maxBitrate,
|
|
286
|
+
stats,
|
|
287
|
+
close: Effect.sync(close),
|
|
288
|
+
};
|
|
289
|
+
});
|
|
290
|
+
/**
|
|
291
|
+
* The browser's `PeerFactory`. Building it checks for WebRTC, so a browser
|
|
292
|
+
* without it fails there, before any session is allocated:
|
|
293
|
+
* `Reactor.layer().pipe(Layer.provide(BrowserPeer.layer))`.
|
|
294
|
+
*/
|
|
295
|
+
export const layer = Layer.effect(PeerFactory, Effect.suspend(() => typeof RTCPeerConnection === "function" && typeof MediaStream === "function"
|
|
296
|
+
? Effect.succeed(PeerFactory.of({ check: Effect.void, make }))
|
|
297
|
+
: Effect.fail(refused("UnsupportedHost", "the browser peer needs RTCPeerConnection and MediaStream; on a server, use the native peer"))));
|
|
298
|
+
//# sourceMappingURL=BrowserPeer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BrowserPeer.js","sourceRoot":"","sources":["../src/BrowserPeer.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,23 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import type * as Scope from "effect/Scope";
|
|
4
|
-
import { PeerFactory } from "reactor-effect-client";
|
|
5
|
-
import type { ReactorError, Session } from "reactor-effect-client";
|
|
6
|
-
import type { TrackGeneration } from "reactor-effect-client/host";
|
|
7
|
-
/**
|
|
8
|
-
* Browser transport capability for the portable Client. Building the layer
|
|
9
|
-
* detects WebRTC support, so a host without it fails there, before any Client
|
|
10
|
-
* exists to allocate a remote session. Provide it to the client layer:
|
|
11
|
-
* `Reactor.layer(configuration).pipe(Layer.provide(Browser.layer))`.
|
|
12
|
-
*/
|
|
13
|
-
export declare const layer: Layer.Layer<PeerFactory, ReactorError>;
|
|
14
|
-
export interface MediaGeneration extends Omit<TrackGeneration, "track" | "publish"> {
|
|
15
|
-
readonly track: (name: string) => Effect.Effect<MediaStreamTrack, ReactorError, Scope.Scope>;
|
|
16
|
-
readonly publish: (name: string, source: MediaStreamTrack) => Effect.Effect<void, ReactorError>;
|
|
17
|
-
}
|
|
18
|
-
/** Track leases and publication retain the generation that negotiated them. */
|
|
19
|
-
export declare const media: (session: Session) => Effect.Effect<MediaGeneration, ReactorError>;
|
|
20
|
-
export * as Recording from "./_internal/recording.js";
|
|
21
|
-
export { videoFrames, audioSamples, mediaFacilities, audioContext, webAudioSamples, play, nextPresentation, } from "./_internal/media.js";
|
|
22
|
-
export type { VideoSample, AudioSample, AudioContextOptions, MediaOptions, PlayOptions, Presentation, WebAudioOptions, WebAudioSample, } from "./_internal/media.js";
|
|
1
|
+
export * as BrowserMedia from "./BrowserMedia.js";
|
|
2
|
+
export * as BrowserPeer from "./BrowserPeer.js";
|
|
23
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,22 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { PeerFactory } from "reactor-effect-client";
|
|
4
|
-
import { parsed, trackGeneration } from "reactor-effect-client/host";
|
|
5
|
-
import { BrowserPeer, requireBrowserPeer } from "./_internal/peer.js";
|
|
6
|
-
/**
|
|
7
|
-
* Browser transport capability for the portable Client. Building the layer
|
|
8
|
-
* detects WebRTC support, so a host without it fails there, before any Client
|
|
9
|
-
* exists to allocate a remote session. Provide it to the client layer:
|
|
10
|
-
* `Reactor.layer(configuration).pipe(Layer.provide(Browser.layer))`.
|
|
11
|
-
*/
|
|
12
|
-
export const layer = Layer.effect(PeerFactory, parsed(requireBrowserPeer).pipe(Effect.as(PeerFactory.of({ make: () => new BrowserPeer() }))));
|
|
13
|
-
/** Track leases and publication retain the generation that negotiated them. */
|
|
14
|
-
export const media = (session) => trackGeneration(session).pipe(Effect.map((generation) => ({
|
|
15
|
-
...generation,
|
|
16
|
-
// The browser peer advertises this capability and is the only producer of
|
|
17
|
-
// these DOM tracks. The portable session keeps their lifetime contract.
|
|
18
|
-
track: (name) => generation.track(name).pipe(Effect.map((track) => track)),
|
|
19
|
-
})));
|
|
20
|
-
export * as Recording from "./_internal/recording.js";
|
|
21
|
-
export { videoFrames, audioSamples, mediaFacilities, audioContext, webAudioSamples, play, nextPresentation, } from "./_internal/media.js";
|
|
1
|
+
export * as BrowserMedia from "./BrowserMedia.js";
|
|
2
|
+
export * as BrowserPeer from "./BrowserPeer.js";
|
|
22
3
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC"}
|