Add avifinfo_demo.c Update README.md. Change-Id: Iea78b740b332bae4e751cd8169bd68b8136ce965
diff --git a/CMakeLists.txt b/CMakeLists.txt index 154183b..29bd74c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -34,6 +34,13 @@ NAME avifinfo_test COMMAND ${CMAKE_CURRENT_BINARY_DIR}/avifinfo_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests) + + add_executable(avifinfo_demo tests/avifinfo_demo.c) + target_link_libraries(avifinfo_demo PRIVATE avifinfo) + add_test( + NAME avifinfo_demo + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/avifinfo_demo avifinfo_test_1x1.avif + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests) endif() # C++ tools
diff --git a/README.md b/README.md index fe27e5d..83d9a6f 100644 --- a/README.md +++ b/README.md
@@ -4,7 +4,7 @@ height, bit depth, number of channels and other metadata from an AVIF payload. See `avifinfo.h` for details on the API and `avifinfo.c` for the implementation. -See `avifinfo_test.cc` for usage examples. +See `tests/avifinfo_demo.cc` for API usage examples. ## Contents @@ -29,15 +29,9 @@ not an option. For decoding an image or extracting more features, please rely on [libavif](https://github.com/AOMediaCodec/libavif). -``` -AvifInfoFeatures features; -if (AvifInfoGet(bytes, number_of_available_bytes, &features) == kAvifInfoOk) { - // Use 'features.width' etc. -} -``` - -Note: `AvifInfoGet()` is designed to return the same `avifImage` field values as -[`avifDecoderRead()`](https://github.com/AOMediaCodec/libavif/blob/9d8f9f9eb24fcea36113c946fa72f9f92aa7b317/include/avif/avif.h#L894). +Note: `AvifInfoGetFeatures()` is designed to return the same `avifImage` field +values as +[`avifDecoderParse()`](https://github.com/AOMediaCodec/libavif/blob/e34204f5370509c72b3b2f065e5ebb2767cbbd48/include/avif/avif.h#L1049). However **libavifinfo** is more permissive and may return features of images considered invalid by **libavif**. @@ -45,7 +39,7 @@ `avifinfo.c` is written in C. To build from this directory: -``` +```sh mkdir build && \ cd build && \ cmake .. && \ @@ -56,7 +50,7 @@ Tests are written in C++. GoogleTest is required. -``` +```sh mkdir build && \ cd build && \ cmake .. -DAVIFINFO_BUILD_TESTS=ON && \
diff --git a/tests/avifinfo_demo.c b/tests/avifinfo_demo.c new file mode 100644 index 0000000..b016c94 --- /dev/null +++ b/tests/avifinfo_demo.c
@@ -0,0 +1,147 @@ +// Copyright (c) 2023, 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 <stdint.h> +#include <stdio.h> +#include <string.h> + +#include "avifinfo.h" + +//------------------------------------------------------------------------------ +// Simple API + +// Returns true if the file is an AVIF file. +static int Identify(const char* file_path) { + FILE* file = fopen(file_path, "rb"); + if (!file) return 0; + uint8_t data[32]; // File type is likely known in the first 32 bytes. + const size_t data_size = fread(data, 1, sizeof(data), file); + fclose(file); + if (AvifInfoIdentify(data, data_size) == kAvifInfoOk) { + // The file signature identifies it as AVIF. + return 1; + } + return 0; +} + +// Returns true if the file is an AVIF file and if a few features were +// successfully parsed. +static int IdentifyAndGetFeatures(const char* file_path) { + FILE* file = fopen(file_path, "rb"); + if (!file) return 0; + uint8_t data[512]; // Features are probably available within 512 bytes. + const size_t data_size = fread(data, 1, sizeof(data), file); + fclose(file); + AvifInfoFeatures features; + if (AvifInfoGetFeatures(data, data_size, &features) == kAvifInfoOk) { + // Use features.width, features.height etc. + return 1; + } + return 0; +} + +//------------------------------------------------------------------------------ +// Stream API + +typedef struct Stream { + FILE* file; + uint8_t data[AVIFINFO_MAX_NUM_READ_BYTES]; +} Stream; + +static const uint8_t* StreamRead(void* stream, size_t num_bytes) { + Stream* s = (Stream*)stream; + if (!s->file) return NULL; + const size_t data_size = fread(s->data, 1, num_bytes, s->file); + if (num_bytes > data_size) return NULL; + return s->data; +} + +static void StreamSkip(void* stream, size_t num_bytes) { + Stream* s = (Stream*)stream; + if (!s->file) return; + if (fseek(s->file, num_bytes, SEEK_CUR)) { + fclose(s->file); + s->file = NULL; + } +} + +// Returns true if the file is an AVIF file. +static int IdentifyStream(const char* file_path) { + Stream stream = {0}; + stream.file = fopen(file_path, "rb"); + if (AvifInfoIdentifyStream(&stream, StreamRead, StreamSkip) == kAvifInfoOk) { + // The file signature identifies it as AVIF. + fclose(stream.file); + return 1; + } + fclose(stream.file); + return 0; +} + +// Returns true if the file is an AVIF file and if a few features were +// successfully parsed. +static int IdentifyAndGetFeaturesStream(const char* file_path) { + Stream stream = {0}; + stream.file = fopen(file_path, "rb"); + if (AvifInfoIdentifyStream(&stream, StreamRead, StreamSkip) == kAvifInfoOk) { + // The file signature identifies it as AVIF. + + AvifInfoFeatures features; + if (AvifInfoGetFeaturesStream(&stream, StreamRead, StreamSkip, &features) == + kAvifInfoOk) { + // Use features.width, features.height etc. + fclose(stream.file); + return 1; + } + } + fclose(stream.file); + return 0; +} + +// Alternative to IdentifyAndGetFeaturesStream() where the stream object cannot +// be shared between AvifInfoIdentifyStream() and AvifInfoGetFeaturesStream(). +static int IdentifyAndGetFeaturesStreams(const char* file_path) { + { + Stream stream = {0}; + stream.file = fopen(file_path, "rb"); + if (AvifInfoIdentifyStream(&stream, StreamRead, StreamSkip) == + kAvifInfoOk) { + // The file signature identifies it as AVIF. + fclose(stream.file); + return 1; + } + fclose(stream.file); + } + { + Stream stream = {0}; + stream.file = fopen(file_path, "rb"); + AvifInfoFeatures features; + // This is allowed because AvifInfoIdentifyStream() was successful on the + // same input file bytes. + if (AvifInfoGetFeaturesStream(&stream, StreamRead, StreamSkip, &features) == + kAvifInfoOk) { + // Use features.width, features.height etc. + fclose(stream.file); + return 1; + } + fclose(stream.file); + } + return 0; +} + +//------------------------------------------------------------------------------ + +int main(int argc, char** argv) { + if (argc >= 2 && Identify(argv[1]) && IdentifyAndGetFeatures(argv[1]) && + IdentifyStream(argv[1]) && IdentifyAndGetFeaturesStream(argv[1]) && + IdentifyAndGetFeaturesStreams(argv[1])) { + return 0; + } + return 1; +}