[FFmpeg-devel] [PATCH 1/2] avformat/hevc: add support for writing alpha layer

James Almer jamrial at gmail.com
Tue Dec 10 21:21:58 EET 2024


On 12/10/2024 4:16 PM, Timo Rothenpieler wrote:
> ---
>   libavformat/hevc.c | 138 ++++++++++++++++++++++++++++++++++++++++-----
>   1 file changed, 123 insertions(+), 15 deletions(-)
> 
> diff --git a/libavformat/hevc.c b/libavformat/hevc.c
> index 7cf0b0ffb2..50e7f91bd2 100644
> --- a/libavformat/hevc.c
> +++ b/libavformat/hevc.c
> @@ -82,6 +82,8 @@ typedef struct HEVCDecoderConfigurationRecord {
>       uint8_t  lengthSizeMinusOne;
>       uint8_t  numOfArrays;
>       HVCCNALUnitArray arrays[NB_ARRAYS];
> +
> +    uint8_t alpha_layer_nuh_id;
>   } HEVCDecoderConfigurationRecord;
>   
>   typedef struct HVCCProfileTierLevel {
> @@ -149,6 +151,7 @@ static void hvcc_update_ptl(HEVCDecoderConfigurationRecord *hvcc,
>   
>   static void hvcc_parse_ptl(GetBitContext *gb,
>                              HEVCDecoderConfigurationRecord *hvcc,
> +                           int profile_present_flag,
>                              unsigned int max_sub_layers_minus1)
>   {
>       unsigned int i;
> @@ -156,13 +159,17 @@ static void hvcc_parse_ptl(GetBitContext *gb,
>       uint8_t sub_layer_profile_present_flag[HEVC_MAX_SUB_LAYERS];
>       uint8_t sub_layer_level_present_flag[HEVC_MAX_SUB_LAYERS];
>   
> -    general_ptl.profile_space               = get_bits(gb, 2);
> -    general_ptl.tier_flag                   = get_bits1(gb);
> -    general_ptl.profile_idc                 = get_bits(gb, 5);
> -    general_ptl.profile_compatibility_flags = get_bits_long(gb, 32);
> -    general_ptl.constraint_indicator_flags  = get_bits64(gb, 48);
> -    general_ptl.level_idc                   = get_bits(gb, 8);
> -    hvcc_update_ptl(hvcc, &general_ptl);
> +    if (profile_present_flag) {
> +        general_ptl.profile_space               = get_bits(gb, 2);
> +        general_ptl.tier_flag                   = get_bits1(gb);
> +        general_ptl.profile_idc                 = get_bits(gb, 5);
> +        general_ptl.profile_compatibility_flags = get_bits_long(gb, 32);
> +        general_ptl.constraint_indicator_flags  = get_bits64(gb, 48);
> +        general_ptl.level_idc                   = get_bits(gb, 8);
> +        hvcc_update_ptl(hvcc, &general_ptl);
> +    } else {

Nit: unnecesary brackets.

> +        skip_bits(gb, 8); // general_level_idc
> +    }
>   
>       for (i = 0; i < max_sub_layers_minus1; i++) {
>           sub_layer_profile_present_flag[i] = get_bits1(gb);
> @@ -387,12 +394,16 @@ static void skip_sub_layer_ordering_info(GetBitContext *gb)
>   static int hvcc_parse_vps(GetBitContext *gb, HVCCNALUnit *nal,
>                             HEVCDecoderConfigurationRecord *hvcc)
>   {
> +    uint8_t vps_base_layer_internal_flag, vps_max_layers_minus1;
> +    uint8_t vps_sub_layer_ordering_info_present_flag, vps_max_layer_id;
> +    int vps_num_layer_sets_minus1;
> +    int i, j;
> +
>       nal->parameter_set_id = get_bits(gb, 4);
> -    /*
> -     * vps_reserved_three_2bits   u(2)
> -     * vps_max_layers_minus1      u(6)
> -     */
> -    skip_bits(gb, 8);
> +
> +    vps_base_layer_internal_flag = get_bits(gb, 1);
> +    skip_bits(gb, 1); // vps_base_layer_available_flag
> +    vps_max_layers_minus1 = get_bits(gb, 6);
>   
>       nal->vps_max_sub_layers_minus1 = get_bits(gb, 3);
>   
> @@ -413,7 +424,104 @@ static int hvcc_parse_vps(GetBitContext *gb, HVCCNALUnit *nal,
>        */
>       skip_bits(gb, 17);
>   
> -    hvcc_parse_ptl(gb, hvcc, nal->vps_max_sub_layers_minus1);
> +    hvcc_parse_ptl(gb, hvcc, 1, nal->vps_max_sub_layers_minus1);
> +
> +    vps_sub_layer_ordering_info_present_flag = get_bits(gb, 1);
> +    for (i = (vps_sub_layer_ordering_info_present_flag ? 0 : nal->vps_max_sub_layers_minus1);
> +         i <= nal->vps_max_sub_layers_minus1; i++) {
> +        get_ue_golomb(gb); // vps_max_dec_pic_buffering_minus1
> +        get_ue_golomb(gb); // vps_max_num_reorder_pics
> +        get_ue_golomb(gb); // vps_max_latency_increase_plus1
> +    }
> +
> +    vps_max_layer_id = get_bits(gb, 6);
> +    vps_num_layer_sets_minus1 = get_ue_golomb(gb);
> +    skip_bits_long(gb, (vps_max_layer_id + 1) * vps_num_layer_sets_minus1); // layer_id_included_flag[i][j]
> +
> +    if (get_bits(gb, 1)) { // vps_timing_info_present_flag
> +        int vps_num_hrd_parameters;
> +
> +        skip_bits_long(gb, 64); // vps_num_units_in_tick, vps_time_scale
> +
> +        if (get_bits(gb, 1)) // vps_poc_proportional_to_timing_flag
> +            get_ue_golomb(gb); // vps_num_ticks_poc_diff_one_minus1
> +
> +        vps_num_hrd_parameters = get_ue_golomb(gb); // vps_num_hrd_parameters
> +
> +        for (i = 0; i < vps_num_hrd_parameters; i++) {
> +            int cprms_present_flag;
> +
> +            get_ue_golomb(gb); // hrd_layer_set_idx[i]
> +            if (i > 0)
> +                cprms_present_flag = get_bits(gb, 1);
> +            else
> +                cprms_present_flag = 1;
> +
> +            skip_hrd_parameters(gb, cprms_present_flag, nal->vps_max_sub_layers_minus1);
> +        }
> +    }
> +
> +    if (get_bits(gb, 1)) { // vps_extension_flag

Maybe move this to its own function, following the syntax in the spec.

> +        uint8_t num_scalability_types = 0;
> +        uint8_t max_layers_minus_1 = FFMIN(62, vps_max_layers_minus1);
> +        uint8_t splitting_flag, vps_nuh_layer_id_present_flag;
> +        uint8_t scalability_mask_flag[16] = { 0 };
> +        uint8_t dimension_id_len[16] = { 0 };
> +        uint8_t layer_id_in_nuh[64] = { 0 };
> +
> +        align_get_bits(gb);
> +
> +        if (vps_max_layers_minus1 > 0 && vps_base_layer_internal_flag)
> +            hvcc_parse_ptl(gb, hvcc, 0, nal->vps_max_sub_layers_minus1);
> +
> +        splitting_flag = get_bits(gb, 1);
> +
> +        for (i = 0; i < 16; i++)
> +            if (get_bits(gb, 1))
> +                scalability_mask_flag[num_scalability_types++] = i;
> +
> +        for (j = 0; j < (num_scalability_types - splitting_flag); j++)
> +            dimension_id_len[j] = get_bits(gb, 3) + 1;
> +
> +        vps_nuh_layer_id_present_flag = get_bits(gb, 1);
> +
> +        for (i = 1; i <= max_layers_minus_1; i++) {
> +            if (vps_nuh_layer_id_present_flag)
> +                layer_id_in_nuh[i] = get_bits(gb, 6);
> +            else
> +                layer_id_in_nuh[i] = i;
> +
> +            if (!splitting_flag) {
> +                for (j = 0; j < num_scalability_types; j++) {
> +                    int dimension_id = get_bits(gb, dimension_id_len[j]);
> +
> +                    if (dimension_id == 1 /* AUX_ALPHA */ && scalability_mask_flag[j] == 3 /* AuxId */)
> +                        hvcc->alpha_layer_nuh_id = layer_id_in_nuh[i];
> +                }
> +            }
> +        }
> +
> +        if (splitting_flag) {
> +            uint8_t dim_bit_offset[17] = { 0 };
> +
> +            dim_bit_offset[0] = 0;
> +            for (j = 1; j < num_scalability_types; j++)
> +                dim_bit_offset[j] = dim_bit_offset[j-1] + dimension_id_len[j-1];
> +            dim_bit_offset[num_scalability_types] = 6;
> +
> +            if (num_scalability_types > 0 && dim_bit_offset[num_scalability_types - 1] >= 6)
> +                return 0; // invalid bitstream
> +
> +            for (i = 1; i <= max_layers_minus_1; i++) {
> +                for (j = 0; j < num_scalability_types; j++) {
> +                    int dimension_id = (layer_id_in_nuh[i] & ((1 << dim_bit_offset[j+1]) - 1)) >> dim_bit_offset[j];
> +
> +                    if (dimension_id == 1 /* AUX_ALPHA */ && scalability_mask_flag[j] == 3 /* AuxId */)
> +                        hvcc->alpha_layer_nuh_id = layer_id_in_nuh[i];
> +                }
> +            }
> +        }
> +    }
>   
>       /* nothing useful for hvcC past this point */
>       return 0;
> @@ -551,7 +659,7 @@ static int hvcc_parse_sps(GetBitContext *gb, HVCCNALUnit *nal,
>   
>       if (!multi_layer_ext_sps_flag) {
>           hvcc->temporalIdNested = get_bits1(gb);
> -        hvcc_parse_ptl(gb, hvcc, sps_max_sub_layers_minus1);
> +        hvcc_parse_ptl(gb, hvcc, 1, sps_max_sub_layers_minus1);
>       }
>   
>       nal->parameter_set_id = get_ue_golomb_long(gb);
> @@ -762,7 +870,7 @@ static int hvcc_add_nal_unit(const uint8_t *nal_buf, uint32_t nal_size,
>           goto end;
>   
>       nal_unit_parse_header(&gbc, &nal_type, &nuh_layer_id);
> -    if (!is_lhvc && nuh_layer_id > 0)
> +    if (!is_lhvc && nuh_layer_id > 0 && nuh_layer_id != hvcc->alpha_layer_nuh_id)
>           goto end;
>   
>       /*

Confirmed working (in that the PS nalus are not filtered when the checks 
succeed and alpha_layer_nuh_id is set), so LGTM.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: OpenPGP_signature.asc
Type: application/pgp-signature
Size: 495 bytes
Desc: OpenPGP digital signature
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20241210/cd04c191/attachment.sig>


More information about the ffmpeg-devel mailing list