blob: c5ee4f6c511f5708669fa60e760560733aa71996 [file] [log] [blame]
// 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;
}