diff mbox series

[FFmpeg-devel,1/4] avcodec/cbs_h265: fix writing extension_data bits

Message ID 20200420214551.6318-1-jamrial@gmail.com
State Accepted
Commit 38d1815cc65dd447de80760895ee008cfc9a0091
Headers show
Series [FFmpeg-devel,1/4] avcodec/cbs_h265: fix writing extension_data bits | expand

Checks

Context Check Description
andriy/default pending
andriy/make success Make finished
andriy/make_fate success Make fate finished

Commit Message

James Almer April 20, 2020, 9:45 p.m. UTC
We only care about the right most bit.

Signed-off-by: James Almer <jamrial@gmail.com>
---
Fixes handling files like
https://trac.ffmpeg.org/attachment/ticket/7965/puppets_with_alpha_hevc.mov
Without this patch, parsing works but passing the VPS through hevc_metadata_bsf
when writing fails.

 libavcodec/cbs_h265_syntax_template.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

James Almer April 21, 2020, 5:37 p.m. UTC | #1
On 4/20/2020 6:45 PM, James Almer wrote:
> Fixes ticket #8622
> 
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
> It fixes it assuming the Picture Timing in that sample is not being misparsed
> by cbs_h265.
> We're compliant with the latest revision of the spec, so any
> reserved_payload_extension_data found in a sample created today is almost
> guaranteed to be bogus. But the spec states that they should be skipped if
> found, for forward compatibility reasons.
> Would be worth checking if the nvenc encoder is at fault, writing faulty SEI
> messages.
> 
> This patch could for that matter make many potential cases of undiscovered
> cbs_h265 SEI misparsing be handled as if the remaining bits were these reserved
> bits.
> 
>  libavcodec/cbs_h265.h                 |  1 +
>  libavcodec/cbs_h265_syntax_template.c | 44 +++++++++++++++++++++++++--
>  2 files changed, 43 insertions(+), 2 deletions(-)
> 
> diff --git a/libavcodec/cbs_h265.h b/libavcodec/cbs_h265.h
> index 2c1e153ad9..73897f77a4 100644
> --- a/libavcodec/cbs_h265.h
> +++ b/libavcodec/cbs_h265.h
> @@ -715,6 +715,7 @@ typedef struct H265RawSEIPayload {
>              AVBufferRef *data_ref;
>          } other;
>      } payload;
> +    H265RawExtensionData extension_data;

Added an av_buffer_unref() call in cbs_h265_free_sei_payload()
cbs_h2645.c locally. Else the buffer will leak.

