nx_mp4_faststart.nx source
↩ module page · 92 lines · 5387 B
1// nx_mp4_faststart.nx -- qt-faststart: relocate the moov atom to the FRONT of an mp4 so a browser <video>
2// can stream it progressively over HTTP. A non-faststart mp4 has [ftyp][mdat...][moov] (moov at the END), so
3// the player's first Range read has ftyp+mdat but NO moov (the track/sample tables) -> "no video, this format
4// is not supported". Moving moov before mdat fixes it. LOSSLESS: no transcode; the ONLY mutation is rewriting
5// each stco (32-bit) / co64 (64-bit) chunk offset by +moov_size, because inserting moov after ftyp shifts every
6// byte of mdat forward by exactly moov_size. Reads argv[1] (the mp4), writes the faststart mp4 to stdout (fd 1).
7// If the file is ALREADY faststart (moov before mdat) or has no moov, it is copied verbatim (safe no-op).
8// license_tier: ORIGINAL No hw writes (Rule 26). module: nishi-core.torrent.mp4_faststart
9import "nx_syscalls.nx"
10
11const FS_RD: i64 = 262144
12
13func fs_be32(b: *u8, o: i64) -> i64 { return ((b[o] as i64)<<24)|((b[o+1] as i64)<<16)|((b[o+2] as i64)<<8)|(b[o+3] as i64) }
14func fs_be64(b: *u8, o: i64) -> i64 { var v: i64=0; var i: i64=0; while i<8 { v=(v<<8)|(b[o+i] as i64); i=i+1 } return v }
15func fs_put32(b: *u8, o: i64, v: i64) -> i64 { b[o]=((v>>24)&0xff) as u8; b[o+1]=((v>>16)&0xff) as u8; b[o+2]=((v>>8)&0xff) as u8; b[o+3]=(v&0xff) as u8; return 0 }
16func fs_put64(b: *u8, o: i64, v: i64) -> i64 { var i: i64=0; while i<8 { b[o+7-i]=((v>>(i*8))&0xff) as u8; i=i+1 } return 0 }
17func fs_teq(b: *u8, o: i64, t: *u8) -> i64 { var i: i64=0; while i<4 { if b[o+i]!=t[i] { return 0 } i=i+1 } return 1 }
18func fs_iscont(b: *u8, o: i64) -> i64 {
19 if fs_teq(b,o,"moov" as *u8)==1 { return 1 } if fs_teq(b,o,"trak" as *u8)==1 { return 1 }
20 if fs_teq(b,o,"mdia" as *u8)==1 { return 1 } if fs_teq(b,o,"minf" as *u8)==1 { return 1 }
21 if fs_teq(b,o,"stbl" as *u8)==1 { return 1 } if fs_teq(b,o,"edts" as *u8)==1 { return 1 }
22 return 0
23}
24
25// recursively walk the box tree in m[start,end) and add `shift` to every stco/co64 chunk offset.
26func fs_adjust(m: *u8, start: i64, end: i64, shift: i64) -> i64 {
27 var p: i64 = start
28 while p + 8 <= end {
29 var sz: i64 = fs_be32(m, p); var hdr: i64 = 8
30 if sz == 1 { if p+16<=end { sz = fs_be64(m, p+8); hdr = 16 } else { p = end } }
31 if sz < 8 { p = end } else {
32 var boxend: i64 = p + sz; if boxend > end { boxend = end }
33 if fs_iscont(m, p+4) == 1 { fs_adjust(m, p+hdr, boxend, shift) }
34 else {
35 if fs_teq(m,p+4,"stco" as *u8)==1 {
36 let cnt: i64 = fs_be32(m, p+12); var e: i64=0
37 while e < cnt { let eo: i64 = p+16+e*4; if eo+4<=boxend { fs_put32(m, eo, fs_be32(m,eo)+shift) } e=e+1 }
38 }
39 if fs_teq(m,p+4,"co64" as *u8)==1 {
40 let cnt2: i64 = fs_be32(m, p+12); var e2: i64=0
41 while e2 < cnt2 { let eo2: i64 = p+16+e2*8; if eo2+8<=boxend { fs_put64(m, eo2, fs_be64(m,eo2)+shift) } e2=e2+1 }
42 }
43 }
44 p = p + sz
45 }
46 }
47 return 0
48}
49
50// stream file[start..start+len) to stdout in FS_RD chunks.
51func fs_emit(fd: i64, start: i64, len: i64, buf: *u8) -> i64 {
52 sys_lseek(fd, start, 0); var rem: i64 = len
53 while rem > 0 { var want: i64 = FS_RD; if rem < want { want = rem } let r: i64 = sys_read(fd, buf, want); if r<=0 { rem=0 } else { sys_write(1, buf, r); rem=rem-r } }
54 return 0
55}
56
57func main(argc: i64, argv: *i64) -> i64 {
58 if argc < 2 { sys_exit(2); return 2 }
59 let fd: i64 = sys_openat_rd(argv[1] as *u8); if fd < 0 { sys_exit(1); return 1 }
60 let fsz: i64 = sys_lseek(fd, 0, 2)
61 let cbuf: *u8 = sys_mmap(FS_RD)
62 // scan top-level boxes: ftyp end F, moov start V + size moovsz
63 let hb: *u8 = sys_mmap(32)
64 var pos: i64 = 0; var F: i64 = 0; var V: i64 = 0-1; var moovsz: i64 = 0; var go: i64 = 1
65 while go == 1 {
66 if pos + 8 > fsz { go = 0 } else {
67 sys_lseek(fd, pos, 0); let r: i64 = sys_read(fd, hb, 16)
68 if r < 8 { go = 0 } else {
69 var sz: i64 = fs_be32(hb, 0)
70 if sz == 1 { sz = fs_be64(hb, 8) }
71 if sz < 8 { go = 0 } else {
72 if fs_teq(hb,4,"ftyp" as *u8)==1 { F = pos + sz }
73 if fs_teq(hb,4,"moov" as *u8)==1 { V = pos; moovsz = sz; go = 0 } // found moov -> done scanning
74 pos = pos + sz
75 }
76 }
77 }
78 }
79 // no moov found, OR moov already at/near the front (before the data) -> emit verbatim (safe no-op)
80 if V < 0 { fs_emit(fd, 0, fsz, cbuf); sys_close(fd); sys_exit(0); return 0 }
81 if V <= F { fs_emit(fd, 0, fsz, cbuf); sys_close(fd); sys_exit(0); return 0 }
82 // load moov, adjust every chunk offset by +moovsz (mdat shifts forward by moovsz when moov moves to front)
83 let moov: *u8 = sys_mmap(moovsz + 64)
84 sys_lseek(fd, V, 0); var mrem: i64 = moovsz; var mo: i64 = 0
85 while mrem > 0 { var w: i64 = FS_RD; if mrem<w { w=mrem } let r2: i64 = sys_read(fd, (moov as i64 + mo) as *u8, w); if r2<=0 { mrem=0 } else { mo=mo+r2; mrem=mrem-r2 } }
86 fs_adjust(moov, 0, moovsz, moovsz)
87 // emit: ftyp [0..F] + adjusted moov + [F..V] (mdat and anything between ftyp and the old moov)
88 fs_emit(fd, 0, F, cbuf)
89 sys_write(1, moov, moovsz)
90 fs_emit(fd, F, V - F, cbuf)
91 sys_close(fd); sys_exit(0); return 0
92}