tirtc-devtools-cli 0.1.1 → 0.2.0
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 -7
- package/USAGE.md +40 -23
- package/dist/cli/src/index.js +35 -21
- package/dist/cli/src/media_assets.d.ts +2 -0
- package/dist/cli/src/media_assets.js +125 -7
- package/dist/cli/src/role_driver.d.ts +11 -10
- package/dist/cli/src/role_driver.js +77 -81
- 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/include/tirtc/error.h +22 -0
- package/vendor/runtime/linux-x64/lib/libmatrix_runtime_audio.a +0 -0
- package/vendor/runtime/linux-x64/lib/libmatrix_runtime_facade.a +0 -0
- package/vendor/runtime/linux-x64/lib/libmatrix_runtime_foundation_http.a +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_transport.a +0 -0
- package/vendor/runtime/linux-x64/lib/libmatrix_runtime_video.a +0 -0
- package/vendor/runtime/linux-x64/manifest.txt +9 -9
- package/vendor/runtime/macos-arm64/include/tirtc/error.h +22 -0
- 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 +9 -9
- package/vendor/runtime/script/prepare_runtime_media_dataset.sh +17 -0
package/README.md
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
- `token issue` / `license qrcode`:本地联调的凭据与二维码工具。
|
|
8
8
|
- `assets prepare`:把默认资产或任意 MP4 准备成 native role driver 使用的媒体资产。
|
|
9
|
-
- `
|
|
10
|
-
- `
|
|
9
|
+
- `device start`:作为标准上行 device 启动 native DevTools driver,送出音视频,按需产出本机 `bootstrap.json` 与 execution evidence。
|
|
10
|
+
- `client start`:作为标准下行 client 消费本机 `bootstrap.json` 或显式 device/token,产出 `frame_dump` 与 summary。
|
|
11
11
|
- CLI 负责参数、环境变量、token 签发、JSON envelope、artifact 摘要与打包定位;真实 TiRTC lifecycle 由 `products/devtools/driver/` 承接。
|
|
12
12
|
|
|
13
13
|
## 不负责什么
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
## 依赖方向
|
|
22
22
|
|
|
23
23
|
- token/license 工具由 CLI 自己承接;仓库级 token helper 入口为 `./script/issue_devtools_token.sh`。
|
|
24
|
-
-
|
|
24
|
+
- device/client 通过本地 native driver executable 执行,默认查找 `.build/devtools-driver/bin/<platform>/devtools_driver_probe` 或 `vendor/devtools/driver/<platform>/devtools_driver_probe`。
|
|
25
25
|
- runtime bundle 默认查找 `.build/products/runtime/<platform>` 或 `vendor/runtime/<platform>`。
|
|
26
26
|
|
|
27
27
|
## 常用命令
|
|
@@ -38,7 +38,7 @@ node products/cli/bin/tirtc-devtools-cli.js --help
|
|
|
38
38
|
./script/issue_devtools_token.sh --token-only
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
真实
|
|
41
|
+
真实 device/client 闭环优先走:
|
|
42
42
|
|
|
43
43
|
```sh
|
|
44
44
|
products/devtools/driver/script/run_capability_probe.sh
|
|
@@ -50,13 +50,13 @@ products/devtools/driver/script/run_capability_probe.sh
|
|
|
50
50
|
node products/cli/bin/tirtc-devtools-cli.js --json assets prepare \
|
|
51
51
|
--source ./movie.mp4 \
|
|
52
52
|
--output-root .build/tirtc-assets
|
|
53
|
-
node products/cli/bin/tirtc-devtools-cli.js --json
|
|
53
|
+
node products/cli/bin/tirtc-devtools-cli.js --json device start \
|
|
54
54
|
--source .build/tirtc-assets/manifest.json \
|
|
55
55
|
--video-codec h264 \
|
|
56
|
-
--artifact-root .build/devtools-cli/
|
|
56
|
+
--artifact-root .build/devtools-cli/device-movie-h264
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
`
|
|
59
|
+
`device start` 默认持续运行直到用户结束进程;需要自动化限时时再显式传
|
|
60
60
|
`--duration-ms <ms>`。prepared asset 会按完整音视频轨循环,任一轨到达源文件末尾时
|
|
61
61
|
audio/video 同步回到源头并保持 PTS 继续递增。
|
|
62
62
|
|
package/USAGE.md
CHANGED
|
@@ -15,8 +15,8 @@ node products/cli/bin/tirtc-devtools-cli.js --help
|
|
|
15
15
|
|
|
16
16
|
## Platform Support
|
|
17
17
|
|
|
18
|
-
- `macos-arm64`: supported for token, assets prepare,
|
|
19
|
-
- `linux-x64`: supported for token, assets prepare,
|
|
18
|
+
- `macos-arm64`: supported for token, assets prepare, device, client, package smoke, and native device+client qualification.
|
|
19
|
+
- `linux-x64`: supported for token, assets prepare, device, client, package smoke, and native driver packaging. Linux native device/client runs through the packaged headless driver on Linux hosts or `linux/amd64` containers.
|
|
20
20
|
|
|
21
21
|
## Token
|
|
22
22
|
|
|
@@ -71,7 +71,7 @@ node products/cli/bin/tirtc-devtools-cli.js --json assets prepare
|
|
|
71
71
|
node products/cli/bin/tirtc-devtools-cli.js --json assets prepare --source runtime/assets/source.mp4
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
Prepare any MP4 and use the returned `data.manifest_path` as `
|
|
74
|
+
Prepare any MP4 and use the returned `data.manifest_path` as `device start --source`:
|
|
75
75
|
|
|
76
76
|
```sh
|
|
77
77
|
node products/cli/bin/tirtc-devtools-cli.js --json assets prepare \
|
|
@@ -79,61 +79,78 @@ node products/cli/bin/tirtc-devtools-cli.js --json assets prepare \
|
|
|
79
79
|
--output-root .build/tirtc-assets
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
##
|
|
82
|
+
## Device
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
Device startup requires:
|
|
85
85
|
|
|
86
86
|
```sh
|
|
87
87
|
export TIRTC_DEVICE_ID="<DEVICE_ID>"
|
|
88
88
|
export TIRTC_DEVICE_SECRET_KEY="<DEVICE_SECRET_KEY>"
|
|
89
|
+
export TIRTC_ENDPOINT="<SERVICE_ENTRY>"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
These can also be passed explicitly with `--device-id`, `--device-secret-key`,
|
|
93
|
+
and `--endpoint`. Missing values fail during CLI preflight before the native
|
|
94
|
+
driver starts.
|
|
95
|
+
|
|
96
|
+
If the local `client start --bootstrap` flow is needed, issue a fresh client
|
|
97
|
+
token first:
|
|
98
|
+
|
|
99
|
+
```sh
|
|
89
100
|
export TIRTC_ACCESS_KEY_ID="<ACCESS_KEY_ID>"
|
|
90
101
|
export TIRTC_SECRET_KEY_ID="<SECRET_KEY_ID>"
|
|
91
102
|
export TIRTC_APP_ID="<APP_ID>"
|
|
92
|
-
export TIRTC_ENDPOINT="<SERVICE_ENTRY>"
|
|
93
103
|
export TIRTC_OPEN_API_ENDPOINT="<OPENAPI_ENDPOINT>"
|
|
104
|
+
|
|
105
|
+
node products/cli/bin/tirtc-devtools-cli.js --json token issue "$TIRTC_DEVICE_ID" \
|
|
106
|
+
--endpoint "$TIRTC_ENDPOINT" \
|
|
107
|
+
--openapi-endpoint "$TIRTC_OPEN_API_ENDPOINT" \
|
|
108
|
+
> .build/devtools-client-token.json
|
|
94
109
|
```
|
|
95
110
|
|
|
96
|
-
Run one
|
|
111
|
+
Run one device with the default prepared asset:
|
|
97
112
|
|
|
98
113
|
```sh
|
|
99
|
-
node products/cli/bin/tirtc-devtools-cli.js --json
|
|
114
|
+
node products/cli/bin/tirtc-devtools-cli.js --json device start \
|
|
100
115
|
--video-codec h264 \
|
|
101
|
-
--artifact-root .build/devtools-cli/
|
|
116
|
+
--artifact-root .build/devtools-cli/device-h264
|
|
102
117
|
```
|
|
103
118
|
|
|
104
|
-
Run one
|
|
119
|
+
Run one device from a prepared MP4 and write a local client bootstrap:
|
|
105
120
|
|
|
106
121
|
```sh
|
|
107
|
-
node products/cli/bin/tirtc-devtools-cli.js --json
|
|
122
|
+
node products/cli/bin/tirtc-devtools-cli.js --json device start \
|
|
108
123
|
--source .build/tirtc-assets/manifest.json \
|
|
109
124
|
--video-codec h264 \
|
|
110
|
-
--
|
|
125
|
+
--client-token-json .build/devtools-client-token.json \
|
|
126
|
+
--artifact-root .build/devtools-cli/device-movie-h264
|
|
111
127
|
```
|
|
112
128
|
|
|
113
|
-
By default `
|
|
129
|
+
By default `device start` keeps running until the process is stopped. Use
|
|
114
130
|
`--duration-ms <ms>` only for bounded automation. Prepared assets loop over the
|
|
115
131
|
full audio/video source cycle; when either track reaches the source end, both
|
|
116
132
|
tracks restart from offset 0 while PTS keeps increasing.
|
|
117
133
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
bootstrap is intended for
|
|
134
|
+
`device start` writes `bootstrap.json` only when `--client-token-json` is
|
|
135
|
+
provided. That file is a local handoff artifact for CLI client, runtime sample
|
|
136
|
+
smoke, and validation automation. The token in that bootstrap is intended for
|
|
137
|
+
one client connection.
|
|
121
138
|
|
|
122
139
|
`bootstrap.json` is not a mobile SDK connection protocol. For phone debugging,
|
|
123
140
|
use token/license QR today; a full session QR or deeplink is a separate product
|
|
124
141
|
slice.
|
|
125
142
|
|
|
126
|
-
##
|
|
143
|
+
## Client
|
|
127
144
|
|
|
128
145
|
```sh
|
|
129
|
-
node products/cli/bin/tirtc-devtools-cli.js --json
|
|
130
|
-
--bootstrap .build/devtools-cli/
|
|
131
|
-
--artifact-root .build/devtools-cli/
|
|
146
|
+
node products/cli/bin/tirtc-devtools-cli.js --json client start \
|
|
147
|
+
--bootstrap .build/devtools-cli/device-h264/bootstrap.json \
|
|
148
|
+
--artifact-root .build/devtools-cli/client-h264
|
|
132
149
|
```
|
|
133
150
|
|
|
134
|
-
|
|
151
|
+
Client writes `summary.json`, `events.jsonl`, runtime logs, and `render/first-video-frame.*` for `frame_dump`.
|
|
135
152
|
|
|
136
|
-
`
|
|
153
|
+
`client start --bootstrap` is meant for the local computer-to-computer DevTools
|
|
137
154
|
flow. Mobile clients should not be required to fetch a local JSON file from the
|
|
138
155
|
developer machine.
|
|
139
156
|
|
|
@@ -145,4 +162,4 @@ For the current macOS gate, use the native capability runner:
|
|
|
145
162
|
products/devtools/driver/script/run_capability_probe.sh
|
|
146
163
|
```
|
|
147
164
|
|
|
148
|
-
It runs H264, H265, and MJPEG
|
|
165
|
+
It runs H264, H265, and MJPEG device+client with a fresh token per case.
|
package/dist/cli/src/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const embedded_paths_1 = require("./embedded_paths");
|
|
10
10
|
const media_assets_1 = require("./media_assets");
|
|
11
|
+
const progress_1 = require("./progress");
|
|
11
12
|
const token_command_1 = require("./token_command");
|
|
12
13
|
const role_driver_1 = require("./role_driver");
|
|
13
14
|
function resolveCliVersion() {
|
|
@@ -69,12 +70,19 @@ function printError(error, options) {
|
|
|
69
70
|
return 1;
|
|
70
71
|
}
|
|
71
72
|
async function runAssetsPrepare(commandOptions, options) {
|
|
73
|
+
const progress = new progress_1.ProgressIndicator();
|
|
74
|
+
progress.start('Preparing media assets');
|
|
72
75
|
try {
|
|
73
76
|
const result = await (0, media_assets_1.prepareMediaAssets)({
|
|
74
77
|
source: commandOptions.source ?? 'runtime/assets/source.mp4',
|
|
75
78
|
outputRoot: commandOptions.outputRoot ?? 'runtime/assets/.workspace',
|
|
76
79
|
overwrite: false,
|
|
80
|
+
}, {
|
|
81
|
+
progress: (message) => {
|
|
82
|
+
progress.update(message);
|
|
83
|
+
},
|
|
77
84
|
});
|
|
85
|
+
progress.succeed(result.cache_hit ? 'Media assets cache hit' : 'Prepared media assets');
|
|
78
86
|
if (options.json) {
|
|
79
87
|
console.log(JSON.stringify({ code: 0, message: 'OK', data: result }));
|
|
80
88
|
}
|
|
@@ -84,6 +92,7 @@ async function runAssetsPrepare(commandOptions, options) {
|
|
|
84
92
|
return 0;
|
|
85
93
|
}
|
|
86
94
|
catch (error) {
|
|
95
|
+
progress.fail('Media assets prepare failed');
|
|
87
96
|
return printError(error, options);
|
|
88
97
|
}
|
|
89
98
|
}
|
|
@@ -97,48 +106,53 @@ program.name('tirtc-devtools-cli')
|
|
|
97
106
|
const assets = program.command('assets').description('准备 DevTools driver 使用的媒体资产');
|
|
98
107
|
assets.command('prepare')
|
|
99
108
|
.description('准备默认 runtime assets 或把显式 source 写入 output root')
|
|
100
|
-
.option('--source <path>', '输入 MP4 路径;
|
|
109
|
+
.option('--source <path>', '输入 MP4 路径;device start 使用输出的 manifest_path')
|
|
101
110
|
.option('--output-root <dir>', 'prepared assets 输出根目录')
|
|
102
111
|
.addHelpText('after', `
|
|
103
112
|
Examples:
|
|
104
113
|
$ tirtc-devtools-cli --json assets prepare --source ./movie.mp4 --output-root .build/tirtc-assets
|
|
105
|
-
$ tirtc-devtools-cli --json
|
|
114
|
+
$ tirtc-devtools-cli --json device start --source .build/tirtc-assets/manifest.json --video-codec h264
|
|
106
115
|
`)
|
|
107
116
|
.action((commandOptions) => {
|
|
108
117
|
runAndExit(runAssetsPrepare(commandOptions, getCliOptions()));
|
|
109
118
|
});
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
.description('
|
|
113
|
-
.option('--execution-id <id>', 'runtime validation execution id')
|
|
114
|
-
.option('--case-id <id>', 'runtime validation case id')
|
|
115
|
-
.option('--app-id <id>', 'runtime validation app id')
|
|
119
|
+
const device = program.command('device').description('作为标准上行 device 运行 native DevTools driver');
|
|
120
|
+
device.command('start')
|
|
121
|
+
.description('启动上行 device,读取 prepared asset 并送出音视频')
|
|
116
122
|
.option('--artifact-root <dir>', 'artifact 输出目录')
|
|
117
|
-
.option('--
|
|
123
|
+
.option('--device-id <id>', 'device id;不传时读取 TIRTC_DEVICE_ID')
|
|
124
|
+
.option('--device-secret-key <key>', 'device secret key;不传时读取 TIRTC_DEVICE_SECRET_KEY')
|
|
125
|
+
.option('--endpoint <url>', 'TiRTC endpoint;不传时读取 TIRTC_ENDPOINT')
|
|
118
126
|
.option('--source <path>', 'prepared asset root、manifest_path 或 encoded track;MP4 先运行 assets prepare')
|
|
119
127
|
.option('--video-codec <codec>', 'h264|h265|mjpeg', 'h264')
|
|
120
128
|
.option('--duration-ms <ms>', '可选自动结束时长;默认持续运行直到用户结束进程')
|
|
121
129
|
.option('--connect-timeout-ms <ms>', 'service ready / connect 最大等待')
|
|
122
130
|
.option('--first-packet-timeout-ms <ms>', '首包最大等待')
|
|
123
|
-
.option('--
|
|
131
|
+
.option('--client-token-json <path>', 'token issue --json 输出文件;传入时写出本机 bootstrap.json')
|
|
124
132
|
.addHelpText('after', `
|
|
125
133
|
Examples:
|
|
126
134
|
$ tirtc-devtools-cli --json assets prepare --source ./movie.mp4 --output-root .build/tirtc-assets
|
|
127
|
-
$ tirtc-devtools-cli --json
|
|
135
|
+
$ tirtc-devtools-cli --json device start --source .build/tirtc-assets/manifest.json --artifact-root .build/tirtc-device
|
|
128
136
|
|
|
129
|
-
|
|
137
|
+
device start requires device id, device secret key, and endpoint.
|
|
138
|
+
Missing flags are read from TIRTC_DEVICE_ID, TIRTC_DEVICE_SECRET_KEY, and TIRTC_ENDPOINT.
|
|
139
|
+
|
|
140
|
+
bootstrap.json is a local handoff artifact for client/sample/validation automation.
|
|
141
|
+
It is only written when --client-token-json is provided.
|
|
130
142
|
It is not a mobile SDK connection protocol.
|
|
131
143
|
`)
|
|
132
144
|
.action((commandOptions) => {
|
|
133
|
-
runAndExit((0, role_driver_1.
|
|
145
|
+
runAndExit((0, role_driver_1.runDeviceStart)(commandOptions, getCliOptions()));
|
|
134
146
|
});
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
.description('
|
|
147
|
+
const client = program.command('client').description('作为标准下行 client 运行 native DevTools driver');
|
|
148
|
+
client.command('start')
|
|
149
|
+
.description('启动下行 client,消费 bootstrap 或显式 token 并产出 frame_dump')
|
|
138
150
|
.option('--artifact-root <dir>', 'artifact 输出目录')
|
|
139
|
-
.option('--bootstrap <path>', '
|
|
140
|
-
.option('--
|
|
151
|
+
.option('--bootstrap <path>', 'device start 产出的 bootstrap.json')
|
|
152
|
+
.option('--target-device-id <id>', '目标 device id;优先于 bootstrap')
|
|
141
153
|
.option('--token <token>', '显式 token;优先于 bootstrap')
|
|
154
|
+
.option('--endpoint <url>', 'TiRTC endpoint;优先于 bootstrap')
|
|
155
|
+
.option('--app-id <id>', 'client connect app id;不传时读取 bootstrap 或 TIRTC_APP_ID')
|
|
142
156
|
.option('--audio-stream-id <id>', '音频 stream id')
|
|
143
157
|
.option('--video-stream-id <id>', '视频 stream id')
|
|
144
158
|
.option('--consumer <consumer>', 'packet_dump|frame_dump', 'frame_dump')
|
|
@@ -149,12 +163,12 @@ receive.command('start')
|
|
|
149
163
|
.option('--first-output-timeout-ms <ms>', '首帧输出最大等待')
|
|
150
164
|
.addHelpText('after', `
|
|
151
165
|
Examples:
|
|
152
|
-
$ tirtc-devtools-cli --json
|
|
166
|
+
$ tirtc-devtools-cli --json client start --bootstrap .build/tirtc-device/bootstrap.json
|
|
153
167
|
|
|
154
|
-
bootstrap.json is expected to come from a local
|
|
168
|
+
bootstrap.json is expected to come from a local device start run.
|
|
155
169
|
For mobile device debugging, use token/license QR or a future session QR/deeplink.
|
|
156
170
|
`)
|
|
157
171
|
.action((commandOptions) => {
|
|
158
|
-
runAndExit((0, role_driver_1.
|
|
172
|
+
runAndExit((0, role_driver_1.runClientStart)(commandOptions, getCliOptions()));
|
|
159
173
|
});
|
|
160
174
|
program.parse(process.argv);
|
|
@@ -17,9 +17,11 @@ type ExecFileLike = (file: string, args: string[], options: {
|
|
|
17
17
|
stdout?: string | Buffer;
|
|
18
18
|
stderr?: string | Buffer;
|
|
19
19
|
}>;
|
|
20
|
+
type ProgressCallback = (message: string) => void;
|
|
20
21
|
type PrepareMediaAssetsOptions = {
|
|
21
22
|
repoRoot?: string;
|
|
22
23
|
execFile?: ExecFileLike;
|
|
24
|
+
progress?: ProgressCallback;
|
|
23
25
|
};
|
|
24
26
|
export declare function prepareMediaAssets(request: PrepareMediaAssetsRequest, options?: PrepareMediaAssetsOptions): Promise<PrepareMediaAssetsResult>;
|
|
25
27
|
export {};
|
|
@@ -18,6 +18,9 @@ class PrepareMediaAssetsError extends Error {
|
|
|
18
18
|
this.reasonCode = reasonCode;
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
+
const prepareProgressPrefix = '[prepare_runtime_media_dataset] progress: ';
|
|
22
|
+
const prepareErrorPrefix = '[prepare_runtime_media_dataset] error: ';
|
|
23
|
+
const prepareExecMaxBuffer = 10 * 1024 * 1024;
|
|
21
24
|
function resolveRepoRoot(fromDir) {
|
|
22
25
|
const candidates = [
|
|
23
26
|
path_1.default.resolve(fromDir, '../../..'),
|
|
@@ -44,9 +47,43 @@ function resolvePrepareScript(repoRoot) {
|
|
|
44
47
|
}
|
|
45
48
|
return path_1.default.join(repoRoot, 'runtime/script/prepare_runtime_media_dataset.sh');
|
|
46
49
|
}
|
|
50
|
+
function appendBounded(chunks, text, currentSize, maxSize) {
|
|
51
|
+
const nextSize = currentSize + Buffer.byteLength(text);
|
|
52
|
+
if (nextSize > maxSize) {
|
|
53
|
+
throw new Error('media assets prepare output exceeded buffer limit');
|
|
54
|
+
}
|
|
55
|
+
chunks.push(text);
|
|
56
|
+
return nextSize;
|
|
57
|
+
}
|
|
58
|
+
function emitProgressLine(line, progress) {
|
|
59
|
+
if (!progress || !line.startsWith(prepareProgressPrefix)) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const message = line.slice(prepareProgressPrefix.length).trim();
|
|
63
|
+
if (message.length > 0) {
|
|
64
|
+
progress(message);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function emitBufferedProgress(stderr, progress) {
|
|
68
|
+
if (!stderr || !progress) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
String(stderr)
|
|
72
|
+
.split(/\r?\n/)
|
|
73
|
+
.forEach((line) => {
|
|
74
|
+
emitProgressLine(line, progress);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function stripProgressLines(message) {
|
|
78
|
+
const lines = message.split(/\r?\n/);
|
|
79
|
+
const nonProgressLines = lines.filter((line) => !line.startsWith(prepareProgressPrefix));
|
|
80
|
+
const cleaned = nonProgressLines.join('\n').trim();
|
|
81
|
+
return cleaned.length > 0 ? cleaned : message.trim();
|
|
82
|
+
}
|
|
47
83
|
function normalizeExecErrorMessage(message) {
|
|
48
|
-
const
|
|
49
|
-
const
|
|
84
|
+
const cleaned = stripProgressLines(message);
|
|
85
|
+
const errorLine = cleaned.split(/\r?\n/).find((line) => line.startsWith(prepareErrorPrefix));
|
|
86
|
+
const normalized = errorLine ? errorLine.slice(prepareErrorPrefix.length).trim() : cleaned;
|
|
50
87
|
const reasonMatch = normalized.match(/^([a-z][a-z0-9_]*):\s*(.+)$/);
|
|
51
88
|
if (reasonMatch) {
|
|
52
89
|
return {
|
|
@@ -56,11 +93,14 @@ function normalizeExecErrorMessage(message) {
|
|
|
56
93
|
}
|
|
57
94
|
return { message: normalized };
|
|
58
95
|
}
|
|
59
|
-
function parseExecError(error) {
|
|
96
|
+
function parseExecError(error, progress) {
|
|
60
97
|
if (!error || typeof error !== 'object') {
|
|
61
98
|
return normalizeExecErrorMessage(String(error));
|
|
62
99
|
}
|
|
63
100
|
const typed = error;
|
|
101
|
+
if (!typed.progressEmitted) {
|
|
102
|
+
emitBufferedProgress(typed.stderr, progress);
|
|
103
|
+
}
|
|
64
104
|
const stderr = typeof typed.stderr === 'string' ? typed.stderr.trim() : typed.stderr?.toString('utf8').trim();
|
|
65
105
|
if (stderr && stderr.length > 0) {
|
|
66
106
|
return normalizeExecErrorMessage(stderr);
|
|
@@ -85,6 +125,74 @@ function assertPrepareRequest(request) {
|
|
|
85
125
|
throw new Error('media assets prepare requires non-empty --output-dir when provided');
|
|
86
126
|
}
|
|
87
127
|
}
|
|
128
|
+
function execPrepareWithProgress(file, args, options) {
|
|
129
|
+
return new Promise((resolve, reject) => {
|
|
130
|
+
const child = (0, child_process_1.spawn)(file, args, {
|
|
131
|
+
cwd: options.cwd,
|
|
132
|
+
env: options.env,
|
|
133
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
134
|
+
});
|
|
135
|
+
const stdoutChunks = [];
|
|
136
|
+
const stderrChunks = [];
|
|
137
|
+
let stdoutSize = 0;
|
|
138
|
+
let stderrSize = 0;
|
|
139
|
+
let stderrLineBuffer = '';
|
|
140
|
+
let settled = false;
|
|
141
|
+
function rejectOnce(error) {
|
|
142
|
+
if (settled) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
settled = true;
|
|
146
|
+
child.kill();
|
|
147
|
+
reject(error);
|
|
148
|
+
}
|
|
149
|
+
child.stdout.on('data', (chunk) => {
|
|
150
|
+
try {
|
|
151
|
+
stdoutSize = appendBounded(stdoutChunks, chunk.toString('utf8'), stdoutSize, options.maxBuffer);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
rejectOnce(error instanceof Error ? error : new Error(String(error)));
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
child.stderr.on('data', (chunk) => {
|
|
158
|
+
const text = chunk.toString('utf8');
|
|
159
|
+
try {
|
|
160
|
+
stderrSize = appendBounded(stderrChunks, text, stderrSize, options.maxBuffer);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
rejectOnce(error instanceof Error ? error : new Error(String(error)));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
stderrLineBuffer += text;
|
|
167
|
+
const lines = stderrLineBuffer.split(/\r?\n/);
|
|
168
|
+
stderrLineBuffer = lines.pop() ?? '';
|
|
169
|
+
lines.forEach((line) => {
|
|
170
|
+
emitProgressLine(line, options.progress);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
child.on('error', (error) => {
|
|
174
|
+
rejectOnce(error);
|
|
175
|
+
});
|
|
176
|
+
child.on('close', (code, signal) => {
|
|
177
|
+
if (settled) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
settled = true;
|
|
181
|
+
if (stderrLineBuffer.length > 0) {
|
|
182
|
+
emitProgressLine(stderrLineBuffer, options.progress);
|
|
183
|
+
}
|
|
184
|
+
const stdout = stdoutChunks.join('');
|
|
185
|
+
const stderr = stderrChunks.join('');
|
|
186
|
+
if (code === 0) {
|
|
187
|
+
resolve({ stdout, stderr });
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const error = new Error(signal ? `process killed by signal ${signal}` : `process exited with code ${code}`);
|
|
191
|
+
Object.assign(error, { stdout, stderr, progressEmitted: true });
|
|
192
|
+
reject(error);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
88
196
|
async function prepareMediaAssets(request, options = {}) {
|
|
89
197
|
assertPrepareRequest(request);
|
|
90
198
|
const repoRoot = options.repoRoot ? path_1.default.resolve(options.repoRoot) : resolveDefaultRepoRoot();
|
|
@@ -108,18 +216,28 @@ async function prepareMediaAssets(request, options = {}) {
|
|
|
108
216
|
}
|
|
109
217
|
let stdout = '';
|
|
110
218
|
try {
|
|
111
|
-
const
|
|
219
|
+
const execOptions = {
|
|
112
220
|
cwd: repoRoot,
|
|
113
221
|
env: {
|
|
114
222
|
...process.env,
|
|
115
223
|
TIRTC_ENSURE_FFMPEG_SCRIPT: ensureFfmpegScriptPath,
|
|
116
224
|
},
|
|
117
|
-
maxBuffer:
|
|
118
|
-
}
|
|
225
|
+
maxBuffer: prepareExecMaxBuffer,
|
|
226
|
+
};
|
|
227
|
+
const useStreamingProgress = options.progress && !options.execFile;
|
|
228
|
+
const result = useStreamingProgress
|
|
229
|
+
? await execPrepareWithProgress('bash', [scriptPath, ...args], {
|
|
230
|
+
...execOptions,
|
|
231
|
+
progress: options.progress,
|
|
232
|
+
})
|
|
233
|
+
: await execFile('bash', [scriptPath, ...args], execOptions);
|
|
234
|
+
if (!useStreamingProgress) {
|
|
235
|
+
emitBufferedProgress(result.stderr, options.progress);
|
|
236
|
+
}
|
|
119
237
|
stdout = String(result.stdout ?? '').trim();
|
|
120
238
|
}
|
|
121
239
|
catch (error) {
|
|
122
|
-
const parsed = parseExecError(error);
|
|
240
|
+
const parsed = parseExecError(error, options.progress);
|
|
123
241
|
throw new PrepareMediaAssetsError(parsed.message, parsed.reasonCode);
|
|
124
242
|
}
|
|
125
243
|
let parsed;
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
export type CliOptions = {
|
|
2
2
|
json?: boolean;
|
|
3
3
|
};
|
|
4
|
-
type
|
|
5
|
-
executionId?: string;
|
|
6
|
-
caseId?: string;
|
|
7
|
-
appId?: string;
|
|
4
|
+
type DeviceCommandOptions = {
|
|
8
5
|
artifactRoot?: string;
|
|
9
|
-
|
|
6
|
+
deviceId?: string;
|
|
7
|
+
deviceSecretKey?: string;
|
|
8
|
+
endpoint?: string;
|
|
10
9
|
source?: string;
|
|
11
10
|
videoCodec?: string;
|
|
12
11
|
durationMs?: string;
|
|
13
12
|
connectTimeoutMs?: string;
|
|
14
13
|
firstPacketTimeoutMs?: string;
|
|
15
|
-
|
|
14
|
+
clientTokenJson?: string;
|
|
16
15
|
};
|
|
17
|
-
type
|
|
16
|
+
type ClientCommandOptions = {
|
|
18
17
|
artifactRoot?: string;
|
|
19
18
|
bootstrap?: string;
|
|
20
|
-
|
|
19
|
+
targetDeviceId?: string;
|
|
21
20
|
token?: string;
|
|
21
|
+
endpoint?: string;
|
|
22
|
+
appId?: string;
|
|
22
23
|
audioStreamId?: string;
|
|
23
24
|
videoStreamId?: string;
|
|
24
25
|
consumer?: string;
|
|
@@ -28,6 +29,6 @@ type ReceiveCommandOptions = {
|
|
|
28
29
|
firstPacketTimeoutMs?: string;
|
|
29
30
|
firstOutputTimeoutMs?: string;
|
|
30
31
|
};
|
|
31
|
-
export declare function
|
|
32
|
-
export declare function
|
|
32
|
+
export declare function runDeviceStart(commandOptions: DeviceCommandOptions, options: CliOptions): Promise<number>;
|
|
33
|
+
export declare function runClientStart(commandOptions: ClientCommandOptions, options: CliOptions): Promise<number>;
|
|
33
34
|
export {};
|
|
@@ -3,15 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
7
|
-
exports.
|
|
6
|
+
exports.runDeviceStart = runDeviceStart;
|
|
7
|
+
exports.runClientStart = runClientStart;
|
|
8
8
|
const child_process_1 = __importDefault(require("child_process"));
|
|
9
9
|
const crypto_1 = __importDefault(require("crypto"));
|
|
10
10
|
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 token_tool_1 = require("./token_tool");
|
|
15
14
|
const defaultAudioStreamId = 10;
|
|
16
15
|
const defaultVideoStreamId = 11;
|
|
17
16
|
const defaultConnectTimeoutMs = 10000;
|
|
@@ -142,13 +141,6 @@ function parseOptionalPositiveInt(raw, name) {
|
|
|
142
141
|
}
|
|
143
142
|
return parsePositiveInt(raw, 1, name);
|
|
144
143
|
}
|
|
145
|
-
function requireEnv(name) {
|
|
146
|
-
const value = process.env[name]?.trim();
|
|
147
|
-
if (!value) {
|
|
148
|
-
throw rolePreflightError('missing_env', 'missing environment: ' + name);
|
|
149
|
-
}
|
|
150
|
-
return value;
|
|
151
|
-
}
|
|
152
144
|
function executionSuffix() {
|
|
153
145
|
return new Date().toISOString().replace(/[-:.TZ]/g, '').slice(0, 14);
|
|
154
146
|
}
|
|
@@ -211,10 +203,30 @@ function readTokenIssueJson(filePath) {
|
|
|
211
203
|
appId: trimOptional(parsed.data?.payload?.app_id),
|
|
212
204
|
};
|
|
213
205
|
}
|
|
206
|
+
function resolveDeviceIdentity(options) {
|
|
207
|
+
const deviceId = trimOptional(options.deviceId) ?? trimOptional(process.env.TIRTC_DEVICE_ID);
|
|
208
|
+
const deviceSecretKey = trimOptional(options.deviceSecretKey) ??
|
|
209
|
+
trimOptional(process.env.TIRTC_DEVICE_SECRET_KEY);
|
|
210
|
+
const endpoint = trimOptional(options.endpoint) ?? trimOptional(process.env.TIRTC_ENDPOINT);
|
|
211
|
+
const missing = [];
|
|
212
|
+
if (!deviceId) {
|
|
213
|
+
missing.push('device id (pass --device-id or set TIRTC_DEVICE_ID)');
|
|
214
|
+
}
|
|
215
|
+
if (!deviceSecretKey) {
|
|
216
|
+
missing.push('device secret key (pass --device-secret-key or set TIRTC_DEVICE_SECRET_KEY)');
|
|
217
|
+
}
|
|
218
|
+
if (!endpoint) {
|
|
219
|
+
missing.push('endpoint (pass --endpoint or set TIRTC_ENDPOINT)');
|
|
220
|
+
}
|
|
221
|
+
if (missing.length > 0 || !deviceId || !deviceSecretKey || !endpoint) {
|
|
222
|
+
throw rolePreflightError('missing_env', 'device start requires ' + missing.join(' and '));
|
|
223
|
+
}
|
|
224
|
+
return { deviceId, deviceSecretKey, endpoint };
|
|
225
|
+
}
|
|
214
226
|
function readBootstrap(bootstrapPath) {
|
|
215
227
|
const resolved = path_1.default.resolve(bootstrapPath);
|
|
216
228
|
const parsed = readJson(resolved);
|
|
217
|
-
if (parsed.schema_version !== 1 || !parsed.remote_id || !parsed.token) {
|
|
229
|
+
if (parsed.schema_version !== 1 || !(parsed.device_id || parsed.remote_id) || !parsed.token) {
|
|
218
230
|
throw roleUsageError('bootstrap_invalid');
|
|
219
231
|
}
|
|
220
232
|
return parsed;
|
|
@@ -277,62 +289,27 @@ function printEnvelope(options, code, message, data) {
|
|
|
277
289
|
}
|
|
278
290
|
}
|
|
279
291
|
}
|
|
280
|
-
|
|
292
|
+
function buildDeviceRequest(roots, artifactRoot, options) {
|
|
281
293
|
const codec = codecOrDefault(options.videoCodec);
|
|
282
|
-
const executionId =
|
|
283
|
-
const caseId =
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
const tokenIssue = readTokenIssueJson(options.bootstrapTokenJson);
|
|
291
|
-
token = tokenIssue.token;
|
|
292
|
-
remoteId = remoteId ?? tokenIssue.remoteId;
|
|
293
|
-
endpoint = tokenIssue.endpoint ?? endpoint;
|
|
294
|
-
appId = appId ?? tokenIssue.appId;
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
remoteId = remoteId ?? requireEnv('TIRTC_DEVICE_ID');
|
|
298
|
-
appId = appId ?? requireEnv('TIRTC_APP_ID');
|
|
299
|
-
token = await (0, token_tool_1.issueToken)({
|
|
300
|
-
accessId: requireEnv('TIRTC_ACCESS_KEY_ID'),
|
|
301
|
-
secretKey: requireEnv('TIRTC_SECRET_KEY_ID'),
|
|
302
|
-
appId,
|
|
303
|
-
remoteId,
|
|
304
|
-
openapiEndpoint: process.env.TIRTC_OPEN_API_ENDPOINT || process.env.TIRTC_OPENAPI_ENDPOINT,
|
|
305
|
-
endpoint,
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
catch (error) {
|
|
310
|
-
if (error instanceof RoleCommandError) {
|
|
311
|
-
throw error;
|
|
312
|
-
}
|
|
313
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
314
|
-
throw rolePreflightError('token_issue_failed', detail);
|
|
315
|
-
}
|
|
316
|
-
remoteId = remoteId ?? requireEnv('TIRTC_DEVICE_ID');
|
|
317
|
-
endpoint = endpoint ?? requireEnv('TIRTC_ENDPOINT');
|
|
318
|
-
appId = appId ?? requireEnv('TIRTC_APP_ID');
|
|
319
|
-
if (!token) {
|
|
320
|
-
throw rolePreflightError('token_issue_invalid', 'missing client token');
|
|
321
|
-
}
|
|
294
|
+
const executionId = 'cli-device-' + codec + '-' + executionSuffix();
|
|
295
|
+
const caseId = 'devtools-cli-device.' + codec;
|
|
296
|
+
const deviceIdentity = resolveDeviceIdentity(options);
|
|
297
|
+
const tokenIssue = options.clientTokenJson ? readTokenIssueJson(options.clientTokenJson) : undefined;
|
|
298
|
+
const bootstrap = tokenIssue ? {
|
|
299
|
+
client_token: tokenIssue.token,
|
|
300
|
+
token_fingerprint: tokenFingerprint(tokenIssue.token),
|
|
301
|
+
} : undefined;
|
|
322
302
|
return {
|
|
323
303
|
schema_version: 1,
|
|
324
304
|
execution_id: executionId,
|
|
325
305
|
case_id: caseId,
|
|
326
|
-
role: '
|
|
327
|
-
endpoint,
|
|
306
|
+
role: 'device',
|
|
307
|
+
endpoint: deviceIdentity.endpoint,
|
|
328
308
|
identity: {
|
|
329
|
-
license:
|
|
330
|
-
|
|
331
|
-
},
|
|
332
|
-
bootstrap: {
|
|
333
|
-
client_token: token,
|
|
334
|
-
token_fingerprint: tokenFingerprint(token),
|
|
309
|
+
license: deviceIdentity.deviceId + ',' + deviceIdentity.deviceSecretKey,
|
|
310
|
+
device_id: deviceIdentity.deviceId,
|
|
335
311
|
},
|
|
312
|
+
bootstrap,
|
|
336
313
|
streams: {
|
|
337
314
|
audio_stream_id: defaultAudioStreamId,
|
|
338
315
|
video_stream_id: defaultVideoStreamId,
|
|
@@ -349,30 +326,49 @@ async function buildSendRequest(roots, artifactRoot, options) {
|
|
|
349
326
|
first_output_timeout_ms: defaultFirstOutputTimeoutMs,
|
|
350
327
|
},
|
|
351
328
|
artifact: { root_dir: artifactRoot },
|
|
352
|
-
probe: { app_id: appId },
|
|
329
|
+
probe: { app_id: tokenIssue?.appId ?? '' },
|
|
353
330
|
};
|
|
354
331
|
}
|
|
355
|
-
function
|
|
332
|
+
function buildClientRequest(roots, artifactRoot, options) {
|
|
356
333
|
const bootstrap = options.bootstrap ? readBootstrap(options.bootstrap) : undefined;
|
|
357
|
-
const
|
|
334
|
+
const targetDeviceId = trimOptional(options.targetDeviceId) ??
|
|
335
|
+
trimOptional(bootstrap?.device_id) ??
|
|
336
|
+
trimOptional(bootstrap?.remote_id) ??
|
|
337
|
+
trimOptional(process.env.TIRTC_TARGET_DEVICE_ID);
|
|
358
338
|
const token = options.token?.trim() || bootstrap?.token || process.env.TIRTC_TOKEN?.trim();
|
|
359
|
-
|
|
360
|
-
|
|
339
|
+
const endpoint = trimOptional(options.endpoint) ??
|
|
340
|
+
trimOptional(bootstrap?.endpoint) ??
|
|
341
|
+
trimOptional(process.env.TIRTC_ENDPOINT);
|
|
342
|
+
const appId = trimOptional(options.appId) ??
|
|
343
|
+
trimOptional(bootstrap?.app_id) ??
|
|
344
|
+
trimOptional(process.env.TIRTC_APP_ID);
|
|
345
|
+
const missing = [];
|
|
346
|
+
if (!targetDeviceId) {
|
|
347
|
+
missing.push('target device id (pass --target-device-id, --bootstrap, or set TIRTC_TARGET_DEVICE_ID)');
|
|
361
348
|
}
|
|
362
349
|
if (!token) {
|
|
363
|
-
|
|
350
|
+
missing.push('token (pass --token, --bootstrap, or set TIRTC_TOKEN)');
|
|
364
351
|
}
|
|
365
|
-
|
|
366
|
-
|
|
352
|
+
if (!endpoint) {
|
|
353
|
+
missing.push('endpoint (pass --endpoint, --bootstrap, or set TIRTC_ENDPOINT)');
|
|
354
|
+
}
|
|
355
|
+
if (!appId) {
|
|
356
|
+
missing.push('app id (pass --app-id, --bootstrap, or set TIRTC_APP_ID)');
|
|
357
|
+
}
|
|
358
|
+
if (missing.length > 0 || !targetDeviceId || !token || !endpoint || !appId) {
|
|
359
|
+
throw rolePreflightError('missing_env', 'client start requires ' + missing.join(' and '));
|
|
360
|
+
}
|
|
361
|
+
const executionId = 'cli-client-' + executionSuffix();
|
|
362
|
+
const identityOverride = Boolean(bootstrap && (options.targetDeviceId || options.token));
|
|
367
363
|
const videoCodec = codecOrDefault(bootstrap?.video_codec);
|
|
368
364
|
return {
|
|
369
365
|
schema_version: 1,
|
|
370
366
|
execution_id: executionId,
|
|
371
|
-
case_id: 'devtools-cli-
|
|
372
|
-
role: '
|
|
373
|
-
endpoint
|
|
367
|
+
case_id: 'devtools-cli-client',
|
|
368
|
+
role: 'client',
|
|
369
|
+
endpoint,
|
|
374
370
|
identity: {
|
|
375
|
-
|
|
371
|
+
device_id: targetDeviceId,
|
|
376
372
|
token,
|
|
377
373
|
bootstrap_path: options.bootstrap ? path_1.default.resolve(options.bootstrap) : undefined,
|
|
378
374
|
bootstrap_id: bootstrap?.bootstrap_id,
|
|
@@ -397,7 +393,7 @@ function buildReceiveRequest(roots, artifactRoot, options) {
|
|
|
397
393
|
first_output_timeout_ms: parsePositiveInt(options.firstOutputTimeoutMs, defaultFirstOutputTimeoutMs, '--first-output-timeout-ms'),
|
|
398
394
|
},
|
|
399
395
|
artifact: { root_dir: artifactRoot },
|
|
400
|
-
probe: { app_id:
|
|
396
|
+
probe: { app_id: appId },
|
|
401
397
|
};
|
|
402
398
|
}
|
|
403
399
|
function runDriver(request, artifactRoot, roots) {
|
|
@@ -460,15 +456,15 @@ async function runRole(role, commandOptions, options) {
|
|
|
460
456
|
const artifactRoot = path_1.default.resolve(commandOptions.artifactRoot ?? defaultRoot);
|
|
461
457
|
ensureDir(artifactRoot);
|
|
462
458
|
try {
|
|
463
|
-
const request = role === '
|
|
464
|
-
|
|
465
|
-
|
|
459
|
+
const request = role === 'device' ?
|
|
460
|
+
buildDeviceRequest(roots, artifactRoot, commandOptions) :
|
|
461
|
+
buildClientRequest(roots, artifactRoot, commandOptions);
|
|
466
462
|
const summary = runDriver(request, artifactRoot, roots);
|
|
467
463
|
const summaryPath = path_1.default.join(artifactRoot, 'summary.json');
|
|
468
464
|
const data = {
|
|
469
465
|
status: summary.status,
|
|
470
466
|
exit_code: summary.exit_code,
|
|
471
|
-
role
|
|
467
|
+
role,
|
|
472
468
|
execution_id: summary.execution_id,
|
|
473
469
|
artifact_root: artifactRoot,
|
|
474
470
|
summary_path: summaryPath,
|
|
@@ -497,9 +493,9 @@ async function runRole(role, commandOptions, options) {
|
|
|
497
493
|
return normalized.exitCode;
|
|
498
494
|
}
|
|
499
495
|
}
|
|
500
|
-
function
|
|
501
|
-
return runRole('
|
|
496
|
+
function runDeviceStart(commandOptions, options) {
|
|
497
|
+
return runRole('device', commandOptions, options);
|
|
502
498
|
}
|
|
503
|
-
function
|
|
504
|
-
return runRole('
|
|
499
|
+
function runClientStart(commandOptions, options) {
|
|
500
|
+
return runRole('client', commandOptions, options);
|
|
505
501
|
}
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -91,6 +91,28 @@ typedef enum TirtcError {
|
|
|
91
91
|
TIRTC_ERROR_VIDEO_DECODER_INPUT_PENDING = 6081,
|
|
92
92
|
TIRTC_ERROR_VIDEO_DECODER_OUTPUT_PENDING = 6082,
|
|
93
93
|
TIRTC_ERROR_MEDIA_UPLINK_INPUT_MODE_MISMATCH = 6083,
|
|
94
|
+
TIRTC_ERROR_TRANSPORT_CONNECT_INVOCATION_FAILED = 6084,
|
|
95
|
+
TIRTC_ERROR_TRANSPORT_DISCONNECT_STATE_UNEXPECTED = 6085,
|
|
96
|
+
TIRTC_ERROR_HTTP_TRANSPORT_FAILED = 6086,
|
|
97
|
+
TIRTC_ERROR_LOG_UPLOAD_ENDPOINT_RESPONSE_INVALID = 6087,
|
|
98
|
+
TIRTC_ERROR_LOG_UPLOAD_TOKEN_RESPONSE_INVALID = 6088,
|
|
99
|
+
TIRTC_ERROR_TRANSPORT_BACKEND_UNKNOWN_STATUS = 6089,
|
|
100
|
+
TIRTC_ERROR_TRANSPORT_BACKEND_CONNECTION_OTHER_ERROR = 6090,
|
|
101
|
+
TIRTC_ERROR_FACADE_HANDLE_UNAVAILABLE = 6091,
|
|
102
|
+
TIRTC_ERROR_AUDIO_PROCESSING_STATE_INVALID = 6092,
|
|
103
|
+
TIRTC_ERROR_AUDIO_PROCESSING_CONFIGURATION_FAILED = 6093,
|
|
104
|
+
TIRTC_ERROR_AUDIO_PROCESSING_PROCESS_FAILED = 6094,
|
|
105
|
+
TIRTC_ERROR_VIDEO_ENCODER_STATE_INVALID = 6095,
|
|
106
|
+
TIRTC_ERROR_VIDEO_ENCODER_INPUT_REJECTED = 6096,
|
|
107
|
+
TIRTC_ERROR_VIDEO_ENCODER_OUTPUT_RETRIEVE_FAILED = 6097,
|
|
108
|
+
TIRTC_ERROR_VIDEO_ENCODER_OUTPUT_CONVERSION_FAILED = 6098,
|
|
109
|
+
TIRTC_ERROR_VIDEO_ENCODER_CONTROL_REQUEST_FAILED = 6099,
|
|
110
|
+
TIRTC_ERROR_VIDEO_DECODER_STATE_INVALID = 6100,
|
|
111
|
+
TIRTC_ERROR_VIDEO_DECODER_INPUT_REJECTED = 6101,
|
|
112
|
+
TIRTC_ERROR_VIDEO_DECODER_OUTPUT_RETRIEVE_FAILED = 6102,
|
|
113
|
+
TIRTC_ERROR_VIDEO_DECODER_OUTPUT_CONVERSION_FAILED = 6103,
|
|
114
|
+
TIRTC_ERROR_VIDEO_DECODER_OUTPUT_RELEASE_FAILED = 6104,
|
|
115
|
+
TIRTC_ERROR_TRANSPORT_BACKEND_NOT_INITIALIZED = 6105,
|
|
94
116
|
} TirtcError;
|
|
95
117
|
|
|
96
118
|
const char* tirtc_error_to_string(TirtcError error);
|
|
Binary file
|
|
Binary file
|
|
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-04-
|
|
3
|
+
staged_at_utc=2026-04-30T14:15:15Z
|
|
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
|
|
@@ -14,7 +14,7 @@ c2e1f31dcc75be461c577d18b1cebe32774f212d51cb4dd2a5b5a9bfe62b693e include/tirtc/
|
|
|
14
14
|
21f60729117260a44af22c1af986ef17d22673b102b7b7a035f492d0665cce16 include/tirtc/audio_processing.h
|
|
15
15
|
0ca7c3c630b1242f51a0fd8154097c0a332b4c816a5707090e4381719852998c include/tirtc/audio_sample_rate.h
|
|
16
16
|
58807ba56770f38f47aec995661cadf9d3c1d6ea7bfc940767f1ce67317c7546 include/tirtc/av.h
|
|
17
|
-
|
|
17
|
+
b3e110ce04057d3be696b120d1abeaf6141711a1c63ca7f9bdf65ed5ec6e16d7 include/tirtc/error.h
|
|
18
18
|
ae805545a9515edc9b94262e72ad2c7b7d649288166f4daeb450d8a55e82ae0b include/tirtc/foundation/build_info.h
|
|
19
19
|
7cf8b372a3d48d4de4a65a04c7f102281a7b42cebb9ec247853d3c53afb63b6a include/tirtc/http.h
|
|
20
20
|
70bbf93b84d9d1a85f376d9986de570c1f658319e1e5ab6d621f7a4d41033f5c include/tirtc/logging.h
|
|
@@ -33,13 +33,13 @@ cae0bbeb884e5466a56da15182c78cc22baab6c743f349a58d3595f623333585 include/tirtc/
|
|
|
33
33
|
8cd6b66bea14890a665cc317f8572429b2c3e4463773f8b77c1e4dc30a4a8747 include/tirtc/video_processing.h
|
|
34
34
|
ceb7cdd45efae57e5bd53d4177cb20c4a0cb9917fcdd23127b050d69c8b919ac lib/libTiRTC.a
|
|
35
35
|
b1f4135025cb8e8520a14268d831b9998920a4239f84bb6b409d0e4909fd5bee lib/libcrypto.a
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
296ba0b5efa9d2ac49e3bf278c5d467b31436e73fbc608b736e974bcf9759bde lib/libmatrix_runtime_audio.a
|
|
37
|
+
88f9c545943e4ef80024baf3620e91817ec16a4fea60b0e8c7887a0a41dc9d1b lib/libmatrix_runtime_facade.a
|
|
38
|
+
ec7bbf8e8744c6645a441a28e24e0645906152cc987d6fcca16af3c3badba940 lib/libmatrix_runtime_foundation_http.a
|
|
39
|
+
4038af89ac4dc7e7c99ac9ace58ce063661ddb3134f71664b79221ade4adb8d1 lib/libmatrix_runtime_foundation_logging.a
|
|
40
|
+
9c7108b70cb442dee02cfdb4f98891a8a099f15c379e9e48cf53d7b276f6e894 lib/libmatrix_runtime_media.a
|
|
41
|
+
fc498c74804ef35dc3939a6368d9ae908b0e35578db32eb7902ad00107781295 lib/libmatrix_runtime_transport.a
|
|
42
|
+
337d38891e264e76aeb61a4e25a3b0bad56ab26697948805e64dec920874093f lib/libmatrix_runtime_video.a
|
|
43
43
|
511d521f7972df3993e5976d6e2dbcad7a6fbce9be15071274d0cbd9d51157f5 lib/libssl.a
|
|
44
44
|
a67ec9034848ef24a1b17671e444daa62a8f9e6b8319a1e932593908720ff2a1 lib/libwebrtc_apm.a
|
|
45
45
|
700e455255897e3cffab13ca593a6e4d9d11383ae609215cbbd6043a63d47161 lib/libxlog.a
|
|
@@ -91,6 +91,28 @@ typedef enum TirtcError {
|
|
|
91
91
|
TIRTC_ERROR_VIDEO_DECODER_INPUT_PENDING = 6081,
|
|
92
92
|
TIRTC_ERROR_VIDEO_DECODER_OUTPUT_PENDING = 6082,
|
|
93
93
|
TIRTC_ERROR_MEDIA_UPLINK_INPUT_MODE_MISMATCH = 6083,
|
|
94
|
+
TIRTC_ERROR_TRANSPORT_CONNECT_INVOCATION_FAILED = 6084,
|
|
95
|
+
TIRTC_ERROR_TRANSPORT_DISCONNECT_STATE_UNEXPECTED = 6085,
|
|
96
|
+
TIRTC_ERROR_HTTP_TRANSPORT_FAILED = 6086,
|
|
97
|
+
TIRTC_ERROR_LOG_UPLOAD_ENDPOINT_RESPONSE_INVALID = 6087,
|
|
98
|
+
TIRTC_ERROR_LOG_UPLOAD_TOKEN_RESPONSE_INVALID = 6088,
|
|
99
|
+
TIRTC_ERROR_TRANSPORT_BACKEND_UNKNOWN_STATUS = 6089,
|
|
100
|
+
TIRTC_ERROR_TRANSPORT_BACKEND_CONNECTION_OTHER_ERROR = 6090,
|
|
101
|
+
TIRTC_ERROR_FACADE_HANDLE_UNAVAILABLE = 6091,
|
|
102
|
+
TIRTC_ERROR_AUDIO_PROCESSING_STATE_INVALID = 6092,
|
|
103
|
+
TIRTC_ERROR_AUDIO_PROCESSING_CONFIGURATION_FAILED = 6093,
|
|
104
|
+
TIRTC_ERROR_AUDIO_PROCESSING_PROCESS_FAILED = 6094,
|
|
105
|
+
TIRTC_ERROR_VIDEO_ENCODER_STATE_INVALID = 6095,
|
|
106
|
+
TIRTC_ERROR_VIDEO_ENCODER_INPUT_REJECTED = 6096,
|
|
107
|
+
TIRTC_ERROR_VIDEO_ENCODER_OUTPUT_RETRIEVE_FAILED = 6097,
|
|
108
|
+
TIRTC_ERROR_VIDEO_ENCODER_OUTPUT_CONVERSION_FAILED = 6098,
|
|
109
|
+
TIRTC_ERROR_VIDEO_ENCODER_CONTROL_REQUEST_FAILED = 6099,
|
|
110
|
+
TIRTC_ERROR_VIDEO_DECODER_STATE_INVALID = 6100,
|
|
111
|
+
TIRTC_ERROR_VIDEO_DECODER_INPUT_REJECTED = 6101,
|
|
112
|
+
TIRTC_ERROR_VIDEO_DECODER_OUTPUT_RETRIEVE_FAILED = 6102,
|
|
113
|
+
TIRTC_ERROR_VIDEO_DECODER_OUTPUT_CONVERSION_FAILED = 6103,
|
|
114
|
+
TIRTC_ERROR_VIDEO_DECODER_OUTPUT_RELEASE_FAILED = 6104,
|
|
115
|
+
TIRTC_ERROR_TRANSPORT_BACKEND_NOT_INITIALIZED = 6105,
|
|
94
116
|
} TirtcError;
|
|
95
117
|
|
|
96
118
|
const char* tirtc_error_to_string(TirtcError error);
|
|
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-04-
|
|
3
|
+
staged_at_utc=2026-04-30T14:13:34Z
|
|
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
|
|
@@ -14,7 +14,7 @@ c2e1f31dcc75be461c577d18b1cebe32774f212d51cb4dd2a5b5a9bfe62b693e include/tirtc/
|
|
|
14
14
|
21f60729117260a44af22c1af986ef17d22673b102b7b7a035f492d0665cce16 include/tirtc/audio_processing.h
|
|
15
15
|
0ca7c3c630b1242f51a0fd8154097c0a332b4c816a5707090e4381719852998c include/tirtc/audio_sample_rate.h
|
|
16
16
|
58807ba56770f38f47aec995661cadf9d3c1d6ea7bfc940767f1ce67317c7546 include/tirtc/av.h
|
|
17
|
-
|
|
17
|
+
b3e110ce04057d3be696b120d1abeaf6141711a1c63ca7f9bdf65ed5ec6e16d7 include/tirtc/error.h
|
|
18
18
|
ae805545a9515edc9b94262e72ad2c7b7d649288166f4daeb450d8a55e82ae0b include/tirtc/foundation/build_info.h
|
|
19
19
|
7cf8b372a3d48d4de4a65a04c7f102281a7b42cebb9ec247853d3c53afb63b6a include/tirtc/http.h
|
|
20
20
|
70bbf93b84d9d1a85f376d9986de570c1f658319e1e5ab6d621f7a4d41033f5c include/tirtc/logging.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
|
+
9466d90d68de9ea40a5ef2c5ff393fe94f3ec22f410dcc9c1707253344971a17 lib/libmatrix_runtime_audio.a
|
|
39
|
+
8bc2920d99c7cf575af169baeba9725951d56d3891e2ba4bf50d99023ac4608c lib/libmatrix_runtime_facade.a
|
|
40
|
+
3d3ef3d682052b7ab982b82597a3810e091ab4eb3a6b40177a88d60d0391d800 lib/libmatrix_runtime_foundation_http.a
|
|
41
|
+
e329006b0f653f338c22addb307bd5fca56163e8f6ab6adf3d8ce2fa83c28523 lib/libmatrix_runtime_foundation_logging.a
|
|
42
|
+
1e7e5b699d3625d4ba3b514b21d43ca3915d6a3f15997a74de93d86509d4e58f lib/libmatrix_runtime_media.a
|
|
43
|
+
7945859c05cbc4e55f95b1eee109e0f604d90ff3cbe69bb17e3932b6b094ad01 lib/libmatrix_runtime_transport.a
|
|
44
|
+
b93f5eec5e4fe4278c161095c3b3445f71f78898f4b293efc2b7299d18c91253 lib/libmatrix_runtime_video.a
|
|
45
45
|
c11c65d373a127028350c41fa58cd2d1223f2b5d70a84e13b115d90daaba25ca lib/libssl.a
|
|
46
46
|
ef1c1104bbdd2528ed7b958fb7252bd6249875f92300b0c9577d6c4bd6c0d88a lib/libssl.dylib
|
|
47
47
|
e14e846e43d64e240fa0e5745bf4e702b79d0f2442e7f768beb990610735c71b lib/libtgrtc.dylib
|
|
@@ -20,6 +20,10 @@ fail() {
|
|
|
20
20
|
exit 1
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
progress() {
|
|
24
|
+
echo "[prepare_runtime_media_dataset] progress: $*" >&2
|
|
25
|
+
}
|
|
26
|
+
|
|
23
27
|
require_cmd() {
|
|
24
28
|
command -v "$1" >/dev/null 2>&1 || fail "missing required command: $1"
|
|
25
29
|
}
|
|
@@ -145,6 +149,7 @@ readonly k_audio_codec="g711a"
|
|
|
145
149
|
readonly k_output_audio_format="pcm_s16"
|
|
146
150
|
readonly k_output_video_format="rgba8888"
|
|
147
151
|
|
|
152
|
+
progress "checking source media streams"
|
|
148
153
|
AUDIO_STREAM_PRESENT="$(
|
|
149
154
|
AUDIO_STREAM_JSON="$("$FFPROBE_BIN" -v error -select_streams a:0 \
|
|
150
155
|
-show_entries stream=index -of json "$SOURCE_ABS")" \
|
|
@@ -230,6 +235,7 @@ fi
|
|
|
230
235
|
MANIFEST_PATH="$ASSETS_DIR/manifest.json"
|
|
231
236
|
|
|
232
237
|
if [[ -f "$MANIFEST_PATH" && "$OVERWRITE" -ne 1 ]]; then
|
|
238
|
+
progress "using cached prepared media assets"
|
|
233
239
|
emit_result "$ASSETS_DIR" "$MANIFEST_PATH" true
|
|
234
240
|
exit 0
|
|
235
241
|
fi
|
|
@@ -274,6 +280,7 @@ else
|
|
|
274
280
|
video_encode_args+=(-crf 18)
|
|
275
281
|
fi
|
|
276
282
|
|
|
283
|
+
progress "encoding h264 video track"
|
|
277
284
|
"$FFMPEG_BIN" -hide_banner -loglevel error -y \
|
|
278
285
|
-i "$SOURCE_ABS" \
|
|
279
286
|
-an \
|
|
@@ -302,6 +309,7 @@ else
|
|
|
302
309
|
video_h265_encode_args+=(-crf 23)
|
|
303
310
|
fi
|
|
304
311
|
|
|
312
|
+
progress "encoding h265 video track"
|
|
305
313
|
"$FFMPEG_BIN" -hide_banner -loglevel error -y \
|
|
306
314
|
-i "$SOURCE_ABS" \
|
|
307
315
|
-an \
|
|
@@ -311,6 +319,7 @@ fi
|
|
|
311
319
|
|
|
312
320
|
mjpeg_frame_dir="$ASSETS_DIR/video/mjpeg_frames"
|
|
313
321
|
mkdir -p "$mjpeg_frame_dir"
|
|
322
|
+
progress "extracting mjpeg video frames"
|
|
314
323
|
"$FFMPEG_BIN" -hide_banner -loglevel error -y \
|
|
315
324
|
-i "$SOURCE_ABS" \
|
|
316
325
|
-vf "fps=1/${k_mjpeg_extract_interval_seconds},scale=${TARGET_VIDEO_WIDTH}:${TARGET_VIDEO_HEIGHT}:flags=lanczos" \
|
|
@@ -326,6 +335,7 @@ if ! find "$mjpeg_frame_dir" -type f -name 'frame_*.jpg' | grep -q .; then
|
|
|
326
335
|
"$mjpeg_frame_dir/frame_000001.jpg"
|
|
327
336
|
fi
|
|
328
337
|
|
|
338
|
+
progress "encoding 8k g711a audio track"
|
|
329
339
|
"$FFMPEG_BIN" -hide_banner -loglevel error -y \
|
|
330
340
|
-i "$SOURCE_ABS" \
|
|
331
341
|
-vn \
|
|
@@ -335,6 +345,7 @@ fi
|
|
|
335
345
|
-f alaw \
|
|
336
346
|
"$AUDIO_PATH"
|
|
337
347
|
|
|
348
|
+
progress "encoding 16k g711a audio track"
|
|
338
349
|
"$FFMPEG_BIN" -hide_banner -loglevel error -y \
|
|
339
350
|
-i "$SOURCE_ABS" \
|
|
340
351
|
-vn \
|
|
@@ -344,6 +355,7 @@ fi
|
|
|
344
355
|
-f alaw \
|
|
345
356
|
"$AUDIO_16K_PATH"
|
|
346
357
|
|
|
358
|
+
progress "indexing h264 video packets"
|
|
347
359
|
VIDEO_PACKET_INDEX_PATH="$VIDEO_H264_PACKET_INDEX_PATH" \
|
|
348
360
|
VIDEO_PATH="$VIDEO_H264_PATH" \
|
|
349
361
|
VIDEO_FPS="$k_video_fps" \
|
|
@@ -441,6 +453,7 @@ for (let index = 0; index < packetOffsets.length; index += 1) {
|
|
|
441
453
|
fs.writeFileSync(outputPath, `${lines.join('\n')}\n`, 'utf8');
|
|
442
454
|
NODE
|
|
443
455
|
|
|
456
|
+
progress "indexing h265 video packets"
|
|
444
457
|
VIDEO_PACKET_INDEX_PATH="$VIDEO_H265_PACKET_INDEX_PATH" \
|
|
445
458
|
VIDEO_PATH="$VIDEO_H265_PATH" \
|
|
446
459
|
VIDEO_FPS="$k_video_fps" \
|
|
@@ -538,6 +551,7 @@ for (let index = 0; index < packetOffsets.length; index += 1) {
|
|
|
538
551
|
fs.writeFileSync(outputPath, `${lines.join('\n')}\n`, 'utf8');
|
|
539
552
|
NODE
|
|
540
553
|
|
|
554
|
+
progress "indexing mjpeg video packets"
|
|
541
555
|
VIDEO_MJPEG_PATH="$VIDEO_MJPEG_PATH" \
|
|
542
556
|
VIDEO_MJPEG_PACKET_INDEX_PATH="$VIDEO_MJPEG_PACKET_INDEX_PATH" \
|
|
543
557
|
MJPEG_FRAME_DIR="$mjpeg_frame_dir" \
|
|
@@ -593,6 +607,7 @@ for (let index = 0; index < packetCount; index += 1) {
|
|
|
593
607
|
fs.writeFileSync(indexPath, `${lines.join('\n')}\n`, 'utf8');
|
|
594
608
|
NODE
|
|
595
609
|
|
|
610
|
+
progress "indexing 8k g711a audio packets"
|
|
596
611
|
AUDIO_BYTES="$(wc -c < "$AUDIO_PATH" | tr -d '[:space:]')"
|
|
597
612
|
AUDIO_PACKET_BYTES=$((k_audio_sample_rate_hz * k_audio_packet_duration_ms / 1000))
|
|
598
613
|
[[ "$AUDIO_PACKET_BYTES" -gt 0 ]] || fail "invalid audio packet bytes"
|
|
@@ -624,6 +639,7 @@ AUDIO_PACKET_COUNT=$((AUDIO_BYTES / AUDIO_PACKET_BYTES))
|
|
|
624
639
|
done
|
|
625
640
|
} > "$AUDIO_PACKET_INDEX_PATH"
|
|
626
641
|
|
|
642
|
+
progress "indexing 16k g711a audio packets"
|
|
627
643
|
AUDIO_16K_BYTES="$(wc -c < "$AUDIO_16K_PATH" | tr -d '[:space:]')"
|
|
628
644
|
AUDIO_16K_PACKET_BYTES=$((k_audio_sample_rate_16k_hz * k_audio_packet_duration_ms / 1000))
|
|
629
645
|
[[ "$AUDIO_16K_PACKET_BYTES" -gt 0 ]] || fail "invalid 16k audio packet bytes"
|
|
@@ -655,6 +671,7 @@ AUDIO_16K_PACKET_COUNT=$((AUDIO_16K_BYTES / AUDIO_16K_PACKET_BYTES))
|
|
|
655
671
|
done
|
|
656
672
|
} > "$AUDIO_16K_PACKET_INDEX_PATH"
|
|
657
673
|
|
|
674
|
+
progress "writing prepared media manifest"
|
|
658
675
|
cat > "$MANIFEST_PATH" <<JSON
|
|
659
676
|
{
|
|
660
677
|
"schema_version": ${k_schema_version},
|