// Copyright (c) 2021, Alliance for Open Media. All rights reserved // // This source code is subject to the terms of the BSD 2 Clause License and // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License // was not distributed with this source code in the LICENSE file, you can // obtain it at www.aomedia.org/license/software. If the Alliance for Open // Media Patent License 1.0 was not distributed with this source code in the // PATENTS file, you can obtain it at www.aomedia.org/license/patent. #include "avifinfo.h" #include <stdint.h> #include <stdio.h> #include <string.h> //------------------------------------------------------------------------------ // Status returned when reading the content of a box (or file). AvifInfoInternalStatus; static AvifInfoStatus AvifInfoInternalConvertStatus(AvifInfoInternalStatus s) { … } // uint32_t is used everywhere in this file. It is unlikely to be insufficient // to parse AVIF headers. #define AVIFINFO_MAX_SIZE … // Be reasonable. Avoid timeouts and out-of-memory. #define AVIFINFO_MAX_NUM_BOXES … // AvifInfoInternalFeatures uses uint8_t to store values. #define AVIFINFO_MAX_VALUE … // Maximum number of stored associations. Past that, they are skipped. #define AVIFINFO_MAX_TILES … #define AVIFINFO_MAX_PROPS … #define AVIFINFO_MAX_FEATURES … #define AVIFINFO_UNDEFINED … // Reads an unsigned integer from 'input' with most significant bits first. // 'input' must be at least 'num_bytes'-long. static uint32_t AvifInfoInternalReadBigEndian(const uint8_t* input, uint32_t num_bytes) { … } //------------------------------------------------------------------------------ // Convenience macros. #if defined(AVIFINFO_LOG_ERROR) // Toggle to log encountered issues. static void AvifInfoInternalLogError(const char* file, int line, AvifInfoInternalStatus status) { const char* kStr[] = {"Found", "NotFound", "Truncated", "Invalid", "Aborted"}; fprintf(stderr, " %s:%d: %s\n", file, line, kStr[status]); // Set a breakpoint here to catch the first detected issue. } #define AVIFINFO_RETURN … #else #define AVIFINFO_RETURN(check_status) … #endif #define AVIFINFO_CHECK(check_condition, check_status) … #define AVIFINFO_CHECK_STATUS_IS(check_status, expected_status) … #define AVIFINFO_CHECK_FOUND(check_status) … #define AVIFINFO_CHECK_NOT_FOUND(check_status) … #if defined(AVIFINFO_ENABLE_DEBUG_LOG) #define AVIF_DEBUG_LOG … #else #define AVIF_DEBUG_LOG(...) … #endif //------------------------------------------------------------------------------ // Streamed input struct and helper functions. AvifInfoInternalStream; // Reads 'num_bytes' from the 'stream'. They are available at '*data'. // 'num_bytes' must be greater than zero. static AvifInfoInternalStatus AvifInfoInternalRead( AvifInfoInternalStream* stream, uint32_t num_bytes, const uint8_t** data) { … } // Skips 'num_bytes' from the 'stream'. 'num_bytes' can be zero. static AvifInfoInternalStatus AvifInfoInternalSkip( AvifInfoInternalStream* stream, uint32_t num_bytes) { … } //------------------------------------------------------------------------------ // Features are parsed into temporary property associations. AvifInfoInternalTile; // Tile item id <-> parent item id associations. AvifInfoInternalProp; // Property index <-> item id associations. AvifInfoInternalDimProp; // Property <-> features associations. AvifInfoInternalChanProp; // Property <-> features associations. AvifInfoInternalFeatures; // Generates the features of a given 'target_item_id' from internal features. static AvifInfoInternalStatus AvifInfoInternalGetItemFeatures( AvifInfoInternalFeatures* f, uint32_t target_item_id, uint32_t tile_depth) { … } // Generates the 'f->primary_item_features' from the AvifInfoInternalFeatures. // Returns kNotFound if there is not enough information. static AvifInfoInternalStatus AvifInfoInternalGetPrimaryItemFeatures( AvifInfoInternalFeatures* f) { … } //------------------------------------------------------------------------------ // Box header parsing and various size checks. AvifInfoInternalBox; // Reads the header of a 'box' starting at the beginning of a 'stream'. // 'num_remaining_bytes' is the remaining size of the container of the 'box' // (either the file size itself or the content size of the parent of the 'box'). static AvifInfoInternalStatus AvifInfoInternalParseBox( int nesting_level, AvifInfoInternalStream* stream, uint32_t num_remaining_bytes, uint32_t* num_parsed_boxes, AvifInfoInternalBox* box) { … } //------------------------------------------------------------------------------ // Parses a 'stream' of an "ipco" box into 'features'. // "ispe" is used for width and height, "pixi" and "av1C" are used for bit depth // and number of channels, and "auxC" is used for alpha. static AvifInfoInternalStatus ParseIpco(int nesting_level, AvifInfoInternalStream* stream, uint32_t num_remaining_bytes, uint32_t* num_parsed_boxes, AvifInfoInternalFeatures* features) { … } // Parses a 'stream' of an "iprp" box into 'features'. The "ipco" box contains // the properties which are linked to items by the "ipma" box. static AvifInfoInternalStatus ParseIprp(int nesting_level, AvifInfoInternalStream* stream, uint32_t num_remaining_bytes, uint32_t* num_parsed_boxes, AvifInfoInternalFeatures* features) { … } //------------------------------------------------------------------------------ // Parses a 'stream' of an "iref" box into 'features'. // 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. static AvifInfoInternalStatus ParseIref(int nesting_level, AvifInfoInternalStream* stream, uint32_t num_remaining_bytes, uint32_t* num_parsed_boxes, AvifInfoInternalFeatures* features) { … } //------------------------------------------------------------------------------ // Parses a 'stream' of an "iinf" box into 'features'. static AvifInfoInternalStatus ParseIinf(int nesting_level, AvifInfoInternalStream* stream, uint32_t num_remaining_bytes, uint32_t box_version, uint32_t* num_parsed_boxes, AvifInfoInternalFeatures* features) { … } //------------------------------------------------------------------------------ // Parses a 'stream' of a "meta" box. It looks for the primary item ID in the // "pitm" box and recurses into other boxes to find its 'features'. static AvifInfoInternalStatus ParseMeta(int nesting_level, AvifInfoInternalStream* stream, uint32_t num_remaining_bytes, uint32_t* num_parsed_boxes, AvifInfoInternalFeatures* features) { … } //------------------------------------------------------------------------------ // Parses a file 'stream'. The file type is checked through the "ftyp" box. static AvifInfoInternalStatus ParseFtyp(AvifInfoInternalStream* stream) { … } // Parses a file 'stream'. 'features' are extracted from the "meta" box. static AvifInfoInternalStatus ParseFile(AvifInfoInternalStream* stream, uint32_t* num_parsed_boxes, AvifInfoInternalFeatures* features) { … } //------------------------------------------------------------------------------ // Helpers for converting the fixed-size input public API to the streamed one. AvifInfoInternalForward; static const uint8_t* AvifInfoInternalForwardRead(void* stream, size_t num_bytes) { … } static void AvifInfoInternalForwardSkip(void* stream, size_t num_bytes) { … } //------------------------------------------------------------------------------ // Fixed-size input public API AvifInfoStatus AvifInfoIdentify(const uint8_t* data, size_t data_size) { … } AvifInfoStatus AvifInfoGetFeatures(const uint8_t* data, size_t data_size, AvifInfoFeatures* features) { … } //------------------------------------------------------------------------------ // Streamed input API AvifInfoStatus AvifInfoIdentifyStream(void* stream, read_stream_t read, skip_stream_t skip) { … } AvifInfoStatus AvifInfoGetFeaturesStream(void* stream, read_stream_t read, skip_stream_t skip, AvifInfoFeatures* features) { … }