diff --git a/packager/app/test/testdata/bear-640x360-hevc-v-cenc-golden.mp4 b/packager/app/test/testdata/bear-640x360-hevc-v-cenc-golden.mp4 index 2ebeb9d96a..b7be288788 100644 Binary files a/packager/app/test/testdata/bear-640x360-hevc-v-cenc-golden.mp4 and b/packager/app/test/testdata/bear-640x360-hevc-v-cenc-golden.mp4 differ diff --git a/packager/media/filters/filters.gyp b/packager/media/filters/filters.gyp index 0066e5ad90..8410654f7e 100644 --- a/packager/media/filters/filters.gyp +++ b/packager/media/filters/filters.gyp @@ -23,6 +23,8 @@ 'h264_byte_to_unit_stream_converter.h', 'h264_parser.cc', 'h264_parser.h', + 'h265_parser.cc', + 'h265_parser.h', 'h26x_bit_reader.cc', 'h26x_bit_reader.h', 'hevc_decoder_configuration.cc', @@ -51,6 +53,7 @@ 'ec3_audio_util_unittest.cc', 'h264_byte_to_unit_stream_converter_unittest.cc', 'h264_parser_unittest.cc', + 'h265_parser_unittest.cc', 'h26x_bit_reader_unittest.cc', 'hevc_decoder_configuration_unittest.cc', 'nal_unit_to_byte_stream_converter_unittest.cc', diff --git a/packager/media/filters/h265_parser.cc b/packager/media/filters/h265_parser.cc new file mode 100644 index 0000000000..1f32c72716 --- /dev/null +++ b/packager/media/filters/h265_parser.cc @@ -0,0 +1,843 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "packager/media/filters/h265_parser.h" + +#include +#include + +#include "packager/base/logging.h" +#include "packager/media/filters/nalu_reader.h" + +#define TRUE_OR_RETURN(a) \ + do { \ + if (!(a)) { \ + DVLOG(1) << "Failure while processing " << #a; \ + return kInvalidStream; \ + } \ + } while (0) + +#define OK_OR_RETURN(a) \ + do { \ + Result status = (a); \ + if (status != kOk) \ + return status; \ + } while (false) + +namespace edash_packager { +namespace media { + +namespace { +int GetNumPicTotalCurr(const H265SliceHeader& slice_header, + const H265Sps& sps) { + int num_pic_total_curr = 0; + const H265ReferencePictureSet& ref_pic_set = + slice_header.short_term_ref_pic_set_sps_flag + ? sps.st_ref_pic_sets[slice_header.short_term_ref_pic_set_idx] + : slice_header.st_ref_pic_set; + + for (int i = 0; i < ref_pic_set.num_negative_pics; i++) { + if (ref_pic_set.used_by_curr_pic_s0[i]) + num_pic_total_curr++; + } + for (int i = 0; i < ref_pic_set.num_positive_pics; i++) { + if (ref_pic_set.used_by_curr_pic_s1[i]) + num_pic_total_curr++; + } + + return num_pic_total_curr + slice_header.used_by_curr_pic_lt; +} +} // namespace + +H265Pps::H265Pps() {} +H265Pps::~H265Pps() {} + +H265Sps::H265Sps() {} +H265Sps::~H265Sps() {} + +int H265Sps::GetPicSizeInCtbsY() const { + int min_cb_log2_size_y = log2_min_luma_coding_block_size_minus3 + 3; + int ctb_log2_size_y = + min_cb_log2_size_y + log2_diff_max_min_luma_coding_block_size; + int ctb_size_y = 1 << ctb_log2_size_y; + + // Round-up division. + int pic_width_in_ctbs_y = (pic_width_in_luma_samples - 1) / ctb_size_y + 1; + int pic_height_in_ctbs_y = (pic_height_in_luma_samples - 1) / ctb_size_y + 1; + return pic_width_in_ctbs_y * pic_height_in_ctbs_y; +} + +int H265Sps::GetChromaArrayType() const { + if (!separate_colour_plane_flag) + return chroma_format_idc; + else + return 0; +} + +H265ReferencePictureListModifications::H265ReferencePictureListModifications() { +} +H265ReferencePictureListModifications:: + ~H265ReferencePictureListModifications() {} + +H265SliceHeader::H265SliceHeader() {} +H265SliceHeader::~H265SliceHeader() {} + +H265Parser::H265Parser() {} +H265Parser::~H265Parser() {} + +H265Parser::Result H265Parser::ParseSliceHeader(const Nalu& nalu, + H265SliceHeader* slice_header) { + DCHECK(nalu.is_video_slice()); + *slice_header = H265SliceHeader(); + + // Parses whole element. + H26xBitReader reader; + reader.Initialize(nalu.data() + nalu.header_size(), nalu.payload_size()); + H26xBitReader* br = &reader; + + TRUE_OR_RETURN(br->ReadBool(&slice_header->first_slice_segment_in_pic_flag)); + if (nalu.type() >= Nalu::H265_BLA_W_LP && + nalu.type() <= Nalu::H265_RSV_IRAP_VCL23) { + TRUE_OR_RETURN(br->ReadBool(&slice_header->no_output_of_prior_pics_flag)); + } + + TRUE_OR_RETURN(br->ReadUE(&slice_header->pic_parameter_set_id)); + const H265Pps* pps = GetPps(slice_header->pic_parameter_set_id); + TRUE_OR_RETURN(pps); + + const H265Sps* sps = GetSps(pps->seq_parameter_set_id); + TRUE_OR_RETURN(sps); + + if (!slice_header->first_slice_segment_in_pic_flag) { + if (pps->dependent_slice_segments_enabled_flag) { + TRUE_OR_RETURN(br->ReadBool(&slice_header->dependent_slice_segment_flag)); + } + const int bit_length = ceil(log2(sps->GetPicSizeInCtbsY())); + TRUE_OR_RETURN(br->ReadBits(bit_length, &slice_header->segment_address)); + } + + if (!slice_header->dependent_slice_segment_flag) { + TRUE_OR_RETURN(br->SkipBits(pps->num_extra_slice_header_bits)); + TRUE_OR_RETURN(br->ReadUE(&slice_header->slice_type)); + if (pps->output_flag_present_flag) { + TRUE_OR_RETURN(br->ReadBool(&slice_header->pic_output_flag)); + } + if (sps->separate_colour_plane_flag) { + TRUE_OR_RETURN(br->ReadBits(2, &slice_header->colour_plane_id)); + } + + if (nalu.type() != Nalu::H265_IDR_W_RADL && + nalu.type() != Nalu::H265_IDR_N_LP) { + TRUE_OR_RETURN(br->ReadBits(sps->log2_max_pic_order_cnt_lsb_minus4 + 4, + &slice_header->slice_pic_order_cnt_lsb)); + + TRUE_OR_RETURN( + br->ReadBool(&slice_header->short_term_ref_pic_set_sps_flag)); + if (!slice_header->short_term_ref_pic_set_sps_flag) { + OK_OR_RETURN(ParseReferencePictureSet( + sps->num_short_term_ref_pic_sets, sps->num_short_term_ref_pic_sets, + sps->st_ref_pic_sets, br, &slice_header->st_ref_pic_set)); + } else if (sps->num_short_term_ref_pic_sets > 1) { + TRUE_OR_RETURN( + br->ReadBits(ceil(log2(sps->num_short_term_ref_pic_sets)), + &slice_header->short_term_ref_pic_set_idx)); + } + + if (sps->long_term_ref_pic_present_flag) { + if (sps->num_long_term_ref_pics > 0) { + TRUE_OR_RETURN(br->ReadUE(&slice_header->num_long_term_sps)); + } + TRUE_OR_RETURN(br->ReadUE(&slice_header->num_long_term_pics)); + + const int pic_count = + slice_header->num_long_term_sps + slice_header->num_long_term_pics; + slice_header->long_term_pics_info.resize(pic_count); + for (int i = 0; i < pic_count; i++) { + if (i < slice_header->num_long_term_sps) { + int lt_idx_sps = 0; + if (sps->num_long_term_ref_pics > 1) { + TRUE_OR_RETURN(br->ReadBits( + ceil(log2(sps->num_long_term_ref_pics)), <_idx_sps)); + } + if (sps->used_by_curr_pic_lt_flag[lt_idx_sps]) + slice_header->used_by_curr_pic_lt++; + } else { + TRUE_OR_RETURN(br->SkipBits(sps->log2_max_pic_order_cnt_lsb_minus4 + + 4)); // poc_lsb_lt + bool used_by_curr_pic_lt_flag; + TRUE_OR_RETURN(br->ReadBool(&used_by_curr_pic_lt_flag)); + if (used_by_curr_pic_lt_flag) + slice_header->used_by_curr_pic_lt++; + } + TRUE_OR_RETURN(br->ReadBool(&slice_header->long_term_pics_info[i] + .delta_poc_msb_present_flag)); + if (slice_header->long_term_pics_info[i].delta_poc_msb_present_flag) { + TRUE_OR_RETURN(br->ReadUE( + &slice_header->long_term_pics_info[i].delta_poc_msb_cycle_lt)); + } + } + } + + if (sps->temporal_mvp_enabled_flag) { + TRUE_OR_RETURN( + br->ReadBool(&slice_header->slice_temporal_mvp_enabled_flag)); + } + } + + if (nalu.nuh_layer_id() != 0) { + NOTIMPLEMENTED() << "Multi-layer streams are not supported."; + return kUnsupportedStream; + } + + if (sps->sample_adaptive_offset_enabled_flag) { + TRUE_OR_RETURN(br->ReadBool(&slice_header->slice_sao_luma_flag)); + if (sps->GetChromaArrayType() != 0) { + TRUE_OR_RETURN(br->ReadBool(&slice_header->slice_sao_chroma_flag)); + } + } + + slice_header->num_ref_idx_l0_active_minus1 = + pps->num_ref_idx_l0_default_active_minus1; + slice_header->num_ref_idx_l1_active_minus1 = + pps->num_ref_idx_l1_default_active_minus1; + if (slice_header->slice_type == kPSlice || + slice_header->slice_type == kBSlice) { + TRUE_OR_RETURN( + br->ReadBool(&slice_header->num_ref_idx_active_override_flag)); + if (slice_header->num_ref_idx_active_override_flag) { + TRUE_OR_RETURN(br->ReadUE(&slice_header->num_ref_idx_l0_active_minus1)); + if (slice_header->slice_type == kBSlice) { + TRUE_OR_RETURN( + br->ReadUE(&slice_header->num_ref_idx_l1_active_minus1)); + } + } + + const int num_pic_total_curr = GetNumPicTotalCurr(*slice_header, *sps); + if (pps->lists_modification_present_flag && num_pic_total_curr > 1) { + OK_OR_RETURN(SkipReferencePictureListModification( + *slice_header, *pps, num_pic_total_curr, br)); + } + + if (slice_header->slice_type == kBSlice) { + TRUE_OR_RETURN(br->ReadBool(&slice_header->mvd_l1_zero_flag)); + } + if (pps->cabac_init_present_flag) { + TRUE_OR_RETURN(br->ReadBool(&slice_header->cabac_init_flag)); + } + if (slice_header->slice_temporal_mvp_enabled_flag) { + if (slice_header->slice_type == kBSlice) { + TRUE_OR_RETURN(br->ReadBool(&slice_header->collocated_from_l0)); + } + bool l0_greater_than_0 = slice_header->num_ref_idx_l0_active_minus1 > 0; + bool l1_greater_than_0 = slice_header->num_ref_idx_l1_active_minus1 > 0; + if (slice_header->collocated_from_l0 ? l0_greater_than_0 + : l1_greater_than_0) { + TRUE_OR_RETURN(br->ReadUE(&slice_header->collocated_ref_idx)); + } + } + + if ((pps->weighted_pred_flag && slice_header->slice_type == kPSlice) || + (pps->weighted_bipred_flag && slice_header->slice_type == kBSlice)) { + OK_OR_RETURN(SkipPredictionWeightTable( + slice_header->slice_type == kBSlice, *sps, *slice_header, br)); + } + TRUE_OR_RETURN(br->ReadUE(&slice_header->five_minus_max_num_merge_cand)); + } + + TRUE_OR_RETURN(br->ReadSE(&slice_header->slice_qp_delta)); + if (pps->slice_chroma_qp_offsets_present_flag) { + TRUE_OR_RETURN(br->ReadSE(&slice_header->slice_cb_qp_offset)); + TRUE_OR_RETURN(br->ReadSE(&slice_header->slice_cr_qp_offset)); + } + + if (pps->chroma_qp_offset_list_enabled_flag) { + TRUE_OR_RETURN( + br->ReadBool(&slice_header->cu_chroma_qp_offset_enabled_flag)); + } + if (pps->deblocking_filter_override_enabled_flag) { + TRUE_OR_RETURN( + br->ReadBool(&slice_header->deblocking_filter_override_flag)); + } + if (slice_header->deblocking_filter_override_flag) { + TRUE_OR_RETURN( + br->ReadBool(&slice_header->slice_deblocking_filter_disabled_flag)); + if (!slice_header->slice_deblocking_filter_disabled_flag) { + TRUE_OR_RETURN(br->ReadSE(&slice_header->slice_beta_offset_div2)); + TRUE_OR_RETURN(br->ReadSE(&slice_header->slice_tc_offset_div2)); + } + } + if (pps->loop_filter_across_slices_enabled_flag && + (slice_header->slice_sao_luma_flag || + slice_header->slice_sao_chroma_flag || + !slice_header->slice_deblocking_filter_disabled_flag)) { + TRUE_OR_RETURN(br->ReadBool( + &slice_header->slice_loop_filter_across_slices_enabled_flag)); + } + } + + if (pps->tiles_enabled_flag || pps->entropy_coding_sync_enabled_flag) { + TRUE_OR_RETURN(br->ReadUE(&slice_header->num_entry_point_offsets)); + if (slice_header->num_entry_point_offsets > 0) { + TRUE_OR_RETURN(br->ReadUE(&slice_header->offset_len_minus1)); + slice_header->entry_point_offset_minus1.resize( + slice_header->num_entry_point_offsets); + for (int i = 0; i < slice_header->num_entry_point_offsets; i++) { + TRUE_OR_RETURN( + br->ReadBits(slice_header->offset_len_minus1 + 1, + &slice_header->entry_point_offset_minus1[i])); + } + } + } + + if (pps->slice_segment_header_extension_present_flag) { + int extension_length; + TRUE_OR_RETURN(br->ReadUE(&extension_length)); + TRUE_OR_RETURN(br->SkipBits(extension_length * 8)); + } + + size_t epb = br->NumEmulationPreventionBytesRead(); + slice_header->header_bit_size = + (nalu.payload_size() - epb) * 8 - br->NumBitsLeft(); + + return kOk; +} + +H265Parser::Result H265Parser::ParsePps(const Nalu& nalu, int* pps_id) { + DCHECK_EQ(Nalu::H265_PPS, nalu.type()); + + // Reads most of the element, not reading the extension data. + H26xBitReader reader; + reader.Initialize(nalu.data() + nalu.header_size(), nalu.payload_size()); + H26xBitReader* br = &reader; + + *pps_id = -1; + scoped_ptr pps(new H265Pps); + + TRUE_OR_RETURN(br->ReadUE(&pps->pic_parameter_set_id)); + TRUE_OR_RETURN(br->ReadUE(&pps->seq_parameter_set_id)); + + TRUE_OR_RETURN(br->ReadBool(&pps->dependent_slice_segments_enabled_flag)); + TRUE_OR_RETURN(br->ReadBool(&pps->output_flag_present_flag)); + TRUE_OR_RETURN(br->ReadBits(3, &pps->num_extra_slice_header_bits)); + TRUE_OR_RETURN(br->ReadBool(&pps->sign_data_hiding_enabled_flag)); + TRUE_OR_RETURN(br->ReadBool(&pps->cabac_init_present_flag)); + + TRUE_OR_RETURN(br->ReadUE(&pps->num_ref_idx_l0_default_active_minus1)); + TRUE_OR_RETURN(br->ReadUE(&pps->num_ref_idx_l1_default_active_minus1)); + TRUE_OR_RETURN(br->ReadSE(&pps->init_qp_minus26)); + TRUE_OR_RETURN(br->ReadBool(&pps->constrained_intra_pred_flag)); + TRUE_OR_RETURN(br->ReadBool(&pps->transform_skip_enabled_flag)); + + TRUE_OR_RETURN(br->ReadBool(&pps->cu_qp_delta_enabled_flag)); + if (pps->cu_qp_delta_enabled_flag) + TRUE_OR_RETURN(br->ReadUE(&pps->diff_cu_qp_delta_depth)); + TRUE_OR_RETURN(br->ReadSE(&pps->cb_qp_offset)); + TRUE_OR_RETURN(br->ReadSE(&pps->cr_qp_offset)); + + TRUE_OR_RETURN(br->ReadBool(&pps->slice_chroma_qp_offsets_present_flag)); + TRUE_OR_RETURN(br->ReadBool(&pps->weighted_pred_flag)); + TRUE_OR_RETURN(br->ReadBool(&pps->weighted_bipred_flag)); + TRUE_OR_RETURN(br->ReadBool(&pps->transquant_bypass_enabled_flag)); + TRUE_OR_RETURN(br->ReadBool(&pps->tiles_enabled_flag)); + TRUE_OR_RETURN(br->ReadBool(&pps->entropy_coding_sync_enabled_flag)); + + if (pps->tiles_enabled_flag) { + TRUE_OR_RETURN(br->ReadUE(&pps->num_tile_columns_minus1)); + TRUE_OR_RETURN(br->ReadUE(&pps->num_tile_rows_minus1)); + TRUE_OR_RETURN(br->ReadBool(&pps->uniform_spacing_flag)); + if (!pps->uniform_spacing_flag) { + pps->column_width_minus1.resize(pps->num_tile_columns_minus1); + for (int i = 0; i < pps->num_tile_columns_minus1; i++) { + TRUE_OR_RETURN(br->ReadUE(&pps->column_width_minus1[i])); + } + pps->row_height_minus1.resize(pps->num_tile_rows_minus1); + for (int i = 0; i < pps->num_tile_rows_minus1; i++) { + TRUE_OR_RETURN(br->ReadUE(&pps->row_height_minus1[i])); + } + } + TRUE_OR_RETURN(br->ReadBool(&pps->loop_filter_across_tiles_enabled_flag)); + } + + TRUE_OR_RETURN(br->ReadBool(&pps->loop_filter_across_slices_enabled_flag)); + TRUE_OR_RETURN(br->ReadBool(&pps->deblocking_filter_control_present_flag)); + if (pps->deblocking_filter_control_present_flag) { + TRUE_OR_RETURN(br->ReadBool(&pps->deblocking_filter_override_enabled_flag)); + TRUE_OR_RETURN(br->ReadBool(&pps->deblocking_filter_disabled_flag)); + if (!pps->deblocking_filter_disabled_flag) { + TRUE_OR_RETURN(br->ReadSE(&pps->beta_offset_div2)); + TRUE_OR_RETURN(br->ReadSE(&pps->tc_offset_div2)); + } + } + + TRUE_OR_RETURN(br->ReadBool(&pps->scaling_list_data_present_flag)); + if (pps->scaling_list_data_present_flag) { + OK_OR_RETURN(SkipScalingListData(br)); + } + + TRUE_OR_RETURN(br->ReadBool(&pps->lists_modification_present_flag)); + TRUE_OR_RETURN(br->ReadUE(&pps->log2_parallel_merge_level_minus2)); + + TRUE_OR_RETURN( + br->ReadBool(&pps->slice_segment_header_extension_present_flag)); + + bool pps_extension_present_flag; + bool pps_range_extension_flag = false; + TRUE_OR_RETURN(br->ReadBool(&pps_extension_present_flag)); + if (pps_extension_present_flag) { + TRUE_OR_RETURN(br->ReadBool(&pps_range_extension_flag)); + // pps_multilayer_extension_flag, pps_3d_extension_flag, pps_extension_5bits + TRUE_OR_RETURN(br->SkipBits(1 + 1 + 5)); + } + + if (pps_range_extension_flag) { + if (pps->transform_skip_enabled_flag) { + // log2_max_transform_skip_block_size_minus2 + int ignored; + TRUE_OR_RETURN(br->ReadUE(&ignored)); + } + + TRUE_OR_RETURN(br->SkipBits(1)); // cross_component_prediction_enabled_flag + TRUE_OR_RETURN(br->ReadBool(&pps->chroma_qp_offset_list_enabled_flag)); + // Incomplete + } + + // Ignore remaining extension data. + + // This will replace any existing PPS instance. The scoped_ptr will delete + // the memory when it is overwritten. + *pps_id = pps->pic_parameter_set_id; + active_ppses_[*pps_id] = pps.Pass(); + + return kOk; +} + +H265Parser::Result H265Parser::ParseSps(const Nalu& nalu, int* sps_id) { + DCHECK_EQ(Nalu::H265_SPS, nalu.type()); + + // Reads most of the element, not reading the extension data. + H26xBitReader reader; + reader.Initialize(nalu.data() + nalu.header_size(), nalu.payload_size()); + H26xBitReader* br = &reader; + + *sps_id = -1; + + scoped_ptr sps(new H265Sps); + + TRUE_OR_RETURN(br->ReadBits(4, &sps->video_parameter_set_id)); + TRUE_OR_RETURN(br->ReadBits(3, &sps->max_sub_layers_minus1)); + TRUE_OR_RETURN(br->ReadBool(&sps->temporal_id_nesting_flag)); + + OK_OR_RETURN(SkipProfileTierLevel(true, sps->max_sub_layers_minus1, br)); + + TRUE_OR_RETURN(br->ReadUE(&sps->seq_parameter_set_id)); + TRUE_OR_RETURN(br->ReadUE(&sps->chroma_format_idc)); + if (sps->chroma_format_idc == 3) { + TRUE_OR_RETURN(br->ReadBool(&sps->separate_colour_plane_flag)); + } + TRUE_OR_RETURN(br->ReadUE(&sps->pic_width_in_luma_samples)); + TRUE_OR_RETURN(br->ReadUE(&sps->pic_height_in_luma_samples)); + + TRUE_OR_RETURN(br->ReadBool(&sps->conformance_window_flag)); + if (sps->conformance_window_flag) { + TRUE_OR_RETURN(br->ReadUE(&sps->conf_win_left_offset)); + TRUE_OR_RETURN(br->ReadUE(&sps->conf_win_right_offset)); + TRUE_OR_RETURN(br->ReadUE(&sps->conf_win_top_offset)); + TRUE_OR_RETURN(br->ReadUE(&sps->conf_win_bottom_offset)); + } + + TRUE_OR_RETURN(br->ReadUE(&sps->bit_depth_luma_minus8)); + TRUE_OR_RETURN(br->ReadUE(&sps->bit_depth_chroma_minus8)); + TRUE_OR_RETURN(br->ReadUE(&sps->log2_max_pic_order_cnt_lsb_minus4)); + + TRUE_OR_RETURN(br->ReadBool(&sps->sub_layer_ordering_info_present_flag)); + int start = sps->sub_layer_ordering_info_present_flag + ? 0 + : sps->max_sub_layers_minus1; + for (int i = start; i <= sps->max_sub_layers_minus1; i++) { + TRUE_OR_RETURN(br->ReadUE(&sps->max_dec_pic_buffering_minus1[i])); + TRUE_OR_RETURN(br->ReadUE(&sps->max_num_reorder_pics[i])); + TRUE_OR_RETURN(br->ReadUE(&sps->max_latency_increase_plus1[i])); + } + + TRUE_OR_RETURN(br->ReadUE(&sps->log2_min_luma_coding_block_size_minus3)); + TRUE_OR_RETURN(br->ReadUE(&sps->log2_diff_max_min_luma_coding_block_size)); + TRUE_OR_RETURN(br->ReadUE(&sps->log2_min_luma_transform_block_size_minus2)); + TRUE_OR_RETURN(br->ReadUE(&sps->log2_diff_max_min_luma_transform_block_size)); + TRUE_OR_RETURN(br->ReadUE(&sps->max_transform_hierarchy_depth_inter)); + TRUE_OR_RETURN(br->ReadUE(&sps->max_transform_hierarchy_depth_intra)); + + TRUE_OR_RETURN(br->ReadBool(&sps->scaling_list_enabled_flag)); + if (sps->scaling_list_enabled_flag) { + TRUE_OR_RETURN(br->ReadBool(&sps->scaling_list_data_present_flag)); + if (sps->scaling_list_data_present_flag) { + OK_OR_RETURN(SkipScalingListData(br)); + } + } + + TRUE_OR_RETURN(br->ReadBool(&sps->amp_enabled_flag)); + TRUE_OR_RETURN(br->ReadBool(&sps->sample_adaptive_offset_enabled_flag)); + TRUE_OR_RETURN(br->ReadBool(&sps->pcm_enabled_flag)); + if (sps->pcm_enabled_flag) { + TRUE_OR_RETURN(br->ReadBits(4, &sps->pcm_sample_bit_depth_luma_minus1)); + TRUE_OR_RETURN(br->ReadBits(4, &sps->pcm_sample_bit_depth_chroma_minus1)); + TRUE_OR_RETURN( + br->ReadUE(&sps->log2_min_pcm_luma_coding_block_size_minus3)); + TRUE_OR_RETURN( + br->ReadUE(&sps->log2_diff_max_min_pcm_luma_coding_block_size)); + TRUE_OR_RETURN(br->ReadBool(&sps->pcm_loop_filter_disabled_flag)); + } + + TRUE_OR_RETURN(br->ReadUE(&sps->num_short_term_ref_pic_sets)); + sps->st_ref_pic_sets.resize(sps->num_short_term_ref_pic_sets); + for (int i = 0; i < sps->num_short_term_ref_pic_sets; i++) { + OK_OR_RETURN(ParseReferencePictureSet(sps->num_short_term_ref_pic_sets, i, + sps->st_ref_pic_sets, br, + &sps->st_ref_pic_sets[i])); + } + + TRUE_OR_RETURN(br->ReadBool(&sps->long_term_ref_pic_present_flag)); + if (sps->long_term_ref_pic_present_flag) { + TRUE_OR_RETURN(br->ReadUE(&sps->num_long_term_ref_pics)); + sps->lt_ref_pic_poc_lsb.resize(sps->num_long_term_ref_pics); + sps->used_by_curr_pic_lt_flag.resize(sps->num_long_term_ref_pics); + for (int i = 0; i < sps->num_long_term_ref_pics; i++) { + TRUE_OR_RETURN(br->ReadBits(sps->log2_max_pic_order_cnt_lsb_minus4 + 4, + &sps->lt_ref_pic_poc_lsb[i])); + bool temp; + TRUE_OR_RETURN(br->ReadBool(&temp)); + sps->used_by_curr_pic_lt_flag[i] = temp; + } + } + + TRUE_OR_RETURN(br->ReadBool(&sps->temporal_mvp_enabled_flag)); + TRUE_OR_RETURN(br->ReadBool(&sps->strong_intra_smoothing_enabled_flag)); + + // Ignore remaining extension data. + + // This will replace any existing SPS instance. The scoped_ptr will delete + // the memory when it is overwritten. + *sps_id = sps->seq_parameter_set_id; + active_spses_[*sps_id] = sps.Pass(); + + return kOk; +} + +const H265Pps* H265Parser::GetPps(int pps_id) { + return active_ppses_[pps_id].get(); +} + +const H265Sps* H265Parser::GetSps(int sps_id) { + return active_spses_[sps_id].get(); +} + +H265Parser::Result H265Parser::ParseReferencePictureSet( + int num_short_term_ref_pic_sets, + int st_rps_idx, + const std::vector& ref_pic_sets, + H26xBitReader* br, + H265ReferencePictureSet* out_ref_pic_set) { + // Parses and processess a short-term reference picture set. This needs to + // be done since the size of this element may be dependent on previous + // reference picture sets. + + bool inter_ref_pic_set_prediction = false; + if (st_rps_idx != 0) { + TRUE_OR_RETURN(br->ReadBool(&inter_ref_pic_set_prediction)); + } + + if (inter_ref_pic_set_prediction) { + int delta_idx = 1; + if (st_rps_idx == num_short_term_ref_pic_sets) { + TRUE_OR_RETURN(br->ReadUE(&delta_idx)); + delta_idx++; + TRUE_OR_RETURN(delta_idx <= st_rps_idx); + } + + int ref_rps_idx = st_rps_idx - delta_idx; + DCHECK_LE(0, ref_rps_idx); + DCHECK_LT(ref_rps_idx, st_rps_idx); + + bool delta_rps_sign; + int abs_delta_rps_minus1; + TRUE_OR_RETURN(br->ReadBool(&delta_rps_sign)); + TRUE_OR_RETURN(br->ReadUE(&abs_delta_rps_minus1)); + int delta_rps = + delta_rps_sign ? -(abs_delta_rps_minus1 + 1) : abs_delta_rps_minus1 + 1; + + int ref_num_delta_pocs = ref_pic_sets[ref_rps_idx].num_delta_pocs; + std::vector used_by_curr_pic(ref_num_delta_pocs + 1); + std::vector use_delta(ref_num_delta_pocs + 1); + for (int j = 0; j <= ref_num_delta_pocs; j++) { + bool temp; + TRUE_OR_RETURN(br->ReadBool(&temp)); + used_by_curr_pic[j] = temp; + + if (!used_by_curr_pic[j]) { + TRUE_OR_RETURN(br->ReadBool(&temp)); + use_delta[j] = temp; + } else { + use_delta[j] = true; + } + } + + int ref_num_positive_pics = ref_pic_sets[ref_rps_idx].num_positive_pics; + int ref_num_negative_pics = ref_pic_sets[ref_rps_idx].num_negative_pics; + int i; + + // Update list 0. + { + i = 0; + for (int j = ref_num_positive_pics - 1; j >= 0; j--) { + int d_poc = ref_pic_sets[ref_rps_idx].delta_poc_s1[j] + delta_rps; + if (d_poc < 0 && use_delta[ref_num_negative_pics + j]) { + out_ref_pic_set->delta_poc_s0[i] = d_poc; + out_ref_pic_set->used_by_curr_pic_s0[i] = + used_by_curr_pic[ref_num_negative_pics + j]; + i++; + } + } + if (delta_rps < 0 && use_delta[ref_num_delta_pocs]) { + out_ref_pic_set->delta_poc_s0[i] = delta_rps; + out_ref_pic_set->used_by_curr_pic_s0[i] = + used_by_curr_pic[ref_num_delta_pocs]; + i++; + } + for (int j = 0; j < ref_num_negative_pics; j++) { + int d_poc = ref_pic_sets[ref_rps_idx].delta_poc_s0[j] + delta_rps; + if (d_poc < 0 && use_delta[j]) { + out_ref_pic_set->delta_poc_s0[i] = d_poc; + out_ref_pic_set->used_by_curr_pic_s0[i] = used_by_curr_pic[j]; + i++; + } + } + out_ref_pic_set->num_negative_pics = i; + } + + // Update list 1. + { + i = 0; + for (int j = ref_num_negative_pics - 1; j >= 0; j--) { + int d_poc = ref_pic_sets[ref_rps_idx].delta_poc_s0[j] + delta_rps; + if (d_poc > 0 && use_delta[j]) { + out_ref_pic_set->delta_poc_s1[i] = d_poc; + out_ref_pic_set->used_by_curr_pic_s1[i] = used_by_curr_pic[j]; + i++; + } + } + if (delta_rps > 0 && use_delta[ref_num_delta_pocs]) { + out_ref_pic_set->delta_poc_s1[i] = delta_rps; + out_ref_pic_set->used_by_curr_pic_s1[i] = + used_by_curr_pic[ref_num_delta_pocs]; + i++; + } + for (int j = 0; j < ref_num_positive_pics; j++) { + int d_poc = ref_pic_sets[ref_rps_idx].delta_poc_s1[j] + delta_rps; + if (d_poc > 0 && use_delta[ref_num_negative_pics + j]) { + out_ref_pic_set->delta_poc_s1[i] = d_poc; + out_ref_pic_set->used_by_curr_pic_s1[i] = + used_by_curr_pic[ref_num_negative_pics + j]; + i++; + } + } + out_ref_pic_set->num_positive_pics = i; + } + } else { + TRUE_OR_RETURN(br->ReadUE(&out_ref_pic_set->num_negative_pics)); + TRUE_OR_RETURN(br->ReadUE(&out_ref_pic_set->num_positive_pics)); + + int prev_poc = 0; + for (int i = 0; i < out_ref_pic_set->num_negative_pics; i++) { + int delta_poc_s0_minus1; + TRUE_OR_RETURN(br->ReadUE(&delta_poc_s0_minus1)); + out_ref_pic_set->delta_poc_s0[i] = prev_poc - (delta_poc_s0_minus1 + 1); + prev_poc = out_ref_pic_set->delta_poc_s0[i]; + + TRUE_OR_RETURN(br->ReadBool(&out_ref_pic_set->used_by_curr_pic_s0[i])); + } + + prev_poc = 0; + for (int i = 0; i < out_ref_pic_set->num_positive_pics; i++) { + int delta_poc_s1_minus1; + TRUE_OR_RETURN(br->ReadUE(&delta_poc_s1_minus1)); + out_ref_pic_set->delta_poc_s1[i] = prev_poc + delta_poc_s1_minus1 + 1; + prev_poc = out_ref_pic_set->delta_poc_s1[i]; + + TRUE_OR_RETURN(br->ReadBool(&out_ref_pic_set->used_by_curr_pic_s1[i])); + } + } + + out_ref_pic_set->num_delta_pocs = + out_ref_pic_set->num_positive_pics + out_ref_pic_set->num_negative_pics; + return kOk; +} + +H265Parser::Result H265Parser::SkipReferencePictureListModification( + const H265SliceHeader& slice_header, + const H265Pps& pps, + int num_pic_total_curr, + H26xBitReader* br) { + // Reads whole element but ignores it all. + + bool ref_pic_list_modification_flag_l0; + TRUE_OR_RETURN(br->ReadBool(&ref_pic_list_modification_flag_l0)); + if (ref_pic_list_modification_flag_l0) { + for (int i = 0; i <= pps.num_ref_idx_l0_default_active_minus1; i++) { + TRUE_OR_RETURN(br->SkipBits(ceil(log2(num_pic_total_curr)))); + } + } + + if (slice_header.slice_type == kBSlice) { + bool ref_pic_list_modification_flag_l1; + TRUE_OR_RETURN(br->ReadBool(&ref_pic_list_modification_flag_l1)); + if (ref_pic_list_modification_flag_l1) { + for (int i = 0; i <= pps.num_ref_idx_l1_default_active_minus1; i++) { + TRUE_OR_RETURN(br->SkipBits(ceil(log2(num_pic_total_curr)))); + } + } + } + + return kOk; +} + +H265Parser::Result H265Parser::SkipPredictionWeightTablePart( + int num_ref_idx_minus1, + int chroma_array_type, + H26xBitReader* br) { + // Reads whole element, ignores it. + int ignored; + std::vector luma_weight_flag(num_ref_idx_minus1 + 1); + std::vector chroma_weight_flag(num_ref_idx_minus1 + 1); + + for (int i = 0; i <= num_ref_idx_minus1; i++) { + bool temp; + TRUE_OR_RETURN(br->ReadBool(&temp)); + luma_weight_flag[i] = temp; + } + if (chroma_array_type != 0) { + for (int i = 0; i <= num_ref_idx_minus1; i++) { + bool temp; + TRUE_OR_RETURN(br->ReadBool(&temp)); + chroma_weight_flag[i] = temp; + } + } + for (int i = 0; i <= num_ref_idx_minus1; i++) { + if (luma_weight_flag[i]) { + TRUE_OR_RETURN(br->ReadSE(&ignored)); // delta_luma_weight_l# + TRUE_OR_RETURN(br->ReadSE(&ignored)); // luma_offset_l# + } + if (chroma_weight_flag[i]) { + for (int j = 0; j < 2; j++) { + TRUE_OR_RETURN(br->ReadSE(&ignored)); // delta_chroma_weight_l# + TRUE_OR_RETURN(br->ReadSE(&ignored)); // delta_chroma_offset_l# + } + } + } + + return kOk; +} + +H265Parser::Result H265Parser::SkipPredictionWeightTable( + bool is_b_slice, + const H265Sps& sps, + const H265SliceHeader& slice_header, + H26xBitReader* br) { + // Reads whole element, ignores it. + int ignored; + int chroma_array_type = sps.GetChromaArrayType(); + + TRUE_OR_RETURN(br->ReadUE(&ignored)); // luma_log2_weight_denom + if (chroma_array_type != 0) { + TRUE_OR_RETURN(br->ReadSE(&ignored)); // delta_chroma_log2_weight_denom + } + OK_OR_RETURN(SkipPredictionWeightTablePart( + slice_header.num_ref_idx_l0_active_minus1, chroma_array_type, br)); + if (is_b_slice) { + OK_OR_RETURN(SkipPredictionWeightTablePart( + slice_header.num_ref_idx_l1_active_minus1, chroma_array_type, br)); + } + + return kOk; +} + +H265Parser::Result H265Parser::SkipProfileTierLevel( + bool profile_present, + int max_num_sub_layers_minus1, + H26xBitReader* br) { + // Reads whole element, ignores it. + + if (profile_present) { + // general_profile_space, general_tier_flag, general_profile_idc + // general_profile_compativility_flag + // general_progressive_source_flag + // general_interlaced_source_flag + // general_non_packed_constraint_flag + // general_frame_only_constraint_flag + // 44-bits of other flags + TRUE_OR_RETURN(br->SkipBits(2 + 1 + 5 + 32 + 4 + 44)); + } + + TRUE_OR_RETURN(br->SkipBits(8)); // general_level_idc + + std::vector sub_layer_profile_present(max_num_sub_layers_minus1); + std::vector sub_layer_level_present(max_num_sub_layers_minus1); + for (int i = 0; i < max_num_sub_layers_minus1; i++) { + bool profile, level; + TRUE_OR_RETURN(br->ReadBool(&profile)); + TRUE_OR_RETURN(br->ReadBool(&level)); + sub_layer_profile_present[i] = profile; + sub_layer_level_present[i] = level; + } + + if (max_num_sub_layers_minus1 > 0) { + for (int i = max_num_sub_layers_minus1; i < 8; i++) + TRUE_OR_RETURN(br->SkipBits(2)); // reserved_zero_2bits + } + + for (int i = 0; i < max_num_sub_layers_minus1; i++) { + if (sub_layer_profile_present[i]) { + // sub_layer_profile_space, sub_layer_tier_flag, sub_layer_profile_idc + // sub_layer_profile_compatibility + // sub_layer_reserved_zero_43bits + // sub_layer_reserved_zero_bit + TRUE_OR_RETURN(br->SkipBits(2 + 1 + 5 + 32 + 4 + 43 + 1)); + } + if (sub_layer_level_present[i]) { + TRUE_OR_RETURN(br->SkipBits(8)); + } + } + + return kOk; +} + +H265Parser::Result H265Parser::SkipScalingListData(H26xBitReader* br) { + // Reads whole element, ignores it. + int ignored; + for (int size_id = 0; size_id < 4; size_id++) { + for (int matrix_id = 0; matrix_id < 6; + matrix_id += ((size_id == 3) ? 3 : 1)) { + bool scaling_list_pred_mode; + TRUE_OR_RETURN(br->ReadBool(&scaling_list_pred_mode)); + if (!scaling_list_pred_mode) { + // scaling_list_pred_matrix_id_delta + TRUE_OR_RETURN(br->ReadUE(&ignored)); + } else { + int coefNum = std::min(64, (1 << (4 + (size_id << 1)))); + if (size_id > 1) { + TRUE_OR_RETURN(br->ReadSE(&ignored)); // scaling_list_dc_coef_minus8 + } + + for (int i = 0; i < coefNum; i++) { + TRUE_OR_RETURN(br->ReadSE(&ignored)); // scaling_list_delta_coef + } + } + } + } + + return kOk; +} + +} // namespace media +} // namespace edash_packager diff --git a/packager/media/filters/h265_parser.h b/packager/media/filters/h265_parser.h new file mode 100644 index 0000000000..e70cf1fdb0 --- /dev/null +++ b/packager/media/filters/h265_parser.h @@ -0,0 +1,320 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#ifndef MEDIA_FILTERS_H265_PARSER_H_ +#define MEDIA_FILTERS_H265_PARSER_H_ + +#include +#include + +#include "packager/base/memory/scoped_ptr.h" +#include "packager/media/filters/h26x_bit_reader.h" + +namespace edash_packager { +namespace media { + +class Nalu; + +enum H265SliceType { kBSlice = 0, kPSlice = 1, kISlice = 2 }; + +const int kMaxRefPicSetCount = 16; + +struct H265ReferencePictureSet { + int delta_poc_s0[kMaxRefPicSetCount]; + int delta_poc_s1[kMaxRefPicSetCount]; + bool used_by_curr_pic_s0[kMaxRefPicSetCount]; + bool used_by_curr_pic_s1[kMaxRefPicSetCount]; + + int num_negative_pics; + int num_positive_pics; + int num_delta_pocs; +}; + +struct H265Pps { + H265Pps(); + ~H265Pps(); + + // Many of the fields here are required when parsing so the default here may + // not be valid. + + int pic_parameter_set_id = 0; + int seq_parameter_set_id = 0; + + bool dependent_slice_segments_enabled_flag = false; + bool output_flag_present_flag = false; + int num_extra_slice_header_bits = 0; + bool sign_data_hiding_enabled_flag = false; + bool cabac_init_present_flag = false; + + int num_ref_idx_l0_default_active_minus1 = 0; + int num_ref_idx_l1_default_active_minus1 = 0; + int init_qp_minus26 = 0; + bool constrained_intra_pred_flag = false; + bool transform_skip_enabled_flag = false; + + bool cu_qp_delta_enabled_flag = 0; + int diff_cu_qp_delta_depth = 0; + int cb_qp_offset = 0; + int cr_qp_offset = 0; + + bool slice_chroma_qp_offsets_present_flag = false; + bool weighted_pred_flag = false; + bool weighted_bipred_flag = false; + bool transquant_bypass_enabled_flag = false; + bool tiles_enabled_flag = false; + bool entropy_coding_sync_enabled_flag = false; + + int num_tile_columns_minus1 = 0; + int num_tile_rows_minus1 = 0; + bool uniform_spacing_flag = true; + std::vector column_width_minus1; + std::vector row_height_minus1; + bool loop_filter_across_tiles_enabled_flag = true; + + bool loop_filter_across_slices_enabled_flag = false; + bool deblocking_filter_control_present_flag = false; + bool deblocking_filter_override_enabled_flag = false; + bool deblocking_filter_disabled_flag = false; + int beta_offset_div2 = 0; + int tc_offset_div2 = 0; + + bool scaling_list_data_present_flag = false; + // Ignored: scaling_list_data( ) + + bool lists_modification_present_flag = false; + int log2_parallel_merge_level_minus2 = 0; + bool slice_segment_header_extension_present_flag = false; + + // Incomplete: pps_range_extension: + bool chroma_qp_offset_list_enabled_flag = false; + + // Ignored: extensions... +}; + +struct H265Sps { + H265Sps(); + ~H265Sps(); + + int GetPicSizeInCtbsY() const; + int GetChromaArrayType() const; + + // Many of the fields here are required when parsing so the default here may + // not be valid. + + int video_parameter_set_id = 0; + int max_sub_layers_minus1 = 0; + bool temporal_id_nesting_flag = false; + // Ignored: profile_tier_level(...) + + int seq_parameter_set_id = 0; + + int chroma_format_idc = 0; + bool separate_colour_plane_flag = false; + int pic_width_in_luma_samples = 0; + int pic_height_in_luma_samples = 0; + + bool conformance_window_flag = false; + int conf_win_left_offset = 0; + int conf_win_right_offset = 0; + int conf_win_top_offset = 0; + int conf_win_bottom_offset = 0; + + int bit_depth_luma_minus8 = 0; + int bit_depth_chroma_minus8 = 0; + int log2_max_pic_order_cnt_lsb_minus4 = 0; + + bool sub_layer_ordering_info_present_flag = false; + int max_dec_pic_buffering_minus1[8]; + int max_num_reorder_pics[8]; + int max_latency_increase_plus1[8]; + + int log2_min_luma_coding_block_size_minus3 = 0; + int log2_diff_max_min_luma_coding_block_size = 0; + int log2_min_luma_transform_block_size_minus2 = 0; + int log2_diff_max_min_luma_transform_block_size = 0; + int max_transform_hierarchy_depth_inter = 0; + int max_transform_hierarchy_depth_intra = 0; + + bool scaling_list_enabled_flag = false; + bool scaling_list_data_present_flag = false; + // Ignored: scaling_list_data() + + bool amp_enabled_flag = false; + bool sample_adaptive_offset_enabled_flag = false; + bool pcm_enabled_flag = false; + int pcm_sample_bit_depth_luma_minus1 = 0; + int pcm_sample_bit_depth_chroma_minus1 = 0; + int log2_min_pcm_luma_coding_block_size_minus3 = 0; + int log2_diff_max_min_pcm_luma_coding_block_size = 0; + bool pcm_loop_filter_disabled_flag = false; + + int num_short_term_ref_pic_sets = 0; + std::vector st_ref_pic_sets; + + bool long_term_ref_pic_present_flag = false; + int num_long_term_ref_pics = 0; + std::vector lt_ref_pic_poc_lsb; + std::vector used_by_curr_pic_lt_flag; + + bool temporal_mvp_enabled_flag = false; + bool strong_intra_smoothing_enabled_flag = false; + + // Ignored: extensions... +}; + +struct H265ReferencePictureListModifications { + H265ReferencePictureListModifications(); + ~H265ReferencePictureListModifications(); + + bool ref_pic_list_modification_flag_l0 = false; + std::vector list_entry_l0; + + bool ref_pic_list_modification_flag_l1 = false; + std::vector list_entry_l1; +}; + +struct H265SliceHeader { + H265SliceHeader(); + ~H265SliceHeader(); + + struct LongTermPicsInfo { + bool delta_poc_msb_present_flag; + int delta_poc_msb_cycle_lt; + }; + // This is the value UsedByCurrPicLt for the current slice segment. This + // value is calulated from the LongTermPicsInfo during parsing. + int used_by_curr_pic_lt = 0; + + // Many of the fields here are required when parsing so the default here may + // not be valid. + + int header_bit_size = 0; + + bool first_slice_segment_in_pic_flag = false; + bool no_output_of_prior_pics_flag = false; + int pic_parameter_set_id = 0; + + bool dependent_slice_segment_flag = false; + int segment_address = 0; + int slice_type = 0; + bool pic_output_flag = true; + int colour_plane_id = 0; + int slice_pic_order_cnt_lsb = 0; + + bool short_term_ref_pic_set_sps_flag = false; + H265ReferencePictureSet st_ref_pic_set; + int short_term_ref_pic_set_idx = 0; + + int num_long_term_sps = 0; + int num_long_term_pics = 0; + std::vector long_term_pics_info; + + bool slice_temporal_mvp_enabled_flag = false; + bool slice_sao_luma_flag = false; + bool slice_sao_chroma_flag = false; + + bool num_ref_idx_active_override_flag = false; + int num_ref_idx_l0_active_minus1 = 0; + int num_ref_idx_l1_active_minus1 = 0; + + H265ReferencePictureListModifications ref_pic_lists_modification; + + bool mvd_l1_zero_flag = false; + bool cabac_init_flag = false; + bool collocated_from_l0 = true; + int collocated_ref_idx = 0; + + int five_minus_max_num_merge_cand = 0; + int slice_qp_delta = 0; + int slice_cb_qp_offset = 0; + int slice_cr_qp_offset = 0; + + bool cu_chroma_qp_offset_enabled_flag = false; + bool deblocking_filter_override_flag = false; + bool slice_deblocking_filter_disabled_flag = false; + int slice_beta_offset_div2 = 0; + int slice_tc_offset_div2 = 0; + bool slice_loop_filter_across_slices_enabled_flag = false; + + int num_entry_point_offsets = 0; + int offset_len_minus1 = 0; + std::vector entry_point_offset_minus1; +}; + +/// A class to parse H.265 streams. This is incomplete and skips many pieces. +/// This will mostly parse PPS and SPS elements as well as fully parse a +/// slice header. +class H265Parser { + public: + enum Result { + kOk, + kInvalidStream, // error in stream + kUnsupportedStream, // stream not supported by the parser + kEOStream, // end of stream + }; + + H265Parser(); + ~H265Parser(); + + /// Parses a video slice header. If this returns kOk, then |*slice_header| + /// will contain the parsed header; if it returns something else, the + /// contents of |*slice_header| are undefined. + Result ParseSliceHeader(const Nalu& nalu, H265SliceHeader* slice_header); + + /// Parses a PPS element. This object is owned and managed by this class. + /// The unique ID of the parsed PPS is stored in |*pps_id| if kOk is returned. + Result ParsePps(const Nalu& nalu, int* pps_id); + /// Parses a SPS element. This object is owned and managed by this class. + /// The unique ID of the parsed SPS is stored in |*sps_id| if kOk is returned. + Result ParseSps(const Nalu& nalu, int* sps_id); + + /// @return a pointer to the PPS with the given ID, or NULL if none exists. + const H265Pps* GetPps(int pps_id); + /// @return a pointer to the SPS with the given ID, or NULL if none exists. + const H265Sps* GetSps(int sps_id); + + private: + Result ParseReferencePictureSet( + int num_short_term_ref_pic_sets, + int st_rpx_idx, + const std::vector& ref_pic_sets, + H26xBitReader* br, + H265ReferencePictureSet* st_ref_pic_set); + + Result SkipReferencePictureListModification( + const H265SliceHeader& slice_header, + const H265Pps& pps, + int num_pic_total_curr, + H26xBitReader* br); + + Result SkipPredictionWeightTablePart(int num_ref_idx_minus1, + int chroma_array_type, + H26xBitReader* br); + + Result SkipPredictionWeightTable(bool is_b_slice, + const H265Sps& sps, + const H265SliceHeader& slice_header, + H26xBitReader* br); + + Result SkipProfileTierLevel(bool profile_present, + int max_num_sub_layers_minus1, + H26xBitReader* br); + + Result SkipScalingListData(H26xBitReader* br); + + typedef std::map> SpsById; + typedef std::map> PpsById; + + SpsById active_spses_; + PpsById active_ppses_; + + DISALLOW_COPY_AND_ASSIGN(H265Parser); +}; + +} // namespace media +} // namespace edash_packager + +#endif // MEDIA_FILTERS_H265_PARSER_H_ diff --git a/packager/media/filters/h265_parser_unittest.cc b/packager/media/filters/h265_parser_unittest.cc new file mode 100644 index 0000000000..b7c7302bca --- /dev/null +++ b/packager/media/filters/h265_parser_unittest.cc @@ -0,0 +1,130 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include + +#include "packager/media/filters/h265_parser.h" +#include "packager/media/filters/nalu_reader.h" + +namespace edash_packager { +namespace media { +namespace H265 { + +namespace { + +// Data taken from bear-640x360-hevc.mp4 +const uint8_t kSpsData[] = { + 0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0xa0, 0x05, 0x02, 0x01, 0x69, 0x65, 0x95, 0xe4, 0x93, + 0x2b, 0xc0, 0x40, 0x40, 0x00, 0x00, 0xfa, 0x40, 0x00, 0x1d, 0x4c, 0x02}; +const uint8_t kPpsData[] = {0x44, 0x01, 0xc1, 0x73, 0xd1, 0x89}; +const uint8_t kSliceData[] = { + // Incomplete segment data. + 0x26, 0x01, 0xaf, 0x08, 0x4c, 0x2e, 0xa6, 0x56, 0xd9, 0xaf, 0x50, 0xeb, + 0x94, 0x9a, 0xae, 0x89, 0x29, 0x0e, 0x42, 0x9f, 0xb9, 0x5e, 0x85, 0xd5}; +const uint8_t kSliceData2[] = {0x02, 0x01, 0xd0, 0x29, 0xc9, 0xfd, 0x63, 0x22, + 0x52, 0x04, 0x06, 0x13, 0x3d, 0xc6, 0xf0, 0xb9, + 0x55, 0x98, 0xa0, 0x16, 0x57, 0xf6, 0xb8, 0x25}; + +} // namespace + +TEST(H265ParserTest, ParseSliceHeader) { + // Parse the SPS and PPS first so the data is available. + int id; + Nalu nalu; + H265Parser parser; + ASSERT_TRUE(nalu.InitializeFromH265(kSpsData, arraysize(kSpsData))); + ASSERT_EQ(H265Parser::kOk, parser.ParseSps(nalu, &id)); + ASSERT_TRUE(nalu.InitializeFromH265(kPpsData, arraysize(kPpsData))); + ASSERT_EQ(H265Parser::kOk, parser.ParsePps(nalu, &id)); + + // Parse the slice header. + ASSERT_TRUE(nalu.InitializeFromH265(kSliceData, arraysize(kSliceData))); + ASSERT_EQ(Nalu::H265_IDR_W_RADL, nalu.type()); + + H265SliceHeader header; + ASSERT_EQ(H265Parser::kOk, parser.ParseSliceHeader(nalu, &header)); + + EXPECT_TRUE(header.first_slice_segment_in_pic_flag); + EXPECT_EQ(0, header.pic_parameter_set_id); + EXPECT_FALSE(header.dependent_slice_segment_flag); + EXPECT_EQ(2, header.slice_type); + EXPECT_EQ(8, header.slice_qp_delta); + EXPECT_FALSE(header.cu_chroma_qp_offset_enabled_flag); + EXPECT_EQ(5, header.num_entry_point_offsets); + EXPECT_EQ(85, header.header_bit_size); +} + +TEST(H265ParserTest, ParseSliceHeader_NonIDR) { + // Parse the SPS and PPS first so the data is available. + int id; + Nalu nalu; + H265Parser parser; + ASSERT_TRUE(nalu.InitializeFromH265(kSpsData, arraysize(kSpsData))); + ASSERT_EQ(H265Parser::kOk, parser.ParseSps(nalu, &id)); + ASSERT_TRUE(nalu.InitializeFromH265(kPpsData, arraysize(kPpsData))); + ASSERT_EQ(H265Parser::kOk, parser.ParsePps(nalu, &id)); + + // Parse the slice header. + ASSERT_TRUE(nalu.InitializeFromH265(kSliceData2, arraysize(kSliceData2))); + ASSERT_EQ(1 /* TRAIL_R */, nalu.type()); + + H265SliceHeader header; + ASSERT_EQ(H265Parser::kOk, parser.ParseSliceHeader(nalu, &header)); + + EXPECT_TRUE(header.first_slice_segment_in_pic_flag); + EXPECT_EQ(0, header.pic_parameter_set_id); + EXPECT_FALSE(header.dependent_slice_segment_flag); + EXPECT_EQ(1, header.slice_type); + EXPECT_EQ(5, header.num_entry_point_offsets); + EXPECT_EQ(124, header.header_bit_size); +} + +TEST(H265ParserTest, ParseSps) { + Nalu nalu; + ASSERT_TRUE(nalu.InitializeFromH265(kSpsData, arraysize(kSpsData))); + ASSERT_EQ(Nalu::H265_SPS, nalu.type()); + + int id = 12; + H265Parser parser; + ASSERT_EQ(H265Parser::kOk, parser.ParseSps(nalu, &id)); + ASSERT_EQ(0, id); + + const H265Sps* sps = parser.GetSps(id); + ASSERT_TRUE(sps); + + EXPECT_EQ(0, sps->video_parameter_set_id); + EXPECT_EQ(0, sps->max_sub_layers_minus1); + EXPECT_EQ(0, sps->seq_parameter_set_id); + EXPECT_EQ(1, sps->chroma_format_idc); + EXPECT_EQ(360, sps->pic_height_in_luma_samples); + EXPECT_EQ(4, sps->log2_max_pic_order_cnt_lsb_minus4); + EXPECT_EQ(3, sps->log2_diff_max_min_luma_transform_block_size); + EXPECT_EQ(0, sps->max_transform_hierarchy_depth_intra); +} + +TEST(H265ParserTest, ParsePps) { + Nalu nalu; + ASSERT_TRUE(nalu.InitializeFromH265(kPpsData, arraysize(kPpsData))); + ASSERT_EQ(Nalu::H265_PPS, nalu.type()); + + int id = 12; + H265Parser parser; + ASSERT_EQ(H265Parser::kOk, parser.ParsePps(nalu, &id)); + ASSERT_EQ(0, id); + + const H265Pps* pps = parser.GetPps(id); + ASSERT_TRUE(pps); + + EXPECT_EQ(0, pps->num_extra_slice_header_bits); + EXPECT_TRUE(pps->weighted_pred_flag); + EXPECT_FALSE(pps->scaling_list_data_present_flag); + EXPECT_EQ(0, pps->log2_parallel_merge_level_minus2); +} + +} // namespace H265 +} // namespace media +} // namespace edash_packager diff --git a/packager/media/filters/h26x_bit_reader.cc b/packager/media/filters/h26x_bit_reader.cc index 68eb100123..2a8cbd75e0 100644 --- a/packager/media/filters/h26x_bit_reader.cc +++ b/packager/media/filters/h26x_bit_reader.cc @@ -86,6 +86,18 @@ bool H26xBitReader::ReadBits(int num_bits, int* out) { return true; } +bool H26xBitReader::SkipBits(int num_bits) { + int bits_left = num_bits; + while (num_remaining_bits_in_curr_byte_ < bits_left) { + bits_left -= num_remaining_bits_in_curr_byte_; + if (!UpdateCurrByte()) + return false; + } + + num_remaining_bits_in_curr_byte_ -= bits_left; + return true; +} + bool H26xBitReader::ReadUE(int* val) { int num_bits = -1; int bit; diff --git a/packager/media/filters/h26x_bit_reader.h b/packager/media/filters/h26x_bit_reader.h index 4ecebd1317..65cb8e8ad9 100644 --- a/packager/media/filters/h26x_bit_reader.h +++ b/packager/media/filters/h26x_bit_reader.h @@ -15,7 +15,7 @@ namespace edash_packager { namespace media { -// A class to provide bit-granularity reading of H.264 streams. +// A class to provide bit-granularity reading of H.264/H.265 streams. // This is not a generic bit reader class, as it takes into account // H.264 stream-specific constraints, such as skipping emulation-prevention // bytes and stop bits. See spec for more details. @@ -36,6 +36,21 @@ class H26xBitReader { // bits in the stream), true otherwise. bool ReadBits(int num_bits, int* out); + // Read a single bit and return in |*out|. + // Return false if the bit cannot be read (not enough bits in the stream), + // true otherwise. + bool ReadBool(bool* out) { + int value; + if (!ReadBits(1, &value)) + return false; + *out = (value != 0); + return true; + } + + // Skips the given number of bits (does not have to be less than 32 bits). + // Return false if there aren't enough bits in the stream, true otherwise. + bool SkipBits(int num_bits); + // Exp-Golomb code parsing as specified in chapter 9.1 of the spec. // Read one unsigned exp-Golomb code from the stream and return in |*val|. bool ReadUE(int* val); diff --git a/packager/media/filters/h26x_bit_reader_unittest.cc b/packager/media/filters/h26x_bit_reader_unittest.cc index 6ec6bdaf72..200fd68551 100644 --- a/packager/media/filters/h26x_bit_reader_unittest.cc +++ b/packager/media/filters/h26x_bit_reader_unittest.cc @@ -57,6 +57,46 @@ TEST(H26xBitReaderTest, SingleByteStream) { EXPECT_FALSE(reader.HasMoreRBSPData()); } +TEST(H26xBitReaderTest, ReadBool) { + H26xBitReader reader; + const unsigned char rbsp[] = {0xc5}; + bool dummy = false; + + EXPECT_TRUE(reader.Initialize(rbsp, sizeof(rbsp))); + EXPECT_EQ(reader.NumBitsLeft(), 8); + + EXPECT_TRUE(reader.ReadBool(&dummy)); + EXPECT_TRUE(dummy); + EXPECT_TRUE(reader.ReadBool(&dummy)); + EXPECT_TRUE(dummy); + EXPECT_TRUE(reader.ReadBool(&dummy)); + EXPECT_FALSE(dummy); + EXPECT_TRUE(reader.ReadBool(&dummy)); + EXPECT_FALSE(dummy); + + EXPECT_EQ(reader.NumBitsLeft(), 4); +} + +TEST(H26xBitReaderTest, SkipBits) { + H26xBitReader reader; + const unsigned char rbsp[] = {0xc5, 0x41, 0x51}; + int dummy; + + EXPECT_TRUE(reader.Initialize(rbsp, sizeof(rbsp))); + EXPECT_EQ(reader.NumBitsLeft(), 24); + + EXPECT_TRUE(reader.SkipBits(3)); + EXPECT_EQ(21, reader.NumBitsLeft()); + EXPECT_TRUE(reader.ReadBits(4, &dummy)); + EXPECT_EQ(0x2, dummy); + EXPECT_TRUE(reader.SkipBits(8)); + EXPECT_EQ(9, reader.NumBitsLeft()); + EXPECT_TRUE(reader.ReadBits(5, &dummy)); + EXPECT_EQ(0x15, dummy); + EXPECT_EQ(4, reader.NumBitsLeft()); + EXPECT_FALSE(reader.SkipBits(5)); +} + TEST(H26xBitReaderTest, StopBitOccupyFullByte) { H26xBitReader reader; const unsigned char rbsp[] = {0xab, 0x80}; diff --git a/packager/media/formats/mp4/encrypting_fragmenter.cc b/packager/media/formats/mp4/encrypting_fragmenter.cc index efc522cd76..d6c17f507e 100644 --- a/packager/media/formats/mp4/encrypting_fragmenter.cc +++ b/packager/media/formats/mp4/encrypting_fragmenter.cc @@ -80,8 +80,12 @@ EncryptingFragmenter::EncryptingFragmenter( vpx_parser_.reset(new VP9Parser); } else if (video_codec_ == kCodecH264) { header_parser_.reset(new H264VideoSliceHeaderParser); + } else if (video_codec_ == kCodecHVC1 || video_codec_ == kCodecHEV1) { + header_parser_.reset(new H265VideoSliceHeaderParser); + } else if (nalu_length_size_ > 0) { + LOG(WARNING) << "Unknown video codec '" << video_codec_ + << "', whole subsamples will be encrypted."; } - // TODO(modmaker): Support H.265. } EncryptingFragmenter::~EncryptingFragmenter() {} diff --git a/packager/media/formats/mp4/video_slice_header_parser.cc b/packager/media/formats/mp4/video_slice_header_parser.cc index ba914cf721..df03b2b6ac 100644 --- a/packager/media/formats/mp4/video_slice_header_parser.cc +++ b/packager/media/formats/mp4/video_slice_header_parser.cc @@ -7,12 +7,23 @@ #include "packager/media/formats/mp4/video_slice_header_parser.h" #include "packager/media/filters/avc_decoder_configuration.h" +#include "packager/media/filters/hevc_decoder_configuration.h" #include "packager/media/formats/mp4/rcheck.h" namespace edash_packager { namespace media { namespace mp4 { +namespace { + +int NumBitsToNumBytes(int size_in_bits) { + // Round-up division. + DCHECK_GE(size_in_bits, 0); + return (size_in_bits - 1) / 8 + 1; +} + +} // namespace + H264VideoSliceHeaderParser::H264VideoSliceHeaderParser() {} H264VideoSliceHeaderParser::~H264VideoSliceHeaderParser() {} @@ -41,8 +52,42 @@ int64_t H264VideoSliceHeaderParser::GetHeaderSize(const Nalu& nalu) { if (parser_.ParseSliceHeader(nalu, &slice_header) != H264Parser::kOk) return -1; - // Round-up to bytes. - return (slice_header.header_bit_size - 1) / 8 + 1; + return NumBitsToNumBytes(slice_header.header_bit_size); +} + +H265VideoSliceHeaderParser::H265VideoSliceHeaderParser() {} +H265VideoSliceHeaderParser::~H265VideoSliceHeaderParser() {} + +bool H265VideoSliceHeaderParser::Initialize( + const std::vector& decoder_configuration) { + int id; + HEVCDecoderConfiguration hevc_config; + RCHECK(hevc_config.Parse(decoder_configuration)); + + for (size_t i = 0; i < hevc_config.nalu_count(); i++) { + const Nalu& nalu = hevc_config.nalu(i); + if (nalu.type() == Nalu::H265_SPS) { + RCHECK(parser_.ParseSps(nalu, &id) == H265Parser::kOk); + } else if (nalu.type() == Nalu::H265_PPS) { + RCHECK(parser_.ParsePps(nalu, &id) == H265Parser::kOk); + } else if (nalu.type() == Nalu::H265_VPS) { + // Ignore since it does not affect video slice header parsing. + } else { + VLOG(1) << "Ignoring decoder configuration Nalu of unknown type " + << nalu.type(); + } + } + + return true; +} + +int64_t H265VideoSliceHeaderParser::GetHeaderSize(const Nalu& nalu) { + DCHECK(nalu.is_video_slice()); + H265SliceHeader slice_header; + if (parser_.ParseSliceHeader(nalu, &slice_header) != H265Parser::kOk) + return -1; + + return NumBitsToNumBytes(slice_header.header_bit_size); } } // namespace mp4 diff --git a/packager/media/formats/mp4/video_slice_header_parser.h b/packager/media/formats/mp4/video_slice_header_parser.h index c78e35b809..01a0db06fc 100644 --- a/packager/media/formats/mp4/video_slice_header_parser.h +++ b/packager/media/formats/mp4/video_slice_header_parser.h @@ -11,6 +11,7 @@ #include "packager/media/base/macros.h" #include "packager/media/filters/h264_parser.h" +#include "packager/media/filters/h265_parser.h" namespace edash_packager { namespace media { @@ -50,7 +51,22 @@ class H264VideoSliceHeaderParser : public VideoSliceHeaderParser { DISALLOW_COPY_AND_ASSIGN(H264VideoSliceHeaderParser); }; -// TODO(modmaker): Add H.265 parser. +class H265VideoSliceHeaderParser : public VideoSliceHeaderParser { + public: + H265VideoSliceHeaderParser(); + ~H265VideoSliceHeaderParser() override; + + /// @name VideoSliceHeaderParser implementation overrides. + /// @{ + bool Initialize(const std::vector& decoder_configuration) override; + int64_t GetHeaderSize(const Nalu& nalu) override; + /// @} + + private: + H265Parser parser_; + + DISALLOW_COPY_AND_ASSIGN(H265VideoSliceHeaderParser); +}; } // namespace mp4 } // namespace media