Skip to content

Conversation

@caglarpir
Copy link
Contributor

Some encodings like H.264 and H.265 support negative offsets.
We cannot rely on the version field since some encoders incorrectly set
ctts version to 0 instead of 1 even when using signed offsets.
Legitimate positive values are relatively small so we can assume the value is signed.

The unsigned to signed conversion could have been gated based on the encoding. However I chose not to since large positive values exceeding 2^31 is unrealistic.

For reference the stsd and ctts boxes from a video:

stsd box: Container:
    size = 233
    type = b'stsd' (total 4)
    data = Container:
        version = 0
        flags = 0
        entries = ListContainer:
            Container:
                format = b'hvc1' (total 4)

ctts box Container:
    size = 128480
    type = b'ctts' (total 4)
    data = Container:
        version = 0
        flags = 0
        entries = ListContainer:
            Container:
                sample_count = 1
                sample_offset = 0
            Container:
                sample_count = 1
                sample_offset = 60
            Container:
                sample_count = 1
                sample_offset = 0
            Container:
                sample_count = 1
                sample_offset = 4294967256
            Container:
                sample_count = 1
                sample_offset = 4294967276

@meta-cla meta-cla bot added the cla signed label Jan 9, 2026
Copy link
Member

@ptpt ptpt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor comments about naming and readability. Thanks for providing the test and the sample video


def _convert_to_signed_offset(offset: int) -> int:
"""Convert unsigned 32-bit offset to signed if high bit is set."""
if (offset & 0x80000000) == 0:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 << 31 is easier to understand than 0x80000000 IMO, or add it as a comment

if (offset & 0x80000000) == 0:
return offset
else:
return offset - 0x100000000
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

offset - (1 << 32)?

from . import construct_mp4_parser as cparser, simple_mp4_parser as sparser


def _convert_to_signed_offset(offset: int) -> int:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming can be generalized as _convert_to_int32(n: int) -> int

# ctts version to 0 instead of 1 even when using signed offsets.
# Leigitimate positive values are relatively small so we can assume the value is signed.
composition_offsets.append(
_convert_to_signed_offset(entry.sample_offset)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use entry["sample_offset"] to be consistent?

They are same, but looks in this context dict access is preferred

@ptpt
Copy link
Member

ptpt commented Jan 9, 2026

@caglarpir A more clean solution IMO is use signed int32 instead of unit32 in CTTS's construct parser directly:

"sample_offset" / C.Int32ub,

So a negative CTTS offset can be also correctly constructed when we build the mp4 data before uploading

@caglarpir caglarpir force-pushed the support-h265 branch 2 times, most recently from 2d97f5c to 878ce18 Compare January 9, 2026 13:19
Summary:
Some encodings like H.264 and H.265 support negative offsets.
We cannot rely on the version field since some encoders incorrectly set
ctts version to 0 instead of 1 even when using signed offsets.
Leigitimate positive values are relatively small so we can assume the value is signed.

The unsigned to signed conversion could have been gated based on the encoding. However I chose not to since large positive values exceeding 2^31 is unrealistic.

For reference the stsd and ctts boxes from a video:

stsd box: Container:
    size = 233
    type = b'stsd' (total 4)
    data = Container:
        version = 0
        flags = 0
        entries = ListContainer:
            Container:
                format = b'hvc1' (total 4)

ctts box Container:
    size = 128480
    type = b'ctts' (total 4)
    data = Container:
        version = 0
        flags = 0
        entries = ListContainer:
            Container:
                sample_count = 1
                sample_offset = 0
            Container:
                sample_count = 1
                sample_offset = 60
            Container:
                sample_count = 1
                sample_offset = 0
            Container:
                sample_count = 1
                sample_offset = 4294967256
            Container:
                sample_count = 1
                sample_offset = 4294967276
@caglarpir caglarpir merged commit 20482b0 into main Jan 9, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants