New Upstream Release - libheif

Ready changes

Summary

Merged new upstream version: 1.15.2 (was: 1.15.1).

Diff

diff --git a/CMakeLists.txt b/CMakeLists.txt
index e37e012..8b8d6d1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
 cmake_minimum_required (VERSION 3.0)
 
-project(libheif LANGUAGES C CXX VERSION 1.15.1)
+project(libheif LANGUAGES C CXX VERSION 1.15.2)
 
 # https://cmake.org/cmake/help/v3.1/policy/CMP0054.html
 cmake_policy(VERSION 3.0...3.22)
diff --git a/configure b/configure
index 6c7417c..ec96513 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for libheif 1.15.1.
+# Generated by GNU Autoconf 2.71 for libheif 1.15.2.
 #
 # Report bugs to <opensource@struktur.de>.
 #
@@ -621,8 +621,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='libheif'
 PACKAGE_TARNAME='libheif'
-PACKAGE_VERSION='1.15.1'
-PACKAGE_STRING='libheif 1.15.1'
+PACKAGE_VERSION='1.15.2'
+PACKAGE_STRING='libheif 1.15.2'
 PACKAGE_BUGREPORT='opensource@struktur.de'
 PACKAGE_URL=''
 
@@ -1491,7 +1491,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures libheif 1.15.1 to adapt to many kinds of systems.
+\`configure' configures libheif 1.15.2 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1563,7 +1563,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of libheif 1.15.1:";;
+     short | recursive ) echo "Configuration of libheif 1.15.2:";;
    esac
   cat <<\_ACEOF
 
@@ -1721,7 +1721,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-libheif configure 1.15.1
+libheif configure 1.15.2
 generated by GNU Autoconf 2.71
 
 Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2177,7 +2177,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by libheif $as_me 1.15.1, which was
+It was created by libheif $as_me 1.15.2, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -3155,7 +3155,7 @@ ac_config_headers="$ac_config_headers config.h"
 # Note: do not forget to set the version in the CMakeLists.txt file accordingly
 PROJECT_VERSION_MAJOR=1
 PROJECT_VERSION_MINOR=15
-PROJECT_VERSION_PATCH=1
+PROJECT_VERSION_PATCH=2
 PROJECT_VERSION_TWEAK=0
 
 
@@ -3169,7 +3169,7 @@ PROJECT_VERSION_TWEAK=0
 # If any interfaces have been removed or changed since the last public release, then set age to 0.
 
 LIBHEIF_CURRENT=16
-LIBHEIF_REVISION=1
+LIBHEIF_REVISION=2
 LIBHEIF_AGE=15
 
 
@@ -17720,7 +17720,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='libheif'
- VERSION='1.15.1'
+ VERSION='1.15.2'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -20746,7 +20746,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by libheif $as_me 1.15.1, which was
+This file was extended by libheif $as_me 1.15.2, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -20814,7 +20814,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-libheif config.status 1.15.1
+libheif config.status 1.15.2
 configured by $0, generated by GNU Autoconf 2.71,
   with options \\"\$ac_cs_config\\"
 
diff --git a/configure.ac b/configure.ac
index da22403..d7de0b0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,12 +1,12 @@
 AC_PREREQ([2.68])
-AC_INIT([libheif],[1.15.1],[opensource@struktur.de])
+AC_INIT([libheif],[1.15.2],[opensource@struktur.de])
 AC_CONFIG_SRCDIR([libheif/box.cc])
 AC_CONFIG_HEADERS([config.h])
 
 # Note: do not forget to set the version in the CMakeLists.txt file accordingly
 PROJECT_VERSION_MAJOR=1
 PROJECT_VERSION_MINOR=15
-PROJECT_VERSION_PATCH=1
+PROJECT_VERSION_PATCH=2
 PROJECT_VERSION_TWEAK=0
 AC_SUBST(PROJECT_VERSION_MAJOR)
 AC_SUBST(PROJECT_VERSION_MINOR)
@@ -20,7 +20,7 @@ AC_SUBST(PROJECT_VERSION_TWEAK)
 # If any interfaces have been removed or changed since the last public release, then set age to 0.
 
 LIBHEIF_CURRENT=16
-LIBHEIF_REVISION=1
+LIBHEIF_REVISION=2
 LIBHEIF_AGE=15
 AC_SUBST(LIBHEIF_CURRENT)
 AC_SUBST(LIBHEIF_REVISION)
diff --git a/debian/changelog b/debian/changelog
index 6a9c4c5..582bcf6 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+libheif (1.15.2-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Wed, 26 Apr 2023 04:16:19 -0000
+
 libheif (1.15.1-1) unstable; urgency=medium
 
   * Team upload
diff --git a/go/heif/heif.go b/go/heif/heif.go
index e2da2e1..0bde20c 100644
--- a/go/heif/heif.go
+++ b/go/heif/heif.go
@@ -309,6 +309,12 @@ const (
 	// --- Encoding_error ---
 
 	SuberrorCannotWriteOutputData = C.heif_suberror_Cannot_write_output_data
+
+	SuberrorEncoderInitialization = C.heif_suberror_Encoder_initialization
+
+	SuberrorEncoderEncoding = C.heif_suberror_Encoder_encoding
+
+	SuberrorEncoderCleanup = C.heif_suberror_Encoder_cleanup
 )
 
 type HeifError struct {
diff --git a/libheif/box.cc b/libheif/box.cc
index dead692..9483ea9 100644
--- a/libheif/box.cc
+++ b/libheif/box.cc
@@ -50,7 +50,7 @@ Fraction::Fraction(int32_t num, int32_t den)
     denominator /= 2;
   }
 
-  while (numerator > MAX_FRACTION_VALUE || numerator < -MAX_FRACTION_VALUE) {
+  while (denominator > 1 && (numerator > MAX_FRACTION_VALUE || numerator < -MAX_FRACTION_VALUE)) {
     numerator /= 2;
     denominator /= 2;
   }
@@ -939,7 +939,7 @@ Error Box_iloc::parse(BitstreamRange& range)
   int base_offset_size = (values4 >> 4) & 0xF;
   int index_size = 0;
 
-  if (get_version() > 1) {
+  if (get_version() >= 1) {
     index_size = (values4 & 0xF);
   }
 
@@ -1003,14 +1003,12 @@ Error Box_iloc::parse(BitstreamRange& range)
     for (int e = 0; e < extent_count; e++) {
       Extent extent;
 
-      if (get_version() > 1 && index_size > 0) {
-        if (index_size == 4) {
-          extent.index = range.read32();
-        }
-        else if (index_size == 8) {
-          extent.index = ((uint64_t) range.read32()) << 32;
-          extent.index |= range.read32();
-        }
+      if (index_size == 4) {
+        extent.index = range.read32();
+      }
+      else if (index_size == 8) {
+        extent.index = ((uint64_t) range.read32()) << 32;
+        extent.index |= range.read32();
       }
 
       extent.offset = 0;
diff --git a/libheif/error.cc b/libheif/error.cc
index f8116a2..d653cb4 100644
--- a/libheif/error.cc
+++ b/libheif/error.cc
@@ -204,6 +204,12 @@ const char* heif::Error::get_error_string(heif_suberror_code err)
 
     case heif_suberror_Cannot_write_output_data:
       return "Cannot write output data";
+    case heif_suberror_Encoder_initialization:
+      return "Initialization problem";
+    case heif_suberror_Encoder_encoding:
+      return "Encoding problem";
+    case heif_suberror_Encoder_cleanup:
+      return "Cleanup problem";
 
       // --- Plugin_loading_error ---
 
diff --git a/libheif/heif.h b/libheif/heif.h
index b5d3b3c..ec88b3d 100644
--- a/libheif/heif.h
+++ b/libheif/heif.h
@@ -281,6 +281,10 @@ enum heif_suberror_code
 
   heif_suberror_Cannot_write_output_data = 5000,
 
+  heif_suberror_Encoder_initialization = 5001,
+  heif_suberror_Encoder_encoding = 5002,
+  heif_suberror_Encoder_cleanup = 5003,
+
 
   // --- Plugin loading error ---
 
diff --git a/libheif/heif_colorconversion.cc b/libheif/heif_colorconversion.cc
index 8245cb1..65f9756 100644
--- a/libheif/heif_colorconversion.cc
+++ b/libheif/heif_colorconversion.cc
@@ -1870,7 +1870,7 @@ Op_mono_to_RGB24_32::convert_colorspace(const std::shared_ptr<const HeifPixelIma
     return nullptr;
   }
 
-  const uint8_t* in_y, * in_a;
+  const uint8_t* in_y, * in_a = nullptr;
   int in_y_stride = 0, in_a_stride;
 
   uint8_t* out_p;
diff --git a/libheif/heif_emscripten.h b/libheif/heif_emscripten.h
index 1f69486..df22fd1 100644
--- a/libheif/heif_emscripten.h
+++ b/libheif/heif_emscripten.h
@@ -203,6 +203,9 @@ EMSCRIPTEN_BINDINGS(libheif) {
     emscripten::enum_<heif_suberror_code>("heif_suberror_code")
     .value("heif_suberror_Unspecified", heif_suberror_Unspecified)
     .value("heif_suberror_Cannot_write_output_data", heif_suberror_Cannot_write_output_data)
+    .value("heif_suberror_Encoder_initialization", heif_suberror_Encoder_initialization)
+    .value("heif_suberror_Encoder_encoding", heif_suberror_Encoder_encoding)
+    .value("heif_suberror_Encoder_cleanup", heif_suberror_Encoder_cleanup)
     .value("heif_suberror_End_of_data", heif_suberror_End_of_data)
     .value("heif_suberror_Invalid_box_size", heif_suberror_Invalid_box_size)
     .value("heif_suberror_No_ftyp_box", heif_suberror_No_ftyp_box)
diff --git a/libheif/heif_version.h b/libheif/heif_version.h
index 44dd2c6..769a6fb 100644
--- a/libheif/heif_version.h
+++ b/libheif/heif_version.h
@@ -28,10 +28,10 @@
 #define LIBHEIF_HEIF_VERSION_H
 
 /* Numeric representation of the version */
-#define LIBHEIF_NUMERIC_VERSION ((1<<24) | (15<<16) | (1<<8) | 0)
+#define LIBHEIF_NUMERIC_VERSION ((1<<24) | (15<<16) | (2<<8) | 0)
 
 /* Version string */
-#define LIBHEIF_VERSION "1.15.1"
+#define LIBHEIF_VERSION "1.15.2"
 
 #define LIBHEIF_PLUGIN_DIRECTORY "@PLUGIN_DIRECTORY@"
 
diff --git a/libheif/plugins/heif_encoder_aom.cc b/libheif/plugins/heif_encoder_aom.cc
index 3dd182a..5baa8db 100644
--- a/libheif/plugins/heif_encoder_aom.cc
+++ b/libheif/plugins/heif_encoder_aom.cc
@@ -36,6 +36,7 @@
 
 #include <aom/aom_encoder.h>
 #include <aom/aomcx.h>
+#include <mutex>
 
 // Detect whether the aom_codec_set_option() function is available.
 // See https://aomedia.googlesource.com/aom/+/c1d42fe6615c96fc929257ed53c41fa094f38836%5E%21/aom/aom_codec.h.
@@ -53,6 +54,13 @@ struct custom_option
 
 struct encoder_struct_aom
 {
+  ~encoder_struct_aom()
+  {
+    for (auto* error : aom_errors) {
+      delete[] error;
+    }
+  }
+
   // --- parameters
 
   bool realtime_mode;
@@ -90,9 +98,17 @@ struct encoder_struct_aom
 
   std::vector<uint8_t> compressedData;
   bool data_read = false;
+
+  // --- error message copies
+
+  std::mutex aom_errors_mutex;
+  std::vector<const char*> aom_errors;
+
+  const char* set_aom_error(const char* aom_error_detail);
 };
 
 #if defined(HAVE_AOM_CODEC_SET_OPTION)
+
 void encoder_struct_aom::add_custom_option(const custom_option& p)
 {
   // if there is already a parameter of that name, remove it from list
@@ -111,15 +127,35 @@ void encoder_struct_aom::add_custom_option(const custom_option& p)
 
 void encoder_struct_aom::add_custom_option(std::string name, std::string value)
 {
-    custom_option p;
-    p.name = name;
-    p.value = value;
-    add_custom_option(p);
+  custom_option p;
+  p.name = name;
+  p.value = value;
+  add_custom_option(p);
 }
+
 #endif
 
-//static const char* kError_out_of_memory = "Out of memory";
-static const char* kError_encode_frame = "Failed to encode frame";
+static const char* kError_undefined_error = "Undefined AOM error";
+static const char* kError_codec_enc_config_default = "Error creating the default encoder config";
+
+const char* encoder_struct_aom::set_aom_error(const char* aom_error)
+{
+  if (aom_error) {
+    // We have to make a copy because the error returned from aom_codec_error_detail() is only valid
+    // while the codec structure exists.
+
+    char* err_copy = new char[strlen(aom_error) + 1];
+    strcpy(err_copy, aom_error);
+
+    std::lock_guard<std::mutex> lock(aom_errors_mutex);
+    aom_errors.push_back(err_copy);
+
+    return err_copy;
+  }
+  else {
+    return kError_undefined_error;
+  }
+}
 
 static const char* kParam_min_q = "min-q";
 static const char* kParam_max_q = "max-q";
@@ -691,7 +727,7 @@ void aom_query_input_colorspace2(void* encoder_raw, heif_colorspace* colorspace,
 }
 
 
-static heif_error encode_frame(aom_codec_ctx_t* codec, aom_image_t* img)
+static heif_error encode_frame(encoder_struct_aom* encoder, aom_codec_ctx_t* codec, aom_image_t* img)
 {
   //aom_codec_iter_t iter = NULL;
   int frame_index = 0; // only encoding a single frame
@@ -702,9 +738,10 @@ static heif_error encode_frame(aom_codec_ctx_t* codec, aom_image_t* img)
   if (res != AOM_CODEC_OK) {
     struct heif_error err = {
         heif_error_Encoder_plugin_error,
-        heif_suberror_Unspecified,
-        kError_encode_frame
+        heif_suberror_Encoder_encoding,
+        encoder->set_aom_error(aom_codec_error_detail(codec))
     };
+    aom_codec_destroy(codec);
     return err;
   }
 
@@ -853,13 +890,13 @@ struct heif_error aom_encode_image(void* encoder_raw, const struct heif_image* i
   aom_codec_err_t res = aom_codec_enc_config_default(iface, &cfg, aomUsage);
   if (res) {
     err = {heif_error_Encoder_plugin_error,
-           heif_suberror_Unspecified,
-           "Failed to get default codec config"};
+           heif_suberror_Encoder_initialization,
+           kError_codec_enc_config_default};
     return err;
   }
 
-  int seq_profile = compute_avif_profile(heif_image_get_bits_per_pixel(image, heif_channel_Y),
-                                     heif_image_get_chroma_format(image));
+  int seq_profile = compute_avif_profile(heif_image_get_bits_per_pixel_range(image, heif_channel_Y),
+                                         heif_image_get_chroma_format(image));
 
   cfg.g_w = source_width;
   cfg.g_h = source_height;
@@ -911,9 +948,11 @@ struct heif_error aom_encode_image(void* encoder_raw, const struct heif_image* i
   }
 
   if (aom_codec_enc_init(&codec, iface, &cfg, encoder_flags)) {
+    // AOM makes sure that the error text returned by aom_codec_error_detail() is always a static
+    // text that is valid even through the codec allocation failed (#788).
     err = {heif_error_Encoder_plugin_error,
-           heif_suberror_Unspecified,
-           "Failed to initialize encoder"};
+           heif_suberror_Encoder_initialization,
+           encoder->set_aom_error(aom_codec_error_detail(&codec))};
     return err;
   }
 
@@ -983,7 +1022,12 @@ struct heif_error aom_encode_image(void* encoder_raw, const struct heif_image* i
 
   // --- encode frame
 
-  err = encode_frame(&codec, &input_image); //, frame_count++, flags, writer);
+  err = encode_frame(encoder, &codec, &input_image); //, frame_count++, flags, writer);
+
+  // Note: we are freeing the input image directly after use.
+  // This covers the usual success case and also all error cases that occur below.
+  aom_img_free(&input_image);
+
   if (err.code != heif_error_Ok) {
     return err;
   }
@@ -1019,8 +1063,9 @@ struct heif_error aom_encode_image(void* encoder_raw, const struct heif_image* i
   res = aom_codec_encode(&codec, NULL, -1, 0, flags);
   if (res != AOM_CODEC_OK) {
     err = {heif_error_Encoder_plugin_error,
-           heif_suberror_Unspecified,
-           kError_encode_frame};
+           heif_suberror_Encoder_encoding,
+           encoder->set_aom_error(aom_codec_error_detail(&codec))};
+    aom_codec_destroy(&codec);
     return err;
   }
 
@@ -1052,12 +1097,11 @@ struct heif_error aom_encode_image(void* encoder_raw, const struct heif_image* i
 
   // --- clean up
 
-  aom_img_free(&input_image);
-
   if (aom_codec_destroy(&codec)) {
+    // Note: do not call aom_codec_error_detail(), because it is not set in aom_codec_destroy(). (see #788)
     err = {heif_error_Encoder_plugin_error,
-           heif_suberror_Unspecified,
-           "Failed to destroy codec"};
+           heif_suberror_Encoder_cleanup,
+           kError_undefined_error};
     return err;
   }
 
diff --git a/libheif/plugins_windows.cc b/libheif/plugins_windows.cc
index 0a831ab..8783886 100644
--- a/libheif/plugins_windows.cc
+++ b/libheif/plugins_windows.cc
@@ -62,9 +62,7 @@ std::vector<std::string> list_all_potential_plugins_in_directory_windows(const c
   }
 #endif
 
-  int nPlugins = 0;
-
-  if ((hFind = FindFirstFile(findPattern.c_str(), &FindFileData)) != INVALID_HANDLE_VALUE) {
+ if ((hFind = FindFirstFile(findPattern.c_str(), &FindFileData)) != INVALID_HANDLE_VALUE) {
     do {
       std::string filename = directory;
       filename += '/';

More details

Full run details

Historical runs