Example #1
0
def test_box_header_force_box_extended_size():
    bs = pack("uintbe:32, bytes:4, uintbe:64", 1, b"abcd", 32)

    box_header = BoxHeader()
    box_header.type = b"abcd"
    box_header.box_ext_size = 32

    assert box_header.type == b"abcd"
    assert box_header.box_size == 32
    assert box_header.box_ext_size == 32
    assert box_header.header_size == 16
    assert box_header.content_size == 16
    assert bytes(box_header) == bs.bytes
Example #2
0
def test_mett_box():
    bs = pack("uintbe:32, bytes:4, "
              "uintbe:8, uintbe:8, uintbe:8, uintbe:8, uintbe:8, uintbe:8, "
              "uintbe:16, "
              "bytes:1, bytes:11",
              28, b"mett",
              0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
              1,
              b'\0', b'image/heif\0')

    box_header = BoxHeader()
    mett = bx_def.METT(box_header)

    mett.header.type = b"mett"
    mett.data_reference_index = 1
    mett.content_encoding = b'\0'
    mett.mime_format = b'image/heif\0'

    mett.refresh_box_size()

    box = mett

    assert box.header.type == b"mett"
    assert box.header.box_size == 28

    assert box.data_reference_index == 1
    assert box.content_encoding == b'\0'
    assert box.mime_format == b'image/heif\0'

    assert len(box.boxes) == 0

    assert bytes(next(Parser.parse(bs))) == bs.bytes
    assert bytes(box) == bs.bytes
Example #3
0
def test_sbtt_box():
    bs = pack("uintbe:32, bytes:4, "
              "uintbe:8, uintbe:8, uintbe:8, uintbe:8, uintbe:8, uintbe:8, "
              "uintbe:16, "
              "bytes:1, bytes:11",
              28, b"sbtt",
              0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
              1,
              b'\0', b'text/plain\0')

    box_header = BoxHeader()
    stxt = bx_def.STXT(box_header)

    stxt.header.type = b"sbtt"
    stxt.data_reference_index = 1
    stxt.content_encoding = b'\0'
    stxt.mime_format = b'text/plain\0'

    stxt.refresh_box_size()

    box = stxt

    assert box.header.type == b"sbtt"
    assert box.header.box_size == 28

    assert box.data_reference_index == 1
    assert box.content_encoding == b'\0'
    assert box.mime_format == b'text/plain\0'

    assert len(box.boxes) == 0

    assert bytes(next(Parser.parse(bs))) == bs.bytes
    assert bytes(box) == bs.bytes
Example #4
0
def test_ftyp_box_w_no_type():
    bs = pack("uintbe:32, bytes:4, "
              "bytes:4, uintbe:32, bytes:8",
              24, b"ftyp",
              b"bzna", 10, b"mp42mp41")

    box_header = BoxHeader()
    ftyp = bx_def.FTYP(box_header)
    ftyp.header.type = b"ftyp"
    ftyp.major_brand = (1652190817,)            # b"bzna"
    ftyp.minor_version = (10,)
    ftyp.compatible_brands = ([1836069938,      # b"mp42"
                               1836069937],)    # b"mp41"
    ftyp.refresh_box_size()

    box = ftyp

    assert box.header.type == b"ftyp"
    assert box.header.box_size == 24
    assert box.major_brand == 1652190817            # b"bzna"
    assert box.minor_version == 10
    assert box.compatible_brands == [1836069938,    # b"mp42"
                                     1836069937]    # b"mp41"

    assert bytes(next(Parser.parse(bs))) == bs.bytes
    assert bytes(box) == bs.bytes
Example #5
0
def test_sample_entry_box():
    bs = pack("uintbe:32, bytes:4, "
              "uintbe:8, uintbe:8, uintbe:8, uintbe:8, uintbe:8, uintbe:8, "
              "uintbe:16",
              16, b"____",
              0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
              1)

    box_header = BoxHeader()
    sample_entry_box = bx_def.SampleEntryBox(box_header)

    sample_entry_box.header.type = b"____"
    sample_entry_box.data_reference_index = 1

    sample_entry_box.refresh_box_size()

    box = sample_entry_box

    assert box.header.type == b"____"
    assert box.header.box_size == 16

    assert box.data_reference_index == 1

    assert len(box.boxes) == 0

    assert bytes(box) == bs.bytes
def tune_video(filename, im_width, im_height):
    bstr = ConstBitStream(filename=filename)
    boxes = [box for box in Parser.parse(bstr)]

    # skip 'free' box
    boxes = boxes[:1] + boxes[2:]

    ftyp = boxes[0]
    mdat = boxes[1]
    moov = boxes[2]

    # pop 'udta' box
    moov.boxes.pop()

    trak = moov.boxes[-1]

    # remove edts
    mdia = trak.pop()
    trak.pop()
    trak.append(mdia)

    for box in boxes:
        box.load(bstr)
        box.refresh_box_size()

    ftyp.major_brand = 1769172845  # b"isom"
    ftyp.minor_version = 0
    ftyp.compatible_brands = [1769172845]  # b"isom"
    ftyp.refresh_box_size()

    # moov.trak.mdia.minf.stbl
    stbl = moov.boxes[-1].boxes[-1].boxes[-1].boxes[-1]

    # moov.trak.mdia.minf.stbl.stsd.hvc1
    hvc1 = stbl.boxes[0].boxes[0]

    clap = bx_def.CLAP(BoxHeader())
    clap.header.type = b"clap"
    clap.clean_aperture_width_n = im_width
    clap.clean_aperture_width_d = 1
    clap.clean_aperture_height_n = im_height
    clap.clean_aperture_height_d = 1
    clap.horiz_off_n = im_width - 512
    clap.horiz_off_d = 2
    clap.vert_off_n = im_height - 512
    clap.vert_off_d = 2

    # insert clap before pasp
    pasp = hvc1.pop()
    hvc1.append(clap)
    hvc1.append(pasp)

    stco = stbl.boxes[-1]
    stco.entries[
        0].chunk_offset = ftyp.header.box_size + mdat.header.header_size

    moov.refresh_box_size()

    return b''.join(bytes(box) for box in boxes)
