-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Add AVIF plugin (decoder + encoder using libavif) #5201
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
3878b58
e2add24
e5494a2
8b8bbba
58ef692
d6a0a15
50b993a
671e3c8
658cdf3
7225cb9
3730bf2
c40bcbf
d76ae2f
9ad8311
de4c6c1
524d802
7b73d77
a56acd8
f5dc957
8d77678
4eaa6b7
bdb24f9
ddc8e7e
b585f9e
da2e18d
9b6e575
3a9a3ab
9328932
be02830
4135664
29c158d
4c63ea6
ce6bf21
4b29af4
38f0d10
1410d23
6cbad27
19ba2dd
4508f37
7de1212
e1509ee
0590f08
5761b44
38b9941
10dfa63
9abfdbc
d80ac3c
b4eec64
d552087
79f7339
46f4508
9bebf37
5da2113
0732554
9ea5e3d
fca6df2
024a894
2ba9356
fdc68e6
9e63868
eff2680
fb096e1
1276543
c8d0408
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -7,7 +7,7 @@ | |||||||||
typedef struct { | ||||||||||
PyObject_HEAD avifEncoder *encoder; | ||||||||||
avifImage *image; | ||||||||||
int frame_index; | ||||||||||
int first_frame; | ||||||||||
} AvifEncoderObject; | ||||||||||
|
||||||||||
static PyTypeObject AvifEncoder_Type; | ||||||||||
|
@@ -16,7 +16,6 @@ | |||||||||
typedef struct { | ||||||||||
PyObject_HEAD avifDecoder *decoder; | ||||||||||
Py_buffer buffer; | ||||||||||
char *mode; | ||||||||||
} AvifDecoderObject; | ||||||||||
|
||||||||||
static PyTypeObject AvifDecoder_Type; | ||||||||||
|
@@ -37,9 +36,9 @@ | |||||||||
static int | ||||||||||
normalize_tiles_log2(int value) { | ||||||||||
if (value < 0) { | ||||||||||
return 0; | ||||||||||
} else if (value > 6) { | ||||||||||
return 6; | ||||||||||
} else { | ||||||||||
return value; | ||||||||||
} | ||||||||||
|
@@ -56,8 +55,8 @@ | |||||||||
case AVIF_RESULT_TRUNCATED_DATA: | ||||||||||
case AVIF_RESULT_NO_CONTENT: | ||||||||||
return PyExc_SyntaxError; | ||||||||||
default: | ||||||||||
return PyExc_RuntimeError; | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
|
@@ -78,15 +77,15 @@ | |||||||||
return axis ? 7 // 90 degrees anti-clockwise then swap left and right. | ||||||||||
: 5; // 90 degrees anti-clockwise then swap top and bottom. | ||||||||||
} | ||||||||||
return 6; // 90 degrees anti-clockwise. | ||||||||||
} | ||||||||||
if (angle == 2) { | ||||||||||
if (imir) { | ||||||||||
return axis | ||||||||||
? 4 // 180 degrees anti-clockwise then swap left and right. | ||||||||||
: 2; // 180 degrees anti-clockwise then swap top and bottom. | ||||||||||
} | ||||||||||
return 3; // 180 degrees anti-clockwise. | ||||||||||
} | ||||||||||
if (angle == 3) { | ||||||||||
if (imir) { | ||||||||||
|
@@ -94,7 +93,7 @@ | |||||||||
? 5 // 270 degrees anti-clockwise then swap left and right. | ||||||||||
: 7; // 270 degrees anti-clockwise then swap top and bottom. | ||||||||||
} | ||||||||||
return 8; // 270 degrees anti-clockwise. | ||||||||||
} | ||||||||||
} | ||||||||||
if (imir) { | ||||||||||
|
@@ -119,11 +118,11 @@ | |||||||||
image->imir.mode = 1; | ||||||||||
#endif | ||||||||||
break; | ||||||||||
case 3: // The 0th row is at the visual bottom of the image, and the 0th column | ||||||||||
// is the visual right-hand side. | ||||||||||
image->transformFlags |= AVIF_TRANSFORM_IROT; | ||||||||||
image->irot.angle = 2; | ||||||||||
break; | ||||||||||
case 4: // The 0th row is at the visual bottom of the image, and the 0th column | ||||||||||
// is the visual left-hand side. | ||||||||||
image->transformFlags |= AVIF_TRANSFORM_IMIR; | ||||||||||
|
@@ -134,27 +133,27 @@ | |||||||||
image->irot.angle = 1; // applied before imir according to MIAF spec | ||||||||||
// ISO/IEC 28002-12:2021 - section 7.3.6.7 | ||||||||||
break; | ||||||||||
case 6: // The 0th row is the visual right-hand side of the image, and the 0th | ||||||||||
// column is the visual top. | ||||||||||
image->transformFlags |= AVIF_TRANSFORM_IROT; | ||||||||||
image->irot.angle = 3; | ||||||||||
break; | ||||||||||
case 7: // The 0th row is the visual right-hand side of the image, and the 0th | ||||||||||
// column is the visual bottom. | ||||||||||
image->transformFlags |= AVIF_TRANSFORM_IROT | AVIF_TRANSFORM_IMIR; | ||||||||||
image->irot.angle = 3; // applied before imir according to MIAF spec | ||||||||||
// ISO/IEC 28002-12:2021 - section 7.3.6.7 | ||||||||||
break; | ||||||||||
case 8: // The 0th row is the visual left-hand side of the image, and the 0th | ||||||||||
// column is the visual bottom. | ||||||||||
image->transformFlags |= AVIF_TRANSFORM_IROT; | ||||||||||
image->irot.angle = 1; | ||||||||||
break; | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
static int | ||||||||||
_codec_available(const char *name, uint32_t flags) { | ||||||||||
_codec_available(const char *name, avifCodecFlags flags) { | ||||||||||
avifCodecChoice codec = avifCodecChoiceFromName(name); | ||||||||||
if (codec == AVIF_CODEC_CHOICE_AUTO) { | ||||||||||
return 0; | ||||||||||
|
@@ -188,38 +187,38 @@ | |||||||||
Py_ssize_t i, size; | ||||||||||
PyObject *keyval, *py_key, *py_val; | ||||||||||
if (!PyTuple_Check(opts)) { | ||||||||||
PyErr_SetString(PyExc_ValueError, "Invalid advanced codec options"); | ||||||||||
return 1; | ||||||||||
} | ||||||||||
size = PyTuple_GET_SIZE(opts); | ||||||||||
|
||||||||||
for (i = 0; i < size; i++) { | ||||||||||
keyval = PyTuple_GetItem(opts, i); | ||||||||||
if (!PyTuple_Check(keyval) || PyTuple_GET_SIZE(keyval) != 2) { | ||||||||||
PyErr_SetString(PyExc_ValueError, "Invalid advanced codec options"); | ||||||||||
return 1; | ||||||||||
} | ||||||||||
py_key = PyTuple_GetItem(keyval, 0); | ||||||||||
py_val = PyTuple_GetItem(keyval, 1); | ||||||||||
if (!PyUnicode_Check(py_key) || !PyUnicode_Check(py_val)) { | ||||||||||
PyErr_SetString(PyExc_ValueError, "Invalid advanced codec options"); | ||||||||||
return 1; | ||||||||||
} | ||||||||||
const char *key = PyUnicode_AsUTF8(py_key); | ||||||||||
const char *val = PyUnicode_AsUTF8(py_val); | ||||||||||
if (key == NULL || val == NULL) { | ||||||||||
PyErr_SetString(PyExc_ValueError, "Invalid advanced codec options"); | ||||||||||
return 1; | ||||||||||
} | ||||||||||
|
||||||||||
avifResult result = avifEncoderSetCodecSpecificOption(encoder, key, val); | ||||||||||
if (result != AVIF_RESULT_OK) { | ||||||||||
PyErr_Format( | ||||||||||
exc_type_for_avif_result(result), | ||||||||||
"Setting advanced codec options failed: %s", | ||||||||||
avifResultToString(result) | ||||||||||
); | ||||||||||
return 1; | ||||||||||
} | ||||||||||
} | ||||||||||
return 0; | ||||||||||
|
@@ -252,7 +251,7 @@ | |||||||||
|
||||||||||
if (!PyArg_ParseTuple( | ||||||||||
args, | ||||||||||
"IIsiiissiiOOy*y*iy*O", | ||||||||||
"(II)siiissiiOOy*y*iy*O", | ||||||||||
&width, | ||||||||||
&height, | ||||||||||
&subsampling, | ||||||||||
|
@@ -303,9 +302,9 @@ | |||||||||
|
||||||||||
// Validate canvas dimensions | ||||||||||
if (width <= 0 || height <= 0) { | ||||||||||
PyErr_SetString(PyExc_ValueError, "invalid canvas dimensions"); | ||||||||||
avifImageDestroy(image); | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
image->width = width; | ||||||||||
image->height = height; | ||||||||||
|
@@ -351,9 +350,9 @@ | |||||||||
if (advanced != Py_None) { | ||||||||||
#if AVIF_VERSION >= 80200 | ||||||||||
if (_add_codec_specific_options(encoder, advanced)) { | ||||||||||
avifImageDestroy(image); | ||||||||||
avifEncoderDestroy(encoder); | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
#else | ||||||||||
PyErr_SetString( | ||||||||||
|
@@ -367,29 +366,29 @@ | |||||||||
|
||||||||||
self = PyObject_New(AvifEncoderObject, &AvifEncoder_Type); | ||||||||||
if (!self) { | ||||||||||
PyErr_SetString(PyExc_RuntimeError, "could not create encoder object"); | ||||||||||
avifImageDestroy(image); | ||||||||||
avifEncoderDestroy(encoder); | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
self->frame_index = -1; | ||||||||||
self->first_frame = 1; | ||||||||||
|
||||||||||
avifResult result; | ||||||||||
if (icc_buffer.len) { | ||||||||||
result = avifImageSetProfileICC(image, icc_buffer.buf, icc_buffer.len); | ||||||||||
if (result != AVIF_RESULT_OK) { | ||||||||||
PyErr_Format( | ||||||||||
exc_type_for_avif_result(result), | ||||||||||
"Setting ICC profile failed: %s", | ||||||||||
avifResultToString(result) | ||||||||||
); | ||||||||||
avifImageDestroy(image); | ||||||||||
avifEncoderDestroy(encoder); | ||||||||||
PyBuffer_Release(&icc_buffer); | ||||||||||
PyBuffer_Release(&exif_buffer); | ||||||||||
PyBuffer_Release(&xmp_buffer); | ||||||||||
PyObject_Del(self); | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
// colorPrimaries and transferCharacteristics are ignored when an ICC | ||||||||||
// profile is present, so set them to UNSPECIFIED. | ||||||||||
|
@@ -405,17 +404,17 @@ | |||||||||
if (exif_buffer.len) { | ||||||||||
result = avifImageSetMetadataExif(image, exif_buffer.buf, exif_buffer.len); | ||||||||||
if (result != AVIF_RESULT_OK) { | ||||||||||
PyErr_Format( | ||||||||||
exc_type_for_avif_result(result), | ||||||||||
"Setting EXIF data failed: %s", | ||||||||||
avifResultToString(result) | ||||||||||
); | ||||||||||
avifImageDestroy(image); | ||||||||||
avifEncoderDestroy(encoder); | ||||||||||
PyBuffer_Release(&exif_buffer); | ||||||||||
PyBuffer_Release(&xmp_buffer); | ||||||||||
PyObject_Del(self); | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
} | ||||||||||
PyBuffer_Release(&exif_buffer); | ||||||||||
|
@@ -423,16 +422,16 @@ | |||||||||
if (xmp_buffer.len) { | ||||||||||
result = avifImageSetMetadataXMP(image, xmp_buffer.buf, xmp_buffer.len); | ||||||||||
if (result != AVIF_RESULT_OK) { | ||||||||||
PyErr_Format( | ||||||||||
exc_type_for_avif_result(result), | ||||||||||
"Setting XMP data failed: %s", | ||||||||||
avifResultToString(result) | ||||||||||
); | ||||||||||
avifImageDestroy(image); | ||||||||||
avifEncoderDestroy(encoder); | ||||||||||
PyBuffer_Release(&xmp_buffer); | ||||||||||
PyObject_Del(self); | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
} | ||||||||||
PyBuffer_Release(&xmp_buffer); | ||||||||||
|
@@ -466,10 +465,9 @@ | |||||||||
unsigned int width; | ||||||||||
unsigned int height; | ||||||||||
char *mode; | ||||||||||
PyObject *is_single_frame = NULL; | ||||||||||
unsigned int is_single_frame; | ||||||||||
PyObject *ret = Py_None; | ||||||||||
|
||||||||||
int is_first_frame; | ||||||||||
avifRGBImage rgb; | ||||||||||
avifResult result; | ||||||||||
|
||||||||||
|
@@ -479,7 +477,7 @@ | |||||||||
|
||||||||||
if (!PyArg_ParseTuple( | ||||||||||
args, | ||||||||||
"z#IIIsO", | ||||||||||
"y#I(II)sp", | ||||||||||
(char **)&rgb_bytes, | ||||||||||
&size, | ||||||||||
&duration, | ||||||||||
|
@@ -488,11 +486,9 @@ | |||||||||
&mode, | ||||||||||
&is_single_frame | ||||||||||
)) { | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
|
||||||||||
is_first_frame = self->frame_index == -1; | ||||||||||
|
||||||||||
if (image->width != width || image->height != height) { | ||||||||||
PyErr_Format( | ||||||||||
PyExc_ValueError, | ||||||||||
|
@@ -505,7 +501,7 @@ | |||||||||
return NULL; | ||||||||||
} | ||||||||||
|
||||||||||
if (is_first_frame) { | ||||||||||
if (self->first_frame) { | ||||||||||
// If we don't have an image populated with yuv planes, this is the first frame | ||||||||||
frame = image; | ||||||||||
} else { | ||||||||||
|
@@ -538,25 +534,25 @@ | |||||||||
|
||||||||||
result = avifRGBImageAllocatePixels(&rgb); | ||||||||||
if (result != AVIF_RESULT_OK) { | ||||||||||
PyErr_Format( | ||||||||||
exc_type_for_avif_result(result), | ||||||||||
"Pixel allocation failed: %s", | ||||||||||
avifResultToString(result) | ||||||||||
); | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
|
||||||||||
if (rgb.rowBytes * rgb.height != size) { | ||||||||||
PyErr_Format( | ||||||||||
PyExc_RuntimeError, | ||||||||||
"rgb data is incorrect size: %u * %u (%u) != %u", | ||||||||||
rgb.rowBytes, | ||||||||||
rgb.height, | ||||||||||
rgb.rowBytes * rgb.height, | ||||||||||
size | ||||||||||
); | ||||||||||
ret = NULL; | ||||||||||
goto end; | ||||||||||
} | ||||||||||
|
||||||||||
// rgb.pixels is safe for writes | ||||||||||
|
@@ -567,17 +563,17 @@ | |||||||||
Py_END_ALLOW_THREADS; | ||||||||||
|
||||||||||
if (result != AVIF_RESULT_OK) { | ||||||||||
PyErr_Format( | ||||||||||
exc_type_for_avif_result(result), | ||||||||||
"Conversion to YUV failed: %s", | ||||||||||
avifResultToString(result) | ||||||||||
); | ||||||||||
ret = NULL; | ||||||||||
goto end; | ||||||||||
} | ||||||||||
|
||||||||||
uint32_t addImageFlags = AVIF_ADD_IMAGE_FLAG_NONE; | ||||||||||
if (PyObject_IsTrue(is_single_frame)) { | ||||||||||
if (is_single_frame) { | ||||||||||
addImageFlags |= AVIF_ADD_IMAGE_FLAG_SINGLE; | ||||||||||
} | ||||||||||
|
||||||||||
|
@@ -597,12 +593,12 @@ | |||||||||
|
||||||||||
end: | ||||||||||
avifRGBImageFreePixels(&rgb); | ||||||||||
if (!is_first_frame) { | ||||||||||
if (!self->first_frame) { | ||||||||||
avifImageDestroy(frame); | ||||||||||
} | ||||||||||
|
||||||||||
if (ret == Py_None) { | ||||||||||
self->frame_index++; | ||||||||||
self->first_frame = 0; | ||||||||||
Py_RETURN_NONE; | ||||||||||
} else { | ||||||||||
return ret; | ||||||||||
|
@@ -622,13 +618,13 @@ | |||||||||
Py_END_ALLOW_THREADS; | ||||||||||
|
||||||||||
if (result != AVIF_RESULT_OK) { | ||||||||||
PyErr_Format( | ||||||||||
exc_type_for_avif_result(result), | ||||||||||
"Failed to finish encoding: %s", | ||||||||||
avifResultToString(result) | ||||||||||
); | ||||||||||
avifRWDataFree(&raw); | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
|
||||||||||
ret = PyBytes_FromStringAndSize((char *)raw.data, raw.size); | ||||||||||
|
@@ -663,9 +659,9 @@ | |||||||||
|
||||||||||
self = PyObject_New(AvifDecoderObject, &AvifDecoder_Type); | ||||||||||
if (!self) { | ||||||||||
PyErr_SetString(PyExc_RuntimeError, "could not create decoder object"); | ||||||||||
PyBuffer_Release(&buffer); | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
|
||||||||||
decoder = avifDecoderCreate(); | ||||||||||
|
@@ -684,15 +680,15 @@ | |||||||||
|
||||||||||
result = avifDecoderSetIOMemory(decoder, buffer.buf, buffer.len); | ||||||||||
if (result != AVIF_RESULT_OK) { | ||||||||||
PyErr_Format( | ||||||||||
exc_type_for_avif_result(result), | ||||||||||
"Setting IO memory failed: %s", | ||||||||||
avifResultToString(result) | ||||||||||
); | ||||||||||
avifDecoderDestroy(decoder); | ||||||||||
PyBuffer_Release(&buffer); | ||||||||||
PyObject_Del(self); | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
|
||||||||||
result = avifDecoderParse(decoder); | ||||||||||
|
@@ -708,12 +704,6 @@ | |||||||||
return NULL; | ||||||||||
} | ||||||||||
|
||||||||||
if (decoder->alphaPresent) { | ||||||||||
self->mode = "RGBA"; | ||||||||||
} else { | ||||||||||
self->mode = "RGB"; | ||||||||||
} | ||||||||||
|
||||||||||
self->decoder = decoder; | ||||||||||
self->buffer = buffer; | ||||||||||
|
||||||||||
|
@@ -757,7 +747,7 @@ | |||||||||
image->width, | ||||||||||
image->height, | ||||||||||
decoder->imageCount, | ||||||||||
self->mode, | ||||||||||
decoder->alphaPresent == AVIF_TRUE ? "RGBA" : "RGB", | ||||||||||
radarhere marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
NULL == icc ? Py_None : icc, | ||||||||||
NULL == exif ? Py_None : exif, | ||||||||||
irot_imir_to_exif_orientation(image), | ||||||||||
|
@@ -785,42 +775,36 @@ | |||||||||
decoder = self->decoder; | ||||||||||
|
||||||||||
if (!PyArg_ParseTuple(args, "I", &frame_index)) { | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
|
||||||||||
result = avifDecoderNthImage(decoder, frame_index); | ||||||||||
if (result != AVIF_RESULT_OK) { | ||||||||||
PyErr_Format( | ||||||||||
exc_type_for_avif_result(result), | ||||||||||
"Failed to decode frame %u: %s", | ||||||||||
decoder->imageIndex + 1, | ||||||||||
frame_index, | ||||||||||
avifResultToString(result) | ||||||||||
); | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
|
||||||||||
image = decoder->image; | ||||||||||
|
||||||||||
memset(&rgb, 0, sizeof(rgb)); | ||||||||||
avifRGBImageSetDefaults(&rgb, image); | ||||||||||
|
||||||||||
rgb.depth = 8; | ||||||||||
|
||||||||||
if (decoder->alphaPresent) { | ||||||||||
rgb.format = AVIF_RGB_FORMAT_RGBA; | ||||||||||
} else { | ||||||||||
rgb.format = AVIF_RGB_FORMAT_RGB; | ||||||||||
rgb.ignoreAlpha = AVIF_TRUE; | ||||||||||
} | ||||||||||
rgb.format = | ||||||||||
decoder->alphaPresent == AVIF_TRUE ? AVIF_RGB_FORMAT_RGBA : AVIF_RGB_FORMAT_RGB; | ||||||||||
radarhere marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
result = avifRGBImageAllocatePixels(&rgb); | ||||||||||
if (result != AVIF_RESULT_OK) { | ||||||||||
PyErr_Format( | ||||||||||
exc_type_for_avif_result(result), | ||||||||||
"Pixel allocation failed: %s", | ||||||||||
avifResultToString(result) | ||||||||||
); | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
|
||||||||||
Py_BEGIN_ALLOW_THREADS; | ||||||||||
|
@@ -828,18 +812,18 @@ | |||||||||
Py_END_ALLOW_THREADS; | ||||||||||
|
||||||||||
if (result != AVIF_RESULT_OK) { | ||||||||||
PyErr_Format( | ||||||||||
exc_type_for_avif_result(result), | ||||||||||
"Conversion from YUV failed: %s", | ||||||||||
avifResultToString(result) | ||||||||||
); | ||||||||||
avifRGBImageFreePixels(&rgb); | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
|
||||||||||
if (rgb.height > PY_SSIZE_T_MAX / rgb.rowBytes) { | ||||||||||
PyErr_SetString(PyExc_MemoryError, "Integer overflow in pixel size"); | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
|
||||||||||
size = rgb.rowBytes * rgb.height; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this guaranteed to not overflow, even in the face of invalid input? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. libavif currently restricts images to a maximum of 2^28 pixels. If the dimensions are larger than 16384x16384 then the function that sets There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added here Lines 619 to 622 in b84a8e0
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Basically, I'm the one who will get a CVE on this if there's a problem, and I'd like really clear guidelines about what the assumptions are for sizes of things and where they come from for dangerous operations like memset, malloc, and pointer reads/writes. This isn't so much for now, but a couple years down the line, things need to be clear. This will be fuzzed, this will be run under valgrind, so hopefully there won't be problems. I've basically had to reverse engineer how SgiRleDecode works over the last month or so, and I'd like to be preventing that sort of experience in the future. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does raising a |
||||||||||
|
@@ -912,7 +896,7 @@ | |||||||||
static int | ||||||||||
setup_module(PyObject *m) { | ||||||||||
if (PyType_Ready(&AvifDecoder_Type) < 0 || PyType_Ready(&AvifEncoder_Type) < 0) { | ||||||||||
return -1; | ||||||||||
} | ||||||||||
|
||||||||||
PyObject *d = PyModule_GetDict(m); | ||||||||||
|
@@ -936,9 +920,13 @@ | |||||||||
|
||||||||||
m = PyModule_Create(&module_def); | ||||||||||
if (setup_module(m) < 0) { | ||||||||||
Py_DECREF(m); | ||||||||||
return NULL; | ||||||||||
} | ||||||||||
|
||||||||||
#ifdef Py_GIL_DISABLED | ||||||||||
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); | ||||||||||
#endif | ||||||||||
|
||||||||||
return m; | ||||||||||
} |
Uh oh!
There was an error while loading. Please reload this page.