Initial commit (basic functionality, not feature complete)
diff --git a/src/read.c b/src/read.c new file mode 100644 index 0000000..c5ee4f6 --- /dev/null +++ b/src/read.c
@@ -0,0 +1,331 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include "aom/aom_decoder.h" +#include "aom/aomdx.h" + +#include <string.h> + +#define MAX_ITEMS 8 + +typedef struct avifItem +{ + int id; + uint8_t type[4]; + uint32_t offset; + uint32_t size; +} avifItem; + +typedef struct avifData +{ + avifBool ispe_seen; + uint32_t ispe_width; + uint32_t ispe_height; + avifItem items[MAX_ITEMS]; +} avifData; + +int findItemID(avifData * data, int itemID) +{ + if (itemID == 0) { + return -1; + } + + for (int i = 0; i < MAX_ITEMS; ++i) { + if (data->items[i].id == itemID) { + return i; + } + } + + for (int i = 0; i < MAX_ITEMS; ++i) { + if (data->items[i].id == 0) { + data->items[i].id = itemID; + return i; + } + } + + return -1; +} + +static avifBool avifParse(avifData * data, uint8_t * raw, size_t rawLen) +{ + avifStream s; + avifRawData rawData = { raw, rawLen }; + avifStreamStart(&s, &rawData); + + while (avifStreamHasBytesLeft(&s, 1)) { + size_t contentSize; + uint8_t type[4]; + CHECK(avifStreamReadBoxHeader(&s, type, &contentSize)); + + if (!memcmp(type, "meta", 4)) { + // Basic container FullBoxes, skip version and parse contents + + CHECK(avifStreamReadAndEnforceVersion(&s, 0)); + contentSize -= 4; + + CHECK(avifParse(data, raw + s.offset, contentSize)); + + } else if (!memcmp(type, "iprp", 4) || !memcmp(type, "ipco", 4)) { + // Basic container Boxes, just parse contents + + CHECK(avifParse(data, raw + s.offset, contentSize)); + + } else if (!memcmp(type, "ispe", 4)) { + // ImageSpatialExtentsProperty + + CHECK(avifStreamReadAndEnforceVersion(&s, 0)); + CHECK(avifStreamReadU32(&s, &data->ispe_width)); + CHECK(avifStreamReadU32(&s, &data->ispe_height)); + data->ispe_seen = AVIF_TRUE; + + } else if (!memcmp(type, "iloc", 4)) { + // ItemLocationBox + + CHECK(avifStreamReadAndEnforceVersion(&s, 0)); // TODO: support more + + uint8_t offsetSizeAndLengthSize; + CHECK(avifStreamRead(&s, &offsetSizeAndLengthSize, 1)); + uint8_t offsetSize = (offsetSizeAndLengthSize >> 4) & 0xf; // unsigned int(4) offset_size; + uint8_t lengthSize = (offsetSizeAndLengthSize >> 0) & 0xf; // unsigned int(4) length_size; + + uint8_t baseOffsetSizeAndReserved; + CHECK(avifStreamRead(&s, &baseOffsetSizeAndReserved, 1)); + uint8_t baseOffsetSize = (baseOffsetSizeAndReserved >> 4) & 0xf; // unsigned int(4) base_offset_size; + + uint16_t itemCount; + CHECK(avifStreamReadU16(&s, &itemCount)); // unsigned int(16) item_count; + for (int itemIndex = 0; itemIndex < itemCount; ++itemIndex) { + uint16_t itemID; // unsigned int(16) item_ID; + CHECK(avifStreamReadU16(&s, &itemID)); // + uint16_t dataReferenceIndex; // unsigned int(16) data_reference_index; + CHECK(avifStreamReadU16(&s, &dataReferenceIndex)); // + uint64_t baseOffset; // unsigned int(base_offset_size*8) base_offset; + CHECK(avifStreamReadUX8(&s, &baseOffset, baseOffsetSize)); // + uint16_t extentCount; // unsigned int(16) extent_count; + CHECK(avifStreamReadU16(&s, &extentCount)); // + if (extentCount == 1) { + uint64_t extentOffset; // unsigned int(offset_size*8) extent_offset; + CHECK(avifStreamReadUX8(&s, &extentOffset, offsetSize)); + uint64_t extentLength; // unsigned int(offset_size*8) extent_length; + CHECK(avifStreamReadUX8(&s, &extentLength, lengthSize)); + + int itemIndex = findItemID(data, itemID); + if (itemIndex == -1) { + return AVIF_FALSE; + } + data->items[itemIndex].id = itemID; + data->items[itemIndex].offset = (uint32_t)(baseOffset + extentOffset); + data->items[itemIndex].size = (uint32_t)extentLength; + } else { + // TODO: support more than one extent + return AVIF_FALSE; + } + } + + } else if (!memcmp(type, "iinf", 4)) { + // ItemInfoBox + + uint8_t version; + CHECK(avifStreamReadVersionAndFlags(&s, &version)); + contentSize -= 4; + if (version == 0) { + uint16_t entryCount; + CHECK(avifStreamReadU16(&s, &entryCount)); // unsigned int(16) entry_count; + contentSize -= sizeof(uint16_t); + } else if (version == 1) { + uint32_t entryCount; + CHECK(avifStreamReadU32(&s, &entryCount)); // unsigned int(16) entry_count; + contentSize -= sizeof(uint32_t); + } else { + return AVIF_FALSE; + } + + CHECK(avifParse(data, raw + s.offset, contentSize)); + + } else if (!memcmp(type, "infe", 4)) { + // ItemInfoEntry + + size_t startOffset = s.offset; + CHECK(avifStreamReadAndEnforceVersion(&s, 2)); // TODO: support version > 2? 2+ is required for item_type + + uint16_t itemID; // unsigned int(16) item_ID; + CHECK(avifStreamReadU16(&s, &itemID)); // + uint16_t itemProtectionIndex; // unsigned int(16) item_protection_index; + CHECK(avifStreamReadU16(&s, &itemProtectionIndex)); // + uint8_t itemType[4]; // unsigned int(32) item_type; + CHECK(avifStreamRead(&s, itemType, 4)); // + + // Skip the remainder of the box + CHECK(avifStreamSkip(&s, contentSize - (s.offset - startOffset))); + + int itemIndex = findItemID(data, itemID); + if (itemIndex == -1) { + return AVIF_FALSE; + } + memcpy(data->items[itemIndex].type, itemType, sizeof(itemType)); + + } else { + // Unsupported box, move on + CHECK(avifStreamSkip(&s, contentSize)); + } + } + return AVIF_TRUE; +} + +static aom_image_t * decodeOBU(aom_codec_ctx_t * decoder, avifRawData * inputOBU) +{ + aom_codec_stream_info_t si; + aom_codec_iface_t * decoder_interface = aom_codec_av1_dx(); + if (aom_codec_dec_init(decoder, decoder_interface, NULL, 0)) { + return NULL; + } + + if (aom_codec_control(decoder, AV1D_SET_OUTPUT_ALL_LAYERS, 1)) { + return NULL; + } + + si.is_annexb = 0; + if (aom_codec_peek_stream_info(decoder_interface, inputOBU->data, inputOBU->size, &si)) { + return NULL; + } + + if (aom_codec_decode(decoder, inputOBU->data, inputOBU->size, NULL)) { + return NULL; + } + + aom_codec_iter_t iter = NULL; + return aom_codec_get_frame(decoder, &iter); // It doesn't appear that I own this / need to free this +} + +avifResult avifImageRead(avifImage * image, avifRawData * input) +{ + // ----------------------------------------------------------------------- + // Parse BMFF boxes + + avifData data; + memset(&data, 0, sizeof(data)); + if (!avifParse(&data, input->data, input->size)) { + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + // ----------------------------------------------------------------------- + + avifRawData colorOBU = AVIF_RAW_DATA_EMPTY; + avifRawData alphaOBU = AVIF_RAW_DATA_EMPTY; + + for (int itemIndex = 0; itemIndex < MAX_ITEMS; ++itemIndex) { + avifItem * item = &data.items[itemIndex]; + if (!item->id || !item->size) { + break; + } + if (item->offset > input->size) { + break; + } + if ((item->offset + item->size) > input->size) { + break; + } + + if (memcmp(item->type, "av01", 4)) { + // probably exif or some other data + continue; + } + + if (colorOBU.size == 0) { + colorOBU.data = input->data + item->offset; + colorOBU.size = item->size; + } else { + alphaOBU.data = input->data + item->offset; + alphaOBU.size = item->size; + break; + } + } + + if (colorOBU.size == 0) { + return AVIF_RESULT_NO_AV1_ITEMS_FOUND; + } + avifBool hasAlpha = (alphaOBU.size > 0) ? AVIF_TRUE : AVIF_FALSE; + + aom_codec_ctx_t colorDecoder; + aom_image_t * aomColorImage = decodeOBU(&colorDecoder, &colorOBU); + if (!aomColorImage) { + aom_codec_destroy(&colorDecoder); + return AVIF_RESULT_DECODE_COLOR_FAILED; + } + + aom_codec_ctx_t alphaDecoder; + aom_image_t * aomAlphaImage = NULL; + if (hasAlpha) { + aomAlphaImage = decodeOBU(&alphaDecoder, &alphaOBU); + if (!aomAlphaImage) { + aom_codec_destroy(&colorDecoder); + aom_codec_destroy(&alphaDecoder); + return AVIF_RESULT_DECODE_ALPHA_FAILED; + } + if ((aomColorImage->d_w != aomAlphaImage->d_w) || (aomColorImage->d_h != aomAlphaImage->d_h)) { + aom_codec_destroy(&colorDecoder); + aom_codec_destroy(&alphaDecoder); + return AVIF_RESULT_COLOR_ALPHA_SIZE_MISMATCH; + } + } + + if (data.ispe_seen && ((data.ispe_width != aomColorImage->d_w) || (data.ispe_height != aomColorImage->d_h))) { + aom_codec_destroy(&colorDecoder); + if (hasAlpha) { + aom_codec_destroy(&alphaDecoder); + } + return AVIF_RESULT_ISPE_SIZE_MISMATCH; + } + + avifPixelFormat pixelFormat; + int xShift = 0; + int yShift = 0; + switch (aomColorImage->fmt) { + case AOM_IMG_FMT_I42016: + pixelFormat = AVIF_PIXEL_FORMAT_YUV420; + xShift = 1; + yShift = 1; + break; + case AOM_IMG_FMT_I44416: + pixelFormat = AVIF_PIXEL_FORMAT_YUV444; + break; + default: + aom_codec_destroy(&colorDecoder); + if (hasAlpha) { + aom_codec_destroy(&alphaDecoder); + } + return AVIF_UNSUPPORTED_PIXEL_FORMAT; + } + + avifImageCreatePixels(image, pixelFormat, aomColorImage->d_w, aomColorImage->d_h, aomColorImage->bit_depth); + + uint16_t maxChannel = (1 << image->depth) - 1; + for (int j = 0; j < image->height; ++j) { + for (int i = 0; i < image->width; ++i) { + uint16_t * planeChannel; + int x = i >> xShift; + int y = j >> yShift; + + planeChannel = (uint16_t *)&aomColorImage->planes[0][(j * aomColorImage->stride[0]) + (2 * i)]; + image->planes[0][i + (j * image->strides[0])] = *planeChannel; + planeChannel = (uint16_t *)&aomColorImage->planes[1][(y * aomColorImage->stride[1]) + (2 * x)]; + image->planes[1][x + (y * image->strides[1])] = *planeChannel; + planeChannel = (uint16_t *)&aomColorImage->planes[2][(y * aomColorImage->stride[2]) + (2 * x)]; + image->planes[2][x + (y * image->strides[2])] = *planeChannel; + + if (hasAlpha) { + uint16_t * planeChannel = (uint16_t *)&aomAlphaImage->planes[0][(j * aomColorImage->stride[0]) + (2 * i)]; + image->planes[3][i + (j * image->strides[3])] = *planeChannel; + } else { + image->planes[3][i + (j * image->strides[3])] = maxChannel; + } + } + } + + aom_codec_destroy(&colorDecoder); + if (hasAlpha) { + aom_codec_destroy(&alphaDecoder); + } + return AVIF_RESULT_OK; +}