refactor: find the mfra in finalize() with zm_mp4::media_end

finalize() located the mfra by reading the trailing mfro with fopen and
fseeko and decoding it by hand, the same job zm_mp4::media_end() does for
the sidx scan. Use media_end() for both, so the last HLS fragment and the
index agree on where the media ends.

media_end() is also stricter: it requires an mfra box of the stated size
at the offset the mfro points to, where the old code accepted any size up
to the file length. A trailer that does not check out now leaves the final
fragment running to EOF, as a missing trailer already did.

A new test covers media_end() against the fixture's real mfra and three
damaged trailers: an mfro size off by four, one larger than the file, and
no mfro at all.

refs #5144

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Isaac ConnorandClaude Opus 5.5 committed 2026-10-01 07:46:15 -04:00
1 parent 26e35beed9
commit 3d858e2ccb
2 files changed
+48 -27

No files matched your search

+12 -27
View File
@@ -31,7 +31,9 @@ extern "C" {
#include <libavutil/display.h>
}
#include <fcntl.h>
#include <string>
#include <unistd.h>
VideoStore::VideoStore(
const char *filename_in,
@@ -1731,35 +1733,18 @@ void VideoStore::finalize() {
oc->pb = nullptr;
// The MOV muxer writes an mfra (Movie Fragment Random Access) box at the end
// of the file when fragmentation is on. Its trailing mfro box is exactly 16
// bytes and contains the mfra size, so we can subtract that to find where
// the final fragment's mdat actually ends.
// of the file when fragmentation is on, so the final fragment's mdat ends
// where the mfra begins.
int64_t fragment_n_end = file_size;
if (!filename.empty() && file_size >= 16) {
FILE *fp = fopen(filename.c_str(), "rb");
if (fp) {
if (fseeko(fp, file_size - 16, SEEK_SET) == 0) {
uint8_t mfro[16];
if (fread(mfro, 1, 16, fp) == 16) {
uint32_t box_size = (static_cast<uint32_t>(mfro[0]) << 24)
| (static_cast<uint32_t>(mfro[1]) << 16)
| (static_cast<uint32_t>(mfro[2]) << 8)
| static_cast<uint32_t>(mfro[3]);
if (box_size == 16
&& mfro[4] == 'm' && mfro[5] == 'f' && mfro[6] == 'r' && mfro[7] == 'o') {
uint32_t mfra_size = (static_cast<uint32_t>(mfro[12]) << 24)
| (static_cast<uint32_t>(mfro[13]) << 16)
| (static_cast<uint32_t>(mfro[14]) << 8)
| static_cast<uint32_t>(mfro[15]);
if (mfra_size > 0 && static_cast<int64_t>(mfra_size) <= file_size) {
fragment_n_end = file_size - mfra_size;
Debug(1, "mfra trailer is %u bytes; final fragment ends at %" PRId64,
mfra_size, fragment_n_end);
}
}
}
if (!filename.empty()) {
const int fd = ::open(filename.c_str(), O_RDONLY | O_CLOEXEC);
if (fd >= 0) {
fragment_n_end = zm_mp4::media_end(fd, file_size);
close(fd);
if (fragment_n_end != file_size) {
Debug(1, "mfra trailer is %" PRId64 " bytes; final fragment ends at %" PRId64,
file_size - fragment_n_end, fragment_n_end);
}
fclose(fp);
}
}
+36
View File
@@ -229,6 +229,42 @@ TEST_CASE("Mp4SidxIndexesAFileWithNoMfra") {
REQUIRE(first_difference(read_file(subject.path), subject.expected) == kSame);
}
TEST_CASE("Mp4SidxMediaEndTrustsOnlyAnMfraThatIsThere") {
// finalize() sizes the last HLS fragment from this, so an mfro whose size
// does not land on an mfra of that size must not cut the media short.
std::vector<uint8_t> file = read_file(fixture("sidx-abs.mp4"));
const int64_t size = static_cast<int64_t>(file.size());
const int64_t mfra = static_cast<int64_t>(offset_of(top_level(file), "mfra"));
const std::filesystem::path path = scratch("mfro.mp4");
auto end_of = [&path](const std::vector<uint8_t> &bytes) {
write_file(path, bytes);
const int fd = open(path.c_str(), O_RDONLY);
REQUIRE(fd >= 0);
const int64_t end = zm_mp4::media_end(fd, static_cast<int64_t>(bytes.size()));
close(fd);
return end;
};
REQUIRE(end_of(file) == mfra);
SECTION("an mfro size off by four") {
file[size - 1] = uint8_t(file[size - 1] + 4);
REQUIRE(end_of(file) == size);
}
SECTION("an mfro size larger than the file") {
file[size - 4] = 0xFF;
REQUIRE(end_of(file) == size);
}
SECTION("no mfro at all") {
memcpy(&file[size - 12], "xxxx", 4);
REQUIRE(end_of(file) == size);
}
std::error_code ignored;
std::filesystem::remove(path, ignored);
}
TEST_CASE("Mp4SidxMergesWhenTheRegionIsTight") {
std::vector<zm_mp4::Fragment> fragments;
for (int i = 0; i < 8; i++) {