1 /*
2 * This file is part of FFmpeg.
3 *
4 * Copyright (c) 2025 Zhao Zhili <quinkblack@foxmail.com>
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "config_components.h"
22
23 #include <stdbool.h>
24 #include <multimedia/player_framework/native_avcapability.h>
25 #include <multimedia/player_framework/native_avcodec_videodecoder.h>
26
34
41
45 /* A RefStruct reference backing dec. Each hardware frame has a reference count to
46 * dec. dec will be destroyed only after oh_decode_close and all hardware
47 * frames have been released.
48 */
50
54
58
60
63
71
76
77 #define OFFSET(x) offsetof(OHCodecDecContext, x)
81
85 OH_AVCodec **
dec_ref;
///< RefStruct reference
87
89 {
90 OH_AVCodec **decp = obj;
91 OH_AVErrCode err = OH_VideoDecoder_Destroy(*decp);
92 if (err == AV_ERR_OK)
94 else
97 }
98
100 {
101 const char *
name =
s->name;
102
105 if (!mime)
107 OH_AVCapability *cap = OH_AVCodec_GetCapabilityByCategory(mime, false, HARDWARE);
108 if (!cap) {
112 }
114 "Failed to get hardware codec %s, try software backend\n", mime);
115 cap = OH_AVCodec_GetCapabilityByCategory(mime, false, SOFTWARE);
116 if (!cap) {
119 }
120 }
121 name = OH_AVCapability_GetName(cap);
124 }
125
126 s->dec = OH_VideoDecoder_CreateByName(
name);
130 }
132
138 }
139 *
s->dec_ref =
s->dec;
140
141 return 0;
142 }
143
145 {
148
154 s->output_to_window =
true;
155 } else {
158 }
159 }
160
163 "Invalid width/height (%dx%d), width and height are mandatory for ohcodec\n",
166 }
167
168 OH_AVFormat *
format = OH_AVFormat_Create();
171
172 OH_AVFormat_SetIntValue(
format, OH_MD_KEY_WIDTH, avctx->
width);
173 OH_AVFormat_SetIntValue(
format, OH_MD_KEY_HEIGHT, avctx->
height);
174 if (!
s->output_to_window)
175 OH_AVFormat_SetIntValue(
format, OH_MD_KEY_PIXEL_FORMAT,
176 AV_PIXEL_FORMAT_NV12);
177 else
178 OH_AVFormat_SetIntValue(
format, OH_MD_KEY_PIXEL_FORMAT,
179 AV_PIXEL_FORMAT_SURFACE_FORMAT);
180 OH_AVErrCode err = OH_VideoDecoder_Configure(
s->dec,
format);
181 OH_AVFormat_Destroy(
format);
182 if (err != AV_ERR_OK) {
187 }
188
189 if (
s->output_to_window) {
190 err = OH_VideoDecoder_SetSurface(
s->dec,
window);
191 if (err != AV_ERR_OK) {
196 }
197 }
198
199 return 0;
200 }
201
203 {
206
207 // Careful on the lock order.
208 // Always lock input first.
214
217 }
218
220 void *userdata)
221 {
225 double d;
226
227 if (!OH_AVFormat_GetIntValue(
format, OH_MD_KEY_VIDEO_PIC_WIDTH, &
s->width) ||
228 !OH_AVFormat_GetIntValue(
format, OH_MD_KEY_VIDEO_PIC_HEIGHT, &
s->height) ||
229 !OH_AVFormat_GetIntValue(
format, OH_MD_KEY_VIDEO_STRIDE, &
s->stride) ||
230 !OH_AVFormat_GetIntValue(
format, OH_MD_KEY_VIDEO_SLICE_HEIGHT,
234 }
235
238
239 if (
s->stride <= 0 ||
s->slice_height <= 0) {
241 "Buffer stride (%d) or slice height (%d) is invalid\n",
242 s->stride,
s->slice_height);
244 }
245
246 if (OH_AVFormat_GetIntValue(
format, OH_MD_KEY_PIXEL_FORMAT, &n)) {
248 /* When use output_to_window, the returned format is the memory
249 * layout of hardware frame, not AV_PIXEL_FORMAT_SURFACE_FORMAT as
250 * expected.
251 */
252 if (
s->output_to_window)
254 else
256 // Check whether this pixel format is supported
259 n);
261 }
262 } else {
265 }
266
267 if (OH_AVFormat_GetIntValue(
format,
268 OH_MD_KEY_MATRIX_COEFFICIENTS,
269 &n))
271 if (OH_AVFormat_GetIntValue(
format,
272 OH_MD_KEY_COLOR_PRIMARIES,
273 &n))
275 if (OH_AVFormat_GetIntValue(
format,
276 OH_MD_KEY_TRANSFER_CHARACTERISTICS,
277 &n))
279 if (OH_AVFormat_GetIntValue(
format,
280 OH_MD_KEY_RANGE_FLAG,
281 &n))
283
284 if (OH_AVFormat_GetDoubleValue(
format, OH_MD_KEY_VIDEO_SAR, &d)) {
287 }
288
289 s->got_stream_info =
true;
290
291 return;
294 OH_AVFormat_DumpInfo(
format));
296 }
297
299 OH_AVBuffer *
buffer,
void *userdata)
300 {
305 };
306
312
315 }
316
318 OH_AVBuffer *
buffer,
void *userdata)
319 {
324 };
325
331
334 }
335
337 {
339 OH_AVErrCode err;
340 OH_AVCodecCallback
cb = {
345 };
346
347 err = OH_VideoDecoder_RegisterCallback(
s->dec,
cb, avctx);
348 if (err != AV_ERR_OK) {
353 }
354 err = OH_VideoDecoder_Prepare(
s->dec);
355 if (err != AV_ERR_OK) {
360 }
361 err = OH_VideoDecoder_Start(
s->dec);
362 if (err != AV_ERR_OK) {
367 }
368
369 return 0;
370 }
371
373 {
375
376 // Initialize these fields first, so oh_decode_close can destroy them safely
380
387
393 if (!
s->input_queue || !
s->output_queue)
395
399
400 return 0;
401 }
402
404 {
406
408 /* Stop but don't destroy dec directly, to keep hardware frames on
409 * the fly valid.
410 */
411 OH_AVErrCode err = OH_VideoDecoder_Stop(
s->dec);
412 if (err == AV_ERR_OK)
414 else
419 }
420
422
424
426
428
429 return 0;
430 }
431
433 {
434 if (!opaque)
435 return;
436
438
440 OH_AVCodec *dec = *
buffer->dec_ref;
441 OH_AVCodecBufferAttr attr;
442 OH_AVErrCode err = OH_AVBuffer_GetBufferAttr(
buffer->buffer, &attr);
443 if (err == AV_ERR_OK && !(attr.flags & AVCODEC_BUFFER_FLAGS_DISCARD))
444 OH_VideoDecoder_RenderOutputBuffer(dec,
buffer->index);
445 else
446 OH_VideoDecoder_FreeOutputBuffer(dec,
buffer->index);
447 }
448
451 }
452
455 const OH_AVCodecBufferAttr *attr)
456 {
458
460 frame->height =
s->height;
464
469
471
477 if (!
frame->buf[0]) {
480 }
481 // Point to OH_AVBuffer
485
486 return 0;
487 }
488
491 const OH_AVCodecBufferAttr *attr)
492 {
494
497 frame->height =
s->height;
501
504
505 uint8_t *
p = OH_AVBuffer_GetAddr(
output->buffer);
509 }
510
511 uint8_t *
src[4] = {0};
512 int src_linesizes[4] = {0};
513
518 src_linesizes);
523
524 OH_AVErrCode err = OH_VideoDecoder_FreeOutputBuffer(
s->dec,
output->index);
525 if (err != AV_ERR_OK) {
530 }
531
532 return 0;
533 }
534
537 {
539 OH_AVCodecBufferAttr attr;
540
541 OH_AVErrCode err = OH_AVBuffer_GetBufferAttr(
output->buffer, &attr);
542 if (err != AV_ERR_OK)
544
545 if (attr.flags & AVCODEC_BUFFER_FLAGS_EOS) {
547 OH_VideoDecoder_FreeOutputBuffer(
s->dec,
output->index);
549 }
550
551 if (!
s->got_stream_info) {
552 // This shouldn't happen, add a warning message.
554 "decoder didn't notify stream info, try get format explicitly\n");
555
556 OH_AVFormat *
format = OH_VideoDecoder_GetOutputDescription(
s->dec);
560 }
561
563 OH_AVFormat_Destroy(
format);
564 if (!
s->got_stream_info)
566 }
567
568 if (
s->output_to_window)
571 }
572
574 {
576 OH_AVErrCode err;
578
579 if (!
s->pkt.size && !
s->eof_sent) {
580 OH_AVCodecBufferAttr attr = {
581 .flags = AVCODEC_BUFFER_FLAGS_EOS,
582 };
583 err = OH_AVBuffer_SetBufferAttr(
input->buffer, &attr);
584 if (err != AV_ERR_OK)
586 err = OH_VideoDecoder_PushInputBuffer(
s->dec,
input->index);
587 if (err != AV_ERR_OK)
590 return 0;
591 }
592
593 uint8_t *
p = OH_AVBuffer_GetAddr(
input->buffer);
597 "Failed to get buffer addr (%p) or capacity (%d)\n",
600 }
601 n =
FFMIN(
s->pkt.size, n);
602 memcpy(
p,
s->pkt.data, n);
603
604 OH_AVCodecBufferAttr attr = {
605 .size = n,
606 .offset = 0,
610 ? AVCODEC_BUFFER_FLAGS_SYNC_FRAME : 0,
611 };
612
613 err = OH_AVBuffer_SetBufferAttr(
input->buffer, &attr);
614 if (err != AV_ERR_OK) {
617 }
618 err = OH_VideoDecoder_PushInputBuffer(
s->dec,
input->index);
619 if (err != AV_ERR_OK) {
624 }
625
629 } else {
631 }
632
633 return 0;
634 }
635
637 {
639
640 while (1) {
643
644 // Try get output
646 while (!
s->decode_status) {
648 break;
649 // Only wait after send EOF
650 if (
s->eof_sent && !
s->decode_status)
652 else
653 break;
654 }
655
656 ret =
s->decode_status;
658
659 // Got a frame
664
666 /* fetch new packet or eof */
670 }
671
672 // Wait input buffer
674 while (!
s->decode_status) {
676 break;
678 }
679
680 ret =
s->decode_status;
682
685
689 }
690
692 }
693
695 {
697
698 OH_VideoDecoder_Flush(
s->dec);
699
704 s->decode_status = 0;
708
709 OH_VideoDecoder_Start(
s->dec);
710 }
711
719 },
721 },
723 };
724
725 #define VD (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM)
727 {"codec_name", "Select codec by name",
729 {"allow_sw", "Allow software decoding",
732 };
733
734 #define DECLARE_OHCODEC_VCLASS(short_name) \
735 static const AVClass short_name##_oh_dec_class = { \
736 .class_name = #short_name "_ohcodec", \
737 .item_name = av_default_item_name, \
738 .option = ohcodec_vdec_options, \
739 .version = LIBAVUTIL_VERSION_INT, \
740 };
741
742 #define DECLARE_OHCODEC_VDEC(short_name, full_name, codec_id, bsf) \
743 DECLARE_OHCODEC_VCLASS(short_name) \
744 const FFCodec ff_##short_name##_oh_decoder = { \
745 .p.name = #short_name "_ohcodec", \
746 CODEC_LONG_NAME(full_name " OpenHarmony Codec"), \
747 .p.type = AVMEDIA_TYPE_VIDEO, \
748 .p.id = codec_id, \
749 .p.priv_class = &short_name##_oh_dec_class, \
750 .priv_data_size = sizeof(OHCodecDecContext), \
751 .init = oh_decode_init, \
752 FF_CODEC_RECEIVE_FRAME_CB(oh_decode_receive_frame), \
753 .flush = oh_decode_flush, \
754 .close = oh_decode_close, \
755 .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | \
756 AV_CODEC_CAP_HARDWARE, \
757 .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, \
758 .bsfs = bsf, \
759 .hw_configs = oh_hw_configs, \
760 .p.wrapper_name = "ohcodec", \
761 };
762
763 #if CONFIG_H264_OH_DECODER
765 #endif
766
767 #if CONFIG_HEVC_OH_DECODER
769 #endif