/src/libheif/libheif/plugins/decoder_libde265.cc
Line | Count | Source |
1 | | /* |
2 | | * HEIF codec. |
3 | | * Copyright (c) 2017 Dirk Farin <dirk.farin@gmail.com> |
4 | | * |
5 | | * This file is part of libheif. |
6 | | * |
7 | | * libheif is free software: you can redistribute it and/or modify |
8 | | * it under the terms of the GNU Lesser General Public License as |
9 | | * published by the Free Software Foundation, either version 3 of |
10 | | * the License, or (at your option) any later version. |
11 | | * |
12 | | * libheif is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public License |
18 | | * along with libheif. If not, see <http://www.gnu.org/licenses/>. |
19 | | */ |
20 | | |
21 | | #include "libheif/heif.h" |
22 | | #include "libheif/heif_plugin.h" |
23 | | #include "decoder_libde265.h" |
24 | | #include <cassert> |
25 | | #include <memory> |
26 | | #include <cstring> |
27 | | #include <limits> |
28 | | #include <string> |
29 | | |
30 | | #include <libde265/de265.h> |
31 | | |
32 | | |
33 | | |
34 | | struct libde265_decoder |
35 | | { |
36 | | de265_decoder_context* ctx; |
37 | | bool strict_decoding = false; |
38 | | std::string error_message; |
39 | | }; |
40 | | |
41 | | static const char kEmptyString[] = ""; |
42 | | static const char kSuccess[] = "Success"; |
43 | | |
44 | | static const int LIBDE265_PLUGIN_PRIORITY = 100; |
45 | | |
46 | 0 | #define MAX_PLUGIN_NAME_LENGTH 80 |
47 | | |
48 | | static char plugin_name[MAX_PLUGIN_NAME_LENGTH]; |
49 | | static constexpr char version_prefix[] = ", version "; |
50 | | |
51 | | static const char* libde265_plugin_name() |
52 | 0 | { |
53 | 0 | strcpy(plugin_name, "libde265 HEVC decoder"); |
54 | |
|
55 | 0 | const char* libde265_version = de265_get_version(); |
56 | |
|
57 | 0 | if (strlen(plugin_name) + strlen(libde265_version) + (sizeof(version_prefix) - 1) < MAX_PLUGIN_NAME_LENGTH) { |
58 | 0 | strcat(plugin_name, version_prefix); |
59 | 0 | strcat(plugin_name, libde265_version); |
60 | 0 | } |
61 | |
|
62 | 0 | return plugin_name; |
63 | 0 | } |
64 | | |
65 | | |
66 | | static void libde265_init_plugin() |
67 | 2.42k | { |
68 | 2.42k | de265_init(); |
69 | 2.42k | } |
70 | | |
71 | | |
72 | | static void libde265_deinit_plugin() |
73 | 2.42k | { |
74 | 2.42k | de265_free(); |
75 | 2.42k | } |
76 | | |
77 | | |
78 | | static int libde265_does_support_format(heif_compression_format format) |
79 | 1.21k | { |
80 | 1.21k | if (format == heif_compression_HEVC) { |
81 | 702 | return LIBDE265_PLUGIN_PRIORITY; |
82 | 702 | } |
83 | 513 | else { |
84 | 513 | return 0; |
85 | 513 | } |
86 | 1.21k | } |
87 | | |
88 | | |
89 | | [[maybe_unused]] |
90 | | static int libde265_does_support_format2(const heif_decoder_plugin_compressed_format_description* format) |
91 | 0 | { |
92 | 0 | return libde265_does_support_format(format->format); |
93 | 0 | } |
94 | | |
95 | | |
96 | | |
97 | | static heif_error convert_libde265_image_to_heif_image(libde265_decoder* decoder, |
98 | | const de265_image* de265img, |
99 | | heif_image** image, |
100 | | const heif_security_limits* limits) |
101 | 319 | { |
102 | 319 | bool is_mono = (de265_get_chroma_format(de265img) == de265_chroma_mono); |
103 | | |
104 | 319 | heif_error err; |
105 | 319 | err = heif_image_create(de265_get_image_width(de265img, 0), |
106 | 319 | de265_get_image_height(de265img, 0), |
107 | 319 | is_mono ? heif_colorspace_monochrome : heif_colorspace_YCbCr, |
108 | 319 | (heif_chroma) de265_get_chroma_format(de265img), |
109 | 319 | image); |
110 | 319 | if (err.code) { |
111 | 0 | return err; |
112 | 0 | } |
113 | | |
114 | | // --- transfer data from de265_image to HeifPixelImage |
115 | | |
116 | 319 | heif_channel channel2plane[3] = { |
117 | 319 | heif_channel_Y, |
118 | 319 | heif_channel_Cb, |
119 | 319 | heif_channel_Cr |
120 | 319 | }; |
121 | | |
122 | | |
123 | 319 | int bpp = de265_get_bits_per_pixel(de265img, 0); |
124 | | |
125 | 319 | int num_planes = (is_mono ? 1 : 3); |
126 | | |
127 | 1.08k | for (int c = 0; c < num_planes; c++) { |
128 | 824 | if (de265_get_bits_per_pixel(de265img, c) != bpp) { |
129 | 61 | heif_image_release(*image); |
130 | 61 | err = {heif_error_Unsupported_feature, |
131 | 61 | heif_suberror_Unsupported_color_conversion, |
132 | 61 | "Channels with different number of bits per pixel are not supported"}; |
133 | 61 | return err; |
134 | 61 | } |
135 | | |
136 | 763 | int stride; |
137 | 763 | const uint8_t* data = de265_get_image_plane(de265img, c, &stride); |
138 | | |
139 | 763 | int w = de265_get_image_width(de265img, c); |
140 | 763 | int h = de265_get_image_height(de265img, c); |
141 | 763 | if (w <= 0 || h <= 0) { |
142 | 0 | heif_image_release(*image); |
143 | 0 | err = {heif_error_Decoder_plugin_error, |
144 | 0 | heif_suberror_Invalid_image_size, |
145 | 0 | kEmptyString}; |
146 | 0 | return err; |
147 | 0 | } |
148 | | |
149 | 763 | err = heif_image_add_plane_safe(*image, channel2plane[c], w,h, bpp, limits); |
150 | 763 | if (err.code) { |
151 | | // copy error message to decoder object because heif_image will be released |
152 | 0 | decoder->error_message = err.message; |
153 | 0 | err.message = decoder->error_message.c_str(); |
154 | |
|
155 | 0 | heif_image_release(*image); |
156 | 0 | return err; |
157 | 0 | } |
158 | | |
159 | 763 | size_t dst_stride; |
160 | 763 | uint8_t* dst_mem = heif_image_get_plane2(*image, channel2plane[c], &dst_stride); |
161 | | |
162 | 763 | int bytes_per_pixel = (bpp + 7) / 8; |
163 | | |
164 | 135k | for (int y = 0; y < h; y++) { |
165 | 134k | memcpy(dst_mem + y * dst_stride, data + static_cast<size_t>(y) * stride, static_cast<size_t>(w) * bytes_per_pixel); |
166 | 134k | } |
167 | 763 | } |
168 | | |
169 | | |
170 | 258 | return {heif_error_Ok, heif_suberror_Unspecified, kSuccess}; |
171 | 319 | } |
172 | | |
173 | | |
174 | | |
175 | | // Create a new decoder context for decoding an image |
176 | | heif_error libde265_new_decoder2(void** dec, const heif_decoder_plugin_options* options) |
177 | 605 | { |
178 | 605 | libde265_decoder* decoder = new libde265_decoder(); |
179 | 605 | heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess}; |
180 | | |
181 | 605 | decoder->ctx = de265_new_decoder(); |
182 | | |
183 | 605 | #if LIBDE265_NUMERIC_VERSION >= 0x01010000 |
184 | | // Pass libheif's max_image_size_pixels through to libde265 so the decoder |
185 | | // rejects bitstreams whose SPS declares an image far larger than what the |
186 | | // surrounding HEIF file claims. libheif tightens this limit per-decode to |
187 | | // ispe + one coding-unit margin (see tighten_image_size_limit_for_ispe), |
188 | | // so the padding margin is already baked into the value we see here. |
189 | 605 | { |
190 | 605 | const heif_security_limits* limits = options->limits ? options->limits : heif_get_global_security_limits(); |
191 | 605 | de265_security_limits* de265_limits = de265_get_security_limits(decoder->ctx); |
192 | 605 | if (limits->max_image_size_pixels > std::numeric_limits<uint32_t>::max()) { |
193 | 3 | de265_limits->max_image_size_pixels = 0; |
194 | 3 | } |
195 | 602 | else { |
196 | 602 | de265_limits->max_image_size_pixels = static_cast<uint32_t>(limits->max_image_size_pixels); |
197 | 602 | } |
198 | 605 | } |
199 | 605 | #endif |
200 | | |
201 | | #if defined(__EMSCRIPTEN__) |
202 | | // Speed up decoding from JavaScript. |
203 | | de265_set_parameter_bool(decoder->ctx, DE265_DECODER_PARAM_DISABLE_DEBLOCKING, 1); |
204 | | de265_set_parameter_bool(decoder->ctx, DE265_DECODER_PARAM_DISABLE_SAO, 1); |
205 | | #else |
206 | 605 | int nThreads = (options->num_threads ? options->num_threads : 1); |
207 | | |
208 | | // Worker threads are not supported when running on Emscripten. |
209 | 605 | de265_start_worker_threads(decoder->ctx, nThreads); |
210 | 605 | #endif |
211 | | |
212 | 605 | decoder->strict_decoding = options->strict_decoding; |
213 | | |
214 | 605 | *dec = decoder; |
215 | 605 | return err; |
216 | 605 | } |
217 | | |
218 | | |
219 | | static heif_error libde265_new_decoder(void** dec) |
220 | 0 | { |
221 | 0 | heif_decoder_plugin_options options{}; |
222 | 0 | options.format = heif_compression_HEVC; |
223 | 0 | options.num_threads = 0; |
224 | 0 | options.strict_decoding = false; |
225 | |
|
226 | 0 | return libde265_new_decoder2(dec, &options); |
227 | 0 | } |
228 | | |
229 | | static void libde265_free_decoder(void* decoder_raw) |
230 | 605 | { |
231 | 605 | libde265_decoder* decoder = (libde265_decoder*) decoder_raw; |
232 | | |
233 | 605 | de265_error err = de265_free_decoder(decoder->ctx); |
234 | 605 | (void) err; |
235 | | |
236 | 605 | delete decoder; |
237 | 605 | } |
238 | | |
239 | | |
240 | | void libde265_set_strict_decoding(void* decoder_raw, int flag) |
241 | 0 | { |
242 | 0 | libde265_decoder* decoder = (libde265_decoder*) decoder_raw; |
243 | |
|
244 | 0 | decoder->strict_decoding = flag; |
245 | 0 | } |
246 | | |
247 | | |
248 | | #if LIBDE265_NUMERIC_VERSION >= 0x02000000 |
249 | | |
250 | | static heif_error libde265_v2_push_data(void* decoder_raw, const void* data, size_t size) |
251 | | { |
252 | | libde265_decoder* decoder = (libde265_decoder*)decoder_raw; |
253 | | |
254 | | const uint8_t* cdata = (const uint8_t*)data; |
255 | | |
256 | | size_t ptr=0; |
257 | | while (ptr < size) { |
258 | | if (4 > size - ptr) { |
259 | | return { heif_error_Decoder_plugin_error, |
260 | | heif_suberror_End_of_data, |
261 | | kEmptyString }; |
262 | | } |
263 | | |
264 | | // TODO: the size of the NAL unit length variable is defined in the hvcC header. |
265 | | // We should not assume that it is always 4 bytes. |
266 | | uint32_t nal_size = (uint32_t)((cdata[ptr]<<24) | (cdata[ptr+1]<<16) | (cdata[ptr+2]<<8) | (cdata[ptr+3])); |
267 | | ptr+=4; |
268 | | |
269 | | if (nal_size > size - ptr) { |
270 | | //sstr << "NAL size (" << size32 << ") exceeds available data in file (" |
271 | | //<< data_bytes_left_to_read << ")"; |
272 | | |
273 | | return { heif_error_Decoder_plugin_error, |
274 | | heif_suberror_End_of_data, |
275 | | kEmptyString }; |
276 | | } |
277 | | |
278 | | de265_push_NAL(decoder->ctx, cdata+ptr, nal_size, 0, nullptr); |
279 | | ptr += nal_size; |
280 | | } |
281 | | |
282 | | |
283 | | return { heif_error_Ok, heif_suberror_Unspecified, kSuccess }; |
284 | | } |
285 | | |
286 | | |
287 | | static heif_error libde265_v2_decode_next_image(void* decoder_raw, |
288 | | heif_image** out_img, |
289 | | const heif_security_limits* limits) |
290 | | { |
291 | | libde265_decoder* decoder = (libde265_decoder*)decoder_raw; |
292 | | |
293 | | de265_push_end_of_stream(decoder->ctx); |
294 | | |
295 | | int action = de265_get_action(decoder->ctx, 1); |
296 | | |
297 | | // TODO: read NCLX from h265 bitstream |
298 | | |
299 | | // TODO(farindk): Set "err" if no image was decoded. |
300 | | if (action==de265_action_get_image) { |
301 | | const de265_image* img = de265_get_next_picture(decoder->ctx); |
302 | | if (img) { |
303 | | heif_error err = convert_libde265_image_to_heif_image(decoder, img, |
304 | | out_img, limits); |
305 | | de265_release_picture(img); |
306 | | |
307 | | return err; |
308 | | } |
309 | | } |
310 | | |
311 | | return { heif_error_Decoder_plugin_error, heif_suberror_Unspecified, kEmptyString }; |
312 | | } |
313 | | |
314 | | static heif_error libde265_v2_decode_image(void* decoder_raw, |
315 | | heif_image** out_img) |
316 | | { |
317 | | auto* limits = heif_get_global_security_limits(); |
318 | | return libde265_v2_decode_next_image(decoder_raw, out_img, limits); |
319 | | } |
320 | | #else |
321 | | |
322 | | static heif_error libde265_v1_push_data2(void* decoder_raw, const void* data, size_t size, uintptr_t user_data) |
323 | 531 | { |
324 | 531 | libde265_decoder* decoder = (libde265_decoder*) decoder_raw; |
325 | | |
326 | 531 | const uint8_t* cdata = (const uint8_t*) data; |
327 | | |
328 | 531 | size_t ptr = 0; |
329 | 3.18k | while (ptr < size) { |
330 | 2.65k | if (4 > size - ptr) { |
331 | 0 | return { |
332 | 0 | heif_error_Decoder_plugin_error, |
333 | 0 | heif_suberror_End_of_data, |
334 | 0 | kEmptyString |
335 | 0 | }; |
336 | 0 | } |
337 | | |
338 | 2.65k | uint32_t nal_size = static_cast<uint32_t>((cdata[ptr] << 24) | (cdata[ptr + 1] << 16) | (cdata[ptr + 2] << 8) | (cdata[ptr + 3])); |
339 | 2.65k | ptr += 4; |
340 | | |
341 | 2.65k | if (nal_size > size - ptr) { |
342 | 0 | return { |
343 | 0 | heif_error_Decoder_plugin_error, |
344 | 0 | heif_suberror_End_of_data, |
345 | 0 | kEmptyString |
346 | 0 | }; |
347 | 0 | } |
348 | | |
349 | | #if 0 |
350 | | FILE* fh = fopen("data.h265", "a"); |
351 | | fputc(0, fh); |
352 | | fputc(0, fh); |
353 | | fputc(1, fh); |
354 | | fwrite(cdata + ptr, nal_size, 1, fh); |
355 | | fclose(fh); |
356 | | |
357 | | printf("put nal with size %d %x\n", nal_size, *(cdata+ptr)); |
358 | | #endif |
359 | | |
360 | 2.65k | de265_push_NAL(decoder->ctx, cdata + ptr, nal_size, 0, (void*)user_data); |
361 | 2.65k | ptr += nal_size; |
362 | 2.65k | } |
363 | | |
364 | | // TODO(farindk): Set "err" if data could not be pushed |
365 | | //de265_push_data(decoder->ctx, data, size, 0, nullptr); |
366 | | |
367 | 531 | return {heif_error_Ok, heif_suberror_Unspecified, kSuccess}; |
368 | 531 | } |
369 | | |
370 | | static heif_error libde265_v1_push_data(void* decoder_raw, const void* data, size_t size) |
371 | 0 | { |
372 | 0 | return libde265_v1_push_data2(decoder_raw, data, size, 0); |
373 | 0 | } |
374 | | |
375 | | static heif_error libde265_flush_data(void* decoder_raw) |
376 | 531 | { |
377 | 531 | libde265_decoder* decoder = (libde265_decoder*) decoder_raw; |
378 | | |
379 | 531 | de265_flush_data(decoder->ctx); |
380 | | |
381 | 531 | return heif_error_ok; |
382 | 531 | } |
383 | | |
384 | | |
385 | | |
386 | | static heif_error libde265_v1_decode_next_image2(void* decoder_raw, |
387 | | heif_image** out_img, |
388 | | uintptr_t* out_user_data, |
389 | | const heif_security_limits* limits) |
390 | 11.2k | { |
391 | 11.2k | libde265_decoder* decoder = (libde265_decoder*) decoder_raw; |
392 | 11.2k | heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess}; |
393 | | |
394 | | // TODO(251119) : de265_flush_data(decoder->ctx); |
395 | | |
396 | | // TODO(farindk): Set "err" if no image was decoded. |
397 | 11.2k | int more; |
398 | 11.2k | de265_error decode_err; |
399 | 11.2k | *out_img = nullptr; |
400 | 12.7k | do { |
401 | 12.7k | more = 0; |
402 | 12.7k | decode_err = de265_decode(decoder->ctx, &more); |
403 | 12.7k | if (decode_err != DE265_OK) { |
404 | | // printf("Error decoding: %s (%d)\n", de265_get_error_text(decode_err), decode_err); |
405 | 828 | break; |
406 | 828 | } |
407 | | |
408 | | // TODO: read NCLX from h265 bitstream |
409 | | |
410 | 11.9k | const de265_image* image = de265_get_next_picture(decoder->ctx); |
411 | 11.9k | if (image) { |
412 | | // TODO(farindk): Should we return the first image instead? |
413 | 319 | if (*out_img) { |
414 | 0 | heif_image_release(*out_img); |
415 | 0 | } |
416 | | |
417 | 319 | if (out_user_data) { |
418 | 0 | *out_user_data = (uintptr_t)de265_get_image_user_data(image); |
419 | 0 | } |
420 | | |
421 | 319 | err = convert_libde265_image_to_heif_image(decoder, image, out_img, limits); |
422 | 319 | if (err.code != heif_error_Ok) { |
423 | 61 | return err; |
424 | 61 | } |
425 | | |
426 | 258 | heif_color_profile_nclx* nclx = heif_nclx_color_profile_alloc(); |
427 | 258 | #if LIBDE265_NUMERIC_VERSION >= 0x01000700 |
428 | 258 | HEIF_WARN_OR_FAIL(decoder->strict_decoding, *out_img, heif_nclx_color_profile_set_color_primaries(nclx, static_cast<uint16_t>(de265_get_image_colour_primaries(image))), |
429 | 258 | { |
430 | 258 | heif_nclx_color_profile_free(nclx); |
431 | 258 | heif_image_release(*out_img); |
432 | 258 | *out_img = nullptr; |
433 | 258 | }); |
434 | 258 | HEIF_WARN_OR_FAIL(decoder->strict_decoding, *out_img, heif_nclx_color_profile_set_transfer_characteristics(nclx, static_cast<uint16_t>(de265_get_image_transfer_characteristics(image))), |
435 | 258 | { |
436 | 258 | heif_nclx_color_profile_free(nclx); |
437 | 258 | heif_image_release(*out_img); |
438 | 258 | *out_img = nullptr; |
439 | 258 | }); |
440 | 258 | HEIF_WARN_OR_FAIL(decoder->strict_decoding, *out_img, heif_nclx_color_profile_set_matrix_coefficients(nclx, static_cast<uint16_t>(de265_get_image_matrix_coefficients(image))), |
441 | 258 | { |
442 | 258 | heif_nclx_color_profile_free(nclx); |
443 | 258 | heif_image_release(*out_img); |
444 | 258 | *out_img = nullptr; |
445 | 258 | }); |
446 | 251 | nclx->full_range_flag = (bool) de265_get_image_full_range_flag(image); |
447 | 251 | #endif |
448 | 251 | heif_image_set_nclx_color_profile(*out_img, nclx); |
449 | 251 | heif_nclx_color_profile_free(nclx); |
450 | | |
451 | 251 | de265_release_next_picture(decoder->ctx); |
452 | 251 | return heif_error_ok; |
453 | 258 | } |
454 | 11.9k | } while (more); |
455 | | |
456 | 10.8k | return err; |
457 | 11.2k | } |
458 | | |
459 | | |
460 | | static heif_error libde265_v1_decode_next_image(void* decoder_raw, |
461 | | heif_image** out_img, |
462 | | const heif_security_limits* limits) |
463 | 0 | { |
464 | 0 | return libde265_v1_decode_next_image2(decoder_raw, out_img, nullptr, limits); |
465 | 0 | } |
466 | | |
467 | | static heif_error libde265_v1_decode_image(void* decoder_raw, |
468 | | heif_image** out_img) |
469 | 0 | { |
470 | 0 | auto* limits = heif_get_global_security_limits(); |
471 | 0 | return libde265_v1_decode_next_image(decoder_raw, out_img, limits); |
472 | 0 | } |
473 | | |
474 | | #endif |
475 | | |
476 | | |
477 | | #if LIBDE265_NUMERIC_VERSION >= 0x02000000 |
478 | | |
479 | | static const heif_decoder_plugin decoder_libde265 |
480 | | { |
481 | | 1, |
482 | | libde265_plugin_name, |
483 | | libde265_init_plugin, |
484 | | libde265_deinit_plugin, |
485 | | libde265_does_support_format, |
486 | | libde265_new_decoder, |
487 | | libde265_free_decoder, |
488 | | libde265_v2_push_data, |
489 | | libde265_v2_decode_image, |
490 | | libde265_set_strict_decoding, |
491 | | "libde265", |
492 | | libde265_v2_decode_next_image |
493 | | }; |
494 | | |
495 | | #else |
496 | | |
497 | | static const heif_decoder_plugin decoder_libde265 |
498 | | { |
499 | | 5, |
500 | | libde265_plugin_name, |
501 | | libde265_init_plugin, |
502 | | libde265_deinit_plugin, |
503 | | libde265_does_support_format, |
504 | | libde265_new_decoder, |
505 | | libde265_free_decoder, |
506 | | libde265_v1_push_data, |
507 | | libde265_v1_decode_image, |
508 | | libde265_set_strict_decoding, |
509 | | "libde265", |
510 | | libde265_v1_decode_next_image, |
511 | | /* minimum_required_libheif_version */ LIBHEIF_MAKE_VERSION(1,21,0), |
512 | | libde265_does_support_format2, |
513 | | libde265_new_decoder2, |
514 | | libde265_v1_push_data2, |
515 | | libde265_flush_data, |
516 | | libde265_v1_decode_next_image2 |
517 | | }; |
518 | | |
519 | | #endif |
520 | | |
521 | | const heif_decoder_plugin* get_decoder_plugin_libde265() |
522 | 2.42k | { |
523 | 2.42k | return &decoder_libde265; |
524 | 2.42k | } |
525 | | |
526 | | |
527 | | |
528 | | #if PLUGIN_LIBDE265 |
529 | | heif_plugin_info plugin_info { |
530 | | 1, |
531 | | heif_plugin_type_decoder, |
532 | | &decoder_libde265 |
533 | | }; |
534 | | #endif |