@xmaxai/web-sdk 1.0.6 → 1.0.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/README.md +2 -290
- package/dist/{chunk-NWNRFTLE.cjs → chunk-24VPU65T.cjs} +644 -282
- package/dist/chunk-24VPU65T.cjs.map +1 -0
- package/dist/{chunk-PLFWPS5A.js → chunk-JGDZ3RD3.js} +411 -49
- package/dist/chunk-JGDZ3RD3.js.map +1 -0
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -8
- package/dist/index.d.ts +32 -8
- package/dist/index.js +1 -1
- package/dist/react/index.cjs +3 -3
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-NWNRFTLE.cjs.map +0 -1
- package/dist/chunk-PLFWPS5A.js.map +0 -1
|
@@ -205,7 +205,7 @@ var XmaxError = class _XmaxError extends Error {
|
|
|
205
205
|
};
|
|
206
206
|
|
|
207
207
|
// src/Foundation/Runtime/RuntimeInfo.ts
|
|
208
|
-
var XMAX_SDK_VERSION = "1.0.
|
|
208
|
+
var XMAX_SDK_VERSION = "1.0.8";
|
|
209
209
|
var _RuntimeInfo = class _RuntimeInfo {
|
|
210
210
|
/**
|
|
211
211
|
* 序列化为接口传输使用的 snake_case 字段。
|
|
@@ -601,6 +601,7 @@ var RealtimeModel = /* @__PURE__ */ ((RealtimeModel2) => {
|
|
|
601
601
|
RealtimeModel2["x2_0"] = "x2.0";
|
|
602
602
|
RealtimeModel2["x2_0_trtc"] = "x2.0-trtc";
|
|
603
603
|
RealtimeModel2["x2_1_preview"] = "x2.1-preview";
|
|
604
|
+
RealtimeModel2["x2_1_preview_1014"] = "x2.1-preview-1014";
|
|
604
605
|
return RealtimeModel2;
|
|
605
606
|
})(RealtimeModel || {});
|
|
606
607
|
function isKnownModel(model) {
|
|
@@ -615,10 +616,12 @@ function modelDefaults(model) {
|
|
|
615
616
|
var MODEL_DISPLAY_NAMES = {
|
|
616
617
|
["x2.0" /* x2_0 */]: "X2.0",
|
|
617
618
|
["x2.0-trtc" /* x2_0_trtc */]: "X2.1-preview-trtc",
|
|
618
|
-
["x2.1-preview" /* x2_1_preview */]: "X2.1-preview-agora"
|
|
619
|
+
["x2.1-preview" /* x2_1_preview */]: "X2.1-preview-agora",
|
|
620
|
+
["x2.1-preview-1014" /* x2_1_preview_1014 */]: "X2.1-preview-1014"
|
|
619
621
|
};
|
|
620
622
|
var MODEL_BASE_URLS = {
|
|
621
|
-
["x2.1-preview" /* x2_1_preview */]: "https://cloud.xmax.22duck.cn/open/api/v1"
|
|
623
|
+
["x2.1-preview" /* x2_1_preview */]: "https://cloud.xmax.22duck.cn/open/api/v1",
|
|
624
|
+
["x2.1-preview-1014" /* x2_1_preview_1014 */]: "https://dev.xmaxai.com/open/api/v1"
|
|
622
625
|
};
|
|
623
626
|
var RESOLUTION_BUCKETS = {
|
|
624
627
|
["x2.0" /* x2_0 */]: [],
|
|
@@ -629,12 +632,17 @@ var RESOLUTION_BUCKETS = {
|
|
|
629
632
|
["x2.1-preview" /* x2_1_preview */]: [
|
|
630
633
|
{ width: 1024, height: 1920 },
|
|
631
634
|
{ width: 1920, height: 1024 }
|
|
635
|
+
],
|
|
636
|
+
["x2.1-preview-1014" /* x2_1_preview_1014 */]: [
|
|
637
|
+
{ width: 1024, height: 1920 },
|
|
638
|
+
{ width: 1920, height: 1024 }
|
|
632
639
|
]
|
|
633
640
|
};
|
|
634
641
|
var MAXIMUM_INPUT_PIXELS = {
|
|
635
642
|
["x2.0" /* x2_0 */]: 128e4,
|
|
636
643
|
["x2.0-trtc" /* x2_0_trtc */]: 21e5,
|
|
637
|
-
["x2.1-preview" /* x2_1_preview */]: 21e5
|
|
644
|
+
["x2.1-preview" /* x2_1_preview */]: 21e5,
|
|
645
|
+
["x2.1-preview-1014" /* x2_1_preview_1014 */]: 21e5
|
|
638
646
|
};
|
|
639
647
|
function modelDisplayName(model) {
|
|
640
648
|
return isKnownModel(model) ? MODEL_DISPLAY_NAMES[model] : model;
|
|
@@ -666,7 +674,8 @@ function defaultCameraVideoFormat(model) {
|
|
|
666
674
|
var MODEL_RTC_PROVIDERS = {
|
|
667
675
|
["x2.0" /* x2_0 */]: Object.freeze(["vertc" /* vertc */]),
|
|
668
676
|
["x2.0-trtc" /* x2_0_trtc */]: Object.freeze(["trtc" /* trtc */]),
|
|
669
|
-
["x2.1-preview" /* x2_1_preview */]: Object.freeze(["agora" /* agora */])
|
|
677
|
+
["x2.1-preview" /* x2_1_preview */]: Object.freeze(["agora" /* agora */]),
|
|
678
|
+
["x2.1-preview-1014" /* x2_1_preview_1014 */]: Object.freeze(["agora" /* agora */])
|
|
670
679
|
};
|
|
671
680
|
function supportedRtcProviders(model) {
|
|
672
681
|
return isKnownModel(model) ? MODEL_RTC_PROVIDERS[model] : CUSTOM_MODEL_RTC_PROVIDERS;
|
|
@@ -1353,15 +1362,178 @@ var AgoraVideoSender = class {
|
|
|
1353
1362
|
}
|
|
1354
1363
|
throw new XmaxError("RTC_ERROR" /* rtcError */, "Browser did not retain WebRTC video sender parameters");
|
|
1355
1364
|
}
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
+
}
|
|
1366
|
+
};
|
|
1367
|
+
|
|
1368
|
+
// src/Foundation/Media/Camera/CameraRotation.ts
|
|
1369
|
+
function validateCameraRotation(rotation) {
|
|
1370
|
+
if (![0, 90, 180, 270].includes(rotation)) {
|
|
1371
|
+
throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Camera rotation must be 0, 90, 180 or 270");
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
// src/Foundation/Media/Camera/CameraRotationProcessor.ts
|
|
1376
|
+
var CameraRotationProcessor = class {
|
|
1377
|
+
constructor(options) {
|
|
1378
|
+
__publicField(this, "options", options);
|
|
1379
|
+
__publicField(this, "video", document.createElement("video"));
|
|
1380
|
+
__publicField(this, "canvas", document.createElement("canvas"));
|
|
1381
|
+
__publicField(this, "context");
|
|
1382
|
+
__publicField(this, "stream");
|
|
1383
|
+
__publicField(this, "source");
|
|
1384
|
+
__publicField(this, "frameCallback");
|
|
1385
|
+
__publicField(this, "timer");
|
|
1386
|
+
__publicField(this, "cancelPending");
|
|
1387
|
+
__publicField(this, "stopped", false);
|
|
1388
|
+
__publicField(this, "version", 0);
|
|
1389
|
+
__publicField(this, "sourceEnded", () => {
|
|
1390
|
+
this.fail(new XmaxError("MEDIA_ERROR" /* mediaError */, "Camera source ended during rotation"));
|
|
1391
|
+
});
|
|
1392
|
+
this.canvas.width = options.width;
|
|
1393
|
+
this.canvas.height = options.height;
|
|
1394
|
+
const context = this.canvas.getContext("2d", { alpha: false });
|
|
1395
|
+
if (!context || typeof this.canvas.captureStream !== "function") {
|
|
1396
|
+
throw new XmaxError("MEDIA_ERROR" /* mediaError */, "Canvas camera rotation is not supported by this browser");
|
|
1397
|
+
}
|
|
1398
|
+
this.context = context;
|
|
1399
|
+
this.video.muted = true;
|
|
1400
|
+
this.video.autoplay = true;
|
|
1401
|
+
this.video.playsInline = true;
|
|
1402
|
+
}
|
|
1403
|
+
/** 等待第一帧实际绘制成功;换摄像头时复用输出轨,不重建 sender。 */
|
|
1404
|
+
async setSource(source) {
|
|
1405
|
+
if (this.stopped) throw new XmaxError("CANCELLED" /* cancelled */, "Camera rotation has stopped");
|
|
1406
|
+
if (source.readyState === "ended") {
|
|
1407
|
+
this.stop();
|
|
1408
|
+
throw new XmaxError("MEDIA_ERROR" /* mediaError */, "Camera rotation requires a live source");
|
|
1409
|
+
}
|
|
1410
|
+
this.cancelPending?.();
|
|
1411
|
+
this.cancelFrame();
|
|
1412
|
+
this.source?.removeEventListener("ended", this.sourceEnded);
|
|
1413
|
+
const version = ++this.version;
|
|
1414
|
+
this.source = source;
|
|
1415
|
+
source.addEventListener("ended", this.sourceEnded);
|
|
1416
|
+
this.video.srcObject = new MediaStream([source]);
|
|
1417
|
+
try {
|
|
1418
|
+
await new Promise((resolve, reject) => {
|
|
1419
|
+
let settled = false;
|
|
1420
|
+
const finish = (error) => {
|
|
1421
|
+
if (settled) return;
|
|
1422
|
+
settled = true;
|
|
1423
|
+
clearTimeout(timeout);
|
|
1424
|
+
this.cancelPending = void 0;
|
|
1425
|
+
if (error) reject(error);
|
|
1426
|
+
else resolve();
|
|
1427
|
+
};
|
|
1428
|
+
const timeout = setTimeout(() => finish(new XmaxError("TIMEOUT" /* timeout */, "Timed out waiting for a camera rotation frame")), 1e4);
|
|
1429
|
+
this.cancelPending = () => finish(new XmaxError("CANCELLED" /* cancelled */, "Camera rotation source changed"));
|
|
1430
|
+
void this.video.play().then(() => {
|
|
1431
|
+
if (settled || this.stopped || version !== this.version) return;
|
|
1432
|
+
const firstFrame = () => {
|
|
1433
|
+
if (settled || this.stopped || version !== this.version) return;
|
|
1434
|
+
try {
|
|
1435
|
+
if (!this.draw()) {
|
|
1436
|
+
this.schedule(firstFrame);
|
|
1437
|
+
return;
|
|
1438
|
+
}
|
|
1439
|
+
this.stream ?? (this.stream = this.canvas.captureStream(this.options.frameRate));
|
|
1440
|
+
if (!this.stream.getVideoTracks()[0]) throw new Error("Canvas did not produce a video track");
|
|
1441
|
+
this.requestFrame();
|
|
1442
|
+
finish();
|
|
1443
|
+
this.schedule(() => this.render(version));
|
|
1444
|
+
} catch (error) {
|
|
1445
|
+
finish(error);
|
|
1446
|
+
}
|
|
1447
|
+
};
|
|
1448
|
+
this.schedule(firstFrame);
|
|
1449
|
+
}, finish);
|
|
1450
|
+
});
|
|
1451
|
+
if (this.stopped || version !== this.version) {
|
|
1452
|
+
throw new XmaxError("CANCELLED" /* cancelled */, "Camera rotation source changed");
|
|
1453
|
+
}
|
|
1454
|
+
return this.stream.getVideoTracks()[0];
|
|
1455
|
+
} catch (error) {
|
|
1456
|
+
if (version === this.version) this.stop();
|
|
1457
|
+
throw XmaxError.from(error);
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
/** 换设备前解除旧原始轨监听,保留 Canvas 最后一帧及输出轨。 */
|
|
1461
|
+
pauseSource() {
|
|
1462
|
+
++this.version;
|
|
1463
|
+
this.cancelPending?.();
|
|
1464
|
+
this.cancelFrame();
|
|
1465
|
+
this.source?.removeEventListener("ended", this.sourceEnded);
|
|
1466
|
+
this.source = void 0;
|
|
1467
|
+
this.video.pause();
|
|
1468
|
+
this.video.srcObject = null;
|
|
1469
|
+
}
|
|
1470
|
+
stop() {
|
|
1471
|
+
if (this.stopped) return;
|
|
1472
|
+
this.stopped = true;
|
|
1473
|
+
++this.version;
|
|
1474
|
+
this.cancelPending?.();
|
|
1475
|
+
this.cancelFrame();
|
|
1476
|
+
this.source?.removeEventListener("ended", this.sourceEnded);
|
|
1477
|
+
this.source = void 0;
|
|
1478
|
+
this.video.pause();
|
|
1479
|
+
this.video.srcObject = null;
|
|
1480
|
+
this.stream?.getTracks().forEach((track) => track.stop());
|
|
1481
|
+
this.stream = void 0;
|
|
1482
|
+
this.canvas.width = this.canvas.height = 0;
|
|
1483
|
+
}
|
|
1484
|
+
draw() {
|
|
1485
|
+
const { videoWidth: width, videoHeight: height } = this.video;
|
|
1486
|
+
if (this.video.readyState < 2 || !width || !height) return false;
|
|
1487
|
+
const { width: outWidth, height: outHeight, rotation } = this.options;
|
|
1488
|
+
const quarterTurn = rotation === 90 || rotation === 270;
|
|
1489
|
+
const targetWidth = quarterTurn ? outHeight : outWidth;
|
|
1490
|
+
const targetHeight = quarterTurn ? outWidth : outHeight;
|
|
1491
|
+
const scale = Math.max(targetWidth / width, targetHeight / height);
|
|
1492
|
+
const cropWidth = targetWidth / scale, cropHeight = targetHeight / scale;
|
|
1493
|
+
this.context.setTransform(1, 0, 0, 1, outWidth / 2, outHeight / 2);
|
|
1494
|
+
this.context.rotate(rotation * Math.PI / 180);
|
|
1495
|
+
this.context.drawImage(
|
|
1496
|
+
this.video,
|
|
1497
|
+
(width - cropWidth) / 2,
|
|
1498
|
+
(height - cropHeight) / 2,
|
|
1499
|
+
cropWidth,
|
|
1500
|
+
cropHeight,
|
|
1501
|
+
-targetWidth / 2,
|
|
1502
|
+
-targetHeight / 2,
|
|
1503
|
+
targetWidth,
|
|
1504
|
+
targetHeight
|
|
1505
|
+
);
|
|
1506
|
+
return true;
|
|
1507
|
+
}
|
|
1508
|
+
render(version) {
|
|
1509
|
+
if (this.stopped || version !== this.version) return;
|
|
1510
|
+
try {
|
|
1511
|
+
if (this.draw()) this.requestFrame();
|
|
1512
|
+
this.schedule(() => this.render(version));
|
|
1513
|
+
} catch (error) {
|
|
1514
|
+
this.fail(XmaxError.from(error));
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
requestFrame() {
|
|
1518
|
+
const track = this.stream?.getVideoTracks()[0];
|
|
1519
|
+
track?.requestFrame?.();
|
|
1520
|
+
}
|
|
1521
|
+
schedule(callback) {
|
|
1522
|
+
if (typeof this.video.requestVideoFrameCallback === "function") {
|
|
1523
|
+
this.frameCallback = this.video.requestVideoFrameCallback(callback);
|
|
1524
|
+
} else {
|
|
1525
|
+
this.timer = setTimeout(callback, 1e3 / this.options.frameRate);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
cancelFrame() {
|
|
1529
|
+
if (this.frameCallback !== void 0) this.video.cancelVideoFrameCallback(this.frameCallback);
|
|
1530
|
+
clearTimeout(this.timer);
|
|
1531
|
+
this.frameCallback = void 0;
|
|
1532
|
+
this.timer = void 0;
|
|
1533
|
+
}
|
|
1534
|
+
fail(error) {
|
|
1535
|
+
this.stop();
|
|
1536
|
+
this.options.onError(error);
|
|
1365
1537
|
}
|
|
1366
1538
|
};
|
|
1367
1539
|
|
|
@@ -1381,6 +1553,9 @@ var _AgoraRtcManager = class _AgoraRtcManager {
|
|
|
1381
1553
|
__publicField(this, "initialization");
|
|
1382
1554
|
__publicField(this, "lifecycle", 0);
|
|
1383
1555
|
__publicField(this, "camera");
|
|
1556
|
+
__publicField(this, "cameraSource");
|
|
1557
|
+
__publicField(this, "cameraRotation");
|
|
1558
|
+
__publicField(this, "captureVersion", 0);
|
|
1384
1559
|
__publicField(this, "microphone");
|
|
1385
1560
|
__publicField(this, "externalVideo");
|
|
1386
1561
|
__publicField(this, "externalAudio");
|
|
@@ -1474,8 +1649,7 @@ var _AgoraRtcManager = class _AgoraRtcManager {
|
|
|
1474
1649
|
this.clearRoom();
|
|
1475
1650
|
this.client = void 0;
|
|
1476
1651
|
client?.removeAllListeners();
|
|
1477
|
-
this.
|
|
1478
|
-
this.camera?.close();
|
|
1652
|
+
this.releaseCameraCapture();
|
|
1479
1653
|
this.microphone?.close();
|
|
1480
1654
|
this.externalVideo?.close();
|
|
1481
1655
|
this.externalAudio?.close();
|
|
@@ -1499,19 +1673,41 @@ var _AgoraRtcManager = class _AgoraRtcManager {
|
|
|
1499
1673
|
*/
|
|
1500
1674
|
async startCameraCapture(options) {
|
|
1501
1675
|
const client = this.requireClient();
|
|
1502
|
-
if (this.camera || this.externalVideo) {
|
|
1676
|
+
if (this.camera || this.cameraSource || this.externalVideo) {
|
|
1503
1677
|
throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "A local video source is already active");
|
|
1504
1678
|
}
|
|
1679
|
+
const rotation = options.rotation ?? 0;
|
|
1680
|
+
validateCameraRotation(rotation);
|
|
1681
|
+
const version = ++this.captureVersion;
|
|
1682
|
+
let source;
|
|
1505
1683
|
try {
|
|
1506
|
-
const captureSize = rtcOrientedVideoSize(options);
|
|
1507
|
-
|
|
1684
|
+
const captureSize = rtcOrientedVideoSize(rotation === 90 || rotation === 270 ? { width: options.height, height: options.width } : options);
|
|
1685
|
+
source = await this.sdk.createCameraVideoTrack({
|
|
1508
1686
|
facingMode: options.position === "front" /* front */ ? "user" : "environment",
|
|
1509
1687
|
encoderConfig: { width: captureSize.width, height: captureSize.height, frameRate: options.frameRate }
|
|
1510
1688
|
});
|
|
1511
|
-
if (this.client !== client) {
|
|
1512
|
-
|
|
1689
|
+
if (this.client !== client || version !== this.captureVersion) {
|
|
1690
|
+
source.close();
|
|
1513
1691
|
throw this.cancelled();
|
|
1514
1692
|
}
|
|
1693
|
+
this.cameraSource = source;
|
|
1694
|
+
let camera = source;
|
|
1695
|
+
if (rotation !== 0) {
|
|
1696
|
+
const processor = new CameraRotationProcessor({
|
|
1697
|
+
...options,
|
|
1698
|
+
rotation,
|
|
1699
|
+
onError: (error) => this.listener?.onError?.(error)
|
|
1700
|
+
});
|
|
1701
|
+
this.cameraRotation = processor;
|
|
1702
|
+
const output = await processor.setSource(source.getMediaStreamTrack());
|
|
1703
|
+
if (this.client !== client || version !== this.captureVersion) throw this.cancelled();
|
|
1704
|
+
camera = this.sdk.createCustomVideoTrack({
|
|
1705
|
+
mediaStreamTrack: output,
|
|
1706
|
+
width: options.width,
|
|
1707
|
+
height: options.height,
|
|
1708
|
+
frameRate: options.frameRate
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1515
1711
|
this.camera = camera;
|
|
1516
1712
|
this.cameraTransceiverListener = () => {
|
|
1517
1713
|
if (this.client === client && client.localTracks.includes(camera) && this.videoPublishRequested && !this.rejoinTask) {
|
|
@@ -1525,6 +1721,7 @@ var _AgoraRtcManager = class _AgoraRtcManager {
|
|
|
1525
1721
|
camera.on("transceiver-updated", this.cameraTransceiverListener);
|
|
1526
1722
|
return camera.getMediaStreamTrack();
|
|
1527
1723
|
} catch (error) {
|
|
1724
|
+
if (version === this.captureVersion) this.releaseCameraCapture();
|
|
1528
1725
|
throw this.mapError(error, "CAMERA_PERMISSION_DENIED" /* cameraPermissionDenied */);
|
|
1529
1726
|
}
|
|
1530
1727
|
}
|
|
@@ -1532,9 +1729,24 @@ var _AgoraRtcManager = class _AgoraRtcManager {
|
|
|
1532
1729
|
* 切换声网相机设备并返回切换后的原生轨道。
|
|
1533
1730
|
*/
|
|
1534
1731
|
async switchCameraCapture(to) {
|
|
1535
|
-
const
|
|
1536
|
-
|
|
1537
|
-
|
|
1732
|
+
const source = this.requireCamera();
|
|
1733
|
+
const camera = this.camera;
|
|
1734
|
+
this.cameraRotation?.pauseSource();
|
|
1735
|
+
try {
|
|
1736
|
+
await this.run(() => source.setDevice({ facingMode: to === "front" /* front */ ? "user" : "environment" }));
|
|
1737
|
+
} catch (error) {
|
|
1738
|
+
if (this.camera === camera && this.cameraRotation) {
|
|
1739
|
+
await this.cameraRotation.setSource(source.getMediaStreamTrack()).catch((restoreError) => {
|
|
1740
|
+
this.listener?.onError?.(XmaxError.from(restoreError));
|
|
1741
|
+
});
|
|
1742
|
+
}
|
|
1743
|
+
throw error;
|
|
1744
|
+
}
|
|
1745
|
+
if (this.camera !== camera || this.cameraSource !== source) throw this.cancelled();
|
|
1746
|
+
if (this.cameraRotation) {
|
|
1747
|
+
await this.cameraRotation.setSource(source.getMediaStreamTrack());
|
|
1748
|
+
if (this.camera !== camera) throw this.cancelled();
|
|
1749
|
+
}
|
|
1538
1750
|
if (this.videoPublishRequested) await this.restoreCameraSender(camera);
|
|
1539
1751
|
return camera.getMediaStreamTrack();
|
|
1540
1752
|
}
|
|
@@ -1542,17 +1754,25 @@ var _AgoraRtcManager = class _AgoraRtcManager {
|
|
|
1542
1754
|
* 停止采集并释放摄像头。
|
|
1543
1755
|
*/
|
|
1544
1756
|
async stopCameraCapture() {
|
|
1757
|
+
this.releaseCameraCapture();
|
|
1758
|
+
}
|
|
1759
|
+
releaseCameraCapture() {
|
|
1760
|
+
++this.captureVersion;
|
|
1545
1761
|
const camera = this.camera;
|
|
1546
1762
|
this.releaseCameraSender();
|
|
1547
1763
|
this.camera = void 0;
|
|
1548
1764
|
camera?.close();
|
|
1765
|
+
this.cameraRotation?.stop();
|
|
1766
|
+
this.cameraRotation = void 0;
|
|
1767
|
+
if (this.cameraSource !== camera) this.cameraSource?.close();
|
|
1768
|
+
this.cameraSource = void 0;
|
|
1549
1769
|
}
|
|
1550
1770
|
/**
|
|
1551
1771
|
* 将外部原生轨包装为 Agora 本地轨,不申请设备权限;失败时释放本次克隆。
|
|
1552
1772
|
*/
|
|
1553
1773
|
async setExternalMediaTracks(tracks) {
|
|
1554
1774
|
this.requireClient();
|
|
1555
|
-
if (this.camera || this.microphone || this.externalVideo) {
|
|
1775
|
+
if (this.camera || this.cameraSource || this.microphone || this.externalVideo) {
|
|
1556
1776
|
throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Release the current local media before attaching external tracks");
|
|
1557
1777
|
}
|
|
1558
1778
|
if (tracks.videoTrack.kind !== "video" || tracks.videoTrack.readyState !== "live" || tracks.audioTrack && (tracks.audioTrack.kind !== "audio" || tracks.audioTrack.readyState !== "live")) {
|
|
@@ -1592,7 +1812,7 @@ var _AgoraRtcManager = class _AgoraRtcManager {
|
|
|
1592
1812
|
};
|
|
1593
1813
|
ensureActive2();
|
|
1594
1814
|
if (this.videoPublishRequested) {
|
|
1595
|
-
await AgoraVideoSender.apply(camera,
|
|
1815
|
+
await AgoraVideoSender.apply(camera, this.cameraEncodingConfiguration(config), ensureActive2);
|
|
1596
1816
|
}
|
|
1597
1817
|
this.cameraSenderConfiguration = { ...config };
|
|
1598
1818
|
}));
|
|
@@ -1630,9 +1850,12 @@ var _AgoraRtcManager = class _AgoraRtcManager {
|
|
|
1630
1850
|
};
|
|
1631
1851
|
ensureActive2();
|
|
1632
1852
|
const config = this.cameraSenderConfiguration;
|
|
1633
|
-
if (config) await AgoraVideoSender.apply(camera,
|
|
1853
|
+
if (config) await AgoraVideoSender.apply(camera, this.cameraEncodingConfiguration(config), ensureActive2);
|
|
1634
1854
|
});
|
|
1635
1855
|
}
|
|
1856
|
+
cameraEncodingConfiguration(config) {
|
|
1857
|
+
return this.cameraRotation ? config : { ...config, ...rtcOrientedVideoSize(config) };
|
|
1858
|
+
}
|
|
1636
1859
|
releaseCameraSender() {
|
|
1637
1860
|
if (this.cameraTransceiverListener) this.camera?.off("transceiver-updated", this.cameraTransceiverListener);
|
|
1638
1861
|
this.cameraTransceiverListener = void 0;
|
|
@@ -2069,8 +2292,8 @@ var _AgoraRtcManager = class _AgoraRtcManager {
|
|
|
2069
2292
|
* 获取已启动的相机采集。
|
|
2070
2293
|
*/
|
|
2071
2294
|
requireCamera() {
|
|
2072
|
-
if (!this.camera) throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Camera capture is not running");
|
|
2073
|
-
return this.
|
|
2295
|
+
if (!this.cameraSource || !this.camera) throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Camera capture is not running");
|
|
2296
|
+
return this.cameraSource;
|
|
2074
2297
|
}
|
|
2075
2298
|
/**
|
|
2076
2299
|
* 获取当前可编码、发布的视频轨,不要求来源为摄像头。
|
|
@@ -2509,6 +2732,9 @@ var TrtcRtcManager = class {
|
|
|
2509
2732
|
* @throws 引擎未初始化、已在采集、设备不可用或权限被拒绝时抛出错误。
|
|
2510
2733
|
*/
|
|
2511
2734
|
async startCameraCapture(options) {
|
|
2735
|
+
if (options.rotation !== void 0 && options.rotation !== 0) {
|
|
2736
|
+
throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Camera rotation currently requires Agora");
|
|
2737
|
+
}
|
|
2512
2738
|
const engine = this.requireEngine();
|
|
2513
2739
|
if (this.isCapturing || this.externalVideo) {
|
|
2514
2740
|
throw new XmaxError(
|
|
@@ -3155,6 +3381,9 @@ var VeRtcManager = class {
|
|
|
3155
3381
|
}
|
|
3156
3382
|
}
|
|
3157
3383
|
async startCameraCapture(options) {
|
|
3384
|
+
if (options.rotation !== void 0 && options.rotation !== 0) {
|
|
3385
|
+
throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Camera rotation currently requires Agora");
|
|
3386
|
+
}
|
|
3158
3387
|
const engine = this.requireEngine();
|
|
3159
3388
|
if (this.camera || this.externalVideo) {
|
|
3160
3389
|
throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Local video is already prepared");
|
|
@@ -3991,6 +4220,7 @@ var _CameraController = class _CameraController {
|
|
|
3991
4220
|
*
|
|
3992
4221
|
* @param options.videoFormat 期望的输出尺寸、帧率和编码配置;尺寸按模型规则调整。
|
|
3993
4222
|
* @param options.position 首次启动时使用的摄像头位置。
|
|
4223
|
+
* @param options.rotation 输入帧顺时针旋转角度,默认 0;videoFormat 为旋转后的规格,目前仅 Agora 支持非零角度。
|
|
3994
4224
|
* @param options.useMicrophone 是否申请麦克风权限并允许实时连接时启动音频采集。
|
|
3995
4225
|
* @param options.enableFrameValidation 是否在发布前等待相机预热(固定 200ms),默认 true;false 跳过等待。
|
|
3996
4226
|
* @returns 包含本地相机视频轨道的媒体流。
|
|
@@ -4003,6 +4233,7 @@ var _CameraController = class _CameraController {
|
|
|
4003
4233
|
"Stop the current local camera stream before creating another one"
|
|
4004
4234
|
);
|
|
4005
4235
|
}
|
|
4236
|
+
validateCameraRotation(options.rotation ?? 0);
|
|
4006
4237
|
const resolvedFormat = this.resolveVideoFormat(options.videoFormat);
|
|
4007
4238
|
const track = new RealtimeVideoTrack({
|
|
4008
4239
|
id: _CameraController.localVideoTrackID,
|
|
@@ -4025,7 +4256,8 @@ var _CameraController = class _CameraController {
|
|
|
4025
4256
|
width: resolvedFormat.width,
|
|
4026
4257
|
height: resolvedFormat.height,
|
|
4027
4258
|
frameRate: resolvedFormat.fps,
|
|
4028
|
-
position: options.position
|
|
4259
|
+
position: options.position,
|
|
4260
|
+
...options.rotation !== void 0 ? { rotation: options.rotation } : {}
|
|
4029
4261
|
});
|
|
4030
4262
|
track.mediaStreamTrack = mediaTrack;
|
|
4031
4263
|
this.previewStream = new MediaStream([mediaTrack]);
|
|
@@ -4610,6 +4842,46 @@ var LocalVideoController = class {
|
|
|
4610
4842
|
}
|
|
4611
4843
|
};
|
|
4612
4844
|
|
|
4845
|
+
// src/Media/Video/ExternalVideoController.ts
|
|
4846
|
+
var ExternalVideoController = class {
|
|
4847
|
+
constructor(onError) {
|
|
4848
|
+
__publicField(this, "onError", onError);
|
|
4849
|
+
__publicField(this, "track");
|
|
4850
|
+
__publicField(this, "onEnded", () => {
|
|
4851
|
+
this.onError(new XmaxError("MEDIA_ERROR" /* mediaError */, "External video source ended"));
|
|
4852
|
+
});
|
|
4853
|
+
}
|
|
4854
|
+
get currentTrack() {
|
|
4855
|
+
return this.track;
|
|
4856
|
+
}
|
|
4857
|
+
create(options, mediaService) {
|
|
4858
|
+
if (this.track) throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Close the current external video first");
|
|
4859
|
+
const { videoTrack, videoFormat } = options;
|
|
4860
|
+
if (!videoTrack || videoTrack.kind !== "video" || videoTrack.readyState !== "live" || typeof videoTrack.clone !== "function") {
|
|
4861
|
+
throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "External video requires a live video MediaStreamTrack");
|
|
4862
|
+
}
|
|
4863
|
+
videoFormat.validate();
|
|
4864
|
+
const size = mediaService.resolveModelInputSize(videoFormat);
|
|
4865
|
+
const format = videoFormat.resized(size.width, size.height);
|
|
4866
|
+
const nativeTrack = videoTrack.clone();
|
|
4867
|
+
if (nativeTrack.readyState !== "live") {
|
|
4868
|
+
nativeTrack.stop();
|
|
4869
|
+
throw new XmaxError("MEDIA_ERROR" /* mediaError */, "External video source ended during setup");
|
|
4870
|
+
}
|
|
4871
|
+
const track = new RealtimeVideoTrack({ id: "external-video", videoFormat: format });
|
|
4872
|
+
track.mediaStreamTrack = nativeTrack;
|
|
4873
|
+
nativeTrack.addEventListener("ended", this.onEnded);
|
|
4874
|
+
this.track = track;
|
|
4875
|
+
return new RealtimeMediaStream({ id: "stream0" /* local */, videoTrack: track });
|
|
4876
|
+
}
|
|
4877
|
+
stop() {
|
|
4878
|
+
const nativeTrack = this.track?.mediaStreamTrack;
|
|
4879
|
+
this.track = void 0;
|
|
4880
|
+
nativeTrack?.removeEventListener("ended", this.onEnded);
|
|
4881
|
+
nativeTrack?.stop();
|
|
4882
|
+
}
|
|
4883
|
+
};
|
|
4884
|
+
|
|
4613
4885
|
// src/Service/Realtime/RealtimeContext.ts
|
|
4614
4886
|
var RealtimeContext = class {
|
|
4615
4887
|
/**
|
|
@@ -5237,6 +5509,7 @@ var RoomEvent = class _RoomEvent {
|
|
|
5237
5509
|
static generationParameters(options) {
|
|
5238
5510
|
return {
|
|
5239
5511
|
model: "default",
|
|
5512
|
+
face_replace: options.faceRestoration ?? false,
|
|
5240
5513
|
size: [options.videoFormat.width, options.videoFormat.height],
|
|
5241
5514
|
target_size: options.targetSize ? [
|
|
5242
5515
|
Math.round(options.targetSize.width),
|
|
@@ -5607,12 +5880,19 @@ var RoomController = class {
|
|
|
5607
5880
|
/**
|
|
5608
5881
|
* 发送生成开始信令。
|
|
5609
5882
|
*
|
|
5883
|
+
* @param options.faceRestoration 是否回贴原始人脸,默认 false。
|
|
5884
|
+
* @param options.mirror 是否由后端镜像输入画面,默认 false。
|
|
5885
|
+
* @param options.taskID 当前生成任务标识。
|
|
5886
|
+
* @param options.videoFormat 模型生成使用的视频规格。
|
|
5887
|
+
* @param options.targetSize 服务端生成后的回传尺寸,缺省使用生成尺寸。
|
|
5888
|
+
* @param options.context 本次生成条件。
|
|
5610
5889
|
* @throws 房间未就绪或信令发送失败时抛出错误。
|
|
5611
5890
|
*/
|
|
5612
5891
|
startGeneration(options) {
|
|
5613
5892
|
return this.send(
|
|
5614
5893
|
RoomEvent.start({
|
|
5615
5894
|
mirror: options.mirror ?? false,
|
|
5895
|
+
faceRestoration: options.faceRestoration ?? false,
|
|
5616
5896
|
userID: this.requireUserID(),
|
|
5617
5897
|
taskID: options.taskID,
|
|
5618
5898
|
videoFormat: options.videoFormat,
|
|
@@ -5624,11 +5904,17 @@ var RoomController = class {
|
|
|
5624
5904
|
/**
|
|
5625
5905
|
* 发送生成条件变更信令。
|
|
5626
5906
|
*
|
|
5907
|
+
* @param options.faceRestoration 是否回贴原始人脸,默认 false。
|
|
5908
|
+
* @param options.taskID 当前生成任务标识。
|
|
5909
|
+
* @param options.videoFormat 模型生成使用的视频规格。
|
|
5910
|
+
* @param options.targetSize 服务端生成后的回传尺寸。
|
|
5911
|
+
* @param options.context 本次生成条件。
|
|
5627
5912
|
* @throws 房间未就绪或信令发送失败时抛出错误。
|
|
5628
5913
|
*/
|
|
5629
5914
|
changeGenerationCondition(options) {
|
|
5630
5915
|
return this.send(
|
|
5631
5916
|
RoomEvent.changeCondition({
|
|
5917
|
+
faceRestoration: options.faceRestoration ?? false,
|
|
5632
5918
|
userID: this.requireUserID(),
|
|
5633
5919
|
taskID: options.taskID,
|
|
5634
5920
|
videoFormat: options.videoFormat,
|
|
@@ -6162,6 +6448,7 @@ var StreamController = class {
|
|
|
6162
6448
|
const sending = this.roomController.startGeneration({
|
|
6163
6449
|
taskID,
|
|
6164
6450
|
mirror: options.mirror ?? false,
|
|
6451
|
+
faceRestoration: options.faceRestoration ?? false,
|
|
6165
6452
|
videoFormat: options.videoFormat,
|
|
6166
6453
|
targetSize: options.targetSize,
|
|
6167
6454
|
context: options.context
|
|
@@ -6222,6 +6509,7 @@ var StreamController = class {
|
|
|
6222
6509
|
try {
|
|
6223
6510
|
await this.roomController.changeGenerationCondition({
|
|
6224
6511
|
taskID: options.taskID,
|
|
6512
|
+
faceRestoration: options.faceRestoration ?? false,
|
|
6225
6513
|
videoFormat: options.videoFormat,
|
|
6226
6514
|
targetSize: options.targetSize,
|
|
6227
6515
|
context: options.context
|
|
@@ -7406,6 +7694,16 @@ var XmaxRealtimeConnectionManager = class {
|
|
|
7406
7694
|
* 创建会话、配置编码、进房并发布本地媒体,启动心跳后返回远端占位流。
|
|
7407
7695
|
* 进房结束即记录连接耗时;可选 beforePublish 检查阻塞发布,计入连接耗时;
|
|
7408
7696
|
* 发布本地流单独计时。
|
|
7697
|
+
* @param options.localTrack 当前本地媒体轨道。
|
|
7698
|
+
* @param options.remoteVideoFormat 原始生成及远端画面规格,缺省使用本地轨道规格。
|
|
7699
|
+
* @param options.localVideoFormat 发布前实际应用的上行编码格式;与原始生成及远端格式分开。
|
|
7700
|
+
* @param options.onVideoEncoderConfigured 上行编码配置成功后的回调。
|
|
7701
|
+
* @param options.model 创建业务会话使用的模型名称。
|
|
7702
|
+
* @param options.includeLocalAudio 是否发布本地音频。
|
|
7703
|
+
* @param options.ensureCurrent 校验当前操作仍有效。
|
|
7704
|
+
* @param options.onPublished 本地发布完成后的回调。
|
|
7705
|
+
* @param options.beforePublish 发布前的可选异步检查。
|
|
7706
|
+
* @param options.publishLocalMedia 是否发布本地媒体,默认 true;网络视频源传 false。
|
|
7409
7707
|
* @throws 会话、进房、发布失败或操作租约已失效时抛出错误,由上层统一清理。
|
|
7410
7708
|
*/
|
|
7411
7709
|
async connect(options) {
|
|
@@ -7734,6 +8032,15 @@ var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
|
|
|
7734
8032
|
/**
|
|
7735
8033
|
* 发送生成指令并等待确认、远端就绪和音频激活,成功后返回任务标识。
|
|
7736
8034
|
* 条件上下文在生成确认后缓存;启动失败时尽力停止本次任务并保留原始错误。
|
|
8035
|
+
* @param options.videoFormat 模型生成使用的视频规格。
|
|
8036
|
+
* @param options.targetSize 服务端生成后的回传尺寸,缺省使用生成尺寸。
|
|
8037
|
+
* @param options.context 生成条件,缺省复用最近缓存的上下文。
|
|
8038
|
+
* @param options.faceRestoration 是否回贴原始人脸,默认 false。
|
|
8039
|
+
* @param options.mirror 是否由后端水平翻转输入,默认 false;仅用于 start 信令。
|
|
8040
|
+
* @param options.signal 取消本次生成启动的信号。
|
|
8041
|
+
* @param options.ensureCurrent 校验当前操作仍有效。
|
|
8042
|
+
* @param options.waitUntilRemoteReady 等待远端生成流就绪。
|
|
8043
|
+
* @param options.onStartSent start 信令发送成功后的可选异步回调。
|
|
7737
8044
|
*/
|
|
7738
8045
|
async start(options) {
|
|
7739
8046
|
const context = this.validateContext(options.context);
|
|
@@ -7747,6 +8054,7 @@ var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
|
|
|
7747
8054
|
context,
|
|
7748
8055
|
...options.targetSize && { targetSize: options.targetSize },
|
|
7749
8056
|
mirror: options.mirror ?? false,
|
|
8057
|
+
faceRestoration: options.faceRestoration ?? false,
|
|
7750
8058
|
...options.onStartSent && { onStartSent: options.onStartSent }
|
|
7751
8059
|
});
|
|
7752
8060
|
await this.awaitConfirmation(confirmation, options.signal);
|
|
@@ -7764,6 +8072,11 @@ var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
|
|
|
7764
8072
|
}
|
|
7765
8073
|
/**
|
|
7766
8074
|
* 更新活动任务的生成条件;信令发送成功后才覆盖缓存,无任务或上下文时抛错。
|
|
8075
|
+
* @param taskID 当前活动任务标识。
|
|
8076
|
+
* @param options.videoFormat 模型生成使用的视频规格。
|
|
8077
|
+
* @param options.targetSize 服务端生成后的回传尺寸。
|
|
8078
|
+
* @param options.context 生成条件,缺省复用最近缓存的上下文。
|
|
8079
|
+
* @param options.faceRestoration 是否回贴原始人脸,默认 false。
|
|
7767
8080
|
*/
|
|
7768
8081
|
async update(taskID, options) {
|
|
7769
8082
|
const context = options.context ?? this.currentContext;
|
|
@@ -7774,6 +8087,7 @@ var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
|
|
|
7774
8087
|
taskID,
|
|
7775
8088
|
videoFormat: options.videoFormat,
|
|
7776
8089
|
context,
|
|
8090
|
+
faceRestoration: options.faceRestoration ?? false,
|
|
7777
8091
|
...options.targetSize && { targetSize: options.targetSize }
|
|
7778
8092
|
});
|
|
7779
8093
|
if (this.activeTaskID !== taskID) throw RealtimeCoordinator.cancelledError();
|
|
@@ -7885,6 +8199,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
7885
8199
|
__publicField(this, "localVideoController", new LocalVideoController((error) => {
|
|
7886
8200
|
void this.coordinator.terminateWithError(error, 0 /* connection */);
|
|
7887
8201
|
}));
|
|
8202
|
+
__publicField(this, "externalVideoController", new ExternalVideoController((error) => {
|
|
8203
|
+
void this.coordinator.terminateWithError(error, 1 /* all */);
|
|
8204
|
+
}));
|
|
7888
8205
|
__publicField(this, "rtcManager");
|
|
7889
8206
|
/**
|
|
7890
8207
|
* 启动计时与首帧通知
|
|
@@ -7979,7 +8296,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
7979
8296
|
throw new XmaxError("CANCELLED" /* cancelled */, "Adaptive video format update was cancelled");
|
|
7980
8297
|
}
|
|
7981
8298
|
await this.coordinator.run("configuration" /* configuration */, void 0, async (token) => {
|
|
7982
|
-
const track = this.
|
|
8299
|
+
const track = this.currentUplinkTrack;
|
|
7983
8300
|
const state2 = track && this.qualityStates.get(track);
|
|
7984
8301
|
if (!track || !state2) throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Adaptive video baseline is unavailable");
|
|
7985
8302
|
const format = qualityVideoFormat(state2.baseline, change.to);
|
|
@@ -8291,7 +8608,11 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8291
8608
|
*
|
|
8292
8609
|
* 将返回的轨道绑定到预览视图;收到有效帧且视图已绑定后进入 `ready`。
|
|
8293
8610
|
*
|
|
8611
|
+
* @param options.videoFormat 旋转后的目标规格,尺寸按模型规则解析。
|
|
8612
|
+
* @param options.position 前置或后置摄像头。
|
|
8613
|
+
* @param options.useMicrophone 是否采集并发布麦克风音频。
|
|
8294
8614
|
* @param options.enableFrameValidation 是否在发布前等待相机预热(固定 200ms),默认 true;false 跳过等待。
|
|
8615
|
+
* @param options.rotation 输入帧顺时针旋转角度,默认 0;videoFormat 为旋转后的规格,目前仅 Agora 支持非零角度。
|
|
8295
8616
|
* @returns 包含本地相机视频轨道的媒体流。
|
|
8296
8617
|
* @throws 模型不支持相机输入、配置无效、权限或采集启动失败时抛出错误。
|
|
8297
8618
|
*/
|
|
@@ -8339,6 +8660,10 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8339
8660
|
}
|
|
8340
8661
|
/**
|
|
8341
8662
|
* 创建网络视频源并初始化接收端 RTC;预览是否加载成功不影响服务端读取视频。
|
|
8663
|
+
* @param options.url 服务端可直接访问的 HTTP/HTTPS 视频地址。
|
|
8664
|
+
* @param options.videoFormat 模型输入及生成画面规格,不是上行编码参数。
|
|
8665
|
+
* @param options.sampleMethod 服务端采样方式,默认 time。
|
|
8666
|
+
* @param options.onFinish 服务端完成当前任务后的回调,不代表尾帧已播放完。
|
|
8342
8667
|
*/
|
|
8343
8668
|
async createNetworkVideoStream(options) {
|
|
8344
8669
|
return this.coordinator.run("media" /* media */, void 0, async (token) => {
|
|
@@ -8356,6 +8681,10 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8356
8681
|
}
|
|
8357
8682
|
/**
|
|
8358
8683
|
* 创建文件音视频源并准备外部轨道;准备失败时统一释放本次媒体和 RTC 资源。
|
|
8684
|
+
* @param options.file 包含可解码视频的本地 File 或 Blob。
|
|
8685
|
+
* @param options.videoFormat 期望的视频规格;尺寸按宽高比匹配模型档位。
|
|
8686
|
+
* @param options.loop 是否循环播放,默认 true。
|
|
8687
|
+
* @param options.includeAudio 是否发送文件音频,默认 true。
|
|
8359
8688
|
*/
|
|
8360
8689
|
async createLocalVideoStream(options) {
|
|
8361
8690
|
return this.coordinator.run("media" /* media */, void 0, async (token) => {
|
|
@@ -8381,10 +8710,36 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8381
8710
|
});
|
|
8382
8711
|
}
|
|
8383
8712
|
/**
|
|
8384
|
-
*
|
|
8713
|
+
* 接入外部视频轨;不创建采集设备或预览,所有异步失败统一释放 SDK 副本。
|
|
8714
|
+
* @param options.videoTrack 用户自行采集或处理的 live 视频轨,原始轨道归调用方所有。
|
|
8715
|
+
* @param options.videoFormat 期望的上传及生成规格,须符合模型规则;不代表修改输入像素。
|
|
8385
8716
|
*/
|
|
8717
|
+
async createExternalVideoStream(options) {
|
|
8718
|
+
return this.coordinator.run("media" /* media */, void 0, async (token) => {
|
|
8719
|
+
this.ensureNoLocalSource();
|
|
8720
|
+
if (!this.rtcManager.setExternalMediaTracks) {
|
|
8721
|
+
throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "The RTC provider does not support external video streams");
|
|
8722
|
+
}
|
|
8723
|
+
const stream = this.externalVideoController.create(options, this.mediaService);
|
|
8724
|
+
token.setFailureScope(1 /* all */);
|
|
8725
|
+
this.launchTimer.start();
|
|
8726
|
+
this.clearVideoStatistics();
|
|
8727
|
+
await this.rtcManager.initialize();
|
|
8728
|
+
token.ensureCurrent();
|
|
8729
|
+
await this.rtcManager.setExternalMediaTracks({ videoTrack: stream.videoTrack.mediaStreamTrack });
|
|
8730
|
+
token.ensureCurrent();
|
|
8731
|
+
this.generationVideoFormat = stream.videoTrack?.videoFormat;
|
|
8732
|
+
await this.coordinator.commit(new RealtimeState({ connectionState: "Ready" /* ready */ }), token);
|
|
8733
|
+
return stream;
|
|
8734
|
+
});
|
|
8735
|
+
}
|
|
8736
|
+
/** 当前可发布的本地视频轨,不包含服务端直接读取的网络视频。 */
|
|
8737
|
+
get currentUplinkTrack() {
|
|
8738
|
+
return this.externalVideoController.currentTrack ?? this.localVideoController.currentTrack ?? this.cameraController.currentTrack;
|
|
8739
|
+
}
|
|
8740
|
+
/** 当前 Manager 拥有的媒体源轨道。 */
|
|
8386
8741
|
get currentLocalTrack() {
|
|
8387
|
-
return this.
|
|
8742
|
+
return this.currentUplinkTrack ?? this.networkVideoController.currentTrack;
|
|
8388
8743
|
}
|
|
8389
8744
|
/**
|
|
8390
8745
|
* 创建前拒绝覆盖活动源,避免旧预览或连接资源被静默遗留。
|
|
@@ -8405,14 +8760,14 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8405
8760
|
* 应用完整的上行视频格式,成功后更新本地轨道元数据,不改变生成格式或生命周期。
|
|
8406
8761
|
* 复用配置操作准入,避免与建连、切换摄像头或其他配置更新并发。
|
|
8407
8762
|
*
|
|
8408
|
-
* @throws
|
|
8763
|
+
* @throws 无本地相机、外部或文件视频流、参数无效、操作冲突或底层编码更新失败时抛错。
|
|
8409
8764
|
*/
|
|
8410
8765
|
async updateUplinkVideoFormat(videoFormat) {
|
|
8411
8766
|
await this.coordinator.run("configuration" /* configuration */, void 0, async (token) => {
|
|
8412
|
-
const track = this.
|
|
8767
|
+
const track = this.currentUplinkTrack;
|
|
8413
8768
|
const previousFormat = track?.videoFormat;
|
|
8414
8769
|
if (!track || !previousFormat) {
|
|
8415
|
-
throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Local camera or file video stream is not started");
|
|
8770
|
+
throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Local camera, external or file video stream is not started");
|
|
8416
8771
|
}
|
|
8417
8772
|
videoFormat.validate();
|
|
8418
8773
|
this.qualityController?.stop();
|
|
@@ -8438,11 +8793,11 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8438
8793
|
}
|
|
8439
8794
|
/** 手动仅调帧接口,成功后的当前格式将成为新的自适应 L1 基准。 */
|
|
8440
8795
|
async applyUplinkFrameRate(fps, token) {
|
|
8441
|
-
const track = this.
|
|
8796
|
+
const track = this.currentUplinkTrack;
|
|
8442
8797
|
const previous = track?.videoFormat;
|
|
8443
8798
|
const state2 = this.currentState.connectionState;
|
|
8444
8799
|
if (!track || !previous || state2 !== "Connected" /* connected */ && state2 !== "Generating" /* generating */) {
|
|
8445
|
-
throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Frame rate updates require a connected local camera or file stream");
|
|
8800
|
+
throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Frame rate updates require a connected local camera, external or file stream");
|
|
8446
8801
|
}
|
|
8447
8802
|
if (!Number.isFinite(fps) || fps <= 0) {
|
|
8448
8803
|
throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Video frame rate must be greater than zero");
|
|
@@ -8469,7 +8824,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8469
8824
|
downlink.resetSamples();
|
|
8470
8825
|
return;
|
|
8471
8826
|
}
|
|
8472
|
-
const track = this.
|
|
8827
|
+
const track = this.currentUplinkTrack;
|
|
8473
8828
|
if (!track) {
|
|
8474
8829
|
controller.stop();
|
|
8475
8830
|
} else if (!controller.snapshot.active) {
|
|
@@ -8501,7 +8856,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8501
8856
|
* 停止本地相机流并释放本地预览与 RTC 资源。
|
|
8502
8857
|
*/
|
|
8503
8858
|
async stopLocalCameraStream() {
|
|
8504
|
-
if (this.networkVideoController.currentTrack || this.localVideoController.isActive) {
|
|
8859
|
+
if (this.networkVideoController.currentTrack || this.localVideoController.isActive || this.externalVideoController.currentTrack) {
|
|
8505
8860
|
return;
|
|
8506
8861
|
}
|
|
8507
8862
|
await this.coordinator.run(
|
|
@@ -8534,6 +8889,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8534
8889
|
"cameraSwitch" /* cameraSwitch */,
|
|
8535
8890
|
void 0,
|
|
8536
8891
|
async (token) => {
|
|
8892
|
+
if (this.externalVideoController.currentTrack) {
|
|
8893
|
+
throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "External video capture is managed by the caller");
|
|
8894
|
+
}
|
|
8537
8895
|
this.qualityController?.stop();
|
|
8538
8896
|
const stream = await this.cameraController.switchCamera();
|
|
8539
8897
|
token.ensureCurrent();
|
|
@@ -8548,7 +8906,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8548
8906
|
* 启用帧检测时,发布前并行等待相机预热(固定 200ms),避免把黑帧推给 RTC。
|
|
8549
8907
|
* 返回的远端媒体流在生成开始后承载远端生成画面。
|
|
8550
8908
|
*
|
|
8551
|
-
* @param localStream 由当前 Manager
|
|
8909
|
+
* @param localStream 由当前 Manager 创建的摄像头、外部视频轨、本地文件或网络视频源。
|
|
8552
8910
|
* @returns 远端生成结果占位的媒体流。
|
|
8553
8911
|
* @throws 本地流不属于当前 Manager、已有活动连接、会话创建或进房
|
|
8554
8912
|
* 发布失败时抛出错误;失败时自动释放连接资源并恢复本地预览。
|
|
@@ -8575,8 +8933,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8575
8933
|
* 尚未连接时先建立实时连接;已在生成时仅更新生成条件,不重启生成。
|
|
8576
8934
|
* 首次生成必须提供条件上下文,之后缺省时复用最近一次缓存的上下文。
|
|
8577
8935
|
*
|
|
8578
|
-
* @param options.localStream 由当前 Manager
|
|
8936
|
+
* @param options.localStream 由当前 Manager 创建的摄像头、外部视频轨、本地文件或网络视频源。
|
|
8579
8937
|
* @param options.context 本次生成使用的条件上下文;缺省时复用缓存。
|
|
8938
|
+
* @param options.faceRestoration 是否启用原始人脸回贴,默认 false;正在生成时通过 change_condition 更新。
|
|
8580
8939
|
* @returns 承载远端生成画面的媒体流。
|
|
8581
8940
|
* @throws 本地流不属于当前 Manager、缺少可用的条件上下文、信令发送
|
|
8582
8941
|
* 或生成确认失败时抛出错误;失败时自动停止生成任务并释放连接资源。
|
|
@@ -8604,6 +8963,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8604
8963
|
if (currentState.connectionState === "Generating" /* generating */) {
|
|
8605
8964
|
await this.generationManager.update(currentState.taskID, {
|
|
8606
8965
|
videoFormat,
|
|
8966
|
+
faceRestoration: options.faceRestoration ?? false,
|
|
8607
8967
|
targetSize: this.downlinkQualityController ? this.downlinkQualityState?.size : void 0,
|
|
8608
8968
|
context: options.context
|
|
8609
8969
|
});
|
|
@@ -8619,7 +8979,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8619
8979
|
if (currentState.connectionState !== "Connected" /* connected */) {
|
|
8620
8980
|
await this.performConnect(options.localStream, token);
|
|
8621
8981
|
}
|
|
8622
|
-
return this.performStartGeneration(token, videoFormat, context);
|
|
8982
|
+
return this.performStartGeneration(token, videoFormat, context, options.faceRestoration ?? false);
|
|
8623
8983
|
}
|
|
8624
8984
|
);
|
|
8625
8985
|
}
|
|
@@ -8678,7 +9038,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8678
9038
|
if (token.signal.aborted) abortWarmup();
|
|
8679
9039
|
try {
|
|
8680
9040
|
let beforePublish;
|
|
8681
|
-
if (
|
|
9041
|
+
if (localTrack === this.cameraController.currentTrack && this.cameraController.isFrameValidationEnabled) {
|
|
8682
9042
|
const cameraReady = this.cameraController.waitForValidCameraFrame(warmupController.signal).then(() => {
|
|
8683
9043
|
token.ensureCurrent();
|
|
8684
9044
|
});
|
|
@@ -8700,7 +9060,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8700
9060
|
this.qualityHistory?.write("uplink", initialQuality.level);
|
|
8701
9061
|
},
|
|
8702
9062
|
model: this.options.model,
|
|
8703
|
-
includeLocalAudio: this.localVideoController.isActive ? this.localVideoController.audioTrack !== void 0 :
|
|
9063
|
+
includeLocalAudio: this.localVideoController.isActive ? this.localVideoController.audioTrack !== void 0 : localTrack === this.cameraController.currentTrack && this.cameraController.useMicrophone,
|
|
8704
9064
|
publishLocalMedia: !this.networkVideoController.currentTrack,
|
|
8705
9065
|
ensureCurrent: () => token.ensureCurrent(),
|
|
8706
9066
|
onPublished: () => {
|
|
@@ -8724,7 +9084,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8724
9084
|
/**
|
|
8725
9085
|
* 编排插帧、首帧计时和生成状态,任务与确认由生成管理器负责。
|
|
8726
9086
|
*/
|
|
8727
|
-
async performStartGeneration(token, videoFormat, context) {
|
|
9087
|
+
async performStartGeneration(token, videoFormat, context, faceRestoration) {
|
|
8728
9088
|
token.setFailureScope(0 /* connection */);
|
|
8729
9089
|
const initialLevel = this.qualityHistory?.read("downlink") ?? 1;
|
|
8730
9090
|
const targetSize = this.downlinkQualityController ? qualityVideoSize(videoFormat, initialLevel) : void 0;
|
|
@@ -8737,6 +9097,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8737
9097
|
videoFormat,
|
|
8738
9098
|
targetSize,
|
|
8739
9099
|
context,
|
|
9100
|
+
faceRestoration,
|
|
8740
9101
|
mirror: this.currentLocalTrack === this.cameraController.currentTrack && this.cameraController.currentTrack?.position === "front" /* front */,
|
|
8741
9102
|
signal: token.signal,
|
|
8742
9103
|
ensureCurrent: () => token.ensureCurrent(),
|
|
@@ -8801,12 +9162,12 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8801
9162
|
await this.generationManager.reset(taskID);
|
|
8802
9163
|
const sessionID = await this.connectionManager.disconnect();
|
|
8803
9164
|
if (scope === 1 /* all */) {
|
|
8804
|
-
const
|
|
9165
|
+
const hadExternalSource = this.networkVideoController.currentTrack !== void 0 || this.localVideoController.isActive || this.externalVideoController.currentTrack !== void 0;
|
|
8805
9166
|
this.networkVideoController.stop();
|
|
8806
9167
|
this.generationVideoFormat = void 0;
|
|
8807
9168
|
this.disposePermissionWatch();
|
|
8808
9169
|
try {
|
|
8809
|
-
if (
|
|
9170
|
+
if (hadExternalSource) {
|
|
8810
9171
|
await this.rtcManager.destroy();
|
|
8811
9172
|
} else {
|
|
8812
9173
|
await this.cameraController.stopLocalCameraStream();
|
|
@@ -8817,6 +9178,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
|
|
|
8817
9178
|
\u2514\u2500 ${XmaxLogger.localized("\u539F\u56E0\uFF1A", "Reason: ")}${XmaxError.from(error).message}`
|
|
8818
9179
|
);
|
|
8819
9180
|
} finally {
|
|
9181
|
+
this.externalVideoController.stop();
|
|
8820
9182
|
if (this.localVideoController.isActive) {
|
|
8821
9183
|
await this.localVideoController.stop();
|
|
8822
9184
|
}
|
|
@@ -12467,4 +12829,4 @@ export {
|
|
|
12467
12829
|
XmaxVideoView,
|
|
12468
12830
|
XmaxRealtimeVideoView
|
|
12469
12831
|
};
|
|
12470
|
-
//# sourceMappingURL=chunk-
|
|
12832
|
+
//# sourceMappingURL=chunk-JGDZ3RD3.js.map
|