From 50cc66377563962768743de0a8108b250c594772 Mon Sep 17 00:00:00 2001 From: Martin Pulec 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 + * Copyright (c) 2013-2015, Joachim Bauch + * + * 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 + +#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; idata[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 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 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; ilength_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; icrop_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 (idata, 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; ilinesize[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> 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; poslinesize[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; poslinesize[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