From: jiyong.min Date: Thu, 4 Feb 2021 23:19:52 +0000 (+0900) Subject: apply HEIF library X-Git-Tag: submit/tizen/20210309.051823^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a48d749ab908baba79a9b27abe9ccb018bfffdf0;p=platform%2Fcore%2Fmultimedia%2Flibheif.git apply HEIF library Multimedia Framework Library for decoding HEIF(ISO/IEC 23008-12) image License: Apache 2.0 Dependency: FFmpeg How to test 1. After building this library by GBS, please install the rpm package on a target device or an emulator. 2. Run test/test.sh (You can modify test/test.sh as enviroment) 3. Check the result images on PC Change-Id: Ida20d33dd2dce8f45b05d6fc2bc66036fabeede2 --- diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..d2cefa3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,78 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +SET(fw_name "libheif") + +# project +SET(prefix "/usr") +SET(maintainer "Haejeong Kim " "Jiyong Min " "Minje Ahn ") +SET(description "A heif decoder library in Tizen Multimedia Framework") +SET(service "mmf") + +# for package file +SET(dependents "dlog glib-2.0 mm-common libavcodec libavformat libavutil libswscale") +SET(pc_dependents "") + +PROJECT(${fw_name} C CXX) + +SET(CMAKE_INSTALL_PREFIX ${prefix}) +SET(PREFIX ${CMAKE_INSTALL_PREFIX}) +SET(VERSION_MAJOR 1) +SET(VERSION "${VERSION_MAJOR}.0.0") + +SET(INC_DIR include) +INCLUDE_DIRECTORIES(${INC_DIR}) + +INCLUDE(FindPkgConfig) +pkg_check_modules(${fw_name} REQUIRED ${dependents}) +FOREACH(cflag ${${fw_name}_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${cflag}") +ENDFOREACH(cflag) +FOREACH(cxxflag ${${fw_name}_CXXFLAGS}) + SET(EXTRA_CXXFLAGS "${EXTRA_CXXFLAGS} ${cxxflag}") +ENDFOREACH(cxxflag) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC -Wall -Werror") +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXXFLAGS} -std=c++11 -fPIC -Wall") +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g") + +IF("${ARCH}" MATCHES "^arm.*") + ADD_DEFINITIONS("-DTARGET") +ENDIF("${ARCH}" MATCHES "^arm.*") + +ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"") +ADD_DEFINITIONS("-DTIZEN_DEBUG") +ADD_DEFINITIONS("-D_FILE_OFFSET_BITS=64") + +SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -Wl,--rpath=${LIB_INSTALL_DIR}") + +aux_source_directory(src SOURCES) +ADD_LIBRARY(${fw_name} SHARED ${SOURCES}) +TARGET_LINK_LIBRARIES(${fw_name} ${${fw_name}_LDFLAGS}) + +SET_TARGET_PROPERTIES(${fw_name} + PROPERTIES + VERSION ${VERSION} + SOVERSION ${VERSION_MAJOR} + CLEAN_DIRECT_OUTPUT 1 +) + +INSTALL(TARGETS ${fw_name} DESTINATION ${LIB_INSTALL_DIR}) +INSTALL( + DIRECTORY ${INC_DIR}/ DESTINATION include/${service} + FILES_MATCHING + PATTERN "*_debug.h" EXCLUDE + PATTERN "*_private.h" EXCLUDE + PATTERN "${INC_DIR}/*.h" + ) + +SET(PC_NAME ${fw_name}) +SET(PC_REQUIRED ${pc_dependents}) +SET(PC_LDFLAGS -l${fw_name}) + +CONFIGURE_FILE( + ${fw_name}.pc.in + ${CMAKE_CURRENT_SOURCE_DIR}/${fw_name}.pc + @ONLY +) +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${fw_name}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) + +ADD_SUBDIRECTORY(test) diff --git a/LICENSE.APLv2.0 b/LICENSE.APLv2.0 new file mode 100644 index 0000000..29f81d8 --- /dev/null +++ b/LICENSE.APLv2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/include/heif.h b/include/heif.h new file mode 100644 index 0000000..51c0022 --- /dev/null +++ b/include/heif.h @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __HEIF_H__ +#define __HEIF_H__ + +#include + +/** +* @brief Decodes the image from the heif file. +* @details This function emits the primary image in heif. +* @since_tizen 6.5 +* +* @remarks The @a image should be released using heif_image_free().\n +* The heif image can have some displayable images, this function emits only the primary image. +* +* @param[in] path The path of heif image +* @param[in] format The color format of the emitted image +* @param[out] image The handle for a decoded image +* +* @return @c 0 on success, +* otherwise a negative error value +* +* @see heif_image_free() +* @see heif_image_get_image() +* + +#include + +int ret = 0; +heif_image_h image = NULL; + +ret = heif_decode_image_from_file("/opt/usr/home/owner/media/sample_test.heic", HEIF_COLOR_FORMAT_YUV420P, &image); +if (ret < 0) + g_print("heif_decode_image_from_file fail %d\n", ret); + +*/ +int heif_decode_image_from_file(const char *path, heif_color_format_e format, heif_image_h *image); + +/** +* @brief Decodes the image from the heif buffer. +* @details This function emits the primary image in heif. +* @since_tizen 6.5 +* +* @remarks The @a image should be released using heif_image_free(). +* +* @param[in] data The buffer of heif image +* @param[in] size The size of buffer +* @param[in] format The color format of the emitted image +* @param[out] image The handle for a decoded image +* +* @return @c 0 on success, +* otherwise a negative error value +* +* @see heif_image_free() +* @see heif_image_get_image() +* +*/ +int heif_decode_image_from_buffer(unsigned char *data, size_t size, heif_color_format_e format, heif_image_h *image); + +/** +* @brief Decodes the thumbnail from the heif file. +* @details This function emits the thumbnail of the primary image in heif. +* @since_tizen 6.5 +* +* @remarks The @a thumb should be released using heif_image_free(). +* +* @param[in] path The path of heif image +* @param[in] format The color format of the emitted thumbnail +* @param[out] image The handle for a decoded image +* +* @return @c 0 on success, +* otherwise a negative error value +* +* @see heif_image_free() +* @see heif_image_get_image() +* + +#include + +int ret = 0; +heif_image_h thumbnail = NULL; + +ret = heif_decode_thumb_from_file("/opt/usr/home/owner/media/sample_test.heic", HEIF_COLOR_FORMAT_YUV420P, &thumbnail); +if (ret < 0) + g_print("heif_decode_thumb_from_file fail %d\n", ret); + +*/ +int heif_decode_thumb_from_file(const char *path, heif_color_format_e format, heif_image_h *thumb); + +/** +* @brief Decodes the thumbnail from the heif buffer. +* @details This function emits the thumbnail of the primary image in heif. +* @since_tizen 6.5 +* +* @remarks The @a thumb should be released using heif_image_free(). +* +* @param[in] data The buffer of heif image +* @param[in] size The size of buffer +* @param[in] format The color format of the emitted thumbnail +* @param[out] image The handle for a decoded image +* +* @return @c 0 on success, +* otherwise a negative error value +* +* @see heif_image_free() +* @see heif_image_get_image() +* +*/ +int heif_decode_thumb_from_buffer(unsigned char *data, size_t size, heif_color_format_e format, heif_image_h *thumb); + +/** +* @brief Gets the data in the image handle. +* @details This function emits the data of an image. +* @since_tizen 6.5 +* +* @param[in] image The handle for the decoded image +* @param[out] width The width of the image +* @param[out] height The height of the image +* @param[out] format The color format of the image +* @param[out] data The data of the image +* @param[out] size The size of the @a data +* +* @return @c 0 on success, +* otherwise a negative error value +* +*/ +int heif_image_get_image(heif_image_h image, unsigned int *width, unsigned int *height, heif_color_format_e *format, unsigned char **data, size_t *size); + +/** +* @brief Destroys an image handle. +* @since_tizen 6.5 +* +* @param[in] image The handle of an image +* +*/ +void heif_image_free(heif_image_h image); + +/** +* @brief Gets metadata of a heif image file. +* @details This function emits the handle of an image information. +* @since_tizen 6.5 +* +* @remarks The @a image_info should be released using heif_image_info_free(). +* +* @param[in] path The path of heif image +* @param[out] image_info The handle for image information +* +* @return @c 0 on success, +* otherwise a negative error value +* +* @see heif_image_info_free() +* + +#include + +int ret = 0; +heif_image_info_h image_info = NULL; + +ret = heif_get_image_info_from_file("/opt/usr/home/owner/media/sample_test.heic", &image_info); +if (ret < 0) + g_print("heif_get_image_info_from_file fail %d\n", ret); + +*/ +int heif_get_image_info_from_file(const char *path, heif_image_info_h *image_info); + +/** +* @brief Gets metadata of a heif image buffer. +* @details This function emits the handle of an image information. +* @since_tizen 6.5 +* +* @remarks The @a image_info should be released using heif_image_info_free(). +* +* @param[in] data The buffer of heif image +* @param[in] size The size of buffer +* @param[out] image_info The handle for an image information +* +* @return @c 0 on success, +* otherwise a negative error value +* +* @see heif_image_info_free() +* +*/ +int heif_get_image_info_from_buffer(unsigned char *data, size_t size, heif_image_info_h *image_info); + +/** +* @brief Gets attributes from the image information handle. +* @since_tizen 6.5 +* +* @remarks This function must be terminated by NULL argument.\n +* +* @param[in] image_info The handle for an image information +* @param[in] first_attribute_name The name of the first attribute to get +* @param[in] ... The value of the first attribute, followed optionally by more name/value pairs, terminated by NULL. +* +* @return @c 0 on success, +* otherwise a negative error value +* + +#include + +int ret = 0; +int width = 0, height = 0, orientation = 0, mirror = 0, mimetype_len = 0; +char *mimetype = NULL; + +// get basic information +ret = heif_image_info_get_attrs(image_info, + HEIF_IMAGE_MIMETYPE, &mimetype, &mimetype_len, + HEIF_IMAGE_WIDTH, &width, + HEIF_IMAGE_HEIGHT, &height, + HEIF_IMAGE_ORIENTATION, &orientation, + HEIF_IMAGE_MIRROR, &mirror, + NULL); + +if (ret < 0) + g_print("heif_image_info_get_attrs fail %d\n", ret); + +g_print("Subtype\t: %s\n", mimetype); +g_print("Width\t: %d\n", width); +g_print("Height\t: %d\n", height); +g_print("Orientation\t: %d\n", orientation); +g_print("Mirror\t: %d\n", mirror); + +*/ +int heif_image_info_get_attrs(heif_image_info_h image_info, const char *first_attribute_name, ...); + +/** +* @brief Destroys an image information handle. +* @since_tizen 6.5 +* +* @param[in] image_info The handle for an image information +* +*/ +void heif_image_info_free(heif_image_info_h image_info); + + +#endif /* __HEIF_H__ */ diff --git a/include/heif_debug.h b/include/heif_debug.h new file mode 100644 index 0000000..75bf54b --- /dev/null +++ b/include/heif_debug.h @@ -0,0 +1,103 @@ +/* +* Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef __HEIF_DEBUG_H__ +#define __HEIF_DEBUG_H__ + +#include +#include + +#ifdef LOG_TAG +#undef LOG_TAG +#endif + +#define LOG_TAG "LIBHEIF" +#define BUF_LENGTH 256 + +#define FONT_COLOR_RESET "\033[0m" +#define FONT_COLOR_RED "\033[31m" +#define FONT_COLOR_GREEN "\033[32m" +#define FONT_COLOR_YELLOW "\033[33m" +#define FONT_COLOR_BLUE "\033[34m" +#define FONT_COLOR_PURPLE "\033[35m" +#define FONT_COLOR_CYAN "\033[36m" +#define FONT_COLOR_GRAY "\033[37m" + +#define heif_debug(fmt, arg...) do { \ + LOGD(FONT_COLOR_RESET""fmt"", ##arg); \ + } while (0) + +#define heif_info(fmt, arg...) do { \ + LOGI(FONT_COLOR_GREEN""fmt""FONT_COLOR_RESET, ##arg); \ + } while (0) + +#define heif_warning(fmt, arg...) do { \ + LOGW(FONT_COLOR_RED""fmt""FONT_COLOR_RESET, ##arg); \ + } while (0) + +#define heif_error(fmt, arg...) do { \ + LOGE(FONT_COLOR_RED""fmt""FONT_COLOR_RESET, ##arg); \ + } while (0) + +#define heif_secure_debug(fmt, arg...) do { \ + SECURE_LOGD(FONT_COLOR_CYAN""fmt""FONT_COLOR_RESET, ##arg); \ + } while (0) + +#define heif_secure_warning(fmt, arg...) do { \ + SECURE_LOGW(FONT_COLOR_RED""fmt""FONT_COLOR_RESET, ##arg); \ + } while (0) + +#define heif_secure_error(fmt, arg...) do { \ + SECURE_LOGE(FONT_COLOR_RED""fmt""FONT_COLOR_RESET, ##arg); \ + } while (0) + +#define heif_debug_fenter() do { \ + LOGD(FONT_COLOR_YELLOW""FONT_COLOR_RESET); \ + } while (0) + +#define heif_debug_fleave() do { \ + LOGD(FONT_COLOR_YELLOW""FONT_COLOR_RESET); \ + } while (0) + +#define heif_info_fenter() do { \ + LOGI(FONT_COLOR_GREEN""FONT_COLOR_RESET); \ + } while (0) + +#define heif_info_fleave() do { \ + LOGI(FONT_COLOR_GREEN""FONT_COLOR_RESET); \ + } while (0) + +#define heif_retm_if_failed(expr) do { \ + if (!(expr)) { \ + heif_error("invalid %s", #expr); \ + return; \ + } \ + } while (0) + +#define heif_retvm_if_failed(expr, val, fmt, arg...) do { \ + if (!(expr)) { \ + LOGE(FONT_COLOR_RED""fmt""FONT_COLOR_RESET, ##arg); \ + return (val); \ + } \ + } while (0) + +#define heif_strerror(fmt) do { \ + char buf[BUF_LENGTH] = {0,}; \ + strerror_r(errno, buf, BUF_LENGTH); \ + LOGE(FONT_COLOR_RED""fmt""" : STANDARD ERROR [%s]"FONT_COLOR_RESET, buf); \ + } while (0) + +#endif /* __HEIF_DEBUG_H__ */ diff --git a/include/heif_decoder.h b/include/heif_decoder.h new file mode 100644 index 0000000..b72a393 --- /dev/null +++ b/include/heif_decoder.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __HEIF_DECODER_H__ +#define __HEIF_DECODER_H__ + +#include "heif_type.h" +#include "heif_itemtable.h" + +typedef void * heif_decoder_h; + +int heif_decoder_decode_primary(heif_itemtable_h item_table, heif_color_format_e format, heif_image_h *image); +int heif_decoder_decode_thumbnail(heif_itemtable_h item_table, heif_color_format_e format, heif_image_h *image); +int heif_decoder_get_image(heif_image_h image, unsigned int *width, unsigned int *height, heif_color_format_e *format, unsigned char **data, size_t *size); +void heif_decoder_destroy_image(heif_image_h image); + +#endif /* __HEIF_DECODER_H__ */ diff --git a/include/heif_extractor.h b/include/heif_extractor.h new file mode 100644 index 0000000..d8a8738 --- /dev/null +++ b/include/heif_extractor.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __HEIF_EXTRACTOR_H__ +#define __HEIF_EXTRACTOR_H__ + +#include "heif_source.h" +#include "heif_itemtable.h" + +int heif_extractor_extract(heif_source_h source, heif_itemtable_h *item_table); + +#endif /* __HEIF_EXTRACTOR_H__ */ diff --git a/include/heif_itemtable.h b/include/heif_itemtable.h new file mode 100644 index 0000000..cdb55da --- /dev/null +++ b/include/heif_itemtable.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __HEIF_ITEMTABLE_H__ +#define __HEIF_ITEMTABLE_H__ + +#include +#include +#include + +#include "heif_source.h" + +typedef struct { + uint8_t *data; + size_t size; +} heif_buffer_t; + +typedef void * heif_itemtable_h; +typedef void * heif_image_item_h; + +int heif_itemtable_create(heif_itemtable_h *handle, heif_source_h source); +int heif_itemtable_parse(heif_itemtable_h handle, int32_t chunk_type, off_t data_offset, off_t chunk_data_size); +bool heif_itemtable_is_valid(heif_itemtable_h handle); +bool heif_itemtable_has_grid(heif_itemtable_h handle); +int heif_itemtable_get_primary_image(heif_itemtable_h handle, unsigned int *primary_id); +int heif_itemtable_get_image_item(heif_itemtable_h handle, unsigned int item_id, heif_image_item_h *image_item); +int heif_itemtable_get_thumb_item(heif_itemtable_h handle, heif_image_item_h *thumb_item); +int heif_itemtable_get_image_resolution(heif_image_item_h image_item, unsigned int *width, unsigned int *height); +int heif_itemtable_get_image_orientation(heif_image_item_h image_item, unsigned int *orientation); +int heif_itemtable_get_image_mirror_mode(heif_image_item_h image_item, unsigned int *axis); +int heif_itemtable_get_grid_info(heif_image_item_h image_item, unsigned int *row, unsigned int *column, GSList **img_refs); +int heif_itemtable_get_coded_data(heif_itemtable_h handle, heif_image_item_h image_item, heif_buffer_t **coded_data); +int heif_itemtable_get_exif(heif_itemtable_h handle, heif_image_item_h image_item, void **exif, size_t *exif_size); +int heif_itemtable_get_icc_profile(heif_itemtable_h handle, heif_image_item_h image_item, void **icc, size_t *icc_size); +void heif_itemtable_print_info(heif_itemtable_h handle); +void heif_itemtable_destroy(heif_itemtable_h handle); +void heif_buffer_destroy(heif_buffer_t *buffer); + +#endif /* __HEIF_ITEMTABLE_H__ */ diff --git a/include/heif_source.h b/include/heif_source.h new file mode 100644 index 0000000..e8a06a3 --- /dev/null +++ b/include/heif_source.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __HEIF_SOURCE_H__ +#define __HEIF_SOURCE_H__ + +#include +#include + +typedef void * heif_source_h; + +int heif_source_create_from_file(const char *path, heif_source_h *source); +int heif_source_create_from_buffer(unsigned char *buf, size_t size, heif_source_h *source); +size_t heif_source_get_size(heif_source_h source); +size_t heif_source_read_at(heif_source_h source, off_t offset, void *data, size_t size); +void heif_source_destroy(heif_source_h source); + +// Convenience methods: +bool heif_source_get_uint16(heif_source_h source, off_t offset, uint16_t *x); +bool heif_source_get_uint24(heif_source_h source, off_t offset, uint32_t *x); +bool heif_source_get_uint32(heif_source_h source, off_t offset, uint32_t *x); +bool heif_source_get_uint64(heif_source_h source, off_t offset, uint64_t *x); +bool heif_source_get_uint16var(heif_source_h source, off_t offset, uint16_t *x, size_t size); +bool heif_source_get_uint32var(heif_source_h source, off_t offset, uint32_t *x, size_t size); +bool heif_source_get_uint64var(heif_source_h source, off_t offset, uint64_t *x, size_t size); + +#endif /* __HEIF_SOURCE_H__ */ diff --git a/include/heif_type.h b/include/heif_type.h new file mode 100644 index 0000000..827e432 --- /dev/null +++ b/include/heif_type.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __HEIF_TYPE_H__ +#define __HEIF_TYPE_H__ + +#include + +/** + * heif attributes. + */ +#define HEIF_IMAGE_MIMETYPE "heif-image-mimetype" /**< Mimetype brand of heif */ +#define HEIF_IMAGE_WIDTH "heif-image-width" /**< Width of primary image in heif */ +#define HEIF_IMAGE_HEIGHT "heif-image-height" /**< Height of primary image in heif */ +#define HEIF_IMAGE_ORIENTATION "heif-image-orientation" /**< Orientation of primary image in heif */ +#define HEIF_IMAGE_MIRROR "heif-image-mirror" /**< Mirror of primary image in heif */ +#define HEIF_IMAGE_EXIF "heif-image-exif" /**< Exif of primary image in heif */ +#define HEIF_IMAGE_ICC_PROFILE "heif-image-icc-profile" /**< ICC profile of primary image in heif */ + +typedef void *heif_image_h; +typedef void *heif_image_info_h; + +typedef enum { + LIBHEIF_ERROR_NONE = TIZEN_ERROR_NONE, + LIBHEIF_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER, + LIBHEIF_ERROR_PERMISSION_DENIED = TIZEN_ERROR_PERMISSION_DENIED, + LIBHEIF_ERROR_NOT_SUPPORTED = TIZEN_ERROR_NOT_SUPPORTED, + LIBHEIF_ERROR_INVALID_OPERATION = TIZEN_ERROR_INVALID_OPERATION, +} heif_error_e; + +/** + * heif color format. + */ +typedef enum { + HEIF_COLOR_FORMAT_NONE, + HEIF_COLOR_FORMAT_YUV420P, + HEIF_COLOR_FORMAT_RGB24, + HEIF_COLOR_FORMAT_ARGB, + HEIF_COLOR_FORMAT_BGRA, + HEIF_COLOR_FORMAT_RGBA, + HEIF_COLOR_FORMAT_MAX, +} heif_color_format_e; + +/** + * mirror mode of a heif image. + */ +typedef enum { + HEIF_MIRROR_NONE, + HEIF_MIRROR_VERTICAL, + HEIF_MIRROR_HORIZONTAL, +} heif_mirror_mode_e; + +#endif /* __HEIF_TYPE_H__ */ diff --git a/libheif.pc.in b/libheif.pc.in new file mode 100644 index 0000000..6718003 --- /dev/null +++ b/libheif.pc.in @@ -0,0 +1,11 @@ +prefix=@PREFIX@ +exec_prefix=/usr +libdir=@LIB_INSTALL_DIR@ +includedir=/usr/include/mmf + +Name : @PC_NAME@ +Description : Read HEIF files +Requires : +Version : @VERSION@ +Libs : -L${libdir} @PC_LDFLAGS@ +Cflags : -I${includedir} -DLIBPREFIX=\"${libdir}\" diff --git a/packaging/libheif.manifest b/packaging/libheif.manifest new file mode 100644 index 0000000..017d22d --- /dev/null +++ b/packaging/libheif.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/libheif.spec b/packaging/libheif.spec new file mode 100644 index 0000000..0754ad7 --- /dev/null +++ b/packaging/libheif.spec @@ -0,0 +1,69 @@ +Name: libheif +Summary: Multimedia Framework Library for HEIF(ISO/IEC 23008-12) image +Version: 0.0.1 +Release: 0 +Group: Multimedia/Libraries +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz +Source1001: libheif.manifest +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig + +BuildRequires: cmake +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(libavcodec) +BuildRequires: pkgconfig(libavformat) +BuildRequires: pkgconfig(libavutil) +BuildRequires: pkgconfig(libswscale) +BuildRequires: pkgconfig(mm-common) +BuildRequires: pkgconfig(mmutil-common) +BuildRequires: pkgconfig(mmutil-jpeg) +BuildRequires: pkgconfig(mmutil-magick) +BuildRoot: %{_tmppath}/%{name}-%{version}-build + +%description +Multimedia Framework Library for HEIF(ISO/IEC 23008-12) image - Main package. + +%package devel +Summary: Multimedia Framework Utility Library (DEV) +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} + +%description devel +Multimedia Framework Utility for HEIF(ISO/IEC 23008-12) image - Development files. + +%prep +%setup -q +cp %{SOURCE1001} . + +%build +export CFLAGS="$CFLAGS -DGMAGICK_DEBUG=0 -D_FORTIFY_SOURCE=2" +# enable debug options if you need +#export CFLAGS+=" -D__ENABLE_DEBUG_MODE -D__DEBUG_SOURCE -D__DEBUG_CODEC_CONFIG" + +%cmake . +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +%make_install + +%clean +rm -rf %{buildroot} + +%post -p /sbin/ldconfig +%postun -p /sbin/ldconfig + +%files +%manifest %{name}.manifest +%defattr(-,root,root,-) +%{_libdir}/*.so.* +%license LICENSE.APLv2.0 + +%files devel +%manifest %{name}.manifest +%defattr(-,root,root,-) +%{_includedir}/* +%{_libdir}/*.so +%{_libdir}/pkgconfig/* diff --git a/src/heif.c b/src/heif.c new file mode 100644 index 0000000..3dc636a --- /dev/null +++ b/src/heif.c @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include "heif.h" +#include "heif_debug.h" +#include "heif_source.h" +#include "heif_extractor.h" +#include "heif_decoder.h" + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif +#define HEIF_ATTRS_NAME "heif-image" +#define HEIC_MIMETYPE "image/heic" +#define DEFAULT_MIMETYPE HEIC_MIMETYPE + + +/** + * global values. + */ +static MMAttrsConstructInfo g_heif_attrs[] = { + {(char *)HEIF_IMAGE_MIMETYPE, MM_ATTRS_TYPE_STRING, MM_ATTRS_FLAG_RW, (void *)DEFAULT_MIMETYPE}, + {(char *)HEIF_IMAGE_WIDTH, MM_ATTRS_TYPE_INT, MM_ATTRS_FLAG_RW, (void *)0}, + {(char *)HEIF_IMAGE_HEIGHT, MM_ATTRS_TYPE_INT, MM_ATTRS_FLAG_RW, (void *)0}, + {(char *)HEIF_IMAGE_ORIENTATION, MM_ATTRS_TYPE_INT, MM_ATTRS_FLAG_RW, (void *)0}, + {(char *)HEIF_IMAGE_MIRROR, MM_ATTRS_TYPE_INT, MM_ATTRS_FLAG_RW, (void *)HEIF_MIRROR_NONE}, + {(char *)HEIF_IMAGE_EXIF, MM_ATTRS_TYPE_DATA, MM_ATTRS_FLAG_RW, (void *)NULL}, + {(char *)HEIF_IMAGE_ICC_PROFILE, MM_ATTRS_TYPE_DATA, MM_ATTRS_FLAG_RW, (void *)NULL}, +}; + +static int __heif_decode_image(heif_source_h source, heif_color_format_e format, heif_image_h *image); +static int __heif_decode_thumb(heif_source_h source, heif_color_format_e format, heif_image_h *thumb); +static int __heif_get_image_info(heif_source_h source, MMHandleType attrs); + + +int heif_decode_image_from_file(const char *path, heif_color_format_e format, heif_image_h *image) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_source_h source = NULL; + + ret = heif_source_create_from_file(path, &source); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_source_create_from_file fail %d", ret); + return ret; + } + + ret = __heif_decode_image(source, format, image); + if (ret != LIBHEIF_ERROR_NONE) + heif_error("__heif_decode_image fail %d", ret); + + heif_source_destroy(source); + + return ret; +} + +int heif_decode_image_from_buffer(unsigned char *data, size_t size, heif_color_format_e format, heif_image_h *image) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_source_h source = NULL; + + ret = heif_source_create_from_buffer(data, size, &source); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_source_create_from_buffer fail %d", ret); + return ret; + } + + ret = __heif_decode_image(source, format, image); + if (ret != LIBHEIF_ERROR_NONE) + heif_error("__heif_decode_image fail %d", ret); + + heif_source_destroy(source); + + return ret; +} + +int heif_decode_thumb_from_file(const char *path, heif_color_format_e format, heif_image_h *thumb) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_source_h source = NULL; + + ret = heif_source_create_from_file(path, &source); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_source_create_from_file fail %d", ret); + return ret; + } + + ret = __heif_decode_thumb(source, format, thumb); + if (ret != LIBHEIF_ERROR_NONE) + heif_error("__heif_decode_thumb fail %d", ret); + + heif_source_destroy(source); + + return ret; +} + +int heif_decode_thumb_from_buffer(unsigned char *data, size_t size, heif_color_format_e format, heif_image_h *thumb) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_source_h source = NULL; + + ret = heif_source_create_from_buffer(data, size, &source); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_source_create_from_buffer fail %d", ret); + return ret; + } + + ret = __heif_decode_thumb(source, format, thumb); + if (ret != LIBHEIF_ERROR_NONE) + heif_error("__heif_decode_thumb fail %d", ret); + + heif_source_destroy(source); + + return ret; +} + +int heif_image_get_image(heif_image_h image, unsigned int *width, unsigned int *height, heif_color_format_e *format, unsigned char **data, size_t *size) +{ + return heif_decoder_get_image(image, width, height, format, data, size); +} + +void heif_image_free(heif_image_h image) +{ + heif_decoder_destroy_image(image); +} + +int heif_get_image_info_from_file(const char *path, heif_image_info_h *image_info) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_source_h source = NULL; + MMHandleType attrs = NULL; + + ret = heif_source_create_from_file(path, &source); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_source_create_from_file fail %d", ret); + return ret; + } + + ret = mm_attrs_new(g_heif_attrs, ARRAY_SIZE(g_heif_attrs), HEIF_ATTRS_NAME, NULL, NULL, &attrs); + if (ret != MM_ERROR_NONE) { + heif_error("mm_attrs_new fail %d", ret); + ret = LIBHEIF_ERROR_INVALID_OPERATION; + goto END; + } + + ret = __heif_get_image_info(source, attrs); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__heif_get_image_info fail %d", ret); + mm_attrs_free(attrs); + goto END; + } + + *image_info = (heif_image_info_h)attrs; + +END: + heif_source_destroy(source); + + return ret; +} + +int heif_get_image_info_from_buffer(unsigned char *data, size_t size, heif_image_info_h *image_info) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_source_h source = NULL; + MMHandleType attrs = NULL; + + ret = heif_source_create_from_buffer(data, size, &source); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_source_create_from_buffer fail %d", ret); + return ret; + } + + ret = mm_attrs_new(g_heif_attrs, ARRAY_SIZE(g_heif_attrs), HEIF_ATTRS_NAME, NULL, NULL, &attrs); + if (ret != MM_ERROR_NONE) { + heif_error("mm_attrs_new fail %d", ret); + ret = LIBHEIF_ERROR_INVALID_OPERATION; + goto END; + } + + ret = __heif_get_image_info(source, attrs); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__heif_get_image_info fail %d", ret); + mm_attrs_free(attrs); + goto END; + } + + *image_info = (heif_image_info_h)attrs; + +END: + heif_source_destroy(source); + + return ret; +} + +int heif_image_info_get_attrs(heif_image_info_h image_info, const char *first_attribute_name, ...) +{ + int ret = LIBHEIF_ERROR_NONE; + MMHandleType attrs = (MMHandleType)image_info; + va_list var_args; + char *err_attr_name = NULL; + + heif_retvm_if_failed(first_attribute_name, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid first_attribute_name"); + + /* get requested attributes */ + va_start(var_args, first_attribute_name); + ret = mm_attrs_get_valist(attrs, &err_attr_name, first_attribute_name, var_args); + va_end(var_args); + + if (ret != MM_ERROR_NONE) { + if (err_attr_name) { + heif_error("mm_attrs_get_valist fail %d: %s", ret, err_attr_name); + free(err_attr_name); + } + if (ret == MM_ERROR_COMMON_INVALID_ARGUMENT) + ret = LIBHEIF_ERROR_INVALID_PARAMETER; + else + ret = LIBHEIF_ERROR_INVALID_OPERATION; + } + + return ret; +} + +void heif_image_info_free(heif_image_info_h image_info) +{ + int ret = LIBHEIF_ERROR_NONE; + MMHandleType attrs = (MMHandleType)image_info; + void *exif = NULL; + void *icc_profile = NULL; + + heif_retm_if_failed(image_info); + + ret = mm_attrs_get_data_by_name(attrs, HEIF_IMAGE_EXIF, &exif); + if (ret == MM_ERROR_NONE) + g_free(exif); + + ret = mm_attrs_get_data_by_name(attrs, HEIF_IMAGE_ICC_PROFILE, &icc_profile); + if (ret == MM_ERROR_NONE) + g_free(icc_profile); + + mm_attrs_free(attrs); +} + +static int __heif_decode_image(heif_source_h source, heif_color_format_e format, heif_image_h *image) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_itemtable_h item_table = NULL; + heif_image_h _image = NULL; + + heif_retvm_if_failed(image, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image"); + + ret = heif_extractor_extract(source, &item_table); + heif_retvm_if_failed(ret == LIBHEIF_ERROR_NONE, ret, "heif_extractor_extract fail %d", ret); + + ret = heif_decoder_decode_primary(item_table, format, &_image); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_decoder_decode_primary fail %d", ret); + goto END; + } + + *image = _image; + +END: + heif_itemtable_destroy(item_table); + + return ret; +} + +static int __heif_decode_thumb(heif_source_h source, heif_color_format_e format, heif_image_h *thumb) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_itemtable_h item_table = NULL; + heif_image_h _thumb = NULL; + + heif_retvm_if_failed(thumb, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid thumb"); + + ret = heif_extractor_extract(source, &item_table); + heif_retvm_if_failed(ret == LIBHEIF_ERROR_NONE, ret, "heif_extractor_extract fail %d", ret); + + ret = heif_decoder_decode_thumbnail(item_table, format, &_thumb); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_decoder_decode_thumbnail fail %d", ret); + goto END; + } + + *thumb = _thumb; + +END: + heif_itemtable_destroy(item_table); + + return ret; +} + +static int __heif_get_image_info(heif_source_h source, MMHandleType attrs) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_itemtable_h item_table = NULL; + unsigned int primary_id = 0; + heif_image_item_h primary_item = NULL; + unsigned int width = 0, height = 0, orientation = 0; + heif_mirror_mode_e mirror_mode = HEIF_MIRROR_NONE; + void *exif = NULL, *icc_profile = NULL; + size_t exif_size = 0, icc_profile_size = 0; + + heif_retvm_if_failed(attrs, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid attrs"); + + /* Extract heif image */ + ret = heif_extractor_extract(source, &item_table); + heif_retvm_if_failed(ret == LIBHEIF_ERROR_NONE, ret, "heif_extractor_extract fail %d", ret); + + /* Get information of primary image in HEIF */ + ret = heif_itemtable_get_primary_image(item_table, &primary_id); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_itemtable_get_primary_image fail %d", ret); + goto END; + } + + ret = heif_itemtable_get_image_item(item_table, primary_id, &primary_item); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_itemtable_get_image_item fail %d", ret); + goto END; + } + + /* Set basic information of primary image into MM_ATTRS */ + ret = heif_itemtable_get_image_resolution(primary_item, &width, &height); + if (ret == LIBHEIF_ERROR_NONE) { + ret = mm_attrs_set_int_by_name(attrs, HEIF_IMAGE_WIDTH, (int)width); + if (ret != MM_ERROR_NONE) + heif_error("mm_attrs_set_int_by_name fail %d: %s", ret, HEIF_IMAGE_WIDTH); + + ret = mm_attrs_set_int_by_name(attrs, HEIF_IMAGE_HEIGHT, (int)height); + if (ret != MM_ERROR_NONE) + heif_error("mm_attrs_set_int_by_name fail %d: %s", ret, HEIF_IMAGE_HEIGHT); + } else { + heif_warning("heif_itemtable_get_image_resolution fail %d", ret); + } + + ret = heif_itemtable_get_image_orientation(primary_item, &orientation); + if (ret == LIBHEIF_ERROR_NONE) { + ret = mm_attrs_set_int_by_name(attrs, HEIF_IMAGE_ORIENTATION, (int)orientation); + if (ret != MM_ERROR_NONE) + heif_error("mm_attrs_set_int_by_name fail %d: %s", ret, HEIF_IMAGE_ORIENTATION); + } else { + heif_warning("heif_itemtable_get_image_orientation fail %d", ret); + } + + ret = heif_itemtable_get_image_mirror_mode(primary_item, &mirror_mode); + if (ret == LIBHEIF_ERROR_NONE) { + ret = mm_attrs_set_int_by_name(attrs, HEIF_IMAGE_MIRROR, (int)mirror_mode); + if (ret != MM_ERROR_NONE) + heif_error("mm_attrs_set_int_by_name fail %d: %s", ret, HEIF_IMAGE_MIRROR); + } else { + heif_warning("heif_itemtable_get_image_orientation fail %d", ret); + } + + /* Set extra information of primary image into MM_ATTRS */ + ret = heif_itemtable_get_exif(item_table, primary_item, &exif, &exif_size); + if (ret == LIBHEIF_ERROR_NONE) { + ret = mm_attrs_set_data_by_name(attrs, HEIF_IMAGE_EXIF, exif, exif_size); + if (ret != MM_ERROR_NONE) + heif_error("mm_attrs_set_data_by_name fail %d: %s", ret, HEIF_IMAGE_EXIF); + } else { + heif_warning("heif_itemtable_get_exif fail %d", ret); + } + + ret = heif_itemtable_get_icc_profile(item_table, primary_item, &icc_profile, &icc_profile_size); + if (ret == LIBHEIF_ERROR_NONE) { + ret = mm_attrs_set_data_by_name(attrs, HEIF_IMAGE_ICC_PROFILE, icc_profile, icc_profile_size); + if (ret != MM_ERROR_NONE) + heif_error("mm_attrs_set_data_by_name fail %d: %s", ret, HEIF_IMAGE_ICC_PROFILE); + } else { + heif_warning("heif_itemtable_get_icc_profile fail %d", ret); + } + + /* Commit attrs */ + ret = mm_attrs_commit_all(attrs); +END: + heif_itemtable_destroy(item_table); + + return ret; +} diff --git a/src/heif_box_idat.c b/src/heif_box_idat.c new file mode 100644 index 0000000..3ac04d3 --- /dev/null +++ b/src/heif_box_idat.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "heif_type.h" +#include "heif_debug.h" +#include "heif_box_idat.h" + +typedef struct { + off_t idat_offset; + size_t idat_size; +} idat_box_t; + + +int heif_box_idat_create(heif_box_idat_h *idat_box) +{ + idat_box_t *_idat_box = NULL; + + heif_retvm_if_failed(idat_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid idat_box"); + + _idat_box = g_new0(idat_box_t, 1); + + *idat_box = (heif_box_idat_h)_idat_box; + + return LIBHEIF_ERROR_NONE; +} + +void heif_box_idat_destroy(heif_box_idat_h idat_box) +{ + heif_retm_if_failed(idat_box); + + g_free(idat_box); +} + +int heif_box_idat_get_idat(heif_box_idat_h idat_box, off_t *offset, size_t *size) +{ + idat_box_t *_idat_box = (idat_box_t *)idat_box; + + heif_retvm_if_failed(idat_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid idat_box"); + heif_retvm_if_failed(offset, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid offset"); + heif_retvm_if_failed(size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size"); + + // only remember the offset and size of idat box for later use + *offset = _idat_box->idat_offset; + *size = _idat_box->idat_size; + + return LIBHEIF_ERROR_NONE; +} + +int heif_box_idat_parse(heif_box_idat_h idat_box, off_t offset, size_t size) +{ + idat_box_t *_idat_box = (idat_box_t *)idat_box; + + heif_retvm_if_failed(idat_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid idat_box"); + + // only remember the offset and size of idat box for later use + _idat_box->idat_offset = offset; + _idat_box->idat_size = size; + + return LIBHEIF_ERROR_NONE; +} + +void heif_box_idat_info(heif_box_idat_h idat_box) +{ + idat_box_t *_idat_box = (idat_box_t *)idat_box; + + heif_retm_if_failed(idat_box); + + heif_info("============ [IDAT] Start ==========="); + heif_info("[IDAT] idatOffset: %jd", _idat_box->idat_offset); + heif_info("[IDAT] idatSize: %zu", _idat_box->idat_size); + heif_info("============ [IDAT] End ===========\n"); +} diff --git a/src/heif_box_idat.h b/src/heif_box_idat.h new file mode 100644 index 0000000..7b4e075 --- /dev/null +++ b/src/heif_box_idat.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __HEIF_BOX_IDAT_H__ +#define __HEIF_BOX_IDAT_H__ + +#include + +typedef void * heif_box_idat_h; + +int heif_box_idat_create(heif_box_idat_h *idat_box); +void heif_box_idat_destroy(heif_box_idat_h idat_box); +int heif_box_idat_get_idat(heif_box_idat_h idat_box, off_t *offset, size_t *size); +int heif_box_idat_parse(heif_box_idat_h idat_box, off_t offset, size_t size); +void heif_box_idat_info(heif_box_idat_h idat_box); + +#endif /* __HEIF_BOX_IDAT_H__ */ diff --git a/src/heif_box_iinf.c b/src/heif_box_iinf.c new file mode 100644 index 0000000..25a4e78 --- /dev/null +++ b/src/heif_box_iinf.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "heif_type.h" +#include "heif_debug.h" +#include "heif_box_util.h" +#include "heif_box_iinf.h" + +typedef struct { + _full_box_t full_box; + GSList *item_infos; +} iinf_box_t; + +static int __parse_infe_box(_item_info_t *item_info, heif_source_h source, off_t offset, size_t size); +static int __iinf_chunk_data_cb(heif_source_h source, uint32_t type, off_t offset, size_t size, void *user_data); +static gint __compare_item_type(gconstpointer data, gconstpointer user_data); +static gpointer __copy_item_info(gconstpointer src, gpointer user_data); + + +int heif_box_iinf_create(heif_box_iinf_h *iinf_box) +{ + iinf_box_t *_iinf_box = NULL; + + heif_retvm_if_failed(iinf_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iinf_box"); + + _iinf_box = g_new0(iinf_box_t, 1); + _iinf_box->item_infos = NULL; + + *iinf_box = (heif_box_iinf_h)_iinf_box; + + return LIBHEIF_ERROR_NONE; +} + +void heif_box_iinf_destroy(heif_box_iinf_h iinf_box) +{ + iinf_box_t *_iinf_box = (iinf_box_t *)iinf_box; + + heif_retm_if_failed(iinf_box); + + g_slist_free_full(_iinf_box->item_infos, g_free); + + g_free(iinf_box); +} + +bool heif_box_iinf_has_type(heif_box_iinf_h iinf_box, uint32_t type) +{ + heif_retvm_if_failed(iinf_box, false, "invalid iinf_box"); + + return g_slist_find_custom(((iinf_box_t *)iinf_box)->item_infos, GUINT_TO_POINTER(type), __compare_item_type) ? true : false; +} + +int heif_box_iinf_get_item_infos(heif_box_iinf_h iinf_box, GSList **item_infos) +{ + iinf_box_t *_iinf_box = (iinf_box_t *)iinf_box; + + heif_retvm_if_failed(iinf_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iinf_box"); + heif_retvm_if_failed(item_infos, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_infos"); + + *item_infos = g_slist_copy_deep(_iinf_box->item_infos, __copy_item_info, NULL); + + return LIBHEIF_ERROR_NONE; +} + +int heif_box_iinf_parse(heif_box_iinf_h iinf_box, heif_source_h source, off_t offset, size_t size) +{ + int ret = LIBHEIF_ERROR_NONE; + iinf_box_t *_iinf_box = (iinf_box_t *)iinf_box; + size_t entry_count_size = 0; + uint32_t entry_count = 0; + off_t stop_offset = 0; + size_t i = 0; + + heif_retvm_if_failed(iinf_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iinf_box"); + + ret = _parse_full_box_header(&(_iinf_box->full_box), source, &offset, &size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("_parse_full_box_header fail %d", ret); + return ret; + } + + entry_count_size = _iinf_box->full_box.version == 0 ? 2 : 4; + if (size < entry_count_size) { + heif_error("invalid entry_count_size %zu", entry_count_size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + if (!heif_source_get_uint32var(source, offset, &entry_count, entry_count_size)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + +#ifdef __ENABLE_DEBUG_MODE + heif_info("entry_count %u", entry_count); +#endif + + stop_offset = offset + size; + offset += entry_count_size; + for (i = 0; i < entry_count && offset < stop_offset; i++) { +#ifdef __ENABLE_DEBUG_MODE + heif_info("entry %zu", i); +#endif + ret = _parse_box_chunk(source, &offset, (_parse_chunk_cb)__iinf_chunk_data_cb, _iinf_box); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("_parse_box_chunk fail %d", ret); + return ret; + } + } + + if (offset != stop_offset) { + heif_error("offset[%jd] did not match stop_offset[%jd]", offset, stop_offset); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + return LIBHEIF_ERROR_NONE; +} + +static void __print_item_infos(gpointer data, gpointer user_data) +{ + _item_info_t *item_info = (_item_info_t *)data; + unsigned int *idx = (unsigned int *)user_data; + + heif_retm_if_failed(data); + heif_retm_if_failed(user_data); + + heif_info("============ [ITEM INFO (%u)] ===========", *idx); + heif_info("[INFE] item_id: %u", item_info->item_id); + heif_info("[INFE] item_type: %u", item_info->item_type); + DEBUG_CHUNK_TYPE("INFE", item_info->item_type); // print string of item_type + heif_info("[INFE] hidden: %d", item_info->hidden); + heif_info("============ [ITEM INFO] End ==========="); + + (*idx)++; +} + +void heif_box_iinf_info(heif_box_iinf_h iinf_box) +{ + iinf_box_t *_iinf_box = (iinf_box_t *)iinf_box; + unsigned int idx = 0; + + heif_retm_if_failed(iinf_box); + + heif_info("============ [IINF] Start ==========="); + heif_info("[IINF] INFE count: %u", g_slist_length(_iinf_box->item_infos)); + g_slist_foreach(_iinf_box->item_infos, __print_item_infos, &idx); + heif_info("============ [IINF] End ===========\n"); +} + +static int __parse_infe_box(_item_info_t *item_info, heif_source_h source, off_t offset, size_t size) +{ + int ret = LIBHEIF_ERROR_NONE; + _full_box_t full_box = { 0, }; + uint32_t item_id = 0; + size_t item_id_size = 0; + uint16_t item_protection_index = 0; + uint32_t item_type = 0; + + heif_retvm_if_failed(item_info, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_info"); + + ret = _parse_full_box_header(&full_box, source, &offset, &size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("_parse_full_box_header fail %d", ret); + return ret; + } + + if (full_box.version == 0 || full_box.version == 1) { + heif_error("version[%u] must be 2 or more", full_box.version); + return LIBHEIF_ERROR_NOT_SUPPORTED; + } + + item_id_size = (full_box.version == 2) ? 2 : 4; + if (size < item_id_size + 6) { + heif_error("size[%zu] is smaller than item_id_size[%zu]", size, item_id_size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + if (!heif_source_get_uint32var(source, offset, &item_id, item_id_size)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + +#ifdef __ENABLE_DEBUG_MODE + heif_info("item_id %u", item_id); +#endif + offset += item_id_size; + + if (!heif_source_get_uint16(source, offset, &item_protection_index)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + +#ifdef __ENABLE_DEBUG_MODE + heif_info("item_protection_index %u", item_protection_index); +#endif + offset += sizeof(item_protection_index); + + if (!heif_source_get_uint32(source, offset, &item_type)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + item_info->item_id = item_id; + item_info->item_type = item_type; + // According to HEIF spec, (flags & 1) indicates the image is hidden + // and not supposed to be displayed. + item_info->hidden = (full_box.flags & 1); + + DEBUG_CHUNK_TYPE("item_type", item_type); + offset += sizeof(item_type); + size -= item_id_size + 6; + + _skip_string(source, &offset, &size, "item_name"); + + if (item_type == FOURCC('m', 'i', 'm', 'e')) { + _skip_string(source, &offset, &size, "content_type"); + + // content_encoding is optional; can be omitted if would be empty + if (size > 0) + _skip_string(source, &offset, &size, "content_encoding"); + } else if (item_type == FOURCC('u', 'r', 'i', ' ')) { + _skip_string(source, &offset, &size, "item_uri_type"); + } + + return LIBHEIF_ERROR_NONE; +} + +static int __iinf_chunk_data_cb(heif_source_h source, uint32_t type, off_t offset, size_t size, void *user_data) +{ + int ret = LIBHEIF_ERROR_NONE; + iinf_box_t *iinf_box = (iinf_box_t *)user_data; + _item_info_t *item_info; + + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); + heif_retvm_if_failed(user_data, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid user_data"); + heif_retvm_if_failed(type == FOURCC('i', 'n', 'f', 'e'), LIBHEIF_ERROR_INVALID_PARAMETER, "invalid type %u", type); + + item_info = g_new0(_item_info_t, 1); + ret = __parse_infe_box(item_info, source, offset, size); + if (ret != LIBHEIF_ERROR_NONE) { + g_free(item_info); + heif_error("__parse_infe_box fail %d", ret); + return (ret == LIBHEIF_ERROR_NOT_SUPPORTED) ? LIBHEIF_ERROR_NONE : ret; + } + + iinf_box->item_infos = g_slist_append(iinf_box->item_infos, item_info); + + return LIBHEIF_ERROR_NONE; +} + +static gint __compare_item_type(gconstpointer data, gconstpointer user_data) +{ + _item_info_t *item_info = (_item_info_t *)data; + uint32_t get_type = (uint32_t)GPOINTER_TO_UINT(user_data); + + if (!item_info) + return -1; + + return (item_info->item_type - get_type); +} + +static gpointer __copy_item_info(gconstpointer src, gpointer user_data) +{ + _item_info_t *src_item = (_item_info_t *)src; + _item_info_t *copy_item = NULL; + + heif_retvm_if_failed(src, NULL, "invalid src"); + + copy_item = g_new0(_item_info_t, 1); + copy_item->item_id = src_item->item_id; + copy_item->item_type = src_item->item_type; + copy_item->hidden = src_item->hidden; + + return copy_item; +} diff --git a/src/heif_box_iinf.h b/src/heif_box_iinf.h new file mode 100644 index 0000000..eff910f --- /dev/null +++ b/src/heif_box_iinf.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __HEIF_BOX_IINF_H__ +#define __HEIF_BOX_IINF_H__ + +#include +#include "heif_source.h" + +typedef void * heif_box_iinf_h; + +int heif_box_iinf_create(heif_box_iinf_h *iinf_box); +void heif_box_iinf_destroy(heif_box_iinf_h iinf_box); +bool heif_box_iinf_has_type(heif_box_iinf_h iinf_box, uint32_t type); +int heif_box_iinf_get_item_infos(heif_box_iinf_h iinf_box, GSList **item_infos); +int heif_box_iinf_parse(heif_box_iinf_h iinf_box, heif_source_h source, off_t offset, size_t size); +void heif_box_iinf_info(heif_box_iinf_h iinf_box); + +#endif /* __HEIF_BOX_IINF_H__ */ diff --git a/src/heif_box_iloc.c b/src/heif_box_iloc.c new file mode 100644 index 0000000..f836e7f --- /dev/null +++ b/src/heif_box_iloc.c @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "heif_type.h" +#include "heif_debug.h" +#include "heif_box_util.h" +#include "heif_box_iloc.h" + +typedef struct { + _full_box_t full_box; + unsigned int item_count; + bool has_construction_method; + GSList *item_locs; +} iloc_box_t; + + +static bool __is_valid_sizefield(uint32_t size); +static gpointer __dup_extent(gconstpointer src, gpointer user_data); +static gpointer __dup_item_loc(gconstpointer src, gpointer user_data); + + +int heif_box_iloc_create(heif_box_iloc_h *iloc_box) +{ + iloc_box_t *_iloc_box = NULL; + + heif_retvm_if_failed(iloc_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iloc_box"); + + _iloc_box = g_new0(iloc_box_t, 1); + + *iloc_box = (heif_box_iloc_h)_iloc_box; + + return LIBHEIF_ERROR_NONE; +} + +void heif_box_iloc_destroy(heif_box_iloc_h iloc_box) +{ + heif_retm_if_failed(iloc_box); + + g_slist_free_full(((iloc_box_t *)iloc_box)->item_locs, _free_item_loc); + + g_free(iloc_box); +} + +bool heif_box_iloc_has_construction_method(heif_box_iloc_h iloc_box) +{ + heif_retvm_if_failed(iloc_box, false, "invalid iloc_box"); + + return ((iloc_box_t *)iloc_box)->has_construction_method; +} + +int heif_box_iloc_get_item_locs(heif_box_iloc_h iloc_box, GSList **item_locs) +{ + iloc_box_t *_iloc_box = (iloc_box_t *)iloc_box; + + heif_retvm_if_failed(iloc_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iloc_box"); + heif_retvm_if_failed(item_locs, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_locs"); + + *item_locs = g_slist_copy_deep(_iloc_box->item_locs, __dup_item_loc, NULL); + + return LIBHEIF_ERROR_NONE; +} + +int heif_box_iloc_parse(heif_box_iloc_h iloc_box, heif_source_h source, off_t offset, size_t size) +{ + int ret = LIBHEIF_ERROR_NONE; + iloc_box_t *_iloc_box = (iloc_box_t *)iloc_box; + _full_box_t *full_box = NULL; + uint8_t offset_size = 0; + uint8_t length_size = 0; + uint8_t base_offset_size = 0; + uint8_t index_size = 0; + uint32_t item_count = 0; + uint8_t item_field_size = 0; + uint32_t i = 0; + + heif_retvm_if_failed(iloc_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iloc_box"); + + full_box = &(_iloc_box->full_box); + + ret = _parse_full_box_header(full_box, source, &offset, &size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("_parse_full_box_header fail %d", ret); + return ret; + } + + if (full_box->version > 2) { + heif_error("invalid version %u", full_box->version); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + if (size < 2) { + heif_error("size[%zu] is too small", size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + if (heif_source_read_at(source, offset++, &offset_size, 1) != 1) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + length_size = (offset_size & 0xF); + offset_size >>= 4; + + if (heif_source_read_at(source, offset++, &base_offset_size, 1) != 1) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + if (full_box->version == 1 || full_box->version == 2) + index_size = (base_offset_size & 0xF); + + base_offset_size >>= 4; + size -= 2; + + if (!__is_valid_sizefield(offset_size) + || !__is_valid_sizefield(length_size) + || !__is_valid_sizefield(base_offset_size) + || !__is_valid_sizefield(index_size)) { + heif_error("loc offset/size not valid: %u, %u, %u, %u", + offset_size, length_size, base_offset_size, index_size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + item_field_size = full_box->version < 2 ? 2 : 4; + if (size < item_field_size) { + heif_error("size[%zu] is smaller than item_field_size[%u]", size, item_field_size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + if (!heif_source_get_uint32var(source, offset, &item_count, (size_t)item_field_size)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + _iloc_box->item_count = (unsigned int)item_count; +#ifdef __ENABLE_DEBUG_MODE + heif_info("item_count %u", _iloc_box->item_count); +#endif + offset += item_field_size; + size -= item_field_size; + + for (i = 0; i < item_count; i++) { + uint32_t item_id = 0; + uint8_t construction_method = 0; + uint16_t data_reference_index = 0; + uint64_t base_offset = 0; + _item_loc_t *item_loc = NULL; + uint16_t extent_count = 0; + + if (!heif_source_get_uint32var(source, offset, &item_id, (size_t)item_field_size)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + +#ifdef __ENABLE_DEBUG_MODE + heif_info("item[%u]: id %u", i, item_id); +#endif + offset += item_field_size; + + if (full_box->version == 1 || full_box->version == 2) { + uint8_t buf[2]; + if (heif_source_read_at(source, offset, buf, 2) != 2) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + construction_method = (buf[1] & 0xF); +#ifdef __ENABLE_DEBUG_MODE + heif_info("construction_method %u", construction_method); +#endif + if (construction_method == 1) + _iloc_box->has_construction_method = true; + + offset += 2; + } + + if (!heif_source_get_uint16(source, offset, &data_reference_index)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + if (data_reference_index != 0) { + // we don't support reference to other files + heif_error("invalid data_reference_index[%u]", data_reference_index); + return LIBHEIF_ERROR_INVALID_OPERATION; + } + offset += 2; + + if (base_offset_size != 0) { + if (!heif_source_get_uint64var(source, offset, &base_offset, base_offset_size)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + offset += base_offset_size; + } +#ifdef __ENABLE_DEBUG_MODE + heif_info("base_offset %"PRIu64, base_offset); +#endif + + item_loc = g_new0(_item_loc_t, 1); + item_loc->item_id = item_id; + item_loc->construction_method = construction_method; + item_loc->data_reference_index = data_reference_index; + item_loc->base_offset = base_offset; + + _iloc_box->item_locs = g_slist_append(_iloc_box->item_locs, item_loc); + + if (!heif_source_get_uint16(source, offset, &extent_count)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + +#ifdef __ENABLE_DEBUG_MODE + heif_info("extent_count %u", extent_count); +#endif + + if (extent_count > 1) { + heif_error("invalid extent_count %d", extent_count); + return LIBHEIF_ERROR_INVALID_OPERATION; + } + offset += 2; + + for (size_t j = 0; j < extent_count; j++) { + uint64_t extent_index = 1; // default=1 + uint64_t extent_offset = 0; // default=0 + uint64_t extent_length = 0; // this indicates full length of file + + if ((full_box->version == 1 || full_box->version == 2) && (index_size > 0)) { + if (!heif_source_get_uint64var(source, offset, &extent_index, index_size)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + // TODO: add support for this mode + offset += index_size; +#ifdef __ENABLE_DEBUG_MODE + heif_info("extent_index %"PRIu64, extent_index); +#endif + } + + if (offset_size > 0) { + if (!heif_source_get_uint64var(source, offset, &extent_offset, offset_size)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + offset += offset_size; + } +#ifdef __ENABLE_DEBUG_MODE + heif_info("extent_offset %"PRIu64, extent_offset); +#endif + + if (length_size > 0) { + if (!heif_source_get_uint64var(source, offset, &extent_length, length_size)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + offset += length_size; + } +#ifdef __ENABLE_DEBUG_MODE + heif_info("extent_length %"PRIu64, extent_length); +#endif + _extent_entry_t *extent_entry = g_new0(_extent_entry_t, 1); + extent_entry->extent_index = extent_index; + extent_entry->extent_offset = extent_offset; + extent_entry->extent_length = extent_length; + + item_loc->extents = g_slist_append(item_loc->extents, extent_entry); + } + } + + return LIBHEIF_ERROR_NONE; +} + +static void __print_extent_entry(gpointer data, gpointer user_data) +{ + _extent_entry_t *extent_entry = (_extent_entry_t *)data; + + heif_retm_if_failed(data); + + heif_info("\t [EXTENT_ENTRY] extent_index: %"PRIu64, extent_entry->extent_index); + heif_info("\t [EXTENT_ENTRY] extent_offset: %"PRIu64, extent_entry->extent_offset); + heif_info("\t [EXTENT_ENTRY] extent_length: %"PRIu64, extent_entry->extent_length); +} + +static void __print_item_loc(gpointer data, gpointer user_data) +{ + _item_loc_t *item_loc = (_item_loc_t *)data; + + heif_retm_if_failed(data); + + heif_info("[ILOC_ITEM] item_id: %u", item_loc->item_id); + heif_info("[ILOC_ITEM] construction_method: %u", item_loc->construction_method); + heif_info("[ILOC_ITEM] data_reference_index: %u", item_loc->data_reference_index); + heif_info("[ILOC_ITEM] base_offset: %jd", item_loc->base_offset); + g_slist_foreach(item_loc->extents, __print_extent_entry, NULL); +} + +void heif_box_iloc_info(heif_box_iloc_h iloc_box) +{ + iloc_box_t *_iloc_box = (iloc_box_t *)iloc_box; + + heif_retm_if_failed(iloc_box); + + heif_info("============ [ILOC] Start ==========="); + DEBUG_FULL_BOX(&_iloc_box->full_box, "ILOC"); + heif_info("[ILOC] itemCount: %u", _iloc_box->item_count); + heif_info("[ILOC] hasConstructionMethod: %d", _iloc_box->has_construction_method); + g_slist_foreach(_iloc_box->item_locs, __print_item_loc, NULL); + heif_info("============ [ILOC] End ===========\n"); +} + +static bool __is_valid_sizefield(uint32_t size) +{ + return size == 0 || size == 4 || size == 8; +} + +static gpointer __dup_extent(gconstpointer src, gpointer user_data) +{ + _extent_entry_t *src_ext = (_extent_entry_t *)src; + _extent_entry_t *copy_ext = NULL; + + heif_retvm_if_failed(src, NULL, "invalid src"); + + copy_ext = g_new0(_extent_entry_t, 1); + copy_ext->extent_index = src_ext->extent_index; + copy_ext->extent_offset = src_ext->extent_offset; + copy_ext->extent_length = src_ext->extent_length; + + return copy_ext; +} + +static gpointer __dup_item_loc(gconstpointer src, gpointer user_data) +{ + _item_loc_t *src_iloc = (_item_loc_t *)src; + _item_loc_t *copy_iloc = NULL; + + heif_retvm_if_failed(src, NULL, "invalid src"); + + copy_iloc = g_new0(_item_loc_t, 1); + copy_iloc->item_id = src_iloc->item_id; + copy_iloc->construction_method = src_iloc->construction_method; + copy_iloc->data_reference_index = src_iloc->data_reference_index; + copy_iloc->base_offset = src_iloc->base_offset; + copy_iloc->extents = g_slist_copy_deep(src_iloc->extents, __dup_extent, NULL); + + return copy_iloc; +} diff --git a/src/heif_box_iloc.h b/src/heif_box_iloc.h new file mode 100644 index 0000000..2a1a550 --- /dev/null +++ b/src/heif_box_iloc.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __HEIF_BOX_ILOC_H__ +#define __HEIF_BOX_ILOC_H__ + +#include + +#include "heif_source.h" + +typedef void * heif_box_iloc_h; + +int heif_box_iloc_create(heif_box_iloc_h *iloc_box); +void heif_box_iloc_destroy(heif_box_iloc_h iloc_box); +bool heif_box_iloc_has_construction_method(heif_box_iloc_h iloc_box); +int heif_box_iloc_get_item_locs(heif_box_iloc_h iloc_box, GSList **item_locs); +int heif_box_iloc_parse(heif_box_iloc_h iloc_box, heif_source_h source, off_t offset, size_t size); +void heif_box_iloc_info(heif_box_iloc_h iloc_box); + +#endif /* __HEIF_BOX_ILOC_H__ */ diff --git a/src/heif_box_iprp.c b/src/heif_box_iprp.c new file mode 100644 index 0000000..8f59825 --- /dev/null +++ b/src/heif_box_iprp.c @@ -0,0 +1,699 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "heif_type.h" +#include "heif_debug.h" +#include "heif_box_util.h" +#include "heif_box_iprp.h" + +typedef struct { + GSList *properties; +} ipco_box_t; + +typedef struct { + _full_box_t full_box; + GSList *associations; +} ipma_box_t; + +typedef struct { + ipco_box_t *ipco_box; + ipma_box_t *ipma_box; +} iprp_box_t; + +static gpointer __copy_properties(gconstpointer src, gpointer data); +static gpointer __copy_associations(gconstpointer src, gpointer data); + +static void __ipco_box_info(ipco_box_t *ipco_box); +static int __ipco_box_create(ipco_box_t **ipco_box); +static void __ipco_box_destroy(ipco_box_t *ipco_box); +static int __ipco_box_parse(ipco_box_t *ipco_box, heif_source_h source, off_t offset, size_t size); + +static void __ipma_box_info(ipma_box_t *ipma_box); +static int __ipma_box_create(ipma_box_t **ipma_box); +static void __ipma_box_destroy(ipma_box_t *ipma_box); +static int __ipma_box_parse(ipma_box_t *ipma_box, heif_source_h source, off_t offset, size_t size); + +static int __iprp_chunk_data_cb(heif_source_h source, uint32_t type, off_t offset, size_t size, void *user_data) +{ + int ret = LIBHEIF_ERROR_NONE; + iprp_box_t *_iprp_box = (iprp_box_t *)user_data; + + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); + heif_retvm_if_failed(user_data, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid user_data"); + + heif_info("offset %jd, size %zu", offset, size); + + switch (type) { + case FOURCC('i', 'p', 'c', 'o'): + if (!_iprp_box->ipco_box) { + ret = __ipco_box_create(&_iprp_box->ipco_box); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__ipco_box_create fail (%d)", ret); + return ret; + } + } + return __ipco_box_parse(_iprp_box->ipco_box, source, offset, size); + + case FOURCC('i', 'p', 'm', 'a'): + if (!_iprp_box->ipma_box) { + ret = __ipma_box_create(&_iprp_box->ipma_box); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__ipco_box_create fail (%d)", ret); + return ret; + } + } + return __ipma_box_parse(_iprp_box->ipma_box, source, offset, size); + + default: + DEBUG_CHUNK_TYPE("Unrecognized", type); + break; + } + return LIBHEIF_ERROR_NONE; +} + +void heif_box_iprp_info(heif_box_iprp_h iprp_box) +{ + iprp_box_t *_iprp_box = (iprp_box_t *)iprp_box; + + heif_retm_if_failed(iprp_box); + + heif_info("============ [IPRP] Start ==========="); + __ipco_box_info(_iprp_box->ipco_box); + __ipma_box_info(_iprp_box->ipma_box); + heif_info("============ [IPRP] End ==========="); +} + +int heif_box_iprp_create(heif_box_iprp_h *iprp_box) +{ + iprp_box_t *_iprp_box = NULL; + + heif_retvm_if_failed(iprp_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iprp_box"); + + _iprp_box = g_new0(iprp_box_t, 1); + + *iprp_box = (heif_box_iprp_h)_iprp_box; + + return LIBHEIF_ERROR_NONE; +} + +void heif_box_iprp_destroy(heif_box_iprp_h iprp_box) +{ + iprp_box_t *_iprp_box = (iprp_box_t *)iprp_box; + + heif_retm_if_failed(iprp_box); + + __ipco_box_destroy(_iprp_box->ipco_box); + __ipma_box_destroy(_iprp_box->ipma_box); + + g_free(iprp_box); +} + +int heif_box_iprp_get_properties(heif_box_iprp_h iprp_box, GSList **properties) +{ + iprp_box_t *_iprp_box = (iprp_box_t *)iprp_box; + + heif_retvm_if_failed(iprp_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iprp_box"); + heif_retvm_if_failed(properties, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid properties"); + + *properties = g_slist_copy_deep(_iprp_box->ipco_box->properties, __copy_properties, NULL); + + return LIBHEIF_ERROR_NONE; +} + +int heif_box_iprp_get_associations(heif_box_iprp_h iprp_box, GSList **associations) +{ + iprp_box_t *_iprp_box = (iprp_box_t *)iprp_box; + + heif_retvm_if_failed(iprp_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iprp_box"); + heif_retvm_if_failed(associations, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid associations"); + + *associations = g_slist_copy_deep(_iprp_box->ipma_box->associations, __copy_associations, NULL); + + return LIBHEIF_ERROR_NONE; +} + +int heif_box_iprp_parse(heif_box_iprp_h iprp_box, heif_source_h source, off_t offset, size_t size) +{ + iprp_box_t *_iprp_box = (iprp_box_t *)iprp_box; + + heif_retvm_if_failed(iprp_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iprp_box"); + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); + + return _parse_box_chunks(source, offset, size, (_parse_chunk_cb)__iprp_chunk_data_cb, _iprp_box); +} + +static gpointer __copy_properties(gconstpointer src, gpointer data) +{ + _item_prop_t *src_prop = (_item_prop_t *)src; + _item_prop_t *copy_prop = NULL; + + heif_retvm_if_failed(src, NULL, "invalid src"); + heif_retvm_if_failed(src_prop->property, NULL, "invalid src"); + + copy_prop = g_new0(_item_prop_t, 1); + copy_prop->type = src_prop->type; + + switch (src_prop->type) { + case FOURCC('h', 'v', 'c', 'C'): + { + _hvcC_prop_t *src_hvcC = (_hvcC_prop_t *)src_prop->property; + _hvcC_prop_t *copy_hvcC = g_new0(_hvcC_prop_t, 1); + + copy_hvcC->data = g_memdup(src_hvcC->data, src_hvcC->size); + copy_hvcC->size = src_hvcC->size; + copy_prop->property = copy_hvcC; + break; + } + + case FOURCC('i', 's', 'p', 'e'): + { + _ispe_prop_t *src_ispe = (_ispe_prop_t *)src_prop->property; + _ispe_prop_t *copy_ispe = g_new0(_ispe_prop_t, 1); + + copy_ispe->width = src_ispe->width; + copy_ispe->height = src_ispe->height; + copy_prop->property = copy_ispe; + break; + } + + case FOURCC('i', 'r', 'o', 't'): + { + _irot_prop_t *src_irot = (_irot_prop_t *)src_prop->property; + _irot_prop_t *copy_irot = g_new0(_irot_prop_t, 1); + + copy_irot->angle = src_irot->angle; + copy_prop->property = copy_irot; + break; + } + + case FOURCC('c', 'o', 'l', 'r'): + { + _colr_prop_t *src_colr = (_colr_prop_t *)src_prop->property; + _colr_prop_t *copy_colr = g_new0(_colr_prop_t, 1); + + copy_colr->icc_data = g_memdup(src_colr->icc_data, src_colr->size); + copy_colr->size = src_colr->size; + copy_prop->property = copy_colr; + break; + } + + case FOURCC('i', 'm', 'i', 'r'): + { + _imir_prop_t *src_imir = (_imir_prop_t *)src_prop->property; + _imir_prop_t *copy_imir = g_new0(_imir_prop_t, 1); + + copy_imir->axis = src_imir->axis; + copy_prop->property = copy_imir; + break; + } + + default: + // do nothing... + break; + } + + return copy_prop; +} + +static gpointer __copy_associations(gconstpointer src, gpointer data) +{ + _association_t *src_assoc = (_association_t *)src; + _association_t *copy_assoc = NULL; + + heif_retvm_if_failed(src, NULL, "invalid src"); + + copy_assoc = g_new0(_association_t, 1); + copy_assoc->item_id = src_assoc->item_id; + copy_assoc->essential = src_assoc->essential; + copy_assoc->index = src_assoc->index; + + return copy_assoc; +} + +void __print_hvcC_box_info(_hvcC_prop_t *hvcC_prop) +{ + heif_retm_if_failed(hvcC_prop); + + heif_info("============ [HVCC] Start ==========="); + heif_info("[HVCC] size: %zu", hvcC_prop->size); + heif_info("============ [HVCC] End ==========="); +} + +static int __parse_hvcC_box(_hvcC_prop_t *hvcC_prop, heif_source_h source, off_t offset, size_t size) +{ + heif_retvm_if_failed(hvcC_prop, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid hvcC_prop"); + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); + heif_retvm_if_failed(size >= 1, LIBHEIF_ERROR_NONE, "invalid size %zu", size); + + hvcC_prop->data = g_malloc0(size); + hvcC_prop->size = size; + if (heif_source_read_at(source, offset, hvcC_prop->data, size) < (ssize_t)size) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + heif_info("property hvcC: size %zu", hvcC_prop->size); + + return LIBHEIF_ERROR_NONE; +} + +void __print_ispe_box_info(_ispe_prop_t *ispe_prop) +{ + heif_retm_if_failed(ispe_prop); + + heif_info("============ [ISPE] Start ==========="); + DEBUG_FULL_BOX(&ispe_prop->full_box, "ISPE"); + heif_info("[ISPE] width: %u", ispe_prop->width); + heif_info("[ISPE] height: %u", ispe_prop->height); + heif_info("============ [ISPE] End ==========="); +} + +static int __parse_ispe_box(_ispe_prop_t *ispe_prop, heif_source_h source, off_t offset, size_t size) +{ + int ret = LIBHEIF_ERROR_NONE; + + heif_retvm_if_failed(ispe_prop, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid ispe_prop"); + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); + heif_retvm_if_failed(size >= 8, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size %zu", size); + + ret = _parse_full_box_header(&ispe_prop->full_box, source, &offset, &size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("_parse_full_box_header fail %d", ret); + return ret; + } + + if (!heif_source_get_uint32(source, offset, &ispe_prop->width) || + !heif_source_get_uint32(source, offset + 4, &ispe_prop->height)) { + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + // Validate that the dimension doesn't cause overflow on calculated max input size. + // Max input size is width*height*1.5, restrict width*height to 1<<29 so that + if (ispe_prop->width == 0 || ispe_prop->height == 0 || ispe_prop->width > (1 << 29) / ispe_prop->height) { + heif_error("invalid width[%u] & height[%u]", ispe_prop->width, ispe_prop->height); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + heif_info("property ispe: %ux%u", ispe_prop->width, ispe_prop->height); + + return LIBHEIF_ERROR_NONE; +} + +void __print_irot_box_info(_irot_prop_t *irot_prop) +{ + heif_retm_if_failed(irot_prop); + + heif_info("============ [IROT] Start ==========="); + heif_info("[IROT] angle: %u", irot_prop->angle); + heif_info("============ [IROT] End ==========="); +} + +static int __parse_irot_box(_irot_prop_t *irot_prop, heif_source_h source, off_t offset, size_t size) +{ + heif_retvm_if_failed(irot_prop, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid irot_prop"); + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); + heif_retvm_if_failed(size >= 1, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size %zu", size); + + if (heif_source_read_at(source, offset, &irot_prop->angle, 1) != 1) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + irot_prop->angle &= 0x3; + heif_info("property irot: %u", irot_prop->angle); + + return LIBHEIF_ERROR_NONE; +} + +void __print_colr_box_info(_colr_prop_t *colr_prop) +{ + heif_retm_if_failed(colr_prop); + + heif_info("============ [COLR] Start ==========="); + heif_info("[COLR] size: %zu", colr_prop->size); + heif_info("============ [COLR] End ==========="); +} + +static int __parse_colr_box(_colr_prop_t *colr_prop, heif_source_h source, off_t offset, size_t size) +{ + uint32_t colour_type; + + heif_retvm_if_failed(colr_prop, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid colr_prop"); + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); + heif_retvm_if_failed(size >= 4, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size %zu", size); + + if (!heif_source_get_uint32(source, offset, &colour_type)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + offset += 4; + size -= 4; + + if (colour_type == FOURCC('n', 'c', 'l', 'x')) { + DEBUG_CHUNK_TYPE("Not supported", colour_type); + return LIBHEIF_ERROR_NONE; + } + + if ((colour_type != FOURCC('r', 'I', 'C', 'C')) && + (colour_type != FOURCC('p', 'r', 'o', 'f'))) { + DEBUG_CHUNK_TYPE("invalid colour type", colour_type); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + colr_prop->icc_data = g_malloc0(size); + colr_prop->size = size; + + if (heif_source_read_at(source, offset, colr_prop->icc_data, size) != (ssize_t)size) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + heif_info("property Colr: size %zd", colr_prop->size); + + return LIBHEIF_ERROR_NONE; +} + +void __print_imir_box_info(_imir_prop_t *imir_prop) +{ + heif_retm_if_failed(imir_prop); + + heif_info("============ [IMIR] Start ==========="); + heif_info("[IMIR] axis: %u", imir_prop->axis); + heif_info("============ [IMIR] End ==========="); +} + +static int __parse_imir_box(_imir_prop_t *imir_prop, heif_source_h source, off_t offset, size_t size) +{ + uint8_t axis = 0; + + heif_retvm_if_failed(imir_prop, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid imir_prop"); + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); + heif_retvm_if_failed(size >= 1, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size %zu", size); + + if (!heif_source_read_at(source, offset, &axis, 1)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + imir_prop->axis = axis; + heif_info("property mirror(axis): %u", axis); + + return LIBHEIF_ERROR_NONE; +} + +static void __print_image_prop(gpointer data, gpointer user_data) +{ + _item_prop_t *image_prop = (_item_prop_t *)data; + unsigned int *idx = (unsigned int *)user_data; + + heif_retm_if_failed(data); + heif_retm_if_failed(user_data); + + heif_info("============ [IMAGE PROP (%u)] ===========", *idx); + switch (image_prop->type) { + case FOURCC('h', 'v', 'c', 'C'): + __print_hvcC_box_info((_hvcC_prop_t *)image_prop->property); + break; + + case FOURCC('i', 's', 'p', 'e'): + __print_ispe_box_info((_ispe_prop_t *)image_prop->property); + break; + + case FOURCC('i', 'r', 'o', 't'): + __print_irot_box_info((_irot_prop_t *)image_prop->property); + break; + + case FOURCC('c', 'o', 'l', 'r'): + __print_colr_box_info((_colr_prop_t *)image_prop->property); + break; + + case FOURCC('i', 'm', 'i', 'r'): + __print_imir_box_info((_imir_prop_t *)image_prop->property); + break; + + default: + DEBUG_CHUNK_TYPE("Not supported", image_prop->type); + break; + } + + (*idx)++; +} + +static int __ipco_chunk_data_cb(heif_source_h source, uint32_t type, off_t offset, size_t size, void *user_data) +{ + int ret = LIBHEIF_ERROR_NONE; + _item_prop_t *image_prop; + GSList **properties = (GSList **)user_data; + + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); + heif_retvm_if_failed(user_data, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid user_data"); + + heif_info("offset %jd, size %zu", offset, size); + + image_prop = g_new0(_item_prop_t, 1); + image_prop->type = type; + + switch (type) { + case FOURCC('h', 'v', 'c', 'C'): + image_prop->property = g_new0(_hvcC_prop_t, 1); + ret = __parse_hvcC_box(image_prop->property, source, offset, size); + break; + + case FOURCC('i', 's', 'p', 'e'): + image_prop->property = g_new0(_ispe_prop_t, 1); + ret = __parse_ispe_box(image_prop->property, source, offset, size); + break; + + case FOURCC('i', 'r', 'o', 't'): + image_prop->property = g_new0(_irot_prop_t, 1); + ret = __parse_irot_box(image_prop->property, source, offset, size); + break; + + case FOURCC('c', 'o', 'l', 'r'): + image_prop->property = g_new0(_colr_prop_t, 1); + ret = __parse_colr_box(image_prop->property, source, offset, size); + break; + + case FOURCC('i', 'm', 'i', 'r'): + image_prop->property = g_new0(_imir_prop_t, 1); + ret = __parse_imir_box(image_prop->property, source, offset, size); + break; + + default: + DEBUG_CHUNK_TYPE("Not supported", type); + g_free(image_prop); + return LIBHEIF_ERROR_NONE; + } + + if (ret != LIBHEIF_ERROR_NONE) { + DEBUG_CHUNK_TYPE("Invalid property", type); + _free_image_prop(image_prop); + return ret; + } + + *properties = g_slist_append(*properties, image_prop); + + return LIBHEIF_ERROR_NONE; +} + +static void __ipco_box_info(ipco_box_t *ipco_box) +{ + ipco_box_t *_ipco_box = (ipco_box_t *)ipco_box; + unsigned int idx = 1; + + heif_retm_if_failed(ipco_box); + + heif_info("============ [IPCO] Start ==========="); + heif_info("[IPCO] image prop: %u", g_slist_length(_ipco_box->properties)); + g_slist_foreach(_ipco_box->properties, __print_image_prop, &idx); + heif_info("============ [IMAGE PROP] End ==========="); + heif_info("============ [IPCO] End ===========\n"); +} + +static int __ipco_box_create(ipco_box_t **ipco_box) +{ + ipco_box_t *_ipco_box; + + heif_retvm_if_failed(ipco_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid ipco_box"); + + _ipco_box = g_new0(ipco_box_t, 1); + _ipco_box->properties = NULL; + + *ipco_box = (ipco_box_t *)_ipco_box; + + return LIBHEIF_ERROR_NONE; +} + +static void __ipco_box_destroy(ipco_box_t *ipco_box) +{ + ipco_box_t *_ipco_box = (ipco_box_t *)ipco_box; + + heif_retm_if_failed(ipco_box); + + g_slist_free_full(_ipco_box->properties, _free_image_prop); + + g_free(ipco_box); +} + +static int __ipco_box_parse(ipco_box_t *ipco_box, heif_source_h source, off_t offset, size_t size) +{ + ipco_box_t *_ipco_box = (ipco_box_t *)ipco_box; + + heif_retvm_if_failed(ipco_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid ipco_box"); + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); + + // push a placeholder as the index is 1-based + return _parse_box_chunks(source, offset, size, (_parse_chunk_cb)__ipco_chunk_data_cb, &_ipco_box->properties); +} + +static void __print_association(gpointer data, gpointer user_data) +{ + _association_t *association = (_association_t *)data; + unsigned int *idx = (unsigned int *)user_data; + + heif_retm_if_failed(data); + + heif_info("============ [ASSOCIATION (%u)] ===========", *idx); + heif_info("[ASSOCIATION] item id %u", association->item_id); + heif_info("[ASSOCIATION] associated to property %u", association->index); + heif_info("[ASSOCIATION] essential %d", association->essential); + + (*idx)++; +} + +static void __ipma_box_info(ipma_box_t *ipma_box) +{ + ipma_box_t *_ipma_box = (ipma_box_t *)ipma_box; + unsigned int idx = 1; + + heif_retm_if_failed(ipma_box); + + heif_info("============ [IPMA] Start ==========="); + heif_info("[IPMA] associations: %u", g_slist_length(_ipma_box->associations)); + g_slist_foreach(_ipma_box->associations, __print_association, &idx); + heif_info("============ [ASSOCIATION] End ==========="); + heif_info("============ [IPMA] End ===========\n"); +} + +static int __ipma_box_create(ipma_box_t **ipma_box) +{ + ipma_box_t *_ipma_box; + + heif_retvm_if_failed(ipma_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid ipma_box"); + + _ipma_box = g_new0(ipma_box_t, 1); + _ipma_box->associations = NULL; + + *ipma_box = (ipma_box_t *)_ipma_box; + + return LIBHEIF_ERROR_NONE; +} + +static void __ipma_box_destroy(ipma_box_t *ipma_box) +{ + ipma_box_t *_ipma_box = (ipma_box_t *)ipma_box; + + heif_retm_if_failed(ipma_box); + + g_slist_free_full(_ipma_box->associations, g_free); + + g_free(ipma_box); +} + +static int __ipma_box_parse(ipma_box_t *ipma_box, heif_source_h source, off_t offset, size_t size) +{ + int ret = LIBHEIF_ERROR_NONE; + ipma_box_t *_ipma_box = (ipma_box_t *)ipma_box; + uint32_t entryCount = 0; + uint32_t item_id = 0; + size_t item_id_size = 0; + uint8_t associationCount = 0; + _association_t *assoct = NULL; + size_t propIndexSize = 0; + uint16_t propIndex = 0; + uint16_t bitmask = 0; + size_t k, i; + + heif_retvm_if_failed(ipma_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid ipma_box"); + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); + + ret = _parse_full_box_header(&(_ipma_box->full_box), source, &offset, &size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("_parse_full_box_header fail %d", ret); + return ret; + } + + if (size < 4) { + heif_error("size[%zu] is too small", size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + if (!heif_source_get_uint32(source, offset, &entryCount)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + offset += 4; + size -= 4; + + for (k = 0; k < entryCount; ++k) { + item_id_size = (_ipma_box->full_box.version < 1) ? 2 : 4; + + if (size < item_id_size + 1) { + heif_error("size[%zu] is smaller than item_id_size[%zu]", size, item_id_size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + if (!heif_source_get_uint32var(source, offset, &item_id, item_id_size)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + offset += item_id_size; + size -= item_id_size; + + if (heif_source_read_at(source, offset, &associationCount, 1) != 1) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + offset++; + size--; + + for (i = 0; i < associationCount; ++i) { + propIndexSize = (_ipma_box->full_box.flags & 1) ? 2 : 1; + + if (size < propIndexSize) { + heif_error("size[%zu] is smaller than propIndexSize[%zu]", size, propIndexSize); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + if (!heif_source_get_uint16var(source, offset, &propIndex, propIndexSize)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + offset += propIndexSize; + size -= propIndexSize; + bitmask = (1 << (8 * propIndexSize - 1)); + + assoct = g_new0(_association_t, 1); + assoct->item_id = item_id; + assoct->essential = !!(propIndex & bitmask); + assoct->index = (uint16_t) (propIndex & ~bitmask); + +#ifdef __ENABLE_DEBUG_MODE + heif_info("item id %u associated to property %u (essential %d)", + item_id, assoct->index, assoct->essential); +#endif + + _ipma_box->associations = g_slist_append(_ipma_box->associations, assoct); + } + } + + return LIBHEIF_ERROR_NONE; +} diff --git a/src/heif_box_iprp.h b/src/heif_box_iprp.h new file mode 100644 index 0000000..c6a48d9 --- /dev/null +++ b/src/heif_box_iprp.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __HEIF_BOX_IPRP_H__ +#define __HEIF_BOX_IPRP_H__ + +#include + +#include "heif_source.h" + +typedef void * heif_box_iprp_h; +typedef void * heif_ipco_box_h; +typedef void * heif_ipma_box_h; + +void heif_box_iprp_info(heif_box_iprp_h iprp_box); +int heif_box_iprp_create(heif_box_iprp_h *iprp_box); +void heif_box_iprp_destroy(heif_box_iprp_h iprp_box); +int heif_box_iprp_get_properties(heif_box_iprp_h iprp_box, GSList **properties); +int heif_box_iprp_get_associations(heif_box_iprp_h iprp_box, GSList **associations); +int heif_box_iprp_parse(heif_box_iprp_h iprp_box, heif_source_h source, off_t offset, size_t size); + +#endif /* __HEIF_BOX_IPRP_H__ */ diff --git a/src/heif_box_iref.c b/src/heif_box_iref.c new file mode 100644 index 0000000..8f1dfe3 --- /dev/null +++ b/src/heif_box_iref.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "heif_type.h" +#include "heif_debug.h" +#include "heif_box_util.h" +#include "heif_box_iref.h" + +typedef struct { + _full_box_t full_box; + uint32_t ref_id_size; + GSList *item_refs; +} iref_box_t; + +static int __parse_item_ref(_item_ref_t *item_ref, heif_source_h source, off_t offset, size_t size); +static int __iref_chunk_data_cb(heif_source_h source, uint32_t type, off_t offset, size_t size, void *user_data); +static gpointer __copy_item_refs(gconstpointer src, gpointer user_data); + + +int heif_box_iref_create(heif_box_iref_h *iref_box) +{ + iref_box_t *_iref_box = NULL; + + heif_retvm_if_failed(iref_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iref_box"); + + _iref_box = g_new0(iref_box_t, 1); + + *iref_box = (heif_box_iref_h)_iref_box; + + return LIBHEIF_ERROR_NONE; +} + +void heif_box_iref_destroy(heif_box_iref_h iref_box) +{ + iref_box_t *_iref_box = (iref_box_t *)iref_box; + + heif_retm_if_failed(iref_box); + + g_slist_free_full(_iref_box->item_refs, _free_item_ref); + + g_free(iref_box); +} + +int heif_box_iref_get_item_refs(heif_box_iref_h iref_box, GSList **item_refs) +{ + iref_box_t *_iref_box = (iref_box_t *)iref_box; + + heif_retvm_if_failed(iref_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iref_box"); + heif_retvm_if_failed(item_refs, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_refs"); + + *item_refs = g_slist_copy_deep(_iref_box->item_refs, __copy_item_refs, NULL); + + return LIBHEIF_ERROR_NONE; +} + +int heif_box_iref_parse(heif_box_iref_h iref_box, heif_source_h source, off_t offset, size_t size) +{ + int ret = LIBHEIF_ERROR_NONE; + iref_box_t *_iref_box = (iref_box_t *)iref_box; + + heif_retvm_if_failed(iref_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iref_box"); + + ret = _parse_full_box_header(&_iref_box->full_box, source, &offset, &size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("_parse_full_box_header fail %d", ret); + return ret; + } + + _iref_box->ref_id_size = (_iref_box->full_box.version == 0) ? 2 : 4; + + return _parse_box_chunks(source, offset, size, (_parse_chunk_cb)__iref_chunk_data_cb, _iref_box); +} + +static void __print_ref_item_id(gpointer data, gpointer user_data) +{ + uint32_t ref_item_id = (uint32_t)GPOINTER_TO_UINT(data); + + heif_retm_if_failed(data); + + heif_info("[ITEM_REF] refs: %u", ref_item_id); +} + +static void __print_item_ref(gpointer data, gpointer user_data) +{ + _item_ref_t *item_ref = (_item_ref_t *)data; + unsigned int *idx = (unsigned int *)user_data; + + heif_retm_if_failed(data); + heif_retm_if_failed(user_data); + + heif_info("[ITEM_REF/%u]", *idx); + heif_info("[ITEM_REF] item_type: %u", item_ref->item_type); + DEBUG_CHUNK_TYPE("ITEM_REF", item_ref->item_type); + heif_info("[ITEM_REF] item_id: %u", item_ref->item_id); + heif_info("[ITEM_REF] ref_size: %u", item_ref->ref_size); + g_slist_foreach(item_ref->refs, __print_ref_item_id, NULL); + + (*idx)++; +} + +void heif_box_iref_info(heif_box_iref_h iref_box) +{ + iref_box_t *_iref_box = (iref_box_t *)iref_box; + unsigned int idx = 0; + + heif_retm_if_failed(iref_box); + + heif_info("============ [IREF] Start ==========="); + heif_info("[IREF] ref_size: %u", _iref_box->ref_id_size); + g_slist_foreach(_iref_box->item_refs, __print_item_ref, &idx); + heif_info("============ [IREF] End ===========\n"); +} + +static int __parse_item_ref(_item_ref_t *item_ref, heif_source_h source, off_t offset, size_t size) +{ + uint16_t count = 0; + uint32_t refItemId = 0; + size_t i = 0; + + heif_retvm_if_failed(item_ref, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_ref"); + + if (size < item_ref->ref_size + 2) { + heif_error("size[%zu] is smaller than ref_size[%u]", size, item_ref->ref_size + 2); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + if (!heif_source_get_uint32var(source, offset, &item_ref->item_id, item_ref->ref_size)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + offset += item_ref->ref_size; + + if (!heif_source_get_uint16(source, offset, &count)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + offset += 2; + size -= (item_ref->ref_size + 2); + + if (size < count * item_ref->ref_size) { + heif_error("size[%zu] is smaller than refs[%u]", size, count * item_ref->ref_size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + for (i = 0; i < count; i++) { + if (!heif_source_get_uint32var(source, offset, &refItemId, item_ref->ref_size)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + offset += item_ref->ref_size; + item_ref->refs = g_slist_append(item_ref->refs, GUINT_TO_POINTER(refItemId)); +#ifdef __ENABLE_DEBUG_MODE + heif_info("item id %u: referencing item id %u", item_ref->item_id, refItemId); +#endif + } + + return LIBHEIF_ERROR_NONE; +} + +static int __iref_chunk_data_cb(heif_source_h source, uint32_t type, off_t offset, size_t size, void *user_data) +{ + int ret = LIBHEIF_ERROR_NONE; + iref_box_t *iref_box = (iref_box_t *)user_data; + _item_ref_t *item_ref; + + heif_retvm_if_failed(user_data, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid user_data"); + + item_ref = g_new0(_item_ref_t, 1); + item_ref->item_type = type; + item_ref->ref_size = iref_box->ref_id_size; + + ret = __parse_item_ref(item_ref, source, offset, size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__parse_item_ref fail %d", ret); + g_free(item_ref); + return ret; + } + + iref_box->item_refs = g_slist_append(iref_box->item_refs, item_ref); + + return LIBHEIF_ERROR_NONE; +} + +static gpointer __copy_item_refs(gconstpointer src, gpointer user_data) +{ + _item_ref_t *src_ref = (_item_ref_t *)src; + _item_ref_t *copy_ref = NULL; + + heif_retvm_if_failed(src, NULL, "invalid src"); + + copy_ref = g_new0(_item_ref_t, 1); + copy_ref->item_type = src_ref->item_type; + copy_ref->item_id = src_ref->item_id; + copy_ref->ref_size = src_ref->ref_size; + copy_ref->refs = g_slist_copy(src_ref->refs); + + return copy_ref; +} diff --git a/src/heif_box_iref.h b/src/heif_box_iref.h new file mode 100644 index 0000000..062eaa5 --- /dev/null +++ b/src/heif_box_iref.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __HEIF_BOX_IREF_H__ +#define __HEIF_BOX_IREF_H__ + +#include +#include "heif_source.h" + +typedef void * heif_box_iref_h; + +int heif_box_iref_create(heif_box_iref_h *iref_box); +void heif_box_iref_destroy(heif_box_iref_h iref_box); +int heif_box_iref_get_item_refs(heif_box_iref_h iref_box, GSList **item_refs); +int heif_box_iref_parse(heif_box_iref_h iref_box, heif_source_h source, off_t offset, size_t size); +void heif_box_iref_info(heif_box_iref_h iref_box); + +#endif /* __HEIF_BOX_IREF_H__ */ diff --git a/src/heif_box_pitm.c b/src/heif_box_pitm.c new file mode 100644 index 0000000..b72ac1d --- /dev/null +++ b/src/heif_box_pitm.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "heif_type.h" +#include "heif_debug.h" +#include "heif_box_util.h" +#include "heif_box_pitm.h" + +typedef struct { + _full_box_t full_box; + uint32_t primary_item_id; +} pitm_box_t; + + +int heif_box_pitm_create(heif_box_pitm_h *pitm_box) +{ + pitm_box_t *_pitm_box = NULL; + + heif_retvm_if_failed(pitm_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid pitm_box"); + + _pitm_box = g_new0(pitm_box_t, 1); + + *pitm_box = (heif_box_pitm_h)_pitm_box; + + return LIBHEIF_ERROR_NONE; +} + +void heif_box_pitm_destroy(heif_box_pitm_h pitm_box) +{ + heif_retm_if_failed(pitm_box); + + g_free(pitm_box); +} + +int heif_box_pitm_get_primary_id(heif_box_pitm_h pitm_box, uint32_t *primary_id) +{ + pitm_box_t *_pitm_box = (pitm_box_t *)pitm_box; + + heif_retvm_if_failed(pitm_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid pitm_box"); + heif_retvm_if_failed(primary_id, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid primary_id"); + + // only remember the primary id of pitm box for later use + *primary_id = _pitm_box->primary_item_id; + + return LIBHEIF_ERROR_NONE; +} + +int heif_box_pitm_parse(heif_box_pitm_h pitm_box, heif_source_h source, off_t offset, size_t size) +{ + int ret = LIBHEIF_ERROR_NONE; + pitm_box_t *_pitm_box = (pitm_box_t *)pitm_box; + size_t item_id_size = 0; + uint32_t item_id = 0; + + heif_retvm_if_failed(pitm_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid pitm_box"); + + ret = _parse_full_box_header(&(_pitm_box->full_box), source, &offset, &size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("_parse_full_box_header fail %d", ret); + return ret; + } + + item_id_size = (_pitm_box->full_box.version == 0) ? 2 : 4; + if (size < item_id_size) { + heif_error("size[%zu] is smaller than item_id_size[%zu]", size, item_id_size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + if (!heif_source_get_uint32var(source, offset, &item_id, item_id_size)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + heif_info("primary id %u", item_id); + _pitm_box->primary_item_id = item_id; + + return LIBHEIF_ERROR_NONE; +} + +void heif_box_pitm_info(heif_box_pitm_h pitm_box) +{ + heif_retm_if_failed(pitm_box); + + heif_info("============ [PITM] Start ==========="); + heif_info("[PITM] primary_item_id: %u", ((pitm_box_t *)pitm_box)->primary_item_id); + heif_info("============ [PITM] End ===========\n"); +} diff --git a/src/heif_box_pitm.h b/src/heif_box_pitm.h new file mode 100644 index 0000000..d3fae63 --- /dev/null +++ b/src/heif_box_pitm.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __HEIF_BOX_PITM_H__ +#define __HEIF_BOX_PITM_H__ + +#include + +#include "heif_source.h" + +typedef void * heif_box_pitm_h; + +int heif_box_pitm_create(heif_box_pitm_h *pitm_box); +void heif_box_pitm_destroy(heif_box_pitm_h pitm_box); +int heif_box_pitm_get_primary_id(heif_box_pitm_h pitm_box, uint32_t *primary_id); +int heif_box_pitm_parse(heif_box_pitm_h pitm_box, heif_source_h source, off_t offset, size_t size); +void heif_box_pitm_info(heif_box_pitm_h pitm_box); + +#endif /* __HEIF_BOX_PITM_H__ */ diff --git a/src/heif_box_util.c b/src/heif_box_util.c new file mode 100644 index 0000000..fba6887 --- /dev/null +++ b/src/heif_box_util.c @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "heif_type.h" +#include "heif_debug.h" +#include "heif_box_util.h" + + +#ifdef __ENABLE_DEBUG_MODE +static void __type_to_fourcc_string(uint32_t type, char *str); +#endif + + +// XXX warning: these won't work on big-endian host. +uint64_t ntoh64(uint64_t x) +{ + return ((uint64_t)ntohl(x & 0xffffffff) << 32) | ntohl(x >> 32); +} + +uint64_t hton64(uint64_t x) +{ + return ((uint64_t)htonl(x & 0xffffffff) << 32) | htonl(x >> 32); +} + +void _debug_chunk_type(const char *msg, uint32_t chunk_type) +{ +#ifdef __ENABLE_DEBUG_MODE + char chunk[5]; + __type_to_fourcc_string(chunk_type, chunk); + heif_secure_debug("[%s] chunk: %s", msg, chunk); +#endif +} + +void _debug_chunk_offset(const char *msg, uint32_t chunk_type, uint64_t chunk_size, off_t offset) +{ +#ifdef __ENABLE_DEBUG_MODE + char chunk[5]; + __type_to_fourcc_string(chunk_type, chunk); + heif_secure_debug("[%s] chunk: %s %"PRIu64" @ %jd", msg, chunk, chunk_size, offset); +#endif +} + +void _debug_chunk_info(const char *msg, uint32_t chunk_type, uint64_t chunk_size, off_t offset, int depth) +{ +#ifdef __ENABLE_DEBUG_MODE + char chunk[5]; + __type_to_fourcc_string(chunk_type, chunk); + heif_secure_debug("[%s] chunk: %s %"PRIu64" @ %jd, %d", msg, chunk, chunk_size, offset, depth); +#endif +} + +void _debug_full_box_info(_full_box_t *full_box, const char* parent) +{ +#ifdef __ENABLE_DEBUG_MODE + heif_retm_if_failed(full_box); + + heif_secure_debug("[%s] version: %u", parent, full_box->version); + heif_secure_debug("[%s] flags: %u", parent, full_box->flags); +#endif +} + +int _parse_hdr(heif_source_h source, off_t *offset, uint64_t *chunk_size, int32_t *chunk_type, off_t *data_offset) +{ + uint32_t hdr[2]; + + heif_retvm_if_failed(offset, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid offset"); + heif_retvm_if_failed(chunk_size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid chunk_size"); + heif_retvm_if_failed(chunk_type, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid chunk_type"); + heif_retvm_if_failed(data_offset, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid data_offset"); + + if (heif_source_read_at(source, *offset, hdr, 8) < 8) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + *chunk_size = ntohl(hdr[0]); + *chunk_type = ntohl(hdr[1]); + *data_offset = *offset + 8; + + return LIBHEIF_ERROR_NONE; +} + +int _parse_full_box_header(_full_box_t *full_box, heif_source_h source, off_t *offset, size_t *size) +{ + heif_retvm_if_failed(full_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid full_box"); + heif_retvm_if_failed(offset, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid offset"); + heif_retvm_if_failed(size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size"); + + if (*size < 4) { + heif_error("size[%zu] is too small", *size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + if (heif_source_read_at(source, *offset, &full_box->version, 1) != 1) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + if (!heif_source_get_uint24(source, *offset + 1, &full_box->flags)) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + *offset += 4; + *size -= 4; + +#ifdef __ENABLE_DEBUG_MODE + heif_info("version: %u flag: %u", full_box->version, full_box->flags); +#endif + + return LIBHEIF_ERROR_NONE; +} + +int _parse_box_chunk(heif_source_h source, off_t *offset, _parse_chunk_cb parse_chunk_cb, void *user_data) +{ + int ret = LIBHEIF_ERROR_NONE; + uint64_t chunk_size = 0; + int32_t chunk_type = 0; + off_t data_offset = 0; + off_t chunk_data_size = 0; + + heif_retvm_if_failed(offset, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid offset"); + heif_retvm_if_failed(parse_chunk_cb, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid parse_chunk_cb"); + + if (*offset < 0) { + heif_error("only continue parsing if the offset was advanced"); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + ret = _parse_hdr(source, offset, &chunk_size, &chunk_type, &data_offset); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("_parse_hdr fail %d", ret); + return ret; + } + + if (chunk_size == 1) { + if (heif_source_read_at(source, *offset + 8, &chunk_size, 8) < 8) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + chunk_size = ntoh64(chunk_size); + data_offset += 8; + + if (chunk_size < 16) { + // The smallest valid chunk is 16 bytes long in this case. + heif_error("too short chunk size %"PRIu64, chunk_size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + } else if (chunk_size == 0) { + // This shouldn't happen since we should never be top level + heif_error("invalid chunk size 0 for non-top level box"); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } else if (chunk_size < 8) { + // The smallest valid chunk is 8 bytes long. + heif_error("invalid chunk size: %"PRIu64, chunk_size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + DEBUG_CHUNK_TYPE_AND_OFFSET("sub_box", chunk_type, chunk_size, data_offset); + + chunk_data_size = chunk_size - (data_offset - *offset); + if (chunk_data_size < 0) { + heif_error("invalid chunk size: %jd", chunk_data_size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + ret = parse_chunk_cb(source, chunk_type, data_offset, chunk_data_size, user_data); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("parse_chunk_cb fail %d", ret); + return ret; + } + + *offset += chunk_size; + + return LIBHEIF_ERROR_NONE; +} + +int _parse_box_chunks(heif_source_h source, off_t offset, size_t size, _parse_chunk_cb parse_chunk_cb, void *user_data) +{ + int ret = LIBHEIF_ERROR_NONE; + off_t stop_offset = offset + size; + + while (offset < stop_offset) { + ret = _parse_box_chunk(source, &offset, parse_chunk_cb, user_data); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("_parse_box_chunk fail %d", ret); + return ret; + } + } + + if (offset != stop_offset) { + heif_error("offset[%jd] did not match stop_offset[%jd]", offset, stop_offset); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + return LIBHEIF_ERROR_NONE; +} + +bool _parse_string(heif_source_h source, off_t *offset, size_t *size, char **out) +{ + char tmp; + off_t new_offset = 0; + off_t stop_offset = 0; + char buf[256]; + unsigned int index = 0; + + heif_retvm_if_failed(offset, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid offset"); + heif_retvm_if_failed(size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size"); + heif_retvm_if_failed(out, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid out"); + + new_offset = *offset; + stop_offset = *offset + *size; + + while (new_offset < stop_offset) { + if (heif_source_read_at(source, new_offset++, &tmp, 1) != 1) + return false; + + buf[index++] = tmp; + if (tmp == 0) { + *out = g_strdup(buf); + + *offset = new_offset; + *size = stop_offset - new_offset; + + return true; + } + } + return false; +} + +// if you don't want to parse string meta, just skip it. +// this function will update source(cur_pos of reading), offset and size. +bool _skip_string(heif_source_h source, off_t *offset, size_t *size, char *type_name) +{ + char tmp; + off_t new_offset = 0; + off_t stop_offset = 0; +#ifdef __ENABLE_DEBUG_MODE + char buf[256]; + unsigned int index = 0; +#endif + + heif_retvm_if_failed(offset, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid offset"); + heif_retvm_if_failed(size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size"); + + new_offset = *offset; + stop_offset = *offset + *size; + + while (new_offset < stop_offset) { + if (heif_source_read_at(source, new_offset++, &tmp, 1) != 1) + return false; + +#ifdef __ENABLE_DEBUG_MODE + buf[index++] = tmp; +#endif + if (tmp == 0) { +#ifdef __ENABLE_DEBUG_MODE + heif_info("skip [%s:%s]", type_name, buf); +#endif + *offset = new_offset; + *size = stop_offset - new_offset; + + return true; + } + } + return false; +} + +int _get_item_loc(_item_loc_t *item, off_t *offset, size_t *size, off_t idatOffset, size_t idatSize) +{ + _extent_entry_t *extent_item; + + // not support fix extent handling and construction_method = 2 + heif_retvm_if_failed(item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item"); + heif_retvm_if_failed(g_slist_length(item->extents) == 1, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid extents %u", g_slist_length(item->extents)); + heif_retvm_if_failed(offset, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid offset"); + heif_retvm_if_failed(size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size"); + + extent_item = (_extent_entry_t *)item->extents->data; + +#ifdef __ENABLE_DEBUG_MODE + heif_info("base_offset: %jd extent_offset: %"PRIu64" extent_len: %"PRIu64, + item->base_offset, extent_item->extent_offset, extent_item->extent_length); +#endif + + switch (item->construction_method) { + case 0: + *offset = item->base_offset + extent_item->extent_offset; + *size = extent_item->extent_length; + break; + + case 1: + if (item->base_offset + extent_item->extent_offset + extent_item->extent_length + > idatSize) { + heif_error("the offset of location is too large, overflow"); + heif_error("base_offset: %jd extent_offset: %"PRIu64" extent_len: %"PRIu64" data_size: %zu", + item->base_offset, extent_item->extent_offset, extent_item->extent_length, idatSize); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + *offset = item->base_offset + extent_item->extent_offset + idatOffset; + *size = extent_item->extent_length; + break; + + default: + heif_error("invalid construction_method %d", item->construction_method); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + return LIBHEIF_ERROR_NONE; +} + +void _free_item_loc(gpointer data) +{ + heif_retm_if_failed(data); + + g_slist_free_full(((_item_loc_t *)data)->extents, g_free); + + g_free(data); +} + +void _free_image_prop(gpointer data) +{ + _item_prop_t *image_prop = (_item_prop_t *)data; + + heif_retm_if_failed(data); + + switch (image_prop->type) { + case FOURCC('h', 'v', 'c', 'C'): + if (image_prop->property) + g_free(((_hvcC_prop_t *)image_prop->property)->data); + break; + + case FOURCC('c', 'o', 'l', 'r'): + if (image_prop->property) + g_free(((_colr_prop_t *)image_prop->property)->icc_data); + break; + + case FOURCC('i', 's', 'p', 'e'): + // fall through + case FOURCC('i', 'r', 'o', 't'): + // fall through + case FOURCC('i', 'm', 'i', 'r'): + // fall through + default: + break; + } + + g_free(image_prop->property); + g_free(image_prop); +} + +void _free_item_ref(gpointer data) +{ + heif_retm_if_failed(data); + + g_slist_free(((_item_ref_t *)data)->refs); + + g_free(data); +} + +#ifdef __ENABLE_DEBUG_MODE +static void __type_to_fourcc_string(uint32_t type, char *str) +{ + heif_retm_if_failed(str); + + str[0] = type >> 24; + str[1] = (type >> 16) & 0xff; + str[2] = (type >> 8) & 0xff; + str[3] = type & 0xff; + str[4] = '\0'; +} +#endif diff --git a/src/heif_box_util.h b/src/heif_box_util.h new file mode 100644 index 0000000..cf8d8d8 --- /dev/null +++ b/src/heif_box_util.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __HEIF_BOX_UTIL_H__ +#define __HEIF_BOX_UTIL_H__ + +#include +#include +#include + +#include "heif_source.h" + +// utils for all +#define FOURCC(a, b, c, d) ((a << 24) + ((b) << 16) + ((c) << 8) + ((d))) +#define DEBUG_CHUNK_TYPE(m, t) _debug_chunk_type(m, t); +#define DEBUG_CHUNK_TYPE_AND_OFFSET(m, t, s, o) _debug_chunk_offset(m, t, s, o); +#define DEBUG_CHUNK_ALL(m, t, s, o, d) _debug_chunk_info(m, t, s, o, d); +#define DEBUG_FULL_BOX(b, p) _debug_full_box_info(b, p); + +// base container of container boxes +// some container boxes has full box to behave differently for version & flag +// Full box +typedef struct { + uint8_t version; + uint32_t flags; +} _full_box_t; + +// IINF container (META > IINF) +// IINF item information +// item_id: the identity value for each item (e.g. 1, 2, 3, 1001, 1002..) +// item_type: the type of item (e.g. hvc1, grid, Exif) +// hidden: determined by item_type +typedef struct { + uint32_t item_id; + uint32_t item_type; + bool hidden; +} _item_info_t; + +// extent entry of ILOC +typedef struct { + uint64_t extent_index; + uint64_t extent_offset; + uint64_t extent_length; +} _extent_entry_t; + +// ILOC container (META > ILOC) +// ILOC item location +// item_id: the item id at the location in source +// construction_method: not supported yet +// data_reference_index: not supported yet +// base_offset: the base offset in source, generally 0 +// extents: the extents offset and size of item +typedef struct { + uint32_t item_id; + uint16_t construction_method; + uint16_t data_reference_index; + off_t base_offset; + GSList *extents; +} _item_loc_t; + +// HVCC container (META > IPRP > IPCO > HVCC) +// data: the hevc configuration include nal +// size: the size of hevc configuration +typedef struct { + uint8_t *data; + size_t size; +} _hvcC_prop_t; + +// ISPE container (META > IPRP > IPCO > ISPE) +// width: the width of item +// height: the height of item +typedef struct { + _full_box_t full_box; + uint32_t width; + uint32_t height; +} _ispe_prop_t; + +// IROT container (META > IPRP > IPCO > IROT) +// angle: the orientation of item (e.g. 90, 180, 270) +// not support the flip(mirror) value, it exist at different container box +typedef struct { + uint8_t angle; +} _irot_prop_t; + +// COLR container (META > IPRP > IPCO > COLR) +// icc_data: the ICC profile for display, generally not used on hands-on device +// size: the size of ICC profile +typedef struct { + uint8_t *icc_data; + size_t size; +} _colr_prop_t; + +// IMIR container (META > IPRP > IPCO > IMIR) +// angle: the axis for mirroring operation of item +// vertial(axis = 0) or horizontal(axis = 1) +// if image has not mirroring operation, 'imir' property not exist. +typedef struct { + uint8_t axis; +} _imir_prop_t; + +// IPRP container (META > IPRP) +// type: the chunk type of property (e.g. hvcC, ispe, irot...) +// please see __attach_property() function +typedef struct { + uint32_t type; + void *property; +} _item_prop_t; + +// IPMA container (META > IPRP > IPMA) +// item_id: the item id +// essential: the value whether the app should handle it or not +// index: the index of property +// please see __attach_property() function +typedef struct { + uint32_t item_id; + bool essential; + uint16_t index; +} _association_t; + +// IREF container (META > IREF) +// item_type: the type of item (e.g. dimg, thmb, cdsc) +// item_id: the item id referencing the references +// ref_size: the count of reference id +// refs: the reference ids list +typedef struct { + uint32_t item_type; + uint32_t item_id; + uint32_t ref_size; + GSList *refs; +} _item_ref_t; + +typedef int (*_parse_chunk_cb)(heif_source_h source, uint32_t type, off_t offset, size_t size, void *user_data); + +// utility functions for reading 64-bit +uint64_t ntoh64(uint64_t x); +uint64_t hton64(uint64_t x); + +// utility debug function +void _debug_chunk_type(const char *msg, uint32_t chunk_type); +void _debug_chunk_offset(const char *msg, uint32_t chunk_type, uint64_t chunk_size, off_t offset); +void _debug_chunk_info(const char *msg, uint32_t chunk_type, uint64_t chunk_size, off_t offset, int depth); +void _debug_full_box_info(_full_box_t *full_box, const char* parent); + +// utility functions +int _parse_hdr(heif_source_h source, off_t *offset, uint64_t *chunk_size, int32_t *chunk_type, off_t *data_offset); +int _parse_full_box_header(_full_box_t *full_box, heif_source_h source, off_t *offset, size_t *size); +int _parse_box_chunk(heif_source_h source, off_t *offset, _parse_chunk_cb parse_chunk_cb, void *user_data); +int _parse_box_chunks(heif_source_h source, off_t offset, size_t size, _parse_chunk_cb parse_chunk_cb, void *user_data); +bool _parse_string(heif_source_h source, off_t *offset, size_t *size, char **out); +bool _skip_string(heif_source_h source, off_t *offset, size_t *size, char *type_name); +int _get_item_loc(_item_loc_t *item, off_t *offset, size_t *size, off_t idatOffset, size_t idatSize); + +// release memory +void _free_item_loc(gpointer data); +void _free_image_prop(gpointer data); +void _free_item_ref(gpointer data); + +#endif /* __HEIF_BOX_UTIL_H__ */ diff --git a/src/heif_decode_ffmpeg.c b/src/heif_decode_ffmpeg.c new file mode 100644 index 0000000..1602f2e --- /dev/null +++ b/src/heif_decode_ffmpeg.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "heif_type.h" +#include "heif_debug.h" +#include "heif_decode_ffmpeg.h" + + +// decoder of hevc codec +typedef struct { + AVCodecContext *ctx; + AVCodec *codec; + AVPacket pkt; +} _ffmpeg_decoder_t; + + +static int __ffmpeg_create_decoder(plugin_decode_h *decoder); +static void __ffmpeg_destroy_decoder(plugin_decode_h decoder); +static int __ffmpeg_decode_image(plugin_decode_h decoder, heif_buffer_t *media_data, heif_color_format_e colr_fmt, heif_image_t **decoded_image); + + +void register_ffmpeg_decoder(plugin_decode_h *decoder) +{ + plugin_decode_t *new_module; + + heif_retm_if_failed(decoder); + + new_module = g_new0(plugin_decode_t, 1); + + new_module->name = g_strdup("ffmpeg"); + new_module->priority = HEIF_PRIOR_MID; + new_module->init = (init_decoder)__ffmpeg_create_decoder; + new_module->deinit = (deinit_decoder)__ffmpeg_destroy_decoder; + new_module->decode = (do_decode)__ffmpeg_decode_image; + + new_module->supported_codec = g_slist_append(new_module->supported_codec, GINT_TO_POINTER(HEIF_CODEC_HEVC)); + + *decoder = (plugin_decode_h)new_module; +} + +static int __ffmpeg_get_image_from_frame(AVFrame *frame, heif_color_format_e colr_fmt, heif_image_t **libav_output) +{ + struct SwsContext *img_convert_ctx = NULL; + + uint8_t *dst_data[4]; + int dst_linesize[4]; + int pix_fmt = AV_PIX_FMT_NONE; + heif_image_t *output; + + heif_retvm_if_failed(frame, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid frame"); + heif_retvm_if_failed(libav_output, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid libav_output"); + + switch (colr_fmt) { + case HEIF_COLOR_FORMAT_YUV420P: + pix_fmt = AV_PIX_FMT_YUV420P; + break; + + case HEIF_COLOR_FORMAT_RGB24: + pix_fmt = AV_PIX_FMT_RGB24; + break; + + case HEIF_COLOR_FORMAT_ARGB: + pix_fmt = AV_PIX_FMT_ARGB; + break; + + case HEIF_COLOR_FORMAT_BGRA: + pix_fmt = AV_PIX_FMT_BGRA; + break; + + case HEIF_COLOR_FORMAT_RGBA: + pix_fmt = AV_PIX_FMT_RGBA; + break; + + default: + heif_error("invalid colr_fmt %d", colr_fmt); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + heif_secure_debug("frame_format: %d, color_format: %d, pix_fmt: %d", frame->format, colr_fmt, pix_fmt); + + output = g_new0(heif_image_t, 1); + + output->size = (size_t)av_image_get_buffer_size(pix_fmt, frame->width, frame->height, 1); + if (output->size <= 0) { + heif_error("av_image_get_buffer_size fail [%d x %d]", frame->width, frame->height); + goto FAIL; + } + + output->width = (unsigned int)frame->width; + output->height = (unsigned int)frame->height; + output->format = colr_fmt; + output->data = av_malloc(output->size); + if (!output->data) { + heif_error("av_malloc fail"); + goto FAIL; + } + + if (av_image_fill_arrays(dst_data, dst_linesize, output->data, pix_fmt, frame->width, frame->height, 1) < 0) { + heif_error("avpicture_fill fail"); + goto FAIL; + } + + img_convert_ctx = sws_getContext(frame->width, frame->height, frame->format, frame->width, frame->height, pix_fmt, SWS_BICUBIC, NULL, NULL, NULL); + + if (NULL == img_convert_ctx) { + heif_error("sws_getContext fail"); + goto FAIL; + } + + if (sws_scale(img_convert_ctx, (const uint8_t * const *)frame->data, frame->linesize, 0, frame->height, dst_data, dst_linesize) < 0) { + heif_error("sws_scale to convert color format fail"); + sws_freeContext(img_convert_ctx); + goto FAIL; + } + + sws_freeContext(img_convert_ctx); + + *libav_output = output; + + return LIBHEIF_ERROR_NONE; + +FAIL: + av_freep(&output->data); + g_free(output); + + return LIBHEIF_ERROR_INVALID_OPERATION; +} + +static int __ffmpeg_send_media(_ffmpeg_decoder_t *decoder, heif_buffer_t *media_data) +{ + uint8_t *pkt_buf; + + heif_retvm_if_failed(decoder, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid decoder"); + heif_retvm_if_failed(media_data, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid media_data"); + heif_retvm_if_failed(media_data->data, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid media_data->data"); + heif_retvm_if_failed(media_data->size > 0, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid media_data->size"); + + av_packet_unref(&(decoder->pkt)); + + av_init_packet(&(decoder->pkt)); + + // this buffer should be released by av_packet_unref + pkt_buf = g_memdup(media_data->data, media_data->size); + + if (av_packet_from_data(&decoder->pkt, pkt_buf, media_data->size) < 0) { + heif_error("av_packet_from_data fail"); + g_free(pkt_buf); + return LIBHEIF_ERROR_INVALID_OPERATION; + } + + return LIBHEIF_ERROR_NONE; +} + +static int __ffmpeg_get_decoder(int codec_id, _ffmpeg_decoder_t *decoder) +{ + heif_debug_fenter(); + + heif_retvm_if_failed(decoder, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid decoder"); + + if (decoder->ctx) { + avcodec_close(decoder->ctx); + av_freep(&decoder->ctx); + } + + decoder->codec = avcodec_find_decoder(codec_id); + if (!decoder->codec) { + heif_error("Can't find decoder for %d codec.", codec_id); + return LIBHEIF_ERROR_INVALID_OPERATION; + } + + decoder->ctx = avcodec_alloc_context3(decoder->codec); + if (!decoder->ctx) { + heif_error("Can't allocate context for '%s' codec.", decoder->codec->name); + return LIBHEIF_ERROR_INVALID_OPERATION; + } + + decoder->ctx->codec = decoder->codec; + decoder->ctx->codec_id = codec_id; + + if (avcodec_open2(decoder->ctx, decoder->codec, NULL) < 0) { + heif_error("Can't open context for '%s' codec.", decoder->codec->name); + av_freep(&decoder->ctx); + decoder->ctx = NULL; + return LIBHEIF_ERROR_INVALID_OPERATION; + } + + // hevc decoder doesn't support any other fmt. + decoder->ctx->pix_fmt = AV_PIX_FMT_YUVJ420P; + decoder->ctx->thread_count = 1; // sw decoding should set '1' because ffmpeg is thread-unsafety + + heif_debug_fleave(); + + return LIBHEIF_ERROR_NONE; +} + +static int __ffmpeg_create_decoder(plugin_decode_h *decoder) +{ + int ret = LIBHEIF_ERROR_NONE; + _ffmpeg_decoder_t *new_decoder; + + heif_retvm_if_failed(decoder, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid decoder"); + + heif_debug_fenter(); + + new_decoder = g_new0(_ffmpeg_decoder_t, 1); + +#ifdef __ENABLE_DEBUG_MODE + av_log_set_level(AV_LOG_TRACE); +#else + av_log_set_level(AV_LOG_WARNING); +#endif + + // set codec and get decoder + ret = __ffmpeg_get_decoder(AV_CODEC_ID_HEVC, new_decoder); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__ffmpeg_set_codec fail (%d)", ret); + goto FAIL; + } + + *decoder = (plugin_decode_h)new_decoder; + + heif_debug_fleave(); + + return LIBHEIF_ERROR_NONE; + +FAIL: + __ffmpeg_destroy_decoder(new_decoder); + + return LIBHEIF_ERROR_NOT_SUPPORTED; +} + +static void __ffmpeg_destroy_decoder(plugin_decode_h decoder) +{ + _ffmpeg_decoder_t *_decoder = (_ffmpeg_decoder_t *)decoder; + + heif_debug_fenter(); + + heif_retm_if_failed(decoder); + + av_packet_unref(&_decoder->pkt); + if (_decoder->ctx) { + avcodec_close(_decoder->ctx); + av_freep(&_decoder->ctx); + } + g_free(_decoder); + + heif_debug_fleave(); +} + +static int __ffmpeg_decode_image(plugin_decode_h decoder, heif_buffer_t *media_data, heif_color_format_e colr_fmt, heif_image_t **decoded_image) +{ + int ret = LIBHEIF_ERROR_NONE; + AVFrame *frame = NULL; + _ffmpeg_decoder_t *_decoder = (_ffmpeg_decoder_t *)decoder; + + heif_retvm_if_failed(decoder, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid decoder"); + + heif_debug_fenter(); + + ret = __ffmpeg_send_media(_decoder, media_data); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__ffmpeg_send_media fail (%d)", ret); + return LIBHEIF_ERROR_INVALID_OPERATION; + } + + avcodec_flush_buffers(_decoder->ctx); + + frame = av_frame_alloc(); + if (!frame) { + heif_error("av_frame_alloc fail"); + ret = LIBHEIF_ERROR_INVALID_OPERATION; + goto END; + } + + // decode + if (avcodec_send_packet(_decoder->ctx, &_decoder->pkt) < 0) { + heif_error("avcodec_send_packet fail"); + ret = LIBHEIF_ERROR_INVALID_OPERATION; + goto END; + } + + if (avcodec_receive_frame(_decoder->ctx, frame) < 0) { + heif_error("avcodec_receive_frame fail"); + ret = LIBHEIF_ERROR_INVALID_OPERATION; + goto END; + } + + ret = __ffmpeg_get_image_from_frame(frame, colr_fmt, decoded_image); + if (ret != LIBHEIF_ERROR_NONE) + heif_error("__ffmpeg_get_image_from_frame fail (%d)", ret); + +END: + av_frame_free(&frame); + + heif_debug_fleave(); + + return ret; +} diff --git a/src/heif_decode_ffmpeg.h b/src/heif_decode_ffmpeg.h new file mode 100644 index 0000000..3c40310 --- /dev/null +++ b/src/heif_decode_ffmpeg.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __HEIF_DECODE_FFMPEG_H__ +#define __HEIF_DECODE_FFMPEG_H__ + +#include "heif_decode_plugin.h" + +void register_ffmpeg_decoder(plugin_decode_h *decoder); + + +#endif /* __HEIF_DECODE_FFMPEG_H__ */ diff --git a/src/heif_decode_plugin.c b/src/heif_decode_plugin.c new file mode 100644 index 0000000..4dcbffd --- /dev/null +++ b/src/heif_decode_plugin.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "heif_debug.h" + +#include "heif_decode_plugin.h" +#include "heif_decode_ffmpeg.h" + +static gint __compare_name(gconstpointer data, gconstpointer user_data); +static gint __compare_codec(gconstpointer data, gconstpointer user_data); +static gint __compare_supported_codec(gconstpointer data, gconstpointer user_data); +static gint __compare_priority(gconstpointer a, gconstpointer b); +static void __init_decoder(gpointer data, gpointer user_data); +static void __unregister_decoder(gpointer data); + + +void load_decode_modules(decode_module_h *decode_modules) +{ + GSList *new_decode_modules = NULL; + plugin_decode_h module = NULL; + + heif_retm_if_failed(decode_modules); + + register_ffmpeg_decoder(&module); + new_decode_modules = g_slist_insert_sorted(new_decode_modules, module, __compare_priority); + + /* + * register_XXX_decoder(&XXXmodule); + * new_decode_modules = g_slist_insert_sorted(new_decode_modules, XXXmodule, __compare_priority); + */ + + g_slist_foreach(new_decode_modules, __init_decoder, NULL); + + *decode_modules = (decode_module_h)new_decode_modules; +} + +void foreach_supported_module(decode_module_h decode_modules, heif_codec_type_e codec, foreach_supported_decoder_cb callback, void *user_data) +{ + GSList *find_module = NULL, *iter = NULL; + + find_module = g_slist_find_custom((GSList *)decode_modules, GINT_TO_POINTER(codec), __compare_supported_codec); + + for (iter = find_module; iter; iter = g_slist_next(iter)) { + if (!callback((plugin_decode_t *)iter->data, user_data)) + break; + } +} + +int get_decoder_by_name(decode_module_h decode_modules, char *name, plugin_decode_t **decoder) +{ + GSList *find_module = NULL; + + heif_retvm_if_failed(decode_modules, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid decode_modules"); + heif_retvm_if_failed(name, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid name"); + heif_retvm_if_failed(decoder, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid decoder"); + + find_module = g_slist_find_custom((GSList *)decode_modules, name, __compare_name); + + heif_retvm_if_failed(find_module, LIBHEIF_ERROR_INVALID_PARAMETER, "not found %s", name); + + *decoder = (plugin_decode_t *)find_module->data; + + heif_debug("found %s decoder", (*decoder)->name); + + return LIBHEIF_ERROR_NONE; +} + +int get_decoder_by_codec(decode_module_h decode_modules, heif_codec_type_e codec, plugin_decode_t **decoder) +{ + GSList *find_module = NULL; + + heif_retvm_if_failed(decode_modules, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid decode_modules"); + heif_retvm_if_failed(codec > 0 && codec < HEIF_CODEC_MAX, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid codec %d", codec); + heif_retvm_if_failed(decoder, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid decoder"); + + find_module = g_slist_find_custom((GSList *)decode_modules, GINT_TO_POINTER(codec), __compare_supported_codec); + + heif_retvm_if_failed(find_module, LIBHEIF_ERROR_INVALID_PARAMETER, "not found %d", codec); + + *decoder = (plugin_decode_t *)find_module->data; + + heif_debug("found %s decoder", (*decoder)->name); + + return LIBHEIF_ERROR_NONE; +} + +void unload_decode_modules(decode_module_h decode_modules) +{ + g_slist_free_full((GSList *)decode_modules, __unregister_decoder); +} + +static gint __compare_name(gconstpointer data, gconstpointer user_data) +{ + plugin_decode_t *decoder = (plugin_decode_t *)data; + char *get_name = (char *)user_data; + + heif_retvm_if_failed(data, 1, "invalid data"); + heif_retvm_if_failed(decoder->initialized, 1, "invalid decoder"); + + return g_strcmp0(decoder->name, get_name); +} + +static gint __compare_codec(gconstpointer data, gconstpointer user_data) +{ + heif_codec_type_e codec = GPOINTER_TO_INT(data); + heif_codec_type_e get_codec = GPOINTER_TO_INT(user_data); + + heif_retvm_if_failed(data, 1, "invalid data"); + heif_retvm_if_failed(user_data, -1, "invalid user_data"); + + return ((codec == get_codec) ? 0 : 1); +} + +static gint __compare_supported_codec(gconstpointer data, gconstpointer user_data) +{ + plugin_decode_t *decoder = (plugin_decode_t *)data; + heif_codec_type_e get_codec = GPOINTER_TO_INT(user_data); + + heif_retvm_if_failed(data, 1, "invalid data"); + heif_retvm_if_failed(user_data, -1, "invalid user_data"); + heif_retvm_if_failed(decoder->initialized, 1, "invalid decoder"); + + return (g_slist_find_custom(decoder->supported_codec, GINT_TO_POINTER(get_codec), __compare_codec) != NULL) ? 0 : 1; +} + +static gint __compare_priority(gconstpointer a, gconstpointer b) +{ + plugin_decode_t *decoder_a = (plugin_decode_t *)a; + plugin_decode_t *decoder_b = (plugin_decode_t *)b; + + heif_retvm_if_failed(a, -1, "invalid a"); + heif_retvm_if_failed(b, 1, "invalid b"); + + if (decoder_a->priority < decoder_b->priority) + return -1; + else if (decoder_a->priority > decoder_b->priority) + return 1; + + return 0; +} + +static void __init_decoder(gpointer data, gpointer user_data) +{ + plugin_decode_t *decoder = (plugin_decode_t *)data; + + heif_retm_if_failed(data); + + if (decoder->init) + decoder->initialized = (decoder->init(&decoder->handle) == 0) ? true : false; + + heif_info("decoder: %s was %s", decoder->name, decoder->initialized ? "initialized" : "not initialized"); +} + +static void __unregister_decoder(gpointer data) +{ + plugin_decode_t *decoder = (plugin_decode_t *)data; + + heif_retm_if_failed(data); + + if (decoder->deinit) + decoder->deinit(decoder->handle); + + g_slist_free(decoder->supported_codec); + g_free(decoder->name); + g_free(decoder); +} diff --git a/src/heif_decode_plugin.h b/src/heif_decode_plugin.h new file mode 100644 index 0000000..6cec089 --- /dev/null +++ b/src/heif_decode_plugin.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __HEIF_DECODE_PLUGIN_H__ +#define __HEIF_DECODE_PLUGIN_H__ + +#include "heif_type.h" +#include "heif_itemtable.h" + +typedef enum { + HEIF_CODEC_HEVC, + HEIF_CODEC_MAX, +} heif_codec_type_e; + +typedef enum { + HEIF_PRIOR_HIGH, + HEIF_PRIOR_MID, + HEIF_PRIOR_LOW, +} heif_priority_e; + +// output of decoding heif +typedef struct { + unsigned int width; + unsigned int height; + heif_color_format_e format; + unsigned char *data; + size_t size; +} heif_image_t; + +typedef void *plugin_decode_h; + +typedef int (*init_decoder)(plugin_decode_h *decoder); +typedef void (*deinit_decoder)(plugin_decode_h decoder); +typedef int (*do_decode)(plugin_decode_h decoder, heif_buffer_t *media_data, heif_color_format_e colr_fmt, heif_image_t **decoded_image); + +typedef struct { + bool initialized; + char *name; + int priority; // not used yet + plugin_decode_h handle; + init_decoder init; + deinit_decoder deinit; + do_decode decode; + GSList *supported_codec; // not used yet +} plugin_decode_t; + +typedef void *decode_module_h; + +typedef bool (*foreach_supported_decoder_cb)(plugin_decode_t *decoder, void *user_data); + +void load_decode_modules(decode_module_h *decode_modules); +void foreach_supported_module(decode_module_h decode_modules, heif_codec_type_e codec, foreach_supported_decoder_cb callback, void *user_data); +int get_decoder_by_name(decode_module_h decode_modules, char *name, plugin_decode_t **decoder); +int get_decoder_by_codec(decode_module_h decode_modules, heif_codec_type_e codec, plugin_decode_t **decoder); +void unload_decode_modules(decode_module_h decode_modules); + +#endif /* __HEIF_DECODE_PLUGIN_H__ */ diff --git a/src/heif_decoder.c b/src/heif_decoder.c new file mode 100644 index 0000000..1ba5684 --- /dev/null +++ b/src/heif_decoder.c @@ -0,0 +1,550 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include "heif_type.h" +#include "heif_debug.h" +#include "heif_decoder.h" +#include "heif_decode_plugin.h" + + +static bool __is_valid_heif_image(heif_image_t *image); +static int __decode_single_image(heif_itemtable_h item_table, heif_color_format_e format, heif_image_item_h image_item, heif_image_t **image); +static int __create_output_image(heif_image_item_h image_item, heif_color_format_e color_format, heif_image_t **output_image); +static int __combine_rgb(heif_image_t *tile_image, unsigned int pos_x, unsigned int pos_y, heif_image_t *combine_image); +static int __combine_yuv(heif_image_t *tile_image, unsigned int pos_x, unsigned int pos_y, heif_image_t *combine_image); +static int __combine_tile_image(heif_image_t *tile_image, unsigned int pos_x, unsigned int pos_y, heif_image_t *combine_image); +static int __decode_grid_image(heif_itemtable_h item_table, heif_color_format_e format, heif_image_item_h image_item, heif_image_t **image); + + +int heif_decoder_decode_primary(heif_itemtable_h item_table, heif_color_format_e format, heif_image_h *image) +{ + int ret = LIBHEIF_ERROR_NONE; + unsigned int primary_id = 0; + heif_image_item_h primary_item; + + heif_debug_fenter(); + + ret = heif_itemtable_get_primary_image(item_table, &primary_id); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_itemtable_get_primary_image fail (%d)", ret); + goto END; + } + + ret = heif_itemtable_get_image_item(item_table, primary_id, &primary_item); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_itemtable_get_image_item fail (%d)", ret); + goto END; + } + + if (heif_itemtable_has_grid(item_table)) { + heif_info("has grid!"); + ret = __decode_grid_image(item_table, format, primary_item, (heif_image_t **)image); + if (ret != LIBHEIF_ERROR_NONE) + heif_error("__decode_grid_image fail (%d)", ret); + } else { + heif_info("single!"); + ret = __decode_single_image(item_table, format, primary_item, (heif_image_t **)image); + if (ret != LIBHEIF_ERROR_NONE) + heif_error("__decode_single_image fail (%d)", ret); + } + +END: + heif_debug_fleave(); + + return ret; +} + +int heif_decoder_decode_thumbnail(heif_itemtable_h item_table, heif_color_format_e format, heif_image_h *image) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_image_item_h thumb_item; + + heif_debug_fenter(); + + ret = heif_itemtable_get_thumb_item(item_table, &thumb_item); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_itemtable_get_thumb_item fail (%d)", ret); + goto END; + } + + ret = __decode_single_image(item_table, format, thumb_item, (heif_image_t **)image); + if (ret != LIBHEIF_ERROR_NONE) + heif_error("__decode_single_image fail (%d)", ret); + +END: + heif_debug_fleave(); + + return ret; +} + +int heif_decoder_get_image(heif_image_h image, unsigned int *width, unsigned int *height, heif_color_format_e *format, unsigned char **data, size_t *size) +{ + heif_image_t *_image = (heif_image_t *)image; + + heif_retvm_if_failed(image, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image"); + + heif_info("width: %u, height: %u, format: %u, data: %p, size: %zu", _image->width, _image->height, _image->format, _image->data, _image->size); + + if (width) + *width = _image->width; + if (height) + *height = _image->height; + if (format) + *format = _image->format; + if (data) + *data = _image->data; + if (size) + *size = _image->size; + + return LIBHEIF_ERROR_NONE; +} + +void heif_decoder_destroy_image(heif_image_h image) +{ + heif_retm_if_failed(image); + + g_free(((heif_image_t *)image)->data); + g_free(image); +} + +static bool __is_valid_heif_image(heif_image_t *image) +{ + heif_retvm_if_failed(image, false, "invalid image"); + heif_retvm_if_failed(image->width > 0, false, "invalid width"); + heif_retvm_if_failed(image->height > 0, false, "invalid height"); + heif_retvm_if_failed(image->format > HEIF_COLOR_FORMAT_NONE && image->format < HEIF_COLOR_FORMAT_MAX, false, "invalid format %d", image->format); + heif_retvm_if_failed(image->data, false, "invalid data"); + heif_retvm_if_failed(image->size > 0, false, "invalid size"); + + return true; +} + +static int __decode_single_image(heif_itemtable_h item_table, heif_color_format_e format, heif_image_item_h image_item, heif_image_t **image) +{ + int ret = LIBHEIF_ERROR_NONE; + plugin_decode_t *ffmpeg_decoder = NULL; + heif_image_t *decoded_image = NULL; + heif_buffer_t *coded_data = NULL; + decode_module_h decode_modules; + + heif_retvm_if_failed(image, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image"); + + ret = heif_itemtable_get_coded_data(item_table, image_item, &coded_data); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_itemtable_get_coded_data fail (%d)", ret); + return ret; + } + + //ret = get_decoder_by_codec(decoder->decode_modules, HEIF_CODEC_HEVC, &hevc_decoder) + load_decode_modules(&decode_modules); + ret = get_decoder_by_name(decode_modules, "ffmpeg", &ffmpeg_decoder); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("get_decoder_by_name fail (%d)", ret); + goto END; + } + + ret = ffmpeg_decoder->decode(ffmpeg_decoder->handle, coded_data, format, &decoded_image); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("decode fail (%d)", ret); + goto END; + } + + *image = decoded_image; + +END: + heif_buffer_destroy(coded_data); + unload_decode_modules(decode_modules); + + return ret; +} + +static size_t __get_image_data_size(unsigned int width, unsigned int height, heif_color_format_e color_format) +{ + size_t data_size = 0; + size_t y_size = 0, u_size = 0, v_size = 0; + + heif_retvm_if_failed(width > 0, 0, "invalid width"); + heif_retvm_if_failed(height > 0, 0, "invalid height"); + + switch (color_format) { + case HEIF_COLOR_FORMAT_RGB24: + data_size = width * height * 3; + break; + + case HEIF_COLOR_FORMAT_ARGB: + // fall through + case HEIF_COLOR_FORMAT_BGRA: + // fall through + case HEIF_COLOR_FORMAT_RGBA: + data_size = width * height * 4; + break; + + case HEIF_COLOR_FORMAT_YUV420P: + y_size = width * height; + u_size = width / 2 * height / 2; + v_size = width / 2 * height / 2; + heif_info("y_size: %zu, u_size: %zu, v_size: %zu", y_size, u_size, v_size); + data_size = y_size + u_size + v_size; + break; + + default: + heif_error("invalid color_format (%d)", color_format); + return 0; + } + + heif_info("data_size: %zu, width: %u, height: %u, format: %u", data_size, width, height, color_format); + + return data_size; +} + +static int __create_output_image(heif_image_item_h image_item, heif_color_format_e color_format, heif_image_t **output_image) +{ + int ret = LIBHEIF_ERROR_NONE; + unsigned int width = 0, height = 0; + heif_image_t *image = NULL; + size_t data_size = 0; + + heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid heif_decoder"); + heif_retvm_if_failed(color_format > HEIF_COLOR_FORMAT_NONE && color_format < HEIF_COLOR_FORMAT_MAX, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid color_format"); + heif_retvm_if_failed(output_image, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid output_image"); + + ret = heif_itemtable_get_image_resolution(image_item, &width, &height); + heif_retvm_if_failed(ret == LIBHEIF_ERROR_NONE, ret, "heif_itemtable_get_image_resolution fail (%d)", ret); + + data_size = __get_image_data_size(width, height, color_format); + heif_retvm_if_failed(data_size > 0, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid data_size"); + + image = g_new0(heif_image_t, 1); + image->width = width; + image->height = height; + image->format = color_format; + image->size = data_size; + image->data = g_malloc0(image->size); + + heif_info("width: %u, height: %u, format: %u, size: %zu", image->width, image->height, image->format, image->size); + + *output_image = image; + + return LIBHEIF_ERROR_NONE; +} + +static int __combine_rgb(heif_image_t *tile_image, unsigned int pos_x, unsigned int pos_y, heif_image_t *combine_image) +{ + unsigned int bpp = 0; /* byte per pixel */ + unsigned int combine_stride = 0, tile_stride = 0; + unsigned int row = 0; + size_t copy_n = 0; + size_t total_copied_n = 0; + unsigned char *ptr_src = NULL, *ptr_dst = NULL; + + heif_retvm_if_failed(__is_valid_heif_image(tile_image), LIBHEIF_ERROR_INVALID_PARAMETER, "invalid tile_image"); + heif_retvm_if_failed(pos_x < combine_image->width, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid pos_x %u", pos_x); + heif_retvm_if_failed(pos_y < combine_image->height, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid pos_y %u", pos_y); + heif_retvm_if_failed(__is_valid_heif_image(combine_image), LIBHEIF_ERROR_INVALID_PARAMETER, "invalid combine_image"); + + switch (tile_image->format) { + case HEIF_COLOR_FORMAT_RGB24: + bpp = 3; + break; + + case HEIF_COLOR_FORMAT_ARGB: + // fall through + case HEIF_COLOR_FORMAT_BGRA: + // fall through + case HEIF_COLOR_FORMAT_RGBA: + bpp = 4; + break; + + default: + heif_error("not support %u format yet", tile_image->format); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + // combine_stride: row-bytes of combine_image + combine_stride = combine_image->width * bpp; + // tile_stride: row-bytes of tile + tile_stride = tile_image->width * bpp; + // copy_n: bytes to copy to combine_image + // Because the tile image was designed by the encoder limit of the device that was taken. + // The tile image doesn't make up the whole image(combine_image) properly. + // As a result, the tile image has over size data. + // So we should check the width/height to avoid over read & write. + // To explain it easily, + // When you want to tile the walls in the bathroom, the tiles are sized at the factory. + // Therefore, you may not be able to tile it to fit exactly the walls of the bathroom. + // If it doesn't fit, the tiles at the corner must be cut and pasted. + copy_n = (combine_image->width < (pos_x + tile_image->width)) ? (combine_image->width - pos_x) * bpp : tile_stride; + + ptr_src = tile_image->data; + ptr_dst = combine_image->data + pos_y * combine_stride + pos_x * bpp; + + heif_info("combine_stride: %u, tile_stride: %u", combine_stride, tile_stride); + heif_info("ptr_src: %p, ptr_dst: %p, copy_n: %zu", ptr_src, ptr_dst, copy_n); + + // check the size of tile_image to avoid over-read + if (tile_stride * tile_image->height > tile_image->size) { + heif_error("Invalid source size! stride: %u, height: %u, src_size: %zu", + tile_stride, tile_image->height, tile_image->size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + for (row = 0; row < tile_image->height; row++) { + // check the height to avoid over-write + if (pos_y + row >= combine_image->height) { + heif_warning("[No-Error] height limited! tile_h: %u, pos_y: %u, comb_h: %u", + tile_image->height, pos_y, combine_image->height); + break; + } + // check the size to copy to avoid 'ptr_dst' overflow + if (total_copied_n + copy_n > combine_image->size) { + heif_error("dest buffer overflow! total_copied_n: %zu, copy_size: %zu, dest_size: %zu", + total_copied_n, copy_n, combine_image->size); + break; + } + memcpy(ptr_dst, ptr_src, copy_n); + + ptr_src += tile_stride; // update ptr to next row in tile_image + ptr_dst += combine_stride; // update ptr to next row in combile_image + total_copied_n += copy_n; // to check data overflow + } + + return LIBHEIF_ERROR_NONE; +} + +static int __combine_yuv(heif_image_t *tile_image, unsigned int pos_x, unsigned int pos_y, heif_image_t *combine_image) +{ + unsigned int combine_stride = 0; + unsigned int tile_stride = 0; + unsigned int row = 0; + size_t copy_n = 0; + size_t total_copied_n[3] = { 0, }; + size_t dst_size = 0; + unsigned char *ptr_src[3] = { NULL, }, *ptr_dst[3] = { NULL, }; // y = 0, u = 1, v =2 + + heif_retvm_if_failed(__is_valid_heif_image(tile_image), LIBHEIF_ERROR_INVALID_PARAMETER, "invalid tile_image"); + heif_retvm_if_failed(pos_x < combine_image->width, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid pos_x %u", pos_x); + heif_retvm_if_failed(pos_y < combine_image->height, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid pos_y %u", pos_y); + heif_retvm_if_failed(__is_valid_heif_image(combine_image), LIBHEIF_ERROR_INVALID_PARAMETER, "invalid combine_image"); + + switch (tile_image->format) { + case HEIF_COLOR_FORMAT_YUV420P: + break; + default: + heif_error("not support %u format yet", tile_image->format); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + // combine_stride: bytes of the width for entire image + // tile_stride: bytes of the width for tile image + // offset_x: bytes of x for tile image + // offset_y: bytes of y for tile image, each row increase by combine_stride + + // copy 'y' data of tile + combine_stride = combine_image->width; + tile_stride = tile_image->width; + dst_size = combine_stride * combine_image->height; + + ptr_src[0] = tile_image->data; + ptr_dst[0] = combine_image->data + pos_y * combine_stride + pos_x; + + // copy 'y' data of tile + copy_n = ((combine_stride - pos_x) < tile_stride) ? (combine_stride - pos_x) : tile_stride; + + heif_info("combine_stride: %u, tile_stride: %u", combine_stride, tile_stride); + heif_info("ptr_src: %p, ptr_dst: %p, copy_n: %zu", ptr_src[0], ptr_dst[0], copy_n); + + // check the size of tile_image to avoid over-read + if (((tile_stride * tile_image->height) * 3) / 2 > tile_image->size) { + heif_error("Invalid source size! stride: %u, height: %u, src_size: %zu", + tile_stride, tile_image->height, tile_image->size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + for (row = 0; row < tile_image->height; row++) { + // check the height to avoid over-write + if (pos_y + row >= combine_image->height) { + heif_warning("[No-Error] height limited! tile_h: %u, pos_y: %u, comb_h: %u", + tile_image->height, pos_y, combine_image->height); + break; + } + // check the size to copy to avoid 'ptr_dst' overflow + if (total_copied_n[0] + copy_n > dst_size) { + heif_error("dest buffer overflow! total_copied_n: %zu, copy_size: %zu, dest_size: %zu", + total_copied_n[0], copy_n, dst_size); + break; + } + memcpy(ptr_dst[0], ptr_src[0], copy_n); + + ptr_src[0] += tile_stride; + ptr_dst[0] += combine_stride; + total_copied_n[0] += copy_n; + } + + // copy 'uv' data of tile + // uv_stride = y_stride / 2 + // uv_height = y_height / 2 + combine_stride = combine_stride / 2; + tile_stride = tile_stride / 2; + dst_size = dst_size / 4; // (combine_stride * combine_image->height / 2) + + ptr_src[1] = tile_image->data + tile_image->width * tile_image->height; + ptr_src[2] = ptr_src[1] + (tile_image->width / 2) * (tile_image->height / 2); + + ptr_dst[1] = combine_image->data + combine_image->width * combine_image->height + pos_y / 2 * combine_stride + pos_x / 2; + ptr_dst[2] = ptr_dst[1] + (combine_image->width / 2) * (combine_image->height / 2); + + copy_n = ((combine_stride - (pos_x / 2)) < tile_stride) ? (combine_stride - (pos_x / 2)) : tile_stride; + + heif_info("combine_stride: %u, tile_stride: %u", combine_stride, tile_stride); + heif_info("ptr_src: %p, ptr_dst: %p, copy_n: %zu", ptr_src[1], ptr_dst[1], copy_n); + heif_info("ptr_src: %p, ptr_dst: %p, copy_n: %zu", ptr_src[2], ptr_dst[2], copy_n); + + for (row = 0; row < tile_image->height / 2; row++) { + if (pos_y + row * 2 >= combine_image->height) { + heif_warning("[No-Error] height limited! tile_h: %u, pos_y: %u, comb_h: %u", + tile_image->height, pos_y, combine_image->height); + break; + } + if (total_copied_n[1] + copy_n > dst_size) { + heif_error("dest buffer overflow! total_copied_n: %zu, copy_size: %zu, dest_size: %zu", + total_copied_n[1], copy_n, dst_size); + break; + } + if (total_copied_n[2] + copy_n > dst_size) { + heif_error("dest buffer overflow! total_copied_n: %zu, copy_size: %zu, dest_size: %zu", + total_copied_n[2], copy_n, dst_size); + break; + } + memcpy(ptr_dst[1], ptr_src[1], copy_n); + memcpy(ptr_dst[2], ptr_src[2], copy_n); + + ptr_src[1] += tile_stride; + ptr_dst[1] += combine_stride; + total_copied_n[1] += copy_n; + ptr_src[2] += tile_stride; + ptr_dst[2] += combine_stride; + total_copied_n[2] += copy_n; + } + + return LIBHEIF_ERROR_NONE; +} + +static int __combine_tile_image(heif_image_t *tile_image, unsigned int pos_x, unsigned int pos_y, heif_image_t *combine_image) +{ + heif_retvm_if_failed(tile_image, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid tile_image"); + + switch (tile_image->format) { + case HEIF_COLOR_FORMAT_RGB24: + // fall through + case HEIF_COLOR_FORMAT_ARGB: + // fall through + case HEIF_COLOR_FORMAT_BGRA: + // fall through + case HEIF_COLOR_FORMAT_RGBA: + return __combine_rgb(tile_image, pos_x, pos_y, combine_image); + + case HEIF_COLOR_FORMAT_YUV420P: + return __combine_yuv(tile_image, pos_x, pos_y, combine_image); + + default: + heif_error("not support %u format yet", tile_image->format); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } +} + +static int __decode_grid_image(heif_itemtable_h item_table, heif_color_format_e format, heif_image_item_h image_item, heif_image_t **image) +{ + int ret = LIBHEIF_ERROR_NONE; + unsigned int rows = 0, columns = 0; + GSList *tiles, *iter; + heif_image_item_h tile_item; + heif_image_t *tile_image; + unsigned int image_index = 0; + unsigned int pos_x = 0, pos_y = 0; + heif_image_t *combine_image; + + heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item"); + heif_retvm_if_failed(image, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image"); + + ret = heif_itemtable_get_grid_info(image_item, &rows, &columns, &tiles); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_itemtable_get_grid_info fail (%d)", ret); + return ret; + } + + if (g_slist_length(tiles) < rows * columns) { + heif_error("tiles are not enough, invalid source"); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + // create output buffer to combine image + ret = __create_output_image(image_item, format, &combine_image); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__create_output_image fail (%d)", ret); + return ret; + } + + // decode and combine derived images + for (iter = tiles; iter; iter = g_slist_next(iter)) { + ret = heif_itemtable_get_image_item(item_table, GPOINTER_TO_UINT(iter->data), &tile_item); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_itemtable_get_image_item fail (%d)", ret); + heif_decoder_destroy_image(combine_image); + return ret; + } + + ret = __decode_single_image(item_table, format, tile_item, &tile_image); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__decode_single_image fail (%d)", ret); + heif_decoder_destroy_image(tile_image); + heif_decoder_destroy_image(combine_image); + return ret; + } + + heif_info("__decode_single_image(%u) succeeded.", GPOINTER_TO_UINT(iter->data)); + + // combine tile image + ret = __combine_tile_image(tile_image, pos_x, pos_y, combine_image); + + // calculate the position of next tile + if ((++image_index % columns) != 0) { + pos_x += tile_image->width; + } else { + pos_y += tile_image->height; + pos_x = 0; + } + heif_decoder_destroy_image(tile_image); + + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__combine_tile_image fail (%d)", ret); + heif_decoder_destroy_image(combine_image); + return ret; + } + heif_info("__combine_tile_image(%u) succeeded.", GPOINTER_TO_UINT(iter->data)); + } + + *image = combine_image; + + return LIBHEIF_ERROR_NONE; +} diff --git a/src/heif_extractor.c b/src/heif_extractor.c new file mode 100644 index 0000000..4dcf431 --- /dev/null +++ b/src/heif_extractor.c @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include "heif_type.h" +#include "heif_debug.h" +#include "heif_box_util.h" +#include "heif_itemtable.h" +#include "heif_extractor.h" + +typedef struct { + bool is_heic; + heif_source_h source; + heif_itemtable_h item_table; // the item's table after reading all metadata +} heif_extractor_t; + +static gint __extractor_compare_brand(gconstpointer data, gconstpointer user_data); +static int __extractor_parse_chunk(heif_extractor_t *extractor, off_t *offset, unsigned int depth); +static int __extractor_read_metadata(heif_extractor_t *extractor); +static int __extractor_read_brandset(heif_extractor_t *extractor, off_t offset, off_t chunk_data_size); + + +int heif_extractor_extract(heif_source_h source, heif_itemtable_h *item_table) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_extractor_t *_extractor = NULL; + + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); + heif_retvm_if_failed(item_table, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_table"); + + _extractor = g_new0(heif_extractor_t, 1); + _extractor->is_heic = false; + _extractor->item_table = NULL; + _extractor->source = source; + + ret = __extractor_read_metadata(_extractor); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__extractor_read_metadata fail %d", ret); + g_free(_extractor); + return ret; + } + + if (_extractor->is_heic) { + *item_table = _extractor->item_table; + } else { + heif_itemtable_destroy(_extractor->item_table); + ret = LIBHEIF_ERROR_INVALID_PARAMETER; + } + + g_free(_extractor); + + return ret; +} + +static gint __extractor_compare_brand(gconstpointer data, gconstpointer user_data) +{ + heif_retvm_if_failed(data, -1, "invalid data"); + heif_retvm_if_failed(user_data, -1, "invalid user_data"); + + uint32_t brand = (uint32_t)GPOINTER_TO_UINT(data); + uint32_t get_brand = (uint32_t)GPOINTER_TO_UINT(user_data); + + return (brand == get_brand) ? 0 : 1; +} + +static int __extractor_parse_chunk(heif_extractor_t *extractor, off_t *offset, unsigned int depth) +{ + int ret = LIBHEIF_ERROR_NONE; + uint64_t chunk_size = 0; + int32_t chunk_type = 0; + off_t data_offset = 0; + off_t chunk_data_size = 0; + off_t stop_offset = 0; + + heif_info("entering __extractor_parse_chunk %jd/%u", *offset, depth); + + heif_retvm_if_failed(extractor, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid extractor"); + heif_retvm_if_failed(offset && (*offset >= 0), LIBHEIF_ERROR_INVALID_PARAMETER, + "invalid offset %jd", offset ? *offset : 0); + heif_retvm_if_failed(depth <= 100, LIBHEIF_ERROR_INVALID_PARAMETER, "max depth of container was reached"); + + ret = _parse_hdr(extractor->source, offset, &chunk_size, &chunk_type, &data_offset); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("_parse_hdr fail %d", ret); + return ret; + } + + if (chunk_size == 1) { + if (heif_source_read_at(extractor->source, *offset + 8, &chunk_size, 8) != 8) + return LIBHEIF_ERROR_INVALID_PARAMETER; + + chunk_size = ntoh64(chunk_size); + data_offset += 8; + + if (chunk_size < 16) { + // The smallest valid chunk is 16 bytes long in this case. + heif_error("The smallest valid chunk is 16 bytes long in this case %"PRIu64, chunk_size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + } else if (chunk_size == 0) { + if (depth == 0) { + // atom extends to end of file + size_t source_size = heif_source_get_size(extractor->source); + if (source_size == 0) { + heif_error("invalid source_size %zu", source_size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + chunk_size = (source_size - *offset); + } else { + // not allowed for non-toplevel atoms, skip it + *offset += 4; + return LIBHEIF_ERROR_NONE; + } + } else if (chunk_size < 8) { + // The smallest valid chunk is 8 bytes long. + heif_error("invalid chunk size: %"PRIu64, chunk_size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + DEBUG_CHUNK_ALL("hdr", chunk_type, chunk_size, *offset, depth); + + // (data_offset - *offset) is either 8 or 16 + chunk_data_size = chunk_size - (data_offset - *offset); + heif_info("chunk_data_size: %jd", chunk_data_size); + if (chunk_data_size < 0) { + heif_error("invalid chunk size: %jd", chunk_data_size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + switch (chunk_type) { + case FOURCC('m', 'e', 't', 'a'): + // read 'meta' container at the end of 'meta', the usage of 'stop_offset' in library is the same. + // *offset: the start position for read + // chunk_size: the size of 'meta' container + // data_offset: the start position of data exclude chunk_type & chunk_size in 'meta' + stop_offset = *offset + chunk_size; + *offset = data_offset; + + while (*offset < stop_offset) { + ret = __extractor_parse_chunk(extractor, offset, depth + 1); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__extractor_parse_chunk fail %d", ret); + return ret; + } + } + + if (*offset != stop_offset) { + heif_error("offset did not advance till stop: %jd != %jd", *offset, stop_offset); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + break; + + case FOURCC('i', 'l', 'o', 'c'): + // fall through + case FOURCC('i', 'i', 'n', 'f'): + // fall through + case FOURCC('i', 'p', 'r', 'p'): + // fall through + case FOURCC('p', 'i', 't', 'm'): + // fall through + case FOURCC('i', 'd', 'a', 't'): + // fall through + case FOURCC('i', 'r', 'e', 'f'): + // fall through + case FOURCC('i', 'p', 'r', 'o'): + DEBUG_CHUNK_TYPE("top", chunk_type); + if (!extractor->is_heic) { + heif_error("invalid heic: %d", extractor->is_heic); + *offset += chunk_size; + break; + } + + if (!extractor->item_table) { + ret = heif_itemtable_create(&extractor->item_table, extractor->source); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_itemtable_create fail %d", ret); + return ret; + } + } + ret = heif_itemtable_parse(extractor->item_table, chunk_type, data_offset, chunk_data_size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_itemtable_parse fail %d", ret); + heif_itemtable_destroy(extractor->item_table); + extractor->item_table = NULL; + return ret; + } + + *offset += chunk_size; + break; + + case FOURCC('f', 't', 'y', 'p'): + heif_retvm_if_failed(chunk_data_size > 8, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid chunk_data_size: %jd", chunk_data_size); + heif_retvm_if_failed(depth == 0, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid 'ftyp' depth: %u", depth); + + stop_offset = *offset + chunk_size; + + ret = __extractor_read_brandset(extractor, data_offset, chunk_data_size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__extractor_read_brandset fail %d", ret); + break; + } + + *offset = stop_offset; + break; + + default: + DEBUG_CHUNK_TYPE("default", chunk_type); + *offset += chunk_size; + break; + } + + return ret; +} + +static int __extractor_read_metadata(heif_extractor_t *extractor) +{ + int ret = LIBHEIF_ERROR_NONE; + off_t offset = 0, orig_offset = 0; + + heif_debug_fenter(); + + heif_retvm_if_failed(extractor, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid extractor"); + + while (!(extractor->is_heic && extractor->item_table)) { + orig_offset = offset; + + ret = __extractor_parse_chunk(extractor, &offset, 0); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__extractor_parse_chunk fail %d", ret); + break; + } + + if (offset <= orig_offset) { + // only continue parsing if the offset was advanced, + // otherwise we might end up in an infinite loop + heif_error("offset did not advance: %jd->%jd", orig_offset, offset); + ret = LIBHEIF_ERROR_INVALID_PARAMETER; + break; + } + } + + heif_debug_fleave(); + + return ret; +} + +static int __extractor_read_brandset(heif_extractor_t *extractor, off_t offset, off_t chunk_data_size) +{ + uint32_t brand = 0; // value of brand + uint32_t brand_n = 0; // number of brand + GSList *brand_set = NULL; + GSList *mif1_brand = NULL, *heic_brand = NULL; + size_t i = 0; + + heif_retvm_if_failed(extractor, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid extractor"); + heif_retvm_if_failed(offset >= 0, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid offset"); + heif_retvm_if_failed(chunk_data_size >= 8, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid chunk_data_size"); + + brand_n = (chunk_data_size - 8) / 4; + + for (i = 0; i < brand_n + 2; ++i) { + if (i == 1) { + // Skip this index, it refers to the minorVersion, + // not a brand. + continue; + } + + if (heif_source_read_at(extractor->source, offset + 4 * i, &brand, 4) != 4) { + g_slist_free(brand_set); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + brand = ntohl(brand); + DEBUG_CHUNK_TYPE("brand", brand); + brand_set = g_slist_append(brand_set, GUINT_TO_POINTER(brand)); + } + + // Note: + // 'mif1': The source has a still image container. (ref. 'mifs': image sequence/multi-image) + // 'heic': The source has HEVC image compression container. + mif1_brand = g_slist_find_custom(brand_set, GUINT_TO_POINTER(FOURCC('m', 'i', 'f', '1')), __extractor_compare_brand); + heic_brand = g_slist_find_custom(brand_set, GUINT_TO_POINTER(FOURCC('h', 'e', 'i', 'c')), __extractor_compare_brand); + + if (mif1_brand && heic_brand) { + heif_info("identified heic image"); + extractor->is_heic = true; + } else { + heif_error("%s", (mif1_brand ? "source is not heic" : "source is not still image")); + } + + g_slist_free(brand_set); + + return LIBHEIF_ERROR_NONE; +} diff --git a/src/heif_itemtable.c b/src/heif_itemtable.c new file mode 100644 index 0000000..6eee72a --- /dev/null +++ b/src/heif_itemtable.c @@ -0,0 +1,1331 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "heif_type.h" +#include "heif_debug.h" +#include "heif_itemtable.h" + +#include "heif_box_util.h" +#include "heif_box_iloc.h" +#include "heif_box_iinf.h" +#include "heif_box_iprp.h" +#include "heif_box_pitm.h" +#include "heif_box_idat.h" +#include "heif_box_iref.h" + + +#define CHECK_HVCC_OFFSET(hvcC, offset) do { \ + if (((heif_buffer_t *)hvcC)->size < offset) { \ + heif_error("invalid hvcC size(%zu) or too large offset(%jd)", ((heif_buffer_t *)hvcC)->size, offset); \ + return false; \ + } \ + } while (0) \ + +#define START_CODE_LEN 4 + +typedef struct { + uint8_t completeness; + uint8_t unit_type; + heif_buffer_t nal_unit; +} _hvc_config_t; + +typedef struct { + // common + uint32_t type; // the item type of IINF, (e.g. hvc1, grid, exif) + uint32_t item_id; // the item id + off_t offset; // the offset of the item in source + size_t size; // the size of the item + + // for image(hvc1 or grid) item + bool hidden; // the displayable or not + bool is_grid; + int32_t width; // the width of the item + int32_t height; // the height of the item + int32_t rotation; // the rotation of the item + bool has_mirror; + int8_t axis; // the mirroring operation of the item + _hvc_config_t hvc_config; // nal unit created with hvcc by __get_hvc_config_from_hvcC() + heif_buffer_t icc; // the icc profile + + // for only grid + int32_t rows; // the number of tiles in row, only grid item has this value + int32_t columns; // the number of tiles in column, only grid item has this value + + // for ref + GSList *thumbnails; // item ids of thumbnail + GSList *dimg_refs; // item ids of tile + GSList *cdsc_refs; // item ids of content description(exif, xml) +} _image_item_t; + +typedef struct { + // validity + bool is_valid; + GSList *seen_types; // the container boxes read from source + GSList *require_types; // the mandatory container boxes list + + // source + heif_source_h source; + + // building image items + GSList *item_infos; + GSList *item_locs; + GSList *properties; + GSList *associations; + GSList *item_refs; + off_t idat_offset; + size_t idat_size; + + // datas after building image item tables + bool has_grid; + uint32_t primary_id; + GSList *displayables; // item id list, '0' index is primary item id + GSList *image_items; // _image_item_t list of hvc1 and grid + GSList *exif_items; // _image_item_t list of exif +} heif_itemtable_t; + +static const uint8_t START_CODE[START_CODE_LEN] = { 0, 0, 0, 1 }; // start_code is 0x00000001 + +static gint __compare_item_type(gconstpointer a, gconstpointer b); +static gint __compare_image_item_id(gconstpointer data, gconstpointer user_data); +static gint __compare_iloc_item_id(gconstpointer data, gconstpointer user_data); +static void __print_image_item(gpointer data, gpointer user_data); +static void __print_exif_item(gpointer data, gpointer user_data); +static bool __get_hvc_config_from_hvcC(_hvcC_prop_t *hvcC, _hvc_config_t *config); +static void __attach_property(_association_t *association, GSList *image_items, GSList *properties); +static void __attach_refs(_item_ref_t *item_ref, GSList *image_items, GSList *exif_items); +static bool __has_all_require_types(GSList *seen_types, GSList *require_types); +static int __build_image_if_possible(heif_itemtable_t *item_table); +static int __heif_itemtable_parse_iloc_box(heif_itemtable_t *item_table, off_t offset, size_t size); +static int __heif_itemtable_parse_iinf_box(heif_itemtable_t *item_table, off_t offset, size_t size); +static int __heif_itemtable_parse_iprp_box(heif_itemtable_t *item_table, off_t offset, size_t size); +static int __heif_itemtable_parse_pitm_box(heif_itemtable_t *item_table, off_t offset, size_t size); +static int __heif_itemtable_parse_idat_box(heif_itemtable_t *item_table, off_t offset, size_t size); +static int __heif_itemtable_parse_iref_box(heif_itemtable_t *item_table, off_t offset, size_t size); + + +int heif_itemtable_create(heif_itemtable_h *handle, heif_source_h source) +{ + heif_itemtable_t *new_itemtable = NULL; + + heif_retvm_if_failed(handle, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle"); + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); + + new_itemtable = g_new0(heif_itemtable_t, 1); + + // set as 'false' for clarity + new_itemtable->is_valid = false; + new_itemtable->source = source; + + // add minimum set of boxes to 'require_types'. + new_itemtable->require_types = g_slist_insert_sorted(new_itemtable->require_types, GUINT_TO_POINTER(FOURCC('i', 'p', 'r', 'p')), __compare_item_type); + new_itemtable->require_types = g_slist_insert_sorted(new_itemtable->require_types, GUINT_TO_POINTER(FOURCC('i', 'l', 'o', 'c')), __compare_item_type); + new_itemtable->require_types = g_slist_insert_sorted(new_itemtable->require_types, GUINT_TO_POINTER(FOURCC('p', 'i', 't', 'm')), __compare_item_type); + new_itemtable->require_types = g_slist_insert_sorted(new_itemtable->require_types, GUINT_TO_POINTER(FOURCC('i', 'i', 'n', 'f')), __compare_item_type); + + *handle = (heif_itemtable_h)new_itemtable; + + return LIBHEIF_ERROR_NONE; +} + +int heif_itemtable_parse(heif_itemtable_h handle, int32_t chunk_type, off_t data_offset, off_t chunk_data_size) +{ + switch (chunk_type) { + case FOURCC('i', 'l', 'o', 'c'): + return __heif_itemtable_parse_iloc_box((heif_itemtable_t *)handle, data_offset, chunk_data_size); + + case FOURCC('i', 'i', 'n', 'f'): + return __heif_itemtable_parse_iinf_box((heif_itemtable_t *)handle, data_offset, chunk_data_size); + + case FOURCC('i', 'p', 'r', 'p'): + return __heif_itemtable_parse_iprp_box((heif_itemtable_t *)handle, data_offset, chunk_data_size); + + case FOURCC('p', 'i', 't', 'm'): + return __heif_itemtable_parse_pitm_box((heif_itemtable_t *)handle, data_offset, chunk_data_size); + + case FOURCC('i', 'd', 'a', 't'): + return __heif_itemtable_parse_idat_box((heif_itemtable_t *)handle, data_offset, chunk_data_size); + + case FOURCC('i', 'r', 'e', 'f'): + return __heif_itemtable_parse_iref_box((heif_itemtable_t *)handle, data_offset, chunk_data_size); + + case FOURCC('i', 'p', 'r', 'o'): + heif_warning("ipro box not supported!"); + break; + + default: + DEBUG_CHUNK_TYPE("Unrecognized", chunk_type); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + return LIBHEIF_ERROR_NONE; +} + +bool heif_itemtable_is_valid(heif_itemtable_h handle) +{ + heif_retvm_if_failed(handle, false, "invalid handle"); + + return ((heif_itemtable_t *)handle)->is_valid; +} + +bool heif_itemtable_has_grid(heif_itemtable_h handle) +{ + heif_itemtable_t *item_table = (heif_itemtable_t *)handle; + + heif_retvm_if_failed(handle, false, "invalid handle"); + heif_retvm_if_failed(item_table->is_valid, false, "invalid handle"); + + return item_table->has_grid; +} + +int heif_itemtable_get_primary_image(heif_itemtable_h handle, unsigned int *primary_id) +{ + heif_itemtable_t *item_table = (heif_itemtable_t *)handle; + + heif_retvm_if_failed(handle, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle"); + heif_retvm_if_failed(item_table->is_valid, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle"); + heif_retvm_if_failed(item_table->displayables, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle"); + heif_retvm_if_failed(primary_id, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid primary_id"); + + *primary_id = (unsigned int)GPOINTER_TO_UINT(item_table->displayables->data); + + heif_secure_debug("primary_id (%u)", *primary_id); + + return LIBHEIF_ERROR_NONE; +} + +int heif_itemtable_get_image_item(heif_itemtable_h handle, unsigned int item_id, heif_image_item_h *image_item) +{ + heif_itemtable_t *item_table = (heif_itemtable_t *)handle; + GSList *found; + + heif_retvm_if_failed(handle, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle"); + heif_retvm_if_failed(item_table->is_valid, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle"); + heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item"); + + // find image_item + found = g_slist_find_custom(item_table->image_items, GUINT_TO_POINTER(item_id), __compare_image_item_id); + if (!found) { + heif_error("not exist item_id (%u)", item_id); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + *image_item = (heif_image_item_h)found->data; + + heif_secure_debug("item_id (%u)", ((_image_item_t *)found->data)->item_id); + + return LIBHEIF_ERROR_NONE; +} + +int heif_itemtable_get_thumb_item(heif_itemtable_h handle, heif_image_item_h *thumb_item) +{ + heif_itemtable_t *item_table = (heif_itemtable_t *)handle; + uint32_t primary_id; + _image_item_t *primary_item; + GSList *found; + + heif_retvm_if_failed(handle, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle"); + heif_retvm_if_failed(item_table->is_valid, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle"); + heif_retvm_if_failed(item_table->displayables, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle"); + heif_retvm_if_failed(thumb_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid thumb_item"); + + primary_id = (uint32_t)GPOINTER_TO_UINT(item_table->displayables->data); + + // find image_item + found = g_slist_find_custom(item_table->image_items, GUINT_TO_POINTER(primary_id), __compare_image_item_id); + if (!found) { + heif_error("not exist primary_id (%u)", primary_id); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + primary_item = (_image_item_t *)found->data; + if (!primary_item->thumbnails) { + heif_error("not exist thumbnail"); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + heif_secure_debug("thumb item_id (%u)", GPOINTER_TO_UINT(primary_item->thumbnails->data)); + + return heif_itemtable_get_image_item(handle, GPOINTER_TO_UINT(primary_item->thumbnails->data), thumb_item); +} + +int heif_itemtable_get_image_resolution(heif_image_item_h image_item, unsigned int *width, unsigned int *height) +{ + _image_item_t *_image_item = (_image_item_t *)image_item; + + heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item"); + heif_retvm_if_failed(width, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid width"); + heif_retvm_if_failed(height, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid height"); + + *width = _image_item->width; + *height = _image_item->height; + + heif_secure_debug("width: %u, height: %u", *width, *height); + + return LIBHEIF_ERROR_NONE; +} + +int heif_itemtable_get_image_orientation(heif_image_item_h image_item, unsigned int *orientation) +{ + _image_item_t *_image_item = (_image_item_t *)image_item; + + heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item"); + heif_retvm_if_failed(orientation, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid orientation"); + + *orientation = _image_item->rotation; + + heif_secure_debug("orientation: %u", *orientation); + + return LIBHEIF_ERROR_NONE; +} + +int heif_itemtable_get_image_mirror_mode(heif_image_item_h image_item, heif_mirror_mode_e *mirror) +{ + _image_item_t *_image_item = (_image_item_t *)image_item; + + heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item"); + heif_retvm_if_failed(mirror, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid mirror"); + + // initial value + *mirror = HEIF_MIRROR_NONE; + + if (_image_item->has_mirror) + *mirror = (_image_item->axis == 0) ? HEIF_MIRROR_VERTICAL : HEIF_MIRROR_HORIZONTAL; + + heif_secure_debug("mirror: %d", *mirror); + + return LIBHEIF_ERROR_NONE; +} + +int heif_itemtable_get_grid_info(heif_image_item_h image_item, unsigned int *row, unsigned int *column, GSList **img_refs) +{ + _image_item_t *_image_item = (_image_item_t *)image_item; + + heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item"); + heif_retvm_if_failed(_image_item->is_grid, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item"); + heif_retvm_if_failed(row, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid row"); + heif_retvm_if_failed(column, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid column"); + heif_retvm_if_failed(img_refs, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid img_refs"); + + *row = _image_item->rows; + *column = _image_item->columns; + *img_refs = _image_item->dimg_refs; + + return LIBHEIF_ERROR_NONE; +} + +int heif_itemtable_get_coded_data(heif_itemtable_h handle, heif_image_item_h image_item, heif_buffer_t **coded_data) +{ + _image_item_t *_image_item = (_image_item_t *)image_item; + heif_buffer_t *nal_unit = NULL; + heif_buffer_t *mdat = NULL; + size_t read_n = 0; + + heif_retvm_if_failed(handle, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle"); + heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item"); + heif_retvm_if_failed(coded_data, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid coded_data"); + + nal_unit = g_new0(heif_buffer_t, 1); + nal_unit->data = _image_item->hvc_config.nal_unit.data; + nal_unit->size = _image_item->hvc_config.nal_unit.size; + + if (!nal_unit->data || nal_unit->size == 0) { + heif_error("nal_unit: %p, nal_unit: %zu", nal_unit->data, nal_unit->size); + g_free(nal_unit); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } +#ifdef __DEBUG_CODEC_CONFIG + { + size_t i = 0; + for (i = 0; i < nal_unit->size; i++) + heif_error("nal_unit: %x", nal_unit->data[i]); + } +#endif + + mdat = g_new0(heif_buffer_t, 1); + mdat->size = nal_unit->size + _image_item->size; + mdat->data = (uint8_t *)g_malloc0(mdat->size); + + // attach nal_unit in front of media + memcpy(mdat->data, nal_unit->data, nal_unit->size); + memcpy(mdat->data + nal_unit->size, START_CODE, START_CODE_LEN); + + read_n = _image_item->size - START_CODE_LEN; + + // read media + if (heif_source_read_at(((heif_itemtable_t *)handle)->source, + _image_item->offset + START_CODE_LEN, + mdat->data + nal_unit->size + START_CODE_LEN, + read_n) != read_n) { + heif_error("heif_source_read_at fail"); + heif_buffer_destroy(mdat); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + *coded_data = mdat; + heif_secure_debug("heif_itemtable_get_coded_data success %p, %zu", (*coded_data)->data, (*coded_data)->size); + + return LIBHEIF_ERROR_NONE; +} + +int heif_itemtable_get_exif(heif_itemtable_h handle, heif_image_item_h image_item, void **exif, size_t *exif_size) +{ + _image_item_t *_image_item = (_image_item_t *)image_item; + _image_item_t *exif_item = NULL; + GSList *found = NULL; + uint8_t *_exif; + size_t _exif_size; + + heif_retvm_if_failed(handle, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle"); + heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item"); + heif_retvm_if_failed(exif, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid exif"); + heif_retvm_if_failed(exif_size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid exif_size"); + + heif_debug_fenter(); + + // find exif_item referenced by image_item + if (!_image_item->cdsc_refs) { + heif_error("not exist cdsc"); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + found = g_slist_find_custom(((heif_itemtable_t *)handle)->exif_items, GUINT_TO_POINTER(_image_item->cdsc_refs->data), __compare_image_item_id); + if (!found) { + heif_error("not exist item_id (%u)", GPOINTER_TO_UINT(_image_item->cdsc_refs->data)); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + exif_item = (_image_item_t *)found->data; + if (!exif_item) { + heif_error("not exist exif"); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + _exif_size = exif_item->size; + _exif = (uint8_t *)g_malloc0(_exif_size); + + heif_error("_exif: %p offset: %jd exif_size: %zu", _exif, exif_item->offset, exif_item->size); + + if (heif_source_read_at(((heif_itemtable_t *)handle)->source, exif_item->offset, _exif, _exif_size) != _exif_size) { + heif_error("heif_source_read_at fail"); + g_free(_exif); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + *exif = (void *)_exif; + *exif_size = _exif_size; + heif_secure_debug("heif_itemtable_get_exif success %p, %zu", *exif, *exif_size); + + heif_debug_fleave(); + + return LIBHEIF_ERROR_NONE; +} + +int heif_itemtable_get_icc_profile(heif_itemtable_h handle, heif_image_item_h image_item, void **icc, size_t *icc_size) +{ + _image_item_t *_image_item = (_image_item_t *)image_item; + + heif_retvm_if_failed(handle, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle"); + heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item"); + heif_retvm_if_failed(icc, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid icc"); + heif_retvm_if_failed(icc_size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid icc_size"); + + if (!_image_item->icc.data || (_image_item->icc.size == 0)) { + heif_error("not exist icc profile"); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + *icc = g_memdup(_image_item->icc.data, _image_item->icc.size); + *icc_size = _image_item->icc.size; + + return LIBHEIF_ERROR_NONE; +} + +// TODO: for only debug, this is no need for app developer. we can remove or replace it. +void heif_itemtable_print_info(heif_itemtable_h handle) +{ + heif_itemtable_t *item_table = (heif_itemtable_t *)handle; + + heif_retm_if_failed(handle); + + // print image & exif item in table(context) + g_slist_foreach(item_table->image_items, __print_image_item, NULL); + g_slist_foreach(item_table->exif_items, __print_exif_item, NULL); +} + +void heif_itemtable_destroy(heif_itemtable_h handle) +{ + heif_itemtable_t *item_table = (heif_itemtable_t *)handle; + + heif_retm_if_failed(handle); + + // release intermediate data + g_slist_free_full(item_table->item_infos, g_free); + g_slist_free_full(item_table->item_locs, _free_item_loc); + g_slist_free_full(item_table->properties, _free_image_prop); + g_slist_free_full(item_table->associations, g_free); + g_slist_free_full(item_table->item_refs, _free_item_ref); + + g_slist_free(item_table->seen_types); + g_slist_free(item_table->require_types); + + // release item table resource + g_slist_free(item_table->displayables); + g_slist_free_full(item_table->image_items, g_free); + g_slist_free_full(item_table->exif_items, g_free); + + g_free(handle); +} + +void heif_buffer_destroy(heif_buffer_t *buffer) +{ + heif_retm_if_failed(buffer); + + g_free(buffer->data); + g_free(buffer); +} + +static gint __compare_item_type(gconstpointer a, gconstpointer b) +{ + uint32_t item_type = (uint32_t)GPOINTER_TO_UINT(a); + uint32_t get_type = (uint32_t)GPOINTER_TO_UINT(b); + + if (item_type > get_type) + return 1; + + if (item_type < get_type) + return -1; + + return 0; +} + +static gint __compare_image_item_id(gconstpointer data, gconstpointer user_data) +{ + _image_item_t *image_item = (_image_item_t *)data; + uint32_t get_id = (uint32_t)GPOINTER_TO_UINT(user_data); + + heif_retvm_if_failed(image_item, 0, "invalid data"); + + return (image_item->item_id == get_id) ? 0 : 1; +} + +static gint __compare_iloc_item_id(gconstpointer data, gconstpointer user_data) +{ + _item_loc_t *item_loc = (_item_loc_t *)data; + uint32_t get_id = (uint32_t)GPOINTER_TO_UINT(user_data); + + heif_retvm_if_failed(item_loc, 0, "invalid data"); + + return (item_loc->item_id == get_id) ? 0 : 1; +} + +static void __print_ref_item(gpointer data, gpointer user_data) +{ + uint32_t item_id = (uint32_t)GPOINTER_TO_UINT(data); + char *msg = (char *)user_data; + + heif_info("%s : %u", msg, item_id); +} + +// TODO: for only debug, this is no need for app developer. we can remove or replace it. +static void __print_image_item(gpointer data, gpointer user_data) +{ + _image_item_t *image_item = (_image_item_t *)data; + const char *type = image_item->type == FOURCC('g', 'r', 'i', 'd') ? "GRID" : "IMAGE"; + + heif_retm_if_failed(data); + + heif_info("=============== [%s/%u] ==============", type, image_item->item_id); + + heif_info("hidden : %s", image_item->hidden ? "hidden" : "displayable (primary)"); + if (image_item->is_grid) { + heif_info("rows : %d", image_item->rows); + heif_info("columns : %d", image_item->columns); + } + heif_info("-----------------------------"); + heif_info("width : %d", image_item->width); + heif_info("height : %d", image_item->height); + heif_info("rotation : %d", image_item->rotation * 90); + heif_info("-----------------------------"); + + heif_info("offset : %jd", image_item->offset); + heif_info("size : %zu", image_item->size); + + heif_info("-----------------------------"); + if (image_item->thumbnails) + g_slist_foreach(image_item->thumbnails, __print_ref_item, "thumbnail"); + else + heif_info("thumbnail : none"); + + heif_info("-----------------------------"); + if (image_item->dimg_refs) + g_slist_foreach(image_item->dimg_refs, __print_ref_item, "dimg_refs"); + else + heif_info("dimg_refs : none"); + + heif_info("-----------------------------"); + if (image_item->cdsc_refs) + g_slist_foreach(image_item->cdsc_refs, __print_ref_item, "cdsc_refs"); + else + heif_info("cdsc_refs : none"); + +#ifdef __DEBUG_CODEC_CONFIG + heif_info("-----------------------------"); + { + size_t i = 0; + if (image_item->hvc_config.nal_unit.data && image_item->hvc_config.nal_unit.size > 0) { + heif_info("nal_unit.size: %zu", image_item->hvc_config.nal_unit.size); + for (i = 0; i < image_item->hvc_config.nal_unit.size; i++) + heif_info("nal_unit: %x", image_item->hvc_config.nal_unit.data[i]); + } + } +#endif + + heif_info("=============== [%s] End ==============\n", type); +} + +// TODO: for only debug, this is no need for app developer. we can remove or replace it. +static void __print_exif_item(gpointer data, gpointer user_data) +{ + _image_item_t *image_item = (_image_item_t *)data; + + heif_retm_if_failed(data); + + heif_info("=============== [EXIF/%u] ==============", image_item->item_id); + + heif_info("offset : %jd", image_item->offset); + heif_info("size : %zu", image_item->size); + + heif_info("=============== [EXIF] End ==============\n"); +} + +// convert property to nal unit +static bool __get_hvc_config_from_hvcC(_hvcC_prop_t *hvcC, _hvc_config_t *config) +{ + off_t hvcc_offset = 22; // nal start with 22 + off_t total_data_size = 0; + + uint8_t config_n = 0; + uint16_t unit_n = 0; + uint16_t unit_size = 0; + uint16_t cfg_idx = 0, unit_idx = 0; + +#ifdef __DEBUG_CODEC_CONFIG + heif_info("get nal unit from hvcC property size: %zu", hvcC->size); +#endif + + heif_retvm_if_failed(hvcC, false , "invalid hvcC"); + heif_retvm_if_failed(hvcC->data && hvcC->size > 22, false , "invalid hvcC"); + heif_retvm_if_failed(config, false , "invalid config"); + + config_n = (uint8_t)hvcC->data[hvcc_offset]; + hvcc_offset += 1; +#ifdef __DEBUG_CODEC_CONFIG + heif_info("read config_n: %u", config_n); +#endif + + for (cfg_idx = 0; cfg_idx < config_n; cfg_idx++) { +#ifdef __DEBUG_CODEC_CONFIG + heif_info("read config: %x", hvcC->data[hvcc_offset]); +#endif + + CHECK_HVCC_OFFSET(hvcC, hvcc_offset + 1); + config->completeness = (hvcC->data[hvcc_offset] >> 6) & 1; + config->unit_type = (hvcC->data[hvcc_offset] & 0x3F); + hvcc_offset += 1; + + CHECK_HVCC_OFFSET(hvcC, hvcc_offset + 2); + unit_n = (((uint8_t)hvcC->data[hvcc_offset]) << 8) + ((uint8_t)hvcC->data[hvcc_offset + 1]); + hvcc_offset += 2; + +#ifdef __DEBUG_CODEC_CONFIG + heif_info("read unit_n: %u", unit_n); +#endif + + for (unit_idx = 0; unit_idx < unit_n; unit_idx++) { + CHECK_HVCC_OFFSET(hvcC, hvcc_offset + 2); + unit_size = (((uint8_t)hvcC->data[hvcc_offset]) << 8) + ((uint8_t)hvcC->data[hvcc_offset + 1]); + hvcc_offset += 2; + +#ifdef __DEBUG_CODEC_CONFIG + heif_info("read unit_size: %u", unit_size); +#endif + + if (unit_size == 0) + continue; + + CHECK_HVCC_OFFSET(hvcC, hvcc_offset + unit_size); + if (config->nal_unit.data) + config->nal_unit.data = g_realloc(config->nal_unit.data, total_data_size + START_CODE_LEN + unit_size); + else + config->nal_unit.data = g_malloc0(START_CODE_LEN + unit_size); + + memcpy(config->nal_unit.data + total_data_size, START_CODE, START_CODE_LEN); + total_data_size += START_CODE_LEN; + memcpy(config->nal_unit.data + total_data_size, hvcC->data + hvcc_offset, unit_size); + total_data_size += unit_size; + + hvcc_offset += unit_size; + } + } + + config->nal_unit.size = total_data_size; + + return true; +} + +static void __attach_property(_association_t *association, GSList *image_items, GSList *properties) +{ + GSList *found = NULL; + uint16_t property_index = 0; + _image_item_t *image_item = NULL; + _item_prop_t *item_prop = NULL; +#ifdef __DEBUG_CODEC_CONFIG + size_t i = 0; +#endif + + heif_retm_if_failed(association); + heif_retm_if_failed(image_items); + heif_retm_if_failed(properties); + + // ignore non-image items + found = g_slist_find_custom(image_items, GUINT_TO_POINTER(association->item_id), __compare_image_item_id); + if (!found) { + heif_warning("ignoring non-image items!"); + return; + } + + // property index starts with 1 + property_index = association->index; + // check underflow + if (property_index < 1) { + heif_warning("ignoring invalid property index %u", property_index); + return; + } + // check overflow + if (property_index > g_slist_length(properties)) { + heif_warning("ignoring invalid property index %u, list_length %u", property_index, g_slist_length(properties)); + return; + } + +#ifdef __ENABLE_DEBUG_MODE + heif_info("attach property_index %u to item id %u", property_index, association->item_id); +#endif + + image_item = (_image_item_t *)found->data; + // The index of 'properties' starts with 0, but 'property_index' starts with 1. + // so '-1' needs for the difference between 'property_index' and the index of 'properties'. + item_prop = (_item_prop_t *)g_slist_nth_data(properties, property_index - 1); + + // ignore non-image items + if (!item_prop || !item_prop->property) { + heif_warning("ignoring invalid property index %u", property_index); + return; + } + + DEBUG_CHUNK_TYPE("property", item_prop->type); + + switch (item_prop->type) { + case FOURCC('h', 'v', 'c', 'C'): + // get nal_unit for hevc decoder + if (!__get_hvc_config_from_hvcC((_hvcC_prop_t *)item_prop->property, &image_item->hvc_config)) + heif_error("__get_hvc_config_from_hvcC failed"); + +#ifdef __DEBUG_CODEC_CONFIG + if (image_item->hvc_config.nal_unit.data && image_item->hvc_config.nal_unit.size > 0) { + for (i = 0; i < image_item->hvc_config.nal_unit.size; i++) + heif_info("nal_unit: %x", image_item->hvc_config.nal_unit.data[i]); + } +#endif + break; + + case FOURCC('i', 's', 'p', 'e'): + image_item->width = ((_ispe_prop_t *)item_prop->property)->width; + image_item->height = ((_ispe_prop_t *)item_prop->property)->height; + break; + + case FOURCC('i', 'r', 'o', 't'): + image_item->rotation = ((_irot_prop_t *)item_prop->property)->angle; + break; + + case FOURCC('c', 'o', 'l', 'r'): + image_item->icc.data = ((_colr_prop_t *)item_prop->property)->icc_data; + image_item->icc.size = ((_colr_prop_t *)item_prop->property)->size; + break; + + case FOURCC('i', 'm', 'i', 'r'): + // vertial(axis = 0) or horizontal(axis = 1) in property + image_item->has_mirror = true; + image_item->axis = ((_imir_prop_t *)item_prop->property)->axis; + break; + + default: + break; + } +} + +static void __attach_refs(_item_ref_t *item_ref, GSList *image_items, GSList *exif_items) +{ + GSList *iter = NULL, *found = NULL; + _image_item_t *ref_item = NULL; + + heif_info("attach reference type 0x%x to item id %u", item_ref->item_type, item_ref->item_id); + + heif_retm_if_failed(item_ref); + heif_retm_if_failed(image_items); + + switch (item_ref->item_type) { + case FOURCC('d', 'i', 'm', 'g'): + found = g_slist_find_custom(image_items, GUINT_TO_POINTER(item_ref->item_id), __compare_image_item_id); + + // ignore non-image items + if (!found) { + heif_warning("ignoring non-image items!"); + return; + } + + _image_item_t *derived_image = (_image_item_t *)found->data; + if (!derived_image->dimg_refs) + heif_warning("dimg_refs not clean!"); + + derived_image->dimg_refs = g_slist_copy(item_ref->refs); + + // hide reference sources + for (iter = item_ref->refs; iter; iter = g_slist_next(iter)) { + found = g_slist_find_custom(image_items, GUINT_TO_POINTER(iter->data), __compare_image_item_id); + + // ignore non-image items + if (!found) { + heif_warning("ignoring non-image items!"); + continue; + } + heif_info("Item(%u) is the derived image of item(%u)", GPOINTER_TO_UINT(iter->data), item_ref->item_id); + ref_item = (_image_item_t *)found->data; + + // mark the derived image as hidden + ref_item->hidden = true; + } + break; + + case FOURCC('t', 'h', 'm', 'b'): + found = g_slist_find_custom(image_items, GUINT_TO_POINTER(item_ref->item_id), __compare_image_item_id); + + // ignore non-image items + if (!found) { + heif_warning("ignoring non-image items!"); + return; + } + + // mark thumbnail image as hidden, these can be retrieved if the client + // request thumbnail explicitly, but won't be exposed as displayables. + _image_item_t *thumb_image = (_image_item_t *)found->data; + thumb_image->hidden = true; + + for (iter = item_ref->refs; iter; iter = g_slist_next(iter)) { + found = g_slist_find_custom(image_items, GUINT_TO_POINTER(iter->data), __compare_image_item_id); + + // ignore non-image items + if (!found) { + heif_warning("ignoring non-image items!"); + continue; + } + heif_info("Item(%u) is the thumbnail of item(%u)", item_ref->item_id, GPOINTER_TO_UINT(iter->data)); + ref_item = (_image_item_t *)found->data; + if (g_slist_length(ref_item->thumbnails) > 0) + heif_warning("already has thumbnails!"); + + ref_item->thumbnails = g_slist_append(ref_item->thumbnails, GUINT_TO_POINTER(item_ref->item_id)); + } + break; + + case FOURCC('c', 'd', 's', 'c'): + heif_retm_if_failed(exif_items); + + found = g_slist_find_custom(exif_items, GUINT_TO_POINTER(item_ref->item_id), __compare_image_item_id); + + // ignore non-exif block items + if (!found) { + heif_warning("ignoring non-exif items!"); + return; + } + + for (iter = item_ref->refs; iter; iter = g_slist_next(iter)) { + found = g_slist_find_custom(image_items, GUINT_TO_POINTER(iter->data), __compare_image_item_id); + + // ignore non-image items + if (!found) { + heif_warning("ignoring non-image items!"); + continue; + } + heif_info("Item(%u) is the Content-Description of item(%u)", item_ref->item_id, GPOINTER_TO_UINT(iter->data)); + ref_item = (_image_item_t *)found->data; + + ref_item->cdsc_refs = g_slist_append(ref_item->cdsc_refs, GUINT_TO_POINTER(item_ref->item_id)); + } + break; + + case FOURCC('a', 'u', 'x', 'l'): + found = g_slist_find_custom(image_items, GUINT_TO_POINTER(item_ref->item_id), __compare_image_item_id); + + // ignore non-image block items + if (!found) { + heif_warning("ignoring non-image items!"); + return; + } + + // mark the auxiliary image as hidden + ref_item = (_image_item_t *)found->data; + ref_item->hidden = true; + break; + + default: + heif_warning("ignoring unsupported ref type 0x%x", item_ref->item_type); + break; + } +} + +static bool __has_all_require_types(GSList *seen_types, GSList *require_types) +{ + GSList *seen_iter = NULL, *require_iter = NULL; + + heif_retvm_if_failed(seen_types, false, "invalid seen_types"); + heif_retvm_if_failed(require_types, false, "invalid require_types"); + + seen_iter = seen_types; + require_iter = require_types; + + // cond. Both seen_types and require_types are sorted. + for (; seen_iter && require_iter; seen_iter = g_slist_next(seen_iter)) { + uint32_t seen_type = (uint32_t)GPOINTER_TO_UINT(seen_iter->data); + uint32_t require_type = (uint32_t)GPOINTER_TO_UINT(require_iter->data); + + if (seen_type == require_type) + require_iter = g_slist_next(require_iter); + } + + // need at least 'iprp', 'iloc', 'pitm', 'iinf'; + // need 'idat' if any items used construction_method of 2; + // need 'iref' if source has 'grid' item. + if (require_iter) { + DEBUG_CHUNK_TYPE("Not Found!", (uint32_t)GPOINTER_TO_UINT(require_iter->data)); + return false; + } + + return true; +} + +// build image items +static int __build_image_if_possible(heif_itemtable_t *item_table) +{ + int ret = LIBHEIF_ERROR_NONE; + GSList *iter = NULL; + GSList *found = NULL; // for check duplicate or exist, so it is reused for some lists. + _image_item_t *image_item; + off_t offset = 0; + size_t size = 0; + + if (item_table->is_valid) { + heif_warning("already item_table has been built!"); + return LIBHEIF_ERROR_NONE; + } + + if (!__has_all_require_types(item_table->seen_types, item_table->require_types)) + return LIBHEIF_ERROR_NONE; + + heif_warning("building image table..."); + + // insert image items + for (iter = item_table->item_infos; iter; iter = g_slist_next(iter)) { + _item_info_t *item_info = (_item_info_t *)iter->data; + + // Only handle 3 types of items, all others are ignored: + // 'grid': derived image from tiles + // 'hvc1': coded image (or tile) + // 'Exif': EXIF metadata + if (item_info->item_type != FOURCC('g', 'r', 'i', 'd') && + item_info->item_type != FOURCC('h', 'v', 'c', '1') && + item_info->item_type != FOURCC('E', 'x', 'i', 'f')) { + continue; + } + + DEBUG_CHUNK_TYPE("Build/ItemType", item_info->item_type); + + found = g_slist_find_custom(item_table->image_items, GUINT_TO_POINTER(item_info->item_id), __compare_image_item_id); + if (found) { + heif_warning("ignoring duplicate image item id %u", item_info->item_id); + continue; + } + + found = g_slist_find_custom(item_table->item_locs, GUINT_TO_POINTER(item_info->item_id), __compare_iloc_item_id); + if (!found) { + heif_warning("iloc missing for image item id %u", item_info->item_id); + continue; + } + + // get location from iloc box + ret = _get_item_loc((_item_loc_t*)found->data, &offset, &size, item_table->idat_offset, item_table->idat_size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("location of item was missed %d", ret); + return ret; + } + + if (item_info->item_type == FOURCC('E', 'x', 'i', 'f')) { + // Only add if the Exif data is non-empty. The first 4 bytes contain + // the offset to TIFF header, which the Exif parser doesn't use. + if (size > 4) { + heif_info("adding Exif information"); + + _image_item_t *exif_item = g_new0(_image_item_t, 1); + exif_item->type = item_info->item_type; + exif_item->item_id = item_info->item_id; + // just keep offset and size, no read exif immediately + exif_item->offset = offset; + exif_item->size = size; + + item_table->exif_items = g_slist_append(item_table->exif_items, exif_item); + } + continue; + } + + image_item = g_new0(_image_item_t, 1); + image_item->type = item_info->item_type; + image_item->item_id = item_info->item_id; + image_item->hidden = item_info->hidden; + image_item->is_grid = (image_item->type == FOURCC('g', 'r', 'i', 'd')); + +#ifdef __ENABLE_DEBUG_MODE + heif_info("adding %s: item_id %u", image_item->is_grid ? "grid" : "image", image_item->item_id); +#endif + + if (image_item->is_grid) { + // ImageGrid struct is at least 8-byte, at most 12-byte (if flags&1) + if (size < 8 || size > 12) { + g_free(image_item); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + uint8_t buf[12]; + if (heif_source_read_at(item_table->source, offset, buf, size) != size) { + g_free(image_item); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + image_item->rows = buf[2] + 1; + image_item->columns = buf[3] + 1; + + item_table->has_grid = true; + heif_info("rows %d, columns %d", image_item->rows, image_item->columns); + } else { + image_item->offset = offset; + image_item->size = size; + } + + item_table->image_items = g_slist_append(item_table->image_items, image_item); + } + + // attach property(width&height/hvcC/rotation...) for each image item + for (iter = item_table->associations; iter; iter = g_slist_next(iter)) + __attach_property((_association_t *)iter->data, item_table->image_items, item_table->properties); + + // attach reference(grid/exif/thumb) for each image item + for (iter = item_table->item_refs; iter; iter = g_slist_next(iter)) + __attach_refs((_item_ref_t *)iter->data, item_table->image_items, item_table->exif_items); + + // check primary image + found = g_slist_find_custom(item_table->image_items, GUINT_TO_POINTER(item_table->primary_id), __compare_image_item_id); + if (!found) { + heif_error("can't find item of primary id"); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + item_table->displayables = g_slist_append(item_table->displayables, GUINT_TO_POINTER(item_table->primary_id)); + + heif_info("found %u displayables", g_slist_length(item_table->displayables)); + + // fail if no displayables + if (!item_table->displayables) { + heif_error("can't find displayable item"); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + item_table->is_valid = true; + + return LIBHEIF_ERROR_NONE; +} + +static int __heif_itemtable_parse_iloc_box(heif_itemtable_t *item_table, off_t offset, size_t size) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_box_iloc_h iloc_box = NULL; + heif_info("offset %jd, size %zu", offset, size); + + heif_retvm_if_failed(item_table, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_table"); + + ret = heif_box_iloc_create(&iloc_box); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_iloc_create fail (%d)", ret); + return ret; + } + + ret = heif_box_iloc_parse(iloc_box, item_table->source, offset, size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_iloc_parse fail (%d)", ret); + goto END; + } + + item_table->seen_types = g_slist_insert_sorted(item_table->seen_types, GUINT_TO_POINTER(FOURCC('i', 'l', 'o', 'c')), __compare_item_type); + + ret = heif_box_iloc_get_item_locs(iloc_box, &item_table->item_locs); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_iloc_get_item_locs fail (%d)", ret); + goto END; + } + + if (heif_box_iloc_has_construction_method(iloc_box)) + item_table->require_types = g_slist_insert_sorted(item_table->require_types, GUINT_TO_POINTER(FOURCC('i', 'd', 'a', 't')), __compare_item_type); + +END: +#ifdef __ENABLE_DEBUG_MODE + heif_box_iloc_info(iloc_box); +#endif + heif_box_iloc_destroy(iloc_box); + iloc_box = NULL; + + if (ret != LIBHEIF_ERROR_NONE) + return ret; + + return __build_image_if_possible(item_table); +} + +static int __heif_itemtable_parse_iinf_box(heif_itemtable_t *item_table, off_t offset, size_t size) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_box_iinf_h iinf_box = NULL; + heif_info("offset %jd, size %zu", offset, size); + + heif_retvm_if_failed(item_table, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_table"); + + ret = heif_box_iinf_create(&iinf_box); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_iinf_create fail (%d)", ret); + return ret; + } + + ret = heif_box_iinf_parse(iinf_box, item_table->source, offset, size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_iinf_parse fail (%d)", ret); + goto END; + } else { + item_table->seen_types = g_slist_insert_sorted(item_table->seen_types, GUINT_TO_POINTER(FOURCC('i', 'i', 'n', 'f')), __compare_item_type); + } + + ret = heif_box_iinf_get_item_infos(iinf_box, &item_table->item_infos); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_iinf_get_item_infos fail (%d)", ret); + goto END; + } + + if (heif_box_iinf_has_type(iinf_box, FOURCC('g', 'r', 'i', 'd')) || + heif_box_iinf_has_type(iinf_box, FOURCC('E', 'x', 'i', 'f'))) { + item_table->require_types = g_slist_insert_sorted(item_table->require_types, GUINT_TO_POINTER(FOURCC('i', 'r', 'e', 'f')), __compare_item_type); + } + +END: +#ifdef __ENABLE_DEBUG_MODE + heif_box_iinf_info(iinf_box); +#endif + heif_box_iinf_destroy(iinf_box); + iinf_box = NULL; + + if (ret != LIBHEIF_ERROR_NONE) + return ret; + + return __build_image_if_possible(item_table); +} + +static int __heif_itemtable_parse_iprp_box(heif_itemtable_t *item_table, off_t offset, size_t size) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_box_iprp_h iprp_box; + heif_info("offset %jd, size %zu", offset, size); + + heif_retvm_if_failed(item_table, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_table"); + + ret = heif_box_iprp_create(&iprp_box); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_iprp_create fail (%d)", ret); + return ret; + } + + ret = heif_box_iprp_parse(iprp_box, item_table->source, offset, size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_iprp_parse fail (%d)", ret); + goto END; + } else { + item_table->seen_types = g_slist_insert_sorted(item_table->seen_types, GUINT_TO_POINTER(FOURCC('i', 'p', 'r', 'p')), __compare_item_type); + } + + ret = heif_box_iprp_get_properties(iprp_box, &item_table->properties); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_iprp_get_properties fail (%d)", ret); + goto END; + } + + ret = heif_box_iprp_get_associations(iprp_box, &item_table->associations); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_iprp_get_associations fail (%d)", ret); + goto END; + } + +END: +#ifdef __ENABLE_DEBUG_MODE + heif_box_iprp_info(iprp_box); +#endif + heif_box_iprp_destroy(iprp_box); + iprp_box = NULL; + + if (ret != LIBHEIF_ERROR_NONE) + return ret; + + return __build_image_if_possible(item_table); +} + +static int __heif_itemtable_parse_pitm_box(heif_itemtable_t *item_table, off_t offset, size_t size) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_box_iprp_h pitm_box = NULL; + heif_info("offset %jd, size %zu", offset, size); + + heif_retvm_if_failed(item_table, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_table"); + + ret = heif_box_pitm_create(&pitm_box); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_pitm_create fail (%d)", ret); + return ret; + } + + ret = heif_box_pitm_parse(pitm_box, item_table->source, offset, size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_pitm_parse fail (%d)", ret); + goto END; + } + + if (heif_box_pitm_get_primary_id(pitm_box, &item_table->primary_id) != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_pitm_get_primary_id fail (%d)", ret); + goto END; + } + + item_table->seen_types = g_slist_insert_sorted(item_table->seen_types, GUINT_TO_POINTER(FOURCC('p', 'i', 't', 'm')), __compare_item_type); + +END: +#ifdef __ENABLE_DEBUG_MODE + heif_box_pitm_info(pitm_box); +#endif + heif_box_pitm_destroy(pitm_box); + pitm_box = NULL; + + if (ret != LIBHEIF_ERROR_NONE) + return ret; + + return __build_image_if_possible(item_table); +} + +static int __heif_itemtable_parse_idat_box(heif_itemtable_t *item_table, off_t offset, size_t size) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_box_idat_h idat_box = NULL; + heif_info("offset %jd, size %zu", offset, size); + + heif_retvm_if_failed(item_table, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_table"); + + ret = heif_box_idat_create(&idat_box); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_idat_create fail (%d)", ret); + return ret; + } + + ret = heif_box_idat_parse(idat_box, offset, size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_idat_parse fail (%d)", ret); + goto END; + } + + item_table->idat_offset = 0; + item_table->idat_size = 0; + ret = heif_box_idat_get_idat(idat_box, &item_table->idat_offset, &item_table->idat_size); + // because idat is not mandatory, just warning and no return + if (ret != LIBHEIF_ERROR_NONE) + heif_warning("idat was missed"); + + item_table->seen_types = g_slist_insert_sorted(item_table->seen_types, GUINT_TO_POINTER(FOURCC('i', 'd', 'a', 't')), __compare_item_type); + +END: +#ifdef __ENABLE_DEBUG_MODE + heif_box_idat_info(idat_box); +#endif + heif_box_idat_destroy(idat_box); + idat_box = NULL; + + if (ret != LIBHEIF_ERROR_NONE) + return ret; + + return __build_image_if_possible(item_table); +} + +static int __heif_itemtable_parse_iref_box(heif_itemtable_t *item_table, off_t offset, size_t size) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_box_iref_h iref_box = NULL; + heif_info("offset %jd, size %zu", offset, size); + + heif_retvm_if_failed(item_table, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_table"); + + ret = heif_box_iref_create(&iref_box); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_iref_create fail (%d)", ret); + return ret; + } + + ret = heif_box_iref_parse(iref_box, item_table->source, offset, size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_iref_parse fail (%d)", ret); + goto END; + } + + ret = heif_box_iref_get_item_refs(iref_box, &item_table->item_refs); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("heif_box_iref_get_item_refs fail (%d)", ret); + goto END; + } + + item_table->seen_types = g_slist_insert_sorted(item_table->seen_types, GUINT_TO_POINTER(FOURCC('i', 'r', 'e', 'f')), __compare_item_type); + +END: +#ifdef __ENABLE_DEBUG_MODE + heif_box_iref_info(iref_box); +#endif + heif_box_iref_destroy(iref_box); + iref_box = NULL; + + if (ret != LIBHEIF_ERROR_NONE) + return ret; + + return __build_image_if_possible(item_table); +} diff --git a/src/heif_source.c b/src/heif_source.c new file mode 100644 index 0000000..99649a7 --- /dev/null +++ b/src/heif_source.c @@ -0,0 +1,390 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "heif_type.h" +#include "heif_debug.h" +#include "heif_source.h" + +typedef struct { + char *path; + int fd; + uint8_t *buf; + size_t size; + off_t cur_pos; +} heif_source_t; + +static int __source_get_file_size(const char *path, size_t *size); +static int __source_set_path(heif_source_t *source, const char *path); +static int __source_set_buffer(heif_source_t *source, unsigned char *buf, size_t size); +static void __source_free(heif_source_t *source); + + +int heif_source_create_from_file(const char *path, heif_source_h *source) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_source_t *new_source = NULL; + + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); +#ifdef __DEBUG_SOURCE + heif_secure_debug("path: %s", path); +#endif + + new_source = g_new0(heif_source_t, 1); + + ret = __source_set_path(new_source, path); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__source_set_path fail %d", ret); + g_free(new_source); + return ret; + } + + *source = (heif_source_h)new_source; + + return LIBHEIF_ERROR_NONE; +} + +int heif_source_create_from_buffer(unsigned char *buf, size_t size, heif_source_h *source) +{ + int ret = LIBHEIF_ERROR_NONE; + heif_source_t *new_source = NULL; + + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); +#ifdef __DEBUG_SOURCE + heif_info("buf: %p buf_size: %zu", buf, size); +#endif + + new_source = g_new0(heif_source_t, 1); + + ret = __source_set_buffer(new_source, buf, size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__source_set_buffer fail %d", ret); + g_free(new_source); + return ret; + } + + *source = (heif_source_h)new_source; + + return LIBHEIF_ERROR_NONE; +} + +size_t heif_source_get_size(heif_source_h source) +{ + heif_retvm_if_failed(source, 0, "invalid source"); + + return ((heif_source_t *)source)->size; +} + +size_t heif_source_read_at(heif_source_h source, off_t offset, void *data, size_t size) +{ + size_t read_n = 0; + heif_source_t *_src = (heif_source_t *)source; + + heif_retvm_if_failed(source, 0, "invalid source"); + heif_retvm_if_failed(_src->path || _src->buf, 0, "invalid source data"); + heif_retvm_if_failed(_src->size > 0, 0, "invalid source size"); + heif_retvm_if_failed(data, 0, "invalid data"); + heif_retvm_if_failed(size > 0, 0, "invalid size"); + + // check offset advance + // if it needs to check underflow, we can use '(_src->cur_pos + offset) < 0' + // checking offset advance is more efficient because '_src->cur_pos' is always >= 0. + heif_retvm_if_failed(offset >= 0, 0, "invalid offset: %jd", offset); + + // check 'offset + size' overflow + heif_retvm_if_failed(offset + size <= _src->size, 0, "invalid offset: %jd, size: %zu", offset, size); + +#ifdef __DEBUG_SOURCE + heif_info("offset: %jd cur_pos: %jd size: %zu", offset, _src->cur_pos, size); +#endif + + if (_src->path) { + if (lseek(_src->fd, offset - _src->cur_pos, SEEK_CUR) < 0) { + heif_strerror("lseek"); + return 0; + } + + read_n = read(_src->fd, data, size); + if (read_n != size) { + heif_strerror("read"); + return 0; + } + } else { + memcpy(data, _src->buf + offset, size); + read_n = size; + } + + _src->cur_pos = offset + size; +#ifdef __DEBUG_SOURCE + heif_info("updated read_at cur_pos: %jd", _src->cur_pos); +#endif + + return read_n; +} + +void heif_source_destroy(heif_source_h source) +{ + __source_free((heif_source_t *)source); + + g_free(source); +} + +// Convenience functions: +bool heif_source_get_uint16(heif_source_h source, off_t offset, uint16_t *x) +{ + uint8_t byte[2]; + + heif_retvm_if_failed(x, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid x"); + + *x = 0; + + if (heif_source_read_at(source, offset, byte, 2) != 2) + return false; + + *x = (byte[0] << 8) | byte[1]; + + return true; +} + +// 3 byte int, returned as a 32-bit int +bool heif_source_get_uint24(heif_source_h source, off_t offset, uint32_t *x) +{ + uint8_t byte[3]; + + heif_retvm_if_failed(x, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid x"); + + *x = 0; + + if (heif_source_read_at(source, offset, byte, 3) != 3) + return false; + + *x = (byte[0] << 16) | (byte[1] << 8) | byte[2]; + + return true; +} + +bool heif_source_get_uint32(heif_source_h source, off_t offset, uint32_t *x) +{ + uint32_t tmp; + + heif_retvm_if_failed(x, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid x"); + + *x = 0; + + if (heif_source_read_at(source, offset, &tmp, 4) != 4) + return false; + + *x = ntohl(tmp); + + return true; +} + +bool heif_source_get_uint64(heif_source_h source, off_t offset, uint64_t *x) +{ + uint64_t tmp; + + heif_retvm_if_failed(x, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid x"); + + *x = 0; + + if (heif_source_read_at(source, offset, &tmp, 8) != 8) + return false; + + *x = ((uint64_t)ntohl(tmp & 0xffffffff) << 32) | ntohl(tmp >> 32); + + return true; +} + +// read either int or int<2N> into a uint<2N>_t, size is the int size in bytes. +bool heif_source_get_uint16var(heif_source_h source, off_t offset, uint16_t *x, size_t size) +{ + heif_retvm_if_failed(x, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid x"); + + if (size == 2) + return heif_source_get_uint16(source, offset, x); + + if (size == 1) { + uint8_t tmp; + if (heif_source_read_at(source, offset, &tmp, 1) == 1) { + *x = tmp; + return true; + } + } + + return false; +} + +bool heif_source_get_uint32var(heif_source_h source, off_t offset, uint32_t *x, size_t size) +{ + heif_retvm_if_failed(x, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid x"); + + if (size == 4) + return heif_source_get_uint32(source, offset, x); + + if (size == 2) { + uint16_t tmp; + if (heif_source_get_uint16(source, offset, &tmp)) { + *x = tmp; + return true; + } + } + + return false; +} + +bool heif_source_get_uint64var(heif_source_h source, off_t offset, uint64_t *x, size_t size) +{ + heif_retvm_if_failed(x, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid x"); + + if (size == 8) + return heif_source_get_uint64(source, offset, x); + + if (size == 4) { + uint32_t tmp; + if (heif_source_get_uint32(source, offset, &tmp)) { + *x = tmp; + return true; + } + } + + return false; +} + +static int __source_get_file_size(const char *path, size_t *size) +{ + GStatBuf buf; + + heif_retvm_if_failed(path, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid path"); + heif_retvm_if_failed(size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size"); + +#ifdef __DEBUG_SOURCE + heif_debug_fenter(); +#endif + + if (g_stat(path, &buf) < 0) { + if (errno == EACCES || errno == EPERM) { + heif_error("Fail to open path[%s] due to permission denied", path); + return LIBHEIF_ERROR_PERMISSION_DENIED; + } else { + heif_error("Fail to open path[%s] due to invalid path", path); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + } + + *size = (size_t)buf.st_size; + + heif_info("size: %zu", *size); + +#ifdef __DEBUG_SOURCE + heif_debug_fleave(); +#endif + + return LIBHEIF_ERROR_NONE; +} + +static int __source_set_path(heif_source_t *source, const char *path) +{ + int ret = LIBHEIF_ERROR_NONE; + + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); + heif_retvm_if_failed(path, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid path"); + +#ifdef __DEBUG_SOURCE + heif_debug_fenter(); +#endif + + ret = __source_get_file_size(path, &source->size); + if (ret != LIBHEIF_ERROR_NONE) { + heif_error("__source_get_file_size fail %d", ret); + return ret; + } + + if (source->size == 0) { + heif_error("invalid size %zu", source->size); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + source->fd = open(path, O_RDONLY); + if (source->fd < 0) { + heif_strerror("open"); + return LIBHEIF_ERROR_INVALID_PARAMETER; + } + + source->path = g_strdup(path); + +#ifdef __DEBUG_SOURCE + heif_info("path: %s, size: %zu", source->path, source->size); + heif_debug_fleave(); +#endif + + return LIBHEIF_ERROR_NONE; +} + +static int __source_set_buffer(heif_source_t *source, unsigned char *buf, size_t size) +{ + heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source"); + heif_retvm_if_failed(buf, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid buf"); + heif_retvm_if_failed(size > 0, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size"); + +#ifdef __DEBUG_SOURCE + heif_debug_fenter(); +#endif + + source->buf = (uint8_t *)g_memdup(buf, size); + source->size = size; + +#ifdef __DEBUG_SOURCE + heif_info("buf: %p, size: %zu", source->buf, source->size); + heif_debug_fleave(); +#endif + + return LIBHEIF_ERROR_NONE; +} + +static void __source_free(heif_source_t *source) +{ + heif_retm_if_failed(source); +#ifdef __DEBUG_SOURCE + heif_debug_fenter(); +#endif + + g_free(source->path); + source->path = NULL; + + if (source->fd >= 0) { + close(source->fd); + source->fd = -1; + } + + g_free(source->buf); + source->buf = NULL; + +#ifdef __DEBUG_SOURCE + heif_debug_fleave(); +#endif +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..f5118ab --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,18 @@ +SET(fw_name "libheif") +SET(fw_test "${fw_name}-test") + +INCLUDE(FindPkgConfig) +pkg_check_modules(${fw_test} REQUIRED glib-2.0 dlog mmutil-common mmutil-jpeg mmutil-magick) +FOREACH(flag ${${fw_test}_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wall -fPIE -Werror") + +aux_source_directory(. sources) +FOREACH(src ${sources}) + GET_FILENAME_COMPONENT(src_name ${src} NAME_WE) + MESSAGE("${src_name}") + ADD_EXECUTABLE(${src_name} ${src}) + TARGET_LINK_LIBRARIES(${src_name} ${fw_name} ${${fw_test}_LDFLAGS}) +ENDFOREACH() diff --git a/test/heif_testsuite.c b/test/heif_testsuite.c new file mode 100644 index 0000000..116f6c7 --- /dev/null +++ b/test/heif_testsuite.c @@ -0,0 +1,635 @@ +/* + * libheif + * + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Jiyong Min + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +typedef enum { + TEST_AUTO, + TEST_GETINFO_FILE, + TEST_GETINFO_MEMORY, + TEST_DECODE_FILE, + TEST_DECODE_MEMORY, + TEST_THUMB_FILE, + TEST_THUMB_MEMORY, + TEST_NUM, +} heif_test_mode_e; + +static const char *g_test_decode_file_jpeg[] = { + [HEIF_COLOR_FORMAT_NONE] = NULL, + [HEIF_COLOR_FORMAT_YUV420P] = "/opt/usr/home/owner/media/test_heif_file_yuv420p.jpg", + [HEIF_COLOR_FORMAT_RGB24] = "/opt/usr/home/owner/media/test_heif_file_rgb24.jpg", + [HEIF_COLOR_FORMAT_ARGB] = "/opt/usr/home/owner/media/test_heif_file_argb.jpg", + [HEIF_COLOR_FORMAT_BGRA] = "/opt/usr/home/owner/media/test_heif_file_bgra.jpg", + [HEIF_COLOR_FORMAT_RGBA] = "/opt/usr/home/owner/media/test_heif_file_rgba.jpg", +}; +static const char *g_test_decode_file_png[] = { + [HEIF_COLOR_FORMAT_NONE] = NULL, + [HEIF_COLOR_FORMAT_YUV420P] = NULL, // not supported yuv420p in png + [HEIF_COLOR_FORMAT_RGB24] = "/opt/usr/home/owner/media/test_heif_file_rgb24.png", + [HEIF_COLOR_FORMAT_ARGB] = "/opt/usr/home/owner/media/test_heif_file_argb.png", + [HEIF_COLOR_FORMAT_BGRA] = "/opt/usr/home/owner/media/test_heif_file_bgra.png", + [HEIF_COLOR_FORMAT_RGBA] = "/opt/usr/home/owner/media/test_heif_file_rgba.png", +}; +static const char *g_test_decode_buff_jpeg[] = { + [HEIF_COLOR_FORMAT_NONE] = NULL, + [HEIF_COLOR_FORMAT_YUV420P] = "/opt/usr/home/owner/media/test_heif_buff_yuv420p.jpg", + [HEIF_COLOR_FORMAT_RGB24] = "/opt/usr/home/owner/media/test_heif_buff_rgb24.jpg", + [HEIF_COLOR_FORMAT_ARGB] = "/opt/usr/home/owner/media/test_heif_buff_argb.jpg", + [HEIF_COLOR_FORMAT_BGRA] = "/opt/usr/home/owner/media/test_heif_buff_bgra.jpg", + [HEIF_COLOR_FORMAT_RGBA] = "/opt/usr/home/owner/media/test_heif_buff_rgba.jpg", +}; +static const char *g_test_decode_buff_png[] = { + [HEIF_COLOR_FORMAT_NONE] = NULL, + [HEIF_COLOR_FORMAT_YUV420P] = NULL, + [HEIF_COLOR_FORMAT_RGB24] = "/opt/usr/home/owner/media/test_heif_buff_rgb24.png", + [HEIF_COLOR_FORMAT_ARGB] = "/opt/usr/home/owner/media/test_heif_buff_argb.png", + [HEIF_COLOR_FORMAT_BGRA] = "/opt/usr/home/owner/media/test_heif_buff_bgra.png", + [HEIF_COLOR_FORMAT_RGBA] = "/opt/usr/home/owner/media/test_heif_buff_rgba.png", +}; +static const char *g_test_thumb_file_jpeg[] = { + [HEIF_COLOR_FORMAT_NONE] = NULL, + [HEIF_COLOR_FORMAT_YUV420P] = "/opt/usr/home/owner/media/test_heif_thumb_file_yuv420p.jpg", + [HEIF_COLOR_FORMAT_RGB24] = "/opt/usr/home/owner/media/test_heif_thumb_file_rgb24.jpg", + [HEIF_COLOR_FORMAT_ARGB] = "/opt/usr/home/owner/media/test_heif_thumb_file_argb.jpg", + [HEIF_COLOR_FORMAT_BGRA] = "/opt/usr/home/owner/media/test_heif_thumb_file_bgra.jpg", + [HEIF_COLOR_FORMAT_RGBA] = "/opt/usr/home/owner/media/test_heif_thumb_file_rgba.jpg", +}; +static const char *g_test_thumb_file_png[] = { + [HEIF_COLOR_FORMAT_NONE] = NULL, + [HEIF_COLOR_FORMAT_YUV420P] = NULL, + [HEIF_COLOR_FORMAT_RGB24] = "/opt/usr/home/owner/media/test_heif_thumb_file_rgb24.png", + [HEIF_COLOR_FORMAT_ARGB] = "/opt/usr/home/owner/media/test_heif_thumb_file_argb.png", + [HEIF_COLOR_FORMAT_BGRA] = "/opt/usr/home/owner/media/test_heif_thumb_file_bgra.png", + "/opt/usr/home/owner/media/test_heif_thumb_file_rgba.png", +}; +static const char *g_test_thumb_buff_jpeg[] = { + [HEIF_COLOR_FORMAT_NONE] = NULL, + [HEIF_COLOR_FORMAT_YUV420P] = "/opt/usr/home/owner/media/test_heif_thumb_buff_yuv420p.jpg", + [HEIF_COLOR_FORMAT_RGB24] = "/opt/usr/home/owner/media/test_heif_thumb_buff_rgb24.jpg", + [HEIF_COLOR_FORMAT_ARGB] = "/opt/usr/home/owner/media/test_heif_thumb_buff_argb.jpg", + [HEIF_COLOR_FORMAT_BGRA] = "/opt/usr/home/owner/media/test_heif_thumb_buff_bgra.jpg", + [HEIF_COLOR_FORMAT_RGBA] = "/opt/usr/home/owner/media/test_heif_thumb_buff_rgba.jpg", +}; +static const char *g_test_thumb_buff_png[] = { + [HEIF_COLOR_FORMAT_NONE] = NULL, + [HEIF_COLOR_FORMAT_YUV420P] = NULL, + [HEIF_COLOR_FORMAT_RGB24] = "/opt/usr/home/owner/media/test_heif_thumb_buff_rgb24.png", + [HEIF_COLOR_FORMAT_ARGB] = "/opt/usr/home/owner/media/test_heif_thumb_buff_argb.png", + [HEIF_COLOR_FORMAT_BGRA] = "/opt/usr/home/owner/media/test_heif_thumb_buff_bgra.png", + [HEIF_COLOR_FORMAT_RGBA] = "/opt/usr/home/owner/media/test_heif_thumb_buff_rgba.png", +}; + +static int g_test_mode = TEST_AUTO; +static char *g_path = NULL; +static int g_color = HEIF_COLOR_FORMAT_RGB24; + +// for only test +static void __print_help(const char *argv0); +static bool __get_input_data(const char *argv, const long min, const long max, int *data); +static bool __get_arguments(int argc, char *argv[]); +// library tests +static bool __test_auto(void); +static bool __test_get_info(int mode); +static bool __test_decode(int mode); +static bool __test_thumb(int mode); +static int __get_image_attrs(heif_image_info_h image_info); +// encode image using libmm-utility +static int __convert_heif_image_to_mm_image(heif_image_h heif_image, mm_util_image_h *mm_image); +static int __save_to_jpeg(heif_image_h source, const char *path); +static int __save_to_png(heif_image_h source, const char *path); + + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + __print_help(argv[0]); + return 0; + } + + if (false == __get_arguments(argc, argv)) { + g_print("\t[HEIF_testsuite] _get_arguments failed\n"); + goto END; + } + + /* test all functions automatically */ + if (g_test_mode == TEST_AUTO) { + if (false == __test_auto()) + g_print("\t[HEIF_testsuite] _test_auto failed\n"); + goto END; + } + + /* test of getting image information from file or memory */ + if (g_test_mode == TEST_GETINFO_FILE || g_test_mode == TEST_GETINFO_MEMORY) { + if (false == __test_get_info(g_test_mode)) + g_print("\t[HEIF_testsuite] __test_decode failed\n"); + goto END; + } + + /* test of decoding image from file or memory */ + if (g_test_mode == TEST_DECODE_FILE || g_test_mode == TEST_DECODE_MEMORY) { + if (false == __test_decode(g_test_mode)) + g_print("\t[HEIF_testsuite] __test_decode failed\n"); + goto END; + } + + /* test of getting thumbnail from file or memory */ + if (g_test_mode == TEST_THUMB_FILE || g_test_mode == TEST_THUMB_MEMORY) { + if (false == __test_thumb(g_test_mode)) + g_print("\t[HEIF_testsuite] __test_thumb failed\n"); + goto END; + } + +END: + g_free(g_path); + + return 0; +} + +static void __print_help(const char *argv0) +{ + g_print("\t[usage]\n"); + g_print("\t\t1. decode & encode : %s {mode} {path} {color_format(opt)}\n", argv0); + g_print("\t\t2. decode : %s 0 /home/owner/media/test.heif\n", argv0); + g_print("\t\t3. encode : %s 1 /home/owner/media/test.heif\n", argv0); + g_print("\t\t4. encode : %s 3 /home/owner/media/test.heif 2(HEIF_COLOR_FORMAT_RGB24)\n", argv0); + g_print("\t\t5. [mode]\n"); + g_print("\t\t 0 - auto\n"); + g_print("\t\t 1 - get information from file\n"); + g_print("\t\t 2 - get information from memory\n"); + g_print("\t\t 3 - decode from file\n"); + g_print("\t\t 4 - decode from memory\n"); + g_print("\t\t 5 - get thumbnail from file\n"); + g_print("\t\t 6 - get thumbnail from memory\n"); +} + +static bool __get_input_data(const char *argv, const long min, const long max, int *data) +{ + if (argv == NULL || strlen(argv) == 0) + return false; + + long temp = g_ascii_strtoll(argv, NULL, 10); + if (temp < min || temp > max) + return false; + + *data = (int)temp; + + return true; +} + +static bool __get_arguments(int argc, char *argv[]) +{ + if (false == __get_input_data(argv[1], TEST_AUTO, TEST_NUM - 1, &g_test_mode)) { + g_print("\t[HEIF_testsuite] invalid mode(%s)\n", argv[1]); + __print_help(argv[0]); + return false; + } + + g_path = g_strdup(argv[2]); + if (!g_path) { + g_print("\t[HEIF_testsuite] invalid path(%s)\n", argv[2]); + __print_help(argv[0]); + return false; + } + + if (g_test_mode == TEST_AUTO || + g_test_mode == TEST_GETINFO_FILE || + g_test_mode == TEST_GETINFO_MEMORY) { + /* do nothing... */ + } else if (g_test_mode == TEST_DECODE_FILE || + g_test_mode == TEST_DECODE_MEMORY || + g_test_mode == TEST_THUMB_FILE || + g_test_mode == TEST_THUMB_MEMORY) { + if (false == __get_input_data(argv[3], HEIF_COLOR_FORMAT_YUV420P, HEIF_COLOR_FORMAT_MAX - 1, &g_color)) + g_print("\t[HEIF_testsuite] used default color(%d)\n", g_color); + } else { + g_print("\t[HEIF_testsuite] invalid mode for test %s\n", argv[1]); + return false; + } + + return true; +} + +static bool __test_auto(void) +{ + bool result = true; + + if (!__test_get_info(TEST_GETINFO_FILE)) { + g_print("\t[HEIF_testsuite] __test_get_info(%d) failed\n", TEST_GETINFO_FILE); + result = false; + } + + if (!__test_get_info(TEST_GETINFO_MEMORY)) { + g_print("\t[HEIF_testsuite] __test_get_info(%d) failed\n", TEST_GETINFO_MEMORY); + result = false; + } + + for (g_color = HEIF_COLOR_FORMAT_YUV420P; g_color < HEIF_COLOR_FORMAT_MAX; g_color++) { + if (!__test_decode(TEST_DECODE_FILE)) { + g_print("\t[HEIF_TEST] __test_decode(%d/format:%u) fail\n", TEST_DECODE_FILE, g_color); + result = false; + } + } + + for (g_color = HEIF_COLOR_FORMAT_YUV420P; g_color < HEIF_COLOR_FORMAT_MAX; g_color++) { + if (!__test_decode(TEST_DECODE_MEMORY)) { + g_print("\t[HEIF_TEST] __test_decode(%d/format:%u) fail\n", TEST_DECODE_MEMORY, g_color); + result = false; + } + } + + for (g_color = HEIF_COLOR_FORMAT_YUV420P; g_color < HEIF_COLOR_FORMAT_MAX; g_color++) { + if (!__test_thumb(TEST_THUMB_FILE)) { + g_print("\t[HEIF_TEST] __test_thumb(%d/format:%u) fail\n", TEST_THUMB_FILE, g_color); + result = false; + } + } + + for (g_color = HEIF_COLOR_FORMAT_YUV420P; g_color < HEIF_COLOR_FORMAT_MAX; g_color++) { + if (!__test_thumb(TEST_THUMB_MEMORY)) { + g_print("\t[HEIF_TEST] __test_thumb(%d/format:%u) fail\n", TEST_THUMB_MEMORY, g_color); + result = false; + } + } + + return result; +} + +static bool __test_get_info(int mode) +{ + bool result = true; + int ret = 0; + heif_image_info_h image_info = NULL; + gchar *buffer = NULL; + gsize size = 0; + GError *error = NULL; + + switch (mode) { + case TEST_GETINFO_FILE: + ret = heif_get_image_info_from_file(g_path, &image_info); + if (ret < 0) { + g_print("\t[HEIF_TEST] heif_get_image_info_from_file fail %d\n", ret); + return false; + } + + g_print("[HEIF_TEST] heif_get_image_info_from_file succeed !!!\n"); + + ret = __get_image_attrs(image_info); + if (ret < 0) { + g_print("\t[HEIF_TEST] __get_image_attrs fail %d\n", ret); + result = false; + } + break; + + case TEST_GETINFO_MEMORY: + if (!g_file_get_contents(g_path, &buffer, &size, &error)) { + g_print("\t[HEIF_TEST] g_file_get_contents fail [%s: %s]\n", g_path, (error ? error->message : "none")); + if (error) + g_error_free(error); + return false; + } + + ret = heif_get_image_info_from_buffer((unsigned char *)((void *)buffer), (size_t)size, &image_info); + if (ret < 0) { + g_print("\t[HEIF_TEST] heif_get_image_info_from_buffer fail %d\n", ret); + g_free(buffer); + return false; + } + + g_print("[HEIF_TEST] heif_get_image_info_from_buffer succeed !!!\n"); + + ret = __get_image_attrs(image_info); + if (ret < 0) { + g_print("\t[HEIF_TEST] __get_image_attrs fail %d\n", ret); + result = false; + } + + g_free(buffer); + break; + + default: + g_print("\t[HEIF_TEST] invalid mode %d\n", mode); + return false; + } + + heif_image_info_free(image_info); + return result; +} + +static bool __test_decode(int mode) +{ + int ret = 0; + heif_image_h image = NULL; + gchar *buffer = NULL; + gsize size = 0; + GError *error = NULL; + + switch (mode) { + case TEST_DECODE_FILE: + ret = heif_decode_image_from_file(g_path, g_color, &image); + if (ret < 0) { + g_print("\t[HEIF_TEST] heif_decode_image_from_file(format:%u) fail %d\n", g_color, ret); + return false; + } + + g_print("[HEIF_TEST] heif_decode_image_from_file succeed !!!\n"); + + ret = __save_to_jpeg(image, g_test_decode_file_jpeg[g_color]); + if (ret < 0) + g_print("\t[HEIF_TEST] __save_to_jpeg fail %d\n", ret); + + ret = __save_to_png(image, g_test_decode_file_png[g_color]); + if (ret < 0) + g_print("\t[HEIF_TEST] __save_to_png fail %d\n", ret); + + break; + + case TEST_DECODE_MEMORY: + if (!g_file_get_contents(g_path, &buffer, &size, &error)) { + g_print("\t[HEIF_TEST] g_file_get_contents fail [%s: %s]\n", g_path, (error ? error->message : "none")); + if (error) + g_error_free(error); + return false; + } + + ret = heif_decode_image_from_buffer((unsigned char *)((void *)buffer), (size_t)size, g_color, &image); + if (ret < 0) { + g_print("\t[HEIF_TEST] heif_decode_image_from_buffer fail %d\n", ret); + return false; + } + + g_print("[HEIF_TEST] heif_decode_image_from_buffer succeed !!!\n"); + + ret = __save_to_jpeg(image, g_test_decode_buff_jpeg[g_color]); + if (ret < 0) + g_print("\t[HEIF_TEST] __save_to_jpeg fail %d\n", ret); + + ret = __save_to_png(image, g_test_decode_buff_png[g_color]); + if (ret < 0) + g_print("\t[HEIF_TEST] __save_to_png fail %d\n", ret); + + g_free(buffer); + break; + + default: + g_print("\t[HEIF_TEST] invalid mode %d\n", mode); + return false; + } + + heif_decoder_destroy_image(image); + return true; +} + +static bool __test_thumb(int mode) +{ + int ret = 0; + heif_image_h thumbnail = NULL; + gchar *buffer = NULL; + gsize size = 0; + GError *error = NULL; + + switch (mode) { + case TEST_THUMB_FILE: + ret = heif_decode_thumb_from_file(g_path, g_color, &thumbnail); + if (ret < 0) { + g_print("\t[HEIF_TEST] heif_decode_thumb_from_file(format:%u) fail %d\n", g_color, ret); + return false; + } + + g_print("[HEIF_TEST] heif_decode_thumb_from_file succeed !!!\n"); + + ret = __save_to_jpeg(thumbnail, g_test_thumb_file_jpeg[g_color]); + if (ret < 0) + g_print("\t[HEIF_TEST] __save_to_jpeg fail %d\n", ret); + + ret = __save_to_png(thumbnail, g_test_thumb_file_png[g_color]); + if (ret < 0) + g_print("\t[HEIF_TEST] __save_to_png fail %d\n", ret); + + break; + + case TEST_THUMB_MEMORY: + if (!g_file_get_contents(g_path, &buffer, &size, &error)) { + g_print("\t[HEIF_TEST] g_file_get_contents fail [%s: %s]\n", g_path, (error ? error->message : "none")); + if (error) + g_error_free(error); + return false; + } + + ret = heif_decode_thumb_from_buffer((unsigned char *)((void *)buffer), (size_t)size, g_color, &thumbnail); + if (ret < 0) { + g_print("\t[HEIF_TEST] heif_decode_thumb_from_buffer fail %d\n", ret); + return false; + } + + g_print("[HEIF_TEST] heif_decode_thumb_from_buffer succeed !!!\n"); + + ret = __save_to_jpeg(thumbnail, g_test_thumb_buff_jpeg[g_color]); + if (ret < 0) + g_print("\t[HEIF_TEST] __save_to_jpeg fail %d\n", ret); + + ret = __save_to_png(thumbnail, g_test_thumb_buff_png[g_color]); + if (ret < 0) + g_print("\t[HEIF_TEST] __save_to_png fail %d\n", ret); + + g_free(buffer); + break; + + default: + g_print("\t[HEIF_TEST] invalid mode %d\n", mode); + return false; + } + + heif_decoder_destroy_image(thumbnail); + return true; +} + +static int __get_image_attrs(heif_image_info_h image_info) +{ + int ret = 0; + int width = 0, height = 0, orientation = 0, mirror = 0; + char *mimetype = NULL; + void *exif = NULL, *icc_profile = NULL; + int mimetype_len = 0, exif_len = 0, icc_profile_len = 0; + + /* get basic information */ + ret = heif_image_info_get_attrs(image_info, + HEIF_IMAGE_MIMETYPE, &mimetype, &mimetype_len, + HEIF_IMAGE_WIDTH, &width, + HEIF_IMAGE_HEIGHT, &height, + HEIF_IMAGE_ORIENTATION, &orientation, + HEIF_IMAGE_MIRROR, &mirror, + NULL); + if (ret != LIBHEIF_ERROR_NONE) + g_print("\t[HEIF_TEST] heif_image_info_get_attrs fail %d\n", ret); + + g_print("[HEIF_TEST] Mimetype\t: %s\n", mimetype); + g_print("[HEIF_TEST] Width\t: %d\n", width); + g_print("[HEIF_TEST] Height\t: %d\n", height); + g_print("[HEIF_TEST] Orientation\t: %d\n", orientation); + g_print("[HEIF_TEST] Mirror\t: %d\n", mirror); + + /* get extra information */ + ret = heif_image_info_get_attrs(image_info, + HEIF_IMAGE_EXIF, &exif, &exif_len, + HEIF_IMAGE_ICC_PROFILE, &icc_profile, &icc_profile_len, + NULL); + if (ret != LIBHEIF_ERROR_NONE) + g_print("\t[HEIF_TEST] heif_image_info_get_attrs fail %d\n", ret); + + g_print("[HEIF_TEST] Exif\t: %p/%d\n", exif, exif_len); + g_print("[HEIF_TEST] ICC profile\t: %p/%d\n", icc_profile, icc_profile_len); + + return ret; +} + +static int __convert_heif_image_to_mm_image(heif_image_h heif_image, mm_util_image_h *mm_image) +{ + int ret = LIBHEIF_ERROR_NONE; + mm_util_color_format_e color = MM_UTIL_COLOR_RGB24; + unsigned int width = 0, height = 0; + heif_color_format_e format; + unsigned char *data = NULL; + size_t size = 0; + + ret = heif_image_get_image(heif_image, &width, &height, &format, &data, &size); + if (ret != LIBHEIF_ERROR_NONE) { + g_print("\t[HEIF_TEST] heif_image_get_image fail %d\n", ret); + return ret; + } + + g_print("[HEIF_TEST][SOURCE] width: %u, height: %u format: %u data: %p, size: %zu\n", width, height, format, data, size); + + switch (format) { + case HEIF_COLOR_FORMAT_RGB24: + color = MM_UTIL_COLOR_RGB24; + break; + + case HEIF_COLOR_FORMAT_ARGB: + color = MM_UTIL_COLOR_ARGB; + break; + + case HEIF_COLOR_FORMAT_BGRA: + color = MM_UTIL_COLOR_BGRA; + break; + + case HEIF_COLOR_FORMAT_RGBA: + color = MM_UTIL_COLOR_RGBA; + break; + + case HEIF_COLOR_FORMAT_YUV420P: + color = MM_UTIL_COLOR_YUV420; + break; + + default: + g_print("\t[HEIF_TEST] invalid color: %d\n", format); + return MM_UTIL_ERROR_INVALID_PARAMETER; + } + + ret = mm_image_create_image(width, height, color, data, size, mm_image); + if (ret != MM_UTIL_ERROR_NONE) { + g_print("\t[HEIF_TEST] mm_image_create_image fail %d\n", ret); + return ret; + } + + return LIBHEIF_ERROR_NONE; +} + +static int __save_to_jpeg(heif_image_h source, const char *path) +{ + int ret = 0; + mm_util_image_h mm_image = NULL; + + if (!source) { + g_print("\t[HEIF_TEST] source: %p\n", source); + return MM_UTIL_ERROR_INVALID_PARAMETER; + } + + ret = __convert_heif_image_to_mm_image(source, &mm_image); + if (ret < 0) { + g_print("\t[HEIF_TEST] __convert_heif_image_to_mm_image fail %d\n", ret); + goto END; + } + + ret = mm_util_jpeg_encode_to_file(mm_image, 100, path); + if (ret < 0) + g_print("\t[HEIF_TEST] mm_util_jpeg_encode_to_file fail %d\n", ret); + +END: + mm_image_destroy_image(mm_image); + + return ret; +} + +static int __save_to_png(heif_image_h source, const char *path) +{ + int ret = 0; + mm_util_image_h mm_image = NULL; + mm_util_enc_opt_h enc_opt = NULL; + + if (!source) { + g_print("\t[HEIF_TEST] source: %p\n", source); + return MM_UTIL_ERROR_INVALID_PARAMETER; + } + + ret = __convert_heif_image_to_mm_image(source, &mm_image); + if (ret < 0) { + g_print("\t[HEIF_TEST] __convert_heif_image_to_mm_image fail %d\n", ret); + goto END; + } + + ret = mm_util_enc_opt_create(&enc_opt); + if (ret < 0) { + g_print("\t[HEIF_TEST] mm_util_enc_opt_create fail %d\n", ret); + goto END; + } + + ret = mm_util_enc_opt_set_codec(enc_opt, IMG_CODEC_PNG); + if (ret < 0) { + g_print("\t[HEIF_TEST] mm_util_enc_opt_set_codec fail %d\n", ret); + goto END; + } + + ret = mm_util_enc_opt_set_png_compression(enc_opt, 1); + if (ret < 0) { + g_print("\t[HEIF_TEST] mm_util_enc_opt_set_png_compression fail %d\n", ret); + goto END; + } + + ret = mm_util_encode_image_to_file(mm_image, enc_opt, path); + if (ret < 0) + g_print("\t[HEIF_TEST] mm_util_encode_image_to_file fail %d\n", ret); + +END: + mm_image_destroy_image(mm_image); + mm_util_enc_opt_destroy(enc_opt); + + return ret; +} diff --git a/test/sample_test.heic b/test/sample_test.heic new file mode 100644 index 0000000..a605aac Binary files /dev/null and b/test/sample_test.heic differ diff --git a/test/test.sh b/test/test.sh new file mode 100755 index 0000000..90dfc35 --- /dev/null +++ b/test/test.sh @@ -0,0 +1,85 @@ +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved. +# +# Contact: Jiyong Min +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +@echo off +# VERSION should be same with library version. +VERSION=0.0.1 +ARM=armv7l +X86=i586 +AARCH64=aarch64 +X86_64=x86_64 + +# This script run tests automatically. +# To run the test successfully, +# please modify below 'ARCH' and 'LOCAL_DIR' to suit your enviroment. +# +ARCH=$X86 +BUILD_ROOTS=~/GBS-ROOT/local/BUILD-ROOTS/scratch.$ARCH.0/home/abuild/rpmbuild/BUILD +LOCAL_DIR=~/TestHeif/ + +@echo on +sdb push $BUILD_ROOTS/libheif-$VERSION/test/heif_testsuite /home/owner/media/ +sdb push $BUILD_ROOTS/libheif-$VERSION/test/sample_test.heic /home/owner/media/ + +sdb shell "cd /home/owner/media/;rm -rf test_heif_*;./heif_testsuite 0 sample_test.heic" + +TEST_FILELIST=( +"test_heif_file_yuv420p.jpg" +"test_heif_file_rgb24.jpg" +"test_heif_file_rgb24.png" +"test_heif_file_argb.jpg" +"test_heif_file_argb.png" +"test_heif_file_bgra.jpg" +"test_heif_file_bgra.png" +"test_heif_file_rgba.jpg" +"test_heif_file_rgba.png" + +"test_heif_buff_yuv420p.jpg" +"test_heif_buff_rgb24.jpg" +"test_heif_buff_rgb24.png" +"test_heif_buff_argb.jpg" +"test_heif_buff_argb.png" +"test_heif_buff_bgra.jpg" +"test_heif_buff_bgra.png" +"test_heif_buff_rgba.jpg" +"test_heif_buff_rgba.png" + +"test_heif_thumb_file_yuv420p.jpg" +"test_heif_thumb_file_rgb24.jpg" +"test_heif_thumb_file_rgb24.png" +"test_heif_thumb_file_argb.jpg" +"test_heif_thumb_file_argb.png" +"test_heif_thumb_file_bgra.jpg" +"test_heif_thumb_file_bgra.png" +"test_heif_thumb_file_rgba.jpg" +"test_heif_thumb_file_rgba.png" + +"test_heif_thumb_buff_yuv420p.jpg" +"test_heif_thumb_buff_rgb24.jpg" +"test_heif_thumb_buff_rgb24.png" +"test_heif_thumb_buff_argb.jpg" +"test_heif_thumb_buff_argb.png" +"test_heif_thumb_buff_bgra.jpg" +"test_heif_thumb_buff_bgra.png" +"test_heif_thumb_buff_rgba.jpg" +"test_heif_thumb_buff_rgba.png" +) + +for file_name in "${TEST_FILELIST[@]}"; do + sdb pull /home/owner/media/$file_name $LOCAL_DIR +done