tirtc-devtools-cli 0.2.3 → 0.2.5
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 +7 -3
- package/USAGE.md +5 -0
- package/dist/cli/src/index.js +1 -0
- package/dist/cli/src/role_driver.js +81 -14
- package/dist/cli/src/role_live_log.d.ts +17 -0
- package/dist/cli/src/role_live_log.js +305 -0
- package/package.json +1 -1
- package/vendor/devtools/driver/linux-x64/devtools_driver_probe +0 -0
- package/vendor/devtools/driver/macos-arm64/devtools_driver_probe +0 -0
- package/vendor/runtime/linux-x64/lib/libmatrix_runtime_foundation_logging.a +0 -0
- package/vendor/runtime/linux-x64/lib/libmatrix_runtime_media.a +0 -0
- package/vendor/runtime/linux-x64/lib/libmatrix_runtime_video.a +0 -0
- package/vendor/runtime/linux-x64/manifest.txt +4 -4
- package/vendor/runtime/macos-arm64/lib/libmatrix_runtime_audio.a +0 -0
- package/vendor/runtime/macos-arm64/lib/libmatrix_runtime_facade.a +0 -0
- package/vendor/runtime/macos-arm64/lib/libmatrix_runtime_foundation_http.a +0 -0
- package/vendor/runtime/macos-arm64/lib/libmatrix_runtime_foundation_logging.a +0 -0
- package/vendor/runtime/macos-arm64/lib/libmatrix_runtime_media.a +0 -0
- package/vendor/runtime/macos-arm64/lib/libmatrix_runtime_transport.a +0 -0
- package/vendor/runtime/macos-arm64/lib/libmatrix_runtime_video.a +0 -0
- package/vendor/runtime/macos-arm64/manifest.txt +8 -8
package/README.md
CHANGED
|
@@ -57,12 +57,16 @@ node products/cli/bin/tirtc-devtools-cli.js --json device start \
|
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
`device start` 默认持续运行直到用户结束进程;需要自动化限时时再显式传
|
|
60
|
-
`--duration-ms <ms
|
|
61
|
-
|
|
60
|
+
`--duration-ms <ms>`。运行期间 CLI 会把启动、listener ready、client 连接 / 断开、
|
|
61
|
+
首个音视频包与周期运行状态写到 stderr;`--json` 的结构化 envelope 仍只写 stdout。
|
|
62
|
+
prepared asset 会按完整音视频轨循环,任一轨到达源文件末尾时 audio/video 同步回到
|
|
63
|
+
源头并保持 PTS 继续递增。
|
|
62
64
|
|
|
63
65
|
`device start` 的启动成功条件是 device listener 已就绪;客户端是否已经扫码连接不属于
|
|
64
66
|
device 启动失败条件。没有 client 时命令会继续常驻,直到用户结束进程或显式
|
|
65
|
-
`--duration-ms` 到期,summary 中 `media_send`
|
|
67
|
+
`--duration-ms` 到期,summary 中 `media_send` 会保持未开始或标记为跳过。有 client
|
|
68
|
+
连接时 driver 会为该连接送流;client 断开后,device 继续等待下一次连接,并为新连接
|
|
69
|
+
重新挂载音视频输入和从源资产开头送出新的音视频包。同一时刻最多接受一个 client。
|
|
66
70
|
|
|
67
71
|
native role 失败时,`reason_code` 与 `failed_stage` 仍是主失败事实;`log_upload`
|
|
68
72
|
只说明失败现场日志是否已上传。上传成功时,CLI JSON 与 `summary.json` 会包含
|
package/USAGE.md
CHANGED
|
@@ -131,6 +131,11 @@ By default `device start` keeps running until the process is stopped. Use
|
|
|
131
131
|
full audio/video source cycle; when either track reaches the source end, both
|
|
132
132
|
tracks restart from offset 0 while PTS keeps increasing.
|
|
133
133
|
|
|
134
|
+
While the device process is running, CLI writes human-readable lifecycle logs to
|
|
135
|
+
stderr: startup, listener readiness, client connect/disconnect, first audio/video
|
|
136
|
+
packet, and periodic running status. With `--json`, the final machine-readable
|
|
137
|
+
envelope remains on stdout.
|
|
138
|
+
|
|
134
139
|
`device start` is considered started once the device listener is ready. A
|
|
135
140
|
missing client is not a device startup failure. Without a connected client, the
|
|
136
141
|
process remains resident until it is stopped or an explicit `--duration-ms`
|
package/dist/cli/src/index.js
CHANGED
|
@@ -139,6 +139,7 @@ Missing flags are read from TIRTC_DEVICE_ID, TIRTC_DEVICE_SECRET_KEY, and TIRTC_
|
|
|
139
139
|
|
|
140
140
|
device start succeeds once the device listener is ready. It keeps running without a client until
|
|
141
141
|
the process is stopped or an explicit --duration-ms deadline is reached.
|
|
142
|
+
Runtime lifecycle logs are written to stderr. With --json, the final envelope remains on stdout.
|
|
142
143
|
|
|
143
144
|
bootstrap.json is a local handoff artifact for client/sample/validation automation.
|
|
144
145
|
It is only written when --client-token-json is provided.
|
|
@@ -11,6 +11,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
11
11
|
const os_1 = __importDefault(require("os"));
|
|
12
12
|
const path_1 = __importDefault(require("path"));
|
|
13
13
|
const embedded_paths_1 = require("./embedded_paths");
|
|
14
|
+
const role_live_log_1 = require("./role_live_log");
|
|
14
15
|
const defaultAudioStreamId = 10;
|
|
15
16
|
const defaultVideoStreamId = 11;
|
|
16
17
|
const defaultConnectTimeoutMs = 10000;
|
|
@@ -402,7 +403,58 @@ function buildClientRequest(roots, artifactRoot, options) {
|
|
|
402
403
|
probe: { app_id: appId },
|
|
403
404
|
};
|
|
404
405
|
}
|
|
405
|
-
function
|
|
406
|
+
function installSignalForwarding(child, liveLog) {
|
|
407
|
+
const signals = ['SIGINT', 'SIGTERM'];
|
|
408
|
+
const removers = [];
|
|
409
|
+
let forwardedCount = 0;
|
|
410
|
+
for (const signal of signals) {
|
|
411
|
+
const handler = () => {
|
|
412
|
+
forwardedCount += 1;
|
|
413
|
+
liveLog.signalForwarded(signal);
|
|
414
|
+
if (!child.killed) {
|
|
415
|
+
child.kill(forwardedCount > 1 ? 'SIGKILL' : signal);
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
process.on(signal, handler);
|
|
419
|
+
removers.push(() => process.off(signal, handler));
|
|
420
|
+
}
|
|
421
|
+
return () => {
|
|
422
|
+
for (const remove of removers) {
|
|
423
|
+
remove();
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
function spawnDriverProcess(driverPath, args, stdoutFd, stderrFd, liveLog) {
|
|
428
|
+
return new Promise((resolve) => {
|
|
429
|
+
let child;
|
|
430
|
+
try {
|
|
431
|
+
child = child_process_1.default.spawn(driverPath, args, {
|
|
432
|
+
stdio: ['ignore', stdoutFd, stderrFd],
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
437
|
+
liveLog.processError(normalized);
|
|
438
|
+
resolve({
|
|
439
|
+
status: null,
|
|
440
|
+
signal: null,
|
|
441
|
+
error: normalized,
|
|
442
|
+
});
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
let spawnError;
|
|
446
|
+
const removeSignalHandlers = installSignalForwarding(child, liveLog);
|
|
447
|
+
child.once('error', (error) => {
|
|
448
|
+
spawnError = error;
|
|
449
|
+
liveLog.processError(error);
|
|
450
|
+
});
|
|
451
|
+
child.once('close', (code, signal) => {
|
|
452
|
+
removeSignalHandlers();
|
|
453
|
+
resolve({ status: code, signal, error: spawnError });
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
async function runDriver(role, request, artifactRoot, roots) {
|
|
406
458
|
const platform = resolveRuntimePlatform();
|
|
407
459
|
const driverPath = resolveDriverPath(roots, platform);
|
|
408
460
|
const runtimeRoot = resolveRuntimeRoot(roots, platform);
|
|
@@ -428,26 +480,41 @@ function runDriver(request, artifactRoot, roots) {
|
|
|
428
480
|
}
|
|
429
481
|
const requestTempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'tirtc-devtools-cli-request-'));
|
|
430
482
|
const requestPath = path_1.default.join(requestTempDir, 'request.json');
|
|
483
|
+
const summaryPath = path_1.default.join(artifactRoot, 'summary.json');
|
|
484
|
+
fs_1.default.rmSync(summaryPath, { force: true });
|
|
485
|
+
fs_1.default.rmSync(path_1.default.join(artifactRoot, 'events.jsonl'), { force: true });
|
|
431
486
|
writeJson(requestPath, request);
|
|
432
487
|
writeJson(path_1.default.join(artifactRoot, 'request.redacted.json'), redactRequestValue(request));
|
|
433
488
|
const stdoutFd = fs_1.default.openSync(path_1.default.join(artifactRoot, 'stdout.log'), 'w');
|
|
434
489
|
const stderrFd = fs_1.default.openSync(path_1.default.join(artifactRoot, 'stderr.log'), 'w');
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
stdio: ['ignore', stdoutFd, stderrFd],
|
|
490
|
+
const liveLog = (0, role_live_log_1.startRoleLiveLog)({
|
|
491
|
+
role,
|
|
492
|
+
artifactRoot,
|
|
493
|
+
request,
|
|
494
|
+
runtimeRoot,
|
|
495
|
+
assetRoot,
|
|
442
496
|
});
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
497
|
+
let result = { status: null, signal: null };
|
|
498
|
+
try {
|
|
499
|
+
result = await spawnDriverProcess(driverPath, [
|
|
500
|
+
'--request', requestPath,
|
|
501
|
+
'--runtime-root', runtimeRoot,
|
|
502
|
+
'--asset-root', assetRoot,
|
|
503
|
+
'--artifact-root', artifactRoot,
|
|
504
|
+
], stdoutFd, stderrFd, liveLog);
|
|
505
|
+
liveLog.processExit(result.status, result.signal);
|
|
506
|
+
}
|
|
507
|
+
finally {
|
|
508
|
+
fs_1.default.closeSync(stdoutFd);
|
|
509
|
+
fs_1.default.closeSync(stderrFd);
|
|
510
|
+
fs_1.default.rmSync(requestTempDir, { recursive: true, force: true });
|
|
511
|
+
liveLog.stop();
|
|
512
|
+
}
|
|
447
513
|
if (!pathExists(summaryPath)) {
|
|
448
514
|
const message = result.error instanceof Error
|
|
449
515
|
? result.error.message
|
|
450
|
-
: 'driver exited without summary: status=' + String(result.status)
|
|
516
|
+
: 'driver exited without summary: status=' + String(result.status) +
|
|
517
|
+
' signal=' + String(result.signal ?? 'none');
|
|
451
518
|
if (result.error) {
|
|
452
519
|
throw new RoleCommandError('artifact_write_failed', 'artifact', roleFailedExitCode, message);
|
|
453
520
|
}
|
|
@@ -472,7 +539,7 @@ async function runRole(role, commandOptions, options) {
|
|
|
472
539
|
const request = role === 'device' ?
|
|
473
540
|
buildDeviceRequest(roots, artifactRoot, commandOptions) :
|
|
474
541
|
buildClientRequest(roots, artifactRoot, commandOptions);
|
|
475
|
-
const summary = runDriver(request, artifactRoot, roots);
|
|
542
|
+
const summary = await runDriver(role, request, artifactRoot, roots);
|
|
476
543
|
const summaryPath = path_1.default.join(artifactRoot, 'summary.json');
|
|
477
544
|
const data = {
|
|
478
545
|
status: summary.status,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type RoleLiveLogOptions = {
|
|
2
|
+
role: 'device' | 'client';
|
|
3
|
+
artifactRoot: string;
|
|
4
|
+
request: Record<string, unknown>;
|
|
5
|
+
runtimeRoot: string;
|
|
6
|
+
assetRoot: string;
|
|
7
|
+
heartbeatIntervalMs?: number;
|
|
8
|
+
pollIntervalMs?: number;
|
|
9
|
+
emit?: (message: string) => void;
|
|
10
|
+
};
|
|
11
|
+
export type RoleLiveLogHandle = {
|
|
12
|
+
processError(error: Error): void;
|
|
13
|
+
processExit(code: number | null, signal: NodeJS.Signals | null): void;
|
|
14
|
+
signalForwarded(signal: NodeJS.Signals): void;
|
|
15
|
+
stop(): void;
|
|
16
|
+
};
|
|
17
|
+
export declare function startRoleLiveLog(options: RoleLiveLogOptions): RoleLiveLogHandle;
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.startRoleLiveLog = startRoleLiveLog;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const defaultHeartbeatIntervalMs = 5000;
|
|
10
|
+
const defaultPollIntervalMs = 500;
|
|
11
|
+
function asRecord(value) {
|
|
12
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
13
|
+
? value
|
|
14
|
+
: {};
|
|
15
|
+
}
|
|
16
|
+
function nestedRecord(root, key) {
|
|
17
|
+
return asRecord(root[key]);
|
|
18
|
+
}
|
|
19
|
+
function nestedString(root, first, second) {
|
|
20
|
+
const value = second === undefined ? root[first] : nestedRecord(root, first)[second];
|
|
21
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
22
|
+
}
|
|
23
|
+
function nestedNumber(root, first, second) {
|
|
24
|
+
const value = second === undefined ? root[first] : nestedRecord(root, first)[second];
|
|
25
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
26
|
+
}
|
|
27
|
+
function payloadString(payload, key) {
|
|
28
|
+
const value = payload[key];
|
|
29
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
30
|
+
}
|
|
31
|
+
function payloadNumber(payload, key) {
|
|
32
|
+
const value = payload[key];
|
|
33
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
34
|
+
}
|
|
35
|
+
function payloadBoolean(payload, key) {
|
|
36
|
+
const value = payload[key];
|
|
37
|
+
return typeof value === 'boolean' ? value : undefined;
|
|
38
|
+
}
|
|
39
|
+
function formatDurationMs(ms) {
|
|
40
|
+
if (ms < 1000) {
|
|
41
|
+
return String(ms) + 'ms';
|
|
42
|
+
}
|
|
43
|
+
return (ms / 1000).toFixed(1) + 's';
|
|
44
|
+
}
|
|
45
|
+
function formatDurationUs(us) {
|
|
46
|
+
if (us === undefined) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
return formatDurationMs(Math.round(us / 1000));
|
|
50
|
+
}
|
|
51
|
+
function sessionIndex(payload) {
|
|
52
|
+
return payloadNumber(payload, 'session_index');
|
|
53
|
+
}
|
|
54
|
+
function appendField(parts, name, value) {
|
|
55
|
+
if (value !== undefined) {
|
|
56
|
+
parts.push(name + '=' + String(value));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function makeNoopHandle() {
|
|
60
|
+
return {
|
|
61
|
+
processError: () => undefined,
|
|
62
|
+
processExit: () => undefined,
|
|
63
|
+
signalForwarded: () => undefined,
|
|
64
|
+
stop: () => undefined,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function startRoleLiveLog(options) {
|
|
68
|
+
if (options.role !== 'device') {
|
|
69
|
+
return makeNoopHandle();
|
|
70
|
+
}
|
|
71
|
+
const emit = options.emit ?? ((message) => console.error(message));
|
|
72
|
+
const startedAt = Date.now();
|
|
73
|
+
const eventsPath = path_1.default.join(options.artifactRoot, 'events.jsonl');
|
|
74
|
+
const summaryPath = path_1.default.join(options.artifactRoot, 'summary.json');
|
|
75
|
+
const request = options.request;
|
|
76
|
+
const stats = {
|
|
77
|
+
listenerReady: false,
|
|
78
|
+
sessionsStarted: 0,
|
|
79
|
+
sessionsEnded: 0,
|
|
80
|
+
firstAudioSessions: new Set(),
|
|
81
|
+
firstVideoSessions: new Set(),
|
|
82
|
+
};
|
|
83
|
+
let processedLineCount = 0;
|
|
84
|
+
let stopped = false;
|
|
85
|
+
const log = (message) => {
|
|
86
|
+
emit('[device] ' + message);
|
|
87
|
+
};
|
|
88
|
+
const media = nestedRecord(request, 'media');
|
|
89
|
+
const codec = payloadString(nestedRecord(media, 'video'), 'codec') ??
|
|
90
|
+
nestedString(request, 'video_codec') ?? 'unknown';
|
|
91
|
+
const sourcePath = payloadString(nestedRecord(media, 'source'), 'path') ?? options.assetRoot;
|
|
92
|
+
const durationMs = nestedNumber(request, 'run', 'duration_ms');
|
|
93
|
+
const deviceId = nestedString(request, 'identity', 'device_id') ?? 'unknown';
|
|
94
|
+
const endpoint = nestedString(request, 'endpoint') ?? 'unknown';
|
|
95
|
+
log('starting device driver device_id=' + deviceId +
|
|
96
|
+
' endpoint=' + endpoint +
|
|
97
|
+
' codec=' + codec +
|
|
98
|
+
' artifact_root=' + options.artifactRoot);
|
|
99
|
+
log('source=' + sourcePath + ' runtime_root=' + options.runtimeRoot);
|
|
100
|
+
if (durationMs === undefined) {
|
|
101
|
+
log('resident mode enabled; waiting for client connections until the process is stopped');
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
log('bounded mode enabled duration=' + formatDurationMs(durationMs));
|
|
105
|
+
}
|
|
106
|
+
const emitHeartbeat = () => {
|
|
107
|
+
const elapsed = Date.now() - startedAt;
|
|
108
|
+
const parts = [
|
|
109
|
+
'running',
|
|
110
|
+
'elapsed=' + formatDurationMs(elapsed),
|
|
111
|
+
'listener=' + (stats.listenerReady ? 'ready' : 'starting'),
|
|
112
|
+
'sessions=' + String(stats.sessionsStarted) + '/' + String(stats.sessionsEnded),
|
|
113
|
+
'active_session=' + (stats.activeSession ?? 'none'),
|
|
114
|
+
'first_audio_sessions=' + String(stats.firstAudioSessions.size),
|
|
115
|
+
'first_video_sessions=' + String(stats.firstVideoSessions.size),
|
|
116
|
+
];
|
|
117
|
+
appendField(parts, 'last_event', stats.lastEventKind);
|
|
118
|
+
log(parts.join(' '));
|
|
119
|
+
};
|
|
120
|
+
const emitEventLog = (event) => {
|
|
121
|
+
const kind = typeof event.kind === 'string' ? event.kind : undefined;
|
|
122
|
+
if (!kind) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const payload = asRecord(event.payload);
|
|
126
|
+
stats.lastEventKind = kind;
|
|
127
|
+
if (event.level === 'error') {
|
|
128
|
+
const reason = payloadString(payload, 'reason_code') ?? 'unknown';
|
|
129
|
+
log('driver event failed kind=' + kind + ' reason_code=' + reason);
|
|
130
|
+
}
|
|
131
|
+
switch (kind) {
|
|
132
|
+
case 'connection.listen.done':
|
|
133
|
+
stats.listenerReady = true;
|
|
134
|
+
log('listener ready; waiting for client connections');
|
|
135
|
+
break;
|
|
136
|
+
case 'bootstrap.write.done':
|
|
137
|
+
log('bootstrap written path=' + (payloadString(payload, 'path') ?? 'unknown'));
|
|
138
|
+
break;
|
|
139
|
+
case 'connection.connect.done': {
|
|
140
|
+
const session = sessionIndex(payload);
|
|
141
|
+
if (session !== undefined) {
|
|
142
|
+
stats.sessionsStarted = Math.max(stats.sessionsStarted, session);
|
|
143
|
+
stats.activeSession = session;
|
|
144
|
+
}
|
|
145
|
+
log('client connected session=' + (session ?? 'unknown'));
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
case 'connection.session.start': {
|
|
149
|
+
const session = sessionIndex(payload);
|
|
150
|
+
if (session !== undefined) {
|
|
151
|
+
stats.activeSession = session;
|
|
152
|
+
}
|
|
153
|
+
log('media session started session=' + (session ?? 'unknown'));
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
case 'media.audio_send.start': {
|
|
157
|
+
const parts = ['audio input started'];
|
|
158
|
+
appendField(parts, 'session', sessionIndex(payload));
|
|
159
|
+
appendField(parts, 'stream_id', payloadNumber(payload, 'stream_id'));
|
|
160
|
+
appendField(parts, 'codec', payloadString(payload, 'codec'));
|
|
161
|
+
appendField(parts, 'sample_rate_hz', payloadNumber(payload, 'sample_rate_hz'));
|
|
162
|
+
log(parts.join(' '));
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
case 'media.video_send.start': {
|
|
166
|
+
const parts = ['video input started'];
|
|
167
|
+
appendField(parts, 'session', sessionIndex(payload));
|
|
168
|
+
appendField(parts, 'stream_id', payloadNumber(payload, 'stream_id'));
|
|
169
|
+
appendField(parts, 'codec', payloadString(payload, 'codec'));
|
|
170
|
+
log(parts.join(' '));
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
case 'media.asset_cycle.config': {
|
|
174
|
+
const parts = ['asset cycle ready'];
|
|
175
|
+
appendField(parts, 'session', sessionIndex(payload));
|
|
176
|
+
appendField(parts, 'audio_packets', payloadNumber(payload, 'audio_packet_count'));
|
|
177
|
+
appendField(parts, 'video_packets', payloadNumber(payload, 'video_packet_count'));
|
|
178
|
+
appendField(parts, 'cycle_duration', formatDurationUs(payloadNumber(payload, 'cycle_duration_us')));
|
|
179
|
+
log(parts.join(' '));
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
case 'media.audio_send.session_first_packet': {
|
|
183
|
+
const session = sessionIndex(payload);
|
|
184
|
+
if (session !== undefined) {
|
|
185
|
+
stats.firstAudioSessions.add(session);
|
|
186
|
+
}
|
|
187
|
+
const parts = ['first audio packet sent'];
|
|
188
|
+
appendField(parts, 'session', session);
|
|
189
|
+
appendField(parts, 'pts_us', payloadNumber(payload, 'pts_us'));
|
|
190
|
+
appendField(parts, 'bytes', payloadNumber(payload, 'bytes'));
|
|
191
|
+
log(parts.join(' '));
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
case 'media.video_send.session_first_packet': {
|
|
195
|
+
const session = sessionIndex(payload);
|
|
196
|
+
if (session !== undefined) {
|
|
197
|
+
stats.firstVideoSessions.add(session);
|
|
198
|
+
}
|
|
199
|
+
const parts = ['first video packet sent'];
|
|
200
|
+
appendField(parts, 'session', session);
|
|
201
|
+
appendField(parts, 'codec', payloadString(payload, 'codec'));
|
|
202
|
+
appendField(parts, 'key_frame', payloadBoolean(payload, 'is_key_frame'));
|
|
203
|
+
appendField(parts, 'pts_us', payloadNumber(payload, 'pts_us'));
|
|
204
|
+
appendField(parts, 'bytes', payloadNumber(payload, 'bytes'));
|
|
205
|
+
log(parts.join(' '));
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
case 'connection.session.end': {
|
|
209
|
+
const session = sessionIndex(payload);
|
|
210
|
+
if (session !== undefined) {
|
|
211
|
+
stats.sessionsEnded = Math.max(stats.sessionsEnded, session);
|
|
212
|
+
if (stats.activeSession === session) {
|
|
213
|
+
stats.activeSession = undefined;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const disconnected = payloadBoolean(payload, 'disconnected');
|
|
217
|
+
const elapsed = payloadNumber(payload, 'elapsed_ms');
|
|
218
|
+
const parts = [disconnected ? 'client disconnected' : 'client session ended'];
|
|
219
|
+
appendField(parts, 'session', session);
|
|
220
|
+
appendField(parts, 'first_audio', payloadBoolean(payload, 'sent_first_audio'));
|
|
221
|
+
appendField(parts, 'first_video', payloadBoolean(payload, 'sent_first_video'));
|
|
222
|
+
appendField(parts, 'elapsed', elapsed === undefined ? undefined : formatDurationMs(elapsed));
|
|
223
|
+
log(parts.join(' '));
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
case 'connection.wait.no_client': {
|
|
227
|
+
const reason = payloadString(payload, 'reason') ?? 'unknown';
|
|
228
|
+
const elapsed = payloadNumber(payload, 'elapsed_ms');
|
|
229
|
+
log('no client connected before stop reason=' + reason +
|
|
230
|
+
(elapsed === undefined ? '' : ' elapsed=' + formatDurationMs(elapsed)));
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
case 'driver.execution.finished': {
|
|
234
|
+
const status = payloadString(payload, 'status') ?? 'unknown';
|
|
235
|
+
const exitCode = payloadNumber(payload, 'exit_code');
|
|
236
|
+
const reason = payloadString(payload, 'reason_code') ?? 'unknown';
|
|
237
|
+
log('driver finished status=' + status +
|
|
238
|
+
' exit_code=' + String(exitCode ?? 'unknown') +
|
|
239
|
+
' reason_code=' + reason);
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
default:
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
const pollEvents = (flushPartial = false) => {
|
|
247
|
+
if (!fs_1.default.existsSync(eventsPath)) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
let text = '';
|
|
251
|
+
try {
|
|
252
|
+
text = fs_1.default.readFileSync(eventsPath, 'utf8');
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const lines = text.split(/\r?\n/);
|
|
258
|
+
const completeLineCount = text.endsWith('\n')
|
|
259
|
+
? lines.length - 1
|
|
260
|
+
: (flushPartial ? lines.length : lines.length - 1);
|
|
261
|
+
for (let index = processedLineCount; index < completeLineCount; index += 1) {
|
|
262
|
+
const line = lines[index]?.trim();
|
|
263
|
+
if (!line) {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
try {
|
|
267
|
+
emitEventLog(JSON.parse(line));
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
if (flushPartial) {
|
|
271
|
+
log('skipped malformed driver event line=' + String(index + 1));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
processedLineCount = Math.max(processedLineCount, completeLineCount);
|
|
276
|
+
};
|
|
277
|
+
const pollTimer = setInterval(() => pollEvents(false), options.pollIntervalMs ?? defaultPollIntervalMs);
|
|
278
|
+
const heartbeatTimer = setInterval(emitHeartbeat, options.heartbeatIntervalMs ?? defaultHeartbeatIntervalMs);
|
|
279
|
+
return {
|
|
280
|
+
processError: (error) => {
|
|
281
|
+
pollEvents(true);
|
|
282
|
+
log('driver process error message=' + error.message);
|
|
283
|
+
},
|
|
284
|
+
processExit: (code, signal) => {
|
|
285
|
+
pollEvents(true);
|
|
286
|
+
const elapsed = Date.now() - startedAt;
|
|
287
|
+
log('process exited code=' + String(code ?? 'null') +
|
|
288
|
+
' signal=' + String(signal ?? 'none') +
|
|
289
|
+
' elapsed=' + formatDurationMs(elapsed) +
|
|
290
|
+
' summary=' + summaryPath);
|
|
291
|
+
},
|
|
292
|
+
signalForwarded: (signal) => {
|
|
293
|
+
log('forwarding signal=' + signal + ' to native driver');
|
|
294
|
+
},
|
|
295
|
+
stop: () => {
|
|
296
|
+
if (stopped) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
stopped = true;
|
|
300
|
+
clearInterval(pollTimer);
|
|
301
|
+
clearInterval(heartbeatTimer);
|
|
302
|
+
pollEvents(true);
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
}
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
platform=linux-x64
|
|
2
2
|
profile=full
|
|
3
|
-
staged_at_utc=2026-05-
|
|
3
|
+
staged_at_utc=2026-05-05T13:45:31Z
|
|
4
4
|
source_sdk=/Users/allenfeng/Development/Repositories/tirtc-nexus/tirtc-matrix/.build/sdk/linux-x64
|
|
5
5
|
|
|
6
6
|
bfc096be1484ac2b1c2776ccabfa2a4c6a982e869abf875192ff06fad85e82ca include/tirtc/audio.h
|
|
@@ -36,10 +36,10 @@ b1f4135025cb8e8520a14268d831b9998920a4239f84bb6b409d0e4909fd5bee lib/libcrypto.
|
|
|
36
36
|
296ba0b5efa9d2ac49e3bf278c5d467b31436e73fbc608b736e974bcf9759bde lib/libmatrix_runtime_audio.a
|
|
37
37
|
88f9c545943e4ef80024baf3620e91817ec16a4fea60b0e8c7887a0a41dc9d1b lib/libmatrix_runtime_facade.a
|
|
38
38
|
ec7bbf8e8744c6645a441a28e24e0645906152cc987d6fcca16af3c3badba940 lib/libmatrix_runtime_foundation_http.a
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
4005e0f9ff88f2acc1639b79299c6089ba1b81d1a4fd52fe9d64c5e8603d488a lib/libmatrix_runtime_foundation_logging.a
|
|
40
|
+
8964ab2d93bf0fd4ee6666459ffec53cd35b07288fccc0d46e042e60b8466d4f lib/libmatrix_runtime_media.a
|
|
41
41
|
93dd81cfb9ed8aef29bfa3fb9d18aaed140ac25d00beeff1a0156a9333afa8a5 lib/libmatrix_runtime_transport.a
|
|
42
|
-
|
|
42
|
+
ac7b85780c75b5d0c3abf1c5293181739a0fceeb2ad8fa930f98bd86bba5daa2 lib/libmatrix_runtime_video.a
|
|
43
43
|
511d521f7972df3993e5976d6e2dbcad7a6fbce9be15071274d0cbd9d51157f5 lib/libssl.a
|
|
44
44
|
a67ec9034848ef24a1b17671e444daa62a8f9e6b8319a1e932593908720ff2a1 lib/libwebrtc_apm.a
|
|
45
45
|
700e455255897e3cffab13ca593a6e4d9d11383ae609215cbbd6043a63d47161 lib/libxlog.a
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
platform=macos-arm64
|
|
2
2
|
profile=full
|
|
3
|
-
staged_at_utc=2026-05-
|
|
3
|
+
staged_at_utc=2026-05-05T13:43:48Z
|
|
4
4
|
source_sdk=/Users/allenfeng/Development/Repositories/tirtc-nexus/tirtc-matrix/.build/sdk/macos-arm64
|
|
5
5
|
|
|
6
6
|
bfc096be1484ac2b1c2776ccabfa2a4c6a982e869abf875192ff06fad85e82ca include/tirtc/audio.h
|
|
@@ -35,13 +35,13 @@ cae0bbeb884e5466a56da15182c78cc22baab6c743f349a58d3595f623333585 include/tirtc/
|
|
|
35
35
|
8db86d6714264047e8fd4086ddd7315722d675749719e6175f89eb5a636b48a1 lib/libTiRTC.a
|
|
36
36
|
b39daee6a3d39bf0ca20c45084601133c4198de8dca848dcff6dd9c70ae99016 lib/libcrypto.a
|
|
37
37
|
c052857ef315e3d61db9c862cad10709a3a6b2487dc41799cbe4d74a805de875 lib/libcrypto.dylib
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
8cf2dc6547ac1f3aa0d2866005af74c9ae6adf1f358d84cec119205bc1455a2a lib/libmatrix_runtime_audio.a
|
|
39
|
+
11f5e0e50838b9f521acc0eed761120c550be71b87d0a04f172a02a62e6ea6bd lib/libmatrix_runtime_facade.a
|
|
40
|
+
6ec439f590f1468763cdcc7ed6e4a5e24e9d169d37ecdb3d7d3449d633a01da3 lib/libmatrix_runtime_foundation_http.a
|
|
41
|
+
8b49e1ae4eab6cdb74576e28e1df4c34986e65df044bbdf275b4f58daaddc52f lib/libmatrix_runtime_foundation_logging.a
|
|
42
|
+
8151ddd3e587a169050a733db09e216c347a5733e341da90ec234153cf432297 lib/libmatrix_runtime_media.a
|
|
43
|
+
05efbdfd1051206fa407dc81f3da9c35e60cf9d21005d01f59a23da688d97b64 lib/libmatrix_runtime_transport.a
|
|
44
|
+
2f2c455c0fbb4c0d2242586aee3eec6c59f9323b380edeac85f3af0d8b9fb5e4 lib/libmatrix_runtime_video.a
|
|
45
45
|
c11c65d373a127028350c41fa58cd2d1223f2b5d70a84e13b115d90daaba25ca lib/libssl.a
|
|
46
46
|
ef1c1104bbdd2528ed7b958fb7252bd6249875f92300b0c9577d6c4bd6c0d88a lib/libssl.dylib
|
|
47
47
|
e14e846e43d64e240fa0e5745bf4e702b79d0f2442e7f768beb990610735c71b lib/libtgrtc.dylib
|