>  } H265RawSEIPayload;
>  
>  typedef struct H265RawSEI {
> diff --git a/libavcodec/cbs_h265_syntax_template.c b/libavcodec/cbs_h265_syntax_template.c
> index f978e16549..ef3254d27f 100644
> --- a/libavcodec/cbs_h265_syntax_template.c
> +++ b/libavcodec/cbs_h265_syntax_template.c
> @@ -2054,11 +2054,43 @@ static int FUNC(sei_alpha_channel_info)(CodedBitstreamContext *ctx,
>      return 0;
>  }
>  
> +static int FUNC(payload_extension)(CodedBitstreamContext *ctx, RWContext *rw,
> +                                   H265RawExtensionData *current, uint32_t payload_size,
> +                                   int cur_pos)
> +{
> +    int err, i;
> +
> +#ifdef READ
> +    if (cbs_h265_payload_extension_present(rw, payload_size, cur_pos)) {
> +        GetBitContext tmp = *rw;
> +        int payload_zero_bits, bits_left = 8 * payload_size - cur_pos;
> +        if (bits_left > 8)
> +            skip_bits_long(&tmp, bits_left - 8);
> +        payload_zero_bits = ff_ctz(get_bits(&tmp, FFMIN(bits_left, 8)));
> +        if (payload_zero_bits >= bits_left - 1)
> +            return AVERROR_INVALIDDATA;
> +        current->bit_length = bits_left - payload_zero_bits - 1;
> +        allocate(current->data, (current->bit_length + 7) / 8);
> +        for (i = 0; i < current->bit_length; i++) {
> +            uint8_t bit;
> +            xu(1, reserved_payload_extension_data, bit, 0, 1, 0);
> +            current->data[i / 8] |= bit << (7 - i % 8);
> +        }
> +    }
> +#else
> +    for (i = 0; i < current->bit_length; i++)
> +        xu(1, reserved_payload_extension_data,
> +           current->data[i / 8] >> (7 - i % 8) & 1, 0, 1, 0);
> +#endif
> +
> +    return 0;
> +}
> +
>  static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
>                               H265RawSEIPayload *current, int prefix)
>  {
>      int err, i;
> -    int start_position, end_position;
> +    int start_position, current_position, end_position;
>  
>  #ifdef READ
>      start_position = get_bits_count(rw);
> @@ -2122,7 +2154,15 @@ static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
>          }
>      }
>  
> -    if (byte_alignment(rw)) {
> +#ifdef READ
> +    current_position = get_bits_count(rw) - start_position;
> +    if (current_position != 8 * current->payload_size) {
> +#else
> +    current_position = put_bits_count(rw) - start_position;
> +    if (byte_alignment(rw) || current->extension_data.bit_length) {
> +#endif
> +        CHECK(FUNC(payload_extension)(ctx, rw, &current->extension_data,
> +                                      current->payload_size, current_position));
>          fixed(1, bit_equal_to_one, 1);
>          while (byte_alignment(rw))
>              fixed(1, bit_equal_to_zero, 0);
>
Andreas Rheinhardt April 22, 2020, 11:31 p.m. UTC | #2
James Almer:
> Will be reused in the following patch.
> 
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
>  libavcodec/cbs_h2645.c                | 9 +++++++++
>  libavcodec/cbs_h265_syntax_template.c | 8 +++-----
>  2 files changed, 12 insertions(+), 5 deletions(-)
> 
> diff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c
> index d42073cc5a..dffff862e2 100644
> --- a/libavcodec/cbs_h2645.c
> +++ b/libavcodec/cbs_h2645.c
> @@ -233,6 +233,15 @@ static int cbs_write_se_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc,
>      return 0;
>  }
>  
> +static int cbs_h265_payload_extension_present(GetBitContext *gbc, uint32_t payload_size,
> +                                              int cur_pos)
> +{
> +    int bits_left;
> +    bits_left = payload_size * 8 - cur_pos;
> +    return (bits_left > 0 &&
> +            (bits_left > 7 || ff_ctz(show_bits(gbc, bits_left)) < bits_left - 1));
> +}
> +
>  #define HEADER(name) do { \
>          ff_cbs_trace_header(ctx, name); \
>      } while (0)
> diff --git a/libavcodec/cbs_h265_syntax_template.c b/libavcodec/cbs_h265_syntax_template.c
> index fe5ffac80f..f978e16549 100644
> --- a/libavcodec/cbs_h265_syntax_template.c
> +++ b/libavcodec/cbs_h265_syntax_template.c
> @@ -1568,7 +1568,7 @@ static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw,
>      int err, i, length;
>  
>  #ifdef READ
> -    int start_pos, end_pos, bits_left;
> +    int start_pos;
>      start_pos = get_bits_count(rw);
>  #endif
>  
> @@ -1649,10 +1649,8 @@ static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw,
>  #ifdef READ
>      // payload_extension_present() - true if we are before the last 1-bit
>      // in the payload structure, which must be in the last byte.
> -    end_pos = get_bits_count(rw);
> -    bits_left = *payload_size * 8 - (end_pos - start_pos);
> -    if (bits_left > 0 &&
> -        (bits_left > 7 || ff_ctz(show_bits(rw, bits_left)) < bits_left - 1))
> +    if (cbs_h265_payload_extension_present(rw, *payload_size,
> +                                           get_bits_count(rw) - start_pos))
>          flag(use_alt_cpb_params_flag);
>      else
>          infer(use_alt_cpb_params_flag, 0);
> 
The value of ff_ctz is undefined if the argument is zero. It can be zero
for invalid input (namely if all of the bits_left are zero). You should
instead use a check like show_bits(gbc, bits_left) &
MAX_UINT_BITS(bits_left - 1).

(In this situation where you are only reading one bit it doesn't really
matter - invalid input will always be detected as such if ff_ctz returns
something >= 0. But if we use this later in a scenario where it is about
more than just one bit, this can make invalid input slip through.)

This is similar to d4035ca849bdb90e95c87e2737a99ea657be0716.

- Andreas
Andreas Rheinhardt April 22, 2020, 11:35 p.m. UTC | #3
James Almer:
> Fixes ticket #8622
> 
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
> It fixes it assuming the Picture Timing in that sample is not being misparsed
> by cbs_h265.
> We're compliant with the latest revision of the spec, so any
> reserved_payload_extension_data found in a sample created today is almost
> guaranteed to be bogus. But the spec states that they should be skipped if
> found, for forward compatibility reasons.
> Would be worth checking if the nvenc encoder is at fault, writing faulty SEI
> messages.
> 
> This patch could for that matter make many potential cases of undiscovered
> cbs_h265 SEI misparsing be handled as if the remaining bits were these reserved
> bits.
> 
>  libavcodec/cbs_h265.h                 |  1 +
>  libavcodec/cbs_h265_syntax_template.c | 44 +++++++++++++++++++++++++--
>  2 files changed, 43 insertions(+), 2 deletions(-)
> 
> diff --git a/libavcodec/cbs_h265.h b/libavcodec/cbs_h265.h
> index 2c1e153ad9..73897f77a4 100644
> --- a/libavcodec/cbs_h265.h
> +++ b/libavcodec/cbs_h265.h
> @@ -715,6 +715,7 @@ typedef struct H265RawSEIPayload {
>              AVBufferRef *data_ref;
>          } other;
>      } payload;
> +    H265RawExtensionData extension_data;
>  } H265RawSEIPayload;
>  
>  typedef struct H265RawSEI {
> diff --git a/libavcodec/cbs_h265_syntax_template.c b/libavcodec/cbs_h265_syntax_template.c
> index f978e16549..ef3254d27f 100644
> --- a/libavcodec/cbs_h265_syntax_template.c
> +++ b/libavcodec/cbs_h265_syntax_template.c
> @@ -2054,11 +2054,43 @@ static int FUNC(sei_alpha_channel_info)(CodedBitstreamContext *ctx,
>      return 0;
>  }
>  
> +static int FUNC(payload_extension)(CodedBitstreamContext *ctx, RWContext *rw,
> +                                   H265RawExtensionData *current, uint32_t payload_size,
> +                                   int cur_pos)
> +{
> +    int err, i;
> +
> +#ifdef READ
> +    if (cbs_h265_payload_extension_present(rw, payload_size, cur_pos)) {
> +        GetBitContext tmp = *rw;
> +        int payload_zero_bits, bits_left = 8 * payload_size - cur_pos;
> +        if (bits_left > 8)
> +            skip_bits_long(&tmp, bits_left - 8);
> +        payload_zero_bits = ff_ctz(get_bits(&tmp, FFMIN(bits_left, 8)));

Same as in the last mail. Replace the following check with if
(!get_bits(&tmp, FFMIN(bits_left, 8))).

> +        if (payload_zero_bits >= bits_left - 1)
> +            return AVERROR_INVALIDDATA;
> +        current->bit_length = bits_left - payload_zero_bits - 1;
> +        allocate(current->data, (current->bit_length + 7) / 8);
> +        for (i = 0; i < current->bit_length; i++) {
> +            uint8_t bit;
> +            xu(1, reserved_payload_extension_data, bit, 0, 1, 0);
> +            current->data[i / 8] |= bit << (7 - i % 8);
> +        }
> +    }
> +#else
> +    for (i = 0; i < current->bit_length; i++)
> +        xu(1, reserved_payload_extension_data,
> +           current->data[i / 8] >> (7 - i % 8) & 1, 0, 1, 0);
> +#endif
> +
> +    return 0;
> +}
> +
>  static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
>                               H265RawSEIPayload *current, int prefix)
>  {
>      int err, i;
> -    int start_position, end_position;
> +    int start_position, current_position, end_position;
>  
>  #ifdef READ
>      start_position = get_bits_count(rw);
> @@ -2122,7 +2154,15 @@ static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
>          }
>      }
>  
> -    if (byte_alignment(rw)) {
> +#ifdef READ
> +    current_position = get_bits_count(rw) - start_position;
> +    if (current_position != 8 * current->payload_size) {
> +#else
> +    current_position = put_bits_count(rw) - start_position;
> +    if (byte_alignment(rw) || current->extension_data.bit_length) {
> +#endif
> +        CHECK(FUNC(payload_extension)(ctx, rw, &current->extension_data,
> +                                      current->payload_size, current_position));
>          fixed(1, bit_equal_to_one, 1);
>          while (byte_alignment(rw))
>              fixed(1, bit_equal_to_zero, 0);
>
Andreas Rheinhardt April 23, 2020, 12:17 a.m. UTC | #4
James Almer:
> Fixes ticket #8622
> 
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
> It fixes it assuming the Picture Timing in that sample is not being misparsed
> by cbs_h265.
> We're compliant with the latest revision of the spec, so any
> reserved_payload_extension_data found in a sample created today is almost
> guaranteed to be bogus. But the spec states that they should be skipped if
> found, for forward compatibility reasons.
> Would be worth checking if the nvenc encoder is at fault, writing faulty SEI
> messages.
> 
> This patch could for that matter make many potential cases of undiscovered
> cbs_h265 SEI misparsing be handled as if the remaining bits were these reserved
> bits.

That's unfortunately true. I don't see a way to avoid that.

> 
>  libavcodec/cbs_h265.h                 |  1 +
>  libavcodec/cbs_h265_syntax_template.c | 44 +++++++++++++++++++++++++--
>  2 files changed, 43 insertions(+), 2 deletions(-)
> 
> diff --git a/libavcodec/cbs_h265.h b/libavcodec/cbs_h265.h
> index 2c1e153ad9..73897f77a4 100644
> --- a/libavcodec/cbs_h265.h
> +++ b/libavcodec/cbs_h265.h
> @@ -715,6 +715,7 @@ typedef struct H265RawSEIPayload {
>              AVBufferRef *data_ref;
>          } other;
>      } payload;
> +    H265RawExtensionData extension_data;
>  } H265RawSEIPayload;
>  
>  typedef struct H265RawSEI {
> diff --git a/libavcodec/cbs_h265_syntax_template.c b/libavcodec/cbs_h265_syntax_template.c
> index f978e16549..ef3254d27f 100644
> --- a/libavcodec/cbs_h265_syntax_template.c
> +++ b/libavcodec/cbs_h265_syntax_template.c
> @@ -2054,11 +2054,43 @@ static int FUNC(sei_alpha_channel_info)(CodedBitstreamContext *ctx,
>      return 0;
>  }
>  
> +static int FUNC(payload_extension)(CodedBitstreamContext *ctx, RWContext *rw,
> +                                   H265RawExtensionData *current, uint32_t payload_size,
> +                                   int cur_pos)
> +{
> +    int err, i;
> +
> +#ifdef READ
> +    if (cbs_h265_payload_extension_present(rw, payload_size, cur_pos)) {
> +        GetBitContext tmp = *rw;
> +        int payload_zero_bits, bits_left = 8 * payload_size - cur_pos;
> +        if (bits_left > 8)
> +            skip_bits_long(&tmp, bits_left - 8);
> +        payload_zero_bits = ff_ctz(get_bits(&tmp, FFMIN(bits_left, 8)));
> +        if (payload_zero_bits >= bits_left - 1)
> +            return AVERROR_INVALIDDATA;
> +        current->bit_length = bits_left - payload_zero_bits - 1;
> +        allocate(current->data, (current->bit_length + 7) / 8);
> +        for (i = 0; i < current->bit_length; i++) {
> +            uint8_t bit;
> +            xu(1, reserved_payload_extension_data, bit, 0, 1, 0);
> +            current->data[i / 8] |= bit << (7 - i % 8);
> +        }
> +    }
> +#else
> +    for (i = 0; i < current->bit_length; i++)
> +        xu(1, reserved_payload_extension_data,
> +           current->data[i / 8] >> (7 - i % 8) & 1, 0, 1, 0);

Do we really need to write one bit at a time? Why not simply one byte at
a time and then output the rest in one go? The specs treat it as a
bitfield and not as individual bits, so I think we are free to group it
as we please.

The same goes for the other extension_data() function, of course. And
for reading.

And shouldn't i be better size_t?

> +#endif
> +
> +    return 0;
> +}
> +
>  static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
>                               H265RawSEIPayload *current, int prefix)
>  {
>      int err, i;
> -    int start_position, end_position;
> +    int start_position, current_position, end_position;
>  
>  #ifdef READ
>      start_position = get_bits_count(rw);
> @@ -2122,7 +2154,15 @@ static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
>          }
>      }
>  
> -    if (byte_alignment(rw)) {
> +#ifdef READ
> +    current_position = get_bits_count(rw) - start_position;
> +    if (current_position != 8 * current->payload_size) {
> +#else
> +    current_position = put_bits_count(rw) - start_position;
> +    if (byte_alignment(rw) || current->extension_data.bit_length) {
> +#endif
> +        CHECK(FUNC(payload_extension)(ctx, rw, &current->extension_data,
> +                                      current->payload_size, current_position));
>          fixed(1, bit_equal_to_one, 1);
>          while (byte_alignment(rw))
>              fixed(1, bit_equal_to_zero, 0);
>
James Almer April 23, 2020, 2:43 a.m. UTC | #5
On 4/22/2020 9:17 PM, Andreas Rheinhardt wrote:
> James Almer:
>> Fixes ticket #8622
>>
>> Signed-off-by: James Almer <jamrial@gmail.com>
>> ---
>> It fixes it assuming the Picture Timing in that sample is not being misparsed
>> by cbs_h265.
>> We're compliant with the latest revision of the spec, so any
>> reserved_payload_extension_data found in a sample created today is almost
>> guaranteed to be bogus. But the spec states that they should be skipped if
>> found, for forward compatibility reasons.
>> Would be worth checking if the nvenc encoder is at fault, writing faulty SEI
>> messages.
>>
>> This patch could for that matter make many potential cases of undiscovered
>> cbs_h265 SEI misparsing be handled as if the remaining bits were these reserved
>> bits.
> 
> That's unfortunately true. I don't see a way to avoid that.
> 
>>
>>  libavcodec/cbs_h265.h                 |  1 +
>>  libavcodec/cbs_h265_syntax_template.c | 44 +++++++++++++++++++++++++--
>>  2 files changed, 43 insertions(+), 2 deletions(-)
>>
>> diff --git a/libavcodec/cbs_h265.h b/libavcodec/cbs_h265.h
>> index 2c1e153ad9..73897f77a4 100644
>> --- a/libavcodec/cbs_h265.h
>> +++ b/libavcodec/cbs_h265.h
>> @@ -715,6 +715,7 @@ typedef struct H265RawSEIPayload {
>>              AVBufferRef *data_ref;
>>          } other;
>>      } payload;
>> +    H265RawExtensionData extension_data;
>>  } H265RawSEIPayload;
>>  
>>  typedef struct H265RawSEI {
>> diff --git a/libavcodec/cbs_h265_syntax_template.c b/libavcodec/cbs_h265_syntax_template.c
>> index f978e16549..ef3254d27f 100644
>> --- a/libavcodec/cbs_h265_syntax_template.c
>> +++ b/libavcodec/cbs_h265_syntax_template.c
>> @@ -2054,11 +2054,43 @@ static int FUNC(sei_alpha_channel_info)(CodedBitstreamContext *ctx,
>>      return 0;
>>  }
>>  
>> +static int FUNC(payload_extension)(CodedBitstreamContext *ctx, RWContext *rw,
>> +                                   H265RawExtensionData *current, uint32_t payload_size,
>> +                                   int cur_pos)
>> +{
>> +    int err, i;
>> +
>> +#ifdef READ
>> +    if (cbs_h265_payload_extension_present(rw, payload_size, cur_pos)) {
>> +        GetBitContext tmp = *rw;
>> +        int payload_zero_bits, bits_left = 8 * payload_size - cur_pos;
>> +        if (bits_left > 8)
>> +            skip_bits_long(&tmp, bits_left - 8);
>> +        payload_zero_bits = ff_ctz(get_bits(&tmp, FFMIN(bits_left, 8)));
>> +        if (payload_zero_bits >= bits_left - 1)
>> +            return AVERROR_INVALIDDATA;
>> +        current->bit_length = bits_left - payload_zero_bits - 1;
>> +        allocate(current->data, (current->bit_length + 7) / 8);
>> +        for (i = 0; i < current->bit_length; i++) {
>> +            uint8_t bit;
>> +            xu(1, reserved_payload_extension_data, bit, 0, 1, 0);
>> +            current->data[i / 8] |= bit << (7 - i % 8);
>> +        }
>> +    }
>> +#else
>> +    for (i = 0; i < current->bit_length; i++)
>> +        xu(1, reserved_payload_extension_data,
>> +           current->data[i / 8] >> (7 - i % 8) & 1, 0, 1, 0);
> 
> Do we really need to write one bit at a time? Why not simply one byte at
> a time and then output the rest in one go? The specs treat it as a
> bitfield and not as individual bits, so I think we are free to group it
> as we please.

Mmh, ok.

> 
> The same goes for the other extension_data() function, of course. And
> for reading.

Doing it for the other function will affect the existing trace output,
so I'm inclined to leave it as is.

> 
> And shouldn't i be better size_t?

The correct type would be ptrdiff_t, but we never bothered with it and
always used int.

> 
>> +#endif
>> +
>> +    return 0;
>> +}
>> +
>>  static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
>>                               H265RawSEIPayload *current, int prefix)
>>  {
>>      int err, i;
>> -    int start_position, end_position;
>> +    int start_position, current_position, end_position;
>>  
>>  #ifdef READ
>>      start_position = get_bits_count(rw);
>> @@ -2122,7 +2154,15 @@ static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
>>          }
>>      }
>>  
>> -    if (byte_alignment(rw)) {
>> +#ifdef READ
>> +    current_position = get_bits_count(rw) - start_position;
>> +    if (current_position != 8 * current->payload_size) {
>> +#else
>> +    current_position = put_bits_count(rw) - start_position;
>> +    if (byte_alignment(rw) || current->extension_data.bit_length) {
>> +#endif
>> +        CHECK(FUNC(payload_extension)(ctx, rw, &current->extension_data,
>> +                                      current->payload_size, current_position));
>>          fixed(1, bit_equal_to_one, 1);
>>          while (byte_alignment(rw))
>>              fixed(1, bit_equal_to_zero, 0);
>>
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
Andreas Rheinhardt April 23, 2020, 3 a.m. UTC | #6
James Almer:
> On 4/22/2020 9:17 PM, Andreas Rheinhardt wrote:
>> James Almer:
>>> Fixes ticket #8622
>>>
>>> Signed-off-by: James Almer <jamrial@gmail.com>
>>> ---
>>> It fixes it assuming the Picture Timing in that sample is not being misparsed
>>> by cbs_h265.
>>> We're compliant with the latest revision of the spec, so any
>>> reserved_payload_extension_data found in a sample created today is almost
>>> guaranteed to be bogus. But the spec states that they should be skipped if
>>> found, for forward compatibility reasons.
>>> Would be worth checking if the nvenc encoder is at fault, writing faulty SEI
>>> messages.
>>>
>>> This patch could for that matter make many potential cases of undiscovered
>>> cbs_h265 SEI misparsing be handled as if the remaining bits were these reserved
>>> bits.
>>
>> That's unfortunately true. I don't see a way to avoid that.
>>
>>>
>>>  libavcodec/cbs_h265.h                 |  1 +
>>>  libavcodec/cbs_h265_syntax_template.c | 44 +++++++++++++++++++++++++--
>>>  2 files changed, 43 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/libavcodec/cbs_h265.h b/libavcodec/cbs_h265.h
>>> index 2c1e153ad9..73897f77a4 100644
>>> --- a/libavcodec/cbs_h265.h
>>> +++ b/libavcodec/cbs_h265.h
>>> @@ -715,6 +715,7 @@ typedef struct H265RawSEIPayload {
>>>              AVBufferRef *data_ref;
>>>          } other;
>>>      } payload;
>>> +    H265RawExtensionData extension_data;
>>>  } H265RawSEIPayload;
>>>  
>>>  typedef struct H265RawSEI {
>>> diff --git a/libavcodec/cbs_h265_syntax_template.c b/libavcodec/cbs_h265_syntax_template.c
>>> index f978e16549..ef3254d27f 100644
>>> --- a/libavcodec/cbs_h265_syntax_template.c
>>> +++ b/libavcodec/cbs_h265_syntax_template.c
>>> @@ -2054,11 +2054,43 @@ static int FUNC(sei_alpha_channel_info)(CodedBitstreamContext *ctx,
>>>      return 0;
>>>  }
>>>  
>>> +static int FUNC(payload_extension)(CodedBitstreamContext *ctx, RWContext *rw,
>>> +                                   H265RawExtensionData *current, uint32_t payload_size,
>>> +                                   int cur_pos)
>>> +{
>>> +    int err, i;
>>> +
>>> +#ifdef READ
>>> +    if (cbs_h265_payload_extension_present(rw, payload_size, cur_pos)) {
>>> +        GetBitContext tmp = *rw;
>>> +        int payload_zero_bits, bits_left = 8 * payload_size - cur_pos;
>>> +        if (bits_left > 8)
>>> +            skip_bits_long(&tmp, bits_left - 8);
>>> +        payload_zero_bits = ff_ctz(get_bits(&tmp, FFMIN(bits_left, 8)));
>>> +        if (payload_zero_bits >= bits_left - 1)
>>> +            return AVERROR_INVALIDDATA;
>>> +        current->bit_length = bits_left - payload_zero_bits - 1;
>>> +        allocate(current->data, (current->bit_length + 7) / 8);
>>> +        for (i = 0; i < current->bit_length; i++) {
>>> +            uint8_t bit;
>>> +            xu(1, reserved_payload_extension_data, bit, 0, 1, 0);
>>> +            current->data[i / 8] |= bit << (7 - i % 8);
>>> +        }
>>> +    }
>>> +#else
>>> +    for (i = 0; i < current->bit_length; i++)
>>> +        xu(1, reserved_payload_extension_data,
>>> +           current->data[i / 8] >> (7 - i % 8) & 1, 0, 1, 0);
>>
>> Do we really need to write one bit at a time? Why not simply one byte at
>> a time and then output the rest in one go? The specs treat it as a
>> bitfield and not as individual bits, so I think we are free to group it
>> as we please.
> 
> Mmh, ok.
> 
>>
>> The same goes for the other extension_data() function, of course. And
>> for reading.
> 
> Doing it for the other function will affect the existing trace output,
> so I'm inclined to leave it as is.
> 
>>
>> And shouldn't i be better size_t?
> 
> The correct type would be ptrdiff_t, but we never bothered with it and
> always used int.
> 

The bit_length field of the extension data struct is size_t. If you
presume that extension data will always only be passed through, then you
are on the safe side; if not, you are not.

>>
>>> +#endif
>>> +
>>> +    return 0;
>>> +}
>>> +
>>>  static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
>>>                               H265RawSEIPayload *current, int prefix)
>>>  {
>>>      int err, i;
>>> -    int start_position, end_position;
>>> +    int start_position, current_position, end_position;
>>>  
>>>  #ifdef READ
>>>      start_position = get_bits_count(rw);
>>> @@ -2122,7 +2154,15 @@ static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
>>>          }
>>>      }
>>>  
>>> -    if (byte_alignment(rw)) {
>>> +#ifdef READ
>>> +    current_position = get_bits_count(rw) - start_position;
>>> +    if (current_position != 8 * current->payload_size) {
>>> +#else
>>> +    current_position = put_bits_count(rw) - start_position;
>>> +    if (byte_alignment(rw) || current->extension_data.bit_length) {
>>> +#endif
>>> +        CHECK(FUNC(payload_extension)(ctx, rw, &current->extension_data,
>>> +                                      current->payload_size, current_position));
>>>          fixed(1, bit_equal_to_one, 1);
>>>          while (byte_alignment(rw))
>>>              fixed(1, bit_equal_to_zero, 0);
>>>
>>
James Almer April 23, 2020, 4:28 a.m. UTC | #7
On 4/23/2020 12:00 AM, Andreas Rheinhardt wrote:
> James Almer:
>> On 4/22/2020 9:17 PM, Andreas Rheinhardt wrote:
>>> James Almer:
>>>> Fixes ticket #8622
>>>>
>>>> Signed-off-by: James Almer <jamrial@gmail.com>
>>>> ---
>>>> It fixes it assuming the Picture Timing in that sample is not being misparsed
>>>> by cbs_h265.
>>>> We're compliant with the latest revision of the spec, so any
>>>> reserved_payload_extension_data found in a sample created today is almost
>>>> guaranteed to be bogus. But the spec states that they should be skipped if
>>>> found, for forward compatibility reasons.
>>>> Would be worth checking if the nvenc encoder is at fault, writing faulty SEI
>>>> messages.
>>>>
>>>> This patch could for that matter make many potential cases of undiscovered
>>>> cbs_h265 SEI misparsing be handled as if the remaining bits were these reserved
>>>> bits.
>>>
>>> That's unfortunately true. I don't see a way to avoid that.
>>>
>>>>
>>>>  libavcodec/cbs_h265.h                 |  1 +
>>>>  libavcodec/cbs_h265_syntax_template.c | 44 +++++++++++++++++++++++++--
>>>>  2 files changed, 43 insertions(+), 2 deletions(-)
>>>>
>>>> diff --git a/libavcodec/cbs_h265.h b/libavcodec/cbs_h265.h
>>>> index 2c1e153ad9..73897f77a4 100644
>>>> --- a/libavcodec/cbs_h265.h
>>>> +++ b/libavcodec/cbs_h265.h
>>>> @@ -715,6 +715,7 @@ typedef struct H265RawSEIPayload {
>>>>              AVBufferRef *data_ref;
>>>>          } other;
>>>>      } payload;
>>>> +    H265RawExtensionData extension_data;
>>>>  } H265RawSEIPayload;
>>>>  
>>>>  typedef struct H265RawSEI {
>>>> diff --git a/libavcodec/cbs_h265_syntax_template.c b/libavcodec/cbs_h265_syntax_template.c
>>>> index f978e16549..ef3254d27f 100644
>>>> --- a/libavcodec/cbs_h265_syntax_template.c
>>>> +++ b/libavcodec/cbs_h265_syntax_template.c
>>>> @@ -2054,11 +2054,43 @@ static int FUNC(sei_alpha_channel_info)(CodedBitstreamContext *ctx,
>>>>      return 0;
>>>>  }
>>>>  
>>>> +static int FUNC(payload_extension)(CodedBitstreamContext *ctx, RWContext *rw,
>>>> +                                   H265RawExtensionData *current, uint32_t payload_size,
>>>> +                                   int cur_pos)
>>>> +{
>>>> +    int err, i;
>>>> +
>>>> +#ifdef READ
>>>> +    if (cbs_h265_payload_extension_present(rw, payload_size, cur_pos)) {
>>>> +        GetBitContext tmp = *rw;
>>>> +        int payload_zero_bits, bits_left = 8 * payload_size - cur_pos;
>>>> +        if (bits_left > 8)
>>>> +            skip_bits_long(&tmp, bits_left - 8);
>>>> +        payload_zero_bits = ff_ctz(get_bits(&tmp, FFMIN(bits_left, 8)));
>>>> +        if (payload_zero_bits >= bits_left - 1)
>>>> +            return AVERROR_INVALIDDATA;
>>>> +        current->bit_length = bits_left - payload_zero_bits - 1;
>>>> +        allocate(current->data, (current->bit_length + 7) / 8);
>>>> +        for (i = 0; i < current->bit_length; i++) {
>>>> +            uint8_t bit;
>>>> +            xu(1, reserved_payload_extension_data, bit, 0, 1, 0);
>>>> +            current->data[i / 8] |= bit << (7 - i % 8);
>>>> +        }
>>>> +    }
>>>> +#else
>>>> +    for (i = 0; i < current->bit_length; i++)
>>>> +        xu(1, reserved_payload_extension_data,
>>>> +           current->data[i / 8] >> (7 - i % 8) & 1, 0, 1, 0);
>>>
>>> Do we really need to write one bit at a time? Why not simply one byte at
>>> a time and then output the rest in one go? The specs treat it as a
>>> bitfield and not as individual bits, so I think we are free to group it
>>> as we please.
>>
>> Mmh, ok.
>>
>>>
>>> The same goes for the other extension_data() function, of course. And
>>> for reading.
>>
>> Doing it for the other function will affect the existing trace output,
>> so I'm inclined to leave it as is.
>>
>>>
>>> And shouldn't i be better size_t?
>>
>> The correct type would be ptrdiff_t, but we never bothered with it and
>> always used int.
>>
> 
> The bit_length field of the extension data struct is size_t. If you
> presume that extension data will always only be passed through, then you
> are on the safe side; if not, you are not.

You're not supposed to write extension data in SEI messages to begin
with. A passthrough of course does since it keeps the data as it was in
the source stream intact. So no bsf or encoder should ever try to fill
this manually with custom data, as it would just create potentially
invalid files once a SEI message type gets extended in a future revision
of the spec, like it happened with Buffering Period.
It's what really makes me want to figure out why the nvenc encoder added
this blank byte in this sample for the ticket reporter. It's creating
files that could just stop being valid in the future.

Still, the extension data struct does use size_t, so might as well do
the same here.

> 
>>>
>>>> +#endif
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>>  static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
>>>>                               H265RawSEIPayload *current, int prefix)
>>>>  {
>>>>      int err, i;
>>>> -    int start_position, end_position;
>>>> +    int start_position, current_position, end_position;
>>>>  
>>>>  #ifdef READ
>>>>      start_position = get_bits_count(rw);
>>>> @@ -2122,7 +2154,15 @@ static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
>>>>          }
>>>>      }
>>>>  
>>>> -    if (byte_alignment(rw)) {
>>>> +#ifdef READ
>>>> +    current_position = get_bits_count(rw) - start_position;
>>>> +    if (current_position != 8 * current->payload_size) {
>>>> +#else
>>>> +    current_position = put_bits_count(rw) - start_position;
>>>> +    if (byte_alignment(rw) || current->extension_data.bit_length) {
>>>> +#endif
>>>> +        CHECK(FUNC(payload_extension)(ctx, rw, &current->extension_data,
>>>> +                                      current->payload_size, current_position));
>>>>          fixed(1, bit_equal_to_one, 1);
>>>>          while (byte_alignment(rw))
>>>>              fixed(1, bit_equal_to_zero, 0);
>>>>
>>>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
James Almer April 30, 2020, 12:16 a.m. UTC | #8
On 4/20/2020 6:45 PM, James Almer wrote:
> We only care about the right most bit.
> 
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
> Fixes handling files like
> https://trac.ffmpeg.org/attachment/ticket/7965/puppets_with_alpha_hevc.mov
> Without this patch, parsing works but passing the VPS through hevc_metadata_bsf
> when writing fails.
> 
>  libavcodec/cbs_h265_syntax_template.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/libavcodec/cbs_h265_syntax_template.c b/libavcodec/cbs_h265_syntax_template.c
> index 180a045c34..85b952e64c 100644
> --- a/libavcodec/cbs_h265_syntax_template.c
> +++ b/libavcodec/cbs_h265_syntax_template.c
> @@ -80,7 +80,7 @@ static int FUNC(extension_data)(CodedBitstreamContext *ctx, RWContext *rw,
>      }
>  #else
>      for (k = 0; k < current->bit_length; k++)
> -        xu(1, extension_data, current->data[k / 8] >> (7 - k % 8), 0, 1, 0);
> +        xu(1, extension_data, current->data[k / 8] >> (7 - k % 8) & 1, 0, 1, 0);
>  #endif
>      return 0;
>  }

I'll apply this soon.
diff mbox series

Patch

diff --git a/libavcodec/cbs_h265_syntax_template.c b/libavcodec/cbs_h265_syntax_template.c
index 180a045c34..85b952e64c 100644
--- a/libavcodec/cbs_h265_syntax_template.c
+++ b/libavcodec/cbs_h265_syntax_template.c
@@ -80,7 +80,7 @@  static int FUNC(extension_data)(CodedBitstreamContext *ctx, RWContext *rw,
     }
 #else
     for (k = 0; k < current->bit_length; k++)
-        xu(1, extension_data, current->data[k / 8] >> (7 - k % 8), 0, 1, 0);
+        xu(1, extension_data, current->data[k / 8] >> (7 - k % 8) & 1, 0, 1, 0);
 #endif
     return 0;
 }