Example #7
0
def test_avc1_box():
    bs = pack("uintbe:32, bytes:4, "
              "uintbe:8, uintbe:8, uintbe:8, uintbe:8, uintbe:8, uintbe:8, "
              "uintbe:16, "
              "uintbe:16, uintbe:16, uintbe:32, uintbe:32, uintbe:32, "
              "uintbe:16, uintbe:16, uintbe:16, uintbe:16, uintbe:16, uintbe:16, "
              "uintbe:32, "
              "uintbe:16, bytes:32, uintbe:16, "
              "intbe:16",
              86, b"avc1",
              0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
              1,
              0x0, 0x0, 0x0, 0x0, 0x0,
              512, 512, 72, 0, 72, 0,
              0x0,
              1, b'\0' * 32, 24,
              -1)

    box_header = BoxHeader()
    avc1 = bx_def.AVC1(box_header)

    avc1.header.type = b"avc1"
    avc1.data_reference_index = 1
    avc1.width = 512
    avc1.height = 512
    avc1.horizresolution = [72, 0]
    avc1.vertresolution = [72, 0]
    avc1.frame_count = 1
    avc1.compressorname = b'\0' * 32
    avc1.depth = 24

    avc1.refresh_box_size()

    box = avc1

    assert box.header.type == b"avc1"
    assert box.header.box_size == 86

    assert box.data_reference_index == 1
    assert box.width == 512
    assert box.height == 512
    assert box.horizresolution == [72, 0]
    assert box.vertresolution == [72, 0]
    assert box.frame_count == 1
    assert box.compressorname == b'\0' * 32
    assert box.depth == 24

    assert len(box.boxes) == 0

    assert bytes(next(Parser.parse(bs))) == bs.bytes
    assert bytes(box) == bs.bytes
Example #8
0
def test_visual_sample_entry_box():
    bs = pack("uintbe:32, bytes:4, "
              "uintbe:8, uintbe:8, uintbe:8, uintbe:8, uintbe:8, uintbe:8, "
              "uintbe:16, "
              "uintbe:16, uintbe:16, uintbe:32, uintbe:32, uintbe:32, "
              "uintbe:16, uintbe:16, uintbe:16, uintbe:16, uintbe:16, uintbe:16, "
              "uintbe:32, "
              "uintbe:16, bytes:32, uintbe:16, "
              "intbe:16",
              86, b"____",
              0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
              1,
              0x0, 0x0, 0x0, 0x0, 0x0,
              512, 512, 72, 0, 72, 0,
              0x0,
              1, b'\0' * 32, 24,
              -1)

    box_header = BoxHeader()
    visual_sample_entry_box = bx_def.VisualSampleEntryBox(box_header)

    visual_sample_entry_box.header.type = b"____"
    visual_sample_entry_box.data_reference_index = 1
    visual_sample_entry_box.width = 512
    visual_sample_entry_box.height = 512
    visual_sample_entry_box.horizresolution = [72, 0]
    visual_sample_entry_box.vertresolution = [72, 0]
    visual_sample_entry_box.frame_count = 1
    visual_sample_entry_box.compressorname = b'\0' * 32
    visual_sample_entry_box.depth = 24

    visual_sample_entry_box.refresh_box_size()

    box = visual_sample_entry_box

    assert box.header.type == b"____"
    assert box.header.box_size == 86

    assert box.data_reference_index == 1
    assert box.width == 512
    assert box.height == 512
    assert box.horizresolution == [72, 0]
    assert box.vertresolution == [72, 0]
    assert box.frame_count == 1
    assert box.compressorname == b'\0' * 32
    assert box.depth == 24

    assert len(box.boxes) == 0

    assert bytes(box) == bs.bytes
Example #9
0
def test_box_header_extended_user_type_w_drop():
    bs = pack("uintbe:32, bytes:4, uintbe:64, bytes:16", 1, b"uuid",
              MAX_UINT_32 + 1, b":benzina\x00\x00\x00\x00\x00\x00\x00\x00")

    box_header = BoxHeader()
    box_header.type = b"uuid:benzina\x00\x00\x00\x00\x00\x00\x00\x00"
    box_header.box_size = MAX_UINT_32 + 1

    box_header.type = b"abcd"
    box_header.box_size = 100

    box_header.type = b"uuid:benzina\x00\x00\x00\x00\x00\x00\x00\x00"
    box_header.box_size = MAX_UINT_32 + 1

    assert box_header.type == b"uuid:benzina\x00\x00\x00\x00\x00\x00\x00\x00"
    assert box_header.box_size == MAX_UINT_32 + 1
    assert box_header.header_size == 32
    assert box_header.content_size == MAX_UINT_32 + 1 - 32
    assert bytes(box_header) == bs.bytes
Example #10
0
def test_box_header():
    bs = pack("uintbe:32, bytes:4", 100, b"abcd")

    box_header = BoxHeader()
    box_header.type = b"abcd"
    box_header.box_size = 100

    assert box_header.type == b"abcd"
    assert box_header.box_size == 100
    assert box_header.header_size == 8
    assert box_header.content_size == 92
    assert bytes(box_header) == bs.bytes

    box_header = BoxHeader()
    box_header.box_size = 100
    box_header.type = b"abcd"

    assert box_header.type == b"abcd"
    assert box_header.box_size == 100
    assert box_header.header_size == 8
    assert box_header.content_size == 92
    assert bytes(box_header) == bs.bytes
Example #11
0
def test_box_header_w_drop():
    bs = pack("uintbe:32, bytes:4", 100, b"abcd")

    box_header = BoxHeader()
    box_header.type = b"uuid:benzina\x00\x00\x00\x00\x00\x00\x00\x00"
    box_header.box_size = MAX_UINT_32 + 1

    box_header.type = b"abcd"
    box_header.box_size = 100

    assert box_header.type == b"abcd"
    assert box_header.box_size == 100
    assert box_header.header_size == 8
    assert box_header.content_size == 92
    assert bytes(box_header) == bs.bytes
