code wiki / (root) / nx_h264_pframe_probe.nx

nx_h264_pframe_probe.nx source

↩ module page · 128 lines · 6347 B

1// nx_h264_pframe_probe.nx -- P-R0: locate frame 1 (sample 1), parse its P-slice header, 2// reach the first P-macroblock. Proves the P-slice-header parse lands correctly (a wrong 3// parse desyncs -> garbage mb_skip_run/mb_type). Grounds the P-frame (inter) decode arc. 4// Verified target (ffmpeg trace_headers): slice_type=P(5), num_ref_idx_l0=0(+1 ref), QP=30. 5// license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "nx_mp4_demux.nx" 8import "nx_mp4_stbl.nx" 9import "nx_h264_bits.nx" 10import "nx_h264_sps.nx" 11import "nx_h264_pps.nx" 12import "nx_h264_nal.nx" 13import "nx_h264_slice.nx" 14 15func gp(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 16func gn(v: i64) -> i64 { 17 let bb: *u8 = sys_mmap(28); var m: i64 = v 18 if m < 0 { sys_write(1, "-\x00" as *u8, 1); m = 0 - m } 19 let t: *u8 = sys_mmap(28); var k: i64 = 0 20 if m == 0 { t[0] = 48 as u8; k = 1 } 21 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 22 var i: i64 = 0 23 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 24 sys_write(1, bb, k); return 0 25} 26func find_tag(b: *u8, start: i64, end: i64, s: *u8) -> i64 { 27 var i: i64 = start 28 while i + 4 <= end { if mp4_t4(b, i, s) == 1 { return i - 4 } i = i + 1 } 29 return 0 - 1 30} 31 32func main() -> i64 { 33 let szp: *i64 = sys_mmap(16) as *i64 34 let b: *u8 = sys_read_file("knowledge/staging/media/realtest.mp4\x00" as *u8, szp) 35 if (b as i64) == 0 { gp("pframe_probe: no realtest.mp4\n\x00" as *u8); return 1 } 36 let len: i64 = szp[0] 37 38 // SPS + PPS 39 let avcc: i64 = find_tag(b, 0, len, "avcC\x00" as *u8) 40 let pay: i64 = avcc + 8 41 let spsLen: i64 = (b[pay + 6] as i64) * 256 + (b[pay + 7] as i64) 42 let spsdst: *u8 = sys_mmap(spsLen + 16) 43 let spsrbsp: i64 = nx_h264_unescape_rbsp((b as i64 + pay + 9) as *u8, spsLen - 1, spsdst) 44 let sps: *i64 = sys_mmap(128) as *i64 45 nx_h264_parse_sps(spsdst, spsrbsp, sps) 46 let ppspos: i64 = pay + 8 + spsLen 47 let ppsLen: i64 = (b[ppspos + 1] as i64) * 256 + (b[ppspos + 2] as i64) 48 let ppsdst: *u8 = sys_mmap(ppsLen + 16) 49 let ppsrbsp: i64 = nx_h264_unescape_rbsp((b as i64 + ppspos + 3 + 1) as *u8, ppsLen - 1, ppsdst) 50 let pps: *i64 = sys_mmap(128) as *i64 51 nx_h264_parse_pps(ppsdst, ppsrbsp, pps) 52 53 // sample table: chunk0 + sample sizes 54 let vi: *i64 = sys_mmap(64) as *i64 55 nx_mp4_video_info(b, len, vi) 56 let stbl0: i64 = vi[5] 57 let stbl1: i64 = vi[6] 58 let rco: *i64 = sys_mmap(16) as *i64 59 let rsz: *i64 = sys_mmap(16) as *i64 60 mp4_find_box(b, stbl0, stbl1, "stco\x00" as *u8, rco) 61 mp4_find_box(b, stbl0, stbl1, "stsz\x00" as *u8, rsz) 62 let chunk0: i64 = mp4_be32(b, rco[0] + 8) 63 let samp_sz_field: i64 = mp4_be32(b, rsz[0] + 4) 64 var samp0: i64 = samp_sz_field 65 var samp1: i64 = samp_sz_field 66 if samp_sz_field == 0 { 67 samp0 = mp4_be32(b, rsz[0] + 12) // entry 0 68 samp1 = mp4_be32(b, rsz[0] + 16) // entry 1 69 } 70 // stco/stsc layout (the mdat interleaves audio, so video samples are NOT contiguous) 71 let stco_n: i64 = mp4_be32(b, rco[0] + 4) 72 gp(" stco entry_count=\x00" as *u8); gn(stco_n); gp(" chunks=[\x00" as *u8) 73 var ci: i64 = 0 74 while ci < stco_n { if ci < 5 { gn(mp4_be32(b, rco[0] + 8 + ci * 4)); gp(" \x00" as *u8) } ci = ci + 1 } 75 gp("]\n\x00" as *u8) 76 let rsc: *i64 = sys_mmap(16) as *i64 77 var stsc_n: i64 = 0 78 if mp4_find_box(b, stbl0, stbl1, "stsc\x00" as *u8, rsc) == 1 { 79 stsc_n = mp4_be32(b, rsc[0] + 4) 80 gp(" stsc entry_count=\x00" as *u8); gn(stsc_n); gp(" first(first_chunk,spc,desc)=\x00" as *u8) 81 gn(mp4_be32(b, rsc[0] + 8)); gp(",\x00" as *u8); gn(mp4_be32(b, rsc[0] + 12)); gp(",\x00" as *u8); gn(mp4_be32(b, rsc[0] + 16)); gp("\n\x00" as *u8) 82 } 83 // try sample1 = chunk1 offset (if 1 sample/chunk) else chunk0+samp0 84 var s1off: i64 = chunk0 + samp0 85 if stco_n > 1 { s1off = mp4_be32(b, rco[0] + 12) } 86 gp(" sample0 size=\x00" as *u8); gn(samp0); gp(" sample1 off=\x00" as *u8); gn(s1off); gp(" size=\x00" as *u8); gn(samp1); gp("\n\x00" as *u8) 87 88 // NAL-split sample 1 89 let offs: *i64 = sys_mmap(256 * 8) as *i64 90 let types: *i64 = sys_mmap(256 * 8) as *i64 91 let nc: i64 = nx_nal_split_avcc(b, s1off, s1off + samp1, 4, offs, types, 256) 92 gp(" sample1 NALs=\x00" as *u8); gn(nc); gp(" types=[\x00" as *u8) 93 var i: i64 = 0 94 while i < nc { gn(types[i]); gp(" \x00" as *u8); i = i + 1 } 95 gp("]\n\x00" as *u8) 96 97 // find the slice NAL (type 1 = non-IDR P) 98 var sidx: i64 = 0 - 1 99 i = 0 100 while i < nc { if types[i] == 1 { if sidx < 0 { sidx = i } } i = i + 1 } 101 if sidx < 0 { gp(" no non-IDR slice in sample1 (P-frame not located)\n\x00" as *u8); return 2 } 102 let noff: i64 = offs[sidx] 103 let ntype: i64 = b[noff] as i64 104 let nlen: i64 = mp4_be32(b, noff - 4) 105 let sl_dst: *u8 = sys_mmap(nlen + 64) 106 let sl_rbsp: i64 = nx_h264_unescape_rbsp((b as i64 + noff + 1) as *u8, nlen - 1, sl_dst) 107 let sh: *i64 = sys_mmap(64) as *i64 108 let sbr: *BitReader = sys_mmap(NX_BR_BYTES) as *BitReader 109 br_init(sbr, sl_dst, sl_rbsp) 110 nx_h264_parse_slice_header_br(sbr, ntype & 31, (ntype >> 5) & 3, sps[9], sps[11], sps[10], sps[6], pps[3], pps[6], sh) 111 gp(" P-SLICE nal_type=\x00" as *u8); gn(ntype & 31); gp(" ref_idc=\x00" as *u8); gn((ntype >> 5) & 3) 112 gp(" slice_type=\x00" as *u8); gn(sh[8]); gp(" (0=P) first_mb=\x00" as *u8); gn(sh[0]); gp(" num_ref_l0_minus1=\x00" as *u8); gn(sh[12]); gp(" QP=\x00" as *u8); gn(sh[7]); gp("\n\x00" as *u8) 113 114 // reach the first P macroblock: mb_skip_run (ue), then (if 0) mb_type (ue) 115 let skip_run: i64 = br_read_ue(sbr) 116 gp(" mb_skip_run=\x00" as *u8); gn(skip_run) 117 if skip_run == 0 { 118 let mbt: i64 = br_read_ue(sbr) 119 gp(" first mb_type=\x00" as *u8); gn(mbt); gp(" (P: 0=L0_16x16 1=16x8 2=8x16 3=P8x8 4=8x8ref0 >=5 intra)\x00" as *u8) 120 } 121 if skip_run > 0 { gp(" (first \x00" as *u8); gn(skip_run); gp(" MBs are P_Skip)\x00" as *u8) } 122 gp("\n\x00" as *u8) 123 124 // verdict: P-slice header parsed sanely? 125 if sh[8] == 0 { if sh[7] >= 0 { if sh[7] <= 51 { gp(" *** P-SLICE HEADER PARSED: slice_type=P, QP in range, first MB reached -> P-R0 GREEN ***\n\x00" as *u8); return 0 } } } 126 gp(" P-slice header parse looks wrong\n\x00" as *u8) 127 return 3 128}