Files
UltraGrid/.github/scripts/Linux/ffmpeg-patches/0001-added-libde265-decoder.patch
Martin Pulec 3f0d98a18f CI Linux FFmpeg: update libde265 patch
the original version no longer applied so the UG/FFmpeg build failed
2025-10-03 15:19:58 +02:00

849 lines
32 KiB
Diff

From 50cc66377563962768743de0a8108b250c594772 Mon Sep 17 00:00:00 2001
From: Martin Pulec <martin.pulec@cesnet.cz>
Date: Fri, 3 Oct 2025 15:11:38 +0200
Subject: [PATCH] added libde265 decoder
---
configure | 4 +
libavcodec/Makefile | 1 +
libavcodec/allcodecs.c | 1 +
libavcodec/libde265dec.c | 766 +++++++++++++++++++++++++++++++++++++++
4 files changed, 772 insertions(+)
create mode 100644 libavcodec/libde265dec.c
diff --git a/configure b/configure
index 399b1ce128..f71df152fc 100755
--- a/configure
+++ b/configure
@@ -228,6 +228,7 @@ External library support:
--enable-libdavs2 enable AVS2 decoding via libdavs2 [no]
--enable-libdc1394 enable IIDC-1394 grabbing using libdc1394
and libraw1394 [no]
+ --enable-libde265 enable HEVC encoding via libde265 [no]
--enable-libdvdnav enable libdvdnav, needed for DVD demuxing [no]
--enable-libdvdread enable libdvdread, needed for DVD demuxing [no]
--enable-libfdk-aac enable AAC de/encoding via libfdk-aac [no]
@@ -1967,6 +1968,7 @@ EXTERNAL_LIBRARY_LIST="
libcodec2
libdav1d
libdc1394
+ libde265
libflite
libfontconfig
libfreetype
@@ -3654,6 +3656,7 @@ libdav1d_decoder_deps="libdav1d"
libdav1d_decoder_select="atsc_a53 dovi_rpudec"
libdavs2_decoder_deps="libdavs2"
libdavs2_decoder_select="avs2_parser"
+libde265_decoder_deps="libde265"
libfdk_aac_decoder_deps="libfdk_aac"
libfdk_aac_encoder_deps="libfdk_aac"
libfdk_aac_encoder_select="audio_frame_queue"
@@ -7119,6 +7122,7 @@ enabled libcodec2 && require libcodec2 codec2/codec2.h codec2_create -lc
enabled libdav1d && require_pkg_config libdav1d "dav1d >= 1.0.0" "dav1d/dav1d.h" dav1d_version
enabled libdavs2 && require_pkg_config libdavs2 "davs2 >= 1.6.0" davs2.h davs2_decoder_open
enabled libdc1394 && require_pkg_config libdc1394 libdc1394-2 dc1394/dc1394.h dc1394_new
+enabled libde265 && require_pkg_config libde265 "libde265 >= 1.0.0" libde265/de265.h de265_get_version
enabled libdrm && check_pkg_config libdrm libdrm xf86drm.h drmGetVersion
enabled libdvdnav && require_pkg_config libdvdnav "dvdnav >= 6.1.1" dvdnav/dvdnav.h dvdnav_open2
enabled libdvdread && require_pkg_config libdvdread "dvdread >= 6.1.2" dvdread/dvd_reader.h DVDOpen2
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index e7fde87b22..d9aabb51e5 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1166,6 +1166,7 @@ OBJS-$(CONFIG_LIBCODEC2_DECODER) += libcodec2.o
OBJS-$(CONFIG_LIBCODEC2_ENCODER) += libcodec2.o
OBJS-$(CONFIG_LIBDAV1D_DECODER) += libdav1d.o av1_parse.o
OBJS-$(CONFIG_LIBDAVS2_DECODER) += libdavs2.o
+OBJS-$(CONFIG_LIBDE265_DECODER) += libde265dec.o
OBJS-$(CONFIG_LIBFDK_AAC_DECODER) += libfdk-aacdec.o
OBJS-$(CONFIG_LIBFDK_AAC_ENCODER) += libfdk-aacenc.o
OBJS-$(CONFIG_LIBGSM_DECODER) += libgsmdec.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index d4b360f289..cfd14d28c3 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -784,6 +784,7 @@ extern const FFCodec ff_libcodec2_encoder;
extern const FFCodec ff_libcodec2_decoder;
extern const FFCodec ff_libdav1d_decoder;
extern const FFCodec ff_libdavs2_decoder;
+extern const FFCodec ff_libde265_decoder;
extern const FFCodec ff_libfdk_aac_encoder;
extern const FFCodec ff_libfdk_aac_decoder;
extern const FFCodec ff_libgsm_encoder;
diff --git a/libavcodec/libde265dec.c b/libavcodec/libde265dec.c
new file mode 100644
index 0000000000..81c8894a52
--- /dev/null
+++ b/libavcodec/libde265dec.c
@@ -0,0 +1,766 @@
+/*
+ * H.265 decoder
+ *
+ * Copyright (c) 2013, Dirk Farin <dirk.farin@gmail.com>
+ * Copyright (c) 2013-2015, Joachim Bauch <bauch@struktur.de>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * H.265 decoder based on libde265
+ */
+
+#include "avcodec.h"
+#include "decode.h"
+#include "codec_internal.h"
+#include "libavutil/cpu.h"
+#include "libavutil/imgutils.h"
+
+#include <libde265/de265.h>
+
+#if !defined(LIBDE265_NUMERIC_VERSION) || LIBDE265_NUMERIC_VERSION < 0x00060000
+#error "You need libde265 0.6 or newer to compile this plugin."
+#endif
+
+#if LIBDE265_NUMERIC_VERSION < 0x01000000
+// libde265 < 1.0 only supported 8 bits per pixel
+#define de265_get_bits_per_pixel(image, plane) 8
+#endif
+
+#define MAX_FRAME_QUEUE 16
+#define MAX_SPEC_QUEUE 16
+
+#define LIBDE265_FFMPEG_MAX(a, b) ((a) > (b) ? (a) : (b))
+#define LIBDE265_FFMPEG_MIN(a, b) ((a) < (b) ? (a) : (b))
+
+typedef struct DE265DecoderContext {
+ de265_decoder_context* decoder;
+
+ int check_extra;
+ int packetized;
+ int length_size;
+#if LIBDE265_NUMERIC_VERSION >= 0x00070000
+ int deblocking;
+ int decode_ratio;
+ int frame_queue_len;
+ AVFrame *frame_queue[MAX_FRAME_QUEUE];
+ int spec_queue_len;
+ struct de265_image_spec *spec_queue[MAX_SPEC_QUEUE];
+#endif
+} DE265Context;
+
+
+#if LIBDE265_NUMERIC_VERSION >= 0x00070000
+static inline int align_value(int value, int alignment) {
+ return ((value + (alignment - 1)) & ~(alignment - 1));
+}
+
+static inline enum de265_chroma get_image_chroma(enum de265_image_format format) {
+ switch (format) {
+ case de265_image_format_mono8:
+ return de265_chroma_mono;
+ case de265_image_format_YUV420P8:
+ return de265_chroma_420;
+ case de265_image_format_YUV422P8:
+ return de265_chroma_422;
+ case de265_image_format_YUV444P8:
+ return de265_chroma_444;
+ default:
+ return (enum de265_chroma) -1;
+ }
+}
+
+static inline enum AVPixelFormat get_pixel_format(AVCodecContext *avctx, enum de265_chroma chroma, int bits_per_pixel) {
+ enum AVPixelFormat result = AV_PIX_FMT_NONE;
+ switch (chroma) {
+ case de265_chroma_mono:
+ result = AV_PIX_FMT_GRAY8;
+ break;
+ case de265_chroma_420:
+ switch (bits_per_pixel) {
+ case 8:
+ result = AV_PIX_FMT_YUV420P;
+ break;
+ case 9:
+ result = AV_PIX_FMT_YUV420P9LE;
+ break;
+ case 10:
+ result = AV_PIX_FMT_YUV420P10LE;
+ break;
+ case 12:
+ result = AV_PIX_FMT_YUV420P12LE;
+ break;
+ case 14:
+ result = AV_PIX_FMT_YUV420P14LE;
+ break;
+ case 16:
+ result = AV_PIX_FMT_YUV420P16LE;
+ break;
+ default:
+ if (bits_per_pixel < 8 || bits_per_pixel > 16) {
+ av_log(avctx, AV_LOG_WARNING, "Unsupported chroma %d with %d bits per pixel", chroma, bits_per_pixel);
+ } else {
+ result = AV_PIX_FMT_YUV420P16LE;
+ }
+ break;
+ }
+ break;
+ case de265_chroma_422:
+ switch (bits_per_pixel) {
+ case 8:
+ result = AV_PIX_FMT_YUV422P;
+ break;
+ case 9:
+ result = AV_PIX_FMT_YUV422P9LE;
+ break;
+ case 10:
+ result = AV_PIX_FMT_YUV422P10LE;
+ break;
+ case 12:
+ result = AV_PIX_FMT_YUV422P12LE;
+ break;
+ case 14:
+ result = AV_PIX_FMT_YUV422P14LE;
+ break;
+ case 16:
+ result = AV_PIX_FMT_YUV422P16LE;
+ break;
+ default:
+ if (bits_per_pixel < 8 || bits_per_pixel > 16) {
+ av_log(avctx, AV_LOG_WARNING, "Unsupported chroma %d with %d bits per pixel", chroma, bits_per_pixel);
+ } else {
+ result = AV_PIX_FMT_YUV422P16LE;
+ }
+ break;
+ }
+ break;
+ case de265_chroma_444:
+ switch (bits_per_pixel) {
+ case 8:
+ result = AV_PIX_FMT_YUV444P;
+ break;
+ case 9:
+ result = AV_PIX_FMT_YUV444P9LE;
+ break;
+ case 10:
+ result = AV_PIX_FMT_YUV444P10LE;
+ break;
+ case 12:
+ result = AV_PIX_FMT_YUV444P12LE;
+ break;
+ case 14:
+ result = AV_PIX_FMT_YUV444P14LE;
+ break;
+ case 16:
+ result = AV_PIX_FMT_YUV444P16LE;
+ break;
+ default:
+ if (bits_per_pixel < 8 || bits_per_pixel > 16) {
+ av_log(avctx, AV_LOG_WARNING, "Unsupported chroma %d with %d bits per pixel", chroma, bits_per_pixel);
+ } else {
+ result = AV_PIX_FMT_YUV444P16LE;
+ }
+ break;
+ }
+ break;
+ default:
+ av_log(avctx, AV_LOG_WARNING, "Unsupported chroma %d with %d bits per pixel", chroma, bits_per_pixel);
+ }
+ return result;
+}
+
+static inline int get_output_bits_per_pixel(enum AVPixelFormat format) {
+ switch (format) {
+ case AV_PIX_FMT_GRAY8:
+ return 8;
+ case AV_PIX_FMT_YUV420P:
+ case AV_PIX_FMT_YUV422P:
+ case AV_PIX_FMT_YUV444P:
+ return 8;
+ case AV_PIX_FMT_YUV420P9LE:
+ case AV_PIX_FMT_YUV422P9LE:
+ case AV_PIX_FMT_YUV444P9LE:
+ return 9;
+ case AV_PIX_FMT_YUV420P10LE:
+ case AV_PIX_FMT_YUV422P10LE:
+ case AV_PIX_FMT_YUV444P10LE:
+ return 10;
+ case AV_PIX_FMT_YUV420P12LE:
+ case AV_PIX_FMT_YUV422P12LE:
+ case AV_PIX_FMT_YUV444P12LE:
+ return 12;
+ case AV_PIX_FMT_YUV420P14LE:
+ case AV_PIX_FMT_YUV422P14LE:
+ case AV_PIX_FMT_YUV444P14LE:
+ return 14;
+ case AV_PIX_FMT_YUV420P16LE:
+ case AV_PIX_FMT_YUV422P16LE:
+ case AV_PIX_FMT_YUV444P16LE:
+ return 16;
+ default:
+ return -1;
+ }
+}
+
+static void free_spec(DE265Context *ctx, struct de265_image_spec* spec) {
+ if (ctx->spec_queue_len < MAX_SPEC_QUEUE) {
+ ctx->spec_queue[ctx->spec_queue_len++] = spec;
+ } else {
+ free(spec);
+ }
+}
+
+static int ff_libde265dec_get_buffer(de265_decoder_context* ctx, struct de265_image_spec* spec, struct de265_image* img, void* userdata)
+{
+ AVCodecContext *avctx = (AVCodecContext *) userdata;
+ DE265Context *dectx = (DE265Context *) avctx->priv_data;
+
+ enum de265_chroma chroma = get_image_chroma(spec->format);
+ if (chroma != de265_chroma_mono) {
+ if (de265_get_bits_per_pixel(img, 0) != de265_get_bits_per_pixel(img, 1) ||
+ de265_get_bits_per_pixel(img, 0) != de265_get_bits_per_pixel(img, 2) ||
+ de265_get_bits_per_pixel(img, 1) != de265_get_bits_per_pixel(img, 2)) {
+ goto fallback;
+ }
+ }
+
+ int bits_per_pixel = de265_get_bits_per_pixel(img, 0);
+ enum AVPixelFormat format = get_pixel_format(avctx, chroma, bits_per_pixel);
+ if (format == AV_PIX_FMT_NONE) {
+ goto fallback;
+ }
+
+ if (get_output_bits_per_pixel(format) != bits_per_pixel) {
+ goto fallback;
+ }
+
+ AVFrame *frame = NULL;
+ if (avctx->get_buffer2) {
+ frame = av_frame_alloc();
+ if (frame == NULL) {
+ goto fallback;
+ }
+
+ frame->width = spec->visible_width;
+ frame->height = spec->visible_height;
+ frame->format = format;
+ avctx->coded_width = align_value(spec->width, spec->alignment);
+ avctx->coded_height = spec->height;
+ avctx->pix_fmt = format;
+ if (avctx->get_buffer2(avctx, frame, 0) < 0) {
+ av_frame_free(&frame);
+ goto fallback;
+ }
+ } else {
+ if (dectx->frame_queue_len > 0) {
+ frame = dectx->frame_queue[0];
+ dectx->frame_queue_len--;
+ if (dectx->frame_queue_len > 0) {
+ memmove(dectx->frame_queue, &dectx->frame_queue[1], dectx->frame_queue_len * sizeof(AVFrame *));
+ }
+ if (frame->width != spec->width || frame->height != spec->height || frame->format != format) {
+ av_frame_free(&frame);
+ } else {
+ av_frame_make_writable(frame);
+ }
+ }
+
+ if (frame == NULL) {
+ frame = av_frame_alloc();
+ if (frame == NULL) {
+ goto fallback;
+ }
+
+ frame->width = spec->width;
+ frame->height = spec->height;
+ frame->format = format;
+ if (av_frame_get_buffer(frame, spec->alignment) != 0) {
+ av_frame_free(&frame);
+ goto fallback;
+ }
+ }
+ }
+
+ if (frame->width != spec->visible_width || frame->height != spec->visible_height) {
+ // we might need to crop later
+ struct de265_image_spec *spec_copy;
+ if (dectx->spec_queue_len > 0) {
+ spec_copy = dectx->spec_queue[--dectx->spec_queue_len];
+ } else {
+ spec_copy = (struct de265_image_spec *) malloc(sizeof(struct de265_image_spec));
+ if (spec_copy == NULL) {
+ av_frame_free(&frame);
+ goto fallback;
+ }
+ }
+
+ memcpy(spec_copy, spec, sizeof(struct de265_image_spec));
+ frame->opaque = spec_copy;
+ }
+
+ int numplanes = (chroma == de265_chroma_mono ? 1 : 3);
+ for (int i=0; i<numplanes; i++) {
+ uint8_t *data = frame->data[i];
+ if ((uintptr_t)data % spec->alignment) {
+ av_frame_free(&frame);
+ goto fallback;
+ }
+
+ int stride = frame->linesize[i];
+ de265_set_image_plane(img, i, data, stride, frame);
+ }
+ return 1;
+
+fallback:
+ return de265_get_default_image_allocation_functions()->get_buffer(ctx, spec, img, userdata);
+}
+
+
+static void ff_libde265dec_release_buffer(de265_decoder_context* ctx, struct de265_image* img, void* userdata)
+{
+ AVCodecContext *avctx = (AVCodecContext *) userdata;
+ DE265Context *dectx = (DE265Context *) avctx->priv_data;
+ AVFrame *frame = (AVFrame *) de265_get_image_plane_user_data(img, 0);
+ if (frame == NULL) {
+ de265_get_default_image_allocation_functions()->release_buffer(ctx, img, userdata);
+ return;
+ }
+
+ if (frame->opaque) {
+ struct de265_image_spec *spec = (struct de265_image_spec *) frame->opaque;
+ frame->opaque = NULL;
+ free_spec(dectx, spec);
+ }
+
+ if (avctx->get_buffer2) {
+ av_frame_free(&frame);
+ return;
+ }
+
+ if (dectx->frame_queue_len == MAX_FRAME_QUEUE) {
+ av_frame_free(&frame);
+ return;
+ }
+
+ dectx->frame_queue[dectx->frame_queue_len++] = frame;
+}
+#endif
+
+
+static int ff_libde265dec_decode(AVCodecContext *avctx,
+ AVFrame *picture, int *got_frame, AVPacket *avpkt)
+{
+ DE265Context *ctx = (DE265Context *) avctx->priv_data;
+ const struct de265_image *img;
+ de265_error err;
+ int ret;
+ int64_t pts;
+ int more = 0;
+
+ const uint8_t* src[4];
+ int stride[4];
+
+ if (ctx->check_extra) {
+ int extradata_size = avctx->extradata_size;
+ ctx->check_extra = 0;
+ if (extradata_size > 0) {
+ unsigned char *extradata = (unsigned char *) avctx->extradata;
+ if (extradata_size > 3 && extradata != NULL && (extradata[0] || extradata[1] || extradata[2] > 1)) {
+ ctx->packetized = 1;
+ if (extradata_size > 22) {
+ if (extradata[0] != 0) {
+ av_log(avctx, AV_LOG_WARNING, "Unsupported extra data version %d, decoding may fail\n", extradata[0]);
+ }
+ ctx->length_size = (extradata[21] & 3) + 1;
+ int num_param_sets = extradata[22];
+ int pos = 23;
+ for (int i=0; i<num_param_sets; i++) {
+ if (pos + 3 > extradata_size) {
+ av_log(avctx, AV_LOG_ERROR, "Buffer underrun in extra header (%d >= %d)\n", pos + 3, extradata_size);
+ return AVERROR_INVALIDDATA;
+ }
+ // ignore flags + NAL type (1 byte)
+ int nal_count = extradata[pos+1] << 8 | extradata[pos+2];
+ pos += 3;
+ for (int j=0; j<nal_count; j++) {
+ if (pos + 2 > extradata_size) {
+ av_log(avctx, AV_LOG_ERROR, "Buffer underrun in extra nal header (%d >= %d)\n", pos + 2, extradata_size);
+ return AVERROR_INVALIDDATA;
+ }
+ int nal_size = extradata[pos] << 8 | extradata[pos+1];
+ if (pos + 2 + nal_size > extradata_size) {
+ av_log(avctx, AV_LOG_ERROR, "Buffer underrun in extra nal (%d >= %d)\n", pos + 2 + nal_size, extradata_size);
+ return AVERROR_INVALIDDATA;
+ }
+ err = de265_push_NAL(ctx->decoder, extradata + pos + 2, nal_size, 0, NULL);
+ if (!de265_isOK(err)) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to push data: %s (%d)\n", de265_get_error_text(err), err);
+ return AVERROR_INVALIDDATA;
+ }
+ pos += 2 + nal_size;
+ }
+ }
+ }
+ av_log(avctx, AV_LOG_DEBUG, "Assuming packetized data (%d bytes length)\n", ctx->length_size);
+ } else {
+ ctx->packetized = 0;
+ av_log(avctx, AV_LOG_DEBUG, "Assuming non-packetized data\n");
+ err = de265_push_data(ctx->decoder, extradata, extradata_size, 0, NULL);
+ if (!de265_isOK(err)) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to push extra data: %s (%d)\n", de265_get_error_text(err), err);
+ return AVERROR_INVALIDDATA;
+ }
+ }
+#if LIBDE265_NUMERIC_VERSION >= 0x00070000
+ de265_push_end_of_NAL(ctx->decoder);
+#endif
+ do {
+ err = de265_decode(ctx->decoder, &more);
+ switch (err) {
+ case DE265_OK:
+ break;
+
+ case DE265_ERROR_IMAGE_BUFFER_FULL:
+ case DE265_ERROR_WAITING_FOR_INPUT_DATA:
+ // not really an error
+ more = 0;
+ break;
+
+ default:
+ if (!de265_isOK(err)) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to decode extra data: %s (%d)\n", de265_get_error_text(err), err);
+ return AVERROR_INVALIDDATA;
+ }
+ }
+ } while (more);
+ }
+ }
+
+ if (avpkt->size > 0) {
+ if (avpkt->pts != AV_NOPTS_VALUE) {
+ pts = avpkt->pts;
+ } else {
+ // pts = avctx->reordered_opaque;
+ }
+
+ if (ctx->packetized) {
+ uint8_t* avpkt_data = avpkt->data;
+ uint8_t* avpkt_end = avpkt->data + avpkt->size;
+ while (avpkt_data + ctx->length_size <= avpkt_end) {
+ int nal_size = 0;
+ int i;
+ for (i=0; i<ctx->length_size; i++) {
+ nal_size = (nal_size << 8) | avpkt_data[i];
+ }
+ err = de265_push_NAL(ctx->decoder, avpkt_data + ctx->length_size, nal_size, pts, NULL);
+ if (err != DE265_OK) {
+ const char *error = de265_get_error_text(err);
+ av_log(avctx, AV_LOG_ERROR, "Failed to push data: %s\n", error);
+ return AVERROR_INVALIDDATA;
+ }
+ avpkt_data += ctx->length_size + nal_size;
+ }
+ } else {
+ err = de265_push_data(ctx->decoder, avpkt->data, avpkt->size, pts, NULL);
+ if (err != DE265_OK) {
+ const char *error = de265_get_error_text(err);
+ av_log(avctx, AV_LOG_ERROR, "Failed to push data: %s\n", error);
+ return AVERROR_INVALIDDATA;
+ }
+ }
+ } else {
+ de265_flush_data(ctx->decoder);
+ }
+
+#if LIBDE265_NUMERIC_VERSION >= 0x00070000
+ // TODO: libde265 should support more fine-grained settings
+ int deblocking = (avctx->skip_loop_filter < AVDISCARD_NONREF);
+ if (deblocking != ctx->deblocking) {
+ ctx->deblocking = deblocking;
+ de265_set_parameter_bool(ctx->decoder, DE265_DECODER_PARAM_DISABLE_DEBLOCKING, deblocking);
+ // TODO: how to notify to disable SAO?
+ de265_set_parameter_bool(ctx->decoder, DE265_DECODER_PARAM_DISABLE_SAO, deblocking);
+ }
+ int decode_ratio = (avctx->skip_frame < AVDISCARD_NONREF) ? 100 : 0;
+ if (decode_ratio != ctx->decode_ratio) {
+ ctx->decode_ratio = decode_ratio;
+ de265_set_framerate_ratio(ctx->decoder, decode_ratio);
+ }
+#endif
+
+ // decode as much as possible up to next image
+ do {
+ more = 0;
+ err = de265_decode(ctx->decoder, &more);
+ if (err != DE265_OK) {
+ break;
+ }
+ if (de265_peek_next_picture(ctx->decoder) != NULL) {
+ break;
+ }
+ } while (more && err == DE265_OK);
+
+ switch (err) {
+ case DE265_OK:
+ case DE265_ERROR_IMAGE_BUFFER_FULL:
+ case DE265_ERROR_WAITING_FOR_INPUT_DATA:
+ break;
+
+ default:
+ {
+ const char *error = de265_get_error_text(err);
+
+ av_log(avctx, AV_LOG_ERROR, "Failed to decode frame: %s\n", error);
+ return AVERROR_INVALIDDATA;
+ }
+ }
+
+ if ((img = de265_get_next_picture(ctx->decoder)) != NULL) {
+ int width;
+ int height;
+ int bits_per_pixel = LIBDE265_FFMPEG_MAX(
+ LIBDE265_FFMPEG_MAX(
+ de265_get_bits_per_pixel(img, 0),
+ de265_get_bits_per_pixel(img, 1)
+ ),
+ de265_get_bits_per_pixel(img, 2));
+ enum de265_chroma chroma = de265_get_chroma_format(img);
+ enum AVPixelFormat format = get_pixel_format(avctx, chroma, bits_per_pixel);
+ if (format == AV_PIX_FMT_NONE) {
+ return AVERROR_INVALIDDATA;
+ }
+
+ int numplanes = (chroma == de265_chroma_mono ? 1 : 3);
+ avctx->pix_fmt = format;
+ width = de265_get_image_width(img,0);
+ height = de265_get_image_height(img,0);
+ if (width != avctx->width || height != avctx->height) {
+ if (avctx->width != 0)
+ av_log(avctx, AV_LOG_INFO, "dimension change! %dx%d -> %dx%d\n",
+ avctx->width, avctx->height, width, height);
+
+ if (av_image_check_size(width, height, 0, avctx)) {
+ return AVERROR_INVALIDDATA;
+ }
+
+ ff_set_dimensions(avctx, width, height);
+ }
+
+#if LIBDE265_NUMERIC_VERSION >= 0x00070000
+ AVFrame *frame = (AVFrame *) de265_get_image_plane_user_data(img, 0);
+ if (frame != NULL) {
+ av_frame_ref(picture, frame);
+ if (frame->opaque) {
+ // Cropping needed.
+ struct de265_image_spec *spec = (struct de265_image_spec *) frame->opaque;
+ frame->opaque = NULL;
+ picture->width = spec->visible_width;
+ picture->height = spec->visible_height;
+ for (int i=0; i<numplanes; i++) {
+ int shift = (i == 0) ? 0 : 1;
+ int offset = (spec->crop_left >> shift) + (spec->crop_top >> shift) * picture->linesize[i];
+ picture->data[i] += offset;
+ }
+ free_spec(ctx, spec);
+ }
+ } else {
+#endif
+ picture->width = avctx->width;
+ picture->height = avctx->height;
+ picture->format = avctx->pix_fmt;
+ if (avctx->get_buffer2 != NULL) {
+ ret = avctx->get_buffer2(avctx, picture, 0);
+ } else {
+ ret = av_frame_get_buffer(picture, 32);
+ }
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (int i=0;i<=3;i++) {
+ if (i<numplanes) {
+ src[i] = de265_get_image_plane(img, i, &stride[i]);
+ } else {
+ src[i] = NULL;
+ stride[i] = 0;
+ }
+ }
+
+ int equal_strides = 1;
+ for (int i=1; i<numplanes; i++) {
+ if (stride[i-1] != stride[i]) {
+ equal_strides = 0;
+ break;
+ }
+ }
+ if (equal_strides) {
+ // All input planes match the output planes, copy directly.
+ av_image_copy(picture->data, picture->linesize, src, stride,
+ avctx->pix_fmt, width, height);
+ } else {
+ int max_bits_per_pixel = get_output_bits_per_pixel(format);
+ for (int i=0; i<numplanes; i++) {
+ int plane_width = de265_get_image_width(img, i);
+ int plane_height = de265_get_image_height(img, i);
+ int plane_bits_per_pixel = de265_get_bits_per_pixel(img, i);
+ int size = LIBDE265_FFMPEG_MIN(stride[i], picture->linesize[i]);
+ uint8_t* src_ptr = (uint8_t*) src[i];
+ uint8_t* dst_ptr = (uint8_t*) picture->data[i];
+ if (plane_bits_per_pixel > max_bits_per_pixel) {
+ // More bits per pixel in this plane than supported by the output format
+ int shift = (plane_bits_per_pixel - max_bits_per_pixel);
+ for( int line = 0; line < plane_height; line++ ) {
+ uint16_t *s = (uint16_t *) src_ptr;
+ uint16_t *d = (uint16_t *) dst_ptr;
+ for (int pos=0; pos<size/2; pos++) {
+ *d = *s >> shift;
+ d++;
+ s++;
+ }
+ src_ptr += stride[i];
+ dst_ptr += picture->linesize[i];
+ }
+ } else if (plane_bits_per_pixel < max_bits_per_pixel && plane_bits_per_pixel > 8) {
+ // Less bits per pixel in this plane than the rest of the picture
+ // but more than 8bpp.
+ int shift = (max_bits_per_pixel - plane_bits_per_pixel);
+ for( int line = 0; line < plane_height; line++ ) {
+ uint16_t *s = (uint16_t *) src_ptr;
+ uint16_t *d = (uint16_t *) dst_ptr;
+ for (int pos=0; pos<size/2; pos++) {
+ *d = *s << shift;
+ d++;
+ s++;
+ }
+ src_ptr += stride[i];
+ dst_ptr += picture->linesize[i];
+ }
+ } else if (plane_bits_per_pixel < max_bits_per_pixel && plane_bits_per_pixel == 8) {
+ // 8 bits per pixel in this plane, which is less than the rest of the picture.
+ int shift = (max_bits_per_pixel - plane_bits_per_pixel);
+ for( int line = 0; line < plane_height; line++ ) {
+ uint8_t *s = (uint8_t *) src_ptr;
+ uint16_t *d = (uint16_t *) dst_ptr;
+ for (int pos=0; pos<size; pos++) {
+ *d = *s << shift;
+ d++;
+ s++;
+ }
+ src_ptr += stride[i];
+ dst_ptr += picture->linesize[i];
+ }
+ } else {
+ // Bits per pixel of plane match output format.
+ av_image_copy_plane(picture->data[i], picture->linesize[i],
+ src[i], stride[i], size, plane_height);
+
+ }
+ }
+ }
+#if LIBDE265_NUMERIC_VERSION >= 0x00070000
+ }
+#endif
+
+ *got_frame = 1;
+
+ // picture->reordered_opaque = de265_get_image_PTS(img);
+ picture->pts = de265_get_image_PTS(img);
+ }
+ return avpkt->size;
+}
+
+
+static av_cold int ff_libde265dec_free(AVCodecContext *avctx)
+{
+ DE265Context *ctx = (DE265Context *) avctx->priv_data;
+ de265_free_decoder(ctx->decoder);
+#if LIBDE265_NUMERIC_VERSION >= 0x00070000
+ while (ctx->frame_queue_len) {
+ AVFrame *frame = ctx->frame_queue[--ctx->frame_queue_len];
+ av_frame_free(&frame);
+ }
+ while (ctx->spec_queue_len) {
+ struct de265_image_spec *spec = ctx->spec_queue[--ctx->spec_queue_len];
+ free(spec);
+ }
+#endif
+ return 0;
+}
+
+
+static av_cold void ff_libde265dec_flush(AVCodecContext *avctx)
+{
+ DE265Context *ctx = (DE265Context *) avctx->priv_data;
+ de265_reset(ctx->decoder);
+}
+
+
+static av_cold int ff_libde265dec_ctx_init(AVCodecContext *avctx)
+{
+ DE265Context *ctx = (DE265Context *) avctx->priv_data;
+ ctx->decoder = de265_new_decoder();
+ // XXX: always decode multiple threads for now
+ if (1 || avctx->active_thread_type & FF_THREAD_SLICE) {
+ int threads = avctx->thread_count;
+ if (threads <= 0) {
+ threads = av_cpu_count();
+ }
+ if (threads > 0) {
+ // XXX: We start more threads than cores for now, as some threads
+ // might get blocked while waiting for dependent data. Having more
+ // threads increases decoding speed by about 10%
+ threads *= 2;
+ if (threads > 32) {
+ // TODO: this limit should come from the libde265 headers
+ threads = 32;
+ }
+ de265_start_worker_threads(ctx->decoder, threads);
+ }
+ }
+#if LIBDE265_NUMERIC_VERSION >= 0x00070000
+ struct de265_image_allocation allocation;
+ allocation.get_buffer = ff_libde265dec_get_buffer;
+ allocation.release_buffer = ff_libde265dec_release_buffer;
+ de265_set_image_allocation_functions(ctx->decoder, &allocation, avctx);
+#endif
+ ctx->check_extra = 1;
+ ctx->packetized = 0; /// @todo
+ ctx->length_size = 4;
+#if LIBDE265_NUMERIC_VERSION >= 0x00070000
+ ctx->deblocking = 1;
+ ctx->decode_ratio = 100;
+ ctx->frame_queue_len = 0;
+ ctx->spec_queue_len = 0;
+#endif
+ return 0;
+}
+
+FFCodec ff_libde265_decoder = {
+ .p.name = "libde265",
+ CODEC_LONG_NAME("libde265 H.265/HEVC decoder"),
+ .priv_data_size = sizeof(DE265Context),
+ .p.type = AVMEDIA_TYPE_VIDEO,
+ .p.id = AV_CODEC_ID_HEVC,
+ .init = ff_libde265dec_ctx_init,
+ FF_CODEC_DECODE_CB(ff_libde265dec_decode),
+ .close = ff_libde265dec_free,
+ .flush = ff_libde265dec_flush,
+ .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS | // AV_CODEC_CAP_DR1 | /// @todo
+ AV_CODEC_CAP_SLICE_THREADS,
+};
--
2.51.0