nx_hevc_parse.nx source
↩ module page · 98 lines · 7092 B
1// nx_hevc_parse.nx -- SOVEREIGN HEVC(H.265) bitstream parser, RUNG 1 of the sovereign HEVC decoder.
2// Pulls the hvcC (CodecPrivate) from a .mkv, splits out VPS/SPS/PPS, de-emulates the SPS RBSP, and
3// Exp-Golomb-decodes the SPS to recover the real stream params (size, chroma, bit depth, profile/level).
4// No third party: this is the foundation the CABAC + reconstruction rungs build on. Usage: nx_hevc_parse <in.mkv>
5// license_tier: ORIGINAL
6import "nx_syscalls.nx"
7const K_MAGIC_8388608: i64 = 8388608
8const K_MAGIC_8192: i64 = 8192
9
10func pe(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return sys_write(1,s,n) }
11func pn(v: i64) -> i64 { var m: i64=v; if m<0{m=0-m} let b: *u8=sys_mmap(32); var i: i64=32; if m==0{i=i-1;b[i]=(48 as u8)} while m>0{let q: i64=m/10; i=i-1; b[i]=((48+(m-q*10)) as u8); m=q} return sys_write(1,((b as i64)+i) as *u8,32-i) }
12// ---- EBML (to fetch CodecPrivate=hvcC) ----
13func vlen(b0: i64) -> i64 { if (b0&0x80)!=0{return 1} if (b0&0x40)!=0{return 2} if (b0&0x20)!=0{return 3} if (b0&0x10)!=0{return 4} if (b0&0x08)!=0{return 5} if (b0&0x04)!=0{return 6} if (b0&0x02)!=0{return 7} return 8 }
14func rid(b: *u8, i: i64, lo: *i64) -> i64 { let L: i64=vlen(b[i] as i64); var v: i64=0; var k: i64=0; while k<L {v=(v<<8)|(b[i+k] as i64); k=k+1} lo[0]=L; return v }
15func rsz(b: *u8, i: i64, lo: *i64) -> i64 { let L: i64=vlen(b[i] as i64); var v: i64=(b[i] as i64)&(0xff>>L); var k: i64=1; while k<L {v=(v<<8)|(b[i+k] as i64); k=k+1} lo[0]=L; return v }
16// ---- bit reader over an RBSP (bs[0]=ptr bs[1]=bitpos bs[2]=len) ----
17func bget1(bs: *i64) -> i64 { let p: *u8=bs[0] as *u8; let bp: i64=bs[1]; let byte: i64=p[bp>>3] as i64; let bit: i64=(byte>>(7-(bp&7)))&1; bs[1]=bp+1; return bit }
18func bget(bs: *i64, n: i64) -> i64 { var v: i64=0; var i: i64=0; while i<n { v=(v<<1)|bget1(bs); i=i+1 } return v }
19func bue(bs: *i64) -> i64 { var lz: i64=0; while bget1(bs)==0 { lz=lz+1; if lz>40 {return 0} } if lz==0 {return 0} return ((1<<lz)-1) + bget(bs,lz) }
20
21func main(argc: i64, argv: *i64) -> i64 {
22 if argc<2 { pe("usage: nx_hevc_parse <in.mkv>\n" as *u8); return 2 }
23 let fd: i64=sys_openat_rd(argv[1] as *u8); if fd<0 { pe("ERROR open\n" as *u8); return 1 }
24 let cap: i64=K_MAGIC_8388608; let b: *u8=sys_mmap(cap+16)
25 var off: i64=0; while off<cap { let r: i64=sys_read(fd, ((b as i64)+off) as *u8, cap-off); if r<=0 {off=off; break} off=off+r }
26 sys_close(fd); let n: i64=off
27 let lo: *i64=sys_mmap(16) as *i64
28 // skip EBML header, find Segment, walk to Tracks, find the video TrackEntry's CodecPrivate
29 var i: i64=0
30 let id0: i64=rid(b,i,lo); let idl0: i64=lo[0]; let sz0: i64=rsz(b,i+idl0,lo); let szl0: i64=lo[0]
31 if id0!=0x1A45DFA3 { pe("not EBML\n" as *u8); return 1 }
32 i=i+idl0+szl0+sz0
33 let sid: i64=rid(b,i,lo); let sidl: i64=lo[0]; let ssz: i64=rsz(b,i+sidl,lo); let sszl: i64=lo[0]
34 if sid!=0x18538067 { pe("no Segment\n" as *u8); return 1 }
35 let cp: *u8=sys_mmap(K_MAGIC_8192); var cpn: i64=0
36 var p: i64=i+sidl+sszl
37 while p+2<n {
38 let eid: i64=rid(b,p,lo); let eidl: i64=lo[0]; let esz: i64=rsz(b,p+eidl,lo); let eszl: i64=lo[0]; let ed: i64=p+eidl+eszl
39 if eid==0x1654AE6B {
40 var q: i64=ed
41 while q+2<=ed+esz {
42 let tid: i64=rid(b,q,lo); let tidl: i64=lo[0]; let tsz: i64=rsz(b,q+tidl,lo); let tszl: i64=lo[0]; let td: i64=q+tidl+tszl
43 if tid==0xAE {
44 var ttype: i64=0; var hashevc: i64=0; let lcp: *u8=sys_mmap(K_MAGIC_8192); var lcpn: i64=0
45 var r2: i64=td
46 while r2+2<=td+tsz {
47 let fid: i64=rid(b,r2,lo); let fidl: i64=lo[0]; let fsz: i64=rsz(b,r2+fidl,lo); let fszl: i64=lo[0]; let fd2: i64=r2+fidl+fszl
48 if fid==0x83 { ttype=(b[fd2] as i64) }
49 if fid==0x86 { if fsz>=14 { if b[fd2+2]==(77 as u8) { if b[fd2+6]==(72 as u8) { hashevc=1 } } } }
50 if fid==0x63A2 { lcpn=fsz; var c: i64=0; while c<fsz { if c<K_MAGIC_8192 { lcp[c]=b[fd2+c] } c=c+1 } }
51 r2=fd2+fsz
52 }
53 if ttype==1 { if hashevc==1 { cpn=lcpn; var c2: i64=0; while c2<lcpn { cp[c2]=lcp[c2]; c2=c2+1 } } }
54 }
55 q=td+tsz
56 }
57 }
58 if eid==0x1F43B675 { p=n } else { if esz>n { p=n } else { p=ed+esz } }
59 }
60 if cpn==0 { pe("no HEVC track / CodecPrivate found\n" as *u8); return 1 }
61 pe("hvcC bytes=" as *u8); pn(cpn); pe("\n" as *u8)
62 // hvcC fixed header is 22 bytes, then numOfArrays at [22]
63 let lenSize: i64=((cp[21] as i64)&3)+1
64 let numArr: i64=cp[22] as i64
65 pe("lengthSize=" as *u8); pn(lenSize); pe(" numArrays=" as *u8); pn(numArr); pe("\n" as *u8)
66 var ap: i64=23; var spsoff: i64=0; var spslen: i64=0
67 var a: i64=0
68 while a<numArr {
69 let natype: i64=(cp[ap] as i64)&0x3f; let numNal: i64=((cp[ap+1] as i64)<<8)|(cp[ap+2] as i64); ap=ap+3
70 var k: i64=0
71 while k<numNal {
72 let nl: i64=((cp[ap] as i64)<<8)|(cp[ap+1] as i64); ap=ap+2
73 if natype==33 { if spslen==0 { spsoff=ap; spslen=nl } } // 33 = SPS
74 pe(" array type=" as *u8); pn(natype); pe(" nal_len=" as *u8); pn(nl); pe("\n" as *u8)
75 ap=ap+nl; k=k+1
76 }
77 a=a+1
78 }
79 if spslen==0 { pe("no SPS in hvcC\n" as *u8); return 1 }
80 // de-emulate the SPS RBSP (drop 0x03 in 00 00 03), skipping the 2-byte NAL header
81 let rb: *u8=sys_mmap(spslen+16); var rn: i64=0; var z: i64=0; var s: i64=spsoff+2
82 while s<spsoff+spslen { let cur: i64=cp[s] as i64; if z>=2 { if cur==3 { z=0; s=s+1 } else { rb[rn]=cur as u8; rn=rn+1; if cur==0 {z=z+1} else {z=0} s=s+1 } } else { rb[rn]=cur as u8; rn=rn+1; if cur==0 {z=z+1} else {z=0} s=s+1 } }
83 // parse SPS
84 let bs: *i64=sys_mmap(32) as *i64; bs[0]=rb as i64; bs[1]=0; bs[2]=rn
85 let vps_id: i64=bget(bs,4); let maxsub: i64=bget(bs,3); let nesting: i64=bget(bs,1)
86 // profile_tier_level (general): 8 + 32 + 4 + 44 + 8 = 96 bits; capture general_profile_idc + level_idc
87 let ptl0: i64=bget(bs,8); let prof_idc: i64=ptl0&0x1f
88 bget(bs,32); bget(bs,32); bget(bs,16) // compat(32) + 4flags+28 + 16 = 32+32+16 = 80 ... split: 32 compat, then 48 (4+44)
89 let level_idc: i64=bget(bs,8)
90 if maxsub>0 { pe("(note: max_sub_layers_minus1=" as *u8); pn(maxsub); pe(" >0; sub-layer PTL not parsed in R1)\n" as *u8) }
91 let sps_id: i64=bue(bs); let chroma: i64=bue(bs)
92 if chroma==3 { bget(bs,1) }
93 let width: i64=bue(bs); let height: i64=bue(bs)
94 let confwin: i64=bget(bs,1); if confwin==1 { bue(bs); bue(bs); bue(bs); bue(bs) }
95 let bd_luma: i64=bue(bs)+8; let bd_chroma: i64=bue(bs)+8
96 pe("SPS: " as *u8); pn(width); pe("x" as *u8); pn(height); pe(" chroma_idc=" as *u8); pn(chroma); pe(" (1=4:2:0) bitdepth=" as *u8); pn(bd_luma); pe("/" as *u8); pn(bd_chroma); pe(" profile_idc=" as *u8); pn(prof_idc); pe(" level=" as *u8); pn(level_idc/30); pe(".x vps=" as *u8); pn(vps_id); pe(" sps=" as *u8); pn(sps_id); pe("\n" as *u8)
97 return 0
98}