[FFmpeg-devel] [PATCH v12 5/9] libavcodec/dnxucdec: DNxUncompressed decoder
Martin Schitter
ms+git at mur.at
Mon Oct 21 22:57:18 EEST 2024
---
libavcodec/Makefile | 1 +
libavcodec/allcodecs.c | 1 +
libavcodec/dnxucdec.c | 338 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 340 insertions(+)
create mode 100644 libavcodec/dnxucdec.c
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index dd5d0de..e13b127 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -328,6 +328,7 @@ OBJS-$(CONFIG_DFPWM_DECODER) += dfpwmdec.o
OBJS-$(CONFIG_DFPWM_ENCODER) += dfpwmenc.o
OBJS-$(CONFIG_DNXHD_DECODER) += dnxhddec.o dnxhddata.o
OBJS-$(CONFIG_DNXHD_ENCODER) += dnxhdenc.o dnxhddata.o
+OBJS-$(CONFIG_DNXUC_DECODER) += dnxucdec.o
OBJS-$(CONFIG_DOLBY_E_DECODER) += dolby_e.o dolby_e_parse.o kbdwin.o
OBJS-$(CONFIG_DPX_DECODER) += dpx.o
OBJS-$(CONFIG_DPX_ENCODER) += dpxenc.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index c7e5f99..ccca2ad 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -93,6 +93,7 @@ extern const FFCodec ff_dfa_decoder;
extern const FFCodec ff_dirac_decoder;
extern const FFCodec ff_dnxhd_encoder;
extern const FFCodec ff_dnxhd_decoder;
+extern const FFCodec ff_dnxuc_decoder;
extern const FFCodec ff_dpx_encoder;
extern const FFCodec ff_dpx_decoder;
extern const FFCodec ff_dsicinvideo_decoder;
diff --git a/libavcodec/dnxucdec.c b/libavcodec/dnxucdec.c
new file mode 100644
index 0000000..9d5847d
--- /dev/null
+++ b/libavcodec/dnxucdec.c
@@ -0,0 +1,338 @@
+/*
+ * Avid DNxUncomressed / SMPTE RDD 50 decoder
+ * Copyright (c) 2024 Martin Schitter
+ *
+ * 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
+ */
+
+/*
+ This decoder for DNxUncompressed video data is mostly based on
+ reverse engineering of output generated by DaVinci Resolve 19
+ but was later also checked against the SMPTE RDD 50 specification.
+
+ Not all DNxUncompressed pixel format variants are supported,
+ but at least an elementary base set is already usable:
+
+ - YUV 4:2:2 8/10/12/16bit/half/float (16bit untested)
+ YUV 4:4:4 8/16bit/half/float (all untested!)
+ - RGB 8/10/12/16bit/half/float (16bit untested)
+ Alpha/Y 8/16bit (all untested!)
+
+ TODO: Compositions of multiple components aren't supportet until now.
+ TODO: This also hinders Image+Alpha use in one file.
+
+*/
+
+#include "avcodec.h"
+#include "codec_internal.h"
+#include "decode.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/intreadwrite.h"
+#include "thread.h"
+
+static int pass_through(AVCodecContext *avctx, AVFrame *frame, const AVPacket *avpkt)
+{
+ /* there is no need to copy as the data already match
+ * a known pixel format */
+
+ frame->buf[0] = av_buffer_ref(avpkt->buf);
+
+ if (!frame->buf[0]) {
+ return AVERROR(ENOMEM);
+ }
+
+ return av_image_fill_arrays(frame->data, frame->linesize, avpkt->data,
+ avctx->pix_fmt, avctx->width, avctx->height, 1);
+}
+
+/// Unpack 10bit value
+static av_always_inline
+uint16_t get10(uint8_t *line_data, uint32_t pos, int msb_bytes)
+{
+ return (line_data[pos] << 2) |
+ ((line_data[msb_bytes + (pos >> 2)] >> ((pos & 0x3u) << 1)) & 0x3u);
+}
+
+/// Unpack 12bit value
+static av_always_inline
+uint16_t get12(uint8_t *line_data, uint32_t pos, int msb_bytes)
+{
+ return (line_data[pos] << 4) |
+ ((line_data[msb_bytes + (pos >> 1)] >> ((pos & 0x1u) << 2)) & 0xfu);
+}
+
+static int unpack_rg10(AVCodecContext *avctx, AVFrame *frame, const AVPacket *pkt)
+{
+ uint8_t *data = &pkt->data[0];
+ int lw = frame->width;
+ int msb_bytes = lw * 3;
+ int line_offset = lw * 3 + (lw * 3 + 3) / 4;
+ int pos = 0, pos_in = 0;
+ for(int y = 0; y < frame->height; y++){
+ for(int x = 0; x < lw; x++){
+ AV_WL16(&frame->data[2][pos], get10(data, pos_in++, msb_bytes)); // r
+ AV_WL16(&frame->data[0][pos], get10(data, pos_in++, msb_bytes)); // g
+ AV_WL16(&frame->data[1][pos], get10(data, pos_in++, msb_bytes)); // b
+ pos += 2;
+ }
+ data += line_offset;
+ pos_in = 0;
+ }
+ return pkt->size;
+}
+
+static int unpack_y410(AVCodecContext *avctx, AVFrame *frame, const AVPacket *pkt)
+{
+ uint8_t *data = &pkt->data[0];
+ int lw = frame->width;
+ int msb_bytes = lw * 3;
+ int line_offset = lw * 3 + (lw * 3 + 3) / 4;
+ int pos = 0, pos_in = 0;
+ for(int y = 0; y < frame->height; y++){
+ for(int x = 0; x < lw; x++){
+ AV_WL16(&frame->data[0][pos], get10(data, pos_in++, msb_bytes)); // y
+ AV_WL16(&frame->data[1][pos], get10(data, pos_in++, msb_bytes)); // u
+ AV_WL16(&frame->data[2][pos], get10(data, pos_in++, msb_bytes)); // v
+ pos += 2;
+ }
+ data += line_offset;
+ pos_in = 0;
+ }
+ return pkt->size;
+}
+
+static int unpack_rg12(AVCodecContext *avctx, AVFrame *frame, const AVPacket *pkt)
+{
+ uint8_t *data = &pkt->data[0];
+ int lw = frame->width;
+ int msb_bytes = lw * 3;
+ int line_offset = lw * 3 + (lw * 3 + 1) / 2;
+ int pos = 0, pos_in = 0;
+ for(int y = 0; y < frame->height; y++){
+ for(int x = 0; x < lw; x++){
+ AV_WL16(&frame->data[2][pos], get12(data, pos_in++, msb_bytes)); // r
+ AV_WL16(&frame->data[0][pos], get12(data, pos_in++, msb_bytes)); // g
+ AV_WL16(&frame->data[1][pos], get12(data, pos_in++, msb_bytes)); // b
+ pos += 2;
+ }
+ data += line_offset;
+ pos_in = 0;
+ }
+ return pkt->size;
+}
+
+static int unpack_y412(AVCodecContext *avctx, AVFrame *frame, const AVPacket *pkt)
+{
+ uint8_t *data = &pkt->data[0];
+ int lw = frame->width;
+ int msb_bytes = lw * 3;
+ int line_offset = lw * 3 + (lw * 3 + 1) / 2;
+ int pos = 0, pos_in = 0;
+ for(int y = 0; y < frame->height; y++){
+ for(int x = 0; x < lw; x++){
+ AV_WL16(&frame->data[0][pos], get12(data, pos_in++, msb_bytes)); // y
+ AV_WL16(&frame->data[1][pos], get12(data, pos_in++, msb_bytes)); // u
+ AV_WL16(&frame->data[2][pos], get12(data, pos_in++, msb_bytes)); // v
+ pos += 2;
+ }
+ data += line_offset;
+ pos_in = 0;
+ }
+ return pkt->size;
+}
+
+static int unpack_y210(AVCodecContext *avctx, AVFrame *frame, const AVPacket *pkt)
+{
+ uint8_t *data = &pkt->data[0];
+ int lw = frame->width;
+ int msb_bytes = lw * 2;
+ int line_offset = lw/2 * 5;
+ int pos_uv = 0, pos_y = 0, pos_in = 0;
+ for(int y = 0; y < frame->height; y++){
+ for(int x = 0; x < lw/2; x++){
+ AV_WL16(&frame->data[1][pos_uv], get10(data, pos_in++, msb_bytes)); // u
+ AV_WL16(&frame->data[0][pos_y], get10(data, pos_in++, msb_bytes)); // y
+ AV_WL16(&frame->data[2][pos_uv], get10(data, pos_in++, msb_bytes)); // v
+ AV_WL16(&frame->data[0][pos_y + 2], get10(data, pos_in++, msb_bytes)); // y
+ pos_uv += 2;
+ pos_y += 4;
+ }
+ data += line_offset;
+ pos_in = 0;
+ }
+ return pkt->size;
+}
+
+static int unpack_y212(AVCodecContext *avctx, AVFrame *frame, const AVPacket *pkt)
+{
+ uint8_t *data = &pkt->data[0];
+ int lw = frame->width;
+ int msb_bytes = lw * 2;
+ int line_offset = lw * 3;
+ int pos_uv = 0, pos_y = 0, pos_in = 0;
+ for(int y = 0; y < frame->height; y++){
+ for(int x = 0; x < lw/2; x++){
+ AV_WL16(&frame->data[1][pos_uv], get12(data, pos_in++, msb_bytes)); // u
+ AV_WL16(&frame->data[0][pos_y], get12(data, pos_in++, msb_bytes)); // y
+ AV_WL16(&frame->data[2][pos_uv], get12(data, pos_in++, msb_bytes)); // v
+ AV_WL16(&frame->data[0][pos_y + 2],get12(data, pos_in++, msb_bytes)); // y
+ pos_uv += 2;
+ pos_y += 4;
+ }
+ data += line_offset;
+ pos_in = 0;
+ }
+ return pkt->size;
+}
+
+static int check_pkt_size(AVCodecContext *avctx, AVPacket *avpkt, int bpp)
+{
+ int needed = ((avctx->width * bpp + 7) / 8) * avctx->height;
+ if (avpkt->size < needed){
+ av_log(avctx, AV_LOG_ERROR,
+ "Insufficient size of AVPacket data (pkg size: %d needed: %d)\n", avpkt->size, needed);
+ return AVERROR_INVALIDDATA;
+ }
+ return 0;
+}
+
+static int fmt_frame(AVCodecContext *avctx, AVFrame *frame, AVPacket *avpkt,
+ enum AVPixelFormat pix_fmt, int src_bpp,
+ int (*frame_handler)(AVCodecContext *avctx, AVFrame *frame, const AVPacket *avpkt))
+{
+ int ret;
+ avctx->pix_fmt = pix_fmt;
+
+ ret = check_pkt_size(avctx, avpkt, src_bpp);
+ if (ret)
+ return ret;
+
+ ret = ff_thread_get_buffer(avctx, frame, 0);
+ if (ret < 0)
+ return ret;
+
+ return frame_handler(avctx, frame, avpkt);
+}
+
+static int dnxuc_decode_frame(AVCodecContext *avctx, AVFrame *frame,
+ int *got_frame, AVPacket *avpkt)
+{
+ char fourcc_buf[AV_FOURCC_MAX_STRING_SIZE];
+ int ret;
+
+ av_fourcc_make_string(fourcc_buf, avctx->codec_tag);
+ if ((avctx->width % 2) && ((fourcc_buf[0] == 'y' && fourcc_buf[1] == '2')
+ ||(fourcc_buf[1] == 'y' && fourcc_buf[2] == '2'))){
+ av_log(avctx, AV_LOG_ERROR,
+ "Image width must be a multiple of 2 for YUV 4:2:2 DNxUncompressed!\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ switch (avctx->codec_tag) {
+ case MKTAG('y','2','0','8'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_UYVY422, 16, pass_through);
+ break;
+ case MKTAG('y','4','0','8'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_YUV444, 24, pass_through);
+ break;
+ case MKTAG('y','2','1','0'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_YUV422P10LE, 20, unpack_y210);
+ break;
+ case MKTAG('y','4','1','0'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_YUV444P10LE, 20, unpack_y410);
+ break;
+ case MKTAG('y','2','1','2'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_YUV422P12LE, 24, unpack_y212);
+ break;
+ case MKTAG('y','4','1','2'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_YUV444P12LE, 24, unpack_y412);
+ break;
+ case MKTAG('y','2','1','6'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_UYVY422_16LE, 32, pass_through);
+ break;
+ case MKTAG('y','4','1','6'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_YUV444_16LE, 48, pass_through);
+ break;
+ case MKTAG(' ','y','2','h'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_UYVY422F16LE, 32, pass_through);
+ break;
+ case MKTAG(' ','y','4','h'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_YUV444F16LE, 48, pass_through);
+ break;
+ case MKTAG(' ','y','2','f'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_UYVY422F32LE, 64, pass_through);
+ break;
+ case MKTAG(' ','y','4','f'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_YUV444F32LE, 96, pass_through);
+ break;
+
+ case MKTAG('r','g','0','8'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_RGB24, 24, pass_through);
+ break;
+ case MKTAG('r','g','1','0'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_GBRP10LE, 30, unpack_rg10);
+ break;
+ case MKTAG('r','g','1','2'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_GBRP12LE, 36, unpack_rg12);
+ break;
+ case MKTAG('r','g','1','6'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_RGB48LE, 48, pass_through);
+ break;
+ case MKTAG(' ','r','g','h'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_RGBF16LE, 48, pass_through);
+ break;
+ case MKTAG(' ','r','g','f'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_RGBF32LE, 96, pass_through);
+ break;
+
+ case MKTAG(' ','a','0','8'):
+ case MKTAG(' ','y','0','8'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_GRAY8, 8, pass_through);
+ break;
+ case MKTAG(' ','a','1','6'):
+ case MKTAG(' ','y','1','6'):
+ ret = fmt_frame(avctx, frame, avpkt, AV_PIX_FMT_GRAY16LE, 16, pass_through);
+ break;
+
+ // case MKTAG('r','l','0','8'): TODO: RLE encoded 8bit alpha
+ // case MKTAG('r','l','1','6'): TODO: RLE encoded 16bit alpha
+
+ default:
+ av_log(avctx, AV_LOG_ERROR,
+ "Unsupported DNxUncompressed pixel format variant: '%s'\n",
+ fourcc_buf);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ if (ret < 0) {
+ av_buffer_unref(&frame->buf[0]);
+ return ret;
+ }
+
+ *got_frame = 1;
+
+ return avpkt->size;
+}
+
+const FFCodec ff_dnxuc_decoder = {
+ .p.name = "dnxuc",
+ CODEC_LONG_NAME("DNxUncompressed (SMPTE RDD 50)"),
+ .p.type = AVMEDIA_TYPE_VIDEO,
+ .p.id = AV_CODEC_ID_DNXUC,
+ FF_CODEC_DECODE_CB(dnxuc_decode_frame),
+ .p.capabilities = AV_CODEC_CAP_FRAME_THREADS,
+};
--
2.45.2
More information about the ffmpeg-devel
mailing list