fix: fix avi delay

This commit is contained in:
2026-03-29 08:44:23 +02:00
parent cbead256c3
commit b3ba4249ee
+211 -4
View File
@@ -29,6 +29,7 @@ static constexpr uint32_t FCC_rec = fourcc('r', 'e', 'c', ' ');
static constexpr uint32_t FCC_avih = fourcc('a', 'v', 'i', 'h');
static constexpr uint32_t FCC_strh = fourcc('s', 't', 'r', 'h');
static constexpr uint32_t FCC_strf = fourcc('s', 't', 'r', 'f');
static constexpr uint32_t FCC_idx1 = fourcc('i', 'd', 'x', '1');
static constexpr uint32_t FCC_vids = fourcc('v', 'i', 'd', 's');
static constexpr uint32_t FCC_auds = fourcc('a', 'u', 'd', 's');
static constexpr uint32_t FCC_MJPG = fourcc('M', 'J', 'P', 'G');
@@ -53,6 +54,10 @@ static uint32_t min_u32(uint32_t a, uint32_t b) {
return a < b ? a : b;
}
static uint64_t min_u64(uint64_t a, uint64_t b) {
return a < b ? a : b;
}
static uint64_t align2(uint64_t n) {
return (n + 1u) & ~1u;
}
@@ -363,6 +368,184 @@ static bool parse_movi_list(File& file, uint64_t start, uint64_t size,
return true;
}
static void clear_chunk_index(File& file) {
if (file.video_chunks) {
montauk::mfree(file.video_chunks);
file.video_chunks = nullptr;
}
if (file.audio_chunks) {
montauk::mfree(file.audio_chunks);
file.audio_chunks = nullptr;
}
file.video_chunk_count = 0;
file.video_chunk_cap = 0;
file.audio_chunk_count = 0;
file.audio_chunk_cap = 0;
file.total_audio_bytes = 0;
file.total_audio_frames = 0;
}
enum Idx1OffsetMode {
IDX1_UNKNOWN = -1,
IDX1_ABS_HEADER = 0,
IDX1_ABS_PAYLOAD,
IDX1_REL_MOVI_HEADER,
IDX1_REL_MOVI_PAYLOAD,
IDX1_REL_DATA_HEADER,
IDX1_REL_DATA_PAYLOAD,
};
static bool compute_idx1_header_offset(const File& file, int mode, uint32_t raw_offset,
uint64_t movi_payload, uint64_t movi_data_start,
uint64_t* out_header) {
if (!out_header) return false;
uint64_t header = 0;
switch (mode) {
case IDX1_ABS_HEADER:
header = (uint64_t)raw_offset;
break;
case IDX1_ABS_PAYLOAD:
if (raw_offset < 8u) return false;
header = (uint64_t)raw_offset - 8ull;
break;
case IDX1_REL_MOVI_HEADER:
header = movi_payload + (uint64_t)raw_offset;
break;
case IDX1_REL_MOVI_PAYLOAD:
if (raw_offset < 8u) return false;
header = movi_payload + (uint64_t)raw_offset - 8ull;
break;
case IDX1_REL_DATA_HEADER:
header = movi_data_start + (uint64_t)raw_offset;
break;
case IDX1_REL_DATA_PAYLOAD:
if (raw_offset < 8u) return false;
header = movi_data_start + (uint64_t)raw_offset - 8ull;
break;
default:
return false;
}
if (header > file.size || file.size - header < 8ull) return false;
*out_header = header;
return true;
}
static bool resolve_idx1_header_offset(const File& file, uint32_t chunk_id, uint32_t chunk_size,
uint32_t raw_offset, uint64_t movi_payload,
uint64_t movi_data_start, int* io_mode,
uint64_t* out_header) {
if (!io_mode || !out_header) return false;
if (*io_mode != IDX1_UNKNOWN) {
return compute_idx1_header_offset(file, *io_mode, raw_offset,
movi_payload, movi_data_start, out_header);
}
static constexpr int kModes[] = {
IDX1_REL_MOVI_HEADER,
IDX1_REL_DATA_HEADER,
IDX1_ABS_HEADER,
IDX1_REL_MOVI_PAYLOAD,
IDX1_REL_DATA_PAYLOAD,
IDX1_ABS_PAYLOAD,
};
uint8_t hdr[8];
for (int mode : kModes) {
uint64_t header = 0;
if (!compute_idx1_header_offset(file, mode, raw_offset,
movi_payload, movi_data_start, &header)) {
continue;
}
if (!read_exact(file, header, hdr, 8)) continue;
if (rd_u32(hdr + 0) != chunk_id || rd_u32(hdr + 4) != chunk_size) continue;
*io_mode = mode;
*out_header = header;
return true;
}
return false;
}
static bool parse_idx1(File& file, uint64_t start, uint64_t size,
int video_stream_index, int audio_stream_index,
uint64_t movi_payload, uint64_t movi_data_start,
bool* out_used_index) {
if (out_used_index) *out_used_index = false;
if (size < 16) return true;
int offset_mode = IDX1_UNKNOWN;
uint64_t pos = start;
uint64_t end = start + size;
uint8_t buf[8192];
while (pos + 16 <= end) {
uint32_t bytes = (uint32_t)min_u64((uint64_t)sizeof(buf), end - pos);
bytes -= bytes % 16u;
if (bytes == 0) break;
if (!read_exact(file, pos, buf, bytes)) {
set_error(file, "Failed to read AVI idx1 index");
return false;
}
for (uint32_t off = 0; off + 16 <= bytes; off += 16) {
uint32_t id = rd_u32(buf + off + 0);
uint32_t chunk_size = rd_u32(buf + off + 12);
if (chunk_size == 0) continue;
int stream_index = -1;
char kind0 = 0;
char kind1 = 0;
if (!chunk_stream_id(id, &stream_index, &kind0, &kind1)) continue;
bool is_video =
stream_index == video_stream_index &&
((kind0 == 'd' && kind1 == 'b') || (kind0 == 'd' && kind1 == 'c') ||
(kind0 == 'D' && kind1 == 'B') || (kind0 == 'D' && kind1 == 'C'));
bool is_audio =
stream_index == audio_stream_index &&
kind0 == 'w' && kind1 == 'b';
if (!is_video && !is_audio) continue;
uint64_t header = 0;
uint32_t raw_offset = rd_u32(buf + off + 8);
if (!resolve_idx1_header_offset(file, id, chunk_size, raw_offset,
movi_payload, movi_data_start,
&offset_mode, &header)) {
continue;
}
uint64_t payload = header + 8ull;
if (payload > 0xFFFFFFFFull) continue;
if (payload > file.size || (uint64_t)chunk_size > file.size - payload) continue;
if (is_video) {
if (!push_chunk(file.video_chunks, file.video_chunk_count, file.video_chunk_cap,
(uint32_t)payload, chunk_size)) {
set_error(file, "Out of memory indexing video frames");
return false;
}
} else {
if (!push_chunk(file.audio_chunks, file.audio_chunk_count, file.audio_chunk_cap,
(uint32_t)payload, chunk_size)) {
set_error(file, "Out of memory indexing audio chunks");
return false;
}
file.total_audio_bytes += chunk_size;
}
if (out_used_index) *out_used_index = true;
}
pos += bytes;
}
return true;
}
void reset(File& file) {
if (file.fd >= 0) montauk::close(file.fd);
if (file.video_chunks) montauk::mfree(file.video_chunks);
@@ -396,6 +579,11 @@ bool parse(File& file, int fd, uint64_t size) {
int stream_counter = 0;
int video_stream_index = -1;
int audio_stream_index = -1;
uint64_t movi_payload = 0;
uint64_t movi_data_start = 0;
uint64_t movi_data_size = 0;
uint64_t idx1_payload = 0;
uint64_t idx1_size = 0;
uint64_t pos = 12;
while (pos + 8 <= size) {
@@ -425,16 +613,35 @@ bool parse(File& file, int fd, uint64_t size) {
return false;
}
} else if (list_type == FCC_movi) {
if (!parse_movi_list(file, payload + 4, chunk_size - 4,
video_stream_index, audio_stream_index)) {
return false;
}
movi_payload = payload;
movi_data_start = payload + 4;
movi_data_size = chunk_size - 4;
}
} else if (id == FCC_idx1) {
idx1_payload = payload;
idx1_size = chunk_size;
}
pos = next;
}
bool used_idx1 = false;
if (idx1_payload > 0 && movi_data_size > 0) {
if (!parse_idx1(file, idx1_payload, idx1_size,
video_stream_index, audio_stream_index,
movi_payload, movi_data_start, &used_idx1)) {
return false;
}
}
if (!used_idx1 && movi_data_size > 0) {
clear_chunk_index(file);
if (!parse_movi_list(file, movi_data_start, movi_data_size,
video_stream_index, audio_stream_index)) {
return false;
}
}
if (video_stream_index < 0 || file.video_chunk_count <= 0 || file.width <= 0 || file.height <= 0) {
set_error(file, "AVI video stream is missing or unsupported");
return false;