Example #12
0
def make_text_trak(creation_time, modification_time, name,
                   samples_sizes, samples_offsets):
    trak = make_trak(creation_time, modification_time, samples_sizes, samples_offsets)

    # MOOV.TRAK.MDIA
    mdia = trak.boxes[-1]

    # MOOV.TRAK.MDIA.HDLR
    hdlr = mdia.boxes[1]
    hdlr.handler_type = (b"text",)
    hdlr.name = (name,)

    # MOOV.TRAK.MDIA.MINF
    minf = mdia.boxes[-1]

    # MOOV.TRAK.MDIA.MINF.NMHD
    nmhd = bx_def.NMHD(FullBoxHeader())
    minf.boxes[0] = nmhd

    nmhd.header.type = b"nmhd"
    nmhd.header.version = (0,)
    nmhd.header.flags = (b"\x00\x00\x00",)

    # MOOV.TRAK.MDIA.MINF.STBL
    stbl = minf.boxes[-1]

    # MOOV.TRAK.MDIA.MINF.STBL.STSD
    stsd = stbl.boxes[0]

    # MOOV.TRAK.MDIA.MINF.STBL.STSD.STXT
    stxt = bx_def.STXT(BoxHeader())

    stxt.header.type = b"stxt"
    stxt.data_reference_index = (1,)
    stxt.content_encoding = (b'\0',)
    stxt.mime_format = (b'text/plain\0',)

    stsd.append(stxt)

    return trak
Example #13
0
def test_pasp_box():
    bs = pack("uintbe:32, bytes:4, uintbe:32, uintbe:32",
              16, b"pasp", 150, 157)

    box_header = BoxHeader()
    pasp = bx_def.PASP(box_header)
    pasp.header.type = b"pasp"

    pasp.h_spacing = 150
    pasp.v_spacing = 157

    pasp.refresh_box_size()

    box = pasp

    assert box.header.type == b"pasp"
    assert box.header.box_size == 16
    assert box.h_spacing == 150
    assert box.v_spacing == 157

    assert bytes(next(Parser.parse(bs))) == bs.bytes
    assert bytes(box) == bs.bytes
Example #14
0
def test_clap_box():
    bs = pack("uintbe:32, bytes:4, "
              "uintbe:32, uintbe:32, uintbe:32, uintbe:32, "
              "intbe:32, uintbe:32, intbe:32, uintbe:32",
              40, b"clap",
              500, 1, 333, 1,
              -12, 2, -179, 2)

    box_header = BoxHeader()
    clap = bx_def.CLAP(box_header)
    clap.header.type = b"clap"

    clap.clean_aperture_width_n = 500
    clap.clean_aperture_width_d = 1
    clap.clean_aperture_height_n = 333
    clap.clean_aperture_height_d = 1
    clap.horiz_off_n = -12
    clap.horiz_off_d = 2
    clap.vert_off_n = -179
    clap.vert_off_d = 2

    clap.refresh_box_size()

    box = clap

    assert box.header.type == b"clap"
    assert box.header.box_size == 40
    assert box.clean_aperture_width_n == 500
    assert box.clean_aperture_width_d == 1
    assert box.clean_aperture_height_n == 333
    assert box.clean_aperture_height_d == 1
    assert box.horiz_off_n == -12
    assert box.horiz_off_d == 2
    assert box.vert_off_n == -179
    assert box.vert_off_d == 2

    assert bytes(next(Parser.parse(bs))) == bs.bytes
    assert bytes(box) == bs.bytes
Example #15
0
def make_vide_trak(creation_time, modification_time, name,
                   samples_sizes, samples_offsets):
    trak = make_trak(creation_time, modification_time, samples_sizes, samples_offsets)

    # MOOV.TRAK.MDIA
    mdia = trak.boxes[-1]

    # MOOV.TRAK.MDIA.HDLR
    hdlr = mdia.boxes[1]
    hdlr.handler_type = (b"vide",)
    hdlr.name = (name,)

    # MOOV.TRAK.MDIA.MINF
    minf = mdia.boxes[-1]

    # MOOV.TRAK.MDIA.MINF.VMHD
    vmhd = bx_def.VMHD(FullBoxHeader())
    minf.boxes[0] = vmhd

    vmhd.header.type = b"vmhd"
    vmhd.header.version = (0,)
    # flag is 1
    vmhd.header.flags = (b"\x00\x00\x01",)
    vmhd.graphicsmode = (0,)
    vmhd.opcolor = ([0, 0, 0],)

    # MOOV.TRAK.MDIA.MINF
    stbl = minf.boxes[-1]

    # MOOV.TRAK.MDIA.MINF.STBL.STSD
    stsd = stbl.boxes[0]

    # MOOV.TRAK.MDIA.MINF.STBL.STSD.AVC1
    avc1 = bx_def.AVC1(BoxHeader())

    avc1.header.type = b"avc1"
    avc1.data_reference_index = (1,)
    avc1.width = (-1,)
    avc1.height = (-1,)
    avc1.horizresolution = ([72, 0],)
    avc1.vertresolution = ([72, 0],)
    avc1.frame_count = (1,)
    avc1.compressorname = (b'\0' * 32,)
    avc1.depth = (24,)

    # TODO: implement MOOV.TRAK.MDIA.MINF.STBL.STSD.AVC1.AVCC
    avcC = bx_def.UnknownBox(BoxHeader())
    avcC.header.type = b"avcC"
    avcC.payload = b'\x01d\x10\x16\xff\xe1\x00\x1bgd\x10\x16\xac\xb8\x10\x02' \
                   b'\r\xff\x80K\x00N\xb6\xa5\x00\x00\x03\x00\x01\x00\x00\x03' \
                   b'\x00\x02\x04\x01\x00\x07h\xee\x01\x9cL\x84\xc0'

    avc1.append(avcC)

    # MOOV.TRAK.MDIA.MINF.STBL.STSD.AVC1.PASP
    pasp = bx_def.PASP(BoxHeader())
    pasp.header.type = b"pasp"
    pasp.h_spacing = (1,)
    pasp.v_spacing = (1,)

    avc1.append(pasp)

    stsd.append(avc1)

    return trak
