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