shithub: hantro9

ref: a15e8798f07b778144f60562643e8c826a7e77b3
dir: /ivfh264.c/

View raw version
#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, vpu;
	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

	vpu = open("/dev/vpu", ORDWR);
	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(
				"decode h264\n"
				"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
			);
			if(pwrite(vpu, s, strlen(s), 0) < 0)
				sysfatal("%r");
			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
			);
			if(pwrite(vpu, s, strlen(s), 0) < 0)
				sysfatal("%r");
			Bseek(&in, off, 0);
			Bread(&in, buf, sz);
			pwrite(vpu, buf, sz, -1);
			pread(vpu, buf, 0, 0);
		}

		Bseek(&in, off+sz, 0);
		bits.nb = 0;
	}

	Bterm(&in);
	Bterm(&stdout);

	exits(nil);
}