: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$status = $this->parse_ipco( $box->content_size );
if ( $status != NOT_FOUND ) {
} else if ( $box->type == 'ipma' ) {
// See ISO/IEC 23008-12:2017(E) 9.3.2
if ( $box->content_size < $num_read_bytes ) {
if ( !( $data = read( $this->handle, $num_read_bytes ) ) ) {
$entry_count = read_big_endian( $data, 4 );
$id_num_bytes = ( $box->version < 1 ) ? 2 : 4;
$index_num_bytes = ( $box->flags & 1 ) ? 2 : 1;
$essential_bit_mask = ( $box->flags & 1 ) ? 0x8000 : 0x80;
for ( $entry = 0; $entry < $entry_count; ++$entry ) {
if ( $entry >= MAX_PROPS ||
count( $this->features->props ) >= MAX_PROPS ) {
$this->data_was_skipped = true;
$num_read_bytes += $id_num_bytes + 1;
if ( $box->content_size < $num_read_bytes ) {
if ( !( $data = read( $this->handle, $id_num_bytes + 1 ) ) ) {
$item_id = read_big_endian(
substr( $data, 0, $id_num_bytes ), $id_num_bytes );
$association_count = read_big_endian(
substr( $data, $id_num_bytes, 1 ), 1 );
for ( $property = 0; $property < $association_count; ++$property ) {
if ( $property >= MAX_PROPS ||
count( $this->features->props ) >= MAX_PROPS ) {
$this->data_was_skipped = true;
$num_read_bytes += $index_num_bytes;
if ( $box->content_size < $num_read_bytes ) {
if ( !( $data = read( $this->handle, $index_num_bytes ) ) ) {
$value = read_big_endian( $data, $index_num_bytes );
// $essential = ($value & $essential_bit_mask); // Unused.
$property_index = ( $value & ~$essential_bit_mask );
if ( $property_index <= MAX_VALUE && $item_id <= MAX_VALUE ) {
$prop_count = count( $this->features->props );
$this->features->props[$prop_count] = new Prop();
$this->features->props[$prop_count]->property_index = $property_index;
$this->features->props[$prop_count]->item_id = $item_id;
$this->data_was_skipped = true;
if ( $property < $association_count ) {
break; // Do not read garbage.
// If all features are available now, do not look further.
$status = $this->features->get_primary_item_features();
if ( $status != NOT_FOUND ) {
// Mostly if 'data_was_skipped'.
if ( !skip( $this->handle, $box->content_size - $num_read_bytes ) ) {
if ( !skip( $this->handle, $box->content_size ) ) {
$num_remaining_bytes -= $box->size;
} while ( $num_remaining_bytes > 0 );
* The "dimg" boxes contain links between tiles and their parent items, which
* can be used to infer bit depth and number of channels for the primary item
* when the latter does not have these properties.
* @param stream $handle The resource the box will be parsed from.
* @param int $num_remaining_bytes The number of bytes that should be available from the resource.
* @return Status FOUND on success or an error on failure.
private function parse_iref( $num_remaining_bytes ) {
$status = $box->parse( $this->handle, $this->num_parsed_boxes, $num_remaining_bytes );
if ( $status != FOUND ) {
if ( $box->type == 'dimg' ) {
// See ISO/IEC 14496-12:2015(E) 8.11.12.2
$num_bytes_per_id = ( $box->version == 0 ) ? 2 : 4;
$num_read_bytes = $num_bytes_per_id + 2;
if ( $box->content_size < $num_read_bytes ) {
if ( !( $data = read( $this->handle, $num_read_bytes ) ) ) {
$from_item_id = read_big_endian( $data, $num_bytes_per_id );
$reference_count = read_big_endian( substr( $data, $num_bytes_per_id, 2 ), 2 );
for ( $i = 0; $i < $reference_count; ++$i ) {
$this->data_was_skipped = true;
$num_read_bytes += $num_bytes_per_id;
if ( $box->content_size < $num_read_bytes ) {
if ( !( $data = read( $this->handle, $num_bytes_per_id ) ) ) {
$to_item_id = read_big_endian( $data, $num_bytes_per_id );
$tile_count = count( $this->features->tiles );
if ( $from_item_id <= MAX_VALUE && $to_item_id <= MAX_VALUE &&
$tile_count < MAX_TILES ) {
$this->features->tiles[$tile_count] = new Tile();
$this->features->tiles[$tile_count]->tile_item_id = $to_item_id;
$this->features->tiles[$tile_count]->parent_item_id = $from_item_id;
$this->data_was_skipped = true;
// If all features are available now, do not look further.
$status = $this->features->get_primary_item_features();
if ( $status != NOT_FOUND ) {
// Mostly if 'data_was_skipped'.
if ( !skip( $this->handle, $box->content_size - $num_read_bytes ) ) {
if ( !skip( $this->handle, $box->content_size ) ) {
$num_remaining_bytes -= $box->size;
} while ( $num_remaining_bytes > 0 );
* It looks for the primary item ID in the "pitm" box and recurses into other boxes
* @param stream $handle The resource the box will be parsed from.
* @param int $num_remaining_bytes The number of bytes that should be available from the resource.
* @return Status FOUND on success or an error on failure.
private function parse_meta( $num_remaining_bytes ) {
$status = $box->parse( $this->handle, $this->num_parsed_boxes, $num_remaining_bytes );
if ( $status != FOUND ) {
if ( $box->type == 'pitm' ) {
// See ISO/IEC 14496-12:2015(E) 8.11.4.2
$num_bytes_per_id = ( $box->version == 0 ) ? 2 : 4;
if ( $num_bytes_per_id > $num_remaining_bytes ) {
if ( !( $data = read( $this->handle, $num_bytes_per_id ) ) ) {
$primary_item_id = read_big_endian( $data, $num_bytes_per_id );
if ( $primary_item_id > MAX_VALUE ) {
$this->features->has_primary_item = true;
$this->features->primary_item_id = $primary_item_id;
if ( !skip( $this->handle, $box->content_size - $num_bytes_per_id ) ) {
} else if ( $box->type == 'iprp' ) {
$status = $this->parse_iprp( $box->content_size );
if ( $status != NOT_FOUND ) {
} else if ( $box->type == 'iref' ) {
$status = $this->parse_iref( $box->content_size );
if ( $status != NOT_FOUND ) {
if ( !skip( $this->handle, $box->content_size ) ) {
$num_remaining_bytes -= $box->size;
} while ( $num_remaining_bytes != 0 );
// According to ISO/IEC 14496-12:2012(E) 8.11.1.1 there is at most one "meta".
* The file type is checked through the "ftyp" box.
* @return bool True if the input stream is an AVIF bitstream or false.
public function parse_ftyp() {
$status = $box->parse( $this->handle, $this->num_parsed_boxes );
if ( $status != FOUND ) {
if ( $box->type != 'ftyp' ) {
// Iterate over brands. See ISO/IEC 14496-12:2012(E) 4.3.1
if ( $box->content_size < 8 ) {
for ( $i = 0; $i + 4 <= $box->content_size; $i += 4 ) {
if ( !( $data = read( $this->handle, 4 ) ) ) {
continue; // Skip minor_version.
if ( substr( $data, 0, 4 ) == 'avif' || substr( $data, 0, 4 ) == 'avis' ) {
return skip( $this->handle, $box->content_size - ( $i + 4 ) );
return false; // Be reasonable.
return false; // No AVIF brand no good.
* Features are extracted from the "meta" box.
* @return bool True if the main features of the primary item were parsed or false.
public function parse_file() {
while ( $box->parse( $this->handle, $this->num_parsed_boxes ) == FOUND ) {
if ( $box->type === 'meta' ) {
if ( $this->parse_meta( $box->content_size ) != FOUND ) {
if ( !skip( $this->handle, $box->content_size ) ) {
return false; // No "meta" no good.