Example #16
0
def test_hvcc_box():
    bs = pack("uintbe:32, bytes:4, "
              "uintbe:8, "
              "int:2, int:1, int:5, uintbe:32, uintbe:48, uintbe:8, "
              "bits:4, int:12, bits:6, int:2, bits:6, int:2, bits:5, int:3, bits:5, int:3, "
              "uintbe:16, uint:2, uint:3, uint:1, uint:2, uint:8, "

              "uint:1, bits:1, uint:6, uintbe:16, "

              "uint:16, bytes:3",
              39, b"hvcc",
              1,
              0, 0, 3, 1879048192, 193514046488576, 90,
              '0b1111', 0, '0b111111', 0, '0b111111', 1, '0b11111', 0, '0b11111', 0,
              0, 0, 1, 0, 3, 1,

              1, '0b0', 32, 1,

              3, b"321")

    box_header = BoxHeader()
    hvcc = bx_def.HVCC(box_header)

    hvcc.header.type = b"hvcc"
    hvcc.header.box_size = 39

    hvcc.configuration_version = 1

    hvcc.general_profile_space = 0
    hvcc.general_tier_flag = 0
    hvcc.general_profile_idc = 3
    hvcc.general_profile_compatibility_flags = 1879048192
    hvcc.general_constraint_indicator_flags = 193514046488576
    hvcc.general_level_idc = 90

    hvcc.min_spatial_segmentation_idc = 0
    hvcc.parallelism_type = 0
    hvcc.chroma_format = 1
    hvcc.bit_depth_luma_minus_8 = 0
    hvcc.bit_depth_chroma_minus_8 = 0

    hvcc.avg_frame_rate = 0
    hvcc.constant_frame_rate = 0
    hvcc.num_temporal_layers = 1
    hvcc.temporal_id_nested = 0
    hvcc.length_size_minus_one = 3

    array = hvcc.append_and_return()
    array.array_completeness = 1
    array.nal_unit_type = 32

    nalu = array.append_and_return()
    nalu.nal_unit_length = 3
    nalu.nal_unit = (b"321", "bytes:3")

    box = hvcc

    assert box.header.type == b"hvcc"
    assert box.header.box_size == 39

    assert box.configuration_version == 1

    assert box.general_profile_space == 0
    assert box.general_tier_flag == 0
    assert box.general_profile_idc == 3
    assert box.general_profile_compatibility_flags == 1879048192
    assert box.general_constraint_indicator_flags == 193514046488576
    assert box.general_level_idc == 90

    assert box.min_spatial_segmentation_idc == 0
    assert box.parallelism_type == 0
    assert box.chroma_format == 1
    assert box.bit_depth_luma_minus_8 == 0
    assert box.bit_depth_chroma_minus_8 == 0

    assert box.avg_frame_rate == 0
    assert box.constant_frame_rate == 0
    assert box.num_temporal_layers == 1
    assert box.temporal_id_nested == 0
    assert box.length_size_minus_one == 3
    assert box.num_of_arrays == 1
    assert len(box.arrays) == 1

    array = box.arrays[0]
    assert array.array_completeness == 1
    assert array.nal_unit_type == 32
    assert array.num_nalus == 1
    assert len(array.nalus) == 1

    nalu = array.nalus[0]
    assert nalu.nal_unit_length == 3
    assert nalu.nal_unit == b"321"

    parsed_box = next(Parser.parse(bs))
    parsed_box.load(bs)
    assert bytes(parsed_box) == bs.bytes
    assert bytes(box) == bs.bytes
