mirror of
https://github.com/outbackdingo/UltraGrid.git
synced 2026-03-19 23:09:13 +00:00
849 lines
32 KiB
Diff
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
|
|
|