diff mbox series

[FFmpeg-devel] libavcodec: r12b decoder

Message ID CAPUCmECSxQwMtttmRqTC-JtOpcfUB9SVQ8ZPZOHULFL9=2DC0Q@mail.gmail.com
State Superseded
Headers show
Series [FFmpeg-devel] libavcodec: r12b decoder | expand

Checks

Context Check Description
andriy/x86_make_warn warning New warnings during build
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

Dennis Fleurbaaij June 7, 2021, 10:09 a.m. UTC
Pulled master and re-applied to resolve version update conflict

Kind regards,
Dennis Fleurbaaij
From b5fff34f545a107e07c9390a983e3a9aadb71a1c Mon Sep 17 00:00:00 2001
From: Dennis Fleurbaaij <mail@dennisfleurbaaij.com>
Date: Mon, 7 Jun 2021 12:06:51 +0200
Subject: [PATCH] libavcodec: r12b decoder added

R12B is a format used by BlackMagic DeckLink cards, it is
a big-endian 12bpp RGB format which packs 8 pixels into 36
bytes.
---
 Changelog               |   1 +
 libavcodec/Makefile     |   1 +
 libavcodec/allcodecs.c  |   1 +
 libavcodec/codec_desc.c |   7 ++
 libavcodec/codec_id.h   |   1 +
 libavcodec/r12bdec.c    | 139 ++++++++++++++++++++++++++++++++++++++++
 libavcodec/version.h    |   4 +-
 libavformat/riff.c      |   1 +
 8 files changed, 153 insertions(+), 2 deletions(-)
 create mode 100644 libavcodec/r12bdec.c

Comments