Example #17
0
def make_trak(creation_time, modification_time, samples_sizes, samples_offsets):
    # MOOV.TRAK
    trak = bx_def.TRAK(BoxHeader())
    trak.header.type = b"trak"

    # MOOV.TRAK.TKHD
    tkhd = bx_def.TKHD(FullBoxHeader())

    tkhd.header.type = b"tkhd"
    tkhd.header.version = (1,)
    tkhd.header.flags = (b"\x00\x00\x00",)

    tkhd.creation_time = (creation_time,)
    tkhd.modification_time = (modification_time,)
    tkhd.track_id = (-1,)
    tkhd.duration = (20 * len(samples_sizes),)

    tkhd.layer = (0,)
    tkhd.alternate_group = (0,)
    tkhd.volume = ([0, 0],)

    # TODO: validate matrix (and check if those are 16.16 floats)
    tkhd.matrix = ([65536, 0, 0, 0, 65536, 0, 0, 0, 1073741824],)

    # TODO: make sure that this is the canvas size
    tkhd.width = ([-1, 0],)
    tkhd.height = ([-1, 0],)

    trak.append(tkhd)

    # # MOOV.TRAK.EDTS
    # edts = bx_def.EDTS(BoxHeader())
    # edts.header.type = b"edts"
    #
    # # MOOV.TRAK.EDTS.ELST
    # elst = bx_def.ELST(FullBoxHeader())
    #
    # elst.header.type = b"elst"
    # elst.header.version = (1,)
    # elst.header.flags = (b"\x00\x00\x00",)
    #
    # entry = elst.append_and_return()
    # entry.segment_duration = (60,)
    # entry.media_time = (0,)
    # entry.media_rate_integer = (1,)
    # entry.media_rate_fraction = (0,)
    #
    # edts.append(elst)
    #
    # trak.append(edts)

    # MOOV.TRAK.MDIA
    mdia = bx_def.MDIA(BoxHeader())
    mdia.header.type = b"mdia"

    # MOOV.TRAK.MDIA.MDHD
    mdhd = bx_def.MDHD(FullBoxHeader())

    mdhd.header.type = b"mdhd"
    mdhd.header.version = (1,)
    mdhd.header.flags = (b"\x00\x00\x00",)

    mdhd.creation_time = (creation_time,)
    mdhd.modification_time = (modification_time,)
    mdhd.timescale = (20,)
    mdhd.duration = (20 * len(samples_sizes),)

    # TODO: check the language code
    mdhd.language = ([21, 14, 4],)
    mdhd.pre_defined = (0,)

    mdia.append(mdhd)

    # MOOV.TRAK.MDIA.HDLR
    hdlr = bx_def.HDLR(FullBoxHeader())

    hdlr.header.type = b"hdlr"
    hdlr.header.version = (0,)
    hdlr.header.flags = (b"\x00\x00\x00",)
    hdlr.pre_defined = (0,)
    hdlr.handler_type = (b"____",)
    hdlr.name = (b"\0",)

    mdia.append(hdlr)

    # MOOV.TRAK.MDIA.MINF
    minf = bx_def.MINF(BoxHeader())
    minf.header.type = b"minf"

    # MOOV.TRAK.MDIA.MINF._MHD (placeholder)
    _mhd = bx_def.UnknownBox(BoxHeader())
    _mhd.header.type = b"_mhd"

    minf.append(_mhd)

    # MOOV.TRAK.MDIA.MINF.DINF
    dinf = bx_def.DINF(BoxHeader())
    dinf.header.type = b"dinf"

    # MOOV.TRAK.MDIA.MINF.DINF.DREF
    dref = bx_def.DREF(FullBoxHeader())
    dref.header.type = b"dref"
    dref.header.version = (0,)
    dref.header.flags = (b"\x00\x00\x00",)

    # MOOV.TRAK.MDIA.MINF.DINF.DREF.URL_
    url_ = bx_def.URL_(FullBoxHeader())

    url_.header.type = b"url "
    url_.header.version = (0,)
    # TODO:  validate that this flags means that the data is in the same file
    url_.header.flags = (b"\x00\x00\x01",)

    dref.append(url_)

    dinf.append(dref)

    minf.append(dinf)

    # MOOV.TRAK.MDIA.MINF.STBL
    stbl = bx_def.STBL(BoxHeader())
    stbl.header.type = b"stbl"

    # MOOV.TRAK.MDIA.MINF.STBL.STSD
    stsd = bx_def.STSD(FullBoxHeader())

    stsd.header.type = b"stsd"
    stsd.header.version = (0,)
    stsd.header.flags = (b"\x00\x00\x00",)

    stbl.append(stsd)

    # MOOV.TRAK.MDIA.MINF.STBL.STTS
    stts = bx_def.STTS(FullBoxHeader())

    stts.header.type = b"stts"
    stts.header.version = (0,)
    stts.header.flags = (b"\x00\x00\x00",)

    entry = stts.append_and_return()
    # imges count
    entry.sample_count = (len(samples_sizes),)
    # 1 img / sec
    entry.sample_delta = (20,)

    stbl.append(stts)

    # MOOV.TRAK.MDIA.MINF.STBL.STSZ
    stsz = bx_def.STSZ(FullBoxHeader())

    stsz.header.type = b"stsz"
    stsz.header.version = (0,)
    stsz.header.flags = (b"\x00\x00\x00",)

    stsz.sample_size = (0,)

    for samples_size in samples_sizes:
        sample = stsz.append_and_return()
        sample.entry_size = (samples_size,)

    stbl.append(stsz)

    # MOOV.TRAK.MDIA.MINF.STBL.STSC
    stsc = bx_def.STSC(FullBoxHeader())

    stsc.header.type = b"stsc"
    stsc.header.version = (0,)
    stsc.header.flags = (b"\x00\x00\x00",)

    entry = stsc.append_and_return()
    entry.first_chunk = (1,)
    entry.samples_per_chunk = (1,)
    entry.sample_description_index = (1,)

    stbl.append(stsc)

    # MOOV.TRAK.MDIA.MINF.STBL.STCO
    co = bx_def.STCO(FullBoxHeader())
    co.header.type = b"stco"
    co.header.version = (0,)
    co.header.flags = (b"\x00\x00\x00",)

    for offset in gen_sample_offsets(samples_sizes, samples_offsets):
        # If the offset gets bigger than 2^32-1, use the 64 bits implementation
        if offset > MAX_UINT_32:
            # MOOV.TRAK.MDIA.MINF.STBL.CO64
            co = bx_def.CO64(FullBoxHeader())
            co.header.type = b"co64"
            co.header.version = (0,)
            co.header.flags = (b"\x00\x00\x00",)
            break
        entry = co.append_and_return()
        entry.chunk_offset = (offset,)

    # If the offset gets bigger than 2^32-1, add again using co64
    if isinstance(co, bx_def.CO64):
        for offset in gen_sample_offsets(samples_sizes, samples_offsets):
            entry = co.append_and_return()
            entry.chunk_offset = (offset,)

    stbl.append(co)

    minf.append(stbl)

    mdia.append(minf)

    trak.append(mdia)

    return trak
