125 lines
4.0 KiB
PowerShell
125 lines
4.0 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$JobRoot,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$OutputPath,
|
|
|
|
[ValidateRange(1, 1000)]
|
|
[int]$FreeGiBFloor = 360
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$ProgressPreference = "SilentlyContinue"
|
|
|
|
function Assert-DDrivePath {
|
|
param([string]$Path, [string]$Label)
|
|
$fullPath = [IO.Path]::GetFullPath($Path)
|
|
if ([IO.Path]::GetPathRoot($fullPath).TrimEnd("\") -ine "D:") {
|
|
throw "$Label must be stored on D:"
|
|
}
|
|
return $fullPath
|
|
}
|
|
|
|
function Assert-FreeSpace {
|
|
param([string]$Phase, [int64]$RequiredAdditionalBytes = 0)
|
|
$freeBytes = [int64](Get-PSDrive -Name D).Free
|
|
$floorBytes = [int64]$FreeGiBFloor * 1GB
|
|
Write-Output (
|
|
"DISK_GUARD PHASE={0} FREE_GIB={1} FLOOR_GIB={2}" -f
|
|
$Phase,
|
|
[math]::Round($freeBytes / 1GB, 3),
|
|
$FreeGiBFloor
|
|
)
|
|
if ($freeBytes -lt ($floorBytes + $RequiredAdditionalBytes)) {
|
|
throw "D: does not have the guarded replay reserve during $Phase"
|
|
}
|
|
}
|
|
|
|
$jobDirectory = Assert-DDrivePath (
|
|
(Resolve-Path -LiteralPath $JobRoot).Path
|
|
) "Job root"
|
|
$output = Assert-DDrivePath $OutputPath "Output path"
|
|
if (Test-Path -LiteralPath $output) {
|
|
Write-Output "REPLAY_ALREADY_PRESENT=$output"
|
|
exit 0
|
|
}
|
|
|
|
$jobPath = Join-Path $jobDirectory "job.json"
|
|
$job = Get-Content -LiteralPath $jobPath -Raw | ConvertFrom-Json
|
|
if (
|
|
$job.schema_version -ne "missioncore.compute-job/v1" -or
|
|
$job.job_id -ne "recorded-camera-602ac89026ed12978619801d" -or
|
|
$job.input.session_id -ne "20260720T065719Z_viewer_live" -or
|
|
$job.input.source_id -ne "sensor.camera.right" -or
|
|
[int]$job.input.segment_count -ne 4489
|
|
) {
|
|
throw "The requested job is not the immutable RAVNOVES00 right-camera source"
|
|
}
|
|
|
|
$epochRoot = Join-Path $jobDirectory "input\camera\sensor.camera.right\epoch-1"
|
|
$initPath = Join-Path $epochRoot "init.mp4"
|
|
$segmentsRoot = Join-Path $epochRoot "segments"
|
|
if (-not (Test-Path -LiteralPath $initPath -PathType Leaf)) {
|
|
throw "Camera initialization segment is absent"
|
|
}
|
|
if (-not (Test-Path -LiteralPath $segmentsRoot -PathType Container)) {
|
|
throw "Camera segment directory is absent"
|
|
}
|
|
|
|
$parent = Split-Path $output -Parent
|
|
$null = New-Item -ItemType Directory -Path $parent -Force
|
|
$temporary = Join-Path $parent (".{0}.{1}.partial" -f (Split-Path $output -Leaf), [Guid]::NewGuid().ToString("N"))
|
|
$requiredBytes = [int64]$job.input.byte_length + 1GB
|
|
Assert-FreeSpace "preflight" $requiredBytes
|
|
|
|
try {
|
|
$destination = [IO.File]::Open(
|
|
$temporary,
|
|
[IO.FileMode]::CreateNew,
|
|
[IO.FileAccess]::Write,
|
|
[IO.FileShare]::None
|
|
)
|
|
try {
|
|
$source = [IO.File]::OpenRead($initPath)
|
|
try { $source.CopyTo($destination) } finally { $source.Dispose() }
|
|
for ($sequence = 1; $sequence -le 4489; $sequence++) {
|
|
$segment = Join-Path $segmentsRoot ("{0}.m4s" -f $sequence)
|
|
if (-not (Test-Path -LiteralPath $segment -PathType Leaf)) {
|
|
throw "Camera segment is absent: $sequence"
|
|
}
|
|
$source = [IO.File]::OpenRead($segment)
|
|
try { $source.CopyTo($destination) } finally { $source.Dispose() }
|
|
}
|
|
$destination.Flush($true)
|
|
}
|
|
finally {
|
|
$destination.Dispose()
|
|
}
|
|
|
|
$probe = & ffprobe -v error -select_streams v:0 -count_frames `
|
|
-show_entries stream=width,height,nb_read_frames `
|
|
-of json $temporary | ConvertFrom-Json
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "ffprobe failed for the reconstructed camera source"
|
|
}
|
|
$stream = @($probe.streams)[0]
|
|
if (
|
|
[int]$stream.width -ne 800 -or
|
|
[int]$stream.height -ne 600 -or
|
|
[int]$stream.nb_read_frames -ne 4489
|
|
) {
|
|
throw "Reconstructed camera stream violates the immutable frame contract"
|
|
}
|
|
Move-Item -LiteralPath $temporary -Destination $output
|
|
Assert-FreeSpace "published"
|
|
Write-Output "REPLAY_PATH=$output"
|
|
Write-Output "FRAME_COUNT=4489"
|
|
}
|
|
finally {
|
|
if (Test-Path -LiteralPath $temporary) {
|
|
Remove-Item -LiteralPath $temporary -Force
|
|
}
|
|
}
|