diff --git a/.gitignore b/.gitignore index 5504d1ab1..df77b5f69 100644 --- a/.gitignore +++ b/.gitignore @@ -70,6 +70,7 @@ /src/tools/diracparser /src/tools/ebml_validator /src/tools/hevc_dump +/src/tools/hevcc_dump /src/tools/mpls_dump /src/tools/vc1parser /tests/data diff --git a/Rakefile b/Rakefile index 25924bc70..9c8f898b2 100644 --- a/Rakefile +++ b/Rakefile @@ -65,7 +65,7 @@ def setup_globals $programs = %w{mkvmerge mkvinfo mkvextract mkvpropedit} $programs << "mkvinfo-gui" if $build_mkvinfo_gui $programs << "mkvtoolnix-gui" if $build_mkvtoolnix_gui - $tools = %w{ac3parser base64tool checksum diracparser ebml_validator hevc_dump mpls_dump vc1parser} + $tools = %w{ac3parser base64tool checksum diracparser ebml_validator hevc_dump hevcc_dump mpls_dump vc1parser} $application_subdirs = { "mkvtoolnix-gui" => "mkvtoolnix-gui/" } $applications = $programs.collect { |name| "src/#{$application_subdirs[name]}#{name}" + c(:EXEEXT) } @@ -1071,6 +1071,16 @@ Application.new("src/tools/hevc_dump"). libraries($common_libs). create +# +# tools: hevcs_dump +# +Application.new("src/tools/hevcc_dump"). + description("Build the hevcc_dump executable"). + aliases("tools:hevcc_dump"). + sources("src/tools/hevcc_dump.cpp"). + libraries($common_libs). + create + # # tools: mpls_dump # diff --git a/src/tools/hevcc_dump.cpp b/src/tools/hevcc_dump.cpp new file mode 100644 index 000000000..a7be7f3b6 --- /dev/null +++ b/src/tools/hevcc_dump.cpp @@ -0,0 +1,681 @@ +/* + hevcc_dump - A tool for dumping the HEVCC element from the middle of a file + + Distributed under the GPL v2 + see the file COPYING for details + or visit http://www.gnu.org/copyleft/gpl.html + + Written by Moritz Bunkus . +*/ + +#include "common/common_pch.h" + +#include "common/bit_cursor.h" +#include "common/command_line.h" +#include "common/hevc.h" +#include "common/mm_io_x.h" +#include "common/mpeg.h" +#include "common/strings/parsing.h" + +static void +v(std::string::size_type indent, + std::string const &label) { + mxinfo(boost::format("%1%%2%\n") % std::string(indent, ' ') % label); +} + +static void +v(std::string::size_type indent, + std::string const &label, + std::string const &value) { + auto width = 60 - indent; + auto fmt = (boost::format("%%1%%%%|2$-%1%s| %%3%%\n") % width).str(); + mxinfo(boost::format(fmt) % std::string(indent, ' ') % (label + ':') % value); +} + +static void +v(std::string::size_type indent, + std::string const &label, + uint64_t value) { + v(indent, label, (boost::format("%1%") % value).str()); +} + +static void +v(std::string::size_type indent, + std::size_t sub_idx, + std::string const &label, + std::string const &value) { + v(indent, (boost::format("%1%[%2%]") % label % sub_idx).str(), value); +} + +static void +v(std::string::size_type indent, + std::size_t sub_idx, + std::string const &label, + uint64_t value) { + v(indent, sub_idx, label, (boost::format("%1%") % value).str()); +} + +static void +v(std::string::size_type indent, + std::size_t sub_idx1, + std::size_t sub_idx2, + std::string const &label, + std::string const &value) { + v(indent, (boost::format("%1%[%2%][%3%]") % label % sub_idx1 % sub_idx2).str(), value); +} + +static void +v(std::string::size_type indent, + std::size_t sub_idx1, + std::size_t sub_idx2, + std::string const &label, + uint64_t value) { + v(indent, sub_idx1, sub_idx2, label, (boost::format("%1%") % value).str()); +} + +static void +v(std::string::size_type indent, + std::size_t sub_idx1, + std::size_t sub_idx2, + std::size_t sub_idx3, + std::string const &label, + std::string const &value) { + v(indent, (boost::format("%1%[%2%][%3%][%4%]") % label % sub_idx1 % sub_idx2 % sub_idx3).str(), value); +} + +static void +v(std::string::size_type indent, + std::size_t sub_idx1, + std::size_t sub_idx2, + std::size_t sub_idx3, + std::string const &label, + uint64_t value) { + v(indent, sub_idx1, sub_idx2, sub_idx3, label, (boost::format("%1%") % value).str()); +} + +static std::string +x(std::size_t width, + uint64_t value) { + auto fmt = (boost::format("0x%%|1$0%1%x|") % width).str(); + return (boost::format(fmt) % value).str(); +} + +static void +show_help() { + mxinfo("hevcc_dump [options] input_file_name position_in_file\n" + "\n" + "General options:\n" + "\n" + " -h, --help This help text\n" + " -V, --version Print version information\n"); + mxexit(); +} + +static void +show_version() { + mxinfo("hevcc_dump v" PACKAGE_VERSION "\n"); + mxexit(); +} + +static std::pair +parse_args(std::vector &args) { + std::string file_name; + uint64_t file_pos = 0; + + for (auto const &arg: args) { + if ((arg == "-h") || (arg == "--help")) + show_help(); + + else if ((arg == "-V") || (arg == "--version")) + show_version(); + + else if (file_name.empty()) + file_name = arg; + + else if (file_pos != 0) + mxerror("Superfluous arguments given.\n"); + + else if (!parse_number(arg, file_pos)) + mxerror("The file position is not a valid number.\n"); + } + + if (file_name.empty()) + mxerror("No file name given\n"); + + if (!file_pos) + mxerror("No file position given\n"); + + return { file_name, file_pos }; +} + +static void +parse_profile_tier_level(std::size_t indent, + bit_reader_c &r, + bool profile_present_flag, + unsigned int max_num_sub_layers_minus_1) { + v(indent, "profile tier level"); + indent += 2; + + if (profile_present_flag) { + unsigned int general_profile_idc, general_profile_compatibility_flag; + + v(indent, "general_profile_space", r.get_bits(2)); + v(indent, "general_tier_flag", r.get_bits(1)); + v(indent, "general_profile_idc", general_profile_idc = r.get_bits(5)); + v(indent, "general_profile_compatibility_flag", x(8, general_profile_compatibility_flag = r.get_bits(32))); + v(indent, "general_progressive_source_flag", r.get_bits(1)); + v(indent, "general_interlaced_source_flag", r.get_bits(1)); + v(indent, "general_non_packed_constraint_flag", r.get_bits(1)); + v(indent, "general_frame_only_constraint_flag", r.get_bits(1)); + + // 3 2 2 1 1 1 + // 1 7 3 9 5 1 7 3 + // 00000000 00000000 00000000 00000000 + // flags 4...10 == bits 27...21 + // bits 27...24 == 0x0f + // bits 23...21 == 0xe0 + if ( ((general_profile_idc >= 4) && (general_profile_idc <= 10)) + || (general_profile_compatibility_flag & 0x0fe00000) != 0) { + v(indent, "general_max_12bit_constraint_flag", r.get_bits(1)); + v(indent, "general_max_10bit_constraint_flag", r.get_bits(1)); + v(indent, "general_max_8bit_constraint_flag", r.get_bits(1)); + v(indent, "general_max_422chroma_constraint_flag", r.get_bits(1)); + v(indent, "general_max_420chroma_constraint_flag", r.get_bits(1)); + v(indent, "general_max_monochrome_constraint_flag", r.get_bits(1)); + v(indent, "general_intra_constraint_flag", r.get_bits(1)); + v(indent, "general_one_picture_only_constraint_flag", r.get_bits(1)); + v(indent, "general_lower_bit_rate_constraint_flag", r.get_bits(1)); + + // flags 5, 9, 10 == bits 26, 22, 21 + // bit 26 == 0x04 + // bits 22, 21 == 0x60 + if ( (general_profile_idc == 5) + || (general_profile_idc == 9) + || (general_profile_idc == 10) + || (general_profile_compatibility_flag & 0x04600000) != 0) { + v(indent, "general_max_14bit_constraint_flag", r.get_bits(1)); + v(indent, "general_reserved_zero_33bits", x(9, r.get_bits(33))); + } else + v(indent, "general_reserved_zero_34bits", x(9, r.get_bits(34))); + } else + v(indent, "general_reserved_zero_43bits", x(11, r.get_bits(43))); + + // flags 1, 2, 3, 4, 5, 9 == bits 30..26, 22 + // bits 30..26 == 0x7c + // bit 22 == 0x40 + if ( ((general_profile_idc >= 1) && (general_profile_idc <= 5)) + || (general_profile_idc == 9) + || (general_profile_compatibility_flag & 0x7c400000) != 0) + v(indent, "general_inbld_flag", r.get_bits(1)); + else + v(indent, "general_reserved_zero_bit", r.get_bits(1)); + } + + v(indent, "general_level_idc", r.get_bits(8)); + + std::vector sub_layer_profile_present_flag, sub_layer_level_present_flag; + for (auto i = 0u; i < max_num_sub_layers_minus_1; i++) { + v(indent, i, "sub_layer_profile_present_flag", sub_layer_profile_present_flag[i] = r.get_bits(1)); + v(indent, i, "sub_layer_level_present_flag", sub_layer_level_present_flag[i] = r.get_bits(1)); + } + + if (max_num_sub_layers_minus_1 > 0) + for (auto i = max_num_sub_layers_minus_1; i < 8; ++i) + v(indent, i, "reserved_zero_2bits", r.get_bits(2)); + + for (auto i = 0u; i < max_num_sub_layers_minus_1; i++) { + if (sub_layer_profile_present_flag[i]) { + unsigned int sub_layer_profile_idc, sub_layer_profile_compatibility_flag; + + v(indent, i, "sub_layer_profile_space", r.get_bits(2)); + v(indent, i, "sub_layer_tier_flag", r.get_bits(1)); + v(indent, i, "sub_layer_profile_idc", (sub_layer_profile_idc = r.get_bits(5))); + v(indent, i, "sub_layer_profile_compatibility_flag", x(8, sub_layer_profile_compatibility_flag = r.get_bits(32))); + + // Flags 4...10 == bits 27...21 + // bits 27...24 == 0x0f + // bits 23...21 == 0xe0 + if ( ((sub_layer_profile_idc >= 4) && (sub_layer_profile_idc <= 10)) + || (sub_layer_profile_compatibility_flag & 0x0fe00000) != 0) { + v(indent, i, "sub_layer_max_12bit_constraint_flag", r.get_bits(1)); + v(indent, i, "sub_layer_max_10bit_constraint_flag", r.get_bits(1)); + v(indent, i, "sub_layer_max_8bit_constraint_flag", r.get_bits(1)); + v(indent, i, "sub_layer_max_422chroma_constraint_flag", r.get_bits(1)); + v(indent, i, "sub_layer_max_420chroma_constraint_flag", r.get_bits(1)); + v(indent, i, "sub_layer_max_monochrome_constraint_flag", r.get_bits(1)); + v(indent, i, "sub_layer_intra_constraint_flag", r.get_bits(1)); + v(indent, i, "sub_layer_one_picture_only_constraint_flag", r.get_bits(1)); + v(indent, i, "sub_layer_lower_bit_rate_constraint_flag", r.get_bits(1)); + + // flags 5 == bit 26 + // bit 26 == 0x04 + if ( (sub_layer_profile_idc == 5) + || (sub_layer_profile_compatibility_flag & 0x04000000) != 0) { + v(indent, i, "sub_layer_max_14bit_constraint_flag", r.get_bits(1)); + v(indent, i, "sub_layer_reserved_zero_33bits", x(9, r.get_bits(33))); + } else + v(indent, i, "sub_layer_reserved_zero_34bits", x(9, r.get_bits(34))); + } else + v(indent, i, "sub_layer_reserved_zero_43bits", x(11, r.get_bits(43))); + + // flags 1, 2, 3, 4, 5, 9 == bits 30..26, 22 + // bits 30..26 == 0x7c + // bit 22 == 0x40 + if ( ((sub_layer_profile_idc >= 1) && (sub_layer_profile_idc <= 5)) + || (sub_layer_profile_idc == 9) + || (sub_layer_profile_compatibility_flag & 0x7c400000) != 0) + v(indent, i, "sub_layer_inbld_flag", r.get_bits(1)); + else + v(indent, i, "sub_layer_reserved_zero_bit", r.get_bits(1)); + } + + if (sub_layer_level_present_flag[i]) + v(indent, i, "sub_layer_level_idc", r.get_bits(8)); + } +} + +static void +parse_scaling_list_data(std::size_t indent, + bit_reader_c &r) { + v(indent, "scaling list data"); + indent += 2; + + for (auto size_id = 0u; size_id < 4u; size_id++) { + for (auto matrix_id = 0u; matrix_id < 6u; matrix_id += (size_id == 3u) ? 3 : 1) { + unsigned int scaling_list_pred_mode_flag; + + v(indent, size_id, matrix_id, "scaling_list_pred_mode_flag", scaling_list_pred_mode_flag = r.get_bits(1)); + + if (!scaling_list_pred_mode_flag) + v(indent, size_id, matrix_id, "scaling_list_pred_matrix_id_delta", r.get_unsigned_golomb()); + + else { + if (size_id > 1) + v(indent, size_id - 2, matrix_id, "scaling_list_dc_coef_minus8", r.get_signed_golomb()); + + auto coeff_num = std::min(64u, (1 << (4 + (size_id << 1)))); + for (auto i = 0u; i < coeff_num; ++i) + v(indent, size_id, matrix_id, i, "scaling_list_delta_coef", r.get_signed_golomb()); + } + } + } +} + +static void +parse_short_term_reference_picture_set(std::size_t indent, + bit_reader_c &r, + unsigned int pic_set_idx, + unsigned int num_short_term_ref_pic_sets) { + v(indent, (boost::format("short-term reference picture set %1%") % pic_set_idx).str()); + indent += 2; + + mxerror("parse_short_term_reference_picture_set NOT IMPLEMENTED YET!\n"); + + unsigned int inter_ref_pic_set_prediction_flag = 0; + + if (pic_set_idx != 0) + v(indent, "inter_ref_pic_set_prediction_flag", inter_ref_pic_set_prediction_flag = r.get_bits(1)); + + if (inter_ref_pic_set_prediction_flag) { + if (pic_set_idx == num_short_term_ref_pic_sets) + v(indent, "delta_idx_minus1", r.get_unsigned_golomb()); + + v(indent, "delta_rps_sign", r.get_bits(1)); + v(indent, "abs_delta_rps_minus1", r.get_unsigned_golomb()); + } +} + +static void +parse_hrd_parameters(std::size_t indent, + bit_reader_c &r, + bool stuff, + unsigned int sps_max_sub_layers_minus1) { + v(indent, "HRD parameters"); + indent += 2; + + mxerror("parse_hrd_parameters NOT IMPLEMENTED YET!\n"); +} + +static void +parse_vui_parameters(std::size_t indent, + bit_reader_c &r, + unsigned int sps_max_sub_layers_minus1) { + unsigned int aspect_ratio_info_present_flag, overscan_info_present_flag, video_signal_type_present_flag, chroma_loc_info_present_flag, vui_timing_info_present_flag, bitstream_restriction_flag; + unsigned int default_display_window_flag{}; + + v(indent, "VUI parameters"); + indent += 2; + + v(indent, "aspect_ratio_info_present_flag", aspect_ratio_info_present_flag = r.get_bits(1)); + + if (aspect_ratio_info_present_flag) { + unsigned int aspect_ratio_idc; + + v(indent, "aspect_ratio_idc", aspect_ratio_idc = r.get_bits(8)); + if (aspect_ratio_idc == HEVC_EXTENDED_SAR) { + v(indent, "sar_width", r.get_bits(16)); + v(indent, "sar_height", r.get_bits(16)); + } + } + + v(indent, "overscan_info_present_flag", overscan_info_present_flag = r.get_bits(1)); + if (overscan_info_present_flag) + v(indent, "overscan_appropriate_flag", r.get_bits(1)); + + v(indent, "video_signal_type_present_flag", video_signal_type_present_flag = r.get_bits(1)); + if (video_signal_type_present_flag) { + unsigned int colour_description_present_flag; + + v(indent, "video_format", r.get_bits(3)); + v(indent, "video_full_range_flag", r.get_bits(1)); + v(indent, "colour_description_present_flag", colour_description_present_flag = r.get_bits(1)); + + if (colour_description_present_flag) { + v(indent, "colour_primaries", r.get_bits(8)); + v(indent, "transfer_characteristics", r.get_bits(8)); + v(indent, "matrix_coeffs", r.get_bits(8)); + } + } + + v(indent, "chroma_loc_info_present_flag", chroma_loc_info_present_flag = r.get_bits(1)); + if (chroma_loc_info_present_flag) { + v(indent, "chroma_sample_loc_type_top_field", r.get_unsigned_golomb()); + v(indent, "chroma_sample_loc_type_bottom_field", r.get_unsigned_golomb()); + } + + v(indent, "neutral_chroma_indication_flag", r.get_bits(1)); + v(indent, "field_seq_flag", r.get_bits(1)); + v(indent, "frame_field_info_present_flag", r.get_bits(1)); + + if ( (r.get_remaining_bits() >= 68) + && (r.peek_bits(21) == 0x100000)) + v(indent, "default_display_window_flag", std::string{"(invalid default display window)"}); + else + v(indent, "default_display_window_flag", default_display_window_flag = r.get_bits(1)); + + if (default_display_window_flag) { + v(indent, "def_disp_win_left_offset", r.get_unsigned_golomb()); + v(indent, "def_disp_win_right_offset", r.get_unsigned_golomb()); + v(indent, "def_disp_win_top_offset", r.get_unsigned_golomb()); + v(indent, "def_disp_win_bottom_offset", r.get_unsigned_golomb()); + } + + v(indent, "vui_timing_info_present_flag", vui_timing_info_present_flag = r.get_bits(1)); + if (vui_timing_info_present_flag) { + unsigned int vui_poc_proportional_to_timing_flag, vui_hrd_parameters_present_flag; + + v(indent, "vui_num_units_in_tick", r.get_bits(32)); + v(indent, "vui_time_scale", r.get_bits(32)); + v(indent, "vui_poc_proportional_to_timing_flag", vui_poc_proportional_to_timing_flag = r.get_bits(1)); + + if (vui_poc_proportional_to_timing_flag) + v(indent, "vui_num_ticks_poc_diff_one_minus1", r.get_unsigned_golomb()); + + v(indent, "vui_hrd_parameters_present_flag", vui_hrd_parameters_present_flag = r.get_bits(1)); + if (vui_hrd_parameters_present_flag) + parse_hrd_parameters(indent, r, true, sps_max_sub_layers_minus1); + } + + v(indent, "bitstream_restriction_flag", bitstream_restriction_flag = r.get_bits(1)); + if (bitstream_restriction_flag) { + v(indent, "tiles_fixed_structure_flag", r.get_bits(1)); + v(indent, "motion_vectors_over_pic_boundaries_flag", r.get_bits(1)); + v(indent, "restricted_ref_pic_lists_flag", r.get_bits(1)); + v(indent, "min_spatial_segmentation_idc", r.get_unsigned_golomb()); + v(indent, "max_bytes_per_pic_denom", r.get_unsigned_golomb()); + v(indent, "max_bits_per_min_cu_denom", r.get_unsigned_golomb()); + v(indent, "log2_max_mv_length_horizontal", r.get_unsigned_golomb()); + v(indent, "log2_max_mv_length_vertical", r.get_unsigned_golomb()); + } +} + +static void +parse_sps_range_extension(std::size_t indent, + bit_reader_c &) { + v(indent, "range extension"); + indent += 2; + + mxerror("parse_sps_range_extension NOT IMPLEMENTED YET!\n"); +} + +static void +parse_sps_multilayer_extension(std::size_t indent, + bit_reader_c &) { + v(indent, "multilayer extension"); + indent += 2; + + mxerror("parse_sps_multilayer_extension NOT IMPLEMENTED YET!\n"); +} + +static void +parse_sps_3d_extension(std::size_t indent, + bit_reader_c &) { + v(indent, "3d extension"); + indent += 2; + + mxerror("parse_sps_3d_extension NOT IMPLEMENTED YET!\n"); +} + +static void +parse_sps_scc_extension(std::size_t indent, + bit_reader_c &) { + v(indent, "scc extension"); + indent += 2; + + mxerror("parse_sps_scc_extension NOT IMPLEMENTED YET!\n"); +} + +static void +parse_sps_extension_4bits(std::size_t indent, + bit_reader_c &) { + v(indent, "extension 4bits"); + indent += 2; + + mxerror("parse_sps_extension_4bits NOT IMPLEMENTED YET!\n"); +} + +static void +parse_sps(memory_cptr const &data) { + unsigned int sps_max_sub_layers_minus1, chroma_format_idc, conformance_window_flag, sps_sub_layer_ordering_info_present_flag, scaling_list_enabled_flag, pcm_enabled_flag, num_short_term_ref_pic_sets, long_term_ref_pics_present_flag; + unsigned int vui_parameters_present_flag, sps_extension_present_flag, log2_max_pic_order_cnt_lsb_minus4; + unsigned int sps_range_extension_flag{}, sps_multilayer_extension_flag{}, sps_3d_extension_flag{}, sps_scc_extension_flag{}, sps_extension_4bits{}; + + auto r = bit_reader_c{data->get_buffer(), data->get_size()}; + + v(4, "sequence parameter set"); + v(6, "forbidden_zero_bit", r.get_bits(1)); + v(6, "nal_unit_type", r.get_bits(6)); + v(6, "nuh_layer_id", r.get_bits(6)); + v(6, "nuh_temporal_id_plus1", r.get_bits(3)); + v(6, "sps_video_parameter_set_id", r.get_bits(4)); + v(6, "sps_max_sub_layers_minus1", sps_max_sub_layers_minus1 = r.get_bits(3)); + v(6, "sps_temporal_id_nesting_flag", r.get_bits(1)); + + parse_profile_tier_level(6, r, true, sps_max_sub_layers_minus1); + + v(6, "sps_seq_parameter_set_id", r.get_unsigned_golomb()); + v(6, "chroma_format_idc", chroma_format_idc = r.get_unsigned_golomb()); + + if (chroma_format_idc == 3) + v(6, "separate_colour_plane_flag", r.get_bits(1)); + + v(6, "pic_width_in_luma_samples", r.get_unsigned_golomb()); + v(6, "pic_height_in_luma_samples", r.get_unsigned_golomb()); + v(6, "conformance_window_flag", conformance_window_flag = r.get_bits(1)); + + if (conformance_window_flag) { + v(6, "conf_win_left_offset", r.get_unsigned_golomb()); + v(6, "conf_win_right_offset", r.get_unsigned_golomb()); + v(6, "conf_win_top_offset", r.get_unsigned_golomb()); + v(6, "conf_win_bottom_offset", r.get_unsigned_golomb()); + } + + v(6, "bit_depth_luma_minus8", r.get_unsigned_golomb()); + v(6, "bit_depth_chroma_minus8", r.get_unsigned_golomb()); + v(6, "log2_max_pic_order_cnt_lsb_minus4", log2_max_pic_order_cnt_lsb_minus4 = r.get_unsigned_golomb()); + v(6, "sps_sub_layer_ordering_info_present_flag", sps_sub_layer_ordering_info_present_flag = r.get_bits(1)); + + for (auto i = (sps_sub_layer_ordering_info_present_flag ? 0 : sps_max_sub_layers_minus1); i <= sps_max_sub_layers_minus1; ++i) { + v(6, i, "sps_max_dec_pic_buffering_minus1", r.get_unsigned_golomb()); + v(6, i, "sps_max_num_reorder_pics", r.get_unsigned_golomb()); + v(6, i, "sps_max_latency_increase_plus1", r.get_unsigned_golomb()); + } + + v(6, "log2_min_luma_coding_block_size_minus3", r.get_unsigned_golomb()); + v(6, "log2_diff_max_min_luma_coding_block_size", r.get_unsigned_golomb()); + v(6, "log2_min_luma_transform_block_size_minus2", r.get_unsigned_golomb()); + v(6, "log2_diff_max_min_luma_transform_block_size", r.get_unsigned_golomb()); + v(6, "max_transform_hierarchy_depth_inter", r.get_unsigned_golomb()); + v(6, "max_transform_hierarchy_depth_intra", r.get_unsigned_golomb()); + v(6, "scaling_list_enabled_flag", scaling_list_enabled_flag = r.get_bits(1)); + + if (scaling_list_enabled_flag) { + unsigned int sps_scaling_list_data_present_flag; + v(6, "sps_scaling_list_data_present_flag", sps_scaling_list_data_present_flag = r.get_bits(1)); + + if (sps_scaling_list_data_present_flag) + parse_scaling_list_data(6, r); + } + + v(6, "amp_enabled_flag", r.get_bits(1)); + v(6, "sample_adaptive_offset_enabled_flag", r.get_bits(1)); + v(6, "pcm_enabled_flag", pcm_enabled_flag = r.get_bits(1)); + + if (pcm_enabled_flag) { + v(6, "pcm_sample_bit_depth_luma_minus1", r.get_bits(4)); + v(6, "pcm_sample_bit_depth_chroma_minus1", r.get_bits(4)); + v(6, "log2_min_pcm_luma_coding_block_size_minus3", r.get_unsigned_golomb()); + v(6, "log2_diff_max_min_pcm_luma_coding_block_size", r.get_unsigned_golomb()); + v(6, "pcm_loop_filter_disabled_flag", r.get_bits(1)); + } + + v(6, "num_short_term_ref_pic_sets", num_short_term_ref_pic_sets = r.get_unsigned_golomb()); + for (auto i = 0u; i < num_short_term_ref_pic_sets; ++i) + parse_short_term_reference_picture_set(6, r, i, num_short_term_ref_pic_sets); + + v(6, "long_term_ref_pics_present_flag", long_term_ref_pics_present_flag = r.get_bits(1)); + if (long_term_ref_pics_present_flag) { + unsigned int num_long_term_ref_pics_sps; + v(6, "num_long_term_ref_pics_sps", num_long_term_ref_pics_sps = r.get_unsigned_golomb()); + + for (auto i = 0u; i < num_long_term_ref_pics_sps; ++i) { + v(6, i, "lt_ref_pic_poc_lsb_sps", r.get_bits(log2_max_pic_order_cnt_lsb_minus4 + 4)); + v(6, i, "used_by_curr_pic_lt_sps_flag", r.get_bits(1)); + } + } + + v(6, "sps_temporal_mvp_enabled_flag", r.get_bits(1)); + v(6, "strong_intra_smoothing_enabled_flag", r.get_bits(1)); + v(6, "vui_parameters_present_flag", vui_parameters_present_flag = r.get_bits(1)); + + if (vui_parameters_present_flag) + parse_vui_parameters(6, r, sps_max_sub_layers_minus1); + + v(6, "sps_extension_present_flag", sps_extension_present_flag = r.get_bits(1)); + if (sps_extension_present_flag) { + v(6, "sps_range_extension_flag", sps_range_extension_flag = r.get_bits(1)); + v(6, "sps_multilayer_extension_flag", sps_multilayer_extension_flag = r.get_bits(1)); + v(6, "sps_3d_extension_flag", sps_3d_extension_flag = r.get_bits(1)); + v(6, "sps_scc_extension_flag", sps_scc_extension_flag = r.get_bits(1)); + v(6, "sps_extension_4bits", sps_extension_4bits = r.get_bits(4)); + } + + if (sps_range_extension_flag) + parse_sps_range_extension(6, r); + if (sps_multilayer_extension_flag) + parse_sps_multilayer_extension(6, r); + if (sps_3d_extension_flag) + parse_sps_3d_extension(6, r); + if (sps_scc_extension_flag) + parse_sps_scc_extension(6, r); + if (sps_extension_4bits) + parse_sps_extension_4bits(6, r); +} + +static void +parse_hevcc(std::string const &file_name, + uint64_t file_pos) { + mm_file_io_c in{file_name}; + + in.setFilePointer(file_pos); + auto data = in.read(23); + auto r = bit_reader_c{data->get_buffer(), data->get_size()}; + unsigned int num_parameter_sets; + + v(0, "configuration_version", r.get_bits(8)); + v(0, "general_profile_space", r.get_bits(2)); + v(0, "general_tier_flag", r.get_bits(1)); + v(0, "general_profile_idc", r.get_bits(5)); + v(0, "general_profile_compatibility_flag", x(8, r.get_bits(32))); + v(0, "general_progressive_source_flag", r.get_bits(1)); + v(0, "general_interlace_source_flag", r.get_bits(1)); + v(0, "general_nonpacked_constraint_flag", r.get_bits(1)); + v(0, "general_frame_only_constraint_flag", r.get_bits(1)); + v(0, "reserved", x(11, r.get_bits(44))); + v(0, "general_level_idc", r.get_bits(8)); + v(0, "reserved", r.get_bits(4)); + v(0, "min_spatial_segmentation_idc", r.get_bits(12)); + v(0, "reserved", r.get_bits(6)); + v(0, "parallelism_type", r.get_bits(2)); + v(0, "reserved", r.get_bits(6)); + v(0, "chroma_format_idc", r.get_bits(2)); + v(0, "reserved", r.get_bits(5)); + v(0, "bit_depth_luma_minus8", r.get_bits(3)); + v(0, "reserved", r.get_bits(5)); + v(0, "bit_depth_chroma_minus8", r.get_bits(3)); + v(0, "reserved", x(4, r.get_bits(16))); + v(0, "reserved", r.get_bits(2)); + v(0, "max_sub_layers", r.get_bits(3)); + v(0, "temporal_id_nesting_flag", r.get_bits(1)); + v(0, "size_nalu_minus_one", r.get_bits(2)); + v(0, "num_parameter_sets", num_parameter_sets = r.get_bits(8)); + + for (auto parameter_set_idx = 0u; parameter_set_idx < num_parameter_sets; ++parameter_set_idx) { + auto byte = in.read_uint8(); + auto type = byte & 0x3f; + auto type_name = type == HEVC_NALU_TYPE_VIDEO_PARAM ? "video parameter set" + : type == HEVC_NALU_TYPE_SEQ_PARAM ? "sequence parameter set" + : type == HEVC_NALU_TYPE_PIC_PARAM ? "picture parameter set" + : type == HEVC_NALU_TYPE_PREFIX_SEI ? "supplemental enhancement information" + : "unknown"; + auto nal_unit_count = in.read_uint16_be(); + + v(0, (boost::format("parameter set %1%") % parameter_set_idx).str()); + v(2, "array_completeness", (byte & 0x80) >> 7); + v(2, "reserved", (byte & 0x40) >> 6); + v(2, "nal_unit_type", (boost::format("%1% (%2%)") % type % type_name).str()); + v(2, "nal_unit_count", nal_unit_count); + + for (auto nal_unit_idx = 0u; nal_unit_idx < nal_unit_count; ++nal_unit_idx) { + auto nal_unit_size = in.read_uint16_be(); + data = in.read(nal_unit_size); + data = mtx::mpeg::nalu_to_rbsp(data); + + v(2, (boost::format("NAL unit %1%") % nal_unit_idx).str()); + v(4, "nal_unit_size", (boost::format("%1% (RBSP size: %2%)") % nal_unit_size % data->get_size()).str()); + + if (type == HEVC_NALU_TYPE_SEQ_PARAM) + parse_sps(data); + } + } +} + +int +main(int argc, + char **argv) { + mtx_common_init("hevcc_dump", argv[0]); + + auto args = command_line_utf8(argc, argv); + while (handle_common_cli_args(args, "-r")) + ; + + auto file_spec = parse_args(args); + + try { + parse_hevcc(file_spec.first, file_spec.second); + } catch (mtx::mm_io::open_x &) { + mxerror("File not found\n"); + } + + mxexit(); +}