/src/libheif/libheif/plugins/decoder_openh264.cc
Line | Count | Source |
1 | | /* |
2 | | * openh264 codec. |
3 | | * Copyright (c) 2024 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_openh264.h" |
24 | | #include <cstring> |
25 | | #include <cassert> |
26 | | #include <vector> |
27 | | #include <cstdio> |
28 | | #include <deque> |
29 | | |
30 | | #include <wels/codec_api.h> |
31 | | #include <string> |
32 | | #include <utility> |
33 | | |
34 | | // TODO: the openh264 decoder seems to fail with some images. |
35 | | // I have not figured out yet which images are affected. |
36 | | // Maybe it has something to do with the image size. I got the error with large images. |
37 | | |
38 | | struct openh264_decoder |
39 | | { |
40 | | struct Packet |
41 | | { |
42 | | std::vector<uint8_t> data; |
43 | | uintptr_t pts; |
44 | | }; |
45 | | |
46 | | std::deque<Packet> input_data; |
47 | | bool m_eof_reached = false; |
48 | | |
49 | | std::string error_message; |
50 | | |
51 | | |
52 | | // --- decoder |
53 | | |
54 | | ISVCDecoder* decoder = nullptr; |
55 | | |
56 | | ~openh264_decoder() |
57 | 0 | { |
58 | 0 | if (decoder) { |
59 | | // Step 6:uninitialize the decoder and memory free |
60 | |
|
61 | 0 | decoder->Uninitialize(); // TODO: do we have to Uninitialize when an error is returned? |
62 | | |
63 | |
|
64 | 0 | WelsDestroyDecoder(decoder); |
65 | 0 | } |
66 | 0 | } |
67 | | }; |
68 | | |
69 | | static const char kSuccess[] = "Success"; |
70 | | |
71 | | // Reduced priority because OpenH264 cannot pass through user-data. |
72 | | // We need this feature for decoding sequences with SAI. |
73 | | // Prefer to use the FFMPEG plugin. |
74 | | static const int OpenH264_PLUGIN_PRIORITY = 70; |
75 | | |
76 | | #define MAX_PLUGIN_NAME_LENGTH 80 |
77 | | |
78 | | static char plugin_name[MAX_PLUGIN_NAME_LENGTH]; |
79 | | |
80 | | static heif_error kError_EOF = {heif_error_Decoder_plugin_error, heif_suberror_End_of_data, "Insufficient input data"}; |
81 | | |
82 | | |
83 | | static const char* openh264_plugin_name() |
84 | 0 | { |
85 | 0 | OpenH264Version version = WelsGetCodecVersion(); |
86 | |
|
87 | 0 | sprintf(plugin_name, "OpenH264 %d.%d.%d", version.uMajor, version.uMinor, version.uRevision); |
88 | |
|
89 | 0 | return plugin_name; |
90 | 0 | } |
91 | | |
92 | | |
93 | | static void openh264_init_plugin() |
94 | 14.6k | { |
95 | 14.6k | } |
96 | | |
97 | | |
98 | | static void openh264_deinit_plugin() |
99 | 14.6k | { |
100 | 14.6k | } |
101 | | |
102 | | |
103 | | static int openh264_does_support_format(heif_compression_format format) |
104 | 10.1k | { |
105 | 10.1k | if (format == heif_compression_AVC) { |
106 | 0 | return OpenH264_PLUGIN_PRIORITY; |
107 | 0 | } |
108 | 10.1k | else { |
109 | 10.1k | return 0; |
110 | 10.1k | } |
111 | 10.1k | } |
112 | | |
113 | | static int openh264_does_support_format2(const heif_decoder_plugin_compressed_format_description* format) |
114 | 0 | { |
115 | 0 | return openh264_does_support_format(format->format); |
116 | 0 | } |
117 | | |
118 | | |
119 | | heif_error openh264_new_decoder2(void** dec, const heif_decoder_plugin_options* options) |
120 | 0 | { |
121 | 0 | auto* decoder = new openh264_decoder(); |
122 | 0 | *dec = decoder; |
123 | | |
124 | | // Step 2:decoder creation |
125 | 0 | WelsCreateDecoder(&decoder->decoder); |
126 | 0 | if (!decoder->decoder) { |
127 | 0 | return { |
128 | 0 | heif_error_Decoder_plugin_error, |
129 | 0 | heif_suberror_Unspecified, |
130 | 0 | "Cannot create OpenH264 decoder" |
131 | 0 | }; |
132 | 0 | } |
133 | | |
134 | | // Step 3:declare required parameter, used to differentiate Decoding only and Parsing only |
135 | 0 | SDecodingParam sDecParam{}; |
136 | 0 | sDecParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_AVC; |
137 | | |
138 | | //for Parsing only, the assignment is mandatory |
139 | | // sDecParam.bParseOnly = true; |
140 | | |
141 | | // Step 4:initialize the parameter and decoder context, allocate memory |
142 | 0 | decoder->decoder->Initialize(&sDecParam); |
143 | |
|
144 | 0 | return {heif_error_Ok, heif_suberror_Unspecified, kSuccess}; |
145 | 0 | } |
146 | | |
147 | | |
148 | | heif_error openh264_new_decoder(void** dec) |
149 | 0 | { |
150 | 0 | heif_decoder_plugin_options options; |
151 | 0 | options.format = heif_compression_AVC; |
152 | 0 | options.num_threads = 0; |
153 | 0 | options.strict_decoding = false; |
154 | |
|
155 | 0 | return openh264_new_decoder2(dec, &options); |
156 | 0 | } |
157 | | |
158 | | void openh264_free_decoder(void* decoder_raw) |
159 | 0 | { |
160 | 0 | auto* decoder = (openh264_decoder*) decoder_raw; |
161 | |
|
162 | 0 | if (!decoder) { |
163 | 0 | return; |
164 | 0 | } |
165 | | |
166 | 0 | delete decoder; |
167 | 0 | } |
168 | | |
169 | | |
170 | | void openh264_set_strict_decoding(void* decoder_raw, int flag) |
171 | 0 | { |
172 | | // auto* decoder = (openh264_decoder*) decoder_raw; |
173 | 0 | } |
174 | | |
175 | | |
176 | | heif_error openh264_push_data2(void* decoder_raw, const void* frame_data, size_t frame_size, uintptr_t user_data) |
177 | 0 | { |
178 | 0 | auto* decoder = (openh264_decoder*) decoder_raw; |
179 | |
|
180 | 0 | const auto* input_data = (const uint8_t*) frame_data; |
181 | |
|
182 | 0 | assert(frame_size > 4); |
183 | | |
184 | 0 | openh264_decoder::Packet pkt; |
185 | 0 | pkt.data.insert(pkt.data.end(), input_data, input_data + frame_size); |
186 | 0 | pkt.pts = user_data; |
187 | 0 | decoder->input_data.push_back(std::move(pkt)); |
188 | |
|
189 | 0 | return {heif_error_Ok, heif_suberror_Unspecified, kSuccess}; |
190 | 0 | } |
191 | | |
192 | | heif_error openh264_push_data(void* decoder_raw, const void* frame_data, size_t frame_size) |
193 | 0 | { |
194 | 0 | return openh264_push_data2(decoder_raw, frame_data, frame_size, 0); |
195 | 0 | } |
196 | | |
197 | | |
198 | | heif_error openh264_decode_next_image2(void* decoder_raw, heif_image** out_img, |
199 | | uintptr_t* out_user_data, |
200 | | const heif_security_limits* limits) |
201 | 0 | { |
202 | 0 | auto* decoder = (openh264_decoder*) decoder_raw; |
203 | 0 | ISVCDecoder* pSvcDecoder = decoder->decoder; |
204 | | |
205 | | // --- Not enough data, but no EOF yet. |
206 | |
|
207 | 0 | if (decoder->input_data.empty() && !decoder->m_eof_reached) { |
208 | 0 | *out_img = nullptr; |
209 | 0 | return heif_error_ok; |
210 | 0 | } |
211 | | |
212 | | |
213 | | //output: [0~2] for Y,U,V buffer for Decoding only |
214 | 0 | unsigned char* pData[3] = {nullptr, nullptr, nullptr}; |
215 | | |
216 | | // in-out: for Decoding only: declare and initialize the output buffer info, this should never co-exist with Parsing only |
217 | |
|
218 | 0 | SBufferInfo sDstBufInfo; |
219 | 0 | memset(&sDstBufInfo, 0, sizeof(SBufferInfo)); |
220 | |
|
221 | 0 | int iRet; |
222 | |
|
223 | 0 | if (!decoder->input_data.empty()) { |
224 | 0 | sDstBufInfo.uiInBsTimeStamp = decoder->input_data.front().pts; |
225 | |
|
226 | 0 | const std::vector<uint8_t>& indata = decoder->input_data.front().data; |
227 | 0 | std::vector<uint8_t> scdata; |
228 | |
|
229 | 0 | size_t idx = 0; |
230 | 0 | while (idx < indata.size()) { |
231 | 0 | if (indata.size() - 4 < idx) { |
232 | 0 | return kError_EOF; |
233 | 0 | } |
234 | | |
235 | 0 | uint32_t size = ((indata[idx] << 24) | (indata[idx + 1] << 16) | (indata[idx + 2] << 8) | indata[idx + 3]); |
236 | 0 | idx += 4; |
237 | |
|
238 | 0 | if (indata.size() < size || indata.size() - size < idx) { |
239 | 0 | return kError_EOF; |
240 | 0 | } |
241 | | |
242 | 0 | scdata.push_back(0); |
243 | 0 | scdata.push_back(0); |
244 | 0 | scdata.push_back(1); |
245 | | |
246 | | // check for need of start code emulation prevention |
247 | |
|
248 | 0 | bool do_start_code_emulation_check = true; |
249 | |
|
250 | 0 | while (do_start_code_emulation_check && size >= 3) { |
251 | 0 | bool found_start_code_emulation = false; |
252 | |
|
253 | 0 | for (size_t i = 0; i < size - 3; i++) { |
254 | 0 | if (indata[idx + 0] == 0 && |
255 | 0 | indata[idx + 1] == 0 && |
256 | 0 | (indata[idx + 2] >= 0 && indata[idx + 2] <= 3)) { |
257 | 0 | scdata.push_back(0); |
258 | 0 | scdata.push_back(0); |
259 | 0 | scdata.push_back(3); |
260 | |
|
261 | 0 | scdata.insert(scdata.end(), &indata[idx + 2], indata.data() + idx + i + 2); |
262 | 0 | idx += i + 2; |
263 | 0 | size -= (uint32_t) (i + 2); |
264 | 0 | found_start_code_emulation = true; |
265 | 0 | break; |
266 | 0 | } |
267 | 0 | } |
268 | |
|
269 | 0 | do_start_code_emulation_check = found_start_code_emulation; |
270 | 0 | } |
271 | |
|
272 | 0 | assert(size > 0); |
273 | | // Note: we cannot write &indata[idx + size] since that would use the operator[] on an element beyond the vector range. |
274 | 0 | scdata.insert(scdata.end(), &indata[idx], indata.data() + idx + size); |
275 | |
|
276 | 0 | idx += size; |
277 | 0 | } |
278 | | |
279 | 0 | if (idx != indata.size()) { |
280 | 0 | decoder->input_data.pop_front(); |
281 | 0 | return kError_EOF; |
282 | 0 | } |
283 | | |
284 | 0 | decoder->input_data.pop_front(); |
285 | | |
286 | | // input: encoded bitstream start position; should include start code prefix |
287 | 0 | unsigned char* pBuf = scdata.data(); |
288 | | |
289 | | // input: encoded bit stream length; should include the size of start code prefix |
290 | 0 | int iSize = static_cast<int>(scdata.size()); |
291 | | |
292 | | |
293 | | // Step 5:do actual decoding process in slice level; this can be done in a loop until data ends |
294 | | |
295 | | |
296 | | //for Decoding only |
297 | 0 | iRet = pSvcDecoder->DecodeFrameNoDelay(pBuf, iSize, pData, &sDstBufInfo); |
298 | 0 | } |
299 | 0 | else { |
300 | 0 | iRet = pSvcDecoder->FlushFrame(pData, &sDstBufInfo); |
301 | 0 | } |
302 | | |
303 | 0 | if (iRet != 0) { |
304 | 0 | return { |
305 | 0 | heif_error_Decoder_plugin_error, |
306 | 0 | heif_suberror_Unspecified, |
307 | 0 | "OpenH264 decoder error" |
308 | 0 | }; |
309 | 0 | } |
310 | | |
311 | 0 | if (sDstBufInfo.iBufferStatus != 1) { |
312 | 0 | *out_img = nullptr; |
313 | 0 | return heif_error_ok; |
314 | 0 | } |
315 | | |
316 | 0 | if (out_user_data) { |
317 | 0 | *out_user_data = sDstBufInfo.uiOutYuvTimeStamp; |
318 | 0 | } |
319 | | |
320 | | /* |
321 | | // TODO: I receive an iBufferStatus==0, but the output image is still decoded |
322 | | if (sDstBufInfo.iBufferStatus == 0) { |
323 | | return {heif_error_Decoder_plugin_error, |
324 | | heif_suberror_Unspecified, |
325 | | "OpenH264 decoder did not output any image"}; |
326 | | } |
327 | | */ |
328 | |
|
329 | 0 | uint32_t width = sDstBufInfo.UsrData.sSystemBuffer.iWidth; |
330 | 0 | uint32_t height = sDstBufInfo.UsrData.sSystemBuffer.iHeight; |
331 | |
|
332 | 0 | heif_image* heif_img; |
333 | 0 | heif_error err{}; |
334 | |
|
335 | 0 | uint32_t cwidth, cheight; |
336 | |
|
337 | 0 | if (sDstBufInfo.UsrData.sSystemBuffer.iFormat == videoFormatI420) { |
338 | 0 | cwidth = (width + 1) / 2; |
339 | 0 | cheight = (height + 1) / 2; |
340 | |
|
341 | 0 | err = heif_image_create(width, height, |
342 | 0 | heif_colorspace_YCbCr, |
343 | 0 | heif_chroma_420, |
344 | 0 | &heif_img); |
345 | 0 | if (err.code != heif_error_Ok) { |
346 | 0 | assert(heif_img == nullptr); |
347 | 0 | return err; |
348 | 0 | } |
349 | | |
350 | 0 | *out_img = heif_img; |
351 | |
|
352 | 0 | err = heif_image_add_plane_safe(heif_img, heif_channel_Y, width, height, 8, limits); |
353 | 0 | if (err.code != heif_error_Ok) { |
354 | | // copy error message to decoder object because heif_image will be released |
355 | 0 | decoder->error_message = err.message; |
356 | 0 | err.message = decoder->error_message.c_str(); |
357 | |
|
358 | 0 | heif_image_release(heif_img); |
359 | 0 | return err; |
360 | 0 | } |
361 | | |
362 | 0 | err = heif_image_add_plane_safe(heif_img, heif_channel_Cb, cwidth, cheight, 8, limits); |
363 | 0 | if (err.code != heif_error_Ok) { |
364 | | // copy error message to decoder object because heif_image will be released |
365 | 0 | decoder->error_message = err.message; |
366 | 0 | err.message = decoder->error_message.c_str(); |
367 | |
|
368 | 0 | heif_image_release(heif_img); |
369 | 0 | return err; |
370 | 0 | } |
371 | | |
372 | 0 | err = heif_image_add_plane_safe(heif_img, heif_channel_Cr, cwidth, cheight, 8, limits); |
373 | 0 | if (err.code != heif_error_Ok) { |
374 | | // copy error message to decoder object because heif_image will be released |
375 | 0 | decoder->error_message = err.message; |
376 | 0 | err.message = decoder->error_message.c_str(); |
377 | |
|
378 | 0 | heif_image_release(heif_img); |
379 | 0 | return err; |
380 | 0 | } |
381 | | |
382 | 0 | size_t y_stride; |
383 | 0 | size_t cb_stride; |
384 | 0 | size_t cr_stride; |
385 | 0 | uint8_t* py = heif_image_get_plane2(heif_img, heif_channel_Y, &y_stride); |
386 | 0 | uint8_t* pcb = heif_image_get_plane2(heif_img, heif_channel_Cb, &cb_stride); |
387 | 0 | uint8_t* pcr = heif_image_get_plane2(heif_img, heif_channel_Cr, &cr_stride); |
388 | |
|
389 | 0 | int ystride = sDstBufInfo.UsrData.sSystemBuffer.iStride[0]; |
390 | 0 | int cstride = sDstBufInfo.UsrData.sSystemBuffer.iStride[1]; |
391 | |
|
392 | 0 | for (uint32_t y = 0; y < height; y++) { |
393 | 0 | memcpy(py + y * y_stride, sDstBufInfo.pDst[0] + y * ystride, width); |
394 | 0 | } |
395 | |
|
396 | 0 | for (uint32_t y = 0; y < (height + 1) / 2; y++) { |
397 | 0 | memcpy(pcb + y * cb_stride, sDstBufInfo.pDst[1] + y * cstride, (width + 1) / 2); |
398 | 0 | memcpy(pcr + y * cr_stride, sDstBufInfo.pDst[2] + y * cstride, (width + 1) / 2); |
399 | 0 | } |
400 | 0 | } |
401 | 0 | else { |
402 | 0 | return { |
403 | 0 | heif_error_Decoder_plugin_error, |
404 | 0 | heif_suberror_Unspecified, |
405 | 0 | "Unsupported image pixel format" |
406 | 0 | }; |
407 | 0 | } |
408 | | |
409 | | // decoder->data.clear(); |
410 | | |
411 | 0 | return heif_error_ok; |
412 | 0 | } |
413 | | |
414 | | |
415 | | heif_error openh264_decode_next_image(void* decoder_raw, heif_image** out_img, |
416 | | const heif_security_limits* limits) |
417 | 0 | { |
418 | 0 | return openh264_decode_next_image2(decoder_raw, out_img, nullptr, limits); |
419 | 0 | } |
420 | | |
421 | | heif_error openh264_decode_image(void* decoder_raw, heif_image** out_img) |
422 | 0 | { |
423 | 0 | auto* limits = heif_get_global_security_limits(); |
424 | 0 | return openh264_decode_next_image(decoder_raw, out_img, limits); |
425 | 0 | } |
426 | | |
427 | | |
428 | | heif_error openh264_flush_data(void* decoder_raw) |
429 | 0 | { |
430 | 0 | auto* decoder = (struct openh264_decoder*) decoder_raw; |
431 | |
|
432 | 0 | decoder->m_eof_reached = true; |
433 | |
|
434 | 0 | return heif_error_ok; |
435 | 0 | } |
436 | | |
437 | | |
438 | | static const heif_decoder_plugin decoder_openh264{ |
439 | | 5, |
440 | | openh264_plugin_name, |
441 | | openh264_init_plugin, |
442 | | openh264_deinit_plugin, |
443 | | openh264_does_support_format, |
444 | | openh264_new_decoder, |
445 | | openh264_free_decoder, |
446 | | openh264_push_data, |
447 | | openh264_decode_image, |
448 | | openh264_set_strict_decoding, |
449 | | "openh264", |
450 | | openh264_decode_next_image, |
451 | | /* minimum_required_libheif_version */ LIBHEIF_MAKE_VERSION(1,21,0), |
452 | | openh264_does_support_format2, |
453 | | openh264_new_decoder2, |
454 | | openh264_push_data2, |
455 | | openh264_flush_data, |
456 | | openh264_decode_next_image2 |
457 | | }; |
458 | | |
459 | | |
460 | | const heif_decoder_plugin* get_decoder_plugin_openh264() |
461 | 14.6k | { |
462 | 14.6k | return &decoder_openh264; |
463 | 14.6k | } |
464 | | |
465 | | |
466 | | #if PLUGIN_OpenH264_DECODER |
467 | | heif_plugin_info plugin_info { |
468 | | 1, |
469 | | heif_plugin_type_decoder, |
470 | | &decoder_openh264 |
471 | | }; |
472 | | #endif |