@pickleball/expo-sdk 0.2.0 → 1.0.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/CHANGELOG.md +41 -0
- package/README.md +155 -101
- package/dist/advanced-DcbHBCGb.d.ts +300 -0
- package/dist/advanced-DeQwg7MK.d.cts +300 -0
- package/dist/advanced.cjs +14 -2
- package/dist/advanced.d.cts +2 -2
- package/dist/advanced.d.ts +2 -2
- package/dist/advanced.js +17 -5
- package/dist/camera-controls-By8tHigY.d.cts +97 -0
- package/dist/camera-controls-rCBUGGPC.d.ts +97 -0
- package/dist/chunk-62YV3PCD.cjs +139 -0
- package/dist/chunk-6F3HN6US.js +50 -0
- package/dist/chunk-6YE5BL3A.cjs +50 -0
- package/dist/chunk-7Z7TN5N3.js +318 -0
- package/dist/{chunk-TRLASNPQ.cjs → chunk-GXQYMPHE.cjs} +1 -1
- package/dist/{chunk-TBPR2QND.cjs → chunk-IUIRVKJI.cjs} +736 -399
- package/dist/chunk-TBUEXRFQ.cjs +318 -0
- package/dist/{chunk-FODE2W2H.js → chunk-XGP2MN5R.js} +688 -351
- package/dist/{chunk-6JOMESNT.js → chunk-XL4CISVQ.js} +1 -1
- package/dist/chunk-ZSKSXOCZ.js +139 -0
- package/dist/convex-command-subscription-Cabh9tND.d.ts +41 -0
- package/dist/convex-command-subscription-DUgpu75l.d.cts +41 -0
- package/dist/device-agent/index.cjs +973 -44
- package/dist/device-agent/index.d.cts +339 -3
- package/dist/device-agent/index.d.ts +339 -3
- package/dist/device-agent/index.js +940 -11
- package/dist/{engine-CCSSqPbZ.d.cts → engine-BSvSdCSE.d.cts} +227 -2
- package/dist/{engine-CCSSqPbZ.d.ts → engine-BSvSdCSE.d.ts} +227 -2
- package/dist/index.cjs +419 -937
- package/dist/index.d.cts +315 -32
- package/dist/index.d.ts +315 -32
- package/dist/index.js +423 -941
- package/dist/plugin.cjs +11 -13
- package/dist/plugin.d.cts +15 -1
- package/dist/plugin.d.ts +15 -1
- package/dist/plugin.js +7 -9
- package/dist/realtime/index.cjs +31 -0
- package/dist/realtime/index.d.cts +15 -0
- package/dist/realtime/index.d.ts +15 -0
- package/dist/realtime/index.js +31 -0
- package/dist/ui/index.cjs +1297 -0
- package/dist/ui/index.d.cts +493 -0
- package/dist/ui/index.d.ts +493 -0
- package/dist/ui/index.js +1297 -0
- package/dist/version.cjs +2 -2
- package/dist/version.js +1 -1
- package/package.json +31 -15
- package/src/advanced.ts +11 -9
- package/src/auto-resume.tsx +259 -0
- package/src/camera-controls.ts +99 -0
- package/src/camera-grant-provider.ts +392 -0
- package/src/camera-settings.ts +133 -0
- package/src/contracts.ts +73 -0
- package/src/device-agent/agent.ts +262 -9
- package/src/device-agent/beat-policy.ts +46 -0
- package/src/device-agent/component.tsx +11 -0
- package/src/device-agent/convex-beat-transport.ts +140 -0
- package/src/device-agent/convex-command-subscription.ts +101 -0
- package/src/device-agent/convex-control-channel.ts +7 -1
- package/src/device-agent/http-transport.ts +22 -1
- package/src/device-agent/index.ts +22 -0
- package/src/device-agent/mqtt-channel.ts +379 -0
- package/src/device-agent/mqtt-codec.ts +347 -0
- package/src/device-agent/tournament.ts +352 -0
- package/src/engine.ts +580 -15
- package/src/http-session-provider.ts +21 -189
- package/src/index.ts +84 -5
- package/src/local-video-quality.ts +43 -0
- package/src/native-runtime.ts +234 -78
- package/src/plugin.ts +19 -6
- package/src/provider.tsx +94 -18
- package/src/realtime/index.ts +56 -0
- package/src/resume-policy.ts +85 -0
- package/src/resume.ts +132 -0
- package/src/rtmp-room-adapter.ts +164 -12
- package/src/session-grant.ts +256 -0
- package/src/ui/camera-screen.tsx +484 -0
- package/src/ui/controls-sheet.tsx +445 -0
- package/src/ui/copy.ts +326 -0
- package/src/ui/helpers.ts +105 -0
- package/src/ui/hooks.ts +59 -0
- package/src/ui/index.ts +42 -0
- package/src/ui/preview-view.tsx +39 -0
- package/src/ui/primitives.tsx +304 -0
- package/src/uplink-check.ts +173 -0
- package/dist/advanced-BXDrDl1h.d.ts +0 -316
- package/dist/advanced-BdAa2Erv.d.cts +0 -316
- package/src/livekit-room-adapter.ts +0 -390
- package/src/livestream-screen.tsx +0 -1002
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# @pickleball/expo-sdk
|
|
2
2
|
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 04e5c5d: **1.0.0 — RTMP-only + camera grant (ADR-0002).** Điện thoại của user đối tác lên
|
|
8
|
+
sóng qua đúng pipeline device-rtmp của thiết bị Picklive; LiveKit gỡ hẳn khỏi SDK.
|
|
9
|
+
|
|
10
|
+
BREAKING
|
|
11
|
+
- Gỡ LiveKit: không còn `LiveKitRoomAdapter`, `createNativeLiveKitBindings`,
|
|
12
|
+
`LivestreamScreen` (thay bằng `PickleballCameraScreen` ở `./ui`), peer
|
|
13
|
+
`@livekit/react-native`, `@livekit/react-native-webrtc`, `livekit-client` và
|
|
14
|
+
dependency `@livekit/react-native-expo-plugin`. `createDefaultRuntime` /
|
|
15
|
+
`createDefaultRoomFactory` giờ là RTMP.
|
|
16
|
+
- `@pickleball/rtmp-native` `>=0.2.0` là peer BẮT BUỘC (không còn optional).
|
|
17
|
+
- Grant `serverUrl` phải là `rtmp(s)://` — `createHttpSessionProvider` /
|
|
18
|
+
`isSessionGrant` từ chối `wss://` (INVALID_RESPONSE).
|
|
19
|
+
- Config plugin không compose plugin LiveKit nữa; prop `modules.livekit` và
|
|
20
|
+
`liveKit` vẫn được nhận nhưng bị bỏ qua. Plugin dùng `PLUGIN_SCHEMA_VERSION`
|
|
21
|
+
("1") thay cho version package trong `createRunOncePlugin` — bump SDK không
|
|
22
|
+
còn đổi fingerprint OTA của app.
|
|
23
|
+
- `withLocalVideoQuality` generic: trả đúng kiểu provider truyền vào (giữ
|
|
24
|
+
`resume`/`getActiveGrant`).
|
|
25
|
+
|
|
26
|
+
MỚI (từ 0.3 additive, gộp vào 1.0)
|
|
27
|
+
- `createCameraGrantProvider` / `useCameraGrant`: grant từ backend đối tác →
|
|
28
|
+
Bearer thẳng `/api/v2/camera/sessions/{id}/*`; tự publish khi start không
|
|
29
|
+
standby; nhận lại phiên sau khi app bị kill (`hasResumableSession`,
|
|
30
|
+
`clearStoredSession`); Bearer hết hạn không bao giờ khoá máy.
|
|
31
|
+
- Resume hạng nhất: `withResume`, `createHttpResumeFetcher`,
|
|
32
|
+
`PickleballAutoResume`, `PickleballAutoPrepare`, `useStandbySession`
|
|
33
|
+
(+ `resumeSessionId`), `resume-policy`.
|
|
34
|
+
- `./ui`: `PickleballCameraScreen` (casual/court, copy vi/en, lỗi bản địa hoá),
|
|
35
|
+
`CameraControlsSheet` (độ phân giải, fps theo capability, chống chớp, camera
|
|
36
|
+
mặc định, zoom/EV/khoá/đèn pin, đo uplink) + primitives/helpers/hooks.
|
|
37
|
+
- `./realtime`: `createGrantCommandSubscription`, `createConvexCommandSubscription`.
|
|
38
|
+
- `createRtmpRuntime`, `createRtmpRoomFactory` ở root; camera-settings store;
|
|
39
|
+
camera-controls (`typeof`-guard); `runUplinkCheck`/`VIDEO_BITRATE_BPS`.
|
|
40
|
+
- Engine: poll lệnh 409 → dừng tại chỗ; không xoay token mỗi giây ở phút cuối;
|
|
41
|
+
hành động của `useLivestream` ổn định qua re-render.
|
|
42
|
+
- `SdkError.reason` (409: superseded|ended|expired) và `retryAfterMs` (429).
|
|
43
|
+
|
|
3
44
|
## 0.2.0
|
|
4
45
|
|
|
5
46
|
### Minor Changes (BREAKING — modular subpath split)
|
package/README.md
CHANGED
|
@@ -1,144 +1,198 @@
|
|
|
1
1
|
# `@pickleball/expo-sdk`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Livestream a pickleball match from the phone camera, straight into Pickleball
|
|
4
|
+
Live's RTMP pipeline (hardware H.264 → RTMP → HLS/VOD on R2), with a headless
|
|
5
|
+
state machine, React hooks, a ready-made camera screen, and an Expo config
|
|
6
|
+
plugin. **1.0 is RTMP-only**: LiveKit/WebRTC is gone, the only native
|
|
7
|
+
dependency is `@pickleball/rtmp-native`.
|
|
4
8
|
|
|
5
|
-
##
|
|
9
|
+
## How a partner app goes live (camera grant)
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
```
|
|
12
|
+
Partner app (this SDK) Partner backend (holds the API key) Pickleball Live
|
|
13
|
+
────────────────────── ─────────────────────────────────── ───────────────
|
|
14
|
+
user taps "Go live" ──────────► POST /api/v1/live-sessions
|
|
15
|
+
{cameraGrant:true, matchRef, title,
|
|
16
|
+
quality?, courtRef?} ── x-api-key ────► creates the session
|
|
17
|
+
◄──── CameraGrant (verbatim) ─── ◄──── {session, cameraGrant} ─────────────
|
|
18
|
+
SDK: Bearer grant.telemetry.token ──────────────────────────────────────► /api/v2/camera/sessions/{id}/
|
|
19
|
+
publish|unpublish|refresh|end|
|
|
20
|
+
telemetry|commands
|
|
21
|
+
SDK: RTMP (grant.serverUrl + stream key) ───────────────────────────────► MediaMTX → HLS → R2 → VOD
|
|
22
|
+
◄──── webhooks session.live / session.ended / recording.ready (matchRef)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The app never holds an API key and never configures a Pickleball URL: every
|
|
26
|
+
endpoint is derived from the grant. The partner backend has exactly one job —
|
|
27
|
+
issue grants (`@pickleball/server-sdk` `liveSessions.createCameraSession`).
|
|
28
|
+
|
|
29
|
+
## Modules (Metro does not tree-shake — import only what you use)
|
|
10
30
|
|
|
11
31
|
| Import | Contents | Extra runtime deps |
|
|
12
32
|
|---|---|---|
|
|
13
|
-
| `@pickleball/expo-sdk` | `PickleballLiveProvider`, `useLivestream`, `
|
|
14
|
-
| `@pickleball/expo-sdk/
|
|
33
|
+
| `@pickleball/expo-sdk` | `PickleballLiveProvider`, `useLivestream`, `createCameraGrantProvider`, resume (`PickleballAutoResume`, `useStandbySession`, `withResume`), camera settings/controls, `runUplinkCheck`, `createRtmpRuntime`, contracts, errors | `@pickleball/rtmp-native` (peer) |
|
|
34
|
+
| `@pickleball/expo-sdk/ui` | `PickleballCameraScreen`, `CameraControlsSheet`, primitives, `statusPillFor`, copy vi/en | `react-native-safe-area-context` |
|
|
35
|
+
| `@pickleball/expo-sdk/realtime` | `createGrantCommandSubscription` — commands over a Convex websocket instead of 2 s polling | `convex` |
|
|
36
|
+
| `@pickleball/expo-sdk/match` | Match scoreboard: `PickleballMatchProvider`, `usePickleballMatch`, SQLite persistence | `expo-sqlite` |
|
|
15
37
|
| `@pickleball/expo-sdk/match/voice` | Offline score announcer (VI/EN) + TTS | `@pickleball/voice-native`, `expo-audio` |
|
|
16
38
|
| `@pickleball/expo-sdk/match/remote` | BLE/HID court remote input | `@pickleball/remote-native`, Bluetooth permissions |
|
|
17
|
-
| `@pickleball/expo-sdk/device-agent` | Device registry + dashboard remote control (
|
|
18
|
-
| `@pickleball/expo-sdk/advanced` | `LivestreamEngine`, DI interfaces
|
|
39
|
+
| `@pickleball/expo-sdk/device-agent` | Device registry + dashboard remote control (Pickleball's own fleet app) | `convex` |
|
|
40
|
+
| `@pickleball/expo-sdk/advanced` | `LivestreamEngine`, DI interfaces, `RtmpRoomAdapter`, grant validators — for mocks/tests/custom wiring | — |
|
|
19
41
|
|
|
20
|
-
|
|
21
|
-
from the matching subpath above; the provider's `deviceAgent` prop was replaced
|
|
22
|
-
by mounting `<PickleballDeviceAgent/>` (from `/device-agent`) inside the
|
|
23
|
-
provider.
|
|
42
|
+
## Install
|
|
24
43
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
44
|
+
```bash
|
|
45
|
+
npx expo install @pickleball/expo-sdk @pickleball/rtmp-native \
|
|
46
|
+
@react-native-community/netinfo expo-file-system expo-keep-awake expo-sqlite \
|
|
47
|
+
react-native-safe-area-context
|
|
48
|
+
```
|
|
28
49
|
|
|
29
|
-
|
|
50
|
+
Add the config plugin and rebuild the native app (Expo Development Build / EAS —
|
|
51
|
+
Expo Go and web cannot run the camera module):
|
|
30
52
|
|
|
31
53
|
```json
|
|
32
54
|
{
|
|
33
55
|
"expo": {
|
|
34
56
|
"plugins": [
|
|
35
|
-
[
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
]
|
|
57
|
+
["@pickleball/expo-sdk", {
|
|
58
|
+
"cameraPermission": "Allow this app to livestream matches.",
|
|
59
|
+
"microphonePermission": "Allow this app to capture match audio.",
|
|
60
|
+
"modules": { "remote": false }
|
|
61
|
+
}]
|
|
42
62
|
]
|
|
43
63
|
}
|
|
44
64
|
}
|
|
45
65
|
```
|
|
46
66
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
the
|
|
52
|
-
|
|
53
|
-
### Slimming the native footprint
|
|
54
|
-
|
|
55
|
-
If the app never uses the BLE/HID court remote, disable its native config so
|
|
56
|
-
the build drops the Bluetooth permissions, the iOS Bluetooth purpose string,
|
|
57
|
-
and the MainActivity key-event patch:
|
|
58
|
-
|
|
59
|
-
```json
|
|
60
|
-
["@pickleball/expo-sdk", { "modules": { "remote": false } }]
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
`modules.voice` is accepted for forward compatibility (microphone/audio
|
|
64
|
-
permissions belong to livestreaming, so it currently gates nothing).
|
|
67
|
+
`modules.remote: false` drops the Bluetooth permissions and the MainActivity
|
|
68
|
+
key-event patch when the app does not use the court remote. `modules.livekit`
|
|
69
|
+
and `liveKit` from 0.x are still accepted and ignored. To keep the voice/remote
|
|
70
|
+
native modules out of the binary entirely, add them to `expo.autolinking.exclude`
|
|
71
|
+
in the app's `package.json`.
|
|
65
72
|
|
|
66
|
-
|
|
67
|
-
autolinking in the **app's** `package.json` — safe only if the app never
|
|
68
|
-
activates voice playback or remote scanning at runtime:
|
|
69
|
-
|
|
70
|
-
```json
|
|
71
|
-
{
|
|
72
|
-
"expo": {
|
|
73
|
-
"autolinking": {
|
|
74
|
-
"exclude": ["@pickleball/voice-native", "@pickleball/remote-native"]
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Usage
|
|
73
|
+
## Quick start (casual: the user goes live from the phone)
|
|
81
74
|
|
|
82
75
|
```tsx
|
|
83
76
|
import {
|
|
84
|
-
LivestreamScreen,
|
|
85
77
|
PickleballLiveProvider,
|
|
86
|
-
|
|
78
|
+
PickleballAutoResume,
|
|
79
|
+
createCameraGrantProvider,
|
|
80
|
+
createCameraSettingsStore,
|
|
81
|
+
createRtmpRuntime,
|
|
82
|
+
cameraSettingsKey,
|
|
83
|
+
rtmpRoomFactoryFor,
|
|
84
|
+
useCameraSettings,
|
|
85
|
+
useLivestream,
|
|
86
|
+
withLocalVideoQuality,
|
|
87
87
|
} from "@pickleball/expo-sdk";
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
import { PickleballCameraScreen } from "@pickleball/expo-sdk/ui";
|
|
89
|
+
import { createGrantCommandSubscription } from "@pickleball/expo-sdk/realtime";
|
|
90
|
+
|
|
91
|
+
const settings = createCameraSettingsStore(); // resolution/fps/camera, persisted
|
|
92
|
+
const grants = createCameraGrantProvider({
|
|
93
|
+
// Your backend: authenticates the user, calls Pickleball REST v1 with the
|
|
94
|
+
// API key, and returns the CameraGrant verbatim.
|
|
95
|
+
requestGrant: async (input) => {
|
|
96
|
+
const res = await fetch(`${API}/matches`, {
|
|
97
|
+
method: "POST",
|
|
98
|
+
headers: { Authorization: `Bearer ${await userToken()}`, "Content-Type": "application/json" },
|
|
99
|
+
body: JSON.stringify({ title: input.title, quality: input.quality }),
|
|
100
|
+
});
|
|
101
|
+
return (await res.json()).data.grant;
|
|
102
|
+
},
|
|
92
103
|
});
|
|
104
|
+
const sessionProvider = withLocalVideoQuality(grants, () => settings.get().resolution);
|
|
93
105
|
|
|
94
|
-
export function
|
|
106
|
+
export function LiveRoot({ children }) {
|
|
107
|
+
const config = useCameraSettings(settings);
|
|
95
108
|
return (
|
|
96
109
|
<PickleballLiveProvider
|
|
110
|
+
key={cameraSettingsKey(config)} // remount → re-prepare on resolution/fps change
|
|
97
111
|
sessionProvider={sessionProvider}
|
|
98
|
-
bootstrapInput={
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
112
|
+
bootstrapInput={identity} // sdkVersion, platform, bundleId, installationId
|
|
113
|
+
runtime={createRtmpRuntime()}
|
|
114
|
+
roomFactory={rtmpRoomFactoryFor(config)}
|
|
115
|
+
keepSessionOnRestart={() => grants.hasResumableSession()}
|
|
116
|
+
commandSubscription={createGrantCommandSubscription(() => grants.getActiveGrant()?.convexUrl)}
|
|
117
|
+
locale="vi"
|
|
104
118
|
>
|
|
105
|
-
<
|
|
106
|
-
|
|
107
|
-
externalSessionId: "match-123",
|
|
108
|
-
title: "Court 1",
|
|
109
|
-
consentVersion: "2026-01",
|
|
110
|
-
}}
|
|
111
|
-
/>
|
|
119
|
+
<PickleballAutoResume provider={grants} resumeInput={{ consentVersion: "2026-09" }} />
|
|
120
|
+
{children}
|
|
112
121
|
</PickleballLiveProvider>
|
|
113
122
|
);
|
|
114
123
|
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
`createHttpSessionProvider` talks only to the partner-owned proxy routes `/bootstrap`, `/sessions`, `/sessions/:id/refresh`, and `/sessions/:id/end`. It has no API-key option. Keep the Pickleball API key in the backend.
|
|
118
|
-
Production proxy URLs must use HTTPS; plain HTTP is accepted only for loopback development URLs such as `http://localhost:3000`.
|
|
119
|
-
|
|
120
|
-
Tokens stay in memory. The only persisted value is a bounded `{sessionId, pendingEnd}` record used to finish a session after connectivity returns. Remote policy can change data and flags, but native changes require a semver release and a rebuilt app.
|
|
121
124
|
|
|
122
|
-
|
|
125
|
+
export function CameraRoute({ matchId, title }) {
|
|
126
|
+
const live = useLivestream();
|
|
127
|
+
return (
|
|
128
|
+
<PickleballCameraScreen
|
|
129
|
+
settings={settings}
|
|
130
|
+
title={title}
|
|
131
|
+
onGoLive={() =>
|
|
132
|
+
live.start(
|
|
133
|
+
{ externalSessionId: `match-${matchId}`, title, consentVersion: "2026-09", quality: settings.get().resolution },
|
|
134
|
+
{ autoPublish: true },
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
/>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
```
|
|
123
141
|
|
|
124
|
-
|
|
142
|
+
What happens: `start()` asks your backend for a grant, publishes on the server
|
|
143
|
+
(`/publish`), connects RTMP and goes live. The screen shows the status pill,
|
|
144
|
+
lens row, a two-step Stop, a "•••" sheet with resolution/fps/camera/zoom/EV/
|
|
145
|
+
locks/torch/upload-speed check, and dims itself after 30 s live to save heat.
|
|
125
146
|
|
|
126
|
-
|
|
127
|
-
mounting `PickleballDeviceAgent` inside the provider (this is the only place
|
|
128
|
-
that pulls the `convex` client into the bundle):
|
|
147
|
+
### Court mode (the phone replaces a fixed court camera)
|
|
129
148
|
|
|
130
149
|
```tsx
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
<
|
|
137
|
-
<PickleballDeviceAgent
|
|
138
|
-
transport={createHttpDeviceAgentTransport({ baseUrl: proxyUrl, headers })}
|
|
139
|
-
remoteStart={{ consentVersion: "2026-01", defaultTitle: "Court 1" }}
|
|
140
|
-
onRemoteSessionStarted={(sessionId) => router.push("/livestream")}
|
|
141
|
-
/>
|
|
142
|
-
{children}
|
|
143
|
-
</PickleballLiveProvider>
|
|
150
|
+
const { armed } = useStandbySession({
|
|
151
|
+
input: { externalSessionId: `court-${courtRef}-${n}`, title: `${courtRef} — chờ trận`,
|
|
152
|
+
consentVersion: "2026-09", metadata: { courtRef } },
|
|
153
|
+
resumeSessionId: hintFromYourBackend ?? null,
|
|
154
|
+
});
|
|
155
|
+
<PickleballCameraScreen mode="court" settings={settings} courtLabel={courtRef} />
|
|
144
156
|
```
|
|
157
|
+
|
|
158
|
+
Your backend forwards `courtRef` when it creates the session; the phone waits in
|
|
159
|
+
standby (12 h preset) and the referee's `POST /api/v1/courts/{courtRef}/match-start`
|
|
160
|
+
flips it live through the command channel; `match-end` ends it. Only one camera
|
|
161
|
+
can hold a court — a fleet device already assigned there returns
|
|
162
|
+
`COURT_HAS_DEVICE`.
|
|
163
|
+
|
|
164
|
+
### Resume after the app is killed
|
|
165
|
+
|
|
166
|
+
The provider stores `{sessionId, base, Bearer}` in the app sandbox (expo-sqlite
|
|
167
|
+
localStorage by default — pass `storage` to use `expo-secure-store`). On the next
|
|
168
|
+
launch `PickleballAutoResume` refreshes the grant and re-joins the same session
|
|
169
|
+
(same watch link, one VOD); a session the server no longer holds is abandoned
|
|
170
|
+
cleanly. Call `grants.clearStoredSession()` on logout and end the session from
|
|
171
|
+
your backend.
|
|
172
|
+
|
|
173
|
+
## Behaviour to know
|
|
174
|
+
|
|
175
|
+
- `standby` is free: no recording until `/publish`. Casual sessions end after
|
|
176
|
+
15 min of standby, 90 s in background, 3 h max; court-bound sessions get
|
|
177
|
+
12 h / 5 min / 6 h.
|
|
178
|
+
- A re-issued grant (your backend calls `/camera-grant` again) supersedes the
|
|
179
|
+
previous one: the old phone gets `409` with `reason: "superseded"` on its next
|
|
180
|
+
poll and stops at once. One grant = one phone.
|
|
181
|
+
- Errors surface as `PickleballExpoError` with `code` (`PERMISSION_DENIED`,
|
|
182
|
+
`NETWORK_UNAVAILABLE`, `TOKEN_EXPIRED`, `SESSION_CONFLICT`, …), `httpStatus`,
|
|
183
|
+
and for `SESSION_CONFLICT` a `reason` (`superseded | ended | expired`).
|
|
184
|
+
- Tokens live only in the grant; the SDK never sees an API key. Keep the key
|
|
185
|
+
in your backend.
|
|
186
|
+
|
|
187
|
+
## Migration 0.2 → 1.0
|
|
188
|
+
|
|
189
|
+
| 0.2 | 1.0 |
|
|
190
|
+
|---|---|
|
|
191
|
+
| `LivestreamScreen` (root) | `PickleballCameraScreen` from `@pickleball/expo-sdk/ui` |
|
|
192
|
+
| `createHttpSessionProvider` + partner proxy (6 routes, authz gateway) | `createCameraGrantProvider` + one backend endpoint that returns a grant |
|
|
193
|
+
| `createDefaultRuntime` (LiveKit) | `createRtmpRuntime` (`createDefaultRuntime` is now an alias) |
|
|
194
|
+
| `createRtmpRoomFactory` from `/advanced` | root export |
|
|
195
|
+
| LiveKit peers + `modules.livekit` | removed; `@pickleball/rtmp-native >=0.2.0` required |
|
|
196
|
+
|
|
197
|
+
`createHttpSessionProvider` still exists for first-party namespaces, but only
|
|
198
|
+
accepts `rtmp(s)://` grants.
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { R as RoomAdapter, a1 as RoomConnection, e as CameraDeviceInfo, g as CaptureOrientation, a2 as RoomEvent, r as SdkBootstrap, C as CameraGrant, b as SdkSessionGrant, y as SdkSessionRefresh, z as SdkTelemetryCredentials, a3 as EngineDependencies, w as SdkNetwork, x as SdkRuntime, a as SdkStorage, T as TelemetrySink, a0 as LivestreamEngine, B as BootstrapInput, m as LivestreamSnapshot, c as StartLivestreamInput, i as EndReason, L as LivestreamSessionProvider, H as SessionCommandChannel, S as SessionCommandSubscription, o as PickleballExpoError } from './engine-BSvSdCSE.js';
|
|
2
|
+
import React, { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Trạng thái từ tầng RTMP native (trùng NativeRtmpStatus của @pickleball/rtmp-native).
|
|
6
|
+
*/
|
|
7
|
+
type RtmpStatus = {
|
|
8
|
+
readonly status: "idle" | "connecting" | "connected" | "publishing";
|
|
9
|
+
} | {
|
|
10
|
+
readonly status: "disconnected";
|
|
11
|
+
readonly reason?: string;
|
|
12
|
+
} | {
|
|
13
|
+
readonly status: "error";
|
|
14
|
+
readonly code: string;
|
|
15
|
+
readonly message: string;
|
|
16
|
+
};
|
|
17
|
+
/** Phản hồi băng thông từ native (trùng NativeRtmpBandwidth của rtmp-native). */
|
|
18
|
+
interface RtmpBandwidth {
|
|
19
|
+
/** false = encoder đang đẩy nhanh hơn đường truyền chịu được. */
|
|
20
|
+
readonly sufficient: boolean;
|
|
21
|
+
readonly videoBitrate: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Dấu hiệu để UI biết render nguồn nào. LiveKit trả về một track object; RTMP
|
|
25
|
+
* không có "track" ở JS (khung hình đi thẳng trong native), nên trả marker này
|
|
26
|
+
* — nếu trả undefined thì UI tưởng chưa có preview và vẽ màn TỐI ĐEN.
|
|
27
|
+
*/
|
|
28
|
+
interface RtmpPreviewMarker {
|
|
29
|
+
readonly kind: "rtmp";
|
|
30
|
+
}
|
|
31
|
+
/** Bề mặt native mà adapter cần — tiêm vào để test được không cần thiết bị thật. */
|
|
32
|
+
interface NativeRtmpBindings {
|
|
33
|
+
prepare(options: {
|
|
34
|
+
quality: 720 | 1080;
|
|
35
|
+
landscape: boolean;
|
|
36
|
+
/** fps mong muốn (24 tiết kiệm băng thông, 30 mặc định) — thiếu = native tự chọn. */
|
|
37
|
+
frameRate?: 24 | 30 | 60;
|
|
38
|
+
/**
|
|
39
|
+
* Camera mở ngay từ đầu. Thiếu = native dùng camera sau mặc định. Đặt ở đây
|
|
40
|
+
* thay vì gọi selectCamera sau prepare để không phải mở camera hai lần.
|
|
41
|
+
*/
|
|
42
|
+
cameraId?: string;
|
|
43
|
+
}): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Xin quyền camera/mic bằng API quyền thuần (PermissionsAndroid, ...).
|
|
46
|
+
* Không có thì engine rơi về getUserMedia của WebRTC — thứ mở camera thật
|
|
47
|
+
* và đụng HaishinKit trên máy Android chỉ cho một camera mở cùng lúc.
|
|
48
|
+
*/
|
|
49
|
+
requestPermissions?(): Promise<boolean>;
|
|
50
|
+
/** Hướng cầm máy hiện tại — cùng nguồn mà LiveKitRoomAdapter dùng. */
|
|
51
|
+
isLandscape?(): boolean;
|
|
52
|
+
connect(options: {
|
|
53
|
+
url: string;
|
|
54
|
+
videoBitrate: number;
|
|
55
|
+
publish: boolean;
|
|
56
|
+
}): Promise<void>;
|
|
57
|
+
setPublishing(enabled: boolean): Promise<void>;
|
|
58
|
+
disconnect(): Promise<void>;
|
|
59
|
+
switchCamera(): Promise<void>;
|
|
60
|
+
listCameras(): Array<{
|
|
61
|
+
deviceId: string;
|
|
62
|
+
label: string;
|
|
63
|
+
facing: string;
|
|
64
|
+
}>;
|
|
65
|
+
selectCamera(deviceId: string, facing: string): Promise<void>;
|
|
66
|
+
setMicrophoneEnabled(enabled: boolean): Promise<void>;
|
|
67
|
+
setVideoBitrate(bitsPerSecond: number): Promise<void>;
|
|
68
|
+
dispose(): Promise<void>;
|
|
69
|
+
addStatusListener(listener: (event: RtmpStatus) => void): {
|
|
70
|
+
remove(): void;
|
|
71
|
+
};
|
|
72
|
+
/** Optional: bản native cũ chưa phát onBandwidth thì adapter bỏ qua thích ứng. */
|
|
73
|
+
addBandwidthListener?(listener: (event: RtmpBandwidth) => void): {
|
|
74
|
+
remove(): void;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Adapter cho mode deliveryMode="device-rtmp".
|
|
79
|
+
*
|
|
80
|
+
* Thiết bị mã hoá H.264 bằng phần cứng MỘT lần rồi đẩy thẳng RTMP; server chỉ
|
|
81
|
+
* remux sang HLS. Không có phòng LiveKit, không egress → $0 transcode.
|
|
82
|
+
*
|
|
83
|
+
* Khác LiveKitRoomAdapter ở một điểm quan trọng: RTMP **không có kênh dữ liệu**,
|
|
84
|
+
* nên adapter này không bao giờ phát RoomEvent "command". Lệnh điều khiển từ
|
|
85
|
+
* dashboard đi đường khác (deviceControl trên Convex) — xem device-agent.
|
|
86
|
+
*/
|
|
87
|
+
declare class RtmpRoomAdapter implements RoomAdapter {
|
|
88
|
+
private readonly bindings;
|
|
89
|
+
private readonly options;
|
|
90
|
+
/**
|
|
91
|
+
* RTMP không tự hồi: native chỉ phát hiện đứt (watchdog iOS / onDisconnect
|
|
92
|
+
* Android) và báo "disconnected" — không tự quay số lại. Engine thấy cờ này
|
|
93
|
+
* là chạy vòng redial backoff. Thiếu cờ: phiên ngồi chờ "reconnected" không
|
|
94
|
+
* bao giờ tới rồi chết network_timeout sau 30s (prod 17/08/2026).
|
|
95
|
+
*/
|
|
96
|
+
readonly needsManualReconnect = true;
|
|
97
|
+
private readonly listeners;
|
|
98
|
+
private nativeSubscription?;
|
|
99
|
+
private bandwidthSubscription?;
|
|
100
|
+
private quality;
|
|
101
|
+
private connected;
|
|
102
|
+
private prepared;
|
|
103
|
+
/** Khung người dùng chọn trên UI (9:16/16:9) — ưu tiên hơn hướng cầm máy. */
|
|
104
|
+
private captureOrientation?;
|
|
105
|
+
private bitrateState?;
|
|
106
|
+
/**
|
|
107
|
+
* Chỉ tồn tại khi bindings hỗ trợ — engine kiểm tra truthiness để quyết định
|
|
108
|
+
* fallback về runtime.requestPermissions() (getUserMedia), nên KHÔNG được
|
|
109
|
+
* khai báo method luôn-có rồi tự fallback bên trong.
|
|
110
|
+
*/
|
|
111
|
+
readonly requestPermissions?: () => Promise<boolean>;
|
|
112
|
+
constructor(bindings: NativeRtmpBindings, options?: {
|
|
113
|
+
frameRate?: 24 | 30 | 60;
|
|
114
|
+
cameraId?: string;
|
|
115
|
+
});
|
|
116
|
+
/**
|
|
117
|
+
* Phần tuỳ chọn của prepare(). Giữ dạng spread có điều kiện: truyền
|
|
118
|
+
* `frameRate: undefined` xuống Expo Record là ghi đè mặc định của native
|
|
119
|
+
* bằng null chứ không phải "bỏ qua trường".
|
|
120
|
+
*/
|
|
121
|
+
private prepareOverrides;
|
|
122
|
+
preparePreview(quality: 720 | 1080): Promise<RtmpPreviewMarker>;
|
|
123
|
+
connect(input: RoomConnection): Promise<void>;
|
|
124
|
+
setPublishing(enabled: boolean): Promise<void>;
|
|
125
|
+
disconnect(_stopTracks?: boolean): Promise<void>;
|
|
126
|
+
switchCamera(): Promise<void>;
|
|
127
|
+
/** Ống kính vật lý (0.5x siêu rộng · 1x · tele) — không có thì UI kẹt ở mặc định. */
|
|
128
|
+
listCameras(): Promise<CameraDeviceInfo[]>;
|
|
129
|
+
selectCamera(deviceId: string, facing: CameraDeviceInfo["facing"]): Promise<void>;
|
|
130
|
+
setMicrophoneEnabled(enabled: boolean): Promise<void>;
|
|
131
|
+
/** Hạ chất lượng khi mạng yếu — thay cho BWE mà WebRTC cho sẵn. */
|
|
132
|
+
setVideoBitrate(bitsPerSecond: number): Promise<void>;
|
|
133
|
+
setCaptureOrientation(orientation: CaptureOrientation): Promise<void>;
|
|
134
|
+
subscribe(listener: (event: RoomEvent) => void): () => void;
|
|
135
|
+
dispose(): Promise<void>;
|
|
136
|
+
private handleBandwidth;
|
|
137
|
+
private emit;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Validator + helper HTTP dùng chung cho hai session provider:
|
|
142
|
+
* - createHttpSessionProvider (proxy đối tác / first-party device-app)
|
|
143
|
+
* - createCameraGrantProvider (điện thoại đối tác, grant từ backend đối tác)
|
|
144
|
+
*
|
|
145
|
+
* Tách ra để hai provider không chép luật "grant thế nào là hợp lệ" ra hai nơi
|
|
146
|
+
* rồi lệch nhau; engine chỉ tin `isSdkRemoteConfig`, còn hình dạng grant là
|
|
147
|
+
* việc của lớp này.
|
|
148
|
+
*/
|
|
149
|
+
type Fetch = typeof globalThis.fetch;
|
|
150
|
+
declare function isBootstrap(value: unknown): value is SdkBootstrap;
|
|
151
|
+
declare function isTelemetryCredentials(value: unknown): value is SdkTelemetryCredentials;
|
|
152
|
+
/**
|
|
153
|
+
* Grant phiên. SDK 1.0 là RTMP-only: `serverUrl` phải là rtmp(s):// (ingest).
|
|
154
|
+
* Grant LiveKit (wss://) của mô hình cũ bị từ chối ngay ở đây — lỗi
|
|
155
|
+
* INVALID_RESPONSE rõ ràng thay vì HaishinKit ném lúc connect.
|
|
156
|
+
*/
|
|
157
|
+
declare function isSessionGrant(value: unknown): value is SdkSessionGrant;
|
|
158
|
+
/**
|
|
159
|
+
* Camera grant (điện thoại đối tác): superset của SdkSessionGrant, BẮT BUỘC là
|
|
160
|
+
* RTMP và mang `status` để đường nhận lại biết phiên cũ đang phát hay đang chờ.
|
|
161
|
+
*/
|
|
162
|
+
declare function isCameraGrant(value: unknown): value is CameraGrant;
|
|
163
|
+
declare function isSessionRefresh(value: unknown): value is SdkSessionRefresh;
|
|
164
|
+
/**
|
|
165
|
+
* Gốc API của phiên suy từ endpoint telemetry trong grant: `…/sessions/{id}/telemetry`
|
|
166
|
+
* → `…/sessions/{id}`. Đây là cách duy nhất SDK biết namespace (sdk / camera /
|
|
167
|
+
* device-app) mà không cần app cấu hình URL PickLive nào.
|
|
168
|
+
*/
|
|
169
|
+
declare function sessionApiBaseFromTelemetryEndpoint(endpoint: string): string;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Runtime cho app RTMP-only (KHÔNG có LiveKit/WebRTC): assert bằng module
|
|
173
|
+
* @pickleball/rtmp-native, xin quyền bằng API quyền thuần (Android) hoặc hàm
|
|
174
|
+
* native `requestPermissions` (iOS, rtmp-native ≥ 0.2; thiếu thì AVFoundation
|
|
175
|
+
* hỏi lúc mở camera). Chuyển từ apps/device-streamer (createRtmpOnlyRuntime).
|
|
176
|
+
* Không dùng getUserMedia: máy MediaTek chỉ cho một camera mở cùng lúc.
|
|
177
|
+
*/
|
|
178
|
+
declare function createRtmpRuntime(): SdkRuntime;
|
|
179
|
+
/** Runtime mặc định của SDK 1.0 = RTMP (LiveKit đã gỡ khỏi SDK — ADR-0002). */
|
|
180
|
+
declare const createDefaultRuntime: typeof createRtmpRuntime;
|
|
181
|
+
declare function createDefaultStorage(): SdkStorage;
|
|
182
|
+
declare function createDefaultNetwork(): SdkNetwork;
|
|
183
|
+
declare function createDefaultTelemetry(fetchImplementation?: typeof fetch, timeoutMs?: number): TelemetrySink;
|
|
184
|
+
/** roomFactory mặc định của SDK 1.0 = RTMP (fps/camera mặc định của native). */
|
|
185
|
+
declare function createDefaultRoomFactory(): EngineDependencies["roomFactory"];
|
|
186
|
+
/**
|
|
187
|
+
* Bindings native RTMP. `require` động: app chưa cài @pickleball/rtmp-native
|
|
188
|
+
* chỉ lỗi khi thật sự mở camera (UnsupportedRuntimeError rõ ràng), không gãy
|
|
189
|
+
* lúc import SDK — static-render web của expo-router vẫn nạp được module.
|
|
190
|
+
*/
|
|
191
|
+
declare function createNativeRtmpBindings(): NativeRtmpBindings;
|
|
192
|
+
/** roomFactory cho mode device-rtmp (thiết bị đẩy RTMP, không dùng LiveKit). */
|
|
193
|
+
declare function createRtmpRoomFactory(options?: {
|
|
194
|
+
/**
|
|
195
|
+
* fps capture + encode (24 tiết kiệm băng thông, 30 mặc định của native).
|
|
196
|
+
* 60 chỉ nên truyền khi `getCameraCapabilities().supportedFrameRates` có nó —
|
|
197
|
+
* native bỏ qua trong im lặng nếu ống kính đang gắn không chạy được.
|
|
198
|
+
*/
|
|
199
|
+
frameRate?: 24 | 30 | 60;
|
|
200
|
+
/** Camera mở sẵn lúc prepare — thiếu = camera sau mặc định của native. */
|
|
201
|
+
cameraId?: string;
|
|
202
|
+
}): EngineDependencies["roomFactory"];
|
|
203
|
+
/**
|
|
204
|
+
* Thu chỉ số thiết bị cho heartbeat. Mỗi nguồn là optional — thiếu package
|
|
205
|
+
* (expo-battery/expo-device/expo-file-system) thì bỏ qua trường đó thay vì lỗi.
|
|
206
|
+
*/
|
|
207
|
+
declare function createDefaultDeviceMetrics(): NonNullable<EngineDependencies["deviceMetrics"]>;
|
|
208
|
+
|
|
209
|
+
interface LivestreamTheme {
|
|
210
|
+
background: string;
|
|
211
|
+
foreground: string;
|
|
212
|
+
accent: string;
|
|
213
|
+
danger: string;
|
|
214
|
+
/** Chữ phụ mờ nhất (labels, chú thích). */
|
|
215
|
+
muted: string;
|
|
216
|
+
overlay: string;
|
|
217
|
+
radius: number;
|
|
218
|
+
/** Token FintechX — đồng bộ với dashboard web. */
|
|
219
|
+
surface: string;
|
|
220
|
+
elevated: string;
|
|
221
|
+
hairline: string;
|
|
222
|
+
/** Màu chữ đặt trên nền accent. */
|
|
223
|
+
ink: string;
|
|
224
|
+
textSecondary: string;
|
|
225
|
+
radiusSm: number;
|
|
226
|
+
fontFamily?: string;
|
|
227
|
+
fontFamilyBold?: string;
|
|
228
|
+
displayFontFamily?: string;
|
|
229
|
+
}
|
|
230
|
+
/** FintechX design tokens — trùng bộ token trong apps/web/globals.css. */
|
|
231
|
+
declare const defaultLivestreamTheme: LivestreamTheme;
|
|
232
|
+
interface PickleballLiveProviderProps {
|
|
233
|
+
children?: ReactNode;
|
|
234
|
+
sessionProvider: LivestreamSessionProvider;
|
|
235
|
+
bootstrapInput: BootstrapInput;
|
|
236
|
+
/**
|
|
237
|
+
* Phiên phải SỐNG TIẾP khi app biến mất giữa chừng (sụp nguồn, bị vuốt tắt,
|
|
238
|
+
* component remount), để lần mở sau nhận lại đúng phiên cũ — giữ nguyên link
|
|
239
|
+
* xem và VOD liền một mạch.
|
|
240
|
+
*
|
|
241
|
+
* Mặc định false = hành vi cũ: mở app lại là dọn sạch phiên cũ. App gác sân bật
|
|
242
|
+
* cờ này khi máy đang được gán sân; máy không gán sân vẫn dọn như cũ để không
|
|
243
|
+
* để lại phiên treo trong các ca ngoài giải.
|
|
244
|
+
*
|
|
245
|
+
* Nhận boolean HOẶC hàm (được phép async). App nên truyền HÀM ĐỌC THẲNG ĐĨA:
|
|
246
|
+
* engine hỏi cờ này đúng lúc khởi động, thường TRƯỚC khi state React kịp
|
|
247
|
+
* restore — truyền boolean suy từ state là dính race keep=false oan (đã dính
|
|
248
|
+
* thật 18/08/2026, sinh phiên chờ thứ hai đè phiên live cũ).
|
|
249
|
+
*/
|
|
250
|
+
keepSessionOnRestart?: boolean | (() => boolean | Promise<boolean>);
|
|
251
|
+
runtime?: SdkRuntime;
|
|
252
|
+
storage?: SdkStorage;
|
|
253
|
+
network?: SdkNetwork;
|
|
254
|
+
telemetry?: TelemetrySink;
|
|
255
|
+
/** Kênh poll lệnh cho mode device-rtmp — mặc định suy từ telemetry endpoint. */
|
|
256
|
+
commandChannel?: SessionCommandChannel;
|
|
257
|
+
/**
|
|
258
|
+
* Kênh nhận lệnh qua websocket (first-party nối thẳng Convex). Có thì lệnh về
|
|
259
|
+
* tức thì và poll tụt về nhịp reconcile chậm — cắt phần lớn function call của
|
|
260
|
+
* đường poll. Thiếu (máy đối tác qua proxy) thì chỉ dùng poll HTTP như cũ.
|
|
261
|
+
*/
|
|
262
|
+
commandSubscription?: SessionCommandSubscription;
|
|
263
|
+
roomFactory?: () => RoomAdapter;
|
|
264
|
+
deviceMetrics?: EngineDependencies["deviceMetrics"];
|
|
265
|
+
theme?: Partial<LivestreamTheme>;
|
|
266
|
+
locale?: "vi" | "en";
|
|
267
|
+
onError?: (error: PickleballExpoError) => void;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Context nội bộ cho các module opt-in (VD PickleballDeviceAgent) cần truy cập
|
|
271
|
+
* engine trực tiếp — export qua "./advanced", không thuộc API tích hợp cơ bản.
|
|
272
|
+
*/
|
|
273
|
+
interface LivestreamHostContextValue {
|
|
274
|
+
engine: LivestreamEngine;
|
|
275
|
+
bootstrapInput: BootstrapInput;
|
|
276
|
+
deviceMetrics: NonNullable<EngineDependencies["deviceMetrics"]>;
|
|
277
|
+
}
|
|
278
|
+
declare function useLivestreamHost(): LivestreamHostContextValue;
|
|
279
|
+
interface LivestreamContextValue extends LivestreamSnapshot {
|
|
280
|
+
locale: "vi" | "en";
|
|
281
|
+
theme: LivestreamTheme;
|
|
282
|
+
prepare(): Promise<void>;
|
|
283
|
+
start(input: StartLivestreamInput, options?: {
|
|
284
|
+
autoPublish?: boolean;
|
|
285
|
+
}): Promise<void>;
|
|
286
|
+
/** Bắt đầu phát khi đang standby. */
|
|
287
|
+
startPublishing(): Promise<void>;
|
|
288
|
+
/** Tạm dừng phát về standby (không kết thúc phiên). */
|
|
289
|
+
stopPublishing(): Promise<void>;
|
|
290
|
+
stop(reason?: EndReason): Promise<void>;
|
|
291
|
+
retry(): Promise<void>;
|
|
292
|
+
switchCamera(): Promise<void>;
|
|
293
|
+
setMicrophoneEnabled(enabled: boolean): Promise<void>;
|
|
294
|
+
setCaptureOrientation(orientation: "portrait" | "landscape"): Promise<void>;
|
|
295
|
+
selectCamera(deviceId: string): Promise<void>;
|
|
296
|
+
}
|
|
297
|
+
declare function PickleballLiveProvider(props: PickleballLiveProviderProps): React.JSX.Element;
|
|
298
|
+
declare function useLivestream(): LivestreamContextValue;
|
|
299
|
+
|
|
300
|
+
export { type Fetch as F, type LivestreamContextValue as L, type NativeRtmpBindings as N, PickleballLiveProvider as P, RtmpRoomAdapter as R, type LivestreamTheme as a, type PickleballLiveProviderProps as b, createDefaultStorage as c, createRtmpRoomFactory as d, createRtmpRuntime as e, defaultLivestreamTheme as f, type LivestreamHostContextValue as g, type RtmpStatus as h, createDefaultDeviceMetrics as i, createDefaultNetwork as j, createDefaultRoomFactory as k, createDefaultRuntime as l, createDefaultTelemetry as m, createNativeRtmpBindings as n, isBootstrap as o, isCameraGrant as p, isSessionGrant as q, isSessionRefresh as r, isTelemetryCredentials as s, sessionApiBaseFromTelemetryEndpoint as t, useLivestream as u, useLivestreamHost as v };
|