On Tue, Dec 22, 2020 at 6:53 AM Mark Thompson <s...@jkqxz.net> wrote:
> On 21/12/2020 06:07, Nuo Mi wrote: > > --- > > libavcodec/Makefile | 1 + > > libavcodec/cbs.c | 6 + > > libavcodec/cbs_h2645.c | 337 ++++++ > > libavcodec/cbs_h266.h | 711 ++++++++++++ > > libavcodec/cbs_h266_syntax_template.c | 1493 +++++++++++++++++++++++++ > > libavcodec/cbs_internal.h | 1 + > > 6 files changed, 2549 insertions(+) > > create mode 100644 libavcodec/cbs_h266.h > > create mode 100644 libavcodec/cbs_h266_syntax_template.c > > > > diff --git a/libavcodec/Makefile b/libavcodec/Makefile > > index 450781886d..4045c002b7 100644 > > --- a/libavcodec/Makefile > > +++ b/libavcodec/Makefile > > > > > > +#define FUNC(name) FUNC_H266(READWRITE, name) > > +#include "cbs_h266_syntax_template.c" > > +#undef FUNC > > + > > #undef READ > > #undef READWRITE > > #undef RWContext > > Please include the second time for write support as well. (With only read > this is incredibly hard to test.) > Done > > > > > +static int cbs_h266_read_nal_unit(CodedBitstreamContext *ctx, > > + CodedBitstreamUnit *unit) > > +{ > > + GetBitContext gbc; > > + int err; > > + > > + err = init_get_bits(&gbc, unit->data, 8 * unit->data_size); > > init_get_bits8() > Done > > > + if (err < 0) > > + return err; > > + > > + err = ff_cbs_alloc_unit_content2(ctx, unit); > > + if (err < 0) > > + return err; > > + > > + switch (unit->type) { > > + case H266_NAL_VPS: > > + { > > + H266RawVPS *vps = unit->content; > > + > > + err = cbs_h266_read_vps(ctx, &gbc, vps); > > This isn't implemented, so don't include it here so it returns ENOSYS. > Done > > > > + > > + case H266_NAL_PPS: > > + { > > + H266RawPPS *pps = unit->content; > > + > > + err = cbs_h266_read_pps(ctx, &gbc, pps); > > + if (err < 0) > > + return err; > > + > > + err = cbs_h266_replace_pps(ctx, unit); > > + if (err < 0) > > + return err; > > + } > > + break; > > + > > + case H266_NAL_TRAIL: > > + case H266_NAL_STSA: > > + case H266_NAL_RADL: > > Missing case H266_NUT_RASL. > Fixed > > > + > > + case H266_NAL_PREFIX_SEI: > > + case H266_NAL_SUFFIX_SEI: > > + { > > + err = cbs_h266_read_sei(ctx, &gbc, unit->content, > > + unit->type == H266_NAL_PREFIX_SEI); > > + > > + if (err < 0) > > + return err; > > + } > > + break; > > Don't include SEI at all given that you haven't implemented it; just let > it return ENOSYS. Implemented. > > + > > + default: > > + return AVERROR(ENOSYS); > > + } > > + return 0; > > +} > > + > > static int cbs_h2645_write_slice_data(CodedBitstreamContext *ctx, > > PutBitContext *pbc, const > uint8_t *data, > > size_t data_size, int > data_bit_start) > > @@ -1207,6 +1328,120 @@ static int > cbs_h265_write_nal_unit(CodedBitstreamContext *ctx, > > return 0; > > } > > > > +static int cbs_h266_write_nal_unit(CodedBitstreamContext *ctx, > > + CodedBitstreamUnit *unit, > > + PutBitContext *pbc) > > +{ > > + printf("TODO: cbs_h266_write_nal_unit"); > > Yes. > Implemented. > > > > > +static void cbs_h266_close(CodedBitstreamContext *ctx) > > +{ > > + CodedBitstreamH266Context *h266 = ctx->priv_data; > > + int i; > > + > > + ff_h2645_packet_uninit(&h266->common.read_packet); > > + > > + for (i = 0; i < FF_ARRAY_ELEMS(h266->vps); i++) > > + av_buffer_unref(&h266->vps_ref[i]); > > + for (i = 0; i < FF_ARRAY_ELEMS(h266->sps); i++) > > + av_buffer_unref(&h266->sps_ref[i]); > > + for (i = 0; i < FF_ARRAY_ELEMS(h266->pps); i++) > > + av_buffer_unref(&h266->pps_ref[i]); > > These loops look like exactly what flush does. > Fixed, will call flush, I will send a separate patch for h264/h265 > > > +} > > + > > static void cbs_h264_free_sei_payload(H264RawSEIPayload *payload) > > { > > switch (payload->payload_type) { > > @@ -1506,6 +1778,55 @@ static const CodedBitstreamUnitTypeDescriptor > cbs_h265_unit_types[] = { > > CBS_UNIT_TYPE_END_OF_LIST > > }; > > > > +static void cbs_h266_free_sei(void *opaque, uint8_t *content) > > +{ > > +} > > + > > +static const CodedBitstreamUnitTypeDescriptor cbs_h266_unit_types[] = { > > + CBS_UNIT_TYPE_INTERNAL_REF(H266_NAL_VPS, H266RawVPS, > extension_data.data), > > + CBS_UNIT_TYPE_INTERNAL_REF(H266_NAL_SPS, H266RawSPS, > extension_data.data), > > + CBS_UNIT_TYPE_INTERNAL_REF(H266_NAL_PPS, H266RawPPS, > extension_data.data), > > + > > + CBS_UNIT_TYPE_POD(H266_NAL_AUD, H266RawAUD), > > + > > + { > > + // Slices of non-IRAP pictures. > > + .nb_unit_types = CBS_UNIT_TYPE_RANGE, > > + .unit_type_range_start = H266_NAL_TRAIL, > > + .unit_type_range_end = H266_NAL_RASL, > > Using the list form would probably be clearer - there isn't the > proliferation of extra types like H.265. > Done > > > + > > + .content_type = CBS_CONTENT_TYPE_INTERNAL_REFS, > > + .content_size = sizeof(H266RawSlice), > > + .nb_ref_offsets = 1, > > + .ref_offsets = { offsetof(H266RawSlice, data) }, > > + }, > > + > > + { > > + // Slices of IRAP pictures. > > + .nb_unit_types = CBS_UNIT_TYPE_RANGE, > > + .unit_type_range_start = H266_NAL_IDR_W_RADL, > > + .unit_type_range_end = H266_NAL_GDR_NUT, > > Likewise here. > Done > > > + > > + .content_type = CBS_CONTENT_TYPE_INTERNAL_REFS, > > + .content_size = sizeof(H266RawSlice), > > + .nb_ref_offsets = 1, > > + .ref_offsets = { offsetof(H266RawSlice, data) }, > > + }, > > + > > + { > > + .nb_unit_types = 2, > > + .unit_types = { > > + H266_NAL_PREFIX_SEI, > > + H266_NAL_SUFFIX_SEI > > + }, > > + .content_type = CBS_CONTENT_TYPE_COMPLEX, > > + .content_size = sizeof(H266RawSEI), > > + .content_free = &cbs_h266_free_sei, > > + }, > > Don't include these if you didn't implement them. > implemented. > > > > > + > > +enum { > > + // This limit is arbitrary - it is sufficient for one message of > each > > + // type plus some repeats, and will therefore easily cover all sane > > + // streams. However, it is possible to make technically-valid > streams > > + // for which it will fail (for example, by including a large number > of > > + // user-data-unregistered messages). > > + H266_MAX_SEI_PAYLOADS = 64, > > +}; > > + > > In this file, please horizontally align things to improve readability. In > particular, the starts of all normal variables and all the matching arrays > would help quite a bit. > Done > > > +typedef struct H266RawNALUnitHeader { > > + uint8_t nuh_layer_id; > > + uint8_t nal_unit_type; > > + uint8_t nuh_temporal_id_plus1; > > +} H266RawNALUnitHeader; > > + > > +typedef struct H266GeneralConstraintsInfo { > > + uint8_t gci_present_flag; > > +} H266GeneralConstraintsInfo; > > + > > +typedef struct H266RawProfileTierLevel { > > + uint8_t general_profile_idc; > > + uint8_t general_tier_flag; > > + uint8_t general_level_idc; > > + uint8_t ptl_frame_only_constraint_flag; > > + uint8_t ptl_multilayer_enabled_flag; > > + H266GeneralConstraintsInfo general_constraints_info; > > + uint8_t ptl_sublayer_level_present_flag[H266_MAX_SUB_LAYERS - 1]; > > That constant should be renamed to H266_MAX_SUBLAYERS, the standard never > uses a space between them. > Done > > > + uint8_t sublayer_level_idc[H266_MAX_SUB_LAYERS - 1]; > > + uint8_t ptl_num_sub_profiles; > > + uint32_t general_sub_profile_idc[256]; > > I think add an H266_MAX_SUB_PROFILES for this size. > > Done > > > > + uint8_t num_ref_entries; > > + uint8_t ltrp_in_header_flag; > > + uint8_t inter_layer_ref_pic_flag[H266_MAX_DPB_SIZE + 13]; > > + uint8_t st_ref_pic_flag[H266_MAX_DPB_SIZE + 13]; > > + uint8_t abs_delta_poc_st[H266_MAX_DPB_SIZE + 13]; > > + uint8_t strp_entry_sign_flag[H266_MAX_DPB_SIZE + 13]; > > + uint8_t rpls_poc_lsb_lt[H266_MAX_DPB_SIZE + 13]; > > + uint8_t ilrp_idx[H266_MAX_DPB_SIZE + 13]; > > + > > + //calculated values; > > + uint8_t num_ltrp_entries; > > Derived values really shouldn't be in the bitstream struct if it can be > avoided. In this case, it's calculated and used inline so you don't need > to store it here. > Done, but I leave num_tile_columns, num_tile_rows, num_tiles_in_pic,slice_height_in_ctus, num_slices_in_subpic in H266RawPPS They are needed by later functions > > > > + > > +static int FUNC(profile_tier_level)(CodedBitstreamContext *ctx, > RWContext *rw, > > + H266RawProfileTierLevel *current, > > + int profile_tire_present_flag, > > tire? > Fixed > > > > + fixed(1, ptl_reserved_zero_bit, 0); > > + for( i = max_num_sub_layers_minus1 - 1; i >= 0; i-- ) > > The styling in this is inconsistent. Put a space after a keyword, but not > around parentheses (also in many more places below). > Fixed. > > > > + > > + left = vui_payload_size * 8 - (end_pos - start_pos); > > + skip_bits(rw, left); > > This needs to record and regenerate the extension data to avoid breaking > passthrough. (Like other extension data cases.) > Done > > > > + > > +static int FUNC(dpb_parameters)(CodedBitstreamContext *ctx, RWContext > *rw, > > + H266DpbParameters *current, uint8_t > max_sublayers_minus1, uint8_t sublayer_info_flag) > > +{ > > + int err, i; > > + for(i = ( sublayer_info_flag ? 0 : max_sublayers_minus1 ); i <= > max_sublayers_minus1; i++ ) { > > + ues(dpb_max_dec_pic_buffering_minus1[i], 0, H266_MAX_DPB_SIZE - > 1, 1, i); > > + ues(dpb_max_num_reorder_pics[i], 0, > current->dpb_max_dec_pic_buffering_minus1[i], 1, i); > > + ues(dpb_max_latency_increase_plus1[i], 0, UINT32_MAX - 1, 1, > i); > > Split lines to keep this to 80 characters. Aligning the matching fields > would be nice too. > Done > > > + > > +static int FUNC(ref_pic_list_struct)(CodedBitstreamContext *ctx, > RWContext *rw, > > + H266RefPicListStruct *current, uint8_t list_idx, > uint8_t rpls_idx, > > + const H266RawSPS* const sps) > > * is part of the declarator, not the type. We generally don't put > redundant const on variables, either. > Fixed. > > > +{ > > + > > + int err, i, j; > > + ue(num_ref_entries, 0, H266_MAX_DPB_SIZE + 13); > > These fields all have more subscripts than you've included here. Is there > a sensible way to make the trace output match the standard? > This will make the code more complex. > > + CodedBitstreamH266Context *h266 = ctx->priv_data; > > + const H266RawSPS *sps = h266->active_sps; > > + const H266RawPPS *pps = h266->active_pps; > > + const H266RefPicListStruct* ref_list; > > * > Done. > > > + int err, i, j; > > + for (i = 0; i < 2; i++) { > > + if (sps->sps_num_ref_pic_lists[i] > 0 && (i == 0 || (i == 1 && > pps->pps_rpl1_idx_present_flag))) { > > + flags(rpl_sps_flag[i], 1, i); > > + } else { > > + if (!sps->sps_num_ref_pic_lists[i]) { > > + infer(rpl_sps_flag[i], 0); > > + } else { > > + if (!pps->pps_rpl1_idx_present_flag && i == 1) > > + infer(rpl_sps_flag[1], current->rpl_sps_flag[0]); > > + } > > + } > > + if (current->rpl_sps_flag[i]) { > > + if(sps->sps_num_ref_pic_lists[i] > 1 && (i == 0 || ( i == 1 > && pps->pps_rpl1_idx_present_flag))) { > > + us(ceil(log2(sps->sps_num_ref_pic_lists[i])), > rpl_idx[i], 0, sps->sps_num_ref_pic_lists[i] - 1, 1, i); > > Don't use floating point arithmetic. There is av_log2() for integers. > Fixed. > > > + } else if (sps->sps_num_ref_pic_lists[i] == 1) { > > + infer(rpl_idx[i], 0); > > + } else if (i == 1 && !pps->pps_rpl1_idx_present_flag){ > > + infer(rpl_idx[1], current->rpl_idx[0]); > > + } else { > > + //how to handle this? or never happpend? > > This sort of thing needs to be resolved. > > I think the other conditions are a cover? > not sure, if this true, we can change "if (i == 1 && !pps->pps_rpl1_idx_present_flag)" to "else" Let us leave it here, maybe we can find some clips that goes to this branch, it will help us find the issue quickly. > > > > + if(current->general_du_hrd_params_present_flag) > > + ub(8, tick_divisor_minus2); > > + ub(4, bit_rate_scale); > > + ub(4, cpb_size_scale); > > + if(current->general_du_hrd_params_present_flag) > > + ub(4, cpb_size_du_scale); > > + ue(hrd_cpb_cnt_minus1, 0, 31); > > + } else { > > + //infer general_same_pic_timing_in_all_ols_flag? > > Looks like it should be, though the standard doesn't actually state it the > way it does with general_du_hrd_params_present_flag. > But not many places use this flag. let us leave the question mark here, it may help debug later. > > > > + > > + ub(4, sps_seq_parameter_set_id); > > + ub(4, sps_video_parameter_set_id); > > + h266->active_vps = vps = > h266->vps[current->sps_video_parameter_set_id]; > > Not if sps_video_parameter_set_id == 0. > > Should we be making an implied VPS in that case? > done > > > > + > > + flag(sps_conformance_window_flag); > > + if (current->sps_conformance_window_flag) { > > + ue(sps_conf_win_left_offset, 0, > current->sps_pic_width_max_in_luma_samples); > > + ue(sps_conf_win_right_offset, 0, > current->sps_pic_width_max_in_luma_samples); > > + ue(sps_conf_win_top_offset, 0, > current->sps_pic_height_max_in_luma_samples); > > + ue(sps_conf_win_bottom_offset, 0, > current->sps_pic_height_max_in_luma_samples); > > These bounds aren't right, because the value is post-subsampling. Divide > by SubWidthC. > Fixed. > > > + } else { > > + infer(sps_conf_win_left_offset, 0); > > + infer(sps_conf_win_right_offset, 0); > > + infer(sps_conf_win_top_offset, 0); > > + infer(sps_conf_win_bottom_offset, 0); > > + } > > + flag(sps_subpic_info_present_flag); > > + if (current->sps_subpic_info_present_flag) { > > + av_log(ctx->log_ctx, AV_LOG_ERROR, "subpic is not supported > yet\n"); > > + return AVERROR_PATCHWELCOME; > > + } else { > > + infer(sps_num_subpics_minus1, 0); > > + infer(sps_independent_subpics_flag, 1); > > + infer(sps_subpic_same_size_flag, 0); > > + //?sps_subpic_id_len_minus1; > > Doesn't matter, because subpic_id fields don't appear if no subpics. > Fixed > > > + infer(sps_subpic_id_mapping_explicitly_signalled_flag, 0); > > + > > + } > > + > > + ue(sps_bitdepth_minus8, 0, 2); > > + current->qp_bd_offset = 6 * current->sps_bitdepth_minus8; > > QpBdOffset isn't a bitstream value and is only used in this function, so > put it on the stack. > Fixed. > > > > + u(2, sps_num_extra_sh_bytes, 0, 0); > > + for (i = 0; i < (current->sps_num_extra_sh_bytes * 8 ); i++) { > > + flags(sps_extra_sh_bit_present_flag[i], 1, i); > > + } > > + current->num_extra_sh_bits = > get_extra_bits(current->sps_extra_sh_bit_present_flag, > current->sps_num_extra_sh_bytes); > > Don't include the derived NumExtra[PS]hBits fields in the raw SPS > structure. They can trivially be recalculated when used, so I think do it > there. > Fixed > > > > + > > + ue(sps_log2_diff_min_qt_min_cb_intra_slice_luma, 0, FFMIN(6, > ctb_log2_size_y) - min_cb_log2_size_y); > > + min_qt_log2_size_intra_y = > current->sps_log2_diff_min_qt_min_cb_intra_slice_luma + min_cb_log2_size_y; > > + > > + ue(sps_max_mtt_hierarchy_depth_intra_slice_luma, 0, 2 * > (ctb_log2_size_y - min_cb_log2_size_y)); > > + > > + if (current->sps_max_mtt_hierarchy_depth_intra_slice_luma) { > > Try to use the same styling as the standard as far as possible, so != 0 if > it isn't a flag. > Done > > > > + > > + flag(sps_transform_skip_enabled_flag); > > + if (current->sps_transform_skip_enabled_flag) { > > + ue(sps_log2_transform_skip_max_size_minus2, 0, 3); > > + flag(sps_bdpcm_enabled_flag); > > + } > > + > > + ... > > I haven't read through all of this in detail. It needs at least one other > person to check through and compare against the standard. > > > > + } > > + > > + flag(sps_extension_flag); > > + if (current->sps_extension_flag) { > > + return AVERROR_PATCHWELCOME; > > Also wanted for passthrough. > Done > > > > +static const uint8_t h266_sub_width_c[] = { > > + 1, 2, 2, 1 > > +}; > > + > > +static const uint8_t h266_sub_height_c[] = { > > + 1, 2, 1, 1 > > +}; > > You can't put fixed tables here, because the file is included twice. > > These can be inside the function below. > Fixed > > > + > > +static int get_num_tile(const uint16_t* pps_tile_size_minus1, uint16_t > num_exp_tile_minus1, unsigned int pic_size_in_ctbs_y, unsigned int > exp_tile_size) > > +{ > > + uint16_t uniform_size = pps_tile_size_minus1[num_exp_tile_minus1] + > 1; > > + unsigned int left = pic_size_in_ctbs_y - exp_tile_size; > > + return num_exp_tile_minus1 + 1 + (left + uniform_size - 1) / > uniform_size; > > +} > > Likewise functions. > > The derived tile values don't want to go in the bitstream value structure, > anyway. They can be calculated locally here. > It's not so easy to calc, also if cbs became the start point of the native decoder, we may need to put more derived value to sps,pps, slice header. > > > > + > > + if(!current->pps_no_pic_partition_flag ) { > > + unsigned int exp_tile_width = 0, exp_tile_height = 0; > > + pic_width_in_ctbs_y = > ceil(current->pps_pic_width_in_luma_samples / (double)ctb_size_y); > > + pic_height_in_ctbs_y = > ceil(current->pps_pic_height_in_luma_samples / (double)ctb_size_y); > > Don't use floating point. (int)ceil((double)a/b) == (a + b - 1) / b > Fixed. > > > + > > + > > + flag(aud_irap_or_gdr_flag); > > + ub(3, aud_pic_type); > > Range is 0 .. 2. > Fixed > > > > + flag(ph_inter_slice_allowed_flag); > > + if (current->ph_inter_slice_allowed_flag) > > + flag(ph_intra_slice_allowed_flag); > > + else > > + infer(ph_intra_slice_allowed_flag, 1); > > + ue(ph_pic_parameter_set_id, 0, 63); > > H266_MAX_PPS_COUNT - 1 > Fixed > > > + > > + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, > -1)); > > + > > + flag(sh_picture_header_in_slice_header_flag); > > + if (current->sh_picture_header_in_slice_header_flag){ > > + CHECK(FUNC(picture_header)(ctx, rw, &h266->ph)); > > That's not going to work. The picture header needs to be a substructure > of slice header to regenerate correctly. > > To fill in other slices, a copy might be needed. I'm not sure what the > best way of doing that is - maybe the other slices should fill their own > picture header with the original one as if inferred? > See description for sh_picture_header_in_slice_header_flag. One PU can only have one picture header, In the second version, I will read/write to slice->*sh_picture_header and copy back to *h266->ph > > + } > > + sps = h266->active_sps; > > + pps = h266->active_pps; > > + ph = &h266->ph; > > + > > + if (!sps || !pps) { > > + av_log(ctx->log_ctx, AV_LOG_ERROR, "no sps(%p) or pps(%p).\n", > sps, pps); > > + return AVERROR_INVALIDDATA; > > + } > > + > > + if(sps->sps_subpic_info_present_flag) > > + ub(sps->sps_subpic_id_len_minus1 + 1, sh_subpic_id); > > + if (pps->pps_rect_slice_flag) > > + return AVERROR_PATCHWELCOME; > > + if((!pps->pps_rect_slice_flag && pps->num_tiles_in_pic > 1)) { > > + unsigned int bits, max; > > + if (!pps->pps_rect_slice_flag) { > > + bits = ceil(log2(pps->num_tiles_in_pic)); > > Floating point. > Fixed > > > + max = pps->num_tiles_in_pic - 1; > > + } else { > > + return AVERROR_PATCHWELCOME; > > + } > > + u(bits, sh_slice_address, 0, max); > > + } else { > > + infer(sh_slice_address, 0); > > + } > > + > > + ... > > + if(pps->pps_slice_header_extension_present_flag) { > > + return AVERROR_PATCHWELCOME; > > + } > > + if(sps->sps_entry_point_offsets_present_flag) { > > + unsigned int num_entry_points = 0; > > + if (pps->pps_rect_slice_flag) { > > + return AVERROR_PATCHWELCOME; > > + } else { > > + unsigned int tile_idx; > > + for (tile_idx = current->sh_slice_address; tile_idx <= > current->sh_slice_address + current->sh_num_tiles_in_slice_minus1; > tile_idx++) { > > + unsigned int tile_y, height; > > + tile_y = tile_idx / pps->num_tile_rows; > > + height = pps->pps_tile_row_height_minus1[FFMIN(tile_y, > pps->pps_num_exp_tile_rows_minus1)] + 1; > > + num_entry_points += > (sps->sps_entropy_coding_sync_enabled_flag ? height : 1); > > + } > > + num_entry_points--; > > + } > > + infer(num_entry_point_offsets, num_entry_points); > > + if (num_entry_points > H266_MAX_ENTRY_POINT_OFFSETS) { > > + av_log(ctx->log_ctx, AV_LOG_ERROR, "Too many entry points: " > > + "%"PRIu16".\n", current->num_entry_point_offsets); > > + return AVERROR_PATCHWELCOME; > > + } > > + if (num_entry_points > 0) { > > + ue(sh_entry_offset_len_minus1, 0, 31); > > + for (i = 0; i < num_entry_points; i++) { > > + ubs(current->sh_entry_offset_len_minus1, > sh_entry_point_offset_minus1[i], 1, i); > > + } > > + } > > + } > > + CHECK(FUNC(byte_alignment)(ctx, rw)); > > + > > + return 0; > > +} > > + > > +static int FUNC(sei)(CodedBitstreamContext *ctx, RWContext *rw, > > + H266RawSEI *current, int prefix) > > +{ > > + return 0; > > +} > > - Mark > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > To unsubscribe, visit link above, or email > ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe". _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".