@roboflow/inference-sdk 0.1.7 → 0.1.8
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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.js +352 -192
- package/dist/index.es.js.map +1 -0
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -0
- package/dist/inference-api.d.ts +12 -0
- package/dist/inference-api.d.ts.map +1 -1
- package/dist/video-upload.d.ts +30 -0
- package/dist/video-upload.d.ts.map +1 -0
- package/dist/webrtc-types.d.ts +44 -0
- package/dist/webrtc-types.d.ts.map +1 -0
- package/dist/webrtc.d.ts +98 -8
- package/dist/webrtc.d.ts.map +1 -1
- package/package.json +4 -3
package/dist/index.es.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
const
|
|
1
|
+
var N = Object.defineProperty;
|
|
2
|
+
var A = (r, e, t) => e in r ? N(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
|
|
3
|
+
var d = (r, e, t) => A(r, typeof e != "symbol" ? e + "" : e, t);
|
|
4
|
+
var U;
|
|
5
|
+
const x = typeof process < "u" && ((U = process.env) != null && U.RF_API_BASE_URL) ? process.env.RF_API_BASE_URL : "https://api.roboflow.com", q = [
|
|
6
6
|
"https://serverless.roboflow.com"
|
|
7
7
|
];
|
|
8
|
-
class
|
|
8
|
+
class b {
|
|
9
9
|
/**
|
|
10
10
|
* @private
|
|
11
11
|
* Use InferenceHTTPClient.init() instead
|
|
12
12
|
*/
|
|
13
13
|
constructor(e, t = "https://serverless.roboflow.com") {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
d(this, "apiKey");
|
|
15
|
+
d(this, "serverUrl");
|
|
16
16
|
this.apiKey = e, this.serverUrl = t;
|
|
17
17
|
}
|
|
18
18
|
static init({ apiKey: e, serverUrl: t }) {
|
|
19
19
|
if (!e)
|
|
20
20
|
throw new Error("apiKey is required");
|
|
21
|
-
return new
|
|
21
|
+
return new b(e, t);
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* Initialize a WebRTC worker pipeline
|
|
@@ -48,59 +48,60 @@ class v {
|
|
|
48
48
|
async initializeWebrtcWorker({
|
|
49
49
|
offer: e,
|
|
50
50
|
workflowSpec: t,
|
|
51
|
-
workspaceName:
|
|
52
|
-
workflowId:
|
|
51
|
+
workspaceName: a,
|
|
52
|
+
workflowId: o,
|
|
53
53
|
config: n = {}
|
|
54
54
|
}) {
|
|
55
55
|
if (!e || !e.sdp || !e.type)
|
|
56
56
|
throw new Error("offer with sdp and type is required");
|
|
57
|
-
const i = !!t,
|
|
58
|
-
if (!i && !
|
|
57
|
+
const i = !!t, l = !!(a && o);
|
|
58
|
+
if (!i && !l)
|
|
59
59
|
throw new Error("Either workflowSpec OR (workspaceName + workflowId) is required");
|
|
60
|
-
if (i &&
|
|
60
|
+
if (i && l)
|
|
61
61
|
throw new Error("Provide either workflowSpec OR (workspaceName + workflowId), not both");
|
|
62
62
|
const {
|
|
63
|
-
imageInputName:
|
|
64
|
-
streamOutputNames:
|
|
65
|
-
dataOutputNames:
|
|
66
|
-
threadPoolWorkers:
|
|
67
|
-
workflowsParameters:
|
|
68
|
-
iceServers:
|
|
69
|
-
processingTimeout:
|
|
70
|
-
requestedPlan:
|
|
71
|
-
requestedRegion:
|
|
72
|
-
|
|
63
|
+
imageInputName: c = "image",
|
|
64
|
+
streamOutputNames: s = [],
|
|
65
|
+
dataOutputNames: u = ["string"],
|
|
66
|
+
threadPoolWorkers: h = 4,
|
|
67
|
+
workflowsParameters: y = {},
|
|
68
|
+
iceServers: w,
|
|
69
|
+
processingTimeout: R,
|
|
70
|
+
requestedPlan: g,
|
|
71
|
+
requestedRegion: v,
|
|
72
|
+
realtimeProcessing: m = !0
|
|
73
|
+
} = n, p = {
|
|
73
74
|
type: "WorkflowConfiguration",
|
|
74
|
-
image_input_name:
|
|
75
|
-
workflows_parameters:
|
|
76
|
-
workflows_thread_pool_workers:
|
|
75
|
+
image_input_name: c,
|
|
76
|
+
workflows_parameters: y,
|
|
77
|
+
workflows_thread_pool_workers: h,
|
|
77
78
|
cancel_thread_pool_tasks_on_exit: !0,
|
|
78
79
|
video_metadata_input_name: "video_metadata"
|
|
79
80
|
};
|
|
80
|
-
i ?
|
|
81
|
-
const
|
|
82
|
-
workflow_configuration:
|
|
81
|
+
i ? p.workflow_specification = t : (p.workspace_name = a, p.workflow_id = o);
|
|
82
|
+
const S = {
|
|
83
|
+
workflow_configuration: p,
|
|
83
84
|
api_key: this.apiKey,
|
|
84
|
-
webrtc_realtime_processing:
|
|
85
|
+
webrtc_realtime_processing: m,
|
|
85
86
|
webrtc_offer: {
|
|
86
87
|
sdp: e.sdp,
|
|
87
88
|
type: e.type
|
|
88
89
|
},
|
|
89
|
-
webrtc_config:
|
|
90
|
-
stream_output:
|
|
91
|
-
data_output:
|
|
90
|
+
webrtc_config: w ? { iceServers: w } : null,
|
|
91
|
+
stream_output: s,
|
|
92
|
+
data_output: u
|
|
92
93
|
};
|
|
93
|
-
|
|
94
|
-
const
|
|
94
|
+
R !== void 0 && (S.processing_timeout = R), g !== void 0 && (S.requested_plan = g), v !== void 0 && (S.requested_region = v);
|
|
95
|
+
const C = await fetch(`${this.serverUrl}/initialise_webrtc_worker`, {
|
|
95
96
|
method: "POST",
|
|
96
97
|
headers: { "Content-Type": "application/json" },
|
|
97
|
-
body: JSON.stringify(
|
|
98
|
+
body: JSON.stringify(S)
|
|
98
99
|
});
|
|
99
|
-
if (!
|
|
100
|
-
const
|
|
101
|
-
throw new Error(`initialise_webrtc_worker failed (${
|
|
100
|
+
if (!C.ok) {
|
|
101
|
+
const k = await C.text().catch(() => "");
|
|
102
|
+
throw new Error(`initialise_webrtc_worker failed (${C.status}): ${k}`);
|
|
102
103
|
}
|
|
103
|
-
return await
|
|
104
|
+
return await C.json();
|
|
104
105
|
}
|
|
105
106
|
async terminatePipeline({ pipelineId: e }) {
|
|
106
107
|
if (!e)
|
|
@@ -130,11 +131,11 @@ class v {
|
|
|
130
131
|
* ```
|
|
131
132
|
*/
|
|
132
133
|
async fetchTurnConfig() {
|
|
133
|
-
if (!
|
|
134
|
+
if (!q.includes(this.serverUrl))
|
|
134
135
|
return null;
|
|
135
136
|
try {
|
|
136
137
|
const e = await fetch(
|
|
137
|
-
`${
|
|
138
|
+
`${x}/webrtc_turn_config?api_key=${this.apiKey}`,
|
|
138
139
|
{
|
|
139
140
|
method: "GET",
|
|
140
141
|
headers: { "Content-Type": "application/json" }
|
|
@@ -143,16 +144,16 @@ class v {
|
|
|
143
144
|
if (!e.ok)
|
|
144
145
|
return console.warn(`[RFWebRTC] Failed to fetch TURN config (${e.status}), using defaults`), null;
|
|
145
146
|
const t = await e.json();
|
|
146
|
-
let
|
|
147
|
+
let a;
|
|
147
148
|
if (Array.isArray(t))
|
|
148
|
-
|
|
149
|
+
a = t;
|
|
149
150
|
else if (t.iceServers && Array.isArray(t.iceServers))
|
|
150
|
-
|
|
151
|
+
a = t.iceServers;
|
|
151
152
|
else if (t.urls)
|
|
152
|
-
|
|
153
|
+
a = [t];
|
|
153
154
|
else
|
|
154
155
|
return console.warn("[RFWebRTC] Invalid TURN config format, using defaults"), null;
|
|
155
|
-
return
|
|
156
|
+
return a.map((n) => ({
|
|
156
157
|
urls: Array.isArray(n.urls) ? n.urls : [n.urls],
|
|
157
158
|
username: n.username,
|
|
158
159
|
credential: n.credential
|
|
@@ -162,7 +163,7 @@ class v {
|
|
|
162
163
|
}
|
|
163
164
|
}
|
|
164
165
|
}
|
|
165
|
-
const
|
|
166
|
+
const ee = {
|
|
166
167
|
/**
|
|
167
168
|
* Create a connector that uses API key directly
|
|
168
169
|
*
|
|
@@ -181,15 +182,15 @@ const $ = {
|
|
|
181
182
|
* const answer = await connector.connectWrtc(offer, wrtcParams);
|
|
182
183
|
* ```
|
|
183
184
|
*/
|
|
184
|
-
withApiKey(
|
|
185
|
+
withApiKey(r, e = {}) {
|
|
185
186
|
const { serverUrl: t } = e;
|
|
186
187
|
typeof window < "u" && console.warn(
|
|
187
188
|
"[Security Warning] Using API key directly in browser will expose it. Use connectors.withProxyUrl() for production. See: https://docs.roboflow.com/api-reference/authentication#securing-your-api-key"
|
|
188
189
|
);
|
|
189
|
-
const
|
|
190
|
+
const a = b.init({ apiKey: r, serverUrl: t });
|
|
190
191
|
return {
|
|
191
|
-
connectWrtc: async (
|
|
192
|
-
offer:
|
|
192
|
+
connectWrtc: async (o, n) => (console.debug("wrtcParams", n), await a.initializeWebrtcWorker({
|
|
193
|
+
offer: o,
|
|
193
194
|
workflowSpec: n.workflowSpec,
|
|
194
195
|
workspaceName: n.workspaceName,
|
|
195
196
|
workflowId: n.workflowId,
|
|
@@ -202,15 +203,16 @@ const $ = {
|
|
|
202
203
|
iceServers: n.iceServers,
|
|
203
204
|
processingTimeout: n.processingTimeout,
|
|
204
205
|
requestedPlan: n.requestedPlan,
|
|
205
|
-
requestedRegion: n.requestedRegion
|
|
206
|
+
requestedRegion: n.requestedRegion,
|
|
207
|
+
realtimeProcessing: n.realtimeProcessing
|
|
206
208
|
}
|
|
207
209
|
})),
|
|
208
210
|
/**
|
|
209
211
|
* Fetch TURN server configuration for improved WebRTC connectivity
|
|
210
212
|
*/
|
|
211
|
-
getIceServers: async () => await
|
|
213
|
+
getIceServers: async () => await a.fetchTurnConfig(),
|
|
212
214
|
// Store apiKey for cleanup
|
|
213
|
-
_apiKey:
|
|
215
|
+
_apiKey: r,
|
|
214
216
|
_serverUrl: t
|
|
215
217
|
};
|
|
216
218
|
},
|
|
@@ -277,16 +279,16 @@ const $ = {
|
|
|
277
279
|
* });
|
|
278
280
|
* ```
|
|
279
281
|
*/
|
|
280
|
-
withProxyUrl(
|
|
282
|
+
withProxyUrl(r, e = {}) {
|
|
281
283
|
const { turnConfigUrl: t } = e;
|
|
282
284
|
return {
|
|
283
|
-
connectWrtc: async (
|
|
284
|
-
const n = await fetch(
|
|
285
|
+
connectWrtc: async (a, o) => {
|
|
286
|
+
const n = await fetch(r, {
|
|
285
287
|
method: "POST",
|
|
286
288
|
headers: { "Content-Type": "application/json" },
|
|
287
289
|
body: JSON.stringify({
|
|
288
|
-
offer:
|
|
289
|
-
wrtcParams:
|
|
290
|
+
offer: a,
|
|
291
|
+
wrtcParams: o
|
|
290
292
|
})
|
|
291
293
|
});
|
|
292
294
|
if (!n.ok) {
|
|
@@ -301,60 +303,100 @@ const $ = {
|
|
|
301
303
|
*/
|
|
302
304
|
getIceServers: t ? async () => {
|
|
303
305
|
try {
|
|
304
|
-
const
|
|
306
|
+
const a = await fetch(t, {
|
|
305
307
|
method: "GET",
|
|
306
308
|
headers: { "Content-Type": "application/json" }
|
|
307
309
|
});
|
|
308
|
-
return
|
|
309
|
-
} catch (
|
|
310
|
-
return console.warn("[RFWebRTC] Error fetching TURN config from proxy:",
|
|
310
|
+
return a.ok ? (await a.json()).iceServers || null : (console.warn(`[RFWebRTC] Failed to fetch TURN config from proxy (${a.status})`), null);
|
|
311
|
+
} catch (a) {
|
|
312
|
+
return console.warn("[RFWebRTC] Error fetching TURN config from proxy:", a), null;
|
|
311
313
|
}
|
|
312
314
|
} : void 0
|
|
313
315
|
};
|
|
314
316
|
}
|
|
315
317
|
};
|
|
316
|
-
async function
|
|
318
|
+
async function K(r = { video: !0 }) {
|
|
317
319
|
try {
|
|
318
|
-
console.log("[RFStreams] requesting with",
|
|
319
|
-
const e = await navigator.mediaDevices.getUserMedia(
|
|
320
|
+
console.log("[RFStreams] requesting with", r);
|
|
321
|
+
const e = await navigator.mediaDevices.getUserMedia(r);
|
|
320
322
|
return console.log("[RFStreams] got stream", e.getVideoTracks().map((t) => ({ id: t.id, label: t.label }))), e;
|
|
321
323
|
} catch (e) {
|
|
322
324
|
console.warn("[RFStreams] failed, falling back", e);
|
|
323
325
|
const t = await navigator.mediaDevices.getUserMedia({ video: !0, audio: !1 });
|
|
324
|
-
return console.log("[RFStreams] fallback stream", t.getVideoTracks().map((
|
|
326
|
+
return console.log("[RFStreams] fallback stream", t.getVideoTracks().map((a) => ({ id: a.id, label: a.label }))), t;
|
|
325
327
|
}
|
|
326
328
|
}
|
|
327
|
-
function
|
|
328
|
-
|
|
329
|
+
function W(r) {
|
|
330
|
+
r && (r.getTracks().forEach((e) => e.stop()), console.log("[RFStreams] Stream stopped"));
|
|
329
331
|
}
|
|
330
|
-
const
|
|
332
|
+
const te = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
331
333
|
__proto__: null,
|
|
332
|
-
stopStream:
|
|
333
|
-
useCamera:
|
|
334
|
-
}, Symbol.toStringTag, { value: "Module" })),
|
|
335
|
-
|
|
334
|
+
stopStream: W,
|
|
335
|
+
useCamera: K
|
|
336
|
+
}, Symbol.toStringTag, { value: "Module" })), T = 49152, j = 262144, B = 10;
|
|
337
|
+
function V(r) {
|
|
338
|
+
return new Promise((e) => setTimeout(e, r));
|
|
339
|
+
}
|
|
340
|
+
class I {
|
|
341
|
+
constructor(e, t) {
|
|
342
|
+
d(this, "file");
|
|
343
|
+
d(this, "channel");
|
|
344
|
+
d(this, "totalChunks");
|
|
345
|
+
d(this, "cancelled", !1);
|
|
346
|
+
this.file = e, this.channel = t, this.totalChunks = Math.ceil(e.size / T);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Cancel the upload
|
|
350
|
+
*/
|
|
351
|
+
cancel() {
|
|
352
|
+
this.cancelled = !0;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Upload the file in chunks with backpressure handling
|
|
356
|
+
*
|
|
357
|
+
* @param onProgress - Optional callback for progress updates (bytesUploaded, totalBytes)
|
|
358
|
+
*/
|
|
359
|
+
async upload(e) {
|
|
360
|
+
const t = this.file.size;
|
|
361
|
+
for (let a = 0; a < this.totalChunks; a++) {
|
|
362
|
+
if (this.cancelled)
|
|
363
|
+
throw new Error("Upload cancelled");
|
|
364
|
+
if (this.channel.readyState !== "open")
|
|
365
|
+
throw new Error("Video upload interrupted");
|
|
366
|
+
const o = a * T, n = Math.min(o + T, t), i = this.file.slice(o, n), l = new Uint8Array(await i.arrayBuffer()), c = new ArrayBuffer(8 + l.length), s = new DataView(c);
|
|
367
|
+
for (s.setUint32(0, a, !0), s.setUint32(4, this.totalChunks, !0), new Uint8Array(c, 8).set(l); this.channel.bufferedAmount > j; ) {
|
|
368
|
+
if (this.channel.readyState !== "open")
|
|
369
|
+
throw new Error("Video upload interrupted");
|
|
370
|
+
await V(B);
|
|
371
|
+
}
|
|
372
|
+
this.channel.send(c), e && e(n, t);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
const $ = 12;
|
|
377
|
+
class P {
|
|
336
378
|
constructor() {
|
|
337
|
-
|
|
379
|
+
d(this, "pendingFrames", /* @__PURE__ */ new Map());
|
|
338
380
|
}
|
|
339
381
|
/**
|
|
340
382
|
* Process an incoming chunk and return the complete message if all chunks received
|
|
341
383
|
*/
|
|
342
|
-
processChunk(e, t,
|
|
343
|
-
if (
|
|
344
|
-
return
|
|
384
|
+
processChunk(e, t, a, o) {
|
|
385
|
+
if (a === 1)
|
|
386
|
+
return o;
|
|
345
387
|
this.pendingFrames.has(e) || this.pendingFrames.set(e, {
|
|
346
388
|
chunks: /* @__PURE__ */ new Map(),
|
|
347
|
-
totalChunks:
|
|
389
|
+
totalChunks: a
|
|
348
390
|
});
|
|
349
391
|
const n = this.pendingFrames.get(e);
|
|
350
|
-
if (n.chunks.set(t,
|
|
351
|
-
const i = Array.from(n.chunks.values()).reduce((
|
|
352
|
-
let
|
|
353
|
-
for (let
|
|
354
|
-
const
|
|
355
|
-
|
|
392
|
+
if (n.chunks.set(t, o), n.chunks.size === a) {
|
|
393
|
+
const i = Array.from(n.chunks.values()).reduce((s, u) => s + u.length, 0), l = new Uint8Array(i);
|
|
394
|
+
let c = 0;
|
|
395
|
+
for (let s = 0; s < a; s++) {
|
|
396
|
+
const u = n.chunks.get(s);
|
|
397
|
+
l.set(u, c), c += u.length;
|
|
356
398
|
}
|
|
357
|
-
return this.pendingFrames.delete(e),
|
|
399
|
+
return this.pendingFrames.delete(e), l;
|
|
358
400
|
}
|
|
359
401
|
return null;
|
|
360
402
|
}
|
|
@@ -365,113 +407,139 @@ class T {
|
|
|
365
407
|
this.pendingFrames.clear();
|
|
366
408
|
}
|
|
367
409
|
}
|
|
368
|
-
function
|
|
369
|
-
const e = new DataView(
|
|
370
|
-
return { frameId: t, chunkIndex:
|
|
410
|
+
function L(r) {
|
|
411
|
+
const e = new DataView(r), t = e.getUint32(0, !0), a = e.getUint32(4, !0), o = e.getUint32(8, !0), n = new Uint8Array(r, $);
|
|
412
|
+
return { frameId: t, chunkIndex: a, totalChunks: o, payload: n };
|
|
371
413
|
}
|
|
372
|
-
async function
|
|
373
|
-
if (
|
|
414
|
+
async function M(r, e = 6e3) {
|
|
415
|
+
if (r.iceGatheringState === "complete") return;
|
|
374
416
|
let t = !1;
|
|
375
|
-
const
|
|
376
|
-
|
|
417
|
+
const a = (o) => {
|
|
418
|
+
o.candidate && o.candidate.type === "srflx" && (t = !0);
|
|
377
419
|
};
|
|
378
|
-
|
|
420
|
+
r.addEventListener("icecandidate", a);
|
|
379
421
|
try {
|
|
380
422
|
await Promise.race([
|
|
381
|
-
new Promise((
|
|
423
|
+
new Promise((o) => {
|
|
382
424
|
const n = () => {
|
|
383
|
-
|
|
425
|
+
r.iceGatheringState === "complete" && (r.removeEventListener("icegatheringstatechange", n), o());
|
|
384
426
|
};
|
|
385
|
-
|
|
427
|
+
r.addEventListener("icegatheringstatechange", n);
|
|
386
428
|
}),
|
|
387
|
-
new Promise((
|
|
429
|
+
new Promise((o, n) => {
|
|
388
430
|
setTimeout(() => {
|
|
389
|
-
t ?
|
|
431
|
+
t ? o() : (console.error("[ICE] timeout with NO srflx candidate! Connection may fail."), n(new Error("ICE gathering timeout without srflx candidate")));
|
|
390
432
|
}, e);
|
|
391
433
|
})
|
|
392
434
|
]);
|
|
393
435
|
} finally {
|
|
394
|
-
|
|
436
|
+
r.removeEventListener("icecandidate", a);
|
|
395
437
|
}
|
|
396
438
|
}
|
|
397
|
-
function
|
|
439
|
+
function z(r) {
|
|
398
440
|
return new Promise((e) => {
|
|
399
|
-
|
|
441
|
+
r.addEventListener("track", (t) => {
|
|
400
442
|
t.streams && t.streams[0] && e(t.streams[0]);
|
|
401
443
|
});
|
|
402
444
|
});
|
|
403
445
|
}
|
|
404
|
-
const
|
|
446
|
+
const H = [
|
|
405
447
|
{ urls: ["stun:stun.l.google.com:19302"] }
|
|
406
448
|
];
|
|
407
|
-
async function
|
|
408
|
-
|
|
409
|
-
|
|
449
|
+
async function G(r, e, t) {
|
|
450
|
+
if (!r && !e || r && e)
|
|
451
|
+
throw new Error("Either localStream or file must be provided, but not both");
|
|
452
|
+
const a = t ?? H, o = new RTCPeerConnection({
|
|
453
|
+
iceServers: a
|
|
410
454
|
});
|
|
411
455
|
try {
|
|
412
|
-
|
|
456
|
+
o.addTransceiver("video", { direction: "recvonly" });
|
|
413
457
|
} catch (s) {
|
|
414
458
|
console.warn("[RFWebRTC] Could not add transceiver:", s);
|
|
415
459
|
}
|
|
416
|
-
|
|
460
|
+
r && r.getVideoTracks().forEach((s) => {
|
|
417
461
|
try {
|
|
418
462
|
s.contentHint = "detail";
|
|
419
463
|
} catch {
|
|
420
464
|
}
|
|
421
|
-
|
|
465
|
+
o.addTrack(s, r);
|
|
422
466
|
});
|
|
423
|
-
const
|
|
467
|
+
const n = z(o), i = o.createDataChannel("inference", {
|
|
424
468
|
ordered: !0
|
|
425
|
-
})
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
469
|
+
});
|
|
470
|
+
let l;
|
|
471
|
+
e && (l = o.createDataChannel("video_upload"));
|
|
472
|
+
const c = await o.createOffer();
|
|
473
|
+
return await o.setLocalDescription(c), await M(o), {
|
|
474
|
+
pc: o,
|
|
475
|
+
offer: o.localDescription,
|
|
476
|
+
remoteStreamPromise: n,
|
|
477
|
+
dataChannel: i,
|
|
478
|
+
uploadChannel: l
|
|
431
479
|
};
|
|
432
480
|
}
|
|
433
|
-
async function
|
|
434
|
-
const e =
|
|
481
|
+
async function J(r) {
|
|
482
|
+
const e = r.getSenders().find((a) => a.track && a.track.kind === "video");
|
|
435
483
|
if (!e) return;
|
|
436
484
|
const t = e.getParameters();
|
|
437
485
|
t.encodings = t.encodings || [{}], t.encodings[0].scaleResolutionDownBy = 1;
|
|
438
486
|
try {
|
|
439
487
|
await e.setParameters(t);
|
|
440
|
-
} catch (
|
|
441
|
-
console.warn("[RFWebRTC] Failed to set encoding parameters:",
|
|
488
|
+
} catch (a) {
|
|
489
|
+
console.warn("[RFWebRTC] Failed to set encoding parameters:", a);
|
|
442
490
|
}
|
|
443
491
|
}
|
|
444
|
-
|
|
492
|
+
function Z(r, e = 3e4) {
|
|
493
|
+
return new Promise((t, a) => {
|
|
494
|
+
if (r.readyState === "open") {
|
|
495
|
+
t();
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
const o = () => {
|
|
499
|
+
r.removeEventListener("open", o), r.removeEventListener("error", n), clearTimeout(i), t();
|
|
500
|
+
}, n = () => {
|
|
501
|
+
r.removeEventListener("open", o), r.removeEventListener("error", n), clearTimeout(i), a(new Error("Datachannel error"));
|
|
502
|
+
}, i = setTimeout(() => {
|
|
503
|
+
r.removeEventListener("open", o), r.removeEventListener("error", n), a(new Error("Datachannel open timeout"));
|
|
504
|
+
}, e);
|
|
505
|
+
r.addEventListener("open", o), r.addEventListener("error", n);
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
class O {
|
|
445
509
|
/** @private */
|
|
446
|
-
constructor(e, t,
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
510
|
+
constructor(e, t, a, o, n, i) {
|
|
511
|
+
d(this, "pc");
|
|
512
|
+
d(this, "_localStream");
|
|
513
|
+
d(this, "remoteStreamPromise");
|
|
514
|
+
d(this, "pipelineId");
|
|
515
|
+
d(this, "apiKey");
|
|
516
|
+
d(this, "dataChannel");
|
|
517
|
+
d(this, "reassembler");
|
|
518
|
+
d(this, "uploadChannel");
|
|
519
|
+
d(this, "uploader");
|
|
520
|
+
d(this, "onComplete");
|
|
521
|
+
this.pc = e, this._localStream = i == null ? void 0 : i.localStream, this.remoteStreamPromise = t, this.pipelineId = a, this.apiKey = o, this.dataChannel = n, this.reassembler = new P(), this.uploadChannel = i == null ? void 0 : i.uploadChannel, this.onComplete = i == null ? void 0 : i.onComplete, this.dataChannel.binaryType = "arraybuffer";
|
|
522
|
+
const l = i == null ? void 0 : i.onData;
|
|
523
|
+
l && (this.dataChannel.addEventListener("message", (c) => {
|
|
456
524
|
try {
|
|
457
|
-
if (
|
|
458
|
-
const { frameId:
|
|
459
|
-
if (
|
|
460
|
-
const
|
|
461
|
-
|
|
525
|
+
if (c.data instanceof ArrayBuffer) {
|
|
526
|
+
const { frameId: s, chunkIndex: u, totalChunks: h, payload: y } = L(c.data), w = this.reassembler.processChunk(s, u, h, y);
|
|
527
|
+
if (w) {
|
|
528
|
+
const g = new TextDecoder("utf-8").decode(w), v = JSON.parse(g);
|
|
529
|
+
l(v);
|
|
462
530
|
}
|
|
463
531
|
} else {
|
|
464
|
-
const
|
|
465
|
-
s
|
|
532
|
+
const s = JSON.parse(c.data);
|
|
533
|
+
l(s);
|
|
466
534
|
}
|
|
467
|
-
} catch (
|
|
468
|
-
console.error("[RFWebRTC] Failed to parse data channel message:",
|
|
535
|
+
} catch (s) {
|
|
536
|
+
console.error("[RFWebRTC] Failed to parse data channel message:", s);
|
|
469
537
|
}
|
|
470
|
-
}), this.dataChannel.addEventListener("error", (
|
|
471
|
-
console.error("[RFWebRTC] Data channel error:",
|
|
472
|
-
}), this.dataChannel.addEventListener("close", () => {
|
|
473
|
-
this.reassembler.clear();
|
|
474
|
-
})
|
|
538
|
+
}), this.dataChannel.addEventListener("error", (c) => {
|
|
539
|
+
console.error("[RFWebRTC] Data channel error:", c);
|
|
540
|
+
})), this.dataChannel.addEventListener("close", () => {
|
|
541
|
+
this.reassembler.clear(), this.onComplete && this.onComplete();
|
|
542
|
+
});
|
|
475
543
|
}
|
|
476
544
|
/**
|
|
477
545
|
* Get the remote stream (processed video from Roboflow)
|
|
@@ -491,13 +559,15 @@ class F {
|
|
|
491
559
|
/**
|
|
492
560
|
* Get the local stream (original camera)
|
|
493
561
|
*
|
|
494
|
-
* @returns The local MediaStream
|
|
562
|
+
* @returns The local MediaStream, or undefined if using file upload mode
|
|
495
563
|
*
|
|
496
564
|
* @example
|
|
497
565
|
* ```typescript
|
|
498
566
|
* const conn = await useStream({ ... });
|
|
499
567
|
* const localStream = conn.localStream();
|
|
500
|
-
*
|
|
568
|
+
* if (localStream) {
|
|
569
|
+
* videoElement.srcObject = localStream;
|
|
570
|
+
* }
|
|
501
571
|
* ```
|
|
502
572
|
*/
|
|
503
573
|
localStream() {
|
|
@@ -507,7 +577,7 @@ class F {
|
|
|
507
577
|
* Cleanup and close connection
|
|
508
578
|
*
|
|
509
579
|
* Terminates the pipeline on Roboflow, closes the peer connection,
|
|
510
|
-
* and stops the local media stream.
|
|
580
|
+
* and stops the local media stream (if applicable).
|
|
511
581
|
*
|
|
512
582
|
* @returns Promise that resolves when cleanup is complete
|
|
513
583
|
*
|
|
@@ -519,7 +589,39 @@ class F {
|
|
|
519
589
|
* ```
|
|
520
590
|
*/
|
|
521
591
|
async cleanup() {
|
|
522
|
-
|
|
592
|
+
if (this.uploader && this.uploader.cancel(), this.reassembler.clear(), this.pipelineId && this.apiKey)
|
|
593
|
+
try {
|
|
594
|
+
await b.init({ apiKey: this.apiKey }).terminatePipeline({ pipelineId: this.pipelineId });
|
|
595
|
+
} catch (e) {
|
|
596
|
+
console.warn("[RFWebRTC] Failed to terminate pipeline:", e);
|
|
597
|
+
}
|
|
598
|
+
this.pc && this.pc.connectionState !== "closed" && this.pc.close(), this._localStream && W(this._localStream);
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Start uploading a file through the connection
|
|
602
|
+
*
|
|
603
|
+
* @param file - The file to upload
|
|
604
|
+
* @param onProgress - Optional callback for progress updates (bytesUploaded, totalBytes)
|
|
605
|
+
* @returns Promise that resolves when upload is complete
|
|
606
|
+
* @throws Error if no upload channel is available
|
|
607
|
+
*
|
|
608
|
+
* @example
|
|
609
|
+
* ```typescript
|
|
610
|
+
* await connection.startUpload(videoFile, (uploaded, total) => {
|
|
611
|
+
* console.log(`Upload progress: ${(uploaded / total * 100).toFixed(1)}%`);
|
|
612
|
+
* });
|
|
613
|
+
* ```
|
|
614
|
+
*/
|
|
615
|
+
async startUpload(e, t) {
|
|
616
|
+
if (!this.uploadChannel)
|
|
617
|
+
throw new Error("No upload channel available. This connection was not created for file uploads.");
|
|
618
|
+
await Z(this.uploadChannel), this.uploader = new I(e, this.uploadChannel), await this.uploader.upload(t);
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Cancel any ongoing file upload
|
|
622
|
+
*/
|
|
623
|
+
cancelUpload() {
|
|
624
|
+
this.uploader && this.uploader.cancel();
|
|
523
625
|
}
|
|
524
626
|
/**
|
|
525
627
|
* Reconfigure pipeline outputs at runtime
|
|
@@ -574,59 +676,117 @@ class F {
|
|
|
574
676
|
}
|
|
575
677
|
}
|
|
576
678
|
}
|
|
577
|
-
async function
|
|
578
|
-
source:
|
|
679
|
+
async function D({
|
|
680
|
+
source: r,
|
|
579
681
|
connector: e,
|
|
580
682
|
wrtcParams: t,
|
|
581
|
-
onData:
|
|
582
|
-
|
|
683
|
+
onData: a,
|
|
684
|
+
onComplete: o,
|
|
685
|
+
onFileUploadProgress: n,
|
|
686
|
+
options: i = {}
|
|
583
687
|
}) {
|
|
584
|
-
var
|
|
688
|
+
var k;
|
|
585
689
|
if (!e || typeof e.connectWrtc != "function")
|
|
586
690
|
throw new Error("connector must have a connectWrtc method");
|
|
587
|
-
const
|
|
588
|
-
let
|
|
589
|
-
if ((!
|
|
691
|
+
const l = r instanceof File, c = l ? void 0 : r, s = l ? r : void 0;
|
|
692
|
+
let u = t.iceServers;
|
|
693
|
+
if ((!u || u.length === 0) && e.getIceServers)
|
|
590
694
|
try {
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
} catch (
|
|
594
|
-
console.warn("[RFWebRTC] Failed to fetch TURN config, using defaults:",
|
|
695
|
+
const f = await e.getIceServers();
|
|
696
|
+
f && f.length > 0 && (u = f, console.log("[RFWebRTC] Using TURN servers from connector"));
|
|
697
|
+
} catch (f) {
|
|
698
|
+
console.warn("[RFWebRTC] Failed to fetch TURN config, using defaults:", f);
|
|
595
699
|
}
|
|
596
|
-
const { pc:
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
700
|
+
const { pc: h, offer: y, remoteStreamPromise: w, dataChannel: R, uploadChannel: g } = await G(
|
|
701
|
+
c,
|
|
702
|
+
s,
|
|
703
|
+
u
|
|
704
|
+
), v = {
|
|
600
705
|
...t,
|
|
601
|
-
iceServers:
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
y
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
706
|
+
iceServers: u,
|
|
707
|
+
realtimeProcessing: t.realtimeProcessing ?? !l
|
|
708
|
+
}, m = await e.connectWrtc(
|
|
709
|
+
{ sdp: y.sdp, type: y.type },
|
|
710
|
+
v
|
|
711
|
+
), p = { sdp: m.sdp, type: m.type };
|
|
712
|
+
if (!(p != null && p.sdp) || !(p != null && p.type))
|
|
713
|
+
throw console.error("[RFWebRTC] Invalid answer from server:", m), new Error("connector.connectWrtc must return answer with sdp and type");
|
|
714
|
+
const S = ((k = m == null ? void 0 : m.context) == null ? void 0 : k.pipeline_id) || null;
|
|
715
|
+
await h.setRemoteDescription(p), await new Promise((f, F) => {
|
|
716
|
+
const _ = () => {
|
|
717
|
+
h.connectionState === "connected" ? (h.removeEventListener("connectionstatechange", _), f()) : h.connectionState === "failed" && (h.removeEventListener("connectionstatechange", _), F(new Error("WebRTC connection failed")));
|
|
612
718
|
};
|
|
613
|
-
|
|
614
|
-
|
|
719
|
+
h.addEventListener("connectionstatechange", _), _(), setTimeout(() => {
|
|
720
|
+
h.removeEventListener("connectionstatechange", _), F(new Error("WebRTC connection timeout after 30s"));
|
|
615
721
|
}, 3e4);
|
|
616
|
-
}),
|
|
617
|
-
const
|
|
618
|
-
|
|
722
|
+
}), c && i.disableInputStreamDownscaling !== !1 && await J(h);
|
|
723
|
+
const C = e._apiKey || null, E = new O(
|
|
724
|
+
h,
|
|
725
|
+
w,
|
|
726
|
+
S,
|
|
727
|
+
C,
|
|
728
|
+
R,
|
|
729
|
+
{
|
|
730
|
+
localStream: c,
|
|
731
|
+
uploadChannel: g,
|
|
732
|
+
onData: a,
|
|
733
|
+
onComplete: o
|
|
734
|
+
}
|
|
735
|
+
);
|
|
736
|
+
return s && g && E.startUpload(s, n).catch((f) => {
|
|
737
|
+
console.error("[RFWebRTC] Upload error:", f);
|
|
738
|
+
}), E;
|
|
739
|
+
}
|
|
740
|
+
async function Q({
|
|
741
|
+
source: r,
|
|
742
|
+
connector: e,
|
|
743
|
+
wrtcParams: t,
|
|
744
|
+
onData: a,
|
|
745
|
+
options: o = {}
|
|
746
|
+
}) {
|
|
747
|
+
if (r instanceof File)
|
|
748
|
+
throw new Error("useStream requires a MediaStream. Use useVideoFile for File uploads.");
|
|
749
|
+
return D({
|
|
750
|
+
source: r,
|
|
751
|
+
connector: e,
|
|
752
|
+
wrtcParams: t,
|
|
753
|
+
onData: a,
|
|
754
|
+
options: o
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
async function X({
|
|
758
|
+
file: r,
|
|
759
|
+
connector: e,
|
|
760
|
+
wrtcParams: t,
|
|
761
|
+
onData: a,
|
|
762
|
+
onUploadProgress: o,
|
|
763
|
+
onComplete: n
|
|
764
|
+
}) {
|
|
765
|
+
return D({
|
|
766
|
+
source: r,
|
|
767
|
+
connector: e,
|
|
768
|
+
wrtcParams: {
|
|
769
|
+
...t,
|
|
770
|
+
realtimeProcessing: t.realtimeProcessing ?? !0
|
|
771
|
+
},
|
|
772
|
+
onData: a,
|
|
773
|
+
onComplete: n,
|
|
774
|
+
onFileUploadProgress: o
|
|
775
|
+
});
|
|
619
776
|
}
|
|
620
|
-
const
|
|
777
|
+
const re = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
621
778
|
__proto__: null,
|
|
622
|
-
ChunkReassembler:
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
779
|
+
ChunkReassembler: P,
|
|
780
|
+
FileUploader: I,
|
|
781
|
+
RFWebRTCConnection: O,
|
|
782
|
+
parseBinaryHeader: L,
|
|
783
|
+
useStream: Q,
|
|
784
|
+
useVideoFile: X
|
|
626
785
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
627
786
|
export {
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
787
|
+
b as InferenceHTTPClient,
|
|
788
|
+
ee as connectors,
|
|
789
|
+
te as streams,
|
|
790
|
+
re as webrtc
|
|
632
791
|
};
|
|
792
|
+
//# sourceMappingURL=index.es.js.map
|