Files
cls 9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
feat: import Chinese-localized Buzz source snapshot
Signed-off-by: cls_宁波本机 <908705107@qq.com>
2026-08-13 18:34:25 +08:00

346 lines
9.1 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
const _maxTopLevelBoxes = 4096;
const _maxNestedBoxes = 100000;
const _maxBoxDepth = 32;
const _maxMoovBytes = 64 * 1024 * 1024;
const _copyBufferBytes = 1024 * 1024;
const _uint32Max = 0xffffffff;
const _uint64Max = 0x7fffffffffffffff;
const _containerTypes = {
'moov',
'trak',
'mdia',
'minf',
'stbl',
'edts',
'dinf',
};
final class _Mp4Box {
final int offset;
final int size;
final int headerSize;
final String type;
const _Mp4Box({
required this.offset,
required this.size,
required this.headerSize,
required this.type,
});
}
/// Rewrites [source] to [destination] with `moov` before the first `mdat`.
///
/// Only chunk offsets into the region moved by the relocation are adjusted.
/// The files must be distinct, and malformed or excessively nested inputs fail
/// closed rather than producing a partially valid upload.
Future<void> rewriteMp4ForFastStart(File source, File destination) async {
if (source.absolute.path == destination.absolute.path) {
throw const FormatException(
'MP4 fast-start rewrite requires a distinct destination',
);
}
final input = await source.open();
try {
final boxes = await _readTopLevelBoxes(input);
if (boxes.isEmpty || boxes.first.type != 'ftyp') {
throw const FormatException('MP4 must start with an ftyp box');
}
final moovBoxes = boxes.where((box) => box.type == 'moov').toList();
if (moovBoxes.length != 1) {
throw const FormatException('MP4 must contain exactly one moov box');
}
final moov = moovBoxes.single;
final mdatBoxes = boxes.where((box) => box.type == 'mdat').toList();
final firstMdat = mdatBoxes.isEmpty ? null : mdatBoxes.first;
if (firstMdat == null) {
throw const FormatException('MP4 is missing an mdat box');
}
if (moov.size > _maxMoovBytes) {
throw const FormatException('MP4 moov box is too large');
}
await input.setPosition(moov.offset);
final moovBytes = await input.read(moov.size);
if (moovBytes.length != moov.size) {
throw const FormatException('truncated MP4 moov box');
}
if (moov.offset > firstMdat.offset) {
_patchChunkOffsets(
moovBytes,
moov.headerSize,
moovBytes.length,
firstMdat.offset,
moov.offset,
moov.size,
0,
[0],
);
}
final output = await destination.open(mode: FileMode.write);
try {
var insertedMoov = false;
for (final box in boxes) {
if (box.type == 'moov') continue;
if (!insertedMoov && box.type == 'mdat') {
await output.writeFrom(moovBytes);
insertedMoov = true;
}
await _copyRange(input, output, box.offset, box.size);
}
if (!insertedMoov) {
throw const FormatException('MP4 is missing an mdat box');
}
await output.flush();
} finally {
await output.close();
}
} catch (_) {
try {
await destination.delete();
} on FileSystemException {
// Best-effort cleanup of a partial rewrite.
}
rethrow;
} finally {
await input.close();
}
}
Future<List<_Mp4Box>> _readTopLevelBoxes(RandomAccessFile input) async {
final length = await input.length();
final boxes = <_Mp4Box>[];
var offset = 0;
while (offset < length) {
if (boxes.length >= _maxTopLevelBoxes) {
throw const FormatException('MP4 has too many top-level boxes');
}
final box = await _readFileBoxHeader(input, offset, length);
boxes.add(box);
offset += box.size;
}
if (offset != length) {
throw const FormatException('MP4 boxes do not cover the file');
}
return boxes;
}
Future<_Mp4Box> _readFileBoxHeader(
RandomAccessFile input,
int offset,
int end,
) async {
if (end - offset < 8) {
throw const FormatException('truncated MP4 box header');
}
await input.setPosition(offset);
final compactHeader = await input.read(8);
final compactSize = _readUint32(compactHeader, 0);
final type = latin1.decode(compactHeader.sublist(4, 8));
final int headerSize;
final int size;
if (compactSize == 1) {
if (end - offset < 16) {
throw const FormatException('truncated extended MP4 box header');
}
final extended = await input.read(8);
headerSize = 16;
size = _readUint64(extended, 0);
} else if (compactSize == 0) {
headerSize = 8;
size = end - offset;
} else {
headerSize = 8;
size = compactSize;
}
if (size < headerSize || size > end - offset) {
throw const FormatException('invalid MP4 box size');
}
return _Mp4Box(
offset: offset,
size: size,
headerSize: headerSize,
type: type,
);
}
Future<void> _copyRange(
RandomAccessFile input,
RandomAccessFile output,
int offset,
int size,
) async {
await input.setPosition(offset);
var remaining = size;
while (remaining > 0) {
final bytes = await input.read(
remaining < _copyBufferBytes ? remaining : _copyBufferBytes,
);
if (bytes.isEmpty) {
throw const FormatException('truncated MP4 while copying');
}
await output.writeFrom(bytes);
remaining -= bytes.length;
}
}
void _patchChunkOffsets(
Uint8List bytes,
int start,
int end,
int movedRegionStart,
int movedRegionEnd,
int delta,
int depth,
List<int> boxesSeen,
) {
if (depth > _maxBoxDepth) {
throw const FormatException('MP4 box nesting is too deep');
}
var offset = start;
while (offset < end) {
boxesSeen[0]++;
if (boxesSeen[0] > _maxNestedBoxes) {
throw const FormatException('MP4 has too many nested boxes');
}
final box = _readMemoryBoxHeader(bytes, offset, end);
final boxEnd = offset + box.size;
switch (box.type) {
case 'stco':
_patchStco(
bytes,
offset + box.headerSize,
boxEnd,
movedRegionStart,
movedRegionEnd,
delta,
);
break;
case 'co64':
_patchCo64(
bytes,
offset + box.headerSize,
boxEnd,
movedRegionStart,
movedRegionEnd,
delta,
);
break;
case final type when _containerTypes.contains(type):
_patchChunkOffsets(
bytes,
offset + box.headerSize,
boxEnd,
movedRegionStart,
movedRegionEnd,
delta,
depth + 1,
boxesSeen,
);
break;
}
offset = boxEnd;
}
if (offset != end) {
throw const FormatException('MP4 child boxes do not cover their parent');
}
}
_Mp4Box _readMemoryBoxHeader(Uint8List bytes, int offset, int end) {
if (end - offset < 8) {
throw const FormatException('truncated MP4 child box');
}
final compactSize = _readUint32(bytes, offset);
final type = latin1.decode(bytes.sublist(offset + 4, offset + 8));
final int headerSize;
final int size;
if (compactSize == 1) {
if (end - offset < 16) {
throw const FormatException('truncated extended MP4 child box');
}
headerSize = 16;
size = _readUint64(bytes, offset + 8);
} else if (compactSize == 0) {
headerSize = 8;
size = end - offset;
} else {
headerSize = 8;
size = compactSize;
}
if (size < headerSize || size > end - offset) {
throw const FormatException('invalid MP4 child box size');
}
return _Mp4Box(
offset: offset,
size: size,
headerSize: headerSize,
type: type,
);
}
void _patchStco(
Uint8List bytes,
int start,
int end,
int movedRegionStart,
int movedRegionEnd,
int delta,
) {
if (end - start < 8) throw const FormatException('truncated stco box');
final count = _readUint32(bytes, start + 4);
if (count > (end - start - 8) ~/ 4) {
throw const FormatException('invalid stco entry count');
}
final data = ByteData.sublistView(bytes);
for (var index = 0; index < count; index++) {
final entry = start + 8 + index * 4;
final value = data.getUint32(entry, Endian.big);
if (value >= movedRegionStart && value < movedRegionEnd) {
final adjusted = value + delta;
if (adjusted > _uint32Max) {
throw const FormatException('stco offset overflow');
}
data.setUint32(entry, adjusted, Endian.big);
}
}
}
void _patchCo64(
Uint8List bytes,
int start,
int end,
int movedRegionStart,
int movedRegionEnd,
int delta,
) {
if (end - start < 8) throw const FormatException('truncated co64 box');
final count = _readUint32(bytes, start + 4);
if (count > (end - start - 8) ~/ 8) {
throw const FormatException('invalid co64 entry count');
}
final data = ByteData.sublistView(bytes);
for (var index = 0; index < count; index++) {
final entry = start + 8 + index * 8;
final value = data.getUint64(entry, Endian.big);
if (value >= movedRegionStart && value < movedRegionEnd) {
final adjusted = value + delta;
if (adjusted > _uint64Max) {
throw const FormatException('co64 offset overflow');
}
data.setUint64(entry, adjusted, Endian.big);
}
}
}
int _readUint32(Uint8List bytes, int offset) =>
ByteData.sublistView(bytes).getUint32(offset, Endian.big);
int _readUint64(Uint8List bytes, int offset) =>
ByteData.sublistView(bytes).getUint64(offset, Endian.big);