shithub: hantro9

Download patch

ref: 73ef8b16fe03183346cd1272d0ed4d15028cb104
parent: 3296f28705a08bd9daff92a677b0fa76098941dd
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Tue May 2 22:39:58 EDT 2023

garbage ivf-h264-extractor tool

--- /dev/null
+++ b/ivfh264.c
@@ -1,0 +1,849 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "vpuctx.h"
+
+enum {
+	Extended_SAR = 255,
+};
+
+typedef struct Bits Bits;
+typedef struct Bitspos Bitspos;
+typedef struct nal_unit_header_svc_extension nal_unit_header_svc_extension;
+typedef struct nal_unit_header_mvc_extension nal_unit_header_mvc_extension;
+
+struct Bitspos {
+	vlong boff;
+	u64int p;
+	int c;
+	int nb;
+	int nz;
+	int eof;
+};
+
+struct Bits {
+	Bitspos;
+	Biobuf *b;
+};
+
+struct nal_unit_header_svc_extension {
+	u8int idr_flag;
+	u8int priority_id;
+	u8int no_inter_layer_pred_flag;
+	u8int dependency_id;
+	u8int quality_id;
+	u8int temporal_id;
+	u8int use_ref_base_pic_flag;
+	u8int discardable_flag;
+	u8int output_flag;
+	u8int reserved_three_2bits;
+};
+
+struct nal_unit_header_mvc_extension {
+	u8int non_idr_flag;
+	u8int priority_id;
+	u16int view_id;
+	u8int temporal_id;
+	u8int anchor_pic_flag;
+	u8int inter_view_flag;
+	u8int reserved_one_bit;
+};
+
+#define Default_4x4_Intra {6, 13, 13, 20, 20, 20, 28, 28, 28, 28, 32, 32, 32, 37, 37, 42}
+#define Default_4x4_Inter {10, 14, 14, 20, 20, 20, 24, 24, 24, 24, 27, 27, 27, 30, 30, 34}
+#define Default_8x8_Intra \
+	{6, 10, 10, 13, 11, 13, 16, 16, 16, 16, 18, 18, 18, 18, 18, 23, \
+	 23, 23, 23, 23, 23, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, \
+	 27, 27, 27, 27, 29, 29, 29, 29, 29, 29, 29, 31, 31, 31, 31, 31, \
+	 31, 33, 33, 33, 33, 33, 36, 36, 36, 36, 38, 38, 38, 40, 40, 42}
+#define Default_8x8_Inter \
+	{9, 13, 13, 15, 13, 15, 17, 17, 17, 17, 19, 19, 19, 19, 19, 21, \
+	 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 24, 24, 24, 24, \
+	 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, 27, \
+	 27, 28, 28, 28, 28, 28, 30, 30, 30, 30, 32, 32, 32, 33, 33, 35}
+
+static u8int defsl4x4[6][16] = {
+	Default_4x4_Intra, /* Sl_4x4_Intra_Y */
+	Default_4x4_Intra, /* Sl_4x4_Intra_Cb */
+	Default_4x4_Intra, /* Sl_4x4_Intra_Cr */
+	Default_4x4_Inter, /* Sl_4x4_Inter_Y */
+	Default_4x4_Inter, /* Sl_4x4_Inter_Cb */
+	Default_4x4_Inter, /* Sl_4x4_Inter_Cr */
+};
+
+static u8int defs8x8[6][64] = {
+	Default_8x8_Intra, /* Sl_8x8_Intra_Y */
+	Default_8x8_Inter, /* Sl_8x8_Inter_Y */
+	Default_8x8_Intra, /* Sl_8x8_Intra_Cb */
+	Default_8x8_Inter, /* Sl_8x8_Inter_Cb */
+	Default_8x8_Intra, /* Sl_8x8_Intra_Cr */
+	Default_8x8_Inter, /* Sl_8x8_Inter_Cr */
+};
+
+static Biobuf stdout;
+
+static int
+clz(u32int x)
+{
+	int r;
+	if(x == 0)
+		return 0;
+	for(r = 0; (x & (1U<<31)) == 0; x <<= 1, r++);
+	return r;
+}
+
+#define ceillog2(x) (32-clz(x))
+
+void
+mark(Bits *b, Bitspos *pos)
+{
+	*pos = b->Bitspos;
+	pos->boff = Boffset(b->b);
+}
+
+void
+unroll(Bits *b, Bitspos *pos)
+{
+	while(Boffset(b->b) != pos->boff){
+		if(Bungetc(b->b) < 0)
+			sysfatal("Bungetc\n");
+	}
+	b->Bitspos = *pos;
+}
+
+u32int
+rb_u(Bits *b, int n, char *s)
+{
+	int m;
+	u32int x;
+
+	if(n == 0)
+		return 0;
+
+	if(s != nil)
+		Bprint(&stdout, "%llud\t%s = ", b->p, s);
+	x = 0;
+	for(; n > 0; n -= m, b->p += m){
+		if(b->nb == 0){
+again:
+			if((b->c = Bgetc(b->b)) == Beof){
+				b->eof = 1;
+				return -1;
+			}
+			if(b->nz == 2 && b->c == 3){
+				b->nz = 0;
+				goto again;
+			}
+			b->nz = b->c == 0 ? b->nz+1 : 0;
+			b->nb = 8;
+		}
+
+		if((m = n) > b->nb)
+			m = b->nb;
+		x <<= m;
+		x |= (b->c >> b->nb-m) & (1<<m)-1;
+		b->nb -= m;
+	}
+	if(s != nil)
+		Bprint(&stdout, "%ud\n", x);
+	return x;
+}
+
+u32int
+rb_ue(Bits *b, char *s)
+{
+	u32int x;
+	int n;
+
+	if(s != nil)
+		Bprint(&stdout, "%llud\t%s = ", b->p, s);
+	for(n = 0; rb_u(b, 1, nil) == 0 && n < 32 && !b->eof; n++);
+	x = rb_u(b, n, nil) + (1<<n) - 1;
+	if(s != nil)
+		Bprint(&stdout, "%ud\n", x);
+	return x;
+}
+
+int
+rb_se(Bits *b, char *s)
+{
+	int x;
+	if(s != nil)
+		Bprint(&stdout, "%llud\t%s = ", b->p, s);
+	x = rb_ue(b, nil);
+	x = ((x & 1) == 0 ? -x : x+1) >> 1;
+	if(s != nil)
+		Bprint(&stdout, "%d\n", x);
+	return x;
+}
+
+#define u(n, s) rb_u(b, n, s)
+#define ue(s) rb_ue(b, s)
+#define se(s) rb_se(b, s)
+
+static void
+hrd_parameters(VPUctx *v, Bits *b)
+{
+	int i, n;
+
+	USED(v);
+	n = ue("cpb_cnt_minus1");
+	u(4, "bit_rate_scale");
+	u(4, "cpb_size_scale");
+	for(i = 0; i < n; i++){
+		char t[32];
+		snprint(t, sizeof(t), "bit_rate_value_minus1[%d]", i);
+		ue(t);
+		snprint(t, sizeof(t), "cpb_size_value_minus1[%d]", i);
+		ue(t);
+		snprint(t, sizeof(t), "cbr_flag[%d]", i);
+		u(1, t);
+	}
+	u(5, "initial_cpb_removal_delay_length_minus1");
+	u(5, "cpb_removal_delay_length_minus1");
+	u(5, "dpb_output_delay_length_minus1");
+	u(5, "time_offset_length");
+}
+
+static void
+scaling_list(VPUctx *v, Bits *b, int i)
+{
+	u8int *scalingList;
+	int f, j, lastScale, nextScale, sizeOfScalingList, delta_scale;
+
+	lastScale = nextScale = 8;
+	if(i < 6){
+		sizeOfScalingList = 16;
+		scalingList = &v->sl4x4[i*sizeOfScalingList];
+	}else{
+		sizeOfScalingList = 64;
+		scalingList = &v->sl8x8[(i-6)*sizeOfScalingList];
+	}
+	for(j = 0; j < sizeOfScalingList; j++){
+		if(nextScale != 0){
+			delta_scale = se("delta_scale");
+			nextScale = (lastScale + delta_scale + 256) % 256;
+			f = (j == 0 && nextScale == 0) << j;
+			v->useDefaultScalingMatrixFlag &= ~f;
+			v->useDefaultScalingMatrixFlag |= f;
+		}
+		lastScale = scalingList[j] = nextScale == 0 ? lastScale : nextScale;
+	}
+}
+
+static void
+vui_parameters(VPUctx *v, Bits *b)
+{
+	int n;
+
+	if(u(1, "aspect_ratio_info_present_flag")){
+		n = u(8, "aspect_ratio_idc");
+		if(n == Extended_SAR){
+			u(16, "sar_width");
+			u(16, "sar_height");
+		}
+	}
+	if(u(1, "overscan_info_present_flag"))
+		u(1, "overscan_appropriate_flag");
+	if(u(1, "video_signal_type_present_flag")){
+		u(3, "video_format");
+		u(1, "video_full_range_flag");
+		if(u(1, "colour_description_present_flag")){
+			u(8, "color_primaries");
+			u(8, "transfer_characteristics");
+			u(8, "matrix_coefficients");
+		}
+	}
+	if(u(1, "chroma_loc_info_present_flag")){
+		ue("chroma_sample_loc_type_top_field");
+		ue("chroma_sample_loc_type_bottom_field");
+	}
+	if(u(1, "timing_info_present_flag")){
+		u(32, "num_units_in_tick");
+		u(32, "time_scale");
+		u(1, "fixed_frame_rate_flag");
+	}
+	if((n = u(1, "nal_hrd_parameters_present_flag")))
+		hrd_parameters(v, b);
+	if((n |= u(1, "vcl_hrd_parameters_present_flag")))
+		hrd_parameters(v, b);
+	if(n != 0)
+		u(1, "low_delay_hrd_flag");
+	u(1, "pic_struct_present_flag");
+	if(u(1, "bitstream_restriction_flag")){
+		u(1, "motion_vectors_over_pic_boundaries_flag");
+		ue("max_bytes_per_pic_denom");
+		ue("max_bits_per_mb_denom");
+		ue("log2_max_mv_length_horizontal");
+		ue("log2_max_mv_length_vertical");
+		ue("max_num_reorder_frames");
+		ue("max_dec_frame_buffering");
+	}
+}
+
+static void
+rbsp_trailing_bits(Bits *b)
+{
+	u(1, "rbsp_stop_one_bit");
+	u(8 - (b->p & 7), "rbsp_alignment_zero_bit");
+}
+
+static int
+more_rbsp_data(Bits *b)
+{
+	Bitspos pos;
+	int more;
+
+	mark(b, &pos);
+	more = u(1, nil) == 0 || u(8 - (b->p & 7), nil) != 0;
+	unroll(b, &pos);
+	return more;
+}
+
+static u32int
+next_bits(Bits *b, int n)
+{
+	Bitspos pos;
+	u32int x;
+
+	mark(b, &pos);
+	x = u(n, nil);
+	unroll(b, &pos);
+	return x;
+}
+
+static void
+unit_header_svc_extension(VPUctx *v, Bits *b)
+{
+	nal_unit_header_svc_extension e;
+
+	USED(v);
+	Bprint(&stdout, "nal_unit_header_svc_extension()\n");
+	e.idr_flag = u(1, "idr_flag");
+	e.priority_id = u(6, "priority_id");
+	e.no_inter_layer_pred_flag = u(1, "no_inter_layer_pred_flag");
+	e.dependency_id = u(3, "dependency_id");
+	e.quality_id = u(4, "quality_id");
+	e.temporal_id = u(3, "temporal_id");
+	e.use_ref_base_pic_flag = u(1, "use_ref_base_pic_flag");
+	e.discardable_flag = u(1, "discardable_flag");
+	e.output_flag = u(1, "output_flag");
+	e.reserved_three_2bits = u(2, "reserved_three_2bits");
+}
+
+static void
+unit_header_mvc_extension(VPUctx *v, Bits *b)
+{
+	nal_unit_header_mvc_extension e;
+
+	USED(v);
+	Bprint(&stdout, "nal_unit_header_mvc_extension()\n");
+	e.non_idr_flag = u(1, "non_idr_flag");
+	e.priority_id = u(6, "priority_id");
+	e.view_id = u(10, "view_id");
+	e.temporal_id = u(3, "temporal_id");
+	e.anchor_pic_flag = u(1, "anchor_pic_flag");
+	e.inter_view_flag = u(1, "inter_view_flag");
+	e.reserved_one_bit = u(1, "reserved_one_bit");
+}
+
+static int
+more_rbsp_trailing_data(Bits *b)
+{
+	USED(b);
+	assert(nil);
+	return 0;
+}
+
+static void
+rbsp_slice_trailing_bits(VPUctx *v, Bits *b)
+{
+	rbsp_trailing_bits(b);
+	if(v->pps.entropy_coding_mode_flag){
+		while(more_rbsp_trailing_data(b))
+			u(16, "cabac_zero_word");
+	}
+}
+
+static void
+slice_header(VPUctx *v, Bits *b)
+{
+	u64int p;
+
+	Bprint(&stdout, "slice_header()\n");
+	ue("first_mb_in_slice");
+	ue("slice_type"); /* % 5 */
+	ue("pic_parameter_set_id");
+	if(v->sps.separate_colour_plane_flag)
+		u(2, "colour_plane_id");
+	v->sh.frame_num = u(v->sps.log2_max_frame_num_minus4 + 4, "frame_num");
+
+	v->sh.field_pic_flag = 0;
+	v->sh.bottom_field_flag = 0;
+	if(!v->sps.frame_mbs_only_flag){
+		if((v->sh.field_pic_flag = u(1, "field_pic_flag")) != 0)
+			v->sh.bottom_field_flag = u(1, "bottom_field_flag");
+	}
+
+	if(v->nal_unit_type == 5)
+		v->sh.idr_pic_id = ue("idr_pic_id");
+
+	v->sh.size_pic_order_cnt_lsb = 0;
+	v->sh.size_delta_pic_order_cnt_bottom = 0;
+	if(v->sps.pic_order_cnt_type == 0){
+		v->sh.size_pic_order_cnt_lsb = v->sps.log2_max_pic_order_cnt_lsb_minus4 + 4;
+		u(v->sh.size_pic_order_cnt_lsb, "pic_order_cnt_lsb");
+		if(v->pps.bottom_field_pic_order_in_frame_present_flag && !v->sh.field_pic_flag){
+			p = b->p;
+			se("delta_pic_order_cnt_bottom");
+			v->sh.size_delta_pic_order_cnt_bottom = b->p - p;
+		}
+	}
+
+	v->sh.size_delta_pic_order_cnt0 = 0;
+	v->sh.size_delta_pic_order_cnt1 = 0;
+	if(v->sps.pic_order_cnt_type == 1 && !v->sps.delta_pic_order_always_zero_flag){
+		p = b->p;
+		se("delta_pic_order_cnt[0]");
+		v->sh.size_delta_pic_order_cnt0 = b->p - p;
+		if(v->pps.bottom_field_pic_order_in_frame_present_flag && !v->sh.field_pic_flag){
+			p = b->p;
+			se("delta_pic_order_cnt[1]");
+			v->sh.size_delta_pic_order_cnt1 = b->p - p;
+		}
+	}
+	/* the rest isn't needed for VPU decoding */
+}
+
+static void
+slice_layer_without_partitioning_rbsp(VPUctx *v, Bits *b)
+{
+	Bprint(&stdout, "slice_layer_without_partitioning_rbsp()\n");
+
+	slice_header(v, b);
+	/* the rest isn't needed for VPU decoding */
+}
+
+static void
+sei_rbsp(VPUctx *v, Bits *b)
+{
+	int payloadType, payloadSize, i, n;
+
+	USED(v);
+	Bprint(&stdout, "sei_rbps()\n");
+	do{
+		Bprint(&stdout, "sei_message()\n");
+		payloadType = 0;
+		while(1){
+			Bprint(&stdout, "%lld\t", b->p);
+			n = u(8, nil);
+			payloadType += n;
+			if(n != 0xff)
+				break;
+			Bprint(&stdout, "ff_byte\n");
+		}
+		Bprint(&stdout, "last_payload_type_byte = %d\n", n);
+
+		payloadSize = 0;
+		while(1){
+			Bprint(&stdout, "%lld\t", b->p);
+			n = u(8, nil);
+			payloadSize += n;
+			if(n != 0xff)
+				break;
+			Bprint(&stdout, "ff_byte\n");
+		}
+		Bprint(&stdout, "last_payload_size_byte = %d\n", n);
+
+		Bprint(&stdout, "%llud\tsei_payload(%d, %d) = ...\n", b->p, payloadType, payloadSize);
+		for(i = 0; i < payloadSize; i++)
+			u(8, nil);
+		if(b->p & 7){
+			u(1, nil);
+			u(8 - (b->p & 7), nil);
+		}
+	}while(more_rbsp_data(b));
+	rbsp_trailing_bits(b);
+}
+
+static void
+seq_parameter_set_rbsp(VPUctx *v, Bits *b)
+{
+	int i, n;
+
+	Bprint(&stdout, "seq_parameter_set_rbsp()\n");
+	v->sps.profile_idc = u(8, "profile_idc");
+	v->sps.frame_mbs_only_flag = 0;
+	for(i = 0; i < 6; i++){
+		char t[32];
+		snprint(t, sizeof(t), "constraint_set%d_flag", i);
+		if(u(1, t) && i == 4 && (v->sps.profile_idc == 77 || v->sps.profile_idc == 88 || v->sps.profile_idc == 100))
+			v->sps.frame_mbs_only_flag = 1;
+	}
+	u(2, "reserved_zero_2bits");
+	u(8, "level_idc");
+	ue("seq_parameter_set_id");
+	switch(v->sps.profile_idc){
+	case 100: case 110:
+	case 122: case 244: case 44:
+	case 83: case 86: case 118:
+	case 128: case 138: case 139:
+	case 134: case 135:
+		if((v->sps.chroma_format_idc = ue("chroma_format_idc")) == 3)
+			v->sps.separate_colour_plane_flag = u(1, "separate_colour_plane_flag");
+		ue("bit_depth_luma_minus8");
+		ue("bit_depth_chroma_minus8");
+		u(1, "qpprime_y_zero_transform_bypass_flag");
+		if((v->sps.seq_scaling_matrix_present_flag = u(1, "seq_scaling_matrix_present_flag"))){
+			for(i = 0; i < (v->sps.chroma_format_idc != 3) ? 8 : 12; i++){
+				if(u(1, "seq_scaling_list_present_flag"))
+					scaling_list(v, b, i);
+			}
+		}
+		break;
+	}
+	v->sps.log2_max_frame_num_minus4 = ue("log2_max_frame_num_minus4");
+	if((v->sps.pic_order_cnt_type = ue("pic_order_cnt_type")) == 0){
+		v->sps.log2_max_pic_order_cnt_lsb_minus4 = ue("log2_max_pic_order_cnt_lsb_minus4");
+	}else if(v->sps.pic_order_cnt_type == 1){
+		v->sps.delta_pic_order_always_zero_flag = u(1, "delta_pic_order_always_zero_flag");
+		se("offset_for_non_ref_pic");
+		se("offset_for_top_to_bottom_field");
+		n = ue("num_ref_frames_in_pic_order_cnt_cycle");
+		for(i = 0; i < n; i++){
+			char t[32];
+			snprint(t, sizeof(t), "offset_for_ref_frame[i]");
+			se(t);
+		}
+	}
+	v->sps.max_num_ref_frames = ue("max_num_ref_frames");
+	u(1, "gaps_in_frame_num_value_allowed_flag");
+	v->sps.pic_width_in_mbs_minus1 = ue("pic_width_in_mbs_minus1");
+	v->sps.pic_height_in_map_units_minus1 = ue("pic_height_in_map_units_minus1");
+	v->PicSizeInMapUnits =
+		((int)v->sps.pic_width_in_mbs_minus1 + 1) *
+		((int)v->sps.pic_height_in_map_units_minus1 + 1);
+	if((v->sps.frame_mbs_only_flag |= u(1, "frame_mbs_only_flag")) == 0)
+		v->sps.mb_adaptive_frame_field_flag = u(1, "mb_adaptive_frame_field_flag");
+	v->sps.direct_8x8_inference_flag = u(1, "direct_8x8_inference_flag");
+	if(u(1, "frame_cropping_flag")){
+		v->crop.left = ue("frame_crop_left_offset");
+		v->crop.right = ue("frame_crop_right_offset");
+		v->crop.top = ue("frame_crop_top_offset");
+		v->crop.bottom = ue("frame_crop_bottom_offset");
+	}
+	if(u(1, "vui_parameters_present_flag"))
+		vui_parameters(v, b);
+	rbsp_trailing_bits(b);
+}
+
+static void
+pic_parameter_set_rbsp(VPUctx *v, Bits *b)
+{
+	int i, n, x;
+
+	Bprint(&stdout, "pic_parameter_set_rbsp()\n");
+
+	v->pps.pic_parameter_set_id = ue("pic_parameter_set_id");
+	ue("seq_parameter_set_id");
+	v->pps.entropy_coding_mode_flag = u(1, "entropy_coding_mode_flag");
+	v->pps.bottom_field_pic_order_in_frame_present_flag = u(1, "bottom_field_pic_order_in_frame_present_flag");
+	if((v->pps.num_slice_groups_minus1 = ue("num_slice_groups_minus1")) > 0){
+		if((v->pps.slice_group_map_type = ue("slice_group_map_type")) == 0){
+			for(i = 0; i <= v->pps.num_slice_groups_minus1; i++){
+				char t[32];
+				snprint(t, sizeof(t), "run_length_minus1[%d]", i);
+				ue(t);
+			}
+		}else if(v->pps.slice_group_map_type == 2){
+			for(i = 0; i <= v->pps.num_slice_groups_minus1; i++){
+				char t[32];
+				snprint(t, sizeof(t), "top_left[%d]", i);
+				ue(t);
+				snprint(t, sizeof(t), "bottom_right[%d]", i);
+				ue(t);
+			}
+		}else if(v->pps.slice_group_map_type >= 3 && v->pps.slice_group_map_type <= 5){
+			u(1, "slice_group_change_direction_flag");
+			ue("slice_group_change_rate_minus1");
+		}else if(v->pps.slice_group_map_type == 6){
+			n = ue("pic_size_in_map_units_minus1");
+			x = ceillog2(v->pps.num_slice_groups_minus1 + 1);
+			for(i = 0; i <= n; i++){
+				char t[32];
+				snprint(t, sizeof(t), "slice_group_id[%d]", i);
+				u(x, t);
+			}
+		}
+	}
+	v->pps.num_ref_idx_l0_default_active_minus1 = ue("num_ref_idx_l0_default_active_minus1");
+	v->pps.num_ref_idx_l1_default_active_minus1 = ue("num_ref_idx_l1_default_active_minus1");
+	v->pps.weighted_pred_flag = u(1, "weighted_pred_flag");
+	v->pps.weighted_bipred_idc = u(2, "weighted_bipred_idc");
+	v->pps.pic_init_qp_minus26 = se("pic_init_qp_minus26");
+	se("pic_init_qs_minus26");
+	v->pps.chroma_qp_index_offset = se("chroma_qp_index_offset");
+	v->pps.deblocking_filter_control_present_flag = u(1, "deblocking_filter_control_present_flag");
+	v->pps.constrained_intra_pred_flag = u(1, "constrained_intra_pred_flag");
+	v->pps.redundant_pic_cnt_present_flag = u(1, "redundant_pic_cnt_present_flag");
+	if(more_rbsp_data(b)){
+		v->pps.transform_8x8_mode_flag = u(1, "transform_8x8_mode_flag");
+		v->pps.pic_scaling_matrix_present_flag = u(1, "pic_scaling_matrix_present_flag");
+		if(v->pps.pic_scaling_matrix_present_flag){
+			for(i = 0; i < 6 + ((v->sps.chroma_format_idc != 3) ? 2 : 6)*v->pps.transform_8x8_mode_flag; i++){
+				char t[32];
+				snprint(t, sizeof(t), "pic_scaling_list_present_flag[%d]", i);
+				if(u(1, t))
+					scaling_list(v, b, i);
+			}
+		}
+		v->pps.second_chroma_qp_index_offset = se("second_chroma_qp_index_offset");
+	}
+	rbsp_trailing_bits(b);
+}
+
+static void (*rbsp_f[])(VPUctx *v, Bits *b) = {
+	[1] = slice_layer_without_partitioning_rbsp,
+	[5] = slice_layer_without_partitioning_rbsp,
+	[6] = sei_rbsp,
+	[7] = seq_parameter_set_rbsp,
+	[8] = pic_parameter_set_rbsp,
+	[19] = slice_layer_without_partitioning_rbsp,
+};
+
+static int
+Bu16le(Biobuf *b, u16int *o)
+{
+	int x;
+
+	x = Bgetc(b);
+	x |= Bgetc(b)<<8;
+	*o = x;
+	if(x < 0)
+		werrstr("failed to read 2 bytes");
+
+	return x < 0 ? -1 : 0;
+}
+
+static int
+Bu32le(Biobuf *b, u32int *o)
+{
+	int x, i;
+
+	*o = 0;
+	for(i = 0; i < 4; *o |= x<<(i*8), i++){
+		if((x = Bgetc(b)) < 0){
+			werrstr("failed to read 4 bytes");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+Bu64le(Biobuf *b, u64int *o)
+{
+	int x, i;
+
+	*o = 0;
+	for(i = 0; i < 8; *o |= (uvlong)x<<(i*8), i++){
+		if((x = Bgetc(b)) < 0){
+			werrstr("failed to read 8 bytes");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s [file.ivf]\n", argv0);
+	exits("usage");
+}
+
+static int
+decode(VPUctx *v, Bits *b, int sz)
+{
+	u32int x;
+
+	Bprint(&stdout, "@ 0x%llx [%d]\n", Boffset(b->b), sz);
+	if((x = u(24, nil)) == 0)
+		x = u(8, nil);
+	if(x != 1){
+		Bprint(&stdout, "invalid start code %02x\n", x);
+		return -1;
+	}
+
+	b->p = 0;
+	u(1, "forbidden_zero_bit");
+	v->nal_ref_idc = u(2, "nal_ref_idc");
+	v->nal_unit_type = u(5, "nal_unit_type");
+
+	if(v->nal_unit_type == 14 || v->nal_unit_type == 20){
+		if(u(1, "svc_extension_flag"))
+			unit_header_svc_extension(v, b);
+		else
+			unit_header_mvc_extension(v, b);
+	}
+
+	if(v->nal_unit_type >= nelem(rbsp_f) || rbsp_f[v->nal_unit_type] == nil)
+		Bprint(&stdout, "unexpected nal_unit_type %d", v->nal_unit_type);
+	else
+		rbsp_f[v->nal_unit_type](v, b);
+	Bprint(&stdout, "\n");
+
+	return 0;
+}
+
+void
+main(int argc, char **argv)
+{
+	u64int timestamp, framenum;
+	int fd, shgo;
+	u32int tbnum, tbdenum;
+	u16int w, h, hlen;
+	Biobuf in, settings;
+	char tmp[6], *s, *buf;
+	u32int sz;
+	VPUctx v;
+	Bits bits;
+	vlong off;
+
+	ARGBEGIN{
+	default:
+		usage();
+	}ARGEND
+
+	fd = 0;
+	if(argc == 1){
+		if((fd = open(*argv, OREAD)) < 0)
+			sysfatal("%r");
+	}else if(argc != 0)
+		usage();
+
+	Binit(&in, fd, OREAD);
+	Binit(&stdout, 2, OWRITE);
+	Binit(&settings, 1, OWRITE);
+	memset(&v, 0, sizeof(v));
+	memset(&bits, 0, sizeof(bits));
+	bits.b = &in;
+
+	if(Bread(&in, tmp, 6) != 6 || Bu16le(&in, &hlen) < 0)
+		sysfatal("header read failed");
+	if(memcmp(tmp, "DKIF", 4) != 0)
+		sysfatal("expected DKIF, got %02x%02x%02x%02x", tmp[0], tmp[1], tmp[2], tmp[3]);
+	if(hlen < 0x20 || Bread(&in, tmp, 4) != 4)
+		sysfatal("invalid header: hlen=%d", hlen);
+	if(Bu16le(&in, &w) < 0 || Bu16le(&in, &h) < 0 || Bu32le(&in, &tbdenum) < 0 || Bu32le(&in, &tbnum) < 0)
+		sysfatal("invalid header: %r");
+	if(Bseek(&in, hlen, 0) != hlen)
+		sysfatal("invalid IVF stream");
+
+	shgo = 0;
+	buf = malloc(1 * 1024 * 1024);
+	for(framenum = 0;; framenum++){
+		if(Bu32le(&in, &sz) < 0 || Bu64le(&in, &timestamp) < 0 || (int)sz < 0)
+			break;
+		off = Boffset(&in);
+		if(decode(&v, &bits, sz) != 0)
+			sysfatal("decode failed");
+
+		if(v.nal_unit_type == 8){ /* have sps+pps */
+			s = smprint(
+				"sps pic_height_in_map_units_minus1 %d\n"
+				"sps pic_width_in_mbs_minus1 %d\n"
+				"sps chroma_format_idc %d\n"
+				"sps frame_mbs_only_flag %d\n"
+				"sps log2_max_frame_num_minus4 %d\n"
+				"sps max_num_ref_frames %d\n"
+				"sps profile_idc %d\n"
+				"sps direct_8x8_inference_flag %d\n"
+				"sps mb_adaptive_frame_field_flag %d\n"
+				"sps seq_scaling_matrix_present_flag %d\n"
+				"sps separate_colour_plane_flag %d\n"
+				"pps chroma_qp_index_offset %d\n"
+				"pps pic_init_qp_minus26 %d\n"
+				"pps second_chroma_qp_index_offset %d\n"
+				"pps weighted_bipred_idc %d\n"
+				"pps num_ref_idx_l0_default_active_minus1 %d\n"
+				"pps num_ref_idx_l1_default_active_minus1 %d\n"
+				"pps constrained_intra_pred_flag %d\n"
+				"pps deblocking_filter_control_present_flag %d\n"
+				"pps entropy_coding_mode_flag %d\n"
+				"pps pic_scaling_matrix_present_flag %d\n"
+				"pps redundant_pic_cnt_present_flag %d\n"
+				"pps transform_8x8_mode_flag %d\n"
+				"pps weighted_pred_flag %d\n"
+				"pps pic_parameter_set_id %d\n",
+				v.sps.pic_height_in_map_units_minus1,
+				v.sps.pic_width_in_mbs_minus1,
+				v.sps.chroma_format_idc,
+				v.sps.frame_mbs_only_flag,
+				v.sps.log2_max_frame_num_minus4,
+				v.sps.max_num_ref_frames,
+				v.sps.profile_idc,
+				v.sps.direct_8x8_inference_flag,
+				v.sps.mb_adaptive_frame_field_flag,
+				v.sps.seq_scaling_matrix_present_flag,
+				v.sps.separate_colour_plane_flag,
+				v.pps.chroma_qp_index_offset,
+				v.pps.pic_init_qp_minus26,
+				v.pps.second_chroma_qp_index_offset,
+				v.pps.weighted_bipred_idc,
+				v.pps.num_ref_idx_l0_default_active_minus1,
+				v.pps.num_ref_idx_l1_default_active_minus1,
+				v.pps.constrained_intra_pred_flag,
+				v.pps.deblocking_filter_control_present_flag,
+				v.pps.entropy_coding_mode_flag,
+				v.pps.pic_scaling_matrix_present_flag,
+				v.pps.redundant_pic_cnt_present_flag,
+				v.pps.transform_8x8_mode_flag,
+				v.pps.weighted_pred_flag,
+				v.pps.pic_parameter_set_id
+			);
+			pwrite(1, s, strlen(s), 0);
+			free(s);
+			shgo = 1;
+		}else if(shgo){
+			s = smprint(
+				"nal_ref_idc %d\n"
+				"nal_unit_type %d\n"
+				"sh frame_num %d\n"
+				"sh idr_pic_id %d\n"
+				"sh size_dec_ref_pic_marking %d\n"
+				"sh size_pic_order_cnt_lsb %d\n"
+				"sh size_delta_pic_order_cnt_bottom %d\n"
+				"sh size_delta_pic_order_cnt0 %d\n"
+				"sh size_delta_pic_order_cnt1 %d\n"
+				"sh field_pic_flag %d\n"
+				"sh bottom_field_flag %d\n",
+				v.nal_ref_idc,
+				v.nal_unit_type,
+				v.sh.frame_num,
+				v.sh.idr_pic_id,
+				v.sh.size_dec_ref_pic_marking,
+				v.sh.size_pic_order_cnt_lsb,
+				v.sh.size_delta_pic_order_cnt_bottom,
+				v.sh.size_delta_pic_order_cnt0,
+				v.sh.size_delta_pic_order_cnt1,
+				v.sh.field_pic_flag,
+				v.sh.bottom_field_flag
+			);
+			pwrite(1, s, strlen(s), 0);
+			Bseek(&in, off, 0);
+			Bread(&in, buf, sz);
+			pwrite(1, buf, sz, -1);
+		}
+
+		Bseek(&in, off+sz, 0);
+		bits.nb = 0;
+	}
+
+	Bterm(&in);
+	Bterm(&stdout);
+
+	exits(nil);
+}
--- a/mkfile
+++ b/mkfile
@@ -1,6 +1,6 @@
 </$objtype/mkfile
 
-TARG=h264 hantest cabac
+TARG=h264 hantest cabac ivfh264
 BIN=/$objtype/bin
 
 default:V: all