@mega-yfue/eufy-sdk 0.5.0-beta.73 → 0.5.0-beta.74
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/client/eufy-mega.d.ts +1 -2
- package/dist/index.js +91 -33
- package/dist/index.js.map +4 -4
- package/dist/model/device-family.d.ts +2 -0
- package/dist/transport/p2p/audio-frame.d.ts +14 -0
- package/dist/transport/rtc/command-router.d.ts +4 -0
- package/dist/transport/rtc/live.d.ts +9 -4
- package/package.json +1 -1
|
@@ -456,8 +456,7 @@ export declare class EufyMega extends EventEmitter {
|
|
|
456
456
|
private rtcRouteFor;
|
|
457
457
|
/**
|
|
458
458
|
* The RTC media provider for a station on a media-track route, or a camera attached to a T9000 whose
|
|
459
|
-
* `device_channel` resolves back to it on the station; `undefined` otherwise,
|
|
460
|
-
* a T8N00, whose route carries commands only.
|
|
459
|
+
* `device_channel` resolves back to it on the station; `undefined` otherwise, unless an NVR camera has its own dedicated media call.
|
|
461
460
|
*/
|
|
462
461
|
private rtcMediaFor;
|
|
463
462
|
/**
|
package/dist/index.js
CHANGED
|
@@ -3655,6 +3655,7 @@ var isIndoorPanTiltS350 = (ctx) => has(INDOOR_PT_S350_TYPES, ctx.deviceType);
|
|
|
3655
3655
|
var isHomeBase = (ctx) => has(HOMEBASE_TYPES, ctx.deviceType);
|
|
3656
3656
|
var isStation9000 = (ctx) => ctx.deviceType === DeviceType.STATION_9000;
|
|
3657
3657
|
var isNvrT8N00 = (ctx) => ctx.model === "T8N00";
|
|
3658
|
+
var isNvrMediaCamera = (ctx) => ctx.model === "T8E00" || ctx.model === "T8P00";
|
|
3658
3659
|
var isDoorbellT8223 = (ctx) => ctx.model === "T8223";
|
|
3659
3660
|
var isAiotVacuum = (ctx) => ctx.category !== "eufy_home_tuya";
|
|
3660
3661
|
var isTuyaVacuum = (ctx) => ctx.category === "eufy_home_tuya";
|
|
@@ -18698,16 +18699,18 @@ function h265Geometry(sps) {
|
|
|
18698
18699
|
};
|
|
18699
18700
|
}
|
|
18700
18701
|
|
|
18701
|
-
// dist/transport/p2p/
|
|
18702
|
-
var CMD_VIDEO_FRAME2 = 1300;
|
|
18702
|
+
// dist/transport/p2p/audio-frame.js
|
|
18703
18703
|
var CMD_AUDIO_FRAME2 = 1301;
|
|
18704
|
-
var VIDEO_HEADER_LEN = 22;
|
|
18705
18704
|
var AUDIO_HEADER_LEN = 16;
|
|
18706
18705
|
var AUDIO_TYPE_OFFSET = 5;
|
|
18706
|
+
var AUDIO_CODECS = { 0: "aac-lc", 2: "g711a", 7: "aac-eld" };
|
|
18707
|
+
|
|
18708
|
+
// dist/transport/p2p/live-stream.js
|
|
18709
|
+
var CMD_VIDEO_FRAME2 = 1300;
|
|
18710
|
+
var VIDEO_HEADER_LEN = 22;
|
|
18707
18711
|
var SC4 = Buffer.from([0, 0, 0, 1]);
|
|
18708
18712
|
var SC3 = Buffer.from([0, 0, 1]);
|
|
18709
18713
|
var AAC_ELD_AUDIO_SPECIFIC_CONFIG = Buffer.from("f8f03000", "hex");
|
|
18710
|
-
var AUDIO_CODECS = { 0: "aac-lc", 2: "g711a", 7: "aac-eld" };
|
|
18711
18714
|
var MAX_TRACED_DECODE_FAILURES = 3;
|
|
18712
18715
|
var DEFAULT_KEEPALIVE_MS = 3e3;
|
|
18713
18716
|
var LiveStream = class extends EventEmitter3 {
|
|
@@ -23423,6 +23426,8 @@ var RtcLiveStream = class extends EventEmitter7 {
|
|
|
23423
23426
|
opts;
|
|
23424
23427
|
running = false;
|
|
23425
23428
|
startSegment = -1;
|
|
23429
|
+
onConnected = () => this.sendStart();
|
|
23430
|
+
onError = (error) => this.fail(error);
|
|
23426
23431
|
keepalive;
|
|
23427
23432
|
width = 0;
|
|
23428
23433
|
height = 0;
|
|
@@ -23439,11 +23444,24 @@ var RtcLiveStream = class extends EventEmitter7 {
|
|
|
23439
23444
|
start() {
|
|
23440
23445
|
if (this.running)
|
|
23441
23446
|
return this;
|
|
23442
|
-
const { session
|
|
23447
|
+
const { session } = this.opts;
|
|
23443
23448
|
this.running = true;
|
|
23449
|
+
this.startSegment = -1;
|
|
23444
23450
|
session.on("mediaData", this.onMedia);
|
|
23445
23451
|
session.on("commandData", this.onCommand);
|
|
23446
23452
|
session.on("close", this.onClose);
|
|
23453
|
+
if (this.opts.nvr) {
|
|
23454
|
+
session.on("connected", this.onConnected);
|
|
23455
|
+
session.on("error", this.onError);
|
|
23456
|
+
session.connect().catch(this.onError);
|
|
23457
|
+
} else
|
|
23458
|
+
this.sendStart();
|
|
23459
|
+
return this;
|
|
23460
|
+
}
|
|
23461
|
+
sendStart() {
|
|
23462
|
+
if (!this.running || this.startSegment !== -1)
|
|
23463
|
+
return;
|
|
23464
|
+
const { session, seg, accountId, nvr } = this.opts;
|
|
23447
23465
|
this.startSegment = seg.next();
|
|
23448
23466
|
const sent2 = session.sendCommand(buildPortalPacket({
|
|
23449
23467
|
commandId: PORTAL_CMD_SET_PAYLOAD,
|
|
@@ -23459,7 +23477,7 @@ var RtcLiveStream = class extends EventEmitter7 {
|
|
|
23459
23477
|
camera_type: 0,
|
|
23460
23478
|
key: "",
|
|
23461
23479
|
msg_id: 116,
|
|
23462
|
-
audio_chn: -1,
|
|
23480
|
+
audio_chn: nvr ? this.opts.channel : -1,
|
|
23463
23481
|
streamtype: 2,
|
|
23464
23482
|
stitch_mode: 1,
|
|
23465
23483
|
chn_list: [{ index: 0, chn: this.opts.channel, sensor: 0, isUps: 0, isClicked: true }],
|
|
@@ -23471,7 +23489,7 @@ var RtcLiveStream = class extends EventEmitter7 {
|
|
|
23471
23489
|
}));
|
|
23472
23490
|
if (!sent2) {
|
|
23473
23491
|
queueMicrotask(() => this.fail(new Error(`rtc live ${this.tag}: command channel not open`)));
|
|
23474
|
-
return
|
|
23492
|
+
return;
|
|
23475
23493
|
}
|
|
23476
23494
|
this.keepalive = setInterval(() => {
|
|
23477
23495
|
if (!session.sendRaw(PORTAL_KEEPALIVE))
|
|
@@ -23479,20 +23497,22 @@ var RtcLiveStream = class extends EventEmitter7 {
|
|
|
23479
23497
|
}, PORTAL_KEEPALIVE_MS);
|
|
23480
23498
|
this.keepalive.unref?.();
|
|
23481
23499
|
this.opts.logger?.debug?.(`[rtc] ${this.tag} live start sent`);
|
|
23482
|
-
return this;
|
|
23483
23500
|
}
|
|
23484
23501
|
stop() {
|
|
23485
23502
|
if (!this.running)
|
|
23486
23503
|
return;
|
|
23487
23504
|
this.teardown();
|
|
23488
23505
|
const { session, seg, accountId } = this.opts;
|
|
23489
|
-
|
|
23490
|
-
|
|
23491
|
-
|
|
23492
|
-
|
|
23493
|
-
|
|
23494
|
-
|
|
23495
|
-
|
|
23506
|
+
if (this.startSegment !== -1)
|
|
23507
|
+
session.sendCommand(buildPortalPacket({
|
|
23508
|
+
commandId: PORTAL_CMD_SET_PAYLOAD,
|
|
23509
|
+
channel: PORTAL_STATION_CHANNEL,
|
|
23510
|
+
segment: seg.next(),
|
|
23511
|
+
isResponse: LIVE_STREAM_ID,
|
|
23512
|
+
payload: { account_id: accountId, cmd: LIVE_STOP, payload: {} }
|
|
23513
|
+
}));
|
|
23514
|
+
if (this.opts.nvr)
|
|
23515
|
+
session.close();
|
|
23496
23516
|
this.emit("stop");
|
|
23497
23517
|
}
|
|
23498
23518
|
teardown() {
|
|
@@ -23504,11 +23524,15 @@ var RtcLiveStream = class extends EventEmitter7 {
|
|
|
23504
23524
|
session.off("mediaData", this.onMedia);
|
|
23505
23525
|
session.off("commandData", this.onCommand);
|
|
23506
23526
|
session.off("close", this.onClose);
|
|
23527
|
+
session.off("connected", this.onConnected);
|
|
23528
|
+
session.off("error", this.onError);
|
|
23507
23529
|
}
|
|
23508
23530
|
fail(err) {
|
|
23509
23531
|
if (!this.running)
|
|
23510
23532
|
return;
|
|
23511
23533
|
this.teardown();
|
|
23534
|
+
if (this.opts.nvr)
|
|
23535
|
+
this.opts.session.close();
|
|
23512
23536
|
this.emit("error", err);
|
|
23513
23537
|
}
|
|
23514
23538
|
/** The start's ACK: a non-zero result code ends the pull. */
|
|
@@ -23521,7 +23545,20 @@ var RtcLiveStream = class extends EventEmitter7 {
|
|
|
23521
23545
|
}
|
|
23522
23546
|
onMediaFrame(frame) {
|
|
23523
23547
|
const h = parsePortalHeader(frame);
|
|
23524
|
-
if (!h || h.
|
|
23548
|
+
if (!h || h.channel !== LIVE_MEDIA_CHANNEL)
|
|
23549
|
+
return;
|
|
23550
|
+
if (h.commandId === CMD_AUDIO_FRAME2 && this.opts.nvr) {
|
|
23551
|
+
if (h.paramLength !== frame.length - PORTAL_HEADER_LENGTH || h.paramLength <= AUDIO_HEADER_LEN)
|
|
23552
|
+
return;
|
|
23553
|
+
const body = frame.subarray(PORTAL_HEADER_LENGTH);
|
|
23554
|
+
const codec = AUDIO_CODECS[body[AUDIO_TYPE_OFFSET]];
|
|
23555
|
+
if (body.readUInt32LE(0) !== body.length - AUDIO_HEADER_LEN || codec !== "aac-lc" && codec !== "g711a")
|
|
23556
|
+
return;
|
|
23557
|
+
const audio = { codec, data: body.subarray(AUDIO_HEADER_LEN) };
|
|
23558
|
+
this.emit("audio", audio);
|
|
23559
|
+
return;
|
|
23560
|
+
}
|
|
23561
|
+
if (h.commandId !== LIVE_MEDIA)
|
|
23525
23562
|
return;
|
|
23526
23563
|
if (h.paramLength > frame.length - PORTAL_HEADER_LENGTH)
|
|
23527
23564
|
return;
|
|
@@ -24782,38 +24819,54 @@ var RtcCommandRouter = class {
|
|
|
24782
24819
|
* session. Recording has no wire here and is refused.
|
|
24783
24820
|
*/
|
|
24784
24821
|
trackMediaProviderFor(route) {
|
|
24785
|
-
|
|
24786
|
-
|
|
24787
|
-
|
|
24822
|
+
return this.callMediaProviderFor(route, route.stationSn, (session) => new RtcCallLiveStream(session, this.deps.logger), `record is not available for ${route.stationSn} over a portal call`, 45e3);
|
|
24823
|
+
}
|
|
24824
|
+
/** NVR media uses a dedicated call per camera, independent of station commands. */
|
|
24825
|
+
nvrMediaProviderFor(route) {
|
|
24826
|
+
return this.callMediaProviderFor(route, route.cameraSn, (session, accountId) => new RtcLiveStream({
|
|
24827
|
+
session,
|
|
24828
|
+
seg: new SegmentCounter(),
|
|
24829
|
+
stationSn: route.stationSn,
|
|
24830
|
+
channel: route.channel,
|
|
24831
|
+
accountId,
|
|
24832
|
+
logger: this.deps.logger,
|
|
24833
|
+
nvr: true
|
|
24834
|
+
}), "record is not available over an NVR portal call");
|
|
24835
|
+
}
|
|
24836
|
+
/** Share a dedicated-call source and its consumer operations under one route key. */
|
|
24837
|
+
callMediaProviderFor(route, key, makeStream, recordError, warmTimeoutMs) {
|
|
24838
|
+
const sourceFor = (opts = {}) => {
|
|
24839
|
+
opts.signal?.throwIfAborted();
|
|
24840
|
+
let source = this.callSources.get(key);
|
|
24788
24841
|
if (!source) {
|
|
24789
24842
|
source = new SharedLiveSource({
|
|
24790
24843
|
makeStream: () => {
|
|
24791
24844
|
const identity = this.deps.identity();
|
|
24792
24845
|
if (!identity)
|
|
24793
|
-
throw new Error(`rtc: not logged in, cannot call ${
|
|
24794
|
-
const
|
|
24795
|
-
return
|
|
24846
|
+
throw new Error(`rtc: not logged in, cannot call ${route.stationSn}`);
|
|
24847
|
+
const accountId = route.adminUserId || identity.userId;
|
|
24848
|
+
return makeStream(this.newSession(route, accountId, identity), accountId);
|
|
24796
24849
|
},
|
|
24797
|
-
label:
|
|
24850
|
+
label: key,
|
|
24798
24851
|
logger: this.deps.logger,
|
|
24799
|
-
powered:
|
|
24800
|
-
preBufferSeconds:
|
|
24801
|
-
warmTimeoutMs
|
|
24852
|
+
powered: opts.powered,
|
|
24853
|
+
preBufferSeconds: opts.preBufferSeconds,
|
|
24854
|
+
warmTimeoutMs
|
|
24802
24855
|
});
|
|
24803
|
-
this.callSources.set(
|
|
24856
|
+
this.callSources.set(key, source);
|
|
24804
24857
|
}
|
|
24805
24858
|
return source;
|
|
24806
24859
|
};
|
|
24807
24860
|
return {
|
|
24808
24861
|
live: async (opts) => attachUnlessAborted(sourceFor(opts), opts?.signal),
|
|
24809
24862
|
openReadable: async (opts) => openReadableFromConsumer(attachUnlessAborted(sourceFor(opts), opts?.signal), opts),
|
|
24810
|
-
snapshotLive: (opts) => captureSnapshotFromShared(sourceFor(opts), {
|
|
24863
|
+
snapshotLive: async (opts) => captureSnapshotFromShared(sourceFor(opts), {
|
|
24811
24864
|
...opts,
|
|
24812
24865
|
logger: this.deps.logger,
|
|
24813
24866
|
ffmpegPath: this.deps.ffmpegPath
|
|
24814
24867
|
}),
|
|
24815
24868
|
record: async () => {
|
|
24816
|
-
throw new Error(
|
|
24869
|
+
throw new Error(recordError);
|
|
24817
24870
|
}
|
|
24818
24871
|
};
|
|
24819
24872
|
}
|
|
@@ -29446,19 +29499,24 @@ var EufyMega = class extends EventEmitter14 {
|
|
|
29446
29499
|
}
|
|
29447
29500
|
/**
|
|
29448
29501
|
* The RTC media provider for a station on a media-track route, or a camera attached to a T9000 whose
|
|
29449
|
-
* `device_channel` resolves back to it on the station; `undefined` otherwise,
|
|
29450
|
-
* a T8N00, whose route carries commands only.
|
|
29502
|
+
* `device_channel` resolves back to it on the station; `undefined` otherwise, unless an NVR camera has its own dedicated media call.
|
|
29451
29503
|
*/
|
|
29452
29504
|
rtcMediaFor(sn) {
|
|
29453
29505
|
const route = this.rtcRouteFor(sn);
|
|
29454
29506
|
if (route?.mediaTrack)
|
|
29455
29507
|
return this.rtc.trackMediaProviderFor(route);
|
|
29456
|
-
if (!route?.attached
|
|
29508
|
+
if (!route?.attached)
|
|
29457
29509
|
return void 0;
|
|
29458
|
-
const
|
|
29510
|
+
const camera = this.registry.list().find((d) => d.sn === sn);
|
|
29511
|
+
const raw = camera?.raw ?? {};
|
|
29459
29512
|
const channel = typeof raw.device_channel === "number" ? raw.device_channel : void 0;
|
|
29460
29513
|
if (channel === void 0 || this.registry.serialForFrame(route.stationSn, channel) !== sn)
|
|
29461
29514
|
return void 0;
|
|
29515
|
+
if (route.signalingMode === "call") {
|
|
29516
|
+
if (!camera || !isNvrMediaCamera(camera))
|
|
29517
|
+
return void 0;
|
|
29518
|
+
return this.rtc.nvrMediaProviderFor({ ...route, cameraSn: sn, channel });
|
|
29519
|
+
}
|
|
29462
29520
|
return this.rtc.mediaProviderFor({ ...route, cameraSn: sn, channel });
|
|
29463
29521
|
}
|
|
29464
29522
|
/**
|