HEIF plug-in extended to read AVCI format

AVCI is H.264 encapsulated in HEIF container
This commit is contained in:
Daniel Novomeský
2024-12-29 23:44:18 +01:00
parent c0d5b8854b
commit fe28130cb3
5 changed files with 80 additions and 54 deletions

View File

@ -111,6 +111,12 @@ if (LibHeif_FOUND)
hej2 hej2
) )
endif() endif()
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.19.0")
kimageformats_read_tests(FUZZ 4
avci
)
endif()
endif() endif()
if (LibJXL_FOUND AND LibJXLThreads_FOUND) if (LibJXL_FOUND AND LibJXLThreads_FOUND)

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

View File

@ -23,6 +23,7 @@ bool HEIFHandler::m_plugins_queried = false;
bool HEIFHandler::m_heif_decoder_available = false; bool HEIFHandler::m_heif_decoder_available = false;
bool HEIFHandler::m_heif_encoder_available = false; bool HEIFHandler::m_heif_encoder_available = false;
bool HEIFHandler::m_hej2_decoder_available = false; bool HEIFHandler::m_hej2_decoder_available = false;
bool HEIFHandler::m_avci_decoder_available = false;
extern "C" { extern "C" {
static struct heif_error heifhandler_write_callback(struct heif_context * /* ctx */, const void *data, size_t size, void *userdata) static struct heif_error heifhandler_write_callback(struct heif_context * /* ctx */, const void *data, size_t size, void *userdata)
@ -74,6 +75,11 @@ bool HEIFHandler::canRead() const
setFormat("hej2"); setFormat("hej2");
return true; return true;
} }
if (HEIFHandler::isSupportedAVCI(header)) {
setFormat("avci");
return true;
}
} }
return false; return false;
} }
@ -164,8 +170,15 @@ bool HEIFHandler::write_helper(const QImage &image)
auto cs = image.colorSpace(); auto cs = image.colorSpace();
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) { if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) {
tmpimage = image.convertedToColorSpace(QColorSpace(QColorSpace::SRgb), tmpformat); tmpimage = image.convertedToColorSpace(QColorSpace(QColorSpace::SRgb), tmpformat);
} else if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Gray
&& (image.format() == QImage::Format_Grayscale8 || image.format() == QImage::Format_Grayscale16)) {
QColorSpace::TransferFunction trc_new = cs.transferFunction();
float gamma_new = cs.gamma();
if (trc_new == QColorSpace::TransferFunction::Custom) {
trc_new = QColorSpace::TransferFunction::SRgb;
} }
else { tmpimage = image.convertedToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, trc_new, gamma_new), tmpformat);
} else {
tmpimage = image.convertToFormat(tmpformat); tmpimage = image.convertToFormat(tmpformat);
} }
#else #else
@ -380,6 +393,22 @@ bool HEIFHandler::isSupportedHEJ2(const QByteArray &header)
return false; return false;
} }
bool HEIFHandler::isSupportedAVCI(const QByteArray &header)
{
if (header.size() < 28) {
return false;
}
const char *buffer = header.constData();
if (qstrncmp(buffer + 4, "ftyp", 4) == 0) {
if (qstrncmp(buffer + 8, "avci", 4) == 0) {
return true;
}
}
return false;
}
QVariant HEIFHandler::option(ImageOption option) const QVariant HEIFHandler::option(ImageOption option) const
{ {
if (option == Quality) { if (option == Quality) {
@ -455,7 +484,7 @@ bool HEIFHandler::ensureDecoder()
} }
const QByteArray buffer = device()->readAll(); const QByteArray buffer = device()->readAll();
if (!HEIFHandler::isSupportedBMFFType(buffer) && !HEIFHandler::isSupportedHEJ2(buffer)) { if (!HEIFHandler::isSupportedBMFFType(buffer) && !HEIFHandler::isSupportedHEJ2(buffer) && !HEIFHandler::isSupportedAVCI(buffer)) {
m_parseState = ParseHeicError; m_parseState = ParseHeicError;
return false; return false;
} }
@ -808,6 +837,14 @@ bool HEIFHandler::ensureDecoder()
case 13: case 13:
q_trc = QColorSpace::TransferFunction::SRgb; q_trc = QColorSpace::TransferFunction::SRgb;
break; break;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
case 16:
q_trc = QColorSpace::TransferFunction::St2084;
break;
case 18:
q_trc = QColorSpace::TransferFunction::Hlg;
break;
#endif
default: default:
qWarning("CICP color_primaries: %d, transfer_characteristics: %d\nThe colorspace is unsupported by this plug-in yet.", qWarning("CICP color_primaries: %d, transfer_characteristics: %d\nThe colorspace is unsupported by this plug-in yet.",
nclx->color_primaries, nclx->color_primaries,
@ -850,61 +887,33 @@ bool HEIFHandler::ensureDecoder()
bool HEIFHandler::isHeifDecoderAvailable() bool HEIFHandler::isHeifDecoderAvailable()
{ {
QMutexLocker locker(&getHEIFHandlerMutex()); HEIFHandler::queryHeifLib();
if (!m_plugins_queried) {
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
if (m_initialized_count == 0) {
heif_init(nullptr);
}
#endif
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
m_hej2_decoder_available = heif_have_decoder_for_format(heif_compression_JPEG2000);
#endif
m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC);
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
m_plugins_queried = true;
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
if (m_initialized_count == 0) {
heif_deinit();
}
#endif
}
return m_heif_decoder_available; return m_heif_decoder_available;
} }
bool HEIFHandler::isHeifEncoderAvailable() bool HEIFHandler::isHeifEncoderAvailable()
{ {
QMutexLocker locker(&getHEIFHandlerMutex()); HEIFHandler::queryHeifLib();
if (!m_plugins_queried) {
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
if (m_initialized_count == 0) {
heif_init(nullptr);
}
#endif
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
m_hej2_decoder_available = heif_have_decoder_for_format(heif_compression_JPEG2000);
#endif
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC);
m_plugins_queried = true;
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
if (m_initialized_count == 0) {
heif_deinit();
}
#endif
}
return m_heif_encoder_available; return m_heif_encoder_available;
} }
bool HEIFHandler::isHej2DecoderAvailable() bool HEIFHandler::isHej2DecoderAvailable()
{
HEIFHandler::queryHeifLib();
return m_hej2_decoder_available;
}
bool HEIFHandler::isAVCIDecoderAvailable()
{
HEIFHandler::queryHeifLib();
return m_avci_decoder_available;
}
void HEIFHandler::queryHeifLib()
{ {
QMutexLocker locker(&getHEIFHandlerMutex()); QMutexLocker locker(&getHEIFHandlerMutex());
@ -919,6 +928,9 @@ bool HEIFHandler::isHej2DecoderAvailable()
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC); m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
#if LIBHEIF_HAVE_VERSION(1, 13, 0) #if LIBHEIF_HAVE_VERSION(1, 13, 0)
m_hej2_decoder_available = heif_have_decoder_for_format(heif_compression_JPEG2000); m_hej2_decoder_available = heif_have_decoder_for_format(heif_compression_JPEG2000);
#endif
#if LIBHEIF_HAVE_VERSION(1, 19, 0)
m_avci_decoder_available = heif_have_decoder_for_format(heif_compression_AVC);
#endif #endif
m_plugins_queried = true; m_plugins_queried = true;
@ -928,8 +940,6 @@ bool HEIFHandler::isHej2DecoderAvailable()
} }
#endif #endif
} }
return m_hej2_decoder_available;
} }
void HEIFHandler::startHeifLib() void HEIFHandler::startHeifLib()
@ -989,6 +999,14 @@ QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const Q
return format_cap; return format_cap;
} }
if (format == "avci") {
Capabilities format_cap;
if (HEIFHandler::isAVCIDecoderAvailable()) {
format_cap |= CanRead;
}
return format_cap;
}
if (!format.isEmpty()) { if (!format.isEmpty()) {
return {}; return {};
} }
@ -1000,11 +1018,9 @@ QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const Q
if (device->isReadable()) { if (device->isReadable()) {
const QByteArray header = device->peek(28); const QByteArray header = device->peek(28);
if (HEIFHandler::isSupportedBMFFType(header) && HEIFHandler::isHeifDecoderAvailable()) { if ((HEIFHandler::isSupportedBMFFType(header) && HEIFHandler::isHeifDecoderAvailable())
cap |= CanRead; || (HEIFHandler::isSupportedHEJ2(header) && HEIFHandler::isHej2DecoderAvailable())
} || (HEIFHandler::isSupportedAVCI(header) && HEIFHandler::isAVCIDecoderAvailable())) {
if (HEIFHandler::isSupportedHEJ2(header) && HEIFHandler::isHej2DecoderAvailable()) {
cap |= CanRead; cap |= CanRead;
} }
} }

