diff --git a/client/Makefile b/client/Makefile index a6c79151..03e77ed4 100644 --- a/client/Makefile +++ b/client/Makefile @@ -17,6 +17,7 @@ CFLAGS += -DBUILD_VERSION='"$(shell git describe --always --long --dirty --abbr OBJS = main.o \ lg-renderer.o \ spice/spice.o \ + parsers/nal.o \ decoders/null.o \ decoders/h264.o \ renderers/opengl.o diff --git a/client/decoders/h264.c b/client/decoders/h264.c index 8cf52417..6edbcdd9 100644 --- a/client/decoders/h264.c +++ b/client/decoders/h264.c @@ -21,6 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include "debug.h" #include "memcpySSE.h" +#include "parsers/nal.h" #include #include @@ -29,7 +30,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA #define SURFACE_NUM 3 -#define NALU_AUD 9 #define SLICE_TYPE_P 0 #define SLICE_TYPE_B 1 #define SLICE_TYPE_I 2 @@ -55,6 +55,8 @@ struct Inst VABufferID datBufferID[SURFACE_NUM]; bool t2First; int sliceType; + + NAL nal; }; static const unsigned char MatrixBufferH264[] = { @@ -143,6 +145,13 @@ static bool lgd_h264_create(void ** opaque) this->sliBufferID[i] = this->datBufferID[i] = VA_INVALID_ID; + if (!nal_initialize(&this->nal)) + { + DEBUG_INFO("Failed to initialize NAL parser"); + free(this); + return false; + } + lgd_h264_deinitialize(this); return true; } @@ -450,13 +459,22 @@ static bool setup_pic_buffer(struct Inst * this) return false; } + const NAL_SPS * sps; + if (!nal_get_sps(this->nal, &sps)) + { + DEBUG_ERROR("nal_get_sps"); + return false; + } + memset(p, 0, sizeof(VAPictureParameterBufferH264)); - p->picture_width_in_mbs_minus1 = (this->format.width + 15) / 16; - p->picture_width_in_mbs_minus1 = (this->format.height + 15) / 16; - p->num_ref_frames = 1; - p->seq_fields.value = 145; - p->pic_fields.value = 0x501; - p->frame_num = this->frameNum % 16; + p->picture_width_in_mbs_minus1 = sps->pic_width_in_mbs_minus1; + p->picture_height_in_mbs_minus1 = sps->pic_height_in_map_units_minus1; + p->bit_depth_luma_minus8 = sps->bit_depth_luma_minus8; + p->bit_depth_chroma_minus8 = sps->bit_depth_chroma_minus8; + p->num_ref_frames = sps->num_ref_frames; + p->seq_fields.value = 145; + p->pic_fields.value = 0x501; + p->frame_num = this->frameNum % 16; for(int i = 0; i < 16; ++i) { p->ReferenceFrames[i].flags = VA_PICTURE_H264_INVALID; @@ -592,55 +610,33 @@ static bool setup_dat_buffer(struct Inst * this, const uint8_t * src, size_t src return true; } -static bool parse_nalu(struct Inst * this, const uint8_t * src, size_t size) -{ - static const uint8_t startCode[4] = {0x00, 0x00, 0x00, 0x01}; - - if (memcmp(src, startCode, sizeof(startCode)) != 0) - { - DEBUG_ERROR("Missing start code"); - return false; - } - src += 4; - - if (*src & 0x80) - { - DEBUG_ERROR("forbidden_zero_bit is set"); - return false; - } - -// uint8_t nal_ref_idc = (*src & 0x60) >> 5; - uint8_t nal_ref_unit_type = (*src & 0x1F); - ++src; - - if (nal_ref_unit_type == NALU_AUD) - { - static const int pic_type_to_slice_type[3] = - { - SLICE_TYPE_I, - SLICE_TYPE_P, - SLICE_TYPE_B - }; - - const uint8_t primary_pic_type = (*src & 0xE0) >> 5; - this->sliceType = pic_type_to_slice_type[primary_pic_type]; - return true; - } - - return false; -} - static bool lgd_h264_decode(void * opaque, const uint8_t * src, size_t srcSize) { VAStatus status; struct Inst * this = (struct Inst *)opaque; - if (!parse_nalu(this, src, srcSize)) + if (!nal_parse(this->nal, src, srcSize)) { DEBUG_ERROR("Failed to parse required information"); return false; } + uint8_t pic_type; + if (!nal_get_primary_picture_type(this->nal, &pic_type)) + { + DEBUG_ERROR("Missing primary picture type"); + return false; + } + + static const int pic_type_to_slice_type[3] = + { + SLICE_TYPE_I, + SLICE_TYPE_P, + SLICE_TYPE_B + }; + + this->sliceType = pic_type_to_slice_type[pic_type]; + // don't start until we have an I-FRAME if (this->frameNum == 0 && this->sliceType != SLICE_TYPE_I) return true; diff --git a/client/parsers/nal.c b/client/parsers/nal.c new file mode 100644 index 00000000..cb968848 --- /dev/null +++ b/client/parsers/nal.c @@ -0,0 +1,453 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +https://looking-glass.hostfission.com + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "nal.h" + +#include "debug.h" +#include "utils.h" + +#include +#include + +#define DEBUG_NAL + +struct NAL +{ + uint8_t primary_pic_type; + bool primary_pic_type_valid; + + bool sps_valid; + NAL_SPS sps; + int32_t * sps_offset_for_ref_frame; + uint32_t sps_num_ref_frames_in_pic_order_cnt_cycle; + + NAL_VUI vui; +}; + +bool nal_initialize(NAL * ptr) +{ + *ptr = (NAL)malloc(sizeof(struct NAL)); + memset(*ptr, 0, sizeof(struct NAL)); + return true; +} + +void nal_deinitialize(NAL this) +{ + if (this->sps_offset_for_ref_frame) + { + free(this->sps_offset_for_ref_frame); + this->sps_offset_for_ref_frame = NULL; + } + + if (this->vui.nal_hrd_parameters.cpb) + { + free(this->vui.nal_hrd_parameters.cpb); + this->vui.nal_hrd_parameters.cpb = NULL; + } + + if (this->vui.vcl_hrd_parameters.cpb) + { + free(this->vui.vcl_hrd_parameters.cpb); + this->vui.vcl_hrd_parameters.cpb = NULL; + } + + free(this); +} + +static bool parse_nal_hrd(NAL_HRD * const hrd, const uint8_t * src, size_t size, size_t * const offset) +{ + hrd->cpb_cnt_minus1 = decode_u_golomb(src, offset); + hrd->bit_rate_scale = get_bits(src, offset, 4); + hrd->cpb_size_scale = get_bits(src, offset, 4); + + if (hrd->cpb_size_count < hrd->cpb_size_scale) + { + hrd->cpb = realloc(hrd->cpb, hrd->cpb_size_scale * sizeof(NAL_CPB)); + hrd->cpb_size_count = hrd->cpb_size_scale; + } + + for(uint32_t i = 0; i < hrd->cpb_size_scale; ++i) + { + hrd->cpb[i].bit_rate_value_minus1 = decode_u_golomb(src, offset); + hrd->cpb[i].cpb_size_value_minus1 = decode_u_golomb(src, offset); + hrd->cpb[i].cbr_flag = get_bit(src, offset); + } + + hrd->initial_cpb_removal_delay_length_minus1 = get_bits(src, offset, 5); + hrd->cpb_removal_delay_length_minus1 = get_bits(src, offset, 5); + hrd->dpb_output_delay_length_minus1 = get_bits(src, offset, 5); + hrd->time_offset_length = get_bits(src, offset, 5); + + return true; +} + +static bool parse_nal_vui(NAL_VUI * const vui, const uint8_t * src, size_t size, size_t * const offset) +{ + NAL_CPB * nal_hrd_cpb = vui->nal_hrd_parameters.cpb; + uint8_t nal_hrd_count = vui->nal_hrd_parameters.cpb_size_count; + NAL_CPB * vcl_hrd_cpb = vui->vcl_hrd_parameters.cpb; + uint8_t vcl_hrd_count = vui->vcl_hrd_parameters.cpb_size_count; + memset(vui, 0, sizeof(NAL_VUI)); + vui->nal_hrd_parameters.cpb = nal_hrd_cpb; + vui->nal_hrd_parameters.cpb_size_count = nal_hrd_count; + vui->vcl_hrd_parameters.cpb = vcl_hrd_cpb; + vui->vcl_hrd_parameters.cpb_size_count = vcl_hrd_count; + + vui->aspect_ratio_info_present_flag = get_bit(src, offset); + if (vui->aspect_ratio_info_present_flag) + { + vui->aspect_ratio_idc = get_bits(src, offset, 8); + if (vui->aspect_ratio_idc == IDC_VUI_ASPECT_RATIO_EXTENDED_SAR) + { + vui->sar_width = get_bits(src, offset, 16); + vui->sar_height = get_bits(src, offset, 16); + } + } + + vui->overscan_info_present_flag = get_bit(src, offset); + if (vui->overscan_info_present_flag) + vui->overscan_appropriate_flag = get_bit(src, offset); + + vui->video_signal_type_present_flag = get_bit(src, offset); + if (vui->video_signal_type_present_flag) + { + vui->video_format = get_bits(src, offset, 3); + vui->video_full_range_flag = get_bit(src, offset); + vui->colour_description_present_flag = get_bit(src, offset); + if (vui->colour_description_present_flag) + { + vui->colour_primaries = get_bits(src, offset, 8); + vui->transfer_characteristics = get_bits(src, offset, 8); + vui->matrix_coefficients = get_bits(src, offset, 8); + } + } + + vui->chroma_loc_info_present_flag = get_bit(src, offset); + if (vui->chroma_loc_info_present_flag) + { + vui->chroma_sample_loc_type_top_field = decode_u_golomb(src, offset); + vui->chroma_sample_loc_type_bottom_field = decode_u_golomb(src, offset); + } + + vui->timing_info_present_flag = get_bit(src, offset); + if (vui->timing_info_present_flag) + { + vui->num_units_in_tick = get_bits(src, offset, 32); + vui->time_scale = get_bits(src, offset, 32); + vui->fixed_frame_rate_flag = get_bit(src, offset); + } + + vui->nal_hrd_parameters_present_flag = get_bit(src, offset); + if (vui->nal_hrd_parameters_present_flag) + if (!parse_nal_hrd(&vui->nal_hrd_parameters, src, size, offset)) + return false; + + vui->vcl_hrd_parameters_present_flag = get_bit(src, offset); + if (vui->vcl_hrd_parameters_present_flag) + if (!parse_nal_hrd(&vui->vcl_hrd_parameters, src, size, offset)) + return false; + + if (vui->nal_hrd_parameters_present_flag || vui->vcl_hrd_parameters_present_flag) + vui->low_delay_hrd_flag = get_bit(src, offset); + + vui->pic_struct_present_flag = get_bit(src, offset); + vui->bitstream_restriction_flag = get_bit(src, offset); + if (vui->bitstream_restriction_flag) + { + vui->motion_vectors_over_pic_boundaries_flag = get_bit(src, offset); + vui->max_bytes_per_pic_denom = decode_u_golomb(src, offset); + vui->max_bits_per_mb_denom = decode_u_golomb(src, offset); + vui->log2_max_mv_length_horizontal = decode_u_golomb(src, offset); + vui->log2_max_mv_length_vertical = decode_u_golomb(src, offset); + vui->num_reorder_frames = decode_u_golomb(src, offset); + vui->max_dec_frame_buffering = decode_u_golomb(src, offset); + } + + return true; +} + +static bool parse_nal_trailing_bits(NAL this, const uint8_t * src, size_t size, size_t * const offset) +{ + if (!get_bit(src, offset)) + { + DEBUG_ERROR("Missing stop bit"); + return false; + } + + // byte align + *offset = (*offset + 0x7) & ~0x7; + return true; +} + +static bool parse_nal_sps(NAL this, const uint8_t * src, size_t size, size_t * const offset) +{ + this->sps_valid = false; + memset(&this->sps, 0, sizeof(this->sps)); + + this->sps.profile_idc = get_bits(src, offset, 8); + if ((this->sps.profile_idc != IDC_PROFILE_BASELINE) && + (this->sps.profile_idc != IDC_PROFILE_MAIN ) && + (this->sps.profile_idc != IDC_PROFILE_EXTENDED) && + (this->sps.profile_idc != IDC_PROFILE_HP ) && + (this->sps.profile_idc != IDC_PROFILE_Hi10P ) && + (this->sps.profile_idc != IDC_PROFILE_Hi422 ) && + (this->sps.profile_idc != IDC_PROFILE_Hi444 ) && + (this->sps.profile_idc != IDC_PROFILE_CAVLC444)) + { + DEBUG_ERROR("Invalid profile IDC (%d) encountered", this->sps.profile_idc); + return false; + } + + this->sps.constraint_set_flags[0] = get_bit(src, offset); + this->sps.constraint_set_flags[1] = get_bit(src, offset); + this->sps.constraint_set_flags[2] = get_bit(src, offset); + *offset += 5; + + this->sps.level_idc = get_bits(src, offset, 8); + this->sps.seq_parameter_set_id = decode_u_golomb(src, offset); + + if ((this->sps.profile_idc == IDC_PROFILE_HP ) || + (this->sps.profile_idc == IDC_PROFILE_Hi10P ) || + (this->sps.profile_idc == IDC_PROFILE_Hi422 ) || + (this->sps.profile_idc == IDC_PROFILE_Hi444 ) || + (this->sps.profile_idc == IDC_PROFILE_CAVLC444)) + { + this->sps.chroma_format_idc = decode_u_golomb(src, offset); + if (this->sps.chroma_format_idc == IDC_CHROMA_FORMAT_YUV444) + this->sps.seperate_colour_plane_flag = get_bit(src, offset); + + this->sps.bit_depth_luma_minus8 = decode_u_golomb(src, offset); + this->sps.bit_depth_chroma_minus8 = decode_u_golomb(src, offset); + this->sps.lossless_qpprime_y_zero_flag = get_bit(src, offset); + this->sps.seq_scaling_matrix_present_flag = get_bit(src, offset); + + if (this->sps.seq_scaling_matrix_present_flag) + { + const int cnt = this->sps.chroma_format_idc == IDC_CHROMA_FORMAT_YUV444 ? 12 : 8; + for(int i = 0; i < cnt; ++i) + this->sps.seq_scaling_list_present_flag[i] = get_bit(src, offset); + } + } + else + this->sps.chroma_format_idc = IDC_CHROMA_FORMAT_YUV420; + + this->sps.log2_max_frame_num_minus4 = decode_u_golomb(src, offset); + this->sps.pic_order_cnt_type = decode_u_golomb(src, offset); + + if (this->sps.pic_order_cnt_type == 0) + this->sps.log2_max_pic_order_cnt_lsb_minus4 = decode_u_golomb(src, offset); + else + { + if (this->sps.pic_order_cnt_type == 1) + { + this->sps.delta_pic_order_always_zero_flag = get_bit(src, offset); + this->sps.offset_for_non_ref_pic = decode_s_golomb(src, offset); + this->sps.offset_for_top_to_bottom_field = decode_s_golomb(src, offset); + + this->sps.num_ref_frames_in_pic_order_cnt_cycle = decode_u_golomb(src, offset); + if (this->sps.num_ref_frames_in_pic_order_cnt_cycle > this->sps_num_ref_frames_in_pic_order_cnt_cycle) + { + this->sps_offset_for_ref_frame = realloc( + this->sps_offset_for_ref_frame, + this->sps.num_ref_frames_in_pic_order_cnt_cycle * sizeof(int32_t) + ); + this->sps_num_ref_frames_in_pic_order_cnt_cycle = this->sps.num_ref_frames_in_pic_order_cnt_cycle; + } + + this->sps.offset_for_ref_frame = this->sps_offset_for_ref_frame; + for(uint32_t i = 0; i < this->sps.num_ref_frames_in_pic_order_cnt_cycle; ++i) + this->sps.offset_for_ref_frame[i] = decode_s_golomb(src, offset); + } + } + + this->sps.num_ref_frames = decode_u_golomb(src, offset); + this->sps.gaps_in_frame_num_value_allowed_flag = get_bit(src, offset); + this->sps.pic_width_in_mbs_minus1 = decode_u_golomb(src, offset); + this->sps.pic_height_in_map_units_minus1 = decode_u_golomb(src, offset); + this->sps.frame_mbs_only_flag = get_bit(src, offset); + + if (!this->sps.frame_mbs_only_flag) + this->sps.mb_adaptive_frame_field_flag = get_bit(src, offset); + + this->sps.direct_8x8_inference_flag = get_bit(src, offset); + this->sps.frame_cropping_flag = get_bit(src, offset); + + if (this->sps.frame_cropping_flag) + { + this->sps.frame_crop_left_offset = decode_u_golomb(src, offset); + this->sps.frame_crop_right_offset = decode_u_golomb(src, offset); + this->sps.frame_crop_top_offset = decode_u_golomb(src, offset); + this->sps.frame_crop_bottom_offset = decode_u_golomb(src, offset); + } + + this->sps.vui_parameters_present_flag = get_bit(src, offset); + +#ifdef DEBUG_NAL + DEBUG_INFO("SPS\n" + "profile_idc : %u\n" + "constraint_set_flags : %u %u %u\n" + "level_idc : %u\n" + "sec_parameter_set_id : %u\n" + "chroma_format_idc : %u\n" + "seperate_colour_plane_flag : %u\n" + "bit_depth_luma_minus8 : %u\n" + "bit_depth_chroma_minus8 : %u\n" + "lossless_qpprime_y_zero_flag : %u\n" + "seq_scaling_matrix_present_flag : %u\n" + "log2_max_frame_num_minus4 : %u\n" + "pic_order_cnt_type : %u\n" + "log2_max_pic_order_cnt_lsb_minus4 : %u\n" + "delta_pic_order_always_zero_flag : %u\n" + "offset_for_non_ref_pic : %d\n" + "offset_for_top_to_bottom_field : %d\n" + "num_ref_frames_in_pic_order_cnt_cycle: %u\n" + "num_ref_frames : %u\n" + "gaps_in_frame_num_value_allowed_flag : %u\n" + "pic_width_in_mbs_minus1 : %3u (%u)\n" + "pic_height_in_map_units_minus1 : %3u (%u)\n" + "frame_mbs_only_flag : %u\n" + "mb_adaptive_frame_field_flag : %u\n" + "direct_8x8_inference_flag : %u\n" + "frame_cropping_flag : %u\n" + "frame_crop_left_offset : %u\n" + "frame_crop_right_offset : %u\n" + "frame_crop_top_offset : %u\n" + "frame_crop_bottom_offset : %u\n" + "vui_parameters_present_flag : %u", + this->sps.profile_idc, + this->sps.constraint_set_flags[0], + this->sps.constraint_set_flags[1], + this->sps.constraint_set_flags[2], + this->sps.level_idc, + this->sps.seq_parameter_set_id, + this->sps.chroma_format_idc, + this->sps.seperate_colour_plane_flag, + this->sps.bit_depth_luma_minus8, + this->sps.bit_depth_chroma_minus8, + this->sps.lossless_qpprime_y_zero_flag, + this->sps.seq_scaling_matrix_present_flag, + this->sps.log2_max_frame_num_minus4, + this->sps.pic_order_cnt_type, + this->sps.log2_max_pic_order_cnt_lsb_minus4, + this->sps.delta_pic_order_always_zero_flag, + this->sps.offset_for_non_ref_pic, + this->sps.offset_for_top_to_bottom_field, + this->sps.num_ref_frames_in_pic_order_cnt_cycle, + this->sps.num_ref_frames, + this->sps.gaps_in_frame_num_value_allowed_flag, + this->sps.pic_width_in_mbs_minus1 , (this->sps.pic_width_in_mbs_minus1 + 1) * 16, + this->sps.pic_height_in_map_units_minus1, (this->sps.pic_height_in_map_units_minus1 + 1) * 16, + this->sps.frame_mbs_only_flag, + this->sps.mb_adaptive_frame_field_flag, + this->sps.direct_8x8_inference_flag, + this->sps.frame_cropping_flag, + this->sps.frame_crop_left_offset, + this->sps.frame_crop_right_offset, + this->sps.frame_crop_top_offset, + this->sps.frame_crop_bottom_offset, + this->sps.vui_parameters_present_flag + ); +#endif + + if (this->sps.vui_parameters_present_flag) + if (!parse_nal_vui(&this->vui, src, size, offset)) + return false; + + if (!parse_nal_trailing_bits(this, src, size, offset)) + return false; + + this->sps_valid = true; + return true; +} + +bool nal_parse(NAL this, const uint8_t * src, size_t size) +{ + static FILE * fd = NULL; + if (!fd) + fd = fopen("/tmp/stream.h264", "w"); + fwrite(src, size, 1, fd); + + const size_t bits = size << 4; + size_t offset = 0; + while(offset < bits) + { + // look for the start header + if (get_bits(src, &offset, 32) != 1) + { + offset -= 24; + continue; + } + + // ensure the forbidden zero bit is not set + if (get_bit(src, &offset) != 0) + { + DEBUG_ERROR("forbidden_zero_bit is set"); + return false; + } + + uint8_t nal_ref_idc = get_bits(src, &offset, 2); + uint8_t nal_ref_unit_type = get_bits(src, &offset, 5); + DEBUG_INFO("ref idc: %d, ref unit type: %d", nal_ref_idc, nal_ref_unit_type); + + switch(nal_ref_unit_type) + { + case NAL_TYPE_AUD: + { + this->primary_pic_type = get_bits(src, &offset, 3); + this->primary_pic_type_valid = true; + if (!parse_nal_trailing_bits(this, src, size, &offset)) + return false; + break; + } + + case NAL_TYPE_SPS: + if (!parse_nal_sps(this, src, size, &offset)) + return false; + break; + + case NAL_TYPE_PPS: + break; + + default: + DEBUG_ERROR("Unknown NAL ref unit type: %d", nal_ref_unit_type); + return false; + } + + // byte align + offset = (offset + 0x7) & ~0x7; + } + + return true; +} + +bool nal_get_sps(NAL this, const NAL_SPS ** sps) +{ + if (!this->sps_valid) + return false; + *sps = &this->sps; + return true; +} + +bool nal_get_primary_picture_type(NAL this, uint8_t * pic_type) +{ + if (!this->primary_pic_type_valid) + return false; + *pic_type = this->primary_pic_type; + return true; +} \ No newline at end of file diff --git a/client/parsers/nal.h b/client/parsers/nal.h new file mode 100644 index 00000000..b50c4316 --- /dev/null +++ b/client/parsers/nal.h @@ -0,0 +1,153 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +https://looking-glass.hostfission.com + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include + +#define NAL_TYPE_SPS 7 +#define NAL_TYPE_PPS 8 +#define NAL_TYPE_AUD 9 + +#define IDC_PROFILE_BASELINE 66 +#define IDC_PROFILE_MAIN 77 +#define IDC_PROFILE_EXTENDED 88 +#define IDC_PROFILE_HP 100 +#define IDC_PROFILE_Hi10P 110 +#define IDC_PROFILE_Hi422 122 +#define IDC_PROFILE_Hi444 244 +#define IDC_PROFILE_CAVLC444 44 + +#define IDC_CHROMA_FORMAT_YUV400 0 +#define IDC_CHROMA_FORMAT_YUV420 1 +#define IDC_CHROMA_FORMAT_YVU422 2 +#define IDC_CHROMA_FORMAT_YUV444 3 + +#define IDC_VUI_ASPECT_RATIO_EXTENDED_SAR 0xFF + +#define NAL_PICTURE_TYPE_I 0 +#define NAL_PICTURE_TYPE_P 1 +#define NAL_PICTURE_TYPE_B 2 + +typedef struct NAL_SPS +{ + uint8_t profile_idc; + uint8_t constraint_set_flags[3]; + uint8_t level_idc; + uint32_t seq_parameter_set_id; + uint32_t chroma_format_idc; + uint8_t seperate_colour_plane_flag; + uint32_t bit_depth_luma_minus8; + uint32_t bit_depth_chroma_minus8; + uint8_t lossless_qpprime_y_zero_flag; + uint8_t seq_scaling_matrix_present_flag; + uint8_t seq_scaling_list_present_flag[12]; + uint32_t log2_max_frame_num_minus4; + uint32_t pic_order_cnt_type; + uint32_t log2_max_pic_order_cnt_lsb_minus4; + uint8_t delta_pic_order_always_zero_flag; + int32_t offset_for_non_ref_pic; + int32_t offset_for_top_to_bottom_field; + uint32_t num_ref_frames_in_pic_order_cnt_cycle; + int32_t * offset_for_ref_frame; + uint32_t num_ref_frames; + uint8_t gaps_in_frame_num_value_allowed_flag; + uint32_t pic_width_in_mbs_minus1; + uint32_t pic_height_in_map_units_minus1; + uint8_t frame_mbs_only_flag; + uint8_t mb_adaptive_frame_field_flag; + uint8_t direct_8x8_inference_flag; + uint8_t frame_cropping_flag; + uint32_t frame_crop_left_offset; + uint32_t frame_crop_right_offset; + uint32_t frame_crop_top_offset; + uint32_t frame_crop_bottom_offset; + uint8_t vui_parameters_present_flag; +} +NAL_SPS; + +typedef struct NAL_CPB +{ + uint32_t bit_rate_value_minus1; + uint32_t cpb_size_value_minus1; + uint8_t cbr_flag; +} +NAL_CPB; + +typedef struct NAL_HRD +{ + uint32_t cpb_cnt_minus1; + uint8_t bit_rate_scale; + uint8_t cpb_size_scale; + uint8_t cpb_size_count; + NAL_CPB * cpb; + uint8_t initial_cpb_removal_delay_length_minus1; + uint8_t cpb_removal_delay_length_minus1; + uint8_t dpb_output_delay_length_minus1; + uint8_t time_offset_length; +} +NAL_HRD; + +typedef struct NAL_VUI +{ + uint8_t aspect_ratio_info_present_flag; + uint8_t aspect_ratio_idc; + uint16_t sar_width; + uint16_t sar_height; + uint8_t overscan_info_present_flag; + uint8_t overscan_appropriate_flag; + uint8_t video_signal_type_present_flag; + uint8_t video_format; + uint8_t video_full_range_flag; + uint8_t colour_description_present_flag; + uint8_t colour_primaries; + uint8_t transfer_characteristics; + uint8_t matrix_coefficients; + uint8_t chroma_loc_info_present_flag; + uint32_t chroma_sample_loc_type_top_field; + uint32_t chroma_sample_loc_type_bottom_field; + uint8_t timing_info_present_flag; + uint32_t num_units_in_tick; + uint32_t time_scale; + uint8_t fixed_frame_rate_flag; + uint8_t nal_hrd_parameters_present_flag; + NAL_HRD nal_hrd_parameters; + uint8_t vcl_hrd_parameters_present_flag; + NAL_HRD vcl_hrd_parameters; + uint8_t low_delay_hrd_flag; + uint8_t pic_struct_present_flag; + uint8_t bitstream_restriction_flag; + uint8_t motion_vectors_over_pic_boundaries_flag; + uint32_t max_bytes_per_pic_denom; + uint32_t max_bits_per_mb_denom; + uint32_t log2_max_mv_length_horizontal; + uint32_t log2_max_mv_length_vertical; + uint32_t num_reorder_frames; + uint32_t max_dec_frame_buffering; +} +NAL_VUI; + +typedef struct NAL * NAL; + +bool nal_initialize (NAL * ptr); +void nal_deinitialize(NAL this ); +bool nal_parse (NAL this, const uint8_t * src, size_t size); + +bool nal_get_sps (NAL this, const NAL_SPS ** sps); +bool nal_get_primary_picture_type(NAL this, uint8_t * pic_type); \ No newline at end of file diff --git a/client/utils.h b/client/utils.h index 618dffc6..1884bbc6 100644 --- a/client/utils.h +++ b/client/utils.h @@ -60,4 +60,34 @@ static inline void nsleep(uint64_t ns) #define LG_LOCK(x) SDL_LockMutex(x) #define LG_UNLOCK(x) SDL_UnlockMutex(x) #define LG_LOCK_FREE(x) SDL_DestroyMutex(x) -#endif \ No newline at end of file +#endif + +static inline uint32_t get_bit(const uint8_t * const base, size_t * const offset) +{ + uint32_t out = ((*(base + (*offset >> 0x3))) >> (0x7 - (*offset & 0x7))) & 0x1; + ++*offset; + return out; +} + +static inline uint32_t get_bits(const uint8_t * const base, size_t * const offset, const uint8_t bits) +{ + uint32_t value = 0; + for (int i = 0; i < bits; ++i) + value |= (get_bit(base, offset) ? 1 : 0) << (bits - i - 1); + return value; +} + +static inline uint32_t decode_u_golomb(const uint8_t * const base, size_t * const offset) +{ + uint32_t i = 0; + while(get_bit(base, offset) == 0) + ++i; + + return ((1 << i) - 1 + get_bits(base, offset, i)); +} + +static inline int32_t decode_s_golomb(const uint8_t * const base, size_t * const offset) +{ + const uint32_t g = decode_u_golomb(base, offset); + return (g & 0x1) ? (g + 1) / 2 : -(g / 2); +} \ No newline at end of file