feat(record): fragment the MP4 so a crash cannot destroy the whole take
TASK 32·4. A plain MP4 keeps its index (moov) at the end, so a power cut or a killed process left the entire recording unreadable even though every video byte was on disk. The record block now carries `-movflags +frag_keyframe+empty_moov+default_base_moof`: index first, a playable fragment flushed at every keyframe, so an interrupted take plays to the last fragment. Record block only — the FLV stream output has no index and never gets the flags. Rename-on-stop is untouched. Test: `Args_RecordBlock_IsFragmentedMp4_SoACrashKeepsTheFile` asserts the flags on the record block and their absence from the streaming block. Not yet proven on a real interrupted take (kill ffmpeg mid-recording, play the result); the args test is by construction. Docs: task-32 slice 4 ticked with that caveat, TASKS row, output.md record note, HANDOFF. verify.sh: 550 passed (mouse-injection test excluded, desktop in use), 0 warnings.
This commit is contained in:
@@ -207,6 +207,41 @@ public class FfmpegEncoderTests
|
||||
Assert.Equal(@"C:\videos\ty-20260829-1405-0000.mp4", args[idx + 1]);
|
||||
}
|
||||
|
||||
/// <summary>TASK 32·4: a recording must survive a crash. A plain MP4 keeps its index at the
|
||||
/// end, so an interrupted file is unreadable — the whole take, not the tail. Fragmented MP4
|
||||
/// writes the index first and flushes a fragment per keyframe, so the file plays to the last
|
||||
/// fragment. The flags belong to the RECORD block only; the FLV stream has no index.</summary>
|
||||
[Fact]
|
||||
public void Args_RecordBlock_IsFragmentedMp4_SoACrashKeepsTheFile()
|
||||
{
|
||||
var recordOnly = FfmpegArgs.Build(
|
||||
new EncoderOptions
|
||||
{
|
||||
StreamEnabled = false,
|
||||
RecordEnabled = true,
|
||||
RecordPath = @"C:\videos\take.mp4",
|
||||
Width = 1920, Height = 1080, Fps = 30, BitrateKbps = 6000,
|
||||
},
|
||||
"h264_nvenc").ToArray();
|
||||
|
||||
var flagsAt = Array.IndexOf(recordOnly, "-movflags");
|
||||
Assert.True(flagsAt > -1, "a recording must be fragmented or a crash destroys the whole file");
|
||||
Assert.Equal("+frag_keyframe+empty_moov+default_base_moof", recordOnly[flagsAt + 1]);
|
||||
|
||||
// …and the stream output must never carry it.
|
||||
var streaming = FfmpegArgs.Build(
|
||||
new EncoderOptions
|
||||
{
|
||||
RtmpUrl = "rtmp://a.rtmp.youtube.com/live2/key",
|
||||
StreamEnabled = true,
|
||||
RecordEnabled = false,
|
||||
Width = 1920, Height = 1080, Fps = 30, BitrateKbps = 6000,
|
||||
},
|
||||
"h264_nvenc").ToArray();
|
||||
|
||||
Assert.DoesNotContain("-movflags", streaming);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Args_DualOutput_HasTwoMapBlocks_FlvAndMp4()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user