Skip to content

Commit 7f1d14c

Browse files
committed
prioritize moov atom (last 10MB) + header for MP4/MOV streaming
Browsers need the moov atom (at end of file) to start playback. Mark these pieces as critical so WebTorrent downloads them first, alongside the file header at the beginning.
1 parent 548e202 commit 7f1d14c

1 file changed

Lines changed: 44 additions & 0 deletions

File tree

‎src/lib/streaming/streaming.ts‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,50 @@ export class StreamingService {
776776
file.select();
777777
logger.debug('File selected for download priority');
778778

779+
// For MP4/MOV files, prioritize downloading the last ~10MB where the moov atom lives
780+
// Without the moov atom, browsers cannot begin playback (even for raw streaming)
781+
const ext = file.name.split('.').pop()?.toLowerCase();
782+
const MOOV_FORMATS = new Set(['mp4', 'm4v', 'mov', 'm4a', '3gp', '3g2']);
783+
if (MOOV_FORMATS.has(ext ?? '') && file.length > 10 * 1024 * 1024) {
784+
const fileOffset = (file as unknown as { offset: number }).offset ?? 0;
785+
const moovBytes = 10 * 1024 * 1024; // 10MB should cover any moov atom
786+
const moovStart = fileOffset + file.length - moovBytes;
787+
const moovStartPiece = Math.floor(moovStart / torrent.pieceLength);
788+
const moovEndPiece = Math.ceil((fileOffset + file.length) / torrent.pieceLength) - 1;
789+
790+
// Also prioritize the first few pieces for the ftyp header
791+
const headerEndPiece = Math.min(
792+
Math.ceil((fileOffset + 1 * 1024 * 1024) / torrent.pieceLength),
793+
torrent.pieces.length - 1
794+
);
795+
const headerStartPiece = Math.floor(fileOffset / torrent.pieceLength);
796+
797+
logger.info('Prioritizing moov atom + header for MP4/MOV', {
798+
fileName: file.name,
799+
fileSizeMB: (file.length / (1024 * 1024)).toFixed(0),
800+
headerPieces: `${headerStartPiece}-${headerEndPiece}`,
801+
moovPieces: `${moovStartPiece}-${moovEndPiece}`,
802+
totalPieces: torrent.pieces.length,
803+
pieceLength: torrent.pieceLength,
804+
});
805+
806+
// Use select with high priority for these critical ranges
807+
// WebTorrent select(start, end, priority) — higher priority = downloaded first
808+
file.select();
809+
try {
810+
// Critical pieces for the header (beginning of file)
811+
for (let i = headerStartPiece; i <= headerEndPiece; i++) {
812+
(torrent as unknown as { critical: (start: number, end: number) => void }).critical(i, i);
813+
}
814+
// Critical pieces for moov atom (end of file)
815+
for (let i = moovStartPiece; i <= moovEndPiece; i++) {
816+
(torrent as unknown as { critical: (start: number, end: number) => void }).critical(i, i);
817+
}
818+
} catch (err) {
819+
logger.warn('Could not set critical pieces', { error: String(err) });
820+
}
821+
}
822+
779823
// Log current download state
780824
const fileProgress = (file as unknown as { progress: number }).progress ?? 0;
781825
const downloadedBytes = fileProgress * file.length;

0 commit comments

Comments
 (0)