View File

@ -31,9 +31,11 @@ public:
static bool isHeifDecoderAvailable(); static bool isHeifDecoderAvailable();
static bool isHeifEncoderAvailable(); static bool isHeifEncoderAvailable();
static bool isHej2DecoderAvailable(); static bool isHej2DecoderAvailable();
static bool isAVCIDecoderAvailable();
static bool isSupportedBMFFType(const QByteArray &header); static bool isSupportedBMFFType(const QByteArray &header);
static bool isSupportedHEJ2(const QByteArray &header); static bool isSupportedHEJ2(const QByteArray &header);
static bool isSupportedAVCI(const QByteArray &header);
private: private:
bool ensureParsed() const; bool ensureParsed() const;
@ -53,12 +55,14 @@ private:
static void startHeifLib(); static void startHeifLib();
static void finishHeifLib(); static void finishHeifLib();
static void queryHeifLib();
static size_t m_initialized_count; static size_t m_initialized_count;
static bool m_plugins_queried; static bool m_plugins_queried;
static bool m_heif_decoder_available; static bool m_heif_decoder_available;
static bool m_heif_encoder_available; static bool m_heif_encoder_available;
static bool m_hej2_decoder_available; static bool m_hej2_decoder_available;
static bool m_avci_decoder_available;
static QMutex &getHEIFHandlerMutex(); static QMutex &getHEIFHandlerMutex();
}; };