Example #18
0
def test_mp4_dataset():
    creation_time = to_mp4_time(datetime(2019, 9, 15, 0, 0, 0))
    modification_time = to_mp4_time(datetime(2019, 9, 16, 0, 0, 0))

    # FTYP
    ftyp = bx_def.FTYP(BoxHeader())
    ftyp.header.type = b"ftyp"
    ftyp.major_brand = 1769172845           # b"isom"
    ftyp.minor_version = 0
    ftyp.compatible_brands = [1652190817,   # b"bzna"
                              1769172845]   # b"isom"

    ftyp.refresh_box_size()

    assert ftyp.header.type == b"ftyp"
    assert ftyp.header.box_size == 24
    assert ftyp.major_brand == 1769172845           # b"isom"
    assert ftyp.minor_version == 0
    assert ftyp.compatible_brands == [1652190817,   # b"bzna"
                                      1769172845]   # b"isom"
    assert bytes(ftyp) == pack("uintbe:32, bytes:4, bytes:4, uintbe:32, "
                               "bytes:8",
                               24, b"ftyp", b"isom", 0,
                               b"bznaisom")

    # MDAT
    mdat = bx_def.MDAT(BoxHeader())
    mdat.header.type = b"mdat"

    data = []

    with open("tests/data/small_vid_mdat_im0", "rb") as f:
        data.append(f.read())
    with open("tests/data/small_vid_mdat_im1", "rb") as f:
        data.append(f.read())
    with open("tests/data/small_vid_mdat_im2", "rb") as f:
        data.append(f.read())

    data.append(b"/path/image_1_name.JPEG")
    data.append(b"/path/image_2_name.JPEG")
    data.append(b"/path/image_3_name.JPEG")

    data.append((0).to_bytes(8, byteorder="little"))
    data.append((1).to_bytes(8, byteorder="little"))
    data.append((0).to_bytes(8, byteorder="little"))

    mdat.data = b''.join(data)

    mdat.refresh_box_size()

    assert mdat.header.type == b"mdat"
    assert mdat.header.box_size == 8 + sum(len(entry) for entry in data)

    # MOOV
    moov = bx_def.MOOV(BoxHeader())
    moov.header.type = b"moov"

    # MOOV.MVHD
    mvhd = make_mvhd(creation_time, modification_time, 3)
    # == total number of tracks
    mvhd.next_track_id = 5

    assert mvhd.next_track_id == 5

    moov.append(mvhd)

    # MOOV.TRAK
    offset = ftyp.header.box_size + mdat.header.header_size
    sizes = [198297, 127477, 192476]
    trak = make_vide_trak(creation_time, modification_time, b"VideoHandler\0",
                          sizes, offset)

    # MOOV.TRAK.TKHD
    tkhd = trak.boxes[0]

    # "\x00\x00\x01" trak is enabled
    # "\x00\x00\x02" trak is used in the presentation
    # "\x00\x00\x04" trak is used in the preview
    # "\x00\x00\x08" trak size in not in pixel but in aspect ratio
    tkhd.header.flags = b"\x00\x00\x03"

    tkhd.track_id = 1

    # TODO: make sure that this is the canvas size
    tkhd.width = [512, 0]
    tkhd.height = [512, 0]

    tkhd.refresh_box_size()

    assert tkhd.header.type == b"tkhd"
    assert tkhd.header.box_size == 104
    assert tkhd.header.flags == b"\x00\x00\x03"

    assert tkhd.track_id == 1

    assert tkhd.width == 512
    assert tkhd.height == 512

    assert bytes(tkhd) == \
           pack("uintbe:32, bytes:4, uintbe:8, bits:24, "
                "uintbe:64, uintbe:64, uintbe:32, "
                "bits:32, "
                "uintbe:64, "
                "bits:32, bits:32, "
                "uintbe:16, uintbe:16, uintbe:8, uintbe:8, "
                "bits:16, "
                "uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, "
                "uintbe:16, uintbe:16, uintbe:16, uintbe:16",
                104, b"tkhd", 1, b"\x00\x00\x03",
                creation_time, modification_time, 1,
                b"\x00" * 4,
                60,
                b"\x00" * 4, b"\x00" * 4,
                0, 0, 0, 0,
                b"\x00" * 2,
                65536, 0, 0, 0, 65536, 0, 0, 0, 1073741824,
                512, 0, 512, 0)

    # MOOV.TRAK.MDIA.MINF.STBL.STSD.AVC1
    avc1 = trak.boxes[-1].boxes[-1].boxes[-1].boxes[0].boxes[0]
    avc1.width = 512
    avc1.height = 512

    assert avc1.header.type == b"avc1"
    assert avc1.width == 512
    assert avc1.height == 512

    moov.append(trak)

    # MOOV.TRAK
    trak = make_meta_trak(creation_time, modification_time, b"bzna_inputs\0",
                          sizes, offset)

    # MOOV.TRAK.TKHD
    tkhd = trak.boxes[0]

    # "\x00\x00\x01" trak is enabled
    # "\x00\x00\x02" trak is used in the presentation
    # "\x00\x00\x04" trak is used in the preview
    # "\x00\x00\x08" trak size in not in pixel but in aspect ratio
    tkhd.header.flags = b"\x00\x00\x00"

    tkhd.track_id = 2

    # TODO: make sure that this is the canvas size
    tkhd.width = [0, 0]
    tkhd.height = [0, 0]

    tkhd.refresh_box_size()

    assert tkhd.header.type == b"tkhd"
    assert tkhd.header.box_size == 104
    assert tkhd.header.flags == b"\x00\x00\x00"

    assert tkhd.track_id == 2

    assert tkhd.width == 0
    assert tkhd.height == 0

    assert bytes(tkhd) == \
           pack("uintbe:32, bytes:4, uintbe:8, bits:24, "
                "uintbe:64, uintbe:64, uintbe:32, "
                "bits:32, "
                "uintbe:64, "
                "bits:32, bits:32, "
                "uintbe:16, uintbe:16, uintbe:8, uintbe:8, "
                "bits:16, "
                "uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, "
                "uintbe:16, uintbe:16, uintbe:16, uintbe:16",
                104, b"tkhd", 1, b"\x00\x00\x00",
                creation_time, modification_time, 2,
                b"\x00" * 4,
                60,
                b"\x00" * 4, b"\x00" * 4,
                0, 0, 0, 0,
                b"\x00" * 2,
                65536, 0, 0, 0, 65536, 0, 0, 0, 1073741824,
                0, 0, 0, 0)

    # MOOV.TRAK.MDIA.MDHD
    mdhd = trak.boxes[-1].boxes[0]
    mdhd.timescale = 20
    mdhd.duration = 60

    mdhd.refresh_box_size()

    assert mdhd.header.type == b"mdhd"
    assert mdhd.header.box_size == 44
    assert mdhd.timescale == 20
    assert mdhd.duration == 60

    assert bytes(mdhd) == \
           pack("uintbe:32, bytes:4, uintbe:8, bits:24, "
                "uintbe:64, uintbe:64, uintbe:32, uintbe:64, "
                "bits:1, uint:5, uint:5, uint:5, "
                "bits:16",
                44, b"mdhd", 1, b"\x00\x00\x00",
                creation_time, modification_time, 20, 60,
                0x1, 21, 14, 4,
                b"\x00" * 2)

    # MOOV.TRAK.MDIA.MINF.STBL.STSD.METT
    mett = trak.boxes[-1].boxes[-1].boxes[-1].boxes[0].boxes[0]
    mett.mime_format = b'video/h264\0'
    mett.refresh_box_size()

    assert mett.header.type == b"mett"
    assert mett.header.box_size == 28
    assert mett.mime_format == b'video/h264\0'

    moov.append(trak)

    # MOOV.TRAK
    offset += sum(sizes)
    sizes = [23, 23, 23]
    trak = make_text_trak(creation_time, modification_time, b"bzna_fnames\0",
                          sizes, offset)

    # MOOV.TRAK.TKHD
    tkhd = trak.boxes[0]

    # "\x00\x00\x01" trak is enabled
    # "\x00\x00\x02" trak is used in the presentation
    # "\x00\x00\x04" trak is used in the preview
    # "\x00\x00\x08" trak size in not in pixel but in aspect ratio
    tkhd.header.flags = b"\x00\x00\x00"

    tkhd.track_id = 3

    # TODO: make sure that this is the canvas size
    tkhd.width = [0, 0]
    tkhd.height = [0, 0]

    tkhd.refresh_box_size()

    assert tkhd.header.type == b"tkhd"
    assert tkhd.header.box_size == 104
    assert tkhd.header.flags == b"\x00\x00\x00"

    assert tkhd.track_id == 3

    assert tkhd.width == 0
    assert tkhd.height == 0

    assert bytes(tkhd) == \
           pack("uintbe:32, bytes:4, uintbe:8, bits:24, "
                "uintbe:64, uintbe:64, uintbe:32, "
                "bits:32, "
                "uintbe:64, "
                "bits:32, bits:32, "
                "uintbe:16, uintbe:16, uintbe:8, uintbe:8, "
                "bits:16, "
                "uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, "
                "uintbe:16, uintbe:16, uintbe:16, uintbe:16",
                104, b"tkhd", 1, b"\x00\x00\x00",
                creation_time, modification_time, 3,
                b"\x00" * 4,
                60,
                b"\x00" * 4, b"\x00" * 4,
                0, 0, 0, 0,
                b"\x00" * 2,
                65536, 0, 0, 0, 65536, 0, 0, 0, 1073741824,
                0, 0, 0, 0)

    # MOOV.TRAK.MDIA.MDHD
    mdhd = trak.boxes[-1].boxes[0]
    mdhd.timescale = 20
    mdhd.duration = 60

    mdhd.refresh_box_size()

    assert mdhd.header.type == b"mdhd"
    assert mdhd.header.box_size == 44
    assert mdhd.timescale == 20
    assert mdhd.duration == 60

    assert bytes(mdhd) == \
           pack("uintbe:32, bytes:4, uintbe:8, bits:24, "
                "uintbe:64, uintbe:64, uintbe:32, uintbe:64, "
                "bits:1, uint:5, uint:5, uint:5, "
                "bits:16",
                44, b"mdhd", 1, b"\x00\x00\x00",
                creation_time, modification_time, 20, 60,
                0x1, 21, 14, 4,
                b"\x00" * 2)

    moov.append(trak)

    # MOOV.TRAK
    offset += sum(sizes)
    sizes = [8, 8, 8]
    trak = make_text_trak(creation_time, modification_time, b"bzna_targets\0",
                          sizes, offset)

    # MOOV.TRAK.TKHD
    tkhd = trak.boxes[0]

    # "\x00\x00\x01" trak is enabled
    # "\x00\x00\x02" trak is used in the presentation
    # "\x00\x00\x04" trak is used in the preview
    # "\x00\x00\x08" trak size in not in pixel but in aspect ratio
    tkhd.header.flags = b"\x00\x00\x00"

    tkhd.track_id = 4

    # TODO: make sure that this is the canvas size
    tkhd.width = [0, 0]
    tkhd.height = [0, 0]

    tkhd.refresh_box_size()

    assert tkhd.header.type == b"tkhd"
    assert tkhd.header.box_size == 104
    assert tkhd.header.flags == b"\x00\x00\x00"

    assert tkhd.track_id == 4

    assert tkhd.width == 0
    assert tkhd.height == 0

    assert bytes(tkhd) == \
           pack("uintbe:32, bytes:4, uintbe:8, bits:24, "
                "uintbe:64, uintbe:64, uintbe:32, "
                "bits:32, "
                "uintbe:64, "
                "bits:32, bits:32, "
                "uintbe:16, uintbe:16, uintbe:8, uintbe:8, "
                "bits:16, "
                "uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, "
                "uintbe:16, uintbe:16, uintbe:16, uintbe:16",
                104, b"tkhd", 1, b"\x00\x00\x00",
                creation_time, modification_time, 4,
                b"\x00" * 4,
                60,
                b"\x00" * 4, b"\x00" * 4,
                0, 0, 0, 0,
                b"\x00" * 2,
                65536, 0, 0, 0, 65536, 0, 0, 0, 1073741824,
                0, 0, 0, 0)

    # MOOV.TRAK.MDIA.MDHD
    mdhd = trak.boxes[-1].boxes[0]
    mdhd.timescale = 20
    mdhd.duration = 60

    mdhd.refresh_box_size()

    assert mdhd.header.type == b"mdhd"
    assert mdhd.header.box_size == 44
    assert mdhd.timescale == 20
    assert mdhd.duration == 60

    assert bytes(mdhd) == \
           pack("uintbe:32, bytes:4, uintbe:8, bits:24, "
                "uintbe:64, uintbe:64, uintbe:32, uintbe:64, "
                "bits:1, uint:5, uint:5, uint:5, "
                "bits:16",
                44, b"mdhd", 1, b"\x00\x00\x00",
                creation_time, modification_time, 20, 60,
                0x1, 21, 14, 4,
                b"\x00" * 2)

    moov.append(trak)

    moov.refresh_box_size()

    assert mvhd.next_track_id == len(moov.boxes)

    assert moov.header.type == b"moov"
    assert len(moov.boxes) == 5

    mp4_bytes = b''.join([bytes(ftyp), bytes(mdat), bytes(moov)])

    for box in moov.boxes:
        if isinstance(box, bx_def.TRAK):
            mdia = box.boxes[1]
        else:
            continue

        # trak.mdia.tkhd.name == b"bzna_inputs\0"
        if mdia.boxes[1].name == b"bzna_inputs\0":
            pass
        # trak.mdia.tkhd.name == b"bzna_names\0"
        elif mdia.boxes[1].name == b"bzna_fnames\0":
            # trak.mdia.minf.stbl.stsz
            stsz = mdia.boxes[2].boxes[2].boxes[2]
            # trak.mdia.minf.stbl.stco
            stco = mdia.boxes[2].boxes[2].boxes[4]
            for i, (sample, entry) in enumerate(zip(stsz.samples, stco.entries)):
                sample_end = entry.chunk_offset + sample.entry_size
                if i == 0:
                    assert mp4_bytes[entry.chunk_offset:sample_end] == \
                           b"/path/image_1_name.JPEG"
                elif i == 1:
                    assert mp4_bytes[entry.chunk_offset:sample_end] == \
                           b"/path/image_2_name.JPEG"
                elif i == 2:
                    assert mp4_bytes[entry.chunk_offset:sample_end] == \
                           b"/path/image_3_name.JPEG"
        # trak.mdia.tkhd.name == b"bzna_targets\0"
        elif mdia.boxes[1].name == b"bzna_targets\0":
            # trak.mdia.minf.stbl.stsz
            stsz = mdia.boxes[2].boxes[2].boxes[2]
            # trak.mdia.minf.stbl.stco
            stco = mdia.boxes[2].boxes[2].boxes[4]
            for i, (sample, entry) in enumerate(zip(stsz.samples, stco.entries)):
                sample_end = entry.chunk_offset + sample.entry_size
                if i == 0:
                    assert mp4_bytes[entry.chunk_offset:sample_end] == \
                           (0).to_bytes(8, byteorder="little")
                elif i == 1:
                    assert mp4_bytes[entry.chunk_offset:sample_end] == \
                           (1).to_bytes(8, byteorder="little")
                elif i == 2:
                    assert mp4_bytes[entry.chunk_offset:sample_end] == \
                           (0).to_bytes(8, byteorder="little")

    with open("tests/data/small_dataset.out.mp4", "rb") as f:
        assert mp4_bytes == f.read()