Andreas Rheinhardt June 7, 2021, 10:59 a.m. UTC | #1
Dennis Fleurbaaij:
> Pulled master and re-applied to resolve version update conflict
> 
> Kind regards,
> Dennis Fleurbaaij
> 
> 
> 
> +    if (avctx->width % PIXELS_PER_BLOCK != 0) {
> +        av_log(avctx, AV_LOG_ERROR, "image width not modulo 8\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    AVFrame *pic = data;
> +    pic->pict_type = AV_PICTURE_TYPE_I;
> +    pic->key_frame = 1;
> +

This (and other declarations below) should give statement-before
declaration warnings (FFmpeg still uses the C90 convention that all
declarations have to be at the top of each block.

> +    const uint8_t* src = (const uint8_t *)avpkt->data;

Unnecessary cast (adding const is safe and doesn't need a cast).

> 
> +            #define GET_F0(word, byte) ((GET_FF(word, byte) & 0xF0) >> 4)

The & is unnecessary, but the compiler will probably figure this out on
its own.

> 
> +#if CONFIG_R12B_DECODER
> +const AVCodec ff_r12b_decoder = {
> +    .name           = "r12b",
> +    .long_name      = NULL_IF_CONFIG_SMALL("Uncompressed RGB 12-bit 8px in 36B"),
> +    .type           = AVMEDIA_TYPE_VIDEO,
> +    .id             = AV_CODEC_ID_R12B,
> +    .init           = decode_init,
> +    .decode         = decode_frame,
> +    .capabilities   = AV_CODEC_CAP_DR1,
> +    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
> +};
> +#endif

The #if is unnecessary as r12bdec.c will only get compiled if
CONFIG_R12B_DECODER is true.

- Andreas
Dennis Fleurbaaij June 7, 2021, 8:10 p.m. UTC | #2
Thanks for the review Andreas!

I've addressed all your concerns besides the "& in the define", I didn't
know that the binary AND is implicit in this situation, any link for this?
Even if it is, I just find it much more pleasing to see the & there
somehow, is there some leniency for personal preference?

Revised patch attached.

Kind regards,
Dennis Fleurbaaij


On Mon, Jun 7, 2021 at 12:59 PM Andreas Rheinhardt <
andreas.rheinhardt@outlook.com> wrote:

> Dennis Fleurbaaij:
> > Pulled master and re-applied to resolve version update conflict
> >
> > Kind regards,
> > Dennis Fleurbaaij
> >
> >
> >
> > +    if (avctx->width % PIXELS_PER_BLOCK != 0) {
> > +        av_log(avctx, AV_LOG_ERROR, "image width not modulo 8\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    AVFrame *pic = data;
> > +    pic->pict_type = AV_PICTURE_TYPE_I;
> > +    pic->key_frame = 1;
> > +
>
> This (and other declarations below) should give statement-before
> declaration warnings (FFmpeg still uses the C90 convention that all
> declarations have to be at the top of each block.
>
> > +    const uint8_t* src = (const uint8_t *)avpkt->data;
>
> Unnecessary cast (adding const is safe and doesn't need a cast).
>
> >
> > +            #define GET_F0(word, byte) ((GET_FF(word, byte) & 0xF0) >>
> 4)
>
> The & is unnecessary, but the compiler will probably figure this out on
> its own.
>
> >
> > +#if CONFIG_R12B_DECODER
> > +const AVCodec ff_r12b_decoder = {
> > +    .name           = "r12b",
> > +    .long_name      = NULL_IF_CONFIG_SMALL("Uncompressed RGB 12-bit 8px
> in 36B"),
> > +    .type           = AVMEDIA_TYPE_VIDEO,
> > +    .id             = AV_CODEC_ID_R12B,
> > +    .init           = decode_init,
> > +    .decode         = decode_frame,
> > +    .capabilities   = AV_CODEC_CAP_DR1,
> > +    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
> > +};
> > +#endif
>
> The #if is unnecessary as r12bdec.c will only get compiled if
> CONFIG_R12B_DECODER is true.
>
> - Andreas
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
From 34f967772e3c8378ac3dd1f55a293e478dcc919c Mon Sep 17 00:00:00 2001
From: Dennis Fleurbaaij <mail@dennisfleurbaaij.com>
Date: Mon, 7 Jun 2021 21:53:42 +0200
Subject: [PATCH] libavcodec: r12b decoder added

R12B is a format used by BlackMagic DeckLink cards, it is
a big-endian 12bpp RGB format which packs 8 pixels into 36
bytes.

Signed-off-by: Dennis Fleurbaaij <mail@dennisfleurbaaij.com>
---
 Changelog               |   1 +
 libavcodec/Makefile     |   1 +
 libavcodec/allcodecs.c  |   1 +
 libavcodec/codec_desc.c |   7 ++
 libavcodec/codec_id.h   |   1 +
 libavcodec/r12bdec.c    | 139 ++++++++++++++++++++++++++++++++++++++++
 libavcodec/version.h    |   4 +-
 libavformat/riff.c      |   1 +
 8 files changed, 153 insertions(+), 2 deletions(-)
 create mode 100644 libavcodec/r12bdec.c

diff --git a/Changelog b/Changelog
index b9d5188cf6..d0717072eb 100644
--- a/Changelog
+++ b/Changelog
@@ -7,6 +7,7 @@ version <next>:
 - ADPCM IMA Acorn Replay decoder
 - Argonaut Games CVG demuxer
 - Argonaut Games CVG muxer
+- r12b decoder
 
 
 version 4.4:
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 4fa8d7ab10..cfef9d57ff 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -575,6 +575,7 @@ OBJS-$(CONFIG_QTRLE_ENCODER)           += qtrleenc.o
 OBJS-$(CONFIG_R10K_DECODER)            += r210dec.o
 OBJS-$(CONFIG_R10K_ENCODER)            += r210enc.o
 OBJS-$(CONFIG_R210_DECODER)            += r210dec.o
+OBJS-$(CONFIG_R12B_DECODER)            += r12bdec.o
 OBJS-$(CONFIG_R210_ENCODER)            += r210enc.o
 OBJS-$(CONFIG_RA_144_DECODER)          += ra144dec.o ra144.o celp_filters.o
 OBJS-$(CONFIG_RA_144_ENCODER)          += ra144enc.o ra144.o celp_filters.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 623db2a9fa..8db7e730a6 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -266,6 +266,7 @@ extern const AVCodec ff_qtrle_encoder;
 extern const AVCodec ff_qtrle_decoder;
 extern const AVCodec ff_r10k_encoder;
 extern const AVCodec ff_r10k_decoder;
+extern const AVCodec ff_r12b_decoder;
 extern const AVCodec ff_r210_encoder;
 extern const AVCodec ff_r210_decoder;
 extern const AVCodec ff_rasc_decoder;
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 35527dcc37..ef58d73576 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -1856,6 +1856,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("Digital Pictures SGA Video"),
         .props     = AV_CODEC_PROP_LOSSY,
     },
+    {
+        .id        = AV_CODEC_ID_R12B,
+        .type      = AVMEDIA_TYPE_VIDEO,
+        .name      = "r12b",
+        .long_name = NULL_IF_CONFIG_SMALL("Uncompressed RGB 12-bit 8px in 36B"),
+        .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS,
+    },
 
     /* various PCM "codecs" */
     {
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index 83e1dbb4b3..ecfdbc46c0 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -306,6 +306,7 @@ enum AVCodecID {
     AV_CODEC_ID_CRI,
     AV_CODEC_ID_SIMBIOSIS_IMX,
     AV_CODEC_ID_SGA_VIDEO,
+    AV_CODEC_ID_R12B,
 
     /* various PCM "codecs" */
     AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/r12bdec.c b/libavcodec/r12bdec.c
new file mode 100644
index 0000000000..4f9c085ecd
--- /dev/null
+++ b/libavcodec/r12bdec.c
@@ -0,0 +1,139 @@
+/*
+ * r12b decoder
+ *
+ * Copyright (c) 2021 Dennis Fleurbaaij <mail@dennisfleurbaaij.com>
+ *
+ * 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
+ */
+
+#include "avcodec.h"
+#include "internal.h"
+#include "libavutil/bswap.h"
+#include "libavutil/common.h"
+
+#define WORDS_PER_BLOCK 9
+#define PIXELS_PER_BLOCK 8
+#define BYTES_PER_BLOCK 36
+
+static av_cold int decode_init(AVCodecContext *avctx)
+{
+    avctx->pix_fmt = AV_PIX_FMT_GBRP12LE;
+    avctx->bits_per_raw_sample = 12;
+
+    return 0;
+}
+
+static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame,
+                        AVPacket *avpkt)
+{
+    int ret, h, w;
+    uint8_t *g_line, *b_line, *r_line;
+    uint16_t *g_dst, *b_dst, *r_dst;
+
+    AVFrame *pic = data;
+    const uint8_t* src = avpkt->data;
+    const int blocks_per_line = avctx->width / PIXELS_PER_BLOCK;
+
+    if (avctx->width % PIXELS_PER_BLOCK != 0) {
+        av_log(avctx, AV_LOG_ERROR, "image width not modulo 8\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    pic->pict_type = AV_PICTURE_TYPE_I;
+    pic->key_frame = 1;
+
+    if ((ret = ff_get_buffer(avctx, pic, 0)) < 0)
+        return ret;
+
+    g_line = pic->data[0];
+    b_line = pic->data[1];
+    r_line = pic->data[2];
+
+    for (h = 0; h < avctx->height; h++) {
+        g_dst = (uint16_t *)g_line;
+        b_dst = (uint16_t *)b_line;
+        r_dst = (uint16_t *)r_line;
+
+        for (w = 0; w < blocks_per_line; w++) {
+
+            // This is an encoding from the table on page 213 of the BlackMagic
+            // Decklink SDK pdf, version 12.0. Few helper defines to directly link
+            // the naming in the doc to the code.
+
+            #define GET_FF(word, byte) (*(src + ((word * 4) + byte)))
+            #define GET_0F(word, byte) (GET_FF(word, byte) & 0x0F)
+            #define GET_F0(word, byte) ((GET_FF(word, byte) & 0xF0) >> 4)
+            #define PUT(dst, pixel) (*(dst + pixel))
+
+            PUT(b_dst, 0) = GET_FF(0, 0) | GET_0F(1, 3) << 8;
+            PUT(g_dst, 0) = GET_F0(0, 2) | GET_FF(0, 1) << 4;
+            PUT(r_dst, 0) = GET_FF(0, 3) | GET_0F(0, 2) << 8;
+
+            PUT(b_dst, 1) = GET_F0(1, 0) | GET_FF(2, 3) << 4;
+            PUT(g_dst, 1) = GET_FF(1, 1) | GET_0F(1, 0) << 8;
+            PUT(r_dst, 1) = GET_F0(1, 3) | GET_FF(1, 2) << 4;
+
+            PUT(b_dst, 2) = GET_FF(3, 3) | GET_0F(3, 2) << 8;
+            PUT(g_dst, 2) = GET_F0(2, 1) | GET_FF(2, 0) << 4;
+            PUT(r_dst, 2) = GET_FF(2, 2) | GET_0F(2, 1) << 8;
+
+            PUT(b_dst, 3) = GET_F0(4, 3) | GET_FF(4, 2) << 4;
+            PUT(g_dst, 3) = GET_FF(3, 0) | GET_0F(4, 3) << 8;
+            PUT(r_dst, 3) = GET_F0(3, 2) | GET_FF(3, 1) << 4;
+
+            PUT(b_dst, 4) = GET_FF(5, 2) | GET_0F(5, 1) << 8;
+            PUT(g_dst, 4) = GET_F0(4, 0) | GET_FF(5, 3) << 4;
+            PUT(r_dst, 4) = GET_FF(4, 1) | GET_0F(4, 0) << 8;
+
+            PUT(b_dst, 5) = GET_F0(6, 2) | GET_FF(6, 1) << 4;
+            PUT(g_dst, 5) = GET_FF(6, 3) | GET_0F(6, 2) << 8;
+            PUT(r_dst, 5) = GET_F0(5, 1) | GET_FF(5, 0) << 4;
+
+            PUT(b_dst, 6) = GET_FF(7, 1) | GET_0F(7, 0) << 8;
+            PUT(g_dst, 6) = GET_F0(7, 3) | GET_FF(7, 2) << 4;
+            PUT(r_dst, 6) = GET_FF(6, 0) | GET_0F(7, 3) << 8;
+
+            PUT(b_dst, 7) = GET_F0(8, 1) | GET_FF(8, 0) << 4;
+            PUT(g_dst, 7) = GET_FF(8, 2) | GET_0F(8, 1) << 8;
+            PUT(r_dst, 7) = GET_F0(7, 0) | GET_FF(8, 3) << 4;
+
+            src += BYTES_PER_BLOCK;
+            b_dst += PIXELS_PER_BLOCK;
+            g_dst += PIXELS_PER_BLOCK;
+            r_dst += PIXELS_PER_BLOCK;
+        }
+
+        g_line += pic->linesize[0];
+        b_line += pic->linesize[1];
+        r_line += pic->linesize[2];
+    }
+
+    *got_frame = 1;
+
+    return avpkt->size;
+}
+
+const AVCodec ff_r12b_decoder = {
+    .name           = "r12b",
+    .long_name      = NULL_IF_CONFIG_SMALL("Uncompressed RGB 12-bit 8px in 36B"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_R12B,
+    .init           = decode_init,
+    .decode         = decode_frame,
+    .capabilities   = AV_CODEC_CAP_DR1,
+    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
+};
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 5b1e9e77f3..1288cecebe 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -28,8 +28,8 @@
 #include "libavutil/version.h"
 
 #define LIBAVCODEC_VERSION_MAJOR  59
-#define LIBAVCODEC_VERSION_MINOR   1
-#define LIBAVCODEC_VERSION_MICRO 101
+#define LIBAVCODEC_VERSION_MINOR   2
+#define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
                                                LIBAVCODEC_VERSION_MINOR, \
diff --git a/libavformat/riff.c b/libavformat/riff.c
index 270ff7c024..6e303f59ff 100644
--- a/libavformat/riff.c
+++ b/libavformat/riff.c
@@ -295,6 +295,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
     { AV_CODEC_ID_FRWU,         MKTAG('F', 'R', 'W', 'U') },
     { AV_CODEC_ID_R10K,         MKTAG('R', '1', '0', 'k') },
     { AV_CODEC_ID_R210,         MKTAG('r', '2', '1', '0') },
+    { AV_CODEC_ID_R12B,         MKTAG('r', '1', '2', 'b') },
     { AV_CODEC_ID_V210,         MKTAG('v', '2', '1', '0') },
     { AV_CODEC_ID_V210,         MKTAG('C', '2', '1', '0') },
     { AV_CODEC_ID_V308,         MKTAG('v', '3', '0', '8') },
Andreas Rheinhardt June 7, 2021, 8:32 p.m. UTC | #3
Dennis Fleurbaaij:
> Thanks for the review Andreas!
> 
> I've addressed all your concerns besides the "& in the define", I didn't
> know that the binary AND is implicit in this situation, any link for this?
> Even if it is, I just find it much more pleasing to see the & there
> somehow, is there some leniency for personal preference?
> 

The lower nibble doesn't survive the right shift; and given that
GET_FF() returns an uint8_t, there are no bits higher than 0xFF anyway.

I have no objection to keeping the "&".

> 
> +    for (h = 0; h < avctx->height; h++) {
> +        g_dst = (uint16_t *)g_line;
> +        b_dst = (uint16_t *)b_line;
> +        r_dst = (uint16_t *)r_line;
> +

C90 disallows declarations in the middle of blocks, but it allows them
at the beginning of *any* block, not only the outermost block of a
function. You can declare these variables here.

And we also allow for loops with variable declarations, i.e. you may
declare h (and w later) via "for (int h = 0;". (This is illegal in C90.)


Your code currently does not check the size of the input packet: It
needs to be at least height * width / PIXELS_PER_BLOCK *
BYTES_PER_BLOCK, but it doesn't check that (and I guess that too big
packets are also invalid?).

- Andreas
Carl Eugen Hoyos June 7, 2021, 10:54 p.m. UTC | #4
Von meinem iPhone gesendet

> Am 07.06.2021 um 12:09 schrieb Dennis Fleurbaaij <mail@dennisfleurbaaij.com>:
> 
> Pulled master and re-applied to resolve version update conflict 

> R12B is a format used by BlackMagic DeckLink cards

How does the card produce avi files / why are you adding a fourcc?

Please elaborate, Carl Eugen
Dennis Fleurbaaij June 8, 2021, 9:52 a.m. UTC | #5
Hi Carl,

Thanks for the question.

The four letter "r12b" format code is from the DeckLink documentation;
there are other described formats there as well such as r12l, r10b, r10l
and r210. I'm using DirectShow which signals the encoding to downstream
components through the use of FOURCC codes and hence for this filter to be
of any use in such applications there needs to be a way to identify it.

r210 from their docs has already gained some traction as a fourcc and hence
adding the others in a similar vein seemed a reasonable approach. "r12b"
also sounds reasonable to me as a fourcc: it starts with an r, indicative
of RGB formats, and the bit/pixel + endianness didn't seem unreasonable.
Also I could find no other fourcc's used for this and hence I added it.

Kind regards,
Dennis Fleurbaaij


On Tue, Jun 8, 2021 at 1:57 AM Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:

>
>
> Von meinem iPhone gesendet
>
> > Am 07.06.2021 um 12:09 schrieb Dennis Fleurbaaij <
> mail@dennisfleurbaaij.com>:
> >
> > Pulled master and re-applied to resolve version update conflict
>
> > R12B is a format used by BlackMagic DeckLink cards
>
> How does the card produce avi files / why are you adding a fourcc?
>
> Please elaborate, Carl Eugen
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
Valerii Zapodovnikov June 8, 2021, 10:09 a.m. UTC | #6
BTW, who knows how to show warnings on make job on patchwork? Only if make
fate fails you can see it, but not otherwise. Also, md5 warnings, when you
are going to fix those??

https://patchwork.ffmpeg.org/project/ffmpeg/patch/CAPUCmECSxQwMtttmRqTC-JtOpcfUB9SVQ8ZPZOHULFL9=2DC0Q@mail.gmail.com/
Dennis Fleurbaaij June 8, 2021, 5:45 p.m. UTC | #7
Thanks again for the review Andreas. I've addressed the comments:

   - moved back to the C90+ style which was in other decoders as well.
   - The buffer size check makes sense and I've added that, I'm unsure
   about how ffmpeg exactly handles alignment in some cases so I've opted to
   stay on the lenient side and only check if it's too small (I see that
   pattern more often such as in in r210.c)
   - Once you said "shift", I understood what you meant with the "& 0xF0"
   comment, it's not about the interaction with the data before it, which I
   was thinking you meant, it's just totally not needed and I agree with that.
   So that has been cleaned up.
   - removed two unused includes
   - updated to the latest master

(There are warnings generated during build as Valerii also points out in
his reply, in the previous patch it was md5, this time there are a few
others. These do not stem from this PR for as far as I can see.)

Kind regards,
Dennis Fleurbaaij


On Mon, Jun 7, 2021 at 10:33 PM Andreas Rheinhardt <
andreas.rheinhardt@outlook.com> wrote:

> Dennis Fleurbaaij:
> > Thanks for the review Andreas!
> >
> > I've addressed all your concerns besides the "& in the define", I didn't
> > know that the binary AND is implicit in this situation, any link for
> this?
> > Even if it is, I just find it much more pleasing to see the & there
> > somehow, is there some leniency for personal preference?
> >
>
> The lower nibble doesn't survive the right shift; and given that
> GET_FF() returns an uint8_t, there are no bits higher than 0xFF anyway.
>
> I have no objection to keeping the "&".
>
> >
> > +    for (h = 0; h < avctx->height; h++) {
> > +        g_dst = (uint16_t *)g_line;
> > +        b_dst = (uint16_t *)b_line;
> > +        r_dst = (uint16_t *)r_line;
> > +
>
> C90 disallows declarations in the middle of blocks, but it allows them
> at the beginning of *any* block, not only the outermost block of a
> function. You can declare these variables here.
>
> And we also allow for loops with variable declarations, i.e. you may
> declare h (and w later) via "for (int h = 0;". (This is illegal in C90.)
>
>
> Your code currently does not check the size of the input packet: It
> needs to be at least height * width / PIXELS_PER_BLOCK *
> BYTES_PER_BLOCK, but it doesn't check that (and I guess that too big
> packets are also invalid?).
>
> - Andreas
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
From f66434cd65c5590c500e49eb6338aaa3a1c71a80 Mon Sep 17 00:00:00 2001
From: Dennis Fleurbaaij <mail@dennisfleurbaaij.com>
Date: Tue, 8 Jun 2021 18:55:02 +0200
Subject: [PATCH] libavcodec: r12b decoder added

R12B is a format used by BlackMagic DeckLink cards, it is
a big-endian 12bpp RGB format which packs 8 pixels into 36
bytes.

Signed-off-by: Dennis Fleurbaaij <mail@dennisfleurbaaij.com>
---
 Changelog               |   1 +
 libavcodec/Makefile     |   1 +
 libavcodec/allcodecs.c  |   1 +
 libavcodec/codec_desc.c |   7 ++
 libavcodec/codec_id.h   |   1 +
 libavcodec/r12bdec.c    | 141 ++++++++++++++++++++++++++++++++++++++++
 libavcodec/version.h    |   4 +-
 libavformat/riff.c      |   1 +
 8 files changed, 155 insertions(+), 2 deletions(-)
 create mode 100644 libavcodec/r12bdec.c

diff --git a/Changelog b/Changelog
index b9d5188cf6..d0717072eb 100644
--- a/Changelog
+++ b/Changelog
@@ -7,6 +7,7 @@ version <next>:
 - ADPCM IMA Acorn Replay decoder
 - Argonaut Games CVG demuxer
 - Argonaut Games CVG muxer
+- r12b decoder
 
 
 version 4.4:
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 4fa8d7ab10..cfef9d57ff 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -575,6 +575,7 @@ OBJS-$(CONFIG_QTRLE_ENCODER)           += qtrleenc.o
 OBJS-$(CONFIG_R10K_DECODER)            += r210dec.o
 OBJS-$(CONFIG_R10K_ENCODER)            += r210enc.o
 OBJS-$(CONFIG_R210_DECODER)            += r210dec.o
+OBJS-$(CONFIG_R12B_DECODER)            += r12bdec.o
 OBJS-$(CONFIG_R210_ENCODER)            += r210enc.o
 OBJS-$(CONFIG_RA_144_DECODER)          += ra144dec.o ra144.o celp_filters.o
 OBJS-$(CONFIG_RA_144_ENCODER)          += ra144enc.o ra144.o celp_filters.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 623db2a9fa..8db7e730a6 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -266,6 +266,7 @@ extern const AVCodec ff_qtrle_encoder;
 extern const AVCodec ff_qtrle_decoder;
 extern const AVCodec ff_r10k_encoder;
 extern const AVCodec ff_r10k_decoder;
+extern const AVCodec ff_r12b_decoder;
 extern const AVCodec ff_r210_encoder;
 extern const AVCodec ff_r210_decoder;
 extern const AVCodec ff_rasc_decoder;
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 35527dcc37..ef58d73576 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -1856,6 +1856,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("Digital Pictures SGA Video"),
         .props     = AV_CODEC_PROP_LOSSY,
     },
+    {
+        .id        = AV_CODEC_ID_R12B,
+        .type      = AVMEDIA_TYPE_VIDEO,
+        .name      = "r12b",
+        .long_name = NULL_IF_CONFIG_SMALL("Uncompressed RGB 12-bit 8px in 36B"),
+        .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS,
+    },
 
     /* various PCM "codecs" */
     {
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index 83e1dbb4b3..ecfdbc46c0 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -306,6 +306,7 @@ enum AVCodecID {
     AV_CODEC_ID_CRI,
     AV_CODEC_ID_SIMBIOSIS_IMX,
     AV_CODEC_ID_SGA_VIDEO,
+    AV_CODEC_ID_R12B,
 
     /* various PCM "codecs" */
     AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/r12bdec.c b/libavcodec/r12bdec.c
new file mode 100644
index 0000000000..986f96e687
--- /dev/null
+++ b/libavcodec/r12bdec.c
@@ -0,0 +1,141 @@
+/*
+ * r12b decoder
+ *
+ * Copyright (c) 2021 Dennis Fleurbaaij <mail@dennisfleurbaaij.com>
+ *
+ * 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
+ */
+
+#include "avcodec.h"
+#include "internal.h"
+
+#define WORDS_PER_BLOCK 9
+#define PIXELS_PER_BLOCK 8
+#define BYTES_PER_BLOCK 36
+
+static av_cold int decode_init(AVCodecContext *avctx)
+{
+    avctx->pix_fmt = AV_PIX_FMT_GBRP12LE;
+    avctx->bits_per_raw_sample = 12;
+
+    return 0;
+}
+
+static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame,
+                        AVPacket *avpkt)
+{
+    int ret;
+    uint8_t *g_line, *b_line, *r_line;
+
+    AVFrame *pic = data;
+    const uint8_t* src = avpkt->data;
+    const int blocks_per_line = avctx->width / PIXELS_PER_BLOCK;
+
+    if (avctx->width % PIXELS_PER_BLOCK != 0) {
+        av_log(avctx, AV_LOG_ERROR, "image width not modulo 8\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    if (avpkt->size < avctx->height * blocks_per_line * BYTES_PER_BLOCK) {
+        av_log(avctx, AV_LOG_ERROR, "packet too small\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    pic->pict_type = AV_PICTURE_TYPE_I;
+    pic->key_frame = 1;
+
+    if ((ret = ff_get_buffer(avctx, pic, 0)) < 0)
+        return ret;
+
+    g_line = pic->data[0];
+    b_line = pic->data[1];
+    r_line = pic->data[2];
+
+    for (int h = 0; h < avctx->height; h++) {
+        uint16_t *g_dst = (uint16_t *)g_line;
+        uint16_t *b_dst = (uint16_t *)b_line;
+        uint16_t *r_dst = (uint16_t *)r_line;
+
+        for (int w = 0; w < blocks_per_line; w++) {
+
+            // This is an encoding from the table on page 213 of the BlackMagic
+            // Decklink SDK pdf, version 12.0. Few helper defines to directly link
+            // the naming in the doc to the code.
+
+            #define GET_FF(word, byte) (*(src + ((word * 4) + byte)))
+            #define GET_0F(word, byte) (GET_FF(word, byte) & 0x0F)
+            #define GET_F0(word, byte) (GET_FF(word, byte) >> 4)
+            #define PUT(dst, pixel) (*(dst + pixel))
+
+            PUT(b_dst, 0) = GET_FF(0, 0) | GET_0F(1, 3) << 8;
+            PUT(g_dst, 0) = GET_F0(0, 2) | GET_FF(0, 1) << 4;
+            PUT(r_dst, 0) = GET_FF(0, 3) | GET_0F(0, 2) << 8;
+
+            PUT(b_dst, 1) = GET_F0(1, 0) | GET_FF(2, 3) << 4;
+            PUT(g_dst, 1) = GET_FF(1, 1) | GET_0F(1, 0) << 8;
+            PUT(r_dst, 1) = GET_F0(1, 3) | GET_FF(1, 2) << 4;
+
+            PUT(b_dst, 2) = GET_FF(3, 3) | GET_0F(3, 2) << 8;
+            PUT(g_dst, 2) = GET_F0(2, 1) | GET_FF(2, 0) << 4;
+            PUT(r_dst, 2) = GET_FF(2, 2) | GET_0F(2, 1) << 8;
+
+            PUT(b_dst, 3) = GET_F0(4, 3) | GET_FF(4, 2) << 4;
+            PUT(g_dst, 3) = GET_FF(3, 0) | GET_0F(4, 3) << 8;
+            PUT(r_dst, 3) = GET_F0(3, 2) | GET_FF(3, 1) << 4;
+
+            PUT(b_dst, 4) = GET_FF(5, 2) | GET_0F(5, 1) << 8;
+            PUT(g_dst, 4) = GET_F0(4, 0) | GET_FF(5, 3) << 4;
+            PUT(r_dst, 4) = GET_FF(4, 1) | GET_0F(4, 0) << 8;
+
+            PUT(b_dst, 5) = GET_F0(6, 2) | GET_FF(6, 1) << 4;
+            PUT(g_dst, 5) = GET_FF(6, 3) | GET_0F(6, 2) << 8;
+            PUT(r_dst, 5) = GET_F0(5, 1) | GET_FF(5, 0) << 4;
+
+            PUT(b_dst, 6) = GET_FF(7, 1) | GET_0F(7, 0) << 8;
+            PUT(g_dst, 6) = GET_F0(7, 3) | GET_FF(7, 2) << 4;
+            PUT(r_dst, 6) = GET_FF(6, 0) | GET_0F(7, 3) << 8;
+
+            PUT(b_dst, 7) = GET_F0(8, 1) | GET_FF(8, 0) << 4;
+            PUT(g_dst, 7) = GET_FF(8, 2) | GET_0F(8, 1) << 8;
+            PUT(r_dst, 7) = GET_F0(7, 0) | GET_FF(8, 3) << 4;
+
+            src += BYTES_PER_BLOCK;
+            b_dst += PIXELS_PER_BLOCK;
+            g_dst += PIXELS_PER_BLOCK;
+            r_dst += PIXELS_PER_BLOCK;
+        }
+
+        g_line += pic->linesize[0];
+        b_line += pic->linesize[1];
+        r_line += pic->linesize[2];
+    }
+
+    *got_frame = 1;
+
+    return avpkt->size;
+}
+
+const AVCodec ff_r12b_decoder = {
+    .name           = "r12b",
+    .long_name      = NULL_IF_CONFIG_SMALL("Uncompressed RGB 12-bit 8px in 36B"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_R12B,
+    .init           = decode_init,
+    .decode         = decode_frame,
+    .capabilities   = AV_CODEC_CAP_DR1,
+    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
+};
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 5b1e9e77f3..1288cecebe 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -28,8 +28,8 @@
 #include "libavutil/version.h"
 
 #define LIBAVCODEC_VERSION_MAJOR  59
-#define LIBAVCODEC_VERSION_MINOR   1
-#define LIBAVCODEC_VERSION_MICRO 101
+#define LIBAVCODEC_VERSION_MINOR   2
+#define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
                                                LIBAVCODEC_VERSION_MINOR, \
diff --git a/libavformat/riff.c b/libavformat/riff.c
index 270ff7c024..6e303f59ff 100644
--- a/libavformat/riff.c
+++ b/libavformat/riff.c
@@ -295,6 +295,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
     { AV_CODEC_ID_FRWU,         MKTAG('F', 'R', 'W', 'U') },
     { AV_CODEC_ID_R10K,         MKTAG('R', '1', '0', 'k') },
     { AV_CODEC_ID_R210,         MKTAG('r', '2', '1', '0') },
+    { AV_CODEC_ID_R12B,         MKTAG('r', '1', '2', 'b') },
     { AV_CODEC_ID_V210,         MKTAG('v', '2', '1', '0') },
     { AV_CODEC_ID_V210,         MKTAG('C', '2', '1', '0') },
     { AV_CODEC_ID_V308,         MKTAG('v', '3', '0', '8') },
Carl Eugen Hoyos June 8, 2021, 11:13 p.m. UTC | #8
Am Di., 8. Juni 2021 um 11:52 Uhr schrieb Dennis Fleurbaaij
<mail@dennisfleurbaaij.com>:

> The four letter "r12b" format code is from the DeckLink documentation;
> there are other described formats there as well such as r12l, r10b, r10l
> and r210. I'm using DirectShow which signals the encoding to downstream
> components through the use of FOURCC codes and hence for this filter to be
> of any use in such applications there needs to be a way to identify it.

This does not sound like a good reason to add a new fourcc.

> r210 from their docs has already gained some traction as a fourcc and hence
> adding the others in a similar vein seemed a reasonable approach. "r12b"
> also sounds reasonable to me as a fourcc: it starts with an r, indicative
> of RGB formats, and the bit/pixel + endianness didn't seem unreasonable.
> Also I could find no other fourcc's used for this and hence I added it.

The change to riff.c should at least be an independent patch.

Carl Eugen
diff mbox series

Patch

diff --git a/Changelog b/Changelog
index b9d5188cf6..d0717072eb 100644
--- a/Changelog
+++ b/Changelog
@@ -7,6 +7,7 @@  version <next>:
 - ADPCM IMA Acorn Replay decoder
 - Argonaut Games CVG demuxer
 - Argonaut Games CVG muxer
+- r12b decoder
 
 
 version 4.4:
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 4fa8d7ab10..cfef9d57ff 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -575,6 +575,7 @@  OBJS-$(CONFIG_QTRLE_ENCODER)           += qtrleenc.o
 OBJS-$(CONFIG_R10K_DECODER)            += r210dec.o
 OBJS-$(CONFIG_R10K_ENCODER)            += r210enc.o
 OBJS-$(CONFIG_R210_DECODER)            += r210dec.o
+OBJS-$(CONFIG_R12B_DECODER)            += r12bdec.o
 OBJS-$(CONFIG_R210_ENCODER)            += r210enc.o
 OBJS-$(CONFIG_RA_144_DECODER)          += ra144dec.o ra144.o celp_filters.o
 OBJS-$(CONFIG_RA_144_ENCODER)          += ra144enc.o ra144.o celp_filters.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 623db2a9fa..8db7e730a6 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -266,6 +266,7 @@  extern const AVCodec ff_qtrle_encoder;
 extern const AVCodec ff_qtrle_decoder;
 extern const AVCodec ff_r10k_encoder;
 extern const AVCodec ff_r10k_decoder;
+extern const AVCodec ff_r12b_decoder;
 extern const AVCodec ff_r210_encoder;
 extern const AVCodec ff_r210_decoder;
 extern const AVCodec ff_rasc_decoder;
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 35527dcc37..ef58d73576 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -1856,6 +1856,13 @@  static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("Digital Pictures SGA Video"),
         .props     = AV_CODEC_PROP_LOSSY,
     },
+    {
+        .id        = AV_CODEC_ID_R12B,
+        .type      = AVMEDIA_TYPE_VIDEO,
+        .name      = "r12b",
+        .long_name = NULL_IF_CONFIG_SMALL("Uncompressed RGB 12-bit 8px in 36B"),
+        .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS,
+    },
 
     /* various PCM "codecs" */
     {
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index 83e1dbb4b3..ecfdbc46c0 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -306,6 +306,7 @@  enum AVCodecID {
     AV_CODEC_ID_CRI,
     AV_CODEC_ID_SIMBIOSIS_IMX,
     AV_CODEC_ID_SGA_VIDEO,
+    AV_CODEC_ID_R12B,
 
     /* various PCM "codecs" */
     AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/r12bdec.c b/libavcodec/r12bdec.c
new file mode 100644
index 0000000000..7d415a8fd6
--- /dev/null
+++ b/libavcodec/r12bdec.c
@@ -0,0 +1,139 @@ 
+/*
+ * r12b decoder
+ *
+ * Copyright (c) 2021 Dennis Fleurbaaij <mail@dennisfleurbaaij.com>
+ *
+ * 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
+ */
+
+#include "avcodec.h"
+#include "internal.h"
+#include "libavutil/bswap.h"
+#include "libavutil/common.h"
+
+#define WORDS_PER_BLOCK 9
+#define PIXELS_PER_BLOCK 8
+#define BYTES_PER_BLOCK 36
+
+static av_cold int decode_init(AVCodecContext *avctx)
+{
+    avctx->pix_fmt = AV_PIX_FMT_GBRP12LE;
+    avctx->bits_per_raw_sample = 12;
+
+    return 0;
+}
+
+static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame,
+                        AVPacket *avpkt)
+{
+    if (avctx->width % PIXELS_PER_BLOCK != 0) {
+        av_log(avctx, AV_LOG_ERROR, "image width not modulo 8\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    AVFrame *pic = data;
+    pic->pict_type = AV_PICTURE_TYPE_I;
+    pic->key_frame = 1;
+
+    const uint8_t* src = (const uint8_t *)avpkt->data;
+
+    const int blocks_per_line = avctx->width / PIXELS_PER_BLOCK;
+
+    int ret;
+    if ((ret = ff_get_buffer(avctx, pic, 0)) < 0)
+        return ret;
+
+    uint8_t* g_line = pic->data[0];
+    uint8_t* b_line = pic->data[1];
+    uint8_t* r_line = pic->data[2];
+
+    for (int  h = 0; h < avctx->height; h++) {
+        uint16_t *g_dst = (uint16_t *)g_line;
+        uint16_t *b_dst = (uint16_t *)b_line;
+        uint16_t *r_dst = (uint16_t *)r_line;
+
+        for (int w = 0; w < blocks_per_line; w++) {
+
+            // This is an encoding from the table on page 213 of the BlackMagic
+            // Decklink SDK pdf, version 12.0. Few helper defines to directly link
+            // the naming in the doc to the code.
+
+            #define GET_FF(word, byte) (*(src + ((word * 4) + byte)))
+            #define GET_0F(word, byte) (GET_FF(word, byte) & 0x0F)
+            #define GET_F0(word, byte) ((GET_FF(word, byte) & 0xF0) >> 4)
+            #define PUT(dst, pixel) (*(dst + pixel))
+
+            PUT(b_dst, 0) = GET_FF(0, 0) | GET_0F(1, 3) << 8;
+            PUT(g_dst, 0) = GET_F0(0, 2) | GET_FF(0, 1) << 4;
+            PUT(r_dst, 0) = GET_FF(0, 3) | GET_0F(0, 2) << 8;
+
+            PUT(b_dst, 1) = GET_F0(1, 0) | GET_FF(2, 3) << 4;
+            PUT(g_dst, 1) = GET_FF(1, 1) | GET_0F(1, 0) << 8;
+            PUT(r_dst, 1) = GET_F0(1, 3) | GET_FF(1, 2) << 4;
+
+            PUT(b_dst, 2) = GET_FF(3, 3) | GET_0F(3, 2) << 8;
+            PUT(g_dst, 2) = GET_F0(2, 1) | GET_FF(2, 0) << 4;
+            PUT(r_dst, 2) = GET_FF(2, 2) | GET_0F(2, 1) << 8;
+
+            PUT(b_dst, 3) = GET_F0(4, 3) | GET_FF(4, 2) << 4;
+            PUT(g_dst, 3) = GET_FF(3, 0) | GET_0F(4, 3) << 8;
+            PUT(r_dst, 3) = GET_F0(3, 2) | GET_FF(3, 1) << 4;
+
+            PUT(b_dst, 4) = GET_FF(5, 2) | GET_0F(5, 1) << 8;
+            PUT(g_dst, 4) = GET_F0(4, 0) | GET_FF(5, 3) << 4;
+            PUT(r_dst, 4) = GET_FF(4, 1) | GET_0F(4, 0) << 8;
+
+            PUT(b_dst, 5) = GET_F0(6, 2) | GET_FF(6, 1) << 4;
+            PUT(g_dst, 5) = GET_FF(6, 3) | GET_0F(6, 2) << 8;
+            PUT(r_dst, 5) = GET_F0(5, 1) | GET_FF(5, 0) << 4;
+
+            PUT(b_dst, 6) = GET_FF(7, 1) | GET_0F(7, 0) << 8;
+            PUT(g_dst, 6) = GET_F0(7, 3) | GET_FF(7, 2) << 4;
+            PUT(r_dst, 6) = GET_FF(6, 0) | GET_0F(7, 3) << 8;
+
+            PUT(b_dst, 7) = GET_F0(8, 1) | GET_FF(8, 0) << 4;
+            PUT(g_dst, 7) = GET_FF(8, 2) | GET_0F(8, 1) << 8;
+            PUT(r_dst, 7) = GET_F0(7, 0) | GET_FF(8, 3) << 4;
+
+            src += BYTES_PER_BLOCK;
+            b_dst += PIXELS_PER_BLOCK;
+            g_dst += PIXELS_PER_BLOCK;
+            r_dst += PIXELS_PER_BLOCK;
+        }
+
+        g_line += pic->linesize[0];
+        b_line += pic->linesize[1];
+        r_line += pic->linesize[2];
+    }
+
+    *got_frame = 1;
+
+    return avpkt->size;
+}
+
+#if CONFIG_R12B_DECODER
+const AVCodec ff_r12b_decoder = {
+    .name           = "r12b",
+    .long_name      = NULL_IF_CONFIG_SMALL("Uncompressed RGB 12-bit 8px in 36B"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_R12B,
+    .init           = decode_init,
+    .decode         = decode_frame,
+    .capabilities   = AV_CODEC_CAP_DR1,
+    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
+};
+#endif
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 5b1e9e77f3..1288cecebe 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -28,8 +28,8 @@ 
 #include "libavutil/version.h"
 
 #define LIBAVCODEC_VERSION_MAJOR  59
-#define LIBAVCODEC_VERSION_MINOR   1
-#define LIBAVCODEC_VERSION_MICRO 101
+#define LIBAVCODEC_VERSION_MINOR   2
+#define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
                                                LIBAVCODEC_VERSION_MINOR, \
diff --git a/libavformat/riff.c b/libavformat/riff.c
index 270ff7c024..6e303f59ff 100644
--- a/libavformat/riff.c
+++ b/libavformat/riff.c
@@ -295,6 +295,7 @@  const AVCodecTag ff_codec_bmp_tags[] = {
     { AV_CODEC_ID_FRWU,         MKTAG('F', 'R', 'W', 'U') },
     { AV_CODEC_ID_R10K,         MKTAG('R', '1', '0', 'k') },
     { AV_CODEC_ID_R210,         MKTAG('r', '2', '1', '0') },
+    { AV_CODEC_ID_R12B,         MKTAG('r', '1', '2', 'b') },
     { AV_CODEC_ID_V210,         MKTAG('v', '2', '1', '0') },
     { AV_CODEC_ID_V210,         MKTAG('C', '2', '1', '0') },
     { AV_CODEC_ID_V308,         MKTAG('v', '3', '0', '8') },