@torrent-tv/proxy 2.89.7 → 2.89.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/opsx/apply.md +155 -155
- package/.claude/commands/opsx/archive.md +160 -160
- package/.claude/commands/opsx/explore.md +174 -174
- package/.claude/commands/opsx/propose.md +109 -109
- package/.claude/commands/opsx/sync.md +143 -143
- package/.claude/skills/openspec-apply-change/SKILL.md +159 -159
- package/.claude/skills/openspec-archive-change/SKILL.md +117 -117
- package/.claude/skills/openspec-explore/SKILL.md +289 -289
- package/.claude/skills/openspec-propose/SKILL.md +113 -113
- package/.claude/skills/openspec-sync-specs/SKILL.md +147 -147
- package/.dockerignore +5 -5
- package/AGENTS.md +358 -338
- package/CHANGELOG.md +2340 -2335
- package/Dockerfile +30 -30
- package/LICENSE +16 -16
- package/README.md +369 -369
- package/assets/calibration/NOTICE.md +100 -100
- package/assets/calibration/audio/NOTICE.md +19 -19
- package/assets/calibration/audio/manifest.json +98 -98
- package/assets/diagnostics/sctpstate.gdb +31 -31
- package/bin/cli.js +684 -684
- package/bin/thread-pool.js +30 -30
- package/biome.json +412 -412
- package/docs/browser-proxy-contract.md +155 -155
- package/docs/container-architecture.md +273 -273
- package/docs/download-architecture.md +261 -261
- package/docs/encode-architecture.md +765 -765
- package/docs/encode-run-state.md +96 -96
- package/docs/logs.md +97 -97
- package/docs/playback-start-forecast.md +113 -113
- package/docs/socks5-egress-research.md +114 -114
- package/docs/storage-architecture.md +273 -273
- package/knip.json +14 -14
- package/openspec/changes/adaptive-bitrate/design.md +92 -92
- package/openspec/changes/adaptive-bitrate/proposal.md +56 -56
- package/openspec/changes/adaptive-bitrate/specs/adaptive-bitrate/spec.md +51 -51
- package/openspec/changes/adaptive-bitrate/tasks.md +40 -40
- package/openspec/changes/binary-distribution/.openspec.yaml +2 -2
- package/openspec/changes/binary-distribution/proposal.md +53 -53
- package/openspec/changes/chunked-request-bodies/design.md +81 -81
- package/openspec/changes/chunked-request-bodies/proposal.md +53 -53
- package/openspec/changes/chunked-request-bodies/specs/chunked-request-bodies/spec.md +30 -30
- package/openspec/changes/chunked-request-bodies/tasks.md +40 -40
- package/openspec/changes/cold-start/design.md +98 -98
- package/openspec/changes/cold-start/proposal.md +62 -62
- package/openspec/changes/cold-start/specs/cold-start/spec.md +47 -47
- package/openspec/changes/cold-start/tasks.md +48 -48
- package/openspec/changes/disk-cap/.openspec.yaml +2 -2
- package/openspec/changes/disk-cap/proposal.md +37 -37
- package/openspec/changes/disk-cap/specs/disk-cap/spec.md +25 -25
- package/openspec/changes/disk-cap/tasks.md +19 -19
- package/openspec/changes/proxy-observability/.openspec.yaml +2 -2
- package/openspec/changes/proxy-observability/design.md +38 -38
- package/openspec/changes/proxy-observability/proposal.md +49 -49
- package/openspec/changes/proxy-observability/specs/observability/spec.md +33 -33
- package/openspec/changes/proxy-observability/tasks.md +21 -21
- package/openspec/changes/seek-debounce/design.md +118 -118
- package/openspec/changes/seek-debounce/proposal.md +59 -59
- package/openspec/changes/seek-debounce/specs/seek-debounce/spec.md +41 -41
- package/openspec/changes/seek-debounce/tasks.md +33 -33
- package/openspec/changes/subtitle-language/.openspec.yaml +2 -2
- package/openspec/changes/subtitle-language/proposal.md +50 -50
- package/openspec/changes/subtitle-language/specs/subtitle-language/spec.md +34 -34
- package/openspec/changes/subtitle-language/tasks.md +26 -26
- package/openspec/changes/track-selection/.openspec.yaml +2 -2
- package/openspec/changes/track-selection/design.md +47 -47
- package/openspec/changes/track-selection/proposal.md +45 -45
- package/openspec/changes/track-selection/specs/track-selection/spec.md +41 -41
- package/openspec/changes/track-selection/tasks.md +20 -20
- package/openspec/changes/transcode-quality/.openspec.yaml +2 -2
- package/openspec/changes/transcode-quality/proposal.md +51 -51
- package/openspec/changes/transcode-quality/specs/transcode-quality/spec.md +139 -139
- package/openspec/changes/transcode-quality/tasks.md +63 -63
- package/openspec/config.yaml +38 -38
- package/package.json +51 -50
- package/public/.well-known/appspecific/com.chrome.devtools.json +1 -1
- package/research/delivery-wedge-stand-ladder-2026-09-13.md +329 -329
- package/research/double-spawn-2026-09-10.md +171 -171
- package/research/handover-2026-09-13.md +209 -209
- package/research/handover-reader-claims-removal-2026-09-12.md +166 -166
- package/research/piece-withdrawn-but-still-claimed-2026-09-12.md +175 -175
- package/research/preset-benchmark-size-scaling-2026-08-22.md +55 -55
- package/research/priority-map-is-the-truth-2026-09-12.md +225 -225
- package/routes/api/client-logs/post.js +86 -86
- package/routes/api/delivery-sink/get.js +72 -72
- package/routes/api/link-probe/get.js +51 -51
- package/routes/api/playback-plan/audio-tracks/post.js +29 -29
- package/routes/api/playback-plan/post.js +53 -53
- package/routes/api/sources/files/get.js +106 -106
- package/routes/api/sources/post.js +36 -36
- package/routes/api/sources/stats/get.js +132 -132
- package/routes/api/sources/warm/post.js +217 -217
- package/routes/api/subtitles/get.js +230 -230
- package/routes/api/transcode-sessions/fragment-far/post.js +60 -60
- package/routes/api/transcode-sessions/net-report/post.js +138 -138
- package/routes/api/transcode-sessions/post.js +178 -178
- package/routes/api/transcode-sessions/progress/get.js +27 -27
- package/routes/api/transcode-sessions/release/post.js +41 -41
- package/routes/api/transcode-sessions/seek/post.js +63 -63
- package/routes/health/get.js +13 -13
- package/routes/healthz/get.js +13 -13
- package/routes/stream/get.js +416 -416
- package/routes/transcode/audio-file/get.js +64 -64
- package/routes/transcode/audio-warm/get.js +95 -95
- package/routes/transcode/session-file/get.js +52 -52
- package/routes/transcode/variant-file/get.js +95 -95
- package/routes/transcode/variant-warm/get.js +84 -84
- package/scripts/build-audio-samples.mjs +31 -31
- package/scripts/render-run-graph.js +173 -173
- package/server.js +748 -748
- package/services/encode/CoverageMap.js +428 -428
- package/services/encode/CushionReport.js +345 -345
- package/services/encode/EncodeAdmission.js +376 -376
- package/services/encode/EncodeOrchestrator.js +1192 -1192
- package/services/encode/EncodePlan.js +1368 -1368
- package/services/encode/EncodeRun.js +36 -12
- package/services/encode/Encoder.js +142 -142
- package/services/encode/EncoderSelection.js +38 -38
- package/services/encode/LocalObservations.js +359 -359
- package/services/encode/NvencEncoder.js +50 -50
- package/services/encode/OutputOpening.js +784 -784
- package/services/encode/OutputTimes.js +579 -579
- package/services/encode/QsvEncoder.js +58 -58
- package/services/encode/Renditions.js +2283 -2283
- package/services/encode/RunClock.js +87 -87
- package/services/encode/RunProgress.js +79 -79
- package/services/encode/SegmentDemand.js +95 -95
- package/services/encode/SoftwareEncoder.js +121 -121
- package/services/encode/V4l2m2mEncoder.js +53 -53
- package/services/encode/VaapiEncoder.js +66 -66
- package/services/encode/args.js +418 -418
- package/services/encode/audio-calibration.js +67 -67
- package/services/encode/audio-work-rate.js +15 -15
- package/services/encode/available-share.js +89 -89
- package/services/encode/calibration.js +361 -361
- package/services/encode/contention.js +128 -128
- package/services/encode/decode-cost-fit.js +363 -363
- package/services/encode/encode-exit.js +116 -116
- package/services/encode/encode-run-state.js +551 -551
- package/services/encode/encoder-readings.js +43 -43
- package/services/encode/fingerprint.js +322 -322
- package/services/encode/host-load.js +174 -174
- package/services/encode/hwaccel.js +1894 -1894
- package/services/encode/index.js +9 -9
- package/services/encode/learned-median.js +90 -90
- package/services/encode/output/EncodedOutput.js +57 -57
- package/services/encode/output/Output.js +131 -131
- package/services/encode/output/OutputCatalog.js +414 -414
- package/services/encode/output/OutputSpec.js +390 -390
- package/services/encode/output/Timeline.js +308 -308
- package/services/encode/output/cut-grid.js +107 -107
- package/services/encode/output/index.js +24 -24
- package/services/encode/output/ladder.js +32 -32
- package/services/encode/output/playlists.js +284 -284
- package/services/encode/output/rates.js +125 -125
- package/services/encode/output-key-format.js +21 -21
- package/services/encode/quality/EncodeCost.js +1043 -1043
- package/services/encode/quality/HostLoad.js +525 -525
- package/services/encode/quality/HostTimings.js +249 -249
- package/services/encode/quality/OutputQualityState.js +24 -24
- package/services/encode/quality/QualityController.js +758 -758
- package/services/encode/quality/QualityOffer.js +416 -416
- package/services/encode/quality/drain-threat.js +47 -47
- package/services/encode/quality/link-budget.js +337 -337
- package/services/encode/quality/output-format.js +319 -319
- package/services/encode/quality/serving-output.js +216 -216
- package/services/encode/quality/visible-rung.js +57 -57
- package/services/encode/run-budget.js +97 -97
- package/services/encode/run-command.js +757 -757
- package/services/encode/run-costs.js +155 -155
- package/services/encode/run-inputs.js +53 -53
- package/services/encode/segment-formats/fmp4.js +476 -476
- package/services/encode/segment-formats/init-compat.js +324 -324
- package/services/encode/segment-formats/mpegts.js +134 -134
- package/services/encode/start-stop-cost.js +178 -178
- package/services/encode/throughput.js +97 -97
- package/services/encode/torrent-cost.js +88 -88
- package/services/identity/installation-id.js +38 -38
- package/services/media/ContainerOrchestrator.js +202 -202
- package/services/media/KeyframeTables.js +356 -356
- package/services/media/SourceFile.js +298 -298
- package/services/media/SubtitleCues.js +592 -592
- package/services/media/SubtitleOrchestrator.js +167 -167
- package/services/media/audio-inventory.js +299 -299
- package/services/media/container/AviContainer.js +268 -268
- package/services/media/container/Container.js +632 -632
- package/services/media/container/ContainerFactory.js +38 -38
- package/services/media/container/KeyframeTable.js +272 -272
- package/services/media/container/MatroskaContainer.js +1031 -1031
- package/services/media/container/Mp4Container.js +1354 -1354
- package/services/media/container/SubtitleFileContainer.js +339 -339
- package/services/media/container/ebml-reader.js +159 -159
- package/services/media/container/ebml-stream.js +174 -174
- package/services/media/container/matroska-clusters.js +1165 -1165
- package/services/media/container/unavailable.js +77 -77
- package/services/media/ffmpeg-banner.js +260 -260
- package/services/media/keyframe-probe.js +157 -157
- package/services/media/media-info-probe.js +93 -93
- package/services/media/playback-planner.js +931 -931
- package/services/media/tracks/AudioTrack.js +143 -143
- package/services/media/tracks/ContainerTrack.js +84 -84
- package/services/media/tracks/ImageSubtitleTrack.js +19 -19
- package/services/media/tracks/SubtitleTrack.js +38 -38
- package/services/media/tracks/TextSubtitleTrack.js +295 -295
- package/services/media/tracks/VideoTrack.js +33 -33
- package/services/media/tracks/audio-bound.js +233 -233
- package/services/media/tracks/index.js +15 -15
- package/services/media/tracks/language-detect.js +174 -174
- package/services/server/OutputLifecycle.js +418 -418
- package/services/server/SegmentServing.js +1152 -1152
- package/services/server/ViewerRequests.js +785 -785
- package/services/server/WaitLedger.js +142 -142
- package/services/server/controllers/PlaybackController.js +40 -40
- package/services/server/controllers/SubtitleController.js +109 -109
- package/services/server/http-responses.js +60 -60
- package/services/server/transcode-session-files.js +325 -325
- package/services/server/wire-outputs.js +1029 -1029
- package/services/storage/Diagnostics.js +202 -202
- package/services/storage/MachineBudget.js +235 -235
- package/services/storage/allowance.js +107 -107
- package/services/storage/core-dumps.js +106 -106
- package/services/storage/files/CompletedFiles.js +391 -391
- package/services/storage/files/piece-from-whole-file.js +118 -118
- package/services/storage/free.js +71 -71
- package/services/storage/keep.js +48 -48
- package/services/storage/machine-memory.js +56 -56
- package/services/storage/memory-report.js +575 -575
- package/services/storage/piece-store/piece-disk-store.js +539 -539
- package/services/storage/piece-store/piece-lru.js +495 -495
- package/services/storage/piece-store/shared-piece-store.js +1937 -1937
- package/services/storage/returns.js +103 -103
- package/services/storage/segment-store/SegmentStore.js +1294 -1294
- package/services/storage/wire.js +175 -175
- package/services/torrent/Contents.js +382 -382
- package/services/torrent/demand/DemandRegister.js +202 -202
- package/services/torrent/demand/Urgency.js +137 -137
- package/services/torrent/demand/Window.js +118 -118
- package/services/torrent/demand/index.js +8 -8
- package/services/torrent/demand/pieces.js +140 -140
- package/services/torrent/download/SwarmSelection.js +386 -386
- package/services/torrent/download/registry.js +102 -102
- package/services/torrent/download/withdraw-claim.js +80 -80
- package/services/torrent/episode-naming.js +182 -182
- package/services/torrent/files.js +455 -455
- package/services/torrent/naming.js +619 -619
- package/services/torrent/piece-store-of.js +29 -29
- package/services/torrent/supply-margin.js +264 -264
- package/services/torrent/torrent-pool.js +2688 -2688
- package/services/torrent/worker/background-fill.js +213 -213
- package/services/torrent/worker/channel.js +271 -271
- package/services/torrent/worker/client.js +836 -836
- package/services/torrent/worker/fastest-wires.js +342 -342
- package/services/torrent/worker/handle-state.js +23 -23
- package/services/torrent/worker/held-bytes.js +231 -231
- package/services/torrent/worker/install-webrtc-shim.js +32 -32
- package/services/torrent/worker/piece-reader.js +1078 -1078
- package/services/torrent/worker/pool-adapter.js +371 -371
- package/services/torrent/worker/protocol.js +184 -184
- package/services/torrent/worker/resume-warm.js +107 -107
- package/services/torrent/worker/webrtc-shim.js +219 -219
- package/services/torrent/worker/worker.js +1137 -1137
- package/services/transport/data-channel-handler.js +1373 -1373
- package/services/transport/delivery-probe.js +842 -842
- package/services/transport/health-collector.js +53 -53
- package/services/transport/nat-classifier.js +231 -231
- package/services/transport/packet-witness.js +808 -808
- package/services/transport/port-mapper.js +235 -235
- package/services/transport/registry-api.js +81 -81
- package/services/transport/tunnel-client.js +447 -447
- package/services/transport/usrsctp-state.js +175 -175
- package/services/transport/webrtc-manager.js +484 -484
- package/services/viewer/Assignments.js +340 -340
- package/services/viewer/PriorityMap.js +421 -421
- package/services/viewer/PriorityOrchestrator.js +325 -325
- package/services/viewer/Viewer.js +691 -691
- package/services/viewer/Viewers.js +368 -368
- package/services/viewer/active-output.js +37 -37
- package/services/viewer/audio-start.js +46 -46
- package/services/viewer/buffer-trend.js +81 -81
- package/services/viewer/choices.js +647 -647
- package/services/viewer/media-time.js +355 -355
- package/services/viewer/playback-readiness.js +635 -635
- package/services/viewer/positions.js +126 -126
- package/services/viewer/report-intake.js +46 -46
- package/store/source-registry.js +71 -71
- package/test/asset-paths.test.js +28 -28
- package/test/audio-buffer-continuity.test.js +77 -77
- package/test/audio-calibration.test.js +23 -23
- package/test/audio-inventory.test.js +212 -212
- package/test/audio-track-bitrate.test.js +40 -40
- package/test/auto-quality-step.test.js +762 -762
- package/test/available-share.test.js +76 -76
- package/test/background-fill.test.js +190 -190
- package/test/client-log-file.test.js +110 -110
- package/test/coalesce.test.js +39 -39
- package/test/completed-files.test.js +115 -115
- package/test/concurrent-cost.test.js +148 -148
- package/test/container-media-info.test.js +228 -228
- package/test/contention.test.js +76 -76
- package/test/core-dumps.test.js +57 -57
- package/test/coverage-follows-the-disk.test.js +233 -228
- package/test/coverage-map.test.js +195 -195
- package/test/cue-push-carries-what-the-walk-found.test.js +58 -58
- package/test/cut-times-timeline.test.js +65 -65
- package/test/cuts-follow-published-grid.test.js +109 -109
- package/test/data-channel-frame.test.js +63 -63
- package/test/data-channel-viewer-lifecycle.test.js +92 -92
- package/test/dead-torrent-handle.test.js +52 -52
- package/test/declared-rates.test.js +116 -116
- package/test/declared-tracks.test.js +43 -43
- package/test/decode-cost-fit.test.js +202 -202
- package/test/decode-cost.test.js +475 -475
- package/test/delivery-probe.test.js +393 -393
- package/test/demand-register.test.js +206 -206
- package/test/dht-bootstrap.test.js +72 -72
- package/test/drain-threat.test.js +65 -65
- package/test/duplicate-add.test.js +48 -48
- package/test/encode-admission.test.js +146 -146
- package/test/encode-cost.test.js +275 -275
- package/test/encode-exit.test.js +111 -111
- package/test/encode-orchestrator.test.js +426 -426
- package/test/encode-plan-viewers.test.js +725 -725
- package/test/encode-plan.test.js +545 -545
- package/test/encode-run-state.test.js +314 -314
- package/test/encode-run.test.js +20 -0
- package/test/encode-slope.test.js +46 -46
- package/test/encoded-output.test.js +39 -39
- package/test/encoder-kinds.test.js +122 -122
- package/test/encoder-readings.test.js +68 -68
- package/test/episode-naming.test.js +131 -131
- package/test/every-claimant-is-told-its-share.test.js +152 -152
- package/test/failed-encoder-still-serves-what-exists.test.js +354 -354
- package/test/failed-start-breaker.test.js +202 -202
- package/test/fastest-wires.test.js +143 -143
- package/test/ffmpeg-stream-counts.test.js +78 -78
- package/test/file-edges.test.js +191 -191
- package/test/fixtures/audio-frame-boundary.json +1 -1
- package/test/fmp4-self-contained.test.js +80 -80
- package/test/fmp4-timestamps.test.js +140 -140
- package/test/fmp4-track-completeness.test.js +41 -41
- package/test/format-keyed-outputs.test.js +78 -78
- package/test/health-metrics.test.js +36 -38
- package/test/held-read-ownership.test.js +77 -77
- package/test/held-request-width.test.js +157 -157
- package/test/helpers/encode-run.js +248 -240
- package/test/helpers/held-file.js +77 -77
- package/test/helpers/manager.js +62 -62
- package/test/helpers/matroska-file.js +296 -296
- package/test/helpers/output-spec.js +63 -63
- package/test/host-load.test.js +68 -68
- package/test/index-accuracy-is-about-the-file.test.js +128 -128
- package/test/init-compat.test.js +219 -219
- package/test/input-lost-quiets-the-plan.test.js +261 -261
- package/test/input-unavailable.test.js +48 -48
- package/test/installation-id.test.js +39 -39
- package/test/keeping-period.test.js +83 -83
- package/test/keyframe-index-accuracy.test.js +188 -188
- package/test/keyframe-table-budget.test.js +123 -123
- package/test/keyframes-belong-to-the-file.test.js +249 -249
- package/test/learned-median.test.js +37 -37
- package/test/letting-go-asks-who-is-watching.test.js +191 -191
- package/test/limit-row-by-area.test.js +60 -60
- package/test/link-budget.test.js +111 -111
- package/test/link-probe.test.js +56 -56
- package/test/local-encoding-data.test.js +273 -273
- package/test/logger-repeats.test.js +134 -134
- package/test/look-ahead-coverage.test.js +56 -56
- package/test/manager-surface.test.js +291 -291
- package/test/matroska-cluster-discovery.test.js +323 -323
- package/test/matroska-cues-track.test.js +192 -192
- package/test/media-time.test.js +62 -62
- package/test/memory-budget.test.js +390 -390
- package/test/move-cost.test.js +216 -216
- package/test/mp4-subtitles.test.js +173 -173
- package/test/one-authority.test.js +285 -285
- package/test/one-budget.test.js +149 -149
- package/test/one-output-one-session.test.js +273 -273
- package/test/one-resource-divided.test.js +185 -185
- package/test/one-serving-output-per-height.test.js +458 -458
- package/test/orchestrator-wired.test.js +207 -207
- package/test/output-catalog.test.js +39 -39
- package/test/output-shape.test.js +21 -21
- package/test/output-spec.test.js +251 -251
- package/test/output-speed.test.js +92 -87
- package/test/outputs-being-prepared.test.js +40 -40
- package/test/packet-witness-ring.test.js +236 -236
- package/test/packet-witness.test.js +148 -148
- package/test/piece-disk-store.test.js +401 -401
- package/test/piece-from-whole-file.test.js +129 -129
- package/test/piece-lru.test.js +319 -319
- package/test/piece-reader.test.js +162 -162
- package/test/piece-selection-offset.test.js +85 -85
- package/test/piece-store-eviction.test.js +595 -595
- package/test/piece-store-never-refuses.test.js +153 -153
- package/test/piece-store-reservations.test.js +403 -403
- package/test/piece-store-slow-disk.test.js +159 -159
- package/test/piece-tail.test.js +97 -97
- package/test/plan-host-timings.test.js +68 -68
- package/test/plan-is-steady.test.js +225 -225
- package/test/playback-readiness.test.js +518 -518
- package/test/playback-start-regressions.test.js +101 -101
- package/test/priority-map-download.test.js +174 -174
- package/test/priority-map-emptied.test.js +204 -204
- package/test/priority-map-per-output.test.js +215 -215
- package/test/priority-map.test.js +163 -163
- package/test/probe-wedge-certainty.test.js +60 -60
- package/test/produced-copy-choice.test.js +213 -213
- package/test/published-timeline.test.js +83 -83
- package/test/quality-offer.test.js +160 -160
- package/test/quality-variants.test.js +1274 -1270
- package/test/rate-control-in-the-output.test.js +181 -180
- package/test/read-survives-withdrawal.test.js +164 -164
- package/test/read-window.test.js +365 -365
- package/test/reads-not-yet-arrived.test.js +218 -218
- package/test/resume-warm.test.js +39 -39
- package/test/run-graph-drift.test.js +37 -37
- package/test/run-inputs.test.js +80 -80
- package/test/run-intervals.test.js +100 -100
- package/test/run-position-follows-published-grid.test.js +89 -89
- package/test/run-progress.test.js +64 -64
- package/test/same-height-switch.test.js +663 -663
- package/test/seek-frees-the-viewers-requests.test.js +217 -217
- package/test/seek-landing.test.js +109 -109
- package/test/seek-target-not-superseded.test.js +195 -195
- package/test/segment-closed-on-both-branches.test.js +114 -114
- package/test/segment-cut-times.test.js +63 -63
- package/test/segment-demand.test.js +80 -80
- package/test/segment-hold-supersede.test.js +91 -91
- package/test/segment-judged-by-its-own-header.test.js +222 -222
- package/test/segment-serve-wiring.test.js +481 -481
- package/test/segment-store-eviction.test.js +247 -247
- package/test/segment-store.test.js +352 -352
- package/test/segment-true-start.test.js +86 -86
- package/test/segments-are-shared.test.js +176 -176
- package/test/send-chunk-bytes.test.js +86 -86
- package/test/serving-existing-output.test.js +111 -111
- package/test/serving-output.test.js +102 -102
- package/test/shared-piece-store.test.js +310 -310
- package/test/sidecar-naming.test.js +142 -142
- package/test/soundtrack-rate-bound.test.js +289 -289
- package/test/source-file.test.js +133 -133
- package/test/source-files-route.test.js +113 -113
- package/test/source-registry.test.js +18 -18
- package/test/split-a-stretch.test.js +139 -139
- package/test/stale-request-after-seek.test.js +158 -158
- package/test/startup-readings.test.js +124 -124
- package/test/stream-route.test.js +237 -237
- package/test/subtitle-cue-framing.test.js +200 -200
- package/test/subtitle-cue-source.test.js +102 -102
- package/test/subtitle-cue-walk.test.js +385 -385
- package/test/subtitle-cursor.test.js +63 -63
- package/test/subtitle-defaults.test.js +118 -118
- package/test/subtitle-language.test.js +252 -252
- package/test/subtitle-metadata.test.js +15 -15
- package/test/subtitle-plan-waits.test.js +125 -125
- package/test/subtitle-track-numbering.test.js +391 -391
- package/test/supply-margin.test.js +190 -190
- package/test/swarm-follows-readers.test.js +174 -174
- package/test/swarm-reach.test.js +130 -130
- package/test/swarm-selection.test.js +220 -220
- package/test/tail-duplication.test.js +167 -167
- package/test/the-channel-has-both-ends.test.js +91 -91
- package/test/the-torrent-thread-serves-bytes.test.js +107 -107
- package/test/thread-pool.test.js +53 -53
- package/test/timeline.test.js +84 -84
- package/test/torrent-contents.test.js +235 -235
- package/test/torrent-cost.test.js +71 -71
- package/test/torrent-files.test.js +221 -221
- package/test/torrent-naming.test.js +255 -255
- package/test/tracks-begin-together.test.js +209 -209
- package/test/tunnel-renewal.test.js +167 -167
- package/test/two-viewers-one-picture.test.js +508 -508
- package/test/upload-hurry.test.js +186 -186
- package/test/usrsctp-state.test.js +131 -131
- package/test/video-facts.test.js +102 -102
- package/test/viewer-assignments.test.js +143 -143
- package/test/viewer-outputs.test.js +285 -285
- package/test/viewer-presence.test.js +116 -116
- package/test/viewer-requests-admission.test.js +92 -92
- package/test/viewer-starts-on-map.test.js +27 -27
- package/test/viewer-states.test.js +192 -189
- package/test/viewer-subtitle-subscription.test.js +96 -96
- package/test/viewer.test.js +139 -139
- package/test/viewing-generations.test.js +390 -390
- package/test/visible-rung.test.js +37 -37
- package/test/wait-ledger.test.js +100 -100
- package/test/webrtc-shim.test.js +149 -149
- package/test/wedge-certainty.test.js +131 -131
- package/test/whole-file-held-read.test.js +30 -30
- package/test/withdraw-piece-claim.test.js +212 -212
- package/test/worker-channel.test.js +158 -158
- package/utils/client-log-file.js +151 -151
- package/utils/coalesce.js +41 -41
- package/utils/logger.js +312 -312
- package/utils/parse-range.js +25 -25
- package/utils/perf.js +59 -59
- package/utils/torrent-source-key.js +38 -38
|
@@ -1,121 +1,121 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file libx264 — the encoder every host has and the only one whose ladder of
|
|
3
|
-
* speed settings is measured at startup.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { Encoder } from "./Encoder.js";
|
|
7
|
-
import {
|
|
8
|
-
CPU_THREADS,
|
|
9
|
-
hasForcedTimes,
|
|
10
|
-
keyFrameArgs,
|
|
11
|
-
rateControlArgs,
|
|
12
|
-
safeDimensions,
|
|
13
|
-
SOFTWARE_CRF,
|
|
14
|
-
SOFTWARE_PRESET,
|
|
15
|
-
TONEMAP_FILTER_CHAIN,
|
|
16
|
-
TRANSCODE_FPS
|
|
17
|
-
} from "./args.js";
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* The presets the startup benchmark walks, slowest first.
|
|
21
|
-
*
|
|
22
|
-
* The same list `benchmarkSoftwarePresets` measures and `pickSoftwarePreset`
|
|
23
|
-
* chooses from; it is stated here because it is a property of this kind, and
|
|
24
|
-
* the four hardware kinds beside it show what its absence looks like.
|
|
25
|
-
*/
|
|
26
|
-
const PRESETS = ["fast", "faster", "veryfast", "superfast", "ultrafast"];
|
|
27
|
-
|
|
28
|
-
export class SoftwareEncoder extends Encoder {
|
|
29
|
-
constructor() {
|
|
30
|
-
super({ name: "libx264", kind: "software", device: null, inputArgs: [] });
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** @returns {import("./Encoder.js").SpeedLadder} */
|
|
34
|
-
get speedLadder() {
|
|
35
|
-
return {
|
|
36
|
-
flag: "-preset",
|
|
37
|
-
values: PRESETS,
|
|
38
|
-
measured: true,
|
|
39
|
-
note:
|
|
40
|
-
"Measured on every host at startup by `benchmarkSoftwarePresets`, in " +
|
|
41
|
-
"pixels per second per preset, and chosen from by `pickSoftwarePreset`."
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Every preset: `buildVideoArgs` passes whichever the offer chose. */
|
|
46
|
-
get selectableRungs() {
|
|
47
|
-
return PRESETS;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** @param {string | null} rung @returns {string[]} */
|
|
51
|
-
benchmarkArgs(rung = null) {
|
|
52
|
-
return ["-c:v", "libx264", "-preset", rung ?? SOFTWARE_PRESET, "-crf", SOFTWARE_CRF, "-pix_fmt", "yuv420p"];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
buildVideoArgs({
|
|
56
|
-
targetWidth,
|
|
57
|
-
targetHeight,
|
|
58
|
-
segmentDurationSec,
|
|
59
|
-
preset,
|
|
60
|
-
fps,
|
|
61
|
-
tonemap,
|
|
62
|
-
forcedKeyframeTimes,
|
|
63
|
-
rateControl = null
|
|
64
|
-
}) {
|
|
65
|
-
const { w, h } = safeDimensions(targetWidth, targetHeight);
|
|
66
|
-
const chosenPreset = typeof preset === "string" && preset.length > 0 ? preset : SOFTWARE_PRESET;
|
|
67
|
-
// Output frame rate: inherited from the source (rounded/capped) by the
|
|
68
|
-
// session manager, TRANSCODE_FPS by default. MUST be an integer and MUST
|
|
69
|
-
// equal the value used in the GOP below, or keyframes drift off the grid.
|
|
70
|
-
const outFps = Number.isInteger(fps) && fps > 0 ? fps : TRANSCODE_FPS;
|
|
71
|
-
// HDR→SDR tone-map, inserted AFTER the downscale so it runs on the smaller
|
|
72
|
-
// frame (cheaper on ARM); only when the source is HDR and the filters are
|
|
73
|
-
// present (session manager gates on both).
|
|
74
|
-
const tonemapPart = tonemap === true ? `,${TONEMAP_FILTER_CHAIN}` : "";
|
|
75
|
-
return [
|
|
76
|
-
// Never upscale: cap the target box to the source size (min with
|
|
77
|
-
// iw/ih), so a small source (e.g. 720x400) is encoded at its own
|
|
78
|
-
// resolution instead of being scaled up to the viewport — far fewer
|
|
79
|
-
// pixels, much faster on ARM. force_original_aspect_ratio keeps aspect.
|
|
80
|
-
"-vf",
|
|
81
|
-
`scale='min(${w},iw)':'min(${h},ih)':force_original_aspect_ratio=decrease:force_divisible_by=2${tonemapPart},fps=${outFps}`,
|
|
82
|
-
"-c:v", "libx264",
|
|
83
|
-
// Preset is chosen per stream by the session manager from the startup
|
|
84
|
-
// benchmark (highest quality that still encodes the source resolution
|
|
85
|
-
// faster than realtime); falls back to the static default.
|
|
86
|
-
"-preset", chosenPreset,
|
|
87
|
-
"-crf", SOFTWARE_CRF,
|
|
88
|
-
// Constrained CRF: bound peak bitrate so a complex scene cannot produce
|
|
89
|
-
// segments a thin viewer link (cellular) can't download in time. The
|
|
90
|
-
// figures and the level are the OUTPUT's own, named by its key
|
|
91
|
-
// (`OutputSpec`), and are not worked out again here.
|
|
92
|
-
...rateControlArgs(rateControl),
|
|
93
|
-
"-threads", String(CPU_THREADS),
|
|
94
|
-
"-pix_fmt", "yuv420p",
|
|
95
|
-
// Fixed GOP: a keyframe exactly every (segmentDurationSec × fps) frames,
|
|
96
|
-
// scene-cut keyframes disabled. This is frame-count based, so it is
|
|
97
|
-
// independent of the PTS offset used on seek-restart — every HLS segment
|
|
98
|
-
// is exactly segmentDurationSec long and starts on a keyframe, so segment
|
|
99
|
-
// boundaries line up with the synthetic playlist with no gaps. (The old
|
|
100
|
-
// the OLD `expr:` form of -force_key_frames broke after a seek, because
|
|
101
|
-
// the `t` it reads is shifted by `-output_ts_offset`.)
|
|
102
|
-
//
|
|
103
|
-
// An explicit cut LIST is a different thing and does work: verified by
|
|
104
|
-
// running it, its times are on the run's own timeline — the same one
|
|
105
|
-
// `-segment_times` is measured on — so both are given one list and
|
|
106
|
-
// cannot drift apart. It replaces the frame-count GOP, which cannot
|
|
107
|
-
// describe the source's keyframes because they are not evenly spaced.
|
|
108
|
-
// `-g` stays as an upper bound on the interval: an extra keyframe
|
|
109
|
-
// inside a segment costs a little bitrate and cuts nothing, while
|
|
110
|
-
// leaving the interval unbounded means a driver that ignores the list
|
|
111
|
-
// produces one enormous segment instead of a wrong but cut one.
|
|
112
|
-
// `-keyint_min` goes, since a MINIMUM interval is the one thing that
|
|
113
|
-
// could argue with a forced keyframe.
|
|
114
|
-
"-g", String(segmentDurationSec * outFps),
|
|
115
|
-
...(hasForcedTimes(forcedKeyframeTimes)
|
|
116
|
-
? keyFrameArgs(segmentDurationSec, forcedKeyframeTimes)
|
|
117
|
-
: ["-keyint_min", String(segmentDurationSec * outFps)]),
|
|
118
|
-
"-sc_threshold", "0"
|
|
119
|
-
];
|
|
120
|
-
}
|
|
121
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @file libx264 — the encoder every host has and the only one whose ladder of
|
|
3
|
+
* speed settings is measured at startup.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Encoder } from "./Encoder.js";
|
|
7
|
+
import {
|
|
8
|
+
CPU_THREADS,
|
|
9
|
+
hasForcedTimes,
|
|
10
|
+
keyFrameArgs,
|
|
11
|
+
rateControlArgs,
|
|
12
|
+
safeDimensions,
|
|
13
|
+
SOFTWARE_CRF,
|
|
14
|
+
SOFTWARE_PRESET,
|
|
15
|
+
TONEMAP_FILTER_CHAIN,
|
|
16
|
+
TRANSCODE_FPS
|
|
17
|
+
} from "./args.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The presets the startup benchmark walks, slowest first.
|
|
21
|
+
*
|
|
22
|
+
* The same list `benchmarkSoftwarePresets` measures and `pickSoftwarePreset`
|
|
23
|
+
* chooses from; it is stated here because it is a property of this kind, and
|
|
24
|
+
* the four hardware kinds beside it show what its absence looks like.
|
|
25
|
+
*/
|
|
26
|
+
const PRESETS = ["fast", "faster", "veryfast", "superfast", "ultrafast"];
|
|
27
|
+
|
|
28
|
+
export class SoftwareEncoder extends Encoder {
|
|
29
|
+
constructor() {
|
|
30
|
+
super({ name: "libx264", kind: "software", device: null, inputArgs: [] });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** @returns {import("./Encoder.js").SpeedLadder} */
|
|
34
|
+
get speedLadder() {
|
|
35
|
+
return {
|
|
36
|
+
flag: "-preset",
|
|
37
|
+
values: PRESETS,
|
|
38
|
+
measured: true,
|
|
39
|
+
note:
|
|
40
|
+
"Measured on every host at startup by `benchmarkSoftwarePresets`, in " +
|
|
41
|
+
"pixels per second per preset, and chosen from by `pickSoftwarePreset`."
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Every preset: `buildVideoArgs` passes whichever the offer chose. */
|
|
46
|
+
get selectableRungs() {
|
|
47
|
+
return PRESETS;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** @param {string | null} rung @returns {string[]} */
|
|
51
|
+
benchmarkArgs(rung = null) {
|
|
52
|
+
return ["-c:v", "libx264", "-preset", rung ?? SOFTWARE_PRESET, "-crf", SOFTWARE_CRF, "-pix_fmt", "yuv420p"];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
buildVideoArgs({
|
|
56
|
+
targetWidth,
|
|
57
|
+
targetHeight,
|
|
58
|
+
segmentDurationSec,
|
|
59
|
+
preset,
|
|
60
|
+
fps,
|
|
61
|
+
tonemap,
|
|
62
|
+
forcedKeyframeTimes,
|
|
63
|
+
rateControl = null
|
|
64
|
+
}) {
|
|
65
|
+
const { w, h } = safeDimensions(targetWidth, targetHeight);
|
|
66
|
+
const chosenPreset = typeof preset === "string" && preset.length > 0 ? preset : SOFTWARE_PRESET;
|
|
67
|
+
// Output frame rate: inherited from the source (rounded/capped) by the
|
|
68
|
+
// session manager, TRANSCODE_FPS by default. MUST be an integer and MUST
|
|
69
|
+
// equal the value used in the GOP below, or keyframes drift off the grid.
|
|
70
|
+
const outFps = Number.isInteger(fps) && fps > 0 ? fps : TRANSCODE_FPS;
|
|
71
|
+
// HDR→SDR tone-map, inserted AFTER the downscale so it runs on the smaller
|
|
72
|
+
// frame (cheaper on ARM); only when the source is HDR and the filters are
|
|
73
|
+
// present (session manager gates on both).
|
|
74
|
+
const tonemapPart = tonemap === true ? `,${TONEMAP_FILTER_CHAIN}` : "";
|
|
75
|
+
return [
|
|
76
|
+
// Never upscale: cap the target box to the source size (min with
|
|
77
|
+
// iw/ih), so a small source (e.g. 720x400) is encoded at its own
|
|
78
|
+
// resolution instead of being scaled up to the viewport — far fewer
|
|
79
|
+
// pixels, much faster on ARM. force_original_aspect_ratio keeps aspect.
|
|
80
|
+
"-vf",
|
|
81
|
+
`scale='min(${w},iw)':'min(${h},ih)':force_original_aspect_ratio=decrease:force_divisible_by=2${tonemapPart},fps=${outFps}`,
|
|
82
|
+
"-c:v", "libx264",
|
|
83
|
+
// Preset is chosen per stream by the session manager from the startup
|
|
84
|
+
// benchmark (highest quality that still encodes the source resolution
|
|
85
|
+
// faster than realtime); falls back to the static default.
|
|
86
|
+
"-preset", chosenPreset,
|
|
87
|
+
"-crf", SOFTWARE_CRF,
|
|
88
|
+
// Constrained CRF: bound peak bitrate so a complex scene cannot produce
|
|
89
|
+
// segments a thin viewer link (cellular) can't download in time. The
|
|
90
|
+
// figures and the level are the OUTPUT's own, named by its key
|
|
91
|
+
// (`OutputSpec`), and are not worked out again here.
|
|
92
|
+
...rateControlArgs(rateControl),
|
|
93
|
+
"-threads", String(CPU_THREADS),
|
|
94
|
+
"-pix_fmt", "yuv420p",
|
|
95
|
+
// Fixed GOP: a keyframe exactly every (segmentDurationSec × fps) frames,
|
|
96
|
+
// scene-cut keyframes disabled. This is frame-count based, so it is
|
|
97
|
+
// independent of the PTS offset used on seek-restart — every HLS segment
|
|
98
|
+
// is exactly segmentDurationSec long and starts on a keyframe, so segment
|
|
99
|
+
// boundaries line up with the synthetic playlist with no gaps. (The old
|
|
100
|
+
// the OLD `expr:` form of -force_key_frames broke after a seek, because
|
|
101
|
+
// the `t` it reads is shifted by `-output_ts_offset`.)
|
|
102
|
+
//
|
|
103
|
+
// An explicit cut LIST is a different thing and does work: verified by
|
|
104
|
+
// running it, its times are on the run's own timeline — the same one
|
|
105
|
+
// `-segment_times` is measured on — so both are given one list and
|
|
106
|
+
// cannot drift apart. It replaces the frame-count GOP, which cannot
|
|
107
|
+
// describe the source's keyframes because they are not evenly spaced.
|
|
108
|
+
// `-g` stays as an upper bound on the interval: an extra keyframe
|
|
109
|
+
// inside a segment costs a little bitrate and cuts nothing, while
|
|
110
|
+
// leaving the interval unbounded means a driver that ignores the list
|
|
111
|
+
// produces one enormous segment instead of a wrong but cut one.
|
|
112
|
+
// `-keyint_min` goes, since a MINIMUM interval is the one thing that
|
|
113
|
+
// could argue with a forced keyframe.
|
|
114
|
+
"-g", String(segmentDurationSec * outFps),
|
|
115
|
+
...(hasForcedTimes(forcedKeyframeTimes)
|
|
116
|
+
? keyFrameArgs(segmentDurationSec, forcedKeyframeTimes)
|
|
117
|
+
: ["-keyint_min", String(segmentDurationSec * outFps)]),
|
|
118
|
+
"-sc_threshold", "0"
|
|
119
|
+
];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file h264_v4l2m2m — the stateful memory-to-memory encoder on an ARM board
|
|
3
|
-
* (Raspberry Pi, HA Yellow).
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { Encoder } from "./Encoder.js";
|
|
7
|
-
import { keyFrameArgs, safeDimensions, TRANSCODE_FPS } from "./args.js";
|
|
8
|
-
|
|
9
|
-
export class V4l2m2mEncoder extends Encoder {
|
|
10
|
-
constructor() {
|
|
11
|
-
// ARM SoC (e.g. Raspberry Pi / HA Yellow) stateful M2M encoder. No GPU
|
|
12
|
-
// scaler — scale in software, hand YUV420 frames to the hardware encoder.
|
|
13
|
-
super({ name: "h264_v4l2m2m", kind: "v4l2m2m", device: null, inputArgs: [] });
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** @returns {import("./Encoder.js").SpeedLadder} */
|
|
17
|
-
get speedLadder() {
|
|
18
|
-
return {
|
|
19
|
-
flag: "",
|
|
20
|
-
values: [],
|
|
21
|
-
measured: false,
|
|
22
|
-
note:
|
|
23
|
-
"This kind has no speed setting at all — its levers are the bitrate " +
|
|
24
|
-
"and the number of capture buffers, which are not a trade of picture " +
|
|
25
|
-
"against speed. So there is nowhere to step, and a host that cannot " +
|
|
26
|
-
"keep up on it has to change something else."
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// `-g` aligns the GOP to the segment length so an IDR lands on every segment
|
|
31
|
-
// boundary; this is verified by the keyframe-alignment test before use,
|
|
32
|
-
// because v4l2m2m does not always honour these hints.
|
|
33
|
-
buildVideoArgs({ targetWidth, targetHeight, segmentDurationSec, fps, forcedKeyframeTimes }) {
|
|
34
|
-
const { w, h } = safeDimensions(targetWidth, targetHeight);
|
|
35
|
-
const outFps = Number.isInteger(fps) && fps > 0 ? fps : TRANSCODE_FPS;
|
|
36
|
-
return [
|
|
37
|
-
"-vf",
|
|
38
|
-
`scale=${w}:${h}:force_original_aspect_ratio=decrease:force_divisible_by=2,fps=${outFps},format=yuv420p`,
|
|
39
|
-
"-c:v", "h264_v4l2m2m",
|
|
40
|
-
// More capture buffers than the default 4 — the default deadlocks /
|
|
41
|
-
// drops frames on the CM4 encoder ("All capture buffers returned to
|
|
42
|
-
// userspace").
|
|
43
|
-
"-num_capture_buffers", "32",
|
|
44
|
-
"-b:v", "3M",
|
|
45
|
-
// Kept even with an explicit cut list, as an upper bound on the
|
|
46
|
-
// interval: this encoder is the one known not always to honour keyframe
|
|
47
|
-
// hints, and without any bound a list it ignores yields one segment for
|
|
48
|
-
// the whole file rather than a wrongly-cut one.
|
|
49
|
-
"-g", String(outFps * segmentDurationSec),
|
|
50
|
-
...keyFrameArgs(segmentDurationSec, forcedKeyframeTimes)
|
|
51
|
-
];
|
|
52
|
-
}
|
|
53
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @file h264_v4l2m2m — the stateful memory-to-memory encoder on an ARM board
|
|
3
|
+
* (Raspberry Pi, HA Yellow).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Encoder } from "./Encoder.js";
|
|
7
|
+
import { keyFrameArgs, safeDimensions, TRANSCODE_FPS } from "./args.js";
|
|
8
|
+
|
|
9
|
+
export class V4l2m2mEncoder extends Encoder {
|
|
10
|
+
constructor() {
|
|
11
|
+
// ARM SoC (e.g. Raspberry Pi / HA Yellow) stateful M2M encoder. No GPU
|
|
12
|
+
// scaler — scale in software, hand YUV420 frames to the hardware encoder.
|
|
13
|
+
super({ name: "h264_v4l2m2m", kind: "v4l2m2m", device: null, inputArgs: [] });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** @returns {import("./Encoder.js").SpeedLadder} */
|
|
17
|
+
get speedLadder() {
|
|
18
|
+
return {
|
|
19
|
+
flag: "",
|
|
20
|
+
values: [],
|
|
21
|
+
measured: false,
|
|
22
|
+
note:
|
|
23
|
+
"This kind has no speed setting at all — its levers are the bitrate " +
|
|
24
|
+
"and the number of capture buffers, which are not a trade of picture " +
|
|
25
|
+
"against speed. So there is nowhere to step, and a host that cannot " +
|
|
26
|
+
"keep up on it has to change something else."
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// `-g` aligns the GOP to the segment length so an IDR lands on every segment
|
|
31
|
+
// boundary; this is verified by the keyframe-alignment test before use,
|
|
32
|
+
// because v4l2m2m does not always honour these hints.
|
|
33
|
+
buildVideoArgs({ targetWidth, targetHeight, segmentDurationSec, fps, forcedKeyframeTimes }) {
|
|
34
|
+
const { w, h } = safeDimensions(targetWidth, targetHeight);
|
|
35
|
+
const outFps = Number.isInteger(fps) && fps > 0 ? fps : TRANSCODE_FPS;
|
|
36
|
+
return [
|
|
37
|
+
"-vf",
|
|
38
|
+
`scale=${w}:${h}:force_original_aspect_ratio=decrease:force_divisible_by=2,fps=${outFps},format=yuv420p`,
|
|
39
|
+
"-c:v", "h264_v4l2m2m",
|
|
40
|
+
// More capture buffers than the default 4 — the default deadlocks /
|
|
41
|
+
// drops frames on the CM4 encoder ("All capture buffers returned to
|
|
42
|
+
// userspace").
|
|
43
|
+
"-num_capture_buffers", "32",
|
|
44
|
+
"-b:v", "3M",
|
|
45
|
+
// Kept even with an explicit cut list, as an upper bound on the
|
|
46
|
+
// interval: this encoder is the one known not always to honour keyframe
|
|
47
|
+
// hints, and without any bound a list it ignores yields one segment for
|
|
48
|
+
// the whole file rather than a wrongly-cut one.
|
|
49
|
+
"-g", String(outFps * segmentDurationSec),
|
|
50
|
+
...keyFrameArgs(segmentDurationSec, forcedKeyframeTimes)
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file h264_vaapi — encode on a VAAPI device, with the scale on the same
|
|
3
|
-
* device so frames never leave it.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { Encoder } from "./Encoder.js";
|
|
7
|
-
import { keyFrameArgs, safeDimensions } from "./args.js";
|
|
8
|
-
|
|
9
|
-
export class VaapiEncoder extends Encoder {
|
|
10
|
-
/**
|
|
11
|
-
* @param {string} device - Render node, e.g. `/dev/dri/renderD128`.
|
|
12
|
-
*/
|
|
13
|
-
constructor(device) {
|
|
14
|
-
super({
|
|
15
|
-
name: "h264_vaapi",
|
|
16
|
-
kind: "vaapi",
|
|
17
|
-
device,
|
|
18
|
-
// Decode on the GPU into VAAPI surfaces; scale and encode stay on-GPU.
|
|
19
|
-
inputArgs: ["-hwaccel", "vaapi", "-hwaccel_output_format", "vaapi", "-vaapi_device", device]
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** @returns {import("./Encoder.js").SpeedLadder} */
|
|
24
|
-
get speedLadder() {
|
|
25
|
-
return {
|
|
26
|
-
flag: "-quality",
|
|
27
|
-
// Read from ffmpeg's own option list on the addon host 2026-09-04:
|
|
28
|
-
// "Set encode quality (trades off against speed, higher is faster)
|
|
29
|
-
// (from -1 to INT_MAX) (default -1)". The upper end is the driver's, not
|
|
30
|
-
// the encoder's, so there is no list to state — only the name of the
|
|
31
|
-
// setting and the direction.
|
|
32
|
-
values: [],
|
|
33
|
-
measured: false,
|
|
34
|
-
note:
|
|
35
|
-
"The setting is `-quality`, higher is faster, and its range belongs to " +
|
|
36
|
-
"the driver rather than to ffmpeg. Nothing here has benchmarked it, " +
|
|
37
|
-
"and nothing passes it: a VAAPI encode runs at the driver's default."
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// No fps filter: VAAPI inherits the source rate and keeps keyframes on the
|
|
42
|
-
// grid via time-based -force_key_frames, so it already honours source fps.
|
|
43
|
-
/** @returns {string[]} */
|
|
44
|
-
benchmarkInputArgs() {
|
|
45
|
-
return this.device ? ["-vaapi_device", this.device] : [];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** @param {string | null} rung @returns {string[]} */
|
|
49
|
-
benchmarkArgs(rung = null) {
|
|
50
|
-
// Raw frames live in this process; VAAPI encodes what is in the device, so
|
|
51
|
-
// the upload is part of what this kind costs and belongs in the reading.
|
|
52
|
-
const quality = rung ? ["-quality", rung] : [];
|
|
53
|
-
return ["-vf", "format=nv12,hwupload", "-c:v", "h264_vaapi", "-qp", "24", ...quality];
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
buildVideoArgs({ targetWidth, targetHeight, segmentDurationSec, forcedKeyframeTimes }) {
|
|
57
|
-
const { w, h } = safeDimensions(targetWidth, targetHeight);
|
|
58
|
-
return [
|
|
59
|
-
"-vf",
|
|
60
|
-
`scale_vaapi=w=${w}:h=${h}:force_original_aspect_ratio=decrease`,
|
|
61
|
-
"-c:v", "h264_vaapi",
|
|
62
|
-
"-qp", "24",
|
|
63
|
-
...keyFrameArgs(segmentDurationSec, forcedKeyframeTimes)
|
|
64
|
-
];
|
|
65
|
-
}
|
|
66
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @file h264_vaapi — encode on a VAAPI device, with the scale on the same
|
|
3
|
+
* device so frames never leave it.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Encoder } from "./Encoder.js";
|
|
7
|
+
import { keyFrameArgs, safeDimensions } from "./args.js";
|
|
8
|
+
|
|
9
|
+
export class VaapiEncoder extends Encoder {
|
|
10
|
+
/**
|
|
11
|
+
* @param {string} device - Render node, e.g. `/dev/dri/renderD128`.
|
|
12
|
+
*/
|
|
13
|
+
constructor(device) {
|
|
14
|
+
super({
|
|
15
|
+
name: "h264_vaapi",
|
|
16
|
+
kind: "vaapi",
|
|
17
|
+
device,
|
|
18
|
+
// Decode on the GPU into VAAPI surfaces; scale and encode stay on-GPU.
|
|
19
|
+
inputArgs: ["-hwaccel", "vaapi", "-hwaccel_output_format", "vaapi", "-vaapi_device", device]
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** @returns {import("./Encoder.js").SpeedLadder} */
|
|
24
|
+
get speedLadder() {
|
|
25
|
+
return {
|
|
26
|
+
flag: "-quality",
|
|
27
|
+
// Read from ffmpeg's own option list on the addon host 2026-09-04:
|
|
28
|
+
// "Set encode quality (trades off against speed, higher is faster)
|
|
29
|
+
// (from -1 to INT_MAX) (default -1)". The upper end is the driver's, not
|
|
30
|
+
// the encoder's, so there is no list to state — only the name of the
|
|
31
|
+
// setting and the direction.
|
|
32
|
+
values: [],
|
|
33
|
+
measured: false,
|
|
34
|
+
note:
|
|
35
|
+
"The setting is `-quality`, higher is faster, and its range belongs to " +
|
|
36
|
+
"the driver rather than to ffmpeg. Nothing here has benchmarked it, " +
|
|
37
|
+
"and nothing passes it: a VAAPI encode runs at the driver's default."
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// No fps filter: VAAPI inherits the source rate and keeps keyframes on the
|
|
42
|
+
// grid via time-based -force_key_frames, so it already honours source fps.
|
|
43
|
+
/** @returns {string[]} */
|
|
44
|
+
benchmarkInputArgs() {
|
|
45
|
+
return this.device ? ["-vaapi_device", this.device] : [];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** @param {string | null} rung @returns {string[]} */
|
|
49
|
+
benchmarkArgs(rung = null) {
|
|
50
|
+
// Raw frames live in this process; VAAPI encodes what is in the device, so
|
|
51
|
+
// the upload is part of what this kind costs and belongs in the reading.
|
|
52
|
+
const quality = rung ? ["-quality", rung] : [];
|
|
53
|
+
return ["-vf", "format=nv12,hwupload", "-c:v", "h264_vaapi", "-qp", "24", ...quality];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
buildVideoArgs({ targetWidth, targetHeight, segmentDurationSec, forcedKeyframeTimes }) {
|
|
57
|
+
const { w, h } = safeDimensions(targetWidth, targetHeight);
|
|
58
|
+
return [
|
|
59
|
+
"-vf",
|
|
60
|
+
`scale_vaapi=w=${w}:h=${h}:force_original_aspect_ratio=decrease`,
|
|
61
|
+
"-c:v", "h264_vaapi",
|
|
62
|
+
"-qp", "24",
|
|
63
|
+
...keyFrameArgs(segmentDurationSec, forcedKeyframeTimes)
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
}
|