tirtc-devtools-cli 0.0.1

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.
Files changed (156) hide show
  1. package/README.md +52 -0
  2. package/USAGE.md +417 -0
  3. package/bin/tirtc-devtool.js +2 -0
  4. package/dist/app-server/protocol-client/index.d.ts +25 -0
  5. package/dist/app-server/protocol-client/index.js +114 -0
  6. package/dist/devtools/cli/src/config.d.ts +46 -0
  7. package/dist/devtools/cli/src/config.js +98 -0
  8. package/dist/devtools/cli/src/dummy.d.ts +0 -0
  9. package/dist/devtools/cli/src/dummy.js +1 -0
  10. package/dist/devtools/cli/src/embedded_paths.d.ts +7 -0
  11. package/dist/devtools/cli/src/embedded_paths.js +85 -0
  12. package/dist/devtools/cli/src/facade.d.ts +723 -0
  13. package/dist/devtools/cli/src/facade.js +194 -0
  14. package/dist/devtools/cli/src/ffmpeg_tool.d.ts +6 -0
  15. package/dist/devtools/cli/src/ffmpeg_tool.js +146 -0
  16. package/dist/devtools/cli/src/guide.d.ts +1 -0
  17. package/dist/devtools/cli/src/guide.js +49 -0
  18. package/dist/devtools/cli/src/index.d.ts +1 -0
  19. package/dist/devtools/cli/src/index.js +753 -0
  20. package/dist/devtools/cli/src/media_assets.d.ts +25 -0
  21. package/dist/devtools/cli/src/media_assets.js +121 -0
  22. package/dist/devtools/cli/src/session_manager.d.ts +25 -0
  23. package/dist/devtools/cli/src/session_manager.js +393 -0
  24. package/dist/devtools/cli/src/token_tool.d.ts +33 -0
  25. package/dist/devtools/cli/src/token_tool.js +217 -0
  26. package/dist/devtools/cli/src/transport.d.ts +30 -0
  27. package/dist/devtools/cli/src/transport.js +84 -0
  28. package/dist/dummy.d.ts +0 -0
  29. package/dist/dummy.js +1 -0
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.js +48 -0
  32. package/package.json +54 -0
  33. package/script/ensure_ffmpeg.sh +22 -0
  34. package/vendor/app-server/bin/native/macos-arm64/credential_napi.node +0 -0
  35. package/vendor/app-server/bin/native/macos-arm64/libcrypto.dylib +0 -0
  36. package/vendor/app-server/bin/native/macos-arm64/libssl.dylib +0 -0
  37. package/vendor/app-server/bin/native/macos-arm64/libtgrtc.dylib +0 -0
  38. package/vendor/app-server/bin/native/macos-arm64/runtime_host_napi.node +0 -0
  39. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_codec.h +23 -0
  40. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_frame.h +36 -0
  41. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_io.h +56 -0
  42. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_io_android.h +19 -0
  43. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_io_apple.h +19 -0
  44. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_io_harmony.h +19 -0
  45. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_io_windows.h +19 -0
  46. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_processing.h +56 -0
  47. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_sample_rate.h +18 -0
  48. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/error.h +20 -0
  49. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/logging.h +53 -0
  50. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/media_codec.h +21 -0
  51. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/media_downlink.h +89 -0
  52. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/media_uplink.h +115 -0
  53. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/runtime.h +236 -0
  54. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/video_codec.h +57 -0
  55. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/video_frame.h +55 -0
  56. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/video_io.h +46 -0
  57. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/video_io_android.h +32 -0
  58. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/video_io_apple.h +34 -0
  59. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/video_io_harmony.h +32 -0
  60. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/video_io_windows.h +26 -0
  61. package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/video_processing.h +34 -0
  62. package/vendor/app-server/bin/runtime/linux-x64/lib/libmatrix_runtime_audio.a +0 -0
  63. package/vendor/app-server/bin/runtime/linux-x64/lib/libmatrix_runtime_facade.a +0 -0
  64. package/vendor/app-server/bin/runtime/linux-x64/lib/libmatrix_runtime_foundation_logging.a +0 -0
  65. package/vendor/app-server/bin/runtime/linux-x64/lib/libmatrix_runtime_media.a +0 -0
  66. package/vendor/app-server/bin/runtime/linux-x64/lib/libmatrix_runtime_video.a +0 -0
  67. package/vendor/app-server/bin/runtime/linux-x64/lib/libwebrtc_apm.a +0 -0
  68. package/vendor/app-server/bin/runtime/linux-x64/lib/libxlog.a +0 -0
  69. package/vendor/app-server/bin/runtime/linux-x64/manifest.txt +34 -0
  70. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio.h +398 -0
  71. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_codec.h +23 -0
  72. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_frame.h +36 -0
  73. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_io.h +56 -0
  74. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_io_android.h +19 -0
  75. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_io_apple.h +19 -0
  76. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_io_harmony.h +19 -0
  77. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_io_windows.h +19 -0
  78. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_processing.h +56 -0
  79. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_sample_rate.h +18 -0
  80. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/av.h +452 -0
  81. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/credential.h +34 -0
  82. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/error.h +30 -0
  83. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/foundation/build_info.h +27 -0
  84. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/http.h +57 -0
  85. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/logging.h +55 -0
  86. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/media_codec.h +21 -0
  87. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/media_downlink.h +95 -0
  88. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/media_fixture_av_sync.h +61 -0
  89. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/media_fixture_source.h +77 -0
  90. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/media_live_source.h +71 -0
  91. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/media_uplink.h +116 -0
  92. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/transport.h +481 -0
  93. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/trp.h +541 -0
  94. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/video_codec.h +58 -0
  95. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/video_frame.h +55 -0
  96. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/video_io.h +46 -0
  97. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/video_io_android.h +32 -0
  98. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/video_io_apple.h +47 -0
  99. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/video_io_harmony.h +32 -0
  100. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/video_io_windows.h +26 -0
  101. package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/video_processing.h +34 -0
  102. package/vendor/app-server/bin/runtime/macos-arm64/lib/libcrypto.a +0 -0
  103. package/vendor/app-server/bin/runtime/macos-arm64/lib/libcrypto.dylib +0 -0
  104. package/vendor/app-server/bin/runtime/macos-arm64/lib/libmatrix_runtime_audio.a +0 -0
  105. package/vendor/app-server/bin/runtime/macos-arm64/lib/libmatrix_runtime_credential.a +0 -0
  106. package/vendor/app-server/bin/runtime/macos-arm64/lib/libmatrix_runtime_facade.a +0 -0
  107. package/vendor/app-server/bin/runtime/macos-arm64/lib/libmatrix_runtime_foundation_http.a +0 -0
  108. package/vendor/app-server/bin/runtime/macos-arm64/lib/libmatrix_runtime_foundation_logging.a +0 -0
  109. package/vendor/app-server/bin/runtime/macos-arm64/lib/libmatrix_runtime_media.a +0 -0
  110. package/vendor/app-server/bin/runtime/macos-arm64/lib/libmatrix_runtime_transport.a +0 -0
  111. package/vendor/app-server/bin/runtime/macos-arm64/lib/libmatrix_runtime_video.a +0 -0
  112. package/vendor/app-server/bin/runtime/macos-arm64/lib/libssl.a +0 -0
  113. package/vendor/app-server/bin/runtime/macos-arm64/lib/libssl.dylib +0 -0
  114. package/vendor/app-server/bin/runtime/macos-arm64/lib/libwebrtc_apm.a +0 -0
  115. package/vendor/app-server/bin/runtime/macos-arm64/lib/libxlog.a +0 -0
  116. package/vendor/app-server/bin/runtime/macos-arm64/manifest.txt +50 -0
  117. package/vendor/app-server/bin/tirtc-devtool-host.js +2 -0
  118. package/vendor/app-server/dist/host/ArtifactManager.d.ts +18 -0
  119. package/vendor/app-server/dist/host/ArtifactManager.js +83 -0
  120. package/vendor/app-server/dist/host/HostProtocol.d.ts +107 -0
  121. package/vendor/app-server/dist/host/HostProtocol.js +256 -0
  122. package/vendor/app-server/dist/host/HostServer.d.ts +49 -0
  123. package/vendor/app-server/dist/host/HostServer.js +635 -0
  124. package/vendor/app-server/dist/host/HostState.d.ts +60 -0
  125. package/vendor/app-server/dist/host/HostState.js +19 -0
  126. package/vendor/app-server/dist/host/RuntimeAdapter.d.ts +81 -0
  127. package/vendor/app-server/dist/host/RuntimeAdapter.js +559 -0
  128. package/vendor/app-server/dist/host/RuntimeCredentialTokenIssuer.d.ts +30 -0
  129. package/vendor/app-server/dist/host/RuntimeCredentialTokenIssuer.js +224 -0
  130. package/vendor/app-server/dist/host/RuntimeReceiveWorker.d.ts +37 -0
  131. package/vendor/app-server/dist/host/RuntimeReceiveWorker.js +186 -0
  132. package/vendor/app-server/dist/host/RuntimeSendWorker.d.ts +42 -0
  133. package/vendor/app-server/dist/host/RuntimeSendWorker.js +274 -0
  134. package/vendor/app-server/dist/host/TokenTool.d.ts +15 -0
  135. package/vendor/app-server/dist/host/TokenTool.js +84 -0
  136. package/vendor/app-server/dist/host/WebPreviewGateway.d.ts +28 -0
  137. package/vendor/app-server/dist/host/WebPreviewGateway.js +815 -0
  138. package/vendor/app-server/dist/host/native/RuntimeCredentialTokenIssuer.d.ts +26 -0
  139. package/vendor/app-server/dist/host/native/RuntimeCredentialTokenIssuer.js +118 -0
  140. package/vendor/app-server/dist/host/native/RuntimeHostBridge.d.ts +19 -0
  141. package/vendor/app-server/dist/host/native/RuntimeHostBridge.js +141 -0
  142. package/vendor/app-server/dist/host/runtime_backed_preflight.d.ts +10 -0
  143. package/vendor/app-server/dist/host/runtime_backed_preflight.js +78 -0
  144. package/vendor/app-server/dist/host/tests/helpers/host_socket_client.d.ts +28 -0
  145. package/vendor/app-server/dist/host/tests/helpers/host_socket_client.js +85 -0
  146. package/vendor/app-server/dist/host/tests/helpers/runtime_e2e_local_config.d.ts +10 -0
  147. package/vendor/app-server/dist/host/tests/helpers/runtime_e2e_local_config.js +41 -0
  148. package/vendor/app-server/dist/host/tests/helpers/runtime_test_env.d.ts +11 -0
  149. package/vendor/app-server/dist/host/tests/helpers/runtime_test_env.js +32 -0
  150. package/vendor/app-server/dist/protocol/contract.d.ts +983 -0
  151. package/vendor/app-server/dist/protocol/contract.js +198 -0
  152. package/vendor/app-server/dist/protocol-client/index.d.ts +25 -0
  153. package/vendor/app-server/dist/protocol-client/index.js +114 -0
  154. package/vendor/app-server/dist/src/index.d.ts +1 -0
  155. package/vendor/app-server/dist/src/index.js +294 -0
  156. package/vendor/runtime/script/prepare_runtime_media_dataset.sh +427 -0