Example #19
0
def test_mp4_small_vid():
    creation_time = to_mp4_time(datetime(2019, 9, 15, 0, 0, 0))
    modification_time = to_mp4_time(datetime(2019, 9, 16, 0, 0, 0))

    # FTYP
    ftyp = bx_def.FTYP(BoxHeader())
    ftyp.header.type = b"ftyp"
    ftyp.major_brand = 1836069937                # b"mp41"
    ftyp.minor_version = 0
    ftyp.compatible_brands = [1836069937]        # b'mp41'

    ftyp.refresh_box_size()

    assert ftyp.header.type == b"ftyp"
    assert ftyp.header.box_size == 20
    assert ftyp.major_brand == 1836069937           # b"mp41"
    assert ftyp.minor_version == 0
    assert ftyp.compatible_brands == [1836069937]   # b'mp41'
    assert bytes(ftyp) == pack("uintbe:32, bytes:4, "
                               "bytes:4, uintbe:32, bytes:4",
                               20, b"ftyp",
                               b"mp41", 0, b'mp41')

    # MDAT
    mdat = bx_def.MDAT(BoxHeader())
    mdat.header.type = b"mdat"

    data = []

    with open("tests/data/small_vid_mdat_im0", "rb") as f:
        data.append(f.read())
    with open("tests/data/small_vid_mdat_im1", "rb") as f:
        data.append(f.read())
    with open("tests/data/small_vid_mdat_im2", "rb") as f:
        data.append(f.read())

    mdat.data = b''.join(data)

    mdat.refresh_box_size()

    assert mdat.header.type == b"mdat"
    assert mdat.header.box_size == 518258

    # MOOV
    moov = bx_def.MOOV(BoxHeader())
    moov.header.type = b"moov"

    # MOOV.MVHD
    mvhd = make_mvhd(creation_time, modification_time, 3)
    # == total number of tracks
    mvhd.next_track_id = 2

    assert mvhd.next_track_id == 2

    moov.append(mvhd)

    # MOOV.TRAK
    offset = ftyp.header.box_size + mdat.header.header_size
    sizes = [198297, 127477, 192476]
    trak = make_vide_trak(creation_time, modification_time, b"VideoHandler\0",
                          sizes, offset)

    # MOOV.TRAK.TKHD
    tkhd = trak.boxes[0]

    # "\x00\x00\x01" trak is enabled
    # "\x00\x00\x02" trak is used in the presentation
    # "\x00\x00\x04" trak is used in the preview
    # "\x00\x00\x08" trak size in not in pixel but in aspect ratio
    tkhd.header.flags = b"\x00\x00\x03"

    tkhd.track_id = 1

    # TODO: make sure that this is the canvas size
    tkhd.width = [512, 0]
    tkhd.height = [512, 0]

    tkhd.refresh_box_size()

    assert tkhd.header.type == b"tkhd"
    assert tkhd.header.box_size == 104
    assert tkhd.header.flags == b"\x00\x00\x03"

    assert tkhd.track_id == 1

    assert tkhd.width == 512
    assert tkhd.height == 512

    assert bytes(tkhd) == \
           pack("uintbe:32, bytes:4, uintbe:8, bits:24, "
                "uintbe:64, uintbe:64, uintbe:32, "
                "bits:32, "
                "uintbe:64, "
                "bits:32, bits:32, "
                "uintbe:16, uintbe:16, uintbe:8, uintbe:8, "
                "bits:16, "
                "uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, uintbe:32, "
                "uintbe:16, uintbe:16, uintbe:16, uintbe:16",
                104, b"tkhd", 1, b"\x00\x00\x03",
                creation_time, modification_time, 1,
                b"\x00" * 4,
                60,
                b"\x00" * 4, b"\x00" * 4,
                0, 0, 0, 0,
                b"\x00" * 2,
                65536, 0, 0, 0, 65536, 0, 0, 0, 1073741824,
                512, 0, 512, 0)

    # MOOV.TRAK.MDIA.MDHD
    mdhd = trak.boxes[-1].boxes[0]
    mdhd.timescale = 20
    mdhd.duration = 60

    mdhd.refresh_box_size()

    assert mdhd.header.type == b"mdhd"
    assert mdhd.header.box_size == 44
    assert mdhd.timescale == 20
    assert mdhd.duration == 60

    assert bytes(mdhd) == \
           pack("uintbe:32, bytes:4, uintbe:8, bits:24, "
                "uintbe:64, uintbe:64, uintbe:32, uintbe:64, "
                "bits:1, uint:5, uint:5, uint:5, "
                "bits:16",
                44, b"mdhd", 1, b"\x00\x00\x00",
                creation_time, modification_time, 20, 60,
                0x1, 21, 14, 4,
                b"\x00" * 2)

    # MOOV.TRAK.MDIA.MINF.STBL.STSD
    stsd = trak.boxes[-1].boxes[-1].boxes[-1].boxes[0]

    assert stsd.header.type == b"stsd"
    assert len(stsd.boxes) == 1

    # MOOV.TRAK.MDIA.MINF.STBL.STSD.AVC1
    avc1 = stsd.boxes[0]
    avc1.width = 512
    avc1.height = 512

    assert avc1.header.type == b"avc1"
    assert avc1.width == 512
    assert avc1.height == 512

    moov.append(trak)

    moov.refresh_box_size()

    assert mvhd.next_track_id == len(moov.boxes)

    assert moov.header.type == b"moov"
    assert len(moov.boxes) == 2

    mp4_bytes = b''.join([bytes(ftyp), bytes(mdat), bytes(moov)])

    with open("tests/data/small_vid.out.mp4", "rb") as f:
        assert mp4_bytes == f.read()