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