Edit File by line
/home/zeestwma/richards.../wp-inclu...
File: class-avif-info.php
<?php
[0] Fix | Delete
/**
[1] Fix | Delete
* Copyright (c) 2021, Alliance for Open Media. All rights reserved
[2] Fix | Delete
*
[3] Fix | Delete
* This source code is subject to the terms of the BSD 2 Clause License and
[4] Fix | Delete
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
[5] Fix | Delete
* was not distributed with this source code in the LICENSE file, you can
[6] Fix | Delete
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
[7] Fix | Delete
* Media Patent License 1.0 was not distributed with this source code in the
[8] Fix | Delete
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
[9] Fix | Delete
*
[10] Fix | Delete
* Note: this class is from libavifinfo - https://aomedia.googlesource.com/libavifinfo/+/refs/heads/main/avifinfo.php at f509487.
[11] Fix | Delete
* It is used as a fallback to parse AVIF files when the server doesn't support AVIF,
[12] Fix | Delete
* primarily to identify the width and height of the image.
[13] Fix | Delete
*
[14] Fix | Delete
* Note PHP 8.2 added native support for AVIF, so this class can be removed when WordPress requires PHP 8.2.
[15] Fix | Delete
*/
[16] Fix | Delete
[17] Fix | Delete
namespace Avifinfo;
[18] Fix | Delete
[19] Fix | Delete
const FOUND = 0; // Input correctly parsed and information retrieved.
[20] Fix | Delete
const NOT_FOUND = 1; // Input correctly parsed but information is missing or elsewhere.
[21] Fix | Delete
const TRUNCATED = 2; // Input correctly parsed until missing bytes to continue.
[22] Fix | Delete
const ABORTED = 3; // Input correctly parsed until stopped to avoid timeout or crash.
[23] Fix | Delete
const INVALID = 4; // Input incorrectly parsed.
[24] Fix | Delete
[25] Fix | Delete
const MAX_SIZE = 4294967295; // Unlikely to be insufficient to parse AVIF headers.
[26] Fix | Delete
const MAX_NUM_BOXES = 4096; // Be reasonable. Avoid timeouts and out-of-memory.
[27] Fix | Delete
const MAX_VALUE = 255;
[28] Fix | Delete
const MAX_TILES = 16;
[29] Fix | Delete
const MAX_PROPS = 32;
[30] Fix | Delete
const MAX_FEATURES = 8;
[31] Fix | Delete
const UNDEFINED = 0; // Value was not yet parsed.
[32] Fix | Delete
[33] Fix | Delete
/**
[34] Fix | Delete
* Reads an unsigned integer with most significant bits first.
[35] Fix | Delete
*
[36] Fix | Delete
* @param binary string $input Must be at least $num_bytes-long.
[37] Fix | Delete
* @param int $num_bytes Number of parsed bytes.
[38] Fix | Delete
* @return int Value.
[39] Fix | Delete
*/
[40] Fix | Delete
function read_big_endian( $input, $num_bytes ) {
[41] Fix | Delete
if ( $num_bytes == 1 ) {
[42] Fix | Delete
return unpack( 'C', $input ) [1];
[43] Fix | Delete
} else if ( $num_bytes == 2 ) {
[44] Fix | Delete
return unpack( 'n', $input ) [1];
[45] Fix | Delete
} else if ( $num_bytes == 3 ) {
[46] Fix | Delete
$bytes = unpack( 'C3', $input );
[47] Fix | Delete
return ( $bytes[1] << 16 ) | ( $bytes[2] << 8 ) | $bytes[3];
[48] Fix | Delete
} else { // $num_bytes is 4
[49] Fix | Delete
// This might fail to read unsigned values >= 2^31 on 32-bit systems.
[50] Fix | Delete
// See https://www.php.net/manual/en/function.unpack.php#106041
[51] Fix | Delete
return unpack( 'N', $input ) [1];
[52] Fix | Delete
}
[53] Fix | Delete
}
[54] Fix | Delete
[55] Fix | Delete
/**
[56] Fix | Delete
* Reads bytes and advances the stream position by the same count.
[57] Fix | Delete
*
[58] Fix | Delete
* @param stream $handle Bytes will be read from this resource.
[59] Fix | Delete
* @param int $num_bytes Number of bytes read. Must be greater than 0.
[60] Fix | Delete
* @return binary string|false The raw bytes or false on failure.
[61] Fix | Delete
*/
[62] Fix | Delete
function read( $handle, $num_bytes ) {
[63] Fix | Delete
$data = fread( $handle, $num_bytes );
[64] Fix | Delete
return ( $data !== false && strlen( $data ) >= $num_bytes ) ? $data : false;
[65] Fix | Delete
}
[66] Fix | Delete
[67] Fix | Delete
/**
[68] Fix | Delete
* Advances the stream position by the given offset.
[69] Fix | Delete
*
[70] Fix | Delete
* @param stream $handle Bytes will be skipped from this resource.
[71] Fix | Delete
* @param int $num_bytes Number of skipped bytes. Can be 0.
[72] Fix | Delete
* @return bool True on success or false on failure.
[73] Fix | Delete
*/
[74] Fix | Delete
// Skips 'num_bytes' from the 'stream'. 'num_bytes' can be zero.
[75] Fix | Delete
function skip( $handle, $num_bytes ) {
[76] Fix | Delete
return ( fseek( $handle, $num_bytes, SEEK_CUR ) == 0 );
[77] Fix | Delete
}
[78] Fix | Delete
[79] Fix | Delete
//------------------------------------------------------------------------------
[80] Fix | Delete
// Features are parsed into temporary property associations.
[81] Fix | Delete
[82] Fix | Delete
class Tile { // Tile item id <-> parent item id associations.
[83] Fix | Delete
public $tile_item_id;
[84] Fix | Delete
public $parent_item_id;
[85] Fix | Delete
}
[86] Fix | Delete
[87] Fix | Delete
class Prop { // Property index <-> item id associations.
[88] Fix | Delete
public $property_index;
[89] Fix | Delete
public $item_id;
[90] Fix | Delete
}
[91] Fix | Delete
[92] Fix | Delete
class Dim_Prop { // Property <-> features associations.
[93] Fix | Delete
public $property_index;
[94] Fix | Delete
public $width;
[95] Fix | Delete
public $height;
[96] Fix | Delete
}
[97] Fix | Delete
[98] Fix | Delete
class Chan_Prop { // Property <-> features associations.
[99] Fix | Delete
public $property_index;
[100] Fix | Delete
public $bit_depth;
[101] Fix | Delete
public $num_channels;
[102] Fix | Delete
}
[103] Fix | Delete
[104] Fix | Delete
class Features {
[105] Fix | Delete
public $has_primary_item = false; // True if "pitm" was parsed.
[106] Fix | Delete
public $has_alpha = false; // True if an alpha "auxC" was parsed.
[107] Fix | Delete
public $primary_item_id;
[108] Fix | Delete
public $primary_item_features = array( // Deduced from the data below.
[109] Fix | Delete
'width' => UNDEFINED, // In number of pixels.
[110] Fix | Delete
'height' => UNDEFINED, // Ignores mirror and rotation.
[111] Fix | Delete
'bit_depth' => UNDEFINED, // Likely 8, 10 or 12 bits per channel per pixel.
[112] Fix | Delete
'num_channels' => UNDEFINED // Likely 1, 2, 3 or 4 channels:
[113] Fix | Delete
// (1 monochrome or 3 colors) + (0 or 1 alpha)
[114] Fix | Delete
);
[115] Fix | Delete
[116] Fix | Delete
public $tiles = array(); // Tile[]
[117] Fix | Delete
public $props = array(); // Prop[]
[118] Fix | Delete
public $dim_props = array(); // Dim_Prop[]
[119] Fix | Delete
public $chan_props = array(); // Chan_Prop[]
[120] Fix | Delete
[121] Fix | Delete
/**
[122] Fix | Delete
* Binds the width, height, bit depth and number of channels from stored internal features.
[123] Fix | Delete
*
[124] Fix | Delete
* @param int $target_item_id Id of the item whose features will be bound.
[125] Fix | Delete
* @param int $tile_depth Maximum recursion to search within tile-parent relations.
[126] Fix | Delete
* @return Status FOUND on success or NOT_FOUND on failure.
[127] Fix | Delete
*/
[128] Fix | Delete
private function get_item_features( $target_item_id, $tile_depth ) {
[129] Fix | Delete
foreach ( $this->props as $prop ) {
[130] Fix | Delete
if ( $prop->item_id != $target_item_id ) {
[131] Fix | Delete
continue;
[132] Fix | Delete
}
[133] Fix | Delete
[134] Fix | Delete
// Retrieve the width and height of the primary item if not already done.
[135] Fix | Delete
if ( $target_item_id == $this->primary_item_id &&
[136] Fix | Delete
( $this->primary_item_features['width'] == UNDEFINED ||
[137] Fix | Delete
$this->primary_item_features['height'] == UNDEFINED ) ) {
[138] Fix | Delete
foreach ( $this->dim_props as $dim_prop ) {
[139] Fix | Delete
if ( $dim_prop->property_index != $prop->property_index ) {
[140] Fix | Delete
continue;
[141] Fix | Delete
}
[142] Fix | Delete
$this->primary_item_features['width'] = $dim_prop->width;
[143] Fix | Delete
$this->primary_item_features['height'] = $dim_prop->height;
[144] Fix | Delete
if ( $this->primary_item_features['bit_depth'] != UNDEFINED &&
[145] Fix | Delete
$this->primary_item_features['num_channels'] != UNDEFINED ) {
[146] Fix | Delete
return FOUND;
[147] Fix | Delete
}
[148] Fix | Delete
break;
[149] Fix | Delete
}
[150] Fix | Delete
}
[151] Fix | Delete
// Retrieve the bit depth and number of channels of the target item if not
[152] Fix | Delete
// already done.
[153] Fix | Delete
if ( $this->primary_item_features['bit_depth'] == UNDEFINED ||
[154] Fix | Delete
$this->primary_item_features['num_channels'] == UNDEFINED ) {
[155] Fix | Delete
foreach ( $this->chan_props as $chan_prop ) {
[156] Fix | Delete
if ( $chan_prop->property_index != $prop->property_index ) {
[157] Fix | Delete
continue;
[158] Fix | Delete
}
[159] Fix | Delete
$this->primary_item_features['bit_depth'] = $chan_prop->bit_depth;
[160] Fix | Delete
$this->primary_item_features['num_channels'] = $chan_prop->num_channels;
[161] Fix | Delete
if ( $this->primary_item_features['width'] != UNDEFINED &&
[162] Fix | Delete
$this->primary_item_features['height'] != UNDEFINED ) {
[163] Fix | Delete
return FOUND;
[164] Fix | Delete
}
[165] Fix | Delete
break;
[166] Fix | Delete
}
[167] Fix | Delete
}
[168] Fix | Delete
}
[169] Fix | Delete
[170] Fix | Delete
// Check for the bit_depth and num_channels in a tile if not yet found.
[171] Fix | Delete
if ( $tile_depth < 3 ) {
[172] Fix | Delete
foreach ( $this->tiles as $tile ) {
[173] Fix | Delete
if ( $tile->parent_item_id != $target_item_id ) {
[174] Fix | Delete
continue;
[175] Fix | Delete
}
[176] Fix | Delete
$status = $this->get_item_features( $tile->tile_item_id, $tile_depth + 1 );
[177] Fix | Delete
if ( $status != NOT_FOUND ) {
[178] Fix | Delete
return $status;
[179] Fix | Delete
}
[180] Fix | Delete
}
[181] Fix | Delete
}
[182] Fix | Delete
return NOT_FOUND;
[183] Fix | Delete
}
[184] Fix | Delete
[185] Fix | Delete
/**
[186] Fix | Delete
* Finds the width, height, bit depth and number of channels of the primary item.
[187] Fix | Delete
*
[188] Fix | Delete
* @return Status FOUND on success or NOT_FOUND on failure.
[189] Fix | Delete
*/
[190] Fix | Delete
public function get_primary_item_features() {
[191] Fix | Delete
// Nothing to do without the primary item ID.
[192] Fix | Delete
if ( !$this->has_primary_item ) {
[193] Fix | Delete
return NOT_FOUND;
[194] Fix | Delete
}
[195] Fix | Delete
// Early exit.
[196] Fix | Delete
if ( empty( $this->dim_props ) || empty( $this->chan_props ) ) {
[197] Fix | Delete
return NOT_FOUND;
[198] Fix | Delete
}
[199] Fix | Delete
$status = $this->get_item_features( $this->primary_item_id, /*tile_depth=*/ 0 );
[200] Fix | Delete
if ( $status != FOUND ) {
[201] Fix | Delete
return $status;
[202] Fix | Delete
}
[203] Fix | Delete
[204] Fix | Delete
// "auxC" is parsed before the "ipma" properties so it is known now, if any.
[205] Fix | Delete
if ( $this->has_alpha ) {
[206] Fix | Delete
++$this->primary_item_features['num_channels'];
[207] Fix | Delete
}
[208] Fix | Delete
return FOUND;
[209] Fix | Delete
}
[210] Fix | Delete
}
[211] Fix | Delete
[212] Fix | Delete
//------------------------------------------------------------------------------
[213] Fix | Delete
[214] Fix | Delete
class Box {
[215] Fix | Delete
public $size; // In bytes.
[216] Fix | Delete
public $type; // Four characters.
[217] Fix | Delete
public $version; // 0 or actual version if this is a full box.
[218] Fix | Delete
public $flags; // 0 or actual value if this is a full box.
[219] Fix | Delete
public $content_size; // 'size' minus the header size.
[220] Fix | Delete
[221] Fix | Delete
/**
[222] Fix | Delete
* Reads the box header.
[223] Fix | Delete
*
[224] Fix | Delete
* @param stream $handle The resource the header will be parsed from.
[225] Fix | Delete
* @param int $num_parsed_boxes The total number of parsed boxes. Prevents timeouts.
[226] Fix | Delete
* @param int $num_remaining_bytes The number of bytes that should be available from the resource.
[227] Fix | Delete
* @return Status FOUND on success or an error on failure.
[228] Fix | Delete
*/
[229] Fix | Delete
public function parse( $handle, &$num_parsed_boxes, $num_remaining_bytes = MAX_SIZE ) {
[230] Fix | Delete
// See ISO/IEC 14496-12:2012(E) 4.2
[231] Fix | Delete
$header_size = 8; // box 32b size + 32b type (at least)
[232] Fix | Delete
if ( $header_size > $num_remaining_bytes ) {
[233] Fix | Delete
return INVALID;
[234] Fix | Delete
}
[235] Fix | Delete
if ( !( $data = read( $handle, 8 ) ) ) {
[236] Fix | Delete
return TRUNCATED;
[237] Fix | Delete
}
[238] Fix | Delete
$this->size = read_big_endian( $data, 4 );
[239] Fix | Delete
$this->type = substr( $data, 4, 4 );
[240] Fix | Delete
// 'box->size==1' means 64-bit size should be read after the box type.
[241] Fix | Delete
// 'box->size==0' means this box extends to all remaining bytes.
[242] Fix | Delete
if ( $this->size == 1 ) {
[243] Fix | Delete
$header_size += 8;
[244] Fix | Delete
if ( $header_size > $num_remaining_bytes ) {
[245] Fix | Delete
return INVALID;
[246] Fix | Delete
}
[247] Fix | Delete
if ( !( $data = read( $handle, 8 ) ) ) {
[248] Fix | Delete
return TRUNCATED;
[249] Fix | Delete
}
[250] Fix | Delete
// Stop the parsing if any box has a size greater than 4GB.
[251] Fix | Delete
if ( read_big_endian( $data, 4 ) != 0 ) {
[252] Fix | Delete
return ABORTED;
[253] Fix | Delete
}
[254] Fix | Delete
// Read the 32 least-significant bits.
[255] Fix | Delete
$this->size = read_big_endian( substr( $data, 4, 4 ), 4 );
[256] Fix | Delete
} else if ( $this->size == 0 ) {
[257] Fix | Delete
$this->size = $num_remaining_bytes;
[258] Fix | Delete
}
[259] Fix | Delete
if ( $this->size < $header_size ) {
[260] Fix | Delete
return INVALID;
[261] Fix | Delete
}
[262] Fix | Delete
if ( $this->size > $num_remaining_bytes ) {
[263] Fix | Delete
return INVALID;
[264] Fix | Delete
}
[265] Fix | Delete
[266] Fix | Delete
$has_fullbox_header = $this->type == 'meta' || $this->type == 'pitm' ||
[267] Fix | Delete
$this->type == 'ipma' || $this->type == 'ispe' ||
[268] Fix | Delete
$this->type == 'pixi' || $this->type == 'iref' ||
[269] Fix | Delete
$this->type == 'auxC';
[270] Fix | Delete
if ( $has_fullbox_header ) {
[271] Fix | Delete
$header_size += 4;
[272] Fix | Delete
}
[273] Fix | Delete
if ( $this->size < $header_size ) {
[274] Fix | Delete
return INVALID;
[275] Fix | Delete
}
[276] Fix | Delete
$this->content_size = $this->size - $header_size;
[277] Fix | Delete
// Avoid timeouts. The maximum number of parsed boxes is arbitrary.
[278] Fix | Delete
++$num_parsed_boxes;
[279] Fix | Delete
if ( $num_parsed_boxes >= MAX_NUM_BOXES ) {
[280] Fix | Delete
return ABORTED;
[281] Fix | Delete
}
[282] Fix | Delete
[283] Fix | Delete
$this->version = 0;
[284] Fix | Delete
$this->flags = 0;
[285] Fix | Delete
if ( $has_fullbox_header ) {
[286] Fix | Delete
if ( !( $data = read( $handle, 4 ) ) ) {
[287] Fix | Delete
return TRUNCATED;
[288] Fix | Delete
}
[289] Fix | Delete
$this->version = read_big_endian( $data, 1 );
[290] Fix | Delete
$this->flags = read_big_endian( substr( $data, 1, 3 ), 3 );
[291] Fix | Delete
// See AV1 Image File Format (AVIF) 8.1
[292] Fix | Delete
// at https://aomediacodec.github.io/av1-avif/#avif-boxes (available when
[293] Fix | Delete
// https://github.com/AOMediaCodec/av1-avif/pull/170 is merged).
[294] Fix | Delete
$is_parsable = ( $this->type == 'meta' && $this->version <= 0 ) ||
[295] Fix | Delete
( $this->type == 'pitm' && $this->version <= 1 ) ||
[296] Fix | Delete
( $this->type == 'ipma' && $this->version <= 1 ) ||
[297] Fix | Delete
( $this->type == 'ispe' && $this->version <= 0 ) ||
[298] Fix | Delete
( $this->type == 'pixi' && $this->version <= 0 ) ||
[299] Fix | Delete
( $this->type == 'iref' && $this->version <= 1 ) ||
[300] Fix | Delete
( $this->type == 'auxC' && $this->version <= 0 );
[301] Fix | Delete
// Instead of considering this file as invalid, skip unparsable boxes.
[302] Fix | Delete
if ( !$is_parsable ) {
[303] Fix | Delete
$this->type = 'unknownversion';
[304] Fix | Delete
}
[305] Fix | Delete
}
[306] Fix | Delete
// print_r( $this ); // Uncomment to print all boxes.
[307] Fix | Delete
return FOUND;
[308] Fix | Delete
}
[309] Fix | Delete
}
[310] Fix | Delete
[311] Fix | Delete
//------------------------------------------------------------------------------
[312] Fix | Delete
[313] Fix | Delete
class Parser {
[314] Fix | Delete
private $handle; // Input stream.
[315] Fix | Delete
private $num_parsed_boxes = 0;
[316] Fix | Delete
private $data_was_skipped = false;
[317] Fix | Delete
public $features;
[318] Fix | Delete
[319] Fix | Delete
function __construct( $handle ) {
[320] Fix | Delete
$this->handle = $handle;
[321] Fix | Delete
$this->features = new Features();
[322] Fix | Delete
}
[323] Fix | Delete
[324] Fix | Delete
/**
[325] Fix | Delete
* Parses an "ipco" box.
[326] Fix | Delete
*
[327] Fix | Delete
* "ispe" is used for width and height, "pixi" and "av1C" are used for bit depth
[328] Fix | Delete
* and number of channels, and "auxC" is used for alpha.
[329] Fix | Delete
*
[330] Fix | Delete
* @param stream $handle The resource the box will be parsed from.
[331] Fix | Delete
* @param int $num_remaining_bytes The number of bytes that should be available from the resource.
[332] Fix | Delete
* @return Status FOUND on success or an error on failure.
[333] Fix | Delete
*/
[334] Fix | Delete
private function parse_ipco( $num_remaining_bytes ) {
[335] Fix | Delete
$box_index = 1; // 1-based index. Used for iterating over properties.
[336] Fix | Delete
do {
[337] Fix | Delete
$box = new Box();
[338] Fix | Delete
$status = $box->parse( $this->handle, $this->num_parsed_boxes, $num_remaining_bytes );
[339] Fix | Delete
if ( $status != FOUND ) {
[340] Fix | Delete
return $status;
[341] Fix | Delete
}
[342] Fix | Delete
[343] Fix | Delete
if ( $box->type == 'ispe' ) {
[344] Fix | Delete
// See ISO/IEC 23008-12:2017(E) 6.5.3.2
[345] Fix | Delete
if ( $box->content_size < 8 ) {
[346] Fix | Delete
return INVALID;
[347] Fix | Delete
}
[348] Fix | Delete
if ( !( $data = read( $this->handle, 8 ) ) ) {
[349] Fix | Delete
return TRUNCATED;
[350] Fix | Delete
}
[351] Fix | Delete
$width = read_big_endian( substr( $data, 0, 4 ), 4 );
[352] Fix | Delete
$height = read_big_endian( substr( $data, 4, 4 ), 4 );
[353] Fix | Delete
if ( $width == 0 || $height == 0 ) {
[354] Fix | Delete
return INVALID;
[355] Fix | Delete
}
[356] Fix | Delete
if ( count( $this->features->dim_props ) <= MAX_FEATURES &&
[357] Fix | Delete
$box_index <= MAX_VALUE ) {
[358] Fix | Delete
$dim_prop_count = count( $this->features->dim_props );
[359] Fix | Delete
$this->features->dim_props[$dim_prop_count] = new Dim_Prop();
[360] Fix | Delete
$this->features->dim_props[$dim_prop_count]->property_index = $box_index;
[361] Fix | Delete
$this->features->dim_props[$dim_prop_count]->width = $width;
[362] Fix | Delete
$this->features->dim_props[$dim_prop_count]->height = $height;
[363] Fix | Delete
} else {
[364] Fix | Delete
$this->data_was_skipped = true;
[365] Fix | Delete
}
[366] Fix | Delete
if ( !skip( $this->handle, $box->content_size - 8 ) ) {
[367] Fix | Delete
return TRUNCATED;
[368] Fix | Delete
}
[369] Fix | Delete
} else if ( $box->type == 'pixi' ) {
[370] Fix | Delete
// See ISO/IEC 23008-12:2017(E) 6.5.6.2
[371] Fix | Delete
if ( $box->content_size < 1 ) {
[372] Fix | Delete
return INVALID;
[373] Fix | Delete
}
[374] Fix | Delete
if ( !( $data = read( $this->handle, 1 ) ) ) {
[375] Fix | Delete
return TRUNCATED;
[376] Fix | Delete
}
[377] Fix | Delete
$num_channels = read_big_endian( $data, 1 );
[378] Fix | Delete
if ( $num_channels < 1 ) {
[379] Fix | Delete
return INVALID;
[380] Fix | Delete
}
[381] Fix | Delete
if ( $box->content_size < 1 + $num_channels ) {
[382] Fix | Delete
return INVALID;
[383] Fix | Delete
}
[384] Fix | Delete
if ( !( $data = read( $this->handle, 1 ) ) ) {
[385] Fix | Delete
return TRUNCATED;
[386] Fix | Delete
}
[387] Fix | Delete
$bit_depth = read_big_endian( $data, 1 );
[388] Fix | Delete
if ( $bit_depth < 1 ) {
[389] Fix | Delete
return INVALID;
[390] Fix | Delete
}
[391] Fix | Delete
for ( $i = 1; $i < $num_channels; ++$i ) {
[392] Fix | Delete
if ( !( $data = read( $this->handle, 1 ) ) ) {
[393] Fix | Delete
return TRUNCATED;
[394] Fix | Delete
}
[395] Fix | Delete
// Bit depth should be the same for all channels.
[396] Fix | Delete
if ( read_big_endian( $data, 1 ) != $bit_depth ) {
[397] Fix | Delete
return INVALID;
[398] Fix | Delete
}
[399] Fix | Delete
if ( $i > 32 ) {
[400] Fix | Delete
return ABORTED; // Be reasonable.
[401] Fix | Delete
}
[402] Fix | Delete
}
[403] Fix | Delete
if ( count( $this->features->chan_props ) <= MAX_FEATURES &&
[404] Fix | Delete
$box_index <= MAX_VALUE && $bit_depth <= MAX_VALUE &&
[405] Fix | Delete
$num_channels <= MAX_VALUE ) {
[406] Fix | Delete
$chan_prop_count = count( $this->features->chan_props );
[407] Fix | Delete
$this->features->chan_props[$chan_prop_count] = new Chan_Prop();
[408] Fix | Delete
$this->features->chan_props[$chan_prop_count]->property_index = $box_index;
[409] Fix | Delete
$this->features->chan_props[$chan_prop_count]->bit_depth = $bit_depth;
[410] Fix | Delete
$this->features->chan_props[$chan_prop_count]->num_channels = $num_channels;
[411] Fix | Delete
} else {
[412] Fix | Delete
$this->data_was_skipped = true;
[413] Fix | Delete
}
[414] Fix | Delete
if ( !skip( $this->handle, $box->content_size - ( 1 + $num_channels ) ) ) {
[415] Fix | Delete
return TRUNCATED;
[416] Fix | Delete
}
[417] Fix | Delete
} else if ( $box->type == 'av1C' ) {
[418] Fix | Delete
// See AV1 Codec ISO Media File Format Binding 2.3.1
[419] Fix | Delete
// at https://aomediacodec.github.io/av1-isobmff/#av1c
[420] Fix | Delete
// Only parse the necessary third byte. Assume that the others are valid.
[421] Fix | Delete
if ( $box->content_size < 3 ) {
[422] Fix | Delete
return INVALID;
[423] Fix | Delete
}
[424] Fix | Delete
if ( !( $data = read( $this->handle, 3 ) ) ) {
[425] Fix | Delete
return TRUNCATED;
[426] Fix | Delete
}
[427] Fix | Delete
$byte = read_big_endian( substr( $data, 2, 1 ), 1 );
[428] Fix | Delete
$high_bitdepth = ( $byte & 0x40 ) != 0;
[429] Fix | Delete
$twelve_bit = ( $byte & 0x20 ) != 0;
[430] Fix | Delete
$monochrome = ( $byte & 0x10 ) != 0;
[431] Fix | Delete
if ( $twelve_bit && !$high_bitdepth ) {
[432] Fix | Delete
return INVALID;
[433] Fix | Delete
}
[434] Fix | Delete
if ( count( $this->features->chan_props ) <= MAX_FEATURES &&
[435] Fix | Delete
$box_index <= MAX_VALUE ) {
[436] Fix | Delete
$chan_prop_count = count( $this->features->chan_props );
[437] Fix | Delete
$this->features->chan_props[$chan_prop_count] = new Chan_Prop();
[438] Fix | Delete
$this->features->chan_props[$chan_prop_count]->property_index = $box_index;
[439] Fix | Delete
$this->features->chan_props[$chan_prop_count]->bit_depth =
[440] Fix | Delete
$high_bitdepth ? $twelve_bit ? 12 : 10 : 8;
[441] Fix | Delete
$this->features->chan_props[$chan_prop_count]->num_channels = $monochrome ? 1 : 3;
[442] Fix | Delete
} else {
[443] Fix | Delete
$this->data_was_skipped = true;
[444] Fix | Delete
}
[445] Fix | Delete
if ( !skip( $this->handle, $box->content_size - 3 ) ) {
[446] Fix | Delete
return TRUNCATED;
[447] Fix | Delete
}
[448] Fix | Delete
} else if ( $box->type == 'auxC' ) {
[449] Fix | Delete
// See AV1 Image File Format (AVIF) 4
[450] Fix | Delete
// at https://aomediacodec.github.io/av1-avif/#auxiliary-images
[451] Fix | Delete
$kAlphaStr = "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha\0";
[452] Fix | Delete
$kAlphaStrLength = 44; // Includes terminating character.
[453] Fix | Delete
if ( $box->content_size >= $kAlphaStrLength ) {
[454] Fix | Delete
if ( !( $data = read( $this->handle, $kAlphaStrLength ) ) ) {
[455] Fix | Delete
return TRUNCATED;
[456] Fix | Delete
}
[457] Fix | Delete
if ( substr( $data, 0, $kAlphaStrLength ) == $kAlphaStr ) {
[458] Fix | Delete
// Note: It is unlikely but it is possible that this alpha plane does
[459] Fix | Delete
// not belong to the primary item or a tile. Ignore this issue.
[460] Fix | Delete
$this->features->has_alpha = true;
[461] Fix | Delete
}
[462] Fix | Delete
if ( !skip( $this->handle, $box->content_size - $kAlphaStrLength ) ) {
[463] Fix | Delete
return TRUNCATED;
[464] Fix | Delete
}
[465] Fix | Delete
} else {
[466] Fix | Delete
if ( !skip( $this->handle, $box->content_size ) ) {
[467] Fix | Delete
return TRUNCATED;
[468] Fix | Delete
}
[469] Fix | Delete
}
[470] Fix | Delete
} else {
[471] Fix | Delete
if ( !skip( $this->handle, $box->content_size ) ) {
[472] Fix | Delete
return TRUNCATED;
[473] Fix | Delete
}
[474] Fix | Delete
}
[475] Fix | Delete
++$box_index;
[476] Fix | Delete
$num_remaining_bytes -= $box->size;
[477] Fix | Delete
} while ( $num_remaining_bytes > 0 );
[478] Fix | Delete
return NOT_FOUND;
[479] Fix | Delete
}
[480] Fix | Delete
[481] Fix | Delete
/**
[482] Fix | Delete
* Parses an "iprp" box.
[483] Fix | Delete
*
[484] Fix | Delete
* The "ipco" box contain the properties which are linked to items by the "ipma" box.
[485] Fix | Delete
*
[486] Fix | Delete
* @param stream $handle The resource the box will be parsed from.
[487] Fix | Delete
* @param int $num_remaining_bytes The number of bytes that should be available from the resource.
[488] Fix | Delete
* @return Status FOUND on success or an error on failure.
[489] Fix | Delete
*/
[490] Fix | Delete
private function parse_iprp( $num_remaining_bytes ) {
[491] Fix | Delete
do {
[492] Fix | Delete
$box = new Box();
[493] Fix | Delete
$status = $box->parse( $this->handle, $this->num_parsed_boxes, $num_remaining_bytes );
[494] Fix | Delete
if ( $status != FOUND ) {
[495] Fix | Delete
return $status;
[496] Fix | Delete
}
[497] Fix | Delete
[498] Fix | Delete
if ( $box->type == 'ipco' ) {
[499] Fix | Delete
12
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function