@@ -0,0 +1,427 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ print_usage() {
5
+ cat <<'USAGE'
6
+ Usage:
7
+ prepare_runtime_media_dataset.sh --source <mp4> --output-root <dir> [--output-dir <dir>] [--overwrite]
8
+
9
+ Options:
10
+ --source <mp4> source mp4 file
11
+ --output-root <dir> root directory that stores prepared local assets
12
+ --output-dir <dir> exact prepared local assets directory (overrides hashed subdir)
13
+ --overwrite rebuild prepared assets even when cache entry exists
14
+ -h, --help show help
15
+ USAGE
16
+ }
17
+
18
+ fail() {
19
+ echo "[prepare_runtime_media_dataset] error: $*" >&2
20
+ exit 1
21
+ }
22
+
23
+ require_cmd() {
24
+ command -v "$1" >/dev/null 2>&1 || fail "missing required command: $1"
25
+ }
26
+
27
+ sha256_file() {
28
+ local file="$1"
29
+ if command -v shasum >/dev/null 2>&1; then
30
+ shasum -a 256 "$file" | awk '{print $1}'
31
+ return
32
+ fi
33
+ if command -v sha256sum >/dev/null 2>&1; then
34
+ sha256sum "$file" | awk '{print $1}'
35
+ return
36
+ fi
37
+ if command -v openssl >/dev/null 2>&1; then
38
+ openssl dgst -sha256 "$file" | awk '{print $NF}'
39
+ return
40
+ fi
41
+ fail "no sha256 tool found (shasum/sha256sum/openssl)"
42
+ }
43
+
44
+ json_escape() {
45
+ local input="$1"
46
+ input="${input//\\/\\\\}"
47
+ input="${input//\"/\\\"}"
48
+ input="${input//$'\n'/\\n}"
49
+ input="${input//$'\r'/\\r}"
50
+ input="${input//$'\t'/\\t}"
51
+ printf '%s' "$input"
52
+ }
53
+
54
+ emit_result() {
55
+ local assets_dir="$1"
56
+ local manifest_path="$2"
57
+ local cache_hit="$3"
58
+ printf '{"assets_dir":"%s","manifest_path":"%s","cache_hit":%s}\n' \
59
+ "$(json_escape "$assets_dir")" \
60
+ "$(json_escape "$manifest_path")" \
61
+ "$cache_hit"
62
+ }
63
+
64
+ SOURCE=""
65
+ OUTPUT_ROOT=""
66
+ OUTPUT_DIR=""
67
+ OVERWRITE=0
68
+
69
+ while [[ $# -gt 0 ]]; do
70
+ case "$1" in
71
+ --source)
72
+ SOURCE="${2:-}"
73
+ shift 2
74
+ ;;
75
+ --output-root)
76
+ OUTPUT_ROOT="${2:-}"
77
+ shift 2
78
+ ;;
79
+ --output-dir)
80
+ OUTPUT_DIR="${2:-}"
81
+ shift 2
82
+ ;;
83
+ --overwrite)
84
+ OVERWRITE=1
85
+ shift
86
+ ;;
87
+ -h|--help)
88
+ print_usage
89
+ exit 0
90
+ ;;
91
+ *)
92
+ fail "unknown argument: $1"
93
+ ;;
94
+ esac
95
+ done
96
+
97
+ [[ -n "$SOURCE" ]] || fail "--source is required"
98
+ [[ -n "$OUTPUT_ROOT" ]] || fail "--output-root is required"
99
+ [[ -f "$SOURCE" ]] || fail "source not found: $SOURCE"
100
+ [[ "$SOURCE" == *.mp4 ]] || fail "source must be an .mp4 file: $SOURCE"
101
+
102
+ require_cmd node
103
+
104
+ FFMPEG_BIN="ffmpeg"
105
+ FFPROBE_BIN="ffprobe"
106
+ if [[ -n "${TIRTC_ENSURE_FFMPEG_SCRIPT:-}" ]]; then
107
+ [[ -x "$TIRTC_ENSURE_FFMPEG_SCRIPT" ]] || fail "ffmpeg bootstrap script is not executable: $TIRTC_ENSURE_FFMPEG_SCRIPT"
108
+ FFMPEG_TOOLS_JSON="$($TIRTC_ENSURE_FFMPEG_SCRIPT)" || fail "ffmpeg bootstrap failed"
109
+ FFMPEG_FIELDS="$(FFMPEG_TOOLS_JSON="$FFMPEG_TOOLS_JSON" node <<'NODE'
110
+ const payload = process.env.FFMPEG_TOOLS_JSON;
111
+ if (!payload) {
112
+ throw new Error('missing FFMPEG_TOOLS_JSON');
113
+ }
114
+ const parsed = JSON.parse(payload);
115
+ if (!parsed.ffmpeg || !parsed.ffprobe) {
116
+ throw new Error('ffmpeg bootstrap returned incomplete result');
117
+ }
118
+ process.stdout.write(String(parsed.ffmpeg) + ' ' + String(parsed.ffprobe));
119
+ NODE
120
+ )"
121
+ IFS=$' ' read -r FFMPEG_BIN FFPROBE_BIN <<EOF
122
+ $FFMPEG_FIELDS
123
+ EOF
124
+ fi
125
+
126
+ require_cmd "$FFMPEG_BIN"
127
+ require_cmd "$FFPROBE_BIN"
128
+
129
+ SOURCE_ABS="$(cd "$(dirname "$SOURCE")" && pwd)/$(basename "$SOURCE")"
130
+ SOURCE_FILE_NAME="$(basename "$SOURCE_ABS")"
131
+
132
+ readonly k_schema_version=1
133
+ readonly k_tool_version="runtime_local_assets_prepare_v4"
134
+ readonly k_video_max_width=1280
135
+ readonly k_video_max_height=720
136
+ readonly k_video_fps=15
137
+ readonly k_audio_sample_rate_hz=8000
138
+ readonly k_audio_channels=1
139
+ readonly k_audio_packet_duration_ms=40
140
+ readonly k_loop_policy="continuous_pts"
141
+ readonly k_video_codec="h264_annexb"
142
+ readonly k_audio_codec="g711a"
143
+ readonly k_output_audio_format="pcm_s16"
144
+ readonly k_output_video_format="rgba8888"
145
+
146
+ VIDEO_STREAM_FIELDS="$(
147
+ VIDEO_STREAM_JSON="$("$FFPROBE_BIN" -v error -select_streams v:0 \
148
+ -show_entries stream=width,height,bit_rate -of json "$SOURCE_ABS")" \
149
+ MAX_WIDTH="$k_video_max_width" \
150
+ MAX_HEIGHT="$k_video_max_height" \
151
+ node <<'NODE'
152
+ const payload = process.env.VIDEO_STREAM_JSON;
153
+ if (!payload) {
154
+ throw new Error('missing VIDEO_STREAM_JSON');
155
+ }
156
+ const maxWidth = Number(process.env.MAX_WIDTH || '1280');
157
+ const maxHeight = Number(process.env.MAX_HEIGHT || '720');
158
+ if (!Number.isFinite(maxWidth) || maxWidth <= 0 || !Number.isFinite(maxHeight) || maxHeight <= 0) {
159
+ throw new Error('invalid target video bound');
160
+ }
161
+ const parsed = JSON.parse(payload);
162
+ const stream = Array.isArray(parsed.streams) ? parsed.streams[0] : null;
163
+ if (!stream) {
164
+ throw new Error('source mp4 is missing a video stream');
165
+ }
166
+ const width = Number(stream.width || 0);
167
+ const height = Number(stream.height || 0);
168
+ if (!Number.isFinite(width) || width <= 0 || !Number.isFinite(height) || height <= 0) {
169
+ throw new Error('source video dimensions are invalid');
170
+ }
171
+ const bitRate = Number(stream.bit_rate || 0);
172
+ const bitrateKbps = Number.isFinite(bitRate) && bitRate > 0 ? Math.max(1, Math.ceil(bitRate / 1000)) : 0;
173
+ const scale = Math.min(1, maxWidth / width, maxHeight / height);
174
+ let targetWidth = Math.floor(width * scale);
175
+ let targetHeight = Math.floor(height * scale);
176
+ if ((targetWidth % 2) !== 0 && targetWidth > 2) {
177
+ targetWidth -= 1;
178
+ }
179
+ if ((targetHeight % 2) !== 0 && targetHeight > 2) {
180
+ targetHeight -= 1;
181
+ }
182
+ targetWidth = Math.max(2, targetWidth);
183
+ targetHeight = Math.max(2, targetHeight);
184
+ const scaledBitrateKbps = bitrateKbps > 0
185
+ ? Math.max(1, Math.ceil(bitrateKbps * ((targetWidth * targetHeight) / (width * height))))
186
+ : 0;
187
+ process.stdout.write([
188
+ String(width),
189
+ String(height),
190
+ String(bitrateKbps),
191
+ String(targetWidth),
192
+ String(targetHeight),
193
+ String(scaledBitrateKbps),
194
+ ].join('\t'));
195
+ NODE
196
+ )"
197
+ IFS=$'\t' read -r SOURCE_VIDEO_WIDTH SOURCE_VIDEO_HEIGHT SOURCE_VIDEO_BITRATE_KBPS TARGET_VIDEO_WIDTH TARGET_VIDEO_HEIGHT TARGET_VIDEO_BITRATE_KBPS <<EOF
198
+ $VIDEO_STREAM_FIELDS
199
+ EOF
200
+
201
+ [[ "$SOURCE_VIDEO_WIDTH" -gt 0 ]] || fail "failed to resolve source video width"
202
+ [[ "$SOURCE_VIDEO_HEIGHT" -gt 0 ]] || fail "failed to resolve source video height"
203
+ [[ "$TARGET_VIDEO_WIDTH" -gt 0 ]] || fail "failed to resolve target video width"
204
+ [[ "$TARGET_VIDEO_HEIGHT" -gt 0 ]] || fail "failed to resolve target video height"
205
+
206
+ mkdir -p "$OUTPUT_ROOT"
207
+ SOURCE_SHA256="$(sha256_file "$SOURCE_ABS")"
208
+ ASSETS_ID="${SOURCE_SHA256}_s${k_schema_version}_${k_tool_version}"
209
+ ASSETS_DIR="$OUTPUT_ROOT/$ASSETS_ID"
210
+ if [[ -n "$OUTPUT_DIR" ]]; then
211
+ ASSETS_DIR="$OUTPUT_DIR"
212
+ fi
213
+ MANIFEST_PATH="$ASSETS_DIR/manifest.json"
214
+
215
+ if [[ -f "$MANIFEST_PATH" && "$OVERWRITE" -ne 1 ]]; then
216
+ emit_result "$ASSETS_DIR" "$MANIFEST_PATH" true
217
+ exit 0
218
+ fi
219
+
220
+ if [[ -e "$ASSETS_DIR" ]]; then
221
+ rm -rf "$ASSETS_DIR"
222
+ fi
223
+
224
+ mkdir -p "$ASSETS_DIR/video"
225
+ mkdir -p "$ASSETS_DIR/audio"
226
+
227
+ VIDEO_PATH="$ASSETS_DIR/video/video.h264"
228
+ VIDEO_PACKET_INDEX_PATH="$ASSETS_DIR/video/video_packets.csv"
229
+ AUDIO_PATH="$ASSETS_DIR/audio/audio.g711a"
230
+ AUDIO_PACKET_INDEX_PATH="$ASSETS_DIR/audio/audio_packets.csv"
231
+
232
+ video_encode_args=(
233
+ -vf "fps=${k_video_fps},scale=${TARGET_VIDEO_WIDTH}:${TARGET_VIDEO_HEIGHT}:flags=lanczos"
234
+ -c:v libx264
235
+ -preset medium
236
+ -tune zerolatency
237
+ -pix_fmt yuv420p
238
+ -bf 0
239
+ -g "${k_video_fps}"
240
+ -keyint_min "${k_video_fps}"
241
+ -sc_threshold 0
242
+ -x264-params repeat-headers=1:aud=1
243
+ )
244
+ if [[ "$TARGET_VIDEO_BITRATE_KBPS" -gt 0 ]]; then
245
+ video_encode_args+=(
246
+ -b:v "${TARGET_VIDEO_BITRATE_KBPS}k"
247
+ -maxrate "${TARGET_VIDEO_BITRATE_KBPS}k"
248
+ -bufsize "$((TARGET_VIDEO_BITRATE_KBPS * 2))k"
249
+ )
250
+ else
251
+ video_encode_args+=(-crf 18)
252
+ fi
253
+
254
+ "$FFMPEG_BIN" -hide_banner -loglevel error -y \
255
+ -i "$SOURCE_ABS" \
256
+ -an \
257
+ "${video_encode_args[@]}" \
258
+ -f h264 \
259
+ "$VIDEO_PATH"
260
+
261
+ "$FFMPEG_BIN" -hide_banner -loglevel error -y \
262
+ -i "$SOURCE_ABS" \
263
+ -vn \
264
+ -ac ${k_audio_channels} \
265
+ -ar ${k_audio_sample_rate_hz} \
266
+ -c:a pcm_alaw \
267
+ -f alaw \
268
+ "$AUDIO_PATH"
269
+
270
+ VIDEO_PACKET_INDEX_PATH="$VIDEO_PACKET_INDEX_PATH" \
271
+ VIDEO_PATH="$VIDEO_PATH" \
272
+ VIDEO_FPS="$k_video_fps" \
273
+ node <<'NODE'
274
+ const fs = require('fs');
275
+
276
+ const videoPath = process.env.VIDEO_PATH;
277
+ const outputPath = process.env.VIDEO_PACKET_INDEX_PATH;
278
+ const fps = Number(process.env.VIDEO_FPS || '15');
279
+
280
+ if (!videoPath || !outputPath || !Number.isFinite(fps) || fps <= 0) {
281
+ throw new Error('video packet index env is incomplete');
282
+ }
283
+
284
+ const data = fs.readFileSync(videoPath);
285
+ const starts = [];
286
+ for (let index = 0; index < data.length - 3; index += 1) {
287
+ if (data[index] === 0 && data[index + 1] === 0) {
288
+ if (data[index + 2] === 1) {
289
+ starts.push({offset: index, prefix: 3});
290
+ index += 2;
291
+ continue;
292
+ }
293
+ if (index + 3 < data.length && data[index + 2] === 0 && data[index + 3] === 1) {
294
+ starts.push({offset: index, prefix: 4});
295
+ index += 3;
296
+ }
297
+ }
298
+ }
299
+
300
+ if (starts.length === 0) {
301
+ throw new Error('video bitstream missing annexb start codes');
302
+ }
303
+
304
+ const packetOffsets = [];
305
+ let currentPacketStart = 0;
306
+ let currentPacketHasData = false;
307
+ let currentPacketKeyFrame = false;
308
+
309
+ for (let index = 0; index < starts.length; index += 1) {
310
+ const start = starts[index];
311
+ const nextOffset = index + 1 < starts.length ? starts[index + 1].offset : data.length;
312
+ const nalHeaderOffset = start.offset + start.prefix;
313
+ if (nalHeaderOffset >= nextOffset) {
314
+ continue;
315
+ }
316
+
317
+ const nalType = data[nalHeaderOffset] & 0x1f;
318
+ if (nalType === 9) {
319
+ if (currentPacketHasData) {
320
+ packetOffsets.push({
321
+ offset: currentPacketStart,
322
+ size: start.offset - currentPacketStart,
323
+ keyFrame: currentPacketKeyFrame,
324
+ });
325
+ }
326
+ currentPacketStart = start.offset;
327
+ currentPacketHasData = true;
328
+ currentPacketKeyFrame = false;
329
+ continue;
330
+ }
331
+
332
+ if (!currentPacketHasData) {
333
+ currentPacketStart = start.offset;
334
+ currentPacketHasData = true;
335
+ currentPacketKeyFrame = false;
336
+ }
337
+ if (nalType === 5) {
338
+ currentPacketKeyFrame = true;
339
+ }
340
+ }
341
+
342
+ if (currentPacketHasData && currentPacketStart < data.length) {
343
+ packetOffsets.push({
344
+ offset: currentPacketStart,
345
+ size: data.length - currentPacketStart,
346
+ keyFrame: currentPacketKeyFrame,
347
+ });
348
+ }
349
+
350
+ if (packetOffsets.length === 0) {
351
+ throw new Error('video packet index is empty');
352
+ }
353
+
354
+ const frameDurationUs = Math.round(1_000_000 / fps);
355
+ const lines = ['pts_us,offset,size,is_key_frame'];
356
+ for (let index = 0; index < packetOffsets.length; index += 1) {
357
+ const entry = packetOffsets[index];
358
+ lines.push(`${index * frameDurationUs},${entry.offset},${entry.size},${entry.keyFrame ? 1 : 0}`);
359
+ }
360
+ fs.writeFileSync(outputPath, `${lines.join('\n')}\n`, 'utf8');
361
+ NODE
362
+
363
+ AUDIO_BYTES="$(wc -c < "$AUDIO_PATH" | tr -d '[:space:]')"
364
+ AUDIO_PACKET_BYTES=$((k_audio_sample_rate_hz * k_audio_packet_duration_ms / 1000))
365
+ [[ "$AUDIO_PACKET_BYTES" -gt 0 ]] || fail "invalid audio packet bytes"
366
+ AUDIO_REMAINDER=$((AUDIO_BYTES % AUDIO_PACKET_BYTES))
367
+ if [[ "$AUDIO_REMAINDER" -ne 0 ]]; then
368
+ AUDIO_ALIGNED_BYTES=$((AUDIO_BYTES - AUDIO_REMAINDER))
369
+ [[ "$AUDIO_ALIGNED_BYTES" -gt 0 ]] || fail "audio output is too short for one full packet"
370
+ echo "[prepare_runtime_media_dataset] trim trailing audio bytes to packet boundary: removed=${AUDIO_REMAINDER}" >&2
371
+ AUDIO_PATH="$AUDIO_PATH" AUDIO_ALIGNED_BYTES="$AUDIO_ALIGNED_BYTES" node <<'NODE'
372
+ const fs = require('fs');
373
+ const audioPath = process.env.AUDIO_PATH;
374
+ const alignedBytes = Number(process.env.AUDIO_ALIGNED_BYTES || '0');
375
+ if (!audioPath || !Number.isFinite(alignedBytes) || alignedBytes <= 0) {
376
+ throw new Error('audio trim env is incomplete');
377
+ }
378
+ fs.truncateSync(audioPath, alignedBytes);
379
+ NODE
380
+ AUDIO_BYTES="$AUDIO_ALIGNED_BYTES"
381
+ fi
382
+ AUDIO_PACKET_COUNT=$((AUDIO_BYTES / AUDIO_PACKET_BYTES))
383
+ [[ "$AUDIO_PACKET_COUNT" -gt 0 ]] || fail "generated audio packets are empty"
384
+
385
+ {
386
+ echo "pts_us,offset,size"
387
+ for ((i = 0; i < AUDIO_PACKET_COUNT; i += 1)); do
388
+ pts_us=$((i * k_audio_packet_duration_ms * 1000))
389
+ offset=$((i * AUDIO_PACKET_BYTES))
390
+ echo "${pts_us},${offset},${AUDIO_PACKET_BYTES}"
391
+ done
392
+ } > "$AUDIO_PACKET_INDEX_PATH"
393
+
394
+ cat > "$MANIFEST_PATH" <<JSON
395
+ {
396
+ "schema_version": ${k_schema_version},
397
+ "tool_version": "${k_tool_version}",
398
+ "source": {
399
+ "file_name": "${SOURCE_FILE_NAME}",
400
+ "sha256": "${SOURCE_SHA256}"
401
+ },
402
+ "video": {
403
+ "path": "video/video.h264",
404
+ "packet_index_path": "video/video_packets.csv",
405
+ "codec": "${k_video_codec}",
406
+ "width": ${TARGET_VIDEO_WIDTH},
407
+ "height": ${TARGET_VIDEO_HEIGHT},
408
+ "fps": ${k_video_fps},
409
+ "bitrate_kbps": ${TARGET_VIDEO_BITRATE_KBPS}
410
+ },
411
+ "audio": {
412
+ "path": "audio/audio.g711a",
413
+ "packet_index_path": "audio/audio_packets.csv",
414
+ "codec": "${k_audio_codec}",
415
+ "sample_rate_hz": ${k_audio_sample_rate_hz},
416
+ "channels": ${k_audio_channels},
417
+ "packet_duration_ms": ${k_audio_packet_duration_ms}
418
+ },
419
+ "output": {
420
+ "audio_format": "${k_output_audio_format}",
421
+ "video_format": "${k_output_video_format}"
422
+ },
423
+ "loop_policy": "${k_loop_policy}"
424
+ }
425
+ JSON
426
+
427
+ emit_result "$ASSETS_DIR" "$MANIFEST_PATH" false