diff mbox

[FFmpeg-devel] avcodec: add Brooktree ProSumer Video decoder

Message ID 20180822160054.18976-1-onemda@gmail.com
State New
Headers show

Commit Message

Paul B Mahol Aug. 22, 2018, 4 p.m. UTC
Signed-off-by: Paul B Mahol <onemda@gmail.com>
---
 libavcodec/Makefile     |   1 +
 libavcodec/allcodecs.c  |   1 +
 libavcodec/avcodec.h    |   1 +
 libavcodec/codec_desc.c |   7 +
 libavcodec/prosumer.c   | 405 ++++++++++++++++++++++++++++++++++++++++
 libavformat/riff.c      |   1 +
 6 files changed, 416 insertions(+)
 create mode 100644 libavcodec/prosumer.c

Comments

Carl Eugen Hoyos Aug. 22, 2018, 7:11 p.m. UTC | #1
2018-08-22 18:00 GMT+02:00, Paul B Mahol <onemda@gmail.com>:

> +    switch (avctx->bits_per_coded_sample) {
> +    case 12:
> +         s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;
> +         break;
> +    default:
> +         return AVERROR_INVALIDDATA;
> +    }

Why are the condition and the error needed?

Carl Eugen
Paul B Mahol Aug. 22, 2018, 7:24 p.m. UTC | #2
On 8/22/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
> 2018-08-22 18:00 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>
>> +    switch (avctx->bits_per_coded_sample) {
>> +    case 12:
>> +         s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;
>> +         break;
>> +    default:
>> +         return AVERROR_INVALIDDATA;
>> +    }
>
> Why are the condition and the error needed?

Because only that is supported.
Carl Eugen Hoyos Aug. 23, 2018, 9:02 a.m. UTC | #3
2018-08-22 21:24 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
> On 8/22/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>> 2018-08-22 18:00 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>
>>> +    switch (avctx->bits_per_coded_sample) {
>>> +    case 12:
>>> +         s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;
>>> +         break;
>>> +    default:
>>> +         return AVERROR_INVALIDDATA;
>>> +    }
>>
>> Why are the condition and the error needed?
>
> Because only that is supported.

Do valid samples with other values exist?

Carl Eugen
Paul B Mahol Aug. 23, 2018, 9:11 a.m. UTC | #4
On 8/23/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
> 2018-08-22 21:24 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>> On 8/22/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>>> 2018-08-22 18:00 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>>
>>>> +    switch (avctx->bits_per_coded_sample) {
>>>> +    case 12:
>>>> +         s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;
>>>> +         break;
>>>> +    default:
>>>> +         return AVERROR_INVALIDDATA;
>>>> +    }
>>>
>>> Why are the condition and the error needed?
>>
>> Because only that is supported.
>
> Do valid samples with other values exist?

No.
Michael Niedermayer Aug. 23, 2018, 11:59 a.m. UTC | #5
On Wed, Aug 22, 2018 at 06:00:54PM +0200, Paul B Mahol wrote:
> Signed-off-by: Paul B Mahol <onemda@gmail.com>
> ---
>  libavcodec/Makefile     |   1 +
>  libavcodec/allcodecs.c  |   1 +
>  libavcodec/avcodec.h    |   1 +
>  libavcodec/codec_desc.c |   7 +
>  libavcodec/prosumer.c   | 405 ++++++++++++++++++++++++++++++++++++++++
>  libavformat/riff.c      |   1 +
>  6 files changed, 416 insertions(+)
>  create mode 100644 libavcodec/prosumer.c
> 
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index f0c8226283..9a309c348e 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -515,6 +515,7 @@ OBJS-$(CONFIG_PRORES_DECODER)          += proresdec2.o proresdsp.o proresdata.o
>  OBJS-$(CONFIG_PRORES_ENCODER)          += proresenc_anatoliy.o
>  OBJS-$(CONFIG_PRORES_AW_ENCODER)       += proresenc_anatoliy.o
>  OBJS-$(CONFIG_PRORES_KS_ENCODER)       += proresenc_kostya.o proresdata.o
> +OBJS-$(CONFIG_PROSUMER_DECODER)        += prosumer.o
>  OBJS-$(CONFIG_PSD_DECODER)             += psd.o
>  OBJS-$(CONFIG_PTX_DECODER)             += ptx.o
>  OBJS-$(CONFIG_QCELP_DECODER)           += qcelpdec.o                     \
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index fd35fc1d0b..b1d1ef26c0 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -235,6 +235,7 @@ extern AVCodec ff_prores_encoder;
>  extern AVCodec ff_prores_decoder;
>  extern AVCodec ff_prores_aw_encoder;
>  extern AVCodec ff_prores_ks_encoder;
> +extern AVCodec ff_prosumer_decoder;
>  extern AVCodec ff_psd_decoder;
>  extern AVCodec ff_ptx_decoder;
>  extern AVCodec ff_qdraw_decoder;
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index 31e50d5a94..2a4be2ca4f 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -448,6 +448,7 @@ enum AVCodecID {
>      AV_CODEC_ID_GDV,
>      AV_CODEC_ID_FITS,
>      AV_CODEC_ID_IMM4,
> +    AV_CODEC_ID_PROSUMER,
>  
>      /* various PCM "codecs" */
>      AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> index af66b35d2b..e611183599 100644
> --- a/libavcodec/codec_desc.c
> +++ b/libavcodec/codec_desc.c
> @@ -1661,6 +1661,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
>          .long_name = NULL_IF_CONFIG_SMALL("Infinity IMM4"),
>          .props     = AV_CODEC_PROP_LOSSY,
>      },
> +    {
> +        .id        = AV_CODEC_ID_PROSUMER,
> +        .type      = AVMEDIA_TYPE_VIDEO,
> +        .name      = "prosumer",
> +        .long_name = NULL_IF_CONFIG_SMALL("Brooktree ProSumer Video"),
> +        .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
> +    },
>  
>      /* various PCM "codecs" */
>      {
> diff --git a/libavcodec/prosumer.c b/libavcodec/prosumer.c
> new file mode 100644
> index 0000000000..7b9d5e7bdb
> --- /dev/null
> +++ b/libavcodec/prosumer.c
> @@ -0,0 +1,405 @@
> +/*
> + * Brooktree ProSumer Video decoder
> + * Copyright (c) 2018 Paul B Mahol
> + *
> + * 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 <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include "libavutil/imgutils.h"
> +#include "libavutil/internal.h"
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/mem.h"
> +
> +#include "avcodec.h"
> +#include "bytestream.h"
> +#include "internal.h"
> +
> +typedef struct ProSumerContext {
> +    GetByteContext gb;
> +    PutByteContext pb;
> +
> +    unsigned stride;
> +    unsigned size;
> +    unsigned lut[0x10000];
> +    uint8_t *table_b;
> +    uint8_t *decbuffer;
> +} ProSumerContext;
> +
> +#define PAIR(high, low) (((uint64_t)(high)<<sizeof(high)*8) | low)
> +
> +static int decompress(GetByteContext *gb, int size, PutByteContext *pb, const unsigned *lut)
> +{
> +    int i, pos, idx, cnt, fill;
> +    unsigned a, b, c;
> +
> +    bytestream2_skip(gb, 32);
> +    cnt = 4;
> +    a = bytestream2_get_le32(gb);
> +    idx = a >> 20;
> +    b = lut[2 * idx];
> +
> +    while (1) {
> +        if (((b & 0xFF00u) != 0x8000u) || (b & 0xFFu)) {
> +            if ((b & 0xFF00u) != 0x8000u) {
> +                bytestream2_put_le16(pb, b);
> +            } else if (b & 0xFFu) {
> +                idx = 0;
> +                for (i = 0; i < (b & 0xFFu); i++)
> +                    bytestream2_put_le32(pb, 0);
> +            }
> +            c = b >> 16;
> +            if (c & 0xFF00u) {
> +                c = (((c >> 8) & 0xFFu) | (c & 0xFF00)) & 0xF00F;
> +                fill = lut[2 * idx + 1];
> +                if ((c & 0xFF00u) == 0x1000) {
> +                    bytestream2_put_le16(pb, fill);
> +                    c &= 0xFFFF00FFu;
> +                } else {
> +                    bytestream2_put_le32(pb, fill);
> +                    c &= 0xFFFF00FFu;
> +                }
> +            }
> +            while (c) {
> +                a <<= 4;
> +                cnt--;
> +                if (!cnt) {
> +                    if (bytestream2_get_bytes_left(gb) <= 0) {
> +                        if (!a)
> +                            return 0;
> +                        cnt = 4;
> +                    } else {
> +                        pos = bytestream2_tell(gb) ^ 2;
> +                        bytestream2_seek(gb, pos, SEEK_SET);
> +                        AV_WN16(&a, bytestream2_peek_le16(gb));
> +                        pos = pos ^ 2;
> +                        bytestream2_seek(gb, pos, SEEK_SET);
> +                        bytestream2_skip(gb, 2);
> +                        cnt = 4;
> +                    }
> +                }
> +                c--;
> +            }
> +            idx = a >> 20;
> +            b = lut[2 * idx];
> +            continue;
> +        }
> +        idx = 2;
> +        while (idx) {
> +            a <<= 4;
> +            cnt--;
> +            if (cnt) {
> +                idx--;
> +                continue;
> +            }
> +            if (bytestream2_get_bytes_left(gb) <= 0) {
> +                if (a) {
> +                    cnt = 4;
> +                    idx--;
> +                    continue;
> +                }
> +                return 0;
> +            }
> +            pos = bytestream2_tell(gb) ^ 2;
> +            bytestream2_seek(gb, pos, SEEK_SET);
> +            AV_WN16(&a, bytestream2_peek_le16(gb));
> +            pos = pos ^ 2;
> +            bytestream2_seek(gb, pos, SEEK_SET);
> +            bytestream2_skip(gb, 2);
> +            cnt = 4;
> +            idx--;
> +        }

> +        b = PAIR(4, a) >> 16;

I think this assumes sizeof(int) == 4, this is not guranteed it could be 8


[...]
Paul B Mahol Aug. 23, 2018, 12:19 p.m. UTC | #6
On 8/23/18, Michael Niedermayer <michael@niedermayer.cc> wrote:
> On Wed, Aug 22, 2018 at 06:00:54PM +0200, Paul B Mahol wrote:
>> Signed-off-by: Paul B Mahol <onemda@gmail.com>
>> ---
>>  libavcodec/Makefile     |   1 +
>>  libavcodec/allcodecs.c  |   1 +
>>  libavcodec/avcodec.h    |   1 +
>>  libavcodec/codec_desc.c |   7 +
>>  libavcodec/prosumer.c   | 405 ++++++++++++++++++++++++++++++++++++++++
>>  libavformat/riff.c      |   1 +
>>  6 files changed, 416 insertions(+)
>>  create mode 100644 libavcodec/prosumer.c
>>

[...]

>> +            }
>> +            pos = bytestream2_tell(gb) ^ 2;
>> +            bytestream2_seek(gb, pos, SEEK_SET);
>> +            AV_WN16(&a, bytestream2_peek_le16(gb));
>> +            pos = pos ^ 2;
>> +            bytestream2_seek(gb, pos, SEEK_SET);
>> +            bytestream2_skip(gb, 2);
>> +            cnt = 4;
>> +            idx--;
>> +        }
>
>> +        b = PAIR(4, a) >> 16;
>
> I think this assumes sizeof(int) == 4, this is not guranteed it could be 8

Fixed locally.
Carl Eugen Hoyos Aug. 23, 2018, 12:24 p.m. UTC | #7
2018-08-23 11:11 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
> On 8/23/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>> 2018-08-22 21:24 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>> On 8/22/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>>>> 2018-08-22 18:00 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>>>
>>>>> +    switch (avctx->bits_per_coded_sample) {
>>>>> +    case 12:
>>>>> +         s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;
>>>>> +         break;
>>>>> +    default:
>>>>> +         return AVERROR_INVALIDDATA;
>>>>> +    }
>>>>
>>>> Why are the condition and the error needed?
>>>
>>> Because only that is supported.
>>
>> Do valid samples with other values exist?
>
> No.

Then I suggest to make the whole block above just:
s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;

Carl Eugen
Paul B Mahol Aug. 23, 2018, 12:29 p.m. UTC | #8
On 8/23/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
> 2018-08-23 11:11 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>> On 8/23/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>>> 2018-08-22 21:24 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>>> On 8/22/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>>>>> 2018-08-22 18:00 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>>>>
>>>>>> +    switch (avctx->bits_per_coded_sample) {
>>>>>> +    case 12:
>>>>>> +         s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;
>>>>>> +         break;
>>>>>> +    default:
>>>>>> +         return AVERROR_INVALIDDATA;
>>>>>> +    }
>>>>>
>>>>> Why are the condition and the error needed?
>>>>
>>>> Because only that is supported.
>>>
>>> Do valid samples with other values exist?
>>
>> No.
>
> Then I suggest to make the whole block above just:
> s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;

Nope, bits per coded sample must be checked.
Carl Eugen Hoyos Aug. 23, 2018, 12:47 p.m. UTC | #9
2018-08-23 14:29 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
> On 8/23/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>> 2018-08-23 11:11 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>> On 8/23/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>>>> 2018-08-22 21:24 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>>>> On 8/22/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>>>>>> 2018-08-22 18:00 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>>>>>
>>>>>>> +    switch (avctx->bits_per_coded_sample) {
>>>>>>> +    case 12:
>>>>>>> +         s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;
>>>>>>> +         break;
>>>>>>> +    default:
>>>>>>> +         return AVERROR_INVALIDDATA;
>>>>>>> +    }
>>>>>>
>>>>>> Why are the condition and the error needed?
>>>>>
>>>>> Because only that is supported.
>>>>
>>>> Do valid samples with other values exist?
>>>
>>> No.
>>
>> Then I suggest to make the whole block above just:
>> s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;
>
> Nope, bits per coded sample must be checked.

Could you explain why?

Am I correct that no other decoder does that?

Carl Eugen
Paul B Mahol Aug. 23, 2018, 12:51 p.m. UTC | #10
On 8/23/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
> 2018-08-23 14:29 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>> On 8/23/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>>> 2018-08-23 11:11 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>>> On 8/23/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>>>>> 2018-08-22 21:24 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>>>>> On 8/22/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>>>>>>> 2018-08-22 18:00 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>>>>>>
>>>>>>>> +    switch (avctx->bits_per_coded_sample) {
>>>>>>>> +    case 12:
>>>>>>>> +         s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;
>>>>>>>> +         break;
>>>>>>>> +    default:
>>>>>>>> +         return AVERROR_INVALIDDATA;
>>>>>>>> +    }
>>>>>>>
>>>>>>> Why are the condition and the error needed?
>>>>>>
>>>>>> Because only that is supported.
>>>>>
>>>>> Do valid samples with other values exist?
>>>>
>>>> No.
>>>
>>> Then I suggest to make the whole block above just:
>>> s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;
>>
>> Nope, bits per coded sample must be checked.
>
> Could you explain why?

Reference decoder checks it, so do we.

>
> Am I correct that no other decoder does that?

Nope.
Carl Eugen Hoyos Aug. 23, 2018, 12:55 p.m. UTC | #11
2018-08-23 14:51 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
> On 8/23/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>> 2018-08-23 14:29 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>> On 8/23/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>>>> 2018-08-23 11:11 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>>>> On 8/23/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>>>>>> 2018-08-22 21:24 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>>>>>> On 8/22/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>>>>>>>> 2018-08-22 18:00 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>>>>>>>
>>>>>>>>> +    switch (avctx->bits_per_coded_sample) {
>>>>>>>>> +    case 12:
>>>>>>>>> +         s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;
>>>>>>>>> +         break;
>>>>>>>>> +    default:
>>>>>>>>> +         return AVERROR_INVALIDDATA;
>>>>>>>>> +    }
>>>>>>>>
>>>>>>>> Why are the condition and the error needed?
>>>>>>>
>>>>>>> Because only that is supported.
>>>>>>
>>>>>> Do valid samples with other values exist?
>>>>>
>>>>> No.
>>>>
>>>> Then I suggest to make the whole block above just:
>>>> s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;
>>>
>>> Nope, bits per coded sample must be checked.
>>
>> Could you explain why?
>
> Reference decoder checks it, so do we.

Shouldn't we try to decode files if at all possible?

>> Am I correct that no other decoder does that?
>
> Nope.

Which decoder does it?

Carl Eugen
Paul B Mahol Aug. 23, 2018, 1:02 p.m. UTC | #12
On 8/23/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
> 2018-08-23 14:51 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>> On 8/23/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>>> 2018-08-23 14:29 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>>> On 8/23/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>>>>> 2018-08-23 11:11 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>>>>> On 8/23/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>>>>>>> 2018-08-22 21:24 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>>>>>>> On 8/22/18, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>>>>>>>>> 2018-08-22 18:00 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>>>>>>>>>
>>>>>>>>>> +    switch (avctx->bits_per_coded_sample) {
>>>>>>>>>> +    case 12:
>>>>>>>>>> +         s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;
>>>>>>>>>> +         break;
>>>>>>>>>> +    default:
>>>>>>>>>> +         return AVERROR_INVALIDDATA;
>>>>>>>>>> +    }
>>>>>>>>>
>>>>>>>>> Why are the condition and the error needed?
>>>>>>>>
>>>>>>>> Because only that is supported.
>>>>>>>
>>>>>>> Do valid samples with other values exist?
>>>>>>
>>>>>> No.
>>>>>
>>>>> Then I suggest to make the whole block above just:
>>>>> s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;
>>>>
>>>> Nope, bits per coded sample must be checked.
>>>
>>> Could you explain why?
>>
>> Reference decoder checks it, so do we.
>
> Shouldn't we try to decode files if at all possible?

Nope.

>
>>> Am I correct that no other decoder does that?
>>
>> Nope.
>
> Which decoder does it?
>

Utvideo, magicyuv, y41p...
Paul B Mahol Aug. 24, 2018, 12:11 p.m. UTC | #13
Will apply.
Rostislav Pehlivanov Aug. 24, 2018, 4:36 p.m. UTC | #14
On Wed, 22 Aug 2018 at 17:01, Paul B Mahol <onemda@gmail.com> wrote:

> Signed-off-by: Paul B Mahol <onemda@gmail.com>
> ---
>  libavcodec/Makefile     |   1 +
>  libavcodec/allcodecs.c  |   1 +
>  libavcodec/avcodec.h    |   1 +
>  libavcodec/codec_desc.c |   7 +
>  libavcodec/prosumer.c   | 405 ++++++++++++++++++++++++++++++++++++++++
>  libavformat/riff.c      |   1 +
>  6 files changed, 416 insertions(+)
>  create mode 100644 libavcodec/prosumer.c
>
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index f0c8226283..9a309c348e 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -515,6 +515,7 @@ OBJS-$(CONFIG_PRORES_DECODER)          += proresdec2.o
> proresdsp.o proresdata.o
>  OBJS-$(CONFIG_PRORES_ENCODER)          += proresenc_anatoliy.o
>  OBJS-$(CONFIG_PRORES_AW_ENCODER)       += proresenc_anatoliy.o
>  OBJS-$(CONFIG_PRORES_KS_ENCODER)       += proresenc_kostya.o proresdata.o
> +OBJS-$(CONFIG_PROSUMER_DECODER)        += prosumer.o
>  OBJS-$(CONFIG_PSD_DECODER)             += psd.o
>  OBJS-$(CONFIG_PTX_DECODER)             += ptx.o
>  OBJS-$(CONFIG_QCELP_DECODER)           += qcelpdec.o                     \
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index fd35fc1d0b..b1d1ef26c0 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -235,6 +235,7 @@ extern AVCodec ff_prores_encoder;
>  extern AVCodec ff_prores_decoder;
>  extern AVCodec ff_prores_aw_encoder;
>  extern AVCodec ff_prores_ks_encoder;
> +extern AVCodec ff_prosumer_decoder;
>  extern AVCodec ff_psd_decoder;
>  extern AVCodec ff_ptx_decoder;
>  extern AVCodec ff_qdraw_decoder;
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index 31e50d5a94..2a4be2ca4f 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -448,6 +448,7 @@ enum AVCodecID {
>      AV_CODEC_ID_GDV,
>      AV_CODEC_ID_FITS,
>      AV_CODEC_ID_IMM4,
> +    AV_CODEC_ID_PROSUMER,
>
>      /* various PCM "codecs" */
>      AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at
> the start of audio codecs
> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> index af66b35d2b..e611183599 100644
> --- a/libavcodec/codec_desc.c
> +++ b/libavcodec/codec_desc.c
> @@ -1661,6 +1661,13 @@ static const AVCodecDescriptor codec_descriptors[]
> = {
>          .long_name = NULL_IF_CONFIG_SMALL("Infinity IMM4"),
>          .props     = AV_CODEC_PROP_LOSSY,
>      },
> +    {
> +        .id        = AV_CODEC_ID_PROSUMER,
> +        .type      = AVMEDIA_TYPE_VIDEO,
> +        .name      = "prosumer",
> +        .long_name = NULL_IF_CONFIG_SMALL("Brooktree ProSumer Video"),
> +        .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
> +    },
>
>      /* various PCM "codecs" */
>      {
> diff --git a/libavcodec/prosumer.c b/libavcodec/prosumer.c
> new file mode 100644
> index 0000000000..7b9d5e7bdb
> --- /dev/null
> +++ b/libavcodec/prosumer.c
> @@ -0,0 +1,405 @@
> +/*
> + * Brooktree ProSumer Video decoder
> + * Copyright (c) 2018 Paul B Mahol
> + *
> + * 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 <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include "libavutil/imgutils.h"
> +#include "libavutil/internal.h"
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/mem.h"
> +
> +#include "avcodec.h"
> +#include "bytestream.h"
> +#include "internal.h"
> +
> +typedef struct ProSumerContext {
> +    GetByteContext gb;
> +    PutByteContext pb;
> +
> +    unsigned stride;
> +    unsigned size;
> +    unsigned lut[0x10000];
>

uint32_t, see below.



> +    uint8_t *table_b;
> +    uint8_t *decbuffer;
> +} ProSumerContext;
> +
> +#define PAIR(high, low) (((uint64_t)(high)<<sizeof(high)*8) | low)
> +
> +static int decompress(GetByteContext *gb, int size, PutByteContext *pb,
> const unsigned *lut)
> +{
> +    int i, pos, idx, cnt, fill;
> +    unsigned a, b, c;
>

uint32_t, see below.


+
> +    bytestream2_skip(gb, 32);
> +    cnt = 4;
> +    a = bytestream2_get_le32(gb);
> +    idx = a >> 20;
> +    b = lut[2 * idx];
> +
> +    while (1) {
>

This loop looks a bit lock-prone but since you check the bytestream reader
status everywhere should be fine.



> +        if (((b & 0xFF00u) != 0x8000u) || (b & 0xFFu)) {
> +            if ((b & 0xFF00u) != 0x8000u) {
> +                bytestream2_put_le16(pb, b);
> +            } else if (b & 0xFFu) {
> +                idx = 0;
> +                for (i = 0; i < (b & 0xFFu); i++)
> +                    bytestream2_put_le32(pb, 0);
> +            }
> +            c = b >> 16;
> +            if (c & 0xFF00u) {
> +                c = (((c >> 8) & 0xFFu) | (c & 0xFF00)) & 0xF00F;
> +                fill = lut[2 * idx + 1];
> +                if ((c & 0xFF00u) == 0x1000) {
> +                    bytestream2_put_le16(pb, fill);
> +                    c &= 0xFFFF00FFu;
> +                } else {
> +                    bytestream2_put_le32(pb, fill);
> +                    c &= 0xFFFF00FFu;
> +                }
> +            }
> +            while (c) {
> +                a <<= 4;
> +                cnt--;
> +                if (!cnt) {
> +                    if (bytestream2_get_bytes_left(gb) <= 0) {
> +                        if (!a)
> +                            return 0;
> +                        cnt = 4;
> +                    } else {
> +                        pos = bytestream2_tell(gb) ^ 2;
> +                        bytestream2_seek(gb, pos, SEEK_SET);
> +                        AV_WN16(&a, bytestream2_peek_le16(gb));
> +                        pos = pos ^ 2;
> +                        bytestream2_seek(gb, pos, SEEK_SET);
> +                        bytestream2_skip(gb, 2);
> +                        cnt = 4;
> +                    }
> +                }
> +                c--;
> +            }
> +            idx = a >> 20;
> +            b = lut[2 * idx];
> +            continue;
> +        }
> +        idx = 2;
> +        while (idx) {
> +            a <<= 4;
> +            cnt--;
> +            if (cnt) {
> +                idx--;
> +                continue;
> +            }
> +            if (bytestream2_get_bytes_left(gb) <= 0) {
> +                if (a) {
> +                    cnt = 4;
> +                    idx--;
> +                    continue;
> +                }
> +                return 0;
> +            }
> +            pos = bytestream2_tell(gb) ^ 2;
> +            bytestream2_seek(gb, pos, SEEK_SET);
> +            AV_WN16(&a, bytestream2_peek_le16(gb));
> +            pos = pos ^ 2;
> +            bytestream2_seek(gb, pos, SEEK_SET);
> +            bytestream2_skip(gb, 2);
> +            cnt = 4;
> +            idx--;
> +        }
> +        b = PAIR(4, a) >> 16;
> +    }
> +
> +    return 0;
> +}
> +
> +static void do_shift(unsigned *dst, int offset, unsigned *src, int
> stride, int height)
> +{
> +    unsigned x = (0x7F7F7F7F >> 1) & 0x7F7F7F7F;
>

I'd rather you use explicit sized types here, e.g. uint32_t. True, we
depend on having sizeof(int) >= 4, but your tables and constants are all 32
bit.



> +    int i, j;
> +
> +    dst += offset >> 2;
> +
> +    for (i = 0; i < height; i++) {
> +        for (j = 0; j < stride >> 2; j++) {
> +            dst[j] = (((src[j] >> 3) + (x & dst[j])) << 3) & 0xFCFCFCFC;
> +        }
>

Brackets are unneeded for the second loop, its one line.
Also you can save a line by using for (int.


+
> +        dst += stride >> 2;
> +        src += stride >> 2;
> +    }
> +}
> +
> +static int decode_frame(AVCodecContext *avctx, void *data,
> +                                      int *got_frame, AVPacket *avpkt)
> +{
> +    ProSumerContext *s = avctx->priv_data;
> +    AVFrame * const frame = data;
> +    int i, j, ret;
> +
> +    if (avpkt->size <= 32)
> +        return AVERROR_INVALIDDATA;
> +
> +    memset(s->decbuffer, 0, s->size);
> +    bytestream2_init(&s->gb, avpkt->data, avpkt->size);
> +    bytestream2_init_writer(&s->pb, s->decbuffer, s->size);
> +
> +    decompress(&s->gb, AV_RL32(avpkt->data + 28) >> 1, &s->pb, s->lut);
> +    do_shift((unsigned *)s->decbuffer, 0, (unsigned *)s->table_b,
> s->stride, 1);
> +    do_shift((unsigned *)s->decbuffer, s->stride, (unsigned
> *)s->decbuffer, s->stride, avctx->height - 1);
> +
> +    ret = ff_get_buffer(avctx, frame, 0);
> +    if (ret < 0)
> +        return ret;
> +
> +    for (i = avctx->height - 1; i >= 0 ; i--) {
> +        uint8_t *y = &frame->data[0][i * frame->linesize[0]];
> +        uint8_t *u = &frame->data[1][i * frame->linesize[1]];
> +        uint8_t *v = &frame->data[2][i * frame->linesize[2]];
> +        uint8_t *src = s->decbuffer + (avctx->height - 1 - i) * s->stride;
> +
> +        for (j = 0; j < avctx->width; j += 8) {
> +            *(u++) = *src++;
> +            *(y++) = *src++;
> +            *(v++) = *src++;
> +            *(y++) = *src++;
> +
> +            *(u++) = *src++;
> +            *(y++) = *src++;
> +            *(v++) = *src++;
> +            *(y++) = *src++;
> +
> +            *(y++) = *src++;
> +            *(y++) = *src++;
> +            *(y++) = *src++;
> +            *(y++) = *src++;
> +        }
> +    }
> +
> +    frame->pict_type = AV_PICTURE_TYPE_I;
> +    frame->key_frame = 1;
> +    *got_frame = 1;
> +
> +    return avpkt->size;
> +}
> +
> +static const uint32_t table[] =
> +{
>

Style issue, { needs to go on the same line.



> +    0x0000, 0x10000001, 0x0101, 0x20000001, 0x0202, 0x30000001, 0xFFFF,
> 0x40000001, 0xFEFE, 0x50000001,
> +    0x0001, 0x70000001, 0x0100, 0x80000001, 0x00FF, 0x90000001, 0xFF00,
> 0xA0000001, 0x8001, 0x60000001,
> +    0x8002, 0xB0000001, 0xFCFC, 0x01000002, 0x0404, 0x03000002, 0x0002,
> 0xD3000002, 0xFEFC, 0x02000002,
> +    0xFCFE, 0x04000002, 0xFEFF, 0xD2000002, 0x0808, 0x06000002, 0xFFFE,
> 0x05000002, 0x0402, 0xC0000002,
> +    0x0204, 0xC1000002, 0xF8F8, 0xC3000002, 0x0201, 0xC4000002, 0x0102,
> 0xC6000002, 0x0804, 0xF3000002,
> +    0x0408, 0xE0000002, 0xF8FC, 0xE1000002, 0xFCF8, 0xC7000002, 0x00FE,
> 0xD0000002, 0xFE00, 0xD4000002,
> +    0xFF01, 0xD5000002, 0x01FF, 0xD6000002, 0x0200, 0xD7000002, 0xFCFF,
> 0xE2000002, 0x0104, 0xE3000002,
> +    0xF0F0, 0xE5000002, 0x0401, 0xE7000002, 0x02FE, 0xF0000002, 0xFE02,
> 0xF1000002, 0xFE01, 0xF2000002,
> +    0x01FE, 0xF4000002, 0xFF02, 0xF5000002, 0x02FF, 0xF6000002, 0x8003,
> 0xC2000002, 0x8004, 0x07000002,
> +    0x8005, 0xD1000002, 0x8006, 0xC5000002, 0x8007, 0xE6000002, 0x8008,
> 0xE4000002, 0x8009, 0xF7000002,
> +    0xFC02, 0x08000003, 0xFE04, 0x08100003, 0xFC00, 0x08200003, 0x02FC,
> 0x08300003, 0x1010, 0x08400003,
> +    0x00FC, 0x08500003, 0x0004, 0x08600003, 0x0400, 0x08700003, 0xFFFC,
> 0x08800003, 0x1008, 0x08900003,
> +    0x0810, 0x08A00003, 0x0802, 0x08B00003, 0x0208, 0x08C00003, 0xFEF8,
> 0x08D00003, 0xFC01, 0x08E00003,
> +    0x04FF, 0x08F00003, 0xF8FE, 0x09000003, 0xFC04, 0x09100003, 0x04FC,
> 0x09200003, 0xFF04, 0x09300003,
> +    0x01FC, 0x09400003, 0xF0F8, 0x09500003, 0xF8F0, 0x09600003, 0x04FE,
> 0x09700003, 0xF0FC, 0x09800003,
> +    0x0008, 0x09900003, 0x08FE, 0x09A00003, 0x01F8, 0x09B00003, 0x0800,
> 0x09C00003, 0x08FC, 0x09D00003,
> +    0xFE08, 0x09E00003, 0xFC08, 0x09F00003, 0xF800, 0x0A000003, 0x0108,
> 0x0A100003, 0xF802, 0x0A200003,
> +    0x0801, 0x0A300003, 0x00F8, 0x0A400003, 0xF804, 0x0A500003, 0xF8FF,
> 0x0A600003, 0xFFF8, 0x0A700003,
> +    0x04F8, 0x0A800003, 0x02F8, 0x0A900003, 0x1004, 0x0AA00003, 0x08F8,
> 0x0AB00003, 0xF808, 0x0AC00003,
> +    0x0410, 0x0AD00003, 0xFF08, 0x0AE00003, 0x08FF, 0x0AF00003, 0xFCF0,
> 0x0B000003, 0xF801, 0x0B100003,
> +    0xE0F0, 0x0B200003, 0xF3F3, 0x0B300003, 0xF0E0, 0x0B400003, 0xFAFA,
> 0x0B500003, 0xF7F7, 0x0B600003,
> +    0xFEF0, 0x0B700003, 0xF0FE, 0x0B800003, 0xE9E9, 0x0B900003, 0xF9F9,
> 0x0BA00003, 0x2020, 0x0BB00003,
> +    0xE0E0, 0x0BC00003, 0x02F0, 0x0BD00003, 0x04F0, 0x0BE00003, 0x2010,
> 0x0BF00003, 0xECEC, 0x0C000003,
> +    0xEFEF, 0x0C100003, 0x1020, 0x0C200003, 0xF5F5, 0x0C300003, 0xF4F4,
> 0x0C400003, 0xEDED, 0x0C500003,
> +    0xEAEA, 0x0C600003, 0xFBFB, 0x0C700003, 0x1002, 0x0C800003, 0xF2F2,
> 0x0C900003, 0xF6F6, 0x0CA00003,
> +    0xF1F1, 0x0CB00003, 0xFDFD, 0x0CC00003, 0x0210, 0x0CD00003, 0x10FF,
> 0x0CE00003, 0xFDFE, 0x0CF00003,
> +    0x10F8, 0x0D000003, 0x1000, 0x0D100003, 0xF001, 0x0D200003, 0x1001,
> 0x0D300003, 0x0010, 0x0D400003,
> +    0x10FE, 0x0D500003, 0xEBEB, 0x0D600003, 0xFE10, 0x0D700003, 0x0110,
> 0x0D800003, 0xF000, 0x0D900003,
> +    0x08F0, 0x0DA00003, 0x01F0, 0x0DB00003, 0x0303, 0x0DC00003, 0x00F0,
> 0x0DD00003, 0xF002, 0x0DE00003,
> +    0x10FC, 0x0DF00003, 0xFC10, 0x0E000003, 0xF0FF, 0x0E100003, 0xEEEE,
> 0x0E200003, 0xF004, 0x0E300003,
> +    0xFFF0, 0x0E400003, 0xF7F8, 0x0E500003, 0xF3F2, 0x0E600003, 0xF9FA,
> 0x0E700003, 0x0820, 0x0E800003,
> +    0x0302, 0x0E900003, 0xE0F8, 0x0EA00003, 0x0505, 0x0EB00003, 0x2008,
> 0x0EC00003, 0xE8E8, 0x0ED00003,
> +    0x0403, 0x0EE00003, 0xFBFC, 0x0EF00003, 0xFCFD, 0x0F000003, 0xFBFA,
> 0x0F100003, 0x0203, 0x0F200003,
> +    0xFCFB, 0x0F300003, 0x0304, 0x0F400003, 0xF810, 0x0F500003, 0xFF10,
> 0x0F600003, 0xF008, 0x0F700003,
> +    0xFEFD, 0x0F800003, 0xF7F6, 0x0F900003, 0xF2F1, 0x0FA00003, 0xF3F4,
> 0x0FB00003, 0xEDEC, 0x0FC00003,
> +    0xF4F1, 0x0FD00003, 0xF5F6, 0x0FE00003, 0xF0F1, 0x0FF00003, 0xF9F8,
> 0xC8000003, 0x10F0, 0xC8100003,
> +    0xF2F3, 0xC8200003, 0xF7F9, 0xC8300003, 0xF6F5, 0xC8400003, 0xF0EF,
> 0xC8500003, 0xF4F5, 0xC8600003,
> +    0xF6F7, 0xC8700003, 0xFAF9, 0xC8800003, 0x0405, 0xC8900003, 0xF8F9,
> 0xC8A00003, 0xFAFB, 0xC8B00003,
> +    0xF1F0, 0xC8C00003, 0xF4F3, 0xC8D00003, 0xF1F2, 0xC8E00003, 0xF8E0,
> 0xC8F00003, 0xF8F7, 0xC9000003,
> +    0xFDFC, 0xC9100003, 0xF8FA, 0xC9200003, 0xFAF6, 0xC9300003, 0xEEEF,
> 0xC9400003, 0xF5F7, 0xC9500003,
> +    0xFDFB, 0xC9600003, 0xF4F6, 0xC9700003, 0xFCFA, 0xC9800003, 0xECED,
> 0xC9900003, 0xF0F3, 0xC9A00003,
> +    0xF3F1, 0xC9B00003, 0xECEB, 0xC9C00003, 0xEDEE, 0xC9D00003, 0xF9F7,
> 0xC9E00003, 0x0420, 0xC9F00003,
> +    0xEBEA, 0xCA000003, 0xF0F4, 0xCA100003, 0xF3F5, 0xCA200003, 0xFAF7,
> 0xCA300003, 0x0301, 0xCA400003,
> +    0xF3F7, 0xCA500003, 0xF7F3, 0xCA600003, 0xEFF0, 0xCA700003, 0xF9F6,
> 0xCA800003, 0xEFEE, 0xCA900003,
> +    0xF4F7, 0xCAA00003, 0x0504, 0xCAB00003, 0xF5F4, 0xCAC00003, 0xF1F3,
> 0xCAD00003, 0xEBEE, 0xCAE00003,
> +    0xF2F5, 0xCAF00003, 0xF3EF, 0xCB000003, 0xF5F1, 0xCB100003, 0xF9F3,
> 0xCB200003, 0xEDF0, 0xCB300003,
> +    0xEEF1, 0xCB400003, 0xF6F9, 0xCB500003, 0xF8FB, 0xCB600003, 0xF010,
> 0xCB700003, 0xF2F6, 0xCB800003,
> +    0xF4ED, 0xCB900003, 0xF7FB, 0xCBA00003, 0xF8F3, 0xCBB00003, 0xEDEB,
> 0xCBC00003, 0xF0F2, 0xCBD00003,
> +    0xF2F9, 0xCBE00003, 0xF8F1, 0xCBF00003, 0xFAFC, 0xCC000003, 0xFBF8,
> 0xCC100003, 0xF6F0, 0xCC200003,
> +    0xFAF8, 0xCC300003, 0x0103, 0xCC400003, 0xF3F6, 0xCC500003, 0xF4F9,
> 0xCC600003, 0xF7F2, 0xCC700003,
> +    0x2004, 0xCC800003, 0xF2F0, 0xCC900003, 0xF4F2, 0xCCA00003, 0xEEED,
> 0xCCB00003, 0xFCE0, 0xCCC00003,
> +    0xEAE9, 0xCCD00003, 0xEAEB, 0xCCE00003, 0xF6F4, 0xCCF00003, 0xFFFD,
> 0xCD000003, 0xE9EA, 0xCD100003,
> +    0xF1F4, 0xCD200003, 0xF6EF, 0xCD300003, 0xF6F8, 0xCD400003, 0xF8F6,
> 0xCD500003, 0xEFF2, 0xCD600003,
> +    0xEFF1, 0xCD700003, 0xF7F1, 0xCD800003, 0xFBFD, 0xCD900003, 0xFEF6,
> 0xCDA00003, 0xFFF7, 0xCDB00003,
> +    0x0605, 0xCDC00003, 0xF0F5, 0xCDD00003, 0xF0FA, 0xCDE00003, 0xF1F9,
> 0xCDF00003, 0xF2FC, 0xCE000003,
> +    0xF7EE, 0xCE100003, 0xF7F5, 0xCE200003, 0xF9FC, 0xCE300003, 0xFAF5,
> 0xCE400003, 0xFBF1, 0xCE500003,
> +    0xF1EF, 0xCE600003, 0xF1FA, 0xCE700003, 0xF4F8, 0xCE800003, 0xF7F0,
> 0xCE900003, 0xF7F4, 0xCEA00003,
> +    0xF7FC, 0xCEB00003, 0xF9FB, 0xCEC00003, 0xFAF1, 0xCED00003, 0xFBF9,
> 0xCEE00003, 0xFDFF, 0xCEF00003,
> +    0xE0FC, 0xCF000003, 0xEBEC, 0xCF100003, 0xEDEF, 0xCF200003, 0xEFED,
> 0xCF300003, 0xF1F6, 0xCF400003,
> +    0xF2F7, 0xCF500003, 0xF3EE, 0xCF600003, 0xF3F8, 0xCF700003, 0xF5F2,
> 0xCF800003, 0xF8F2, 0xCF900003,
> +    0xF9F1, 0xCFA00003, 0xF9F2, 0xCFB00003, 0xFBEF, 0xCFC00003, 0x00FD,
> 0xCFD00003, 0xECEE, 0xCFE00003,
> +    0xF2EF, 0xCFF00003, 0xF2F8, 0xD8000003, 0xF5F0, 0xD8100003, 0xF6F2,
> 0xD8200003, 0xFCF7, 0xD8300003,
> +    0xFCF9, 0xD8400003, 0x0506, 0xD8500003, 0xEEEC, 0xD8600003, 0xF0F6,
> 0xD8700003, 0xF2F4, 0xD8800003,
> +    0xF6F1, 0xD8900003, 0xF8F5, 0xD8A00003, 0xF9F4, 0xD8B00003, 0xFBF7,
> 0xD8C00003, 0x0503, 0xD8D00003,
> +    0xEFEC, 0xD8E00003, 0xF3F0, 0xD8F00003, 0xF4F0, 0xD9000003, 0xF5F3,
> 0xD9100003, 0xF6F3, 0xD9200003,
> +    0xF7FA, 0xD9300003, 0x800A, 0xD9400003, 0x800B, 0xD9500003, 0x800C,
> 0xD9600003, 0x800D, 0xD9700003,
> +    0x800E, 0xD9800003, 0x800F, 0xD9900003, 0x8010, 0xD9A00003, 0x8011,
> 0xD9B00003, 0x8012, 0xD9C00003,
> +    0x8013, 0xD9D00003, 0x8014, 0xD9E00003, 0x8015, 0xD9F00003, 0x8016,
> 0xDA000003, 0x8017, 0xDA100003,
> +    0x8018, 0xDA200003, 0x8019, 0xDA300003, 0x801A, 0xDA400003, 0x801B,
> 0xDA500003, 0x801C, 0xDA600003,
> +    0x801D, 0xDA700003, 0x801E, 0xDA800003, 0x801F, 0xDA900003, 0x8020,
> 0xDAA00003, 0x8021, 0xDAB00003,
> +    0x8022, 0xDAC00003, 0x8023, 0xDAD00003, 0x8024, 0xDAE00003, 0x8025,
> 0xDAF00003, 0x8026, 0xDB000003,
> +    0x8027, 0xDB100003, 0x8028, 0xDB200003, 0x8029, 0xDB300003, 0x802A,
> 0xDB400003, 0x802B, 0xDB500003,
> +    0x802C, 0xDB600003, 0x802D, 0xDB700003, 0x802E, 0xDB800003, 0x802F,
> 0xDB900003, 0x80FF, 0xDBA00003,
> +    0x0001
> +};
> +
> +static void fill_elements(unsigned idx, unsigned shift, int size,
> unsigned *e0, unsigned *e1)
> +{
> +    unsigned a = 1, b, g = 1, h = idx << (32 - shift);
> +    int i;
> +
> +    for (i = 0; i < size; i++) {
>

We support for (int i; loops now, you can save a line.


+        if (!a || !g)
> +            break;
> +        b = 4 * (table[2 * i + 1] & 0xF);
> +        if (shift >= b && (h & (0xFFF00000u << (12 - b))) == (table[2 * i
> + 1] & 0xFFFF0000u)) {
> +            if (table[2 * i] >> 8 == 0x80u) {
> +                g = 0;
> +            } else {
> +                a = 0;
> +                *e1 = table[2 * i];
> +                *e0 = (*e0 & 0xFFFFFFu) | (((12 + b - shift) &
> 0xFFFFFFFCu | 0x40u) << 22);
> +                shift -= b;
> +                h <<= b;
> +            }
> +        }
> +    }
> +    a = 1;
> +    for (i = 0; i < size; i++) {
> +        if (!a || !g)
> +            break;
> +        b = 4 * (table[2 * i + 1] & 0xF);
> +        if (shift >= b && (h & (0xFFF00000u << (12 - b))) == (table[2 * i
> + 1] & 0xFFFF0000u)) {
> +            if ((table[2 * i] >> 8) == 0x80u) {
> +                g = 0;
> +            } else {
> +                a = 0;
> +                *e1 |= table[2 * i] << 16;
> +                *e0 = (*e0 & 0xFFFFFFu) | (((12 + b - shift) &
> 0xFFFFFFFCu | 0x80u) << 22);
> +            }
> +        }
> +    }
> +}
> +
> +static void fill_lut(unsigned *lut)
> +{
> +    int i, j;
> +
> +    for (i = 1; i < FF_ARRAY_ELEMS(table); i += 2) {
>

Same here.


+        unsigned a = table[i];
> +        unsigned b = a & 0xFFu;
> +        unsigned c, d, e;
> +
> +        if (b > 3)
> +            continue;
> +
> +        c = (b << 16) | table[i-1];
> +        d = 4 * (3 - b);
> +        e = (((0xFFF00000u << d) & a) >> 20) & 0xFFF;
> +        if (d <= 0) {
> +            lut[2 * e] = c;
> +            lut[2 * e + 1] = 0;
> +        } else {
> +            for (j = 0; j < 1 << d; j++) {
> +                unsigned f = 0xFFFFFFFFu;
> +                c &= 0xFFFFFFu;
> +                if ((c & 0xFF00u) != 0x8000u)
> +                    fill_elements(j, d, 365, &c, &f);
> +                lut[2 * e + 2 * j] = c;
> +                lut[2 * e + 2 * j + 1] = f;
> +            }
> +        }
> +    }
> +
> +    for (i = 0; i < 32; i += 2) {
> +        lut[i  ] = 0x68000;
> +        lut[i+1] = 0;
> +    }
> +}
> +
> +static av_cold int decode_init(AVCodecContext *avctx)
> +{
> +    ProSumerContext *s = avctx->priv_data;
> +
> +    switch (avctx->bits_per_coded_sample) {
> +    case 12:
> +         s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;
> +         break;
> +    default:
> +         return AVERROR_INVALIDDATA;
> +    }
> +    s->size = avctx->height * s->stride;
> +
> +    avctx->pix_fmt = AV_PIX_FMT_YUV411P;
> +
> +    s->table_b = av_malloc(s->stride);
> +    s->decbuffer = av_malloc(s->size);
> +    if (!s->table_b || !s->decbuffer)
> +        return AVERROR(ENOMEM);
> +    memset(s->table_b, 0x80u, s->stride);
> +
> +    fill_lut(s->lut);
> +
> +    return 0;
> +}
> +
> +static av_cold int decode_close(AVCodecContext *avctx)
> +{
> +    ProSumerContext *s = avctx->priv_data;
> +
> +    av_freep(&s->table_b);
> +    av_freep(&s->decbuffer);
> +
> +    return 0;
> +}
> +
> +AVCodec ff_prosumer_decoder = {
> +    .name           = "prosumer",
> +    .long_name      = NULL_IF_CONFIG_SMALL("Brooktree ProSumer Video"),
> +    .type           = AVMEDIA_TYPE_VIDEO,
> +    .id             = AV_CODEC_ID_PROSUMER,
> +    .priv_data_size = sizeof(ProSumerContext),
> +    .init           = decode_init,
> +    .decode         = decode_frame,
> +    .close          = decode_close,
> +    .capabilities   = AV_CODEC_CAP_DR1,
> +    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE |
> +                      FF_CODEC_CAP_INIT_CLEANUP,
> +};
> diff --git a/libavformat/riff.c b/libavformat/riff.c
> index 3e14644c46..cf27d0dfd5 100644
> --- a/libavformat/riff.c
> +++ b/libavformat/riff.c
> @@ -471,6 +471,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
>      { AV_CODEC_ID_MSCC,         MKTAG('M', 'S', 'C', 'C') },
>      { AV_CODEC_ID_SRGC,         MKTAG('S', 'R', 'G', 'C') },
>      { AV_CODEC_ID_IMM4,         MKTAG('I', 'M', 'M', '4') },
> +    { AV_CODEC_ID_PROSUMER,     MKTAG('B', 'T', '2', '0') },
>      { AV_CODEC_ID_NONE,         0 }
>  };
>
> --
> 2.17.1
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>

Apart from that looks fine.
diff mbox

Patch

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index f0c8226283..9a309c348e 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -515,6 +515,7 @@  OBJS-$(CONFIG_PRORES_DECODER)          += proresdec2.o proresdsp.o proresdata.o
 OBJS-$(CONFIG_PRORES_ENCODER)          += proresenc_anatoliy.o
 OBJS-$(CONFIG_PRORES_AW_ENCODER)       += proresenc_anatoliy.o
 OBJS-$(CONFIG_PRORES_KS_ENCODER)       += proresenc_kostya.o proresdata.o
+OBJS-$(CONFIG_PROSUMER_DECODER)        += prosumer.o
 OBJS-$(CONFIG_PSD_DECODER)             += psd.o
 OBJS-$(CONFIG_PTX_DECODER)             += ptx.o
 OBJS-$(CONFIG_QCELP_DECODER)           += qcelpdec.o                     \
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index fd35fc1d0b..b1d1ef26c0 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -235,6 +235,7 @@  extern AVCodec ff_prores_encoder;
 extern AVCodec ff_prores_decoder;
 extern AVCodec ff_prores_aw_encoder;
 extern AVCodec ff_prores_ks_encoder;
+extern AVCodec ff_prosumer_decoder;
 extern AVCodec ff_psd_decoder;
 extern AVCodec ff_ptx_decoder;
 extern AVCodec ff_qdraw_decoder;
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 31e50d5a94..2a4be2ca4f 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -448,6 +448,7 @@  enum AVCodecID {
     AV_CODEC_ID_GDV,
     AV_CODEC_ID_FITS,
     AV_CODEC_ID_IMM4,
+    AV_CODEC_ID_PROSUMER,
 
     /* various PCM "codecs" */
     AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index af66b35d2b..e611183599 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -1661,6 +1661,13 @@  static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("Infinity IMM4"),
         .props     = AV_CODEC_PROP_LOSSY,
     },
+    {
+        .id        = AV_CODEC_ID_PROSUMER,
+        .type      = AVMEDIA_TYPE_VIDEO,
+        .name      = "prosumer",
+        .long_name = NULL_IF_CONFIG_SMALL("Brooktree ProSumer Video"),
+        .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
+    },
 
     /* various PCM "codecs" */
     {
diff --git a/libavcodec/prosumer.c b/libavcodec/prosumer.c
new file mode 100644
index 0000000000..7b9d5e7bdb
--- /dev/null
+++ b/libavcodec/prosumer.c
@@ -0,0 +1,405 @@ 
+/*
+ * Brooktree ProSumer Video decoder
+ * Copyright (c) 2018 Paul B Mahol
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libavutil/imgutils.h"
+#include "libavutil/internal.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/mem.h"
+
+#include "avcodec.h"
+#include "bytestream.h"
+#include "internal.h"
+
+typedef struct ProSumerContext {
+    GetByteContext gb;
+    PutByteContext pb;
+
+    unsigned stride;
+    unsigned size;
+    unsigned lut[0x10000];
+    uint8_t *table_b;
+    uint8_t *decbuffer;
+} ProSumerContext;
+
+#define PAIR(high, low) (((uint64_t)(high)<<sizeof(high)*8) | low)
+
+static int decompress(GetByteContext *gb, int size, PutByteContext *pb, const unsigned *lut)
+{
+    int i, pos, idx, cnt, fill;
+    unsigned a, b, c;
+
+    bytestream2_skip(gb, 32);
+    cnt = 4;
+    a = bytestream2_get_le32(gb);
+    idx = a >> 20;
+    b = lut[2 * idx];
+
+    while (1) {
+        if (((b & 0xFF00u) != 0x8000u) || (b & 0xFFu)) {
+            if ((b & 0xFF00u) != 0x8000u) {
+                bytestream2_put_le16(pb, b);
+            } else if (b & 0xFFu) {
+                idx = 0;
+                for (i = 0; i < (b & 0xFFu); i++)
+                    bytestream2_put_le32(pb, 0);
+            }
+            c = b >> 16;
+            if (c & 0xFF00u) {
+                c = (((c >> 8) & 0xFFu) | (c & 0xFF00)) & 0xF00F;
+                fill = lut[2 * idx + 1];
+                if ((c & 0xFF00u) == 0x1000) {
+                    bytestream2_put_le16(pb, fill);
+                    c &= 0xFFFF00FFu;
+                } else {
+                    bytestream2_put_le32(pb, fill);
+                    c &= 0xFFFF00FFu;
+                }
+            }
+            while (c) {
+                a <<= 4;
+                cnt--;
+                if (!cnt) {
+                    if (bytestream2_get_bytes_left(gb) <= 0) {
+                        if (!a)
+                            return 0;
+                        cnt = 4;
+                    } else {
+                        pos = bytestream2_tell(gb) ^ 2;
+                        bytestream2_seek(gb, pos, SEEK_SET);
+                        AV_WN16(&a, bytestream2_peek_le16(gb));
+                        pos = pos ^ 2;
+                        bytestream2_seek(gb, pos, SEEK_SET);
+                        bytestream2_skip(gb, 2);
+                        cnt = 4;
+                    }
+                }
+                c--;
+            }
+            idx = a >> 20;
+            b = lut[2 * idx];
+            continue;
+        }
+        idx = 2;
+        while (idx) {
+            a <<= 4;
+            cnt--;
+            if (cnt) {
+                idx--;
+                continue;
+            }
+            if (bytestream2_get_bytes_left(gb) <= 0) {
+                if (a) {
+                    cnt = 4;
+                    idx--;
+                    continue;
+                }
+                return 0;
+            }
+            pos = bytestream2_tell(gb) ^ 2;
+            bytestream2_seek(gb, pos, SEEK_SET);
+            AV_WN16(&a, bytestream2_peek_le16(gb));
+            pos = pos ^ 2;
+            bytestream2_seek(gb, pos, SEEK_SET);
+            bytestream2_skip(gb, 2);
+            cnt = 4;
+            idx--;
+        }
+        b = PAIR(4, a) >> 16;
+    }
+
+    return 0;
+}
+
+static void do_shift(unsigned *dst, int offset, unsigned *src, int stride, int height)
+{
+    unsigned x = (0x7F7F7F7F >> 1) & 0x7F7F7F7F;
+    int i, j;
+
+    dst += offset >> 2;
+
+    for (i = 0; i < height; i++) {
+        for (j = 0; j < stride >> 2; j++) {
+            dst[j] = (((src[j] >> 3) + (x & dst[j])) << 3) & 0xFCFCFCFC;
+        }
+
+        dst += stride >> 2;
+        src += stride >> 2;
+    }
+}
+
+static int decode_frame(AVCodecContext *avctx, void *data,
+                                      int *got_frame, AVPacket *avpkt)
+{
+    ProSumerContext *s = avctx->priv_data;
+    AVFrame * const frame = data;
+    int i, j, ret;
+
+    if (avpkt->size <= 32)
+        return AVERROR_INVALIDDATA;
+
+    memset(s->decbuffer, 0, s->size);
+    bytestream2_init(&s->gb, avpkt->data, avpkt->size);
+    bytestream2_init_writer(&s->pb, s->decbuffer, s->size);
+
+    decompress(&s->gb, AV_RL32(avpkt->data + 28) >> 1, &s->pb, s->lut);
+    do_shift((unsigned *)s->decbuffer, 0, (unsigned *)s->table_b, s->stride, 1);
+    do_shift((unsigned *)s->decbuffer, s->stride, (unsigned *)s->decbuffer, s->stride, avctx->height - 1);
+
+    ret = ff_get_buffer(avctx, frame, 0);
+    if (ret < 0)
+        return ret;
+
+    for (i = avctx->height - 1; i >= 0 ; i--) {
+        uint8_t *y = &frame->data[0][i * frame->linesize[0]];
+        uint8_t *u = &frame->data[1][i * frame->linesize[1]];
+        uint8_t *v = &frame->data[2][i * frame->linesize[2]];
+        uint8_t *src = s->decbuffer + (avctx->height - 1 - i) * s->stride;
+
+        for (j = 0; j < avctx->width; j += 8) {
+            *(u++) = *src++;
+            *(y++) = *src++;
+            *(v++) = *src++;
+            *(y++) = *src++;
+
+            *(u++) = *src++;
+            *(y++) = *src++;
+            *(v++) = *src++;
+            *(y++) = *src++;
+
+            *(y++) = *src++;
+            *(y++) = *src++;
+            *(y++) = *src++;
+            *(y++) = *src++;
+        }
+    }
+
+    frame->pict_type = AV_PICTURE_TYPE_I;
+    frame->key_frame = 1;
+    *got_frame = 1;
+
+    return avpkt->size;
+}
+
+static const uint32_t table[] =
+{
+    0x0000, 0x10000001, 0x0101, 0x20000001, 0x0202, 0x30000001, 0xFFFF, 0x40000001, 0xFEFE, 0x50000001,
+    0x0001, 0x70000001, 0x0100, 0x80000001, 0x00FF, 0x90000001, 0xFF00, 0xA0000001, 0x8001, 0x60000001,
+    0x8002, 0xB0000001, 0xFCFC, 0x01000002, 0x0404, 0x03000002, 0x0002, 0xD3000002, 0xFEFC, 0x02000002,
+    0xFCFE, 0x04000002, 0xFEFF, 0xD2000002, 0x0808, 0x06000002, 0xFFFE, 0x05000002, 0x0402, 0xC0000002,
+    0x0204, 0xC1000002, 0xF8F8, 0xC3000002, 0x0201, 0xC4000002, 0x0102, 0xC6000002, 0x0804, 0xF3000002,
+    0x0408, 0xE0000002, 0xF8FC, 0xE1000002, 0xFCF8, 0xC7000002, 0x00FE, 0xD0000002, 0xFE00, 0xD4000002,
+    0xFF01, 0xD5000002, 0x01FF, 0xD6000002, 0x0200, 0xD7000002, 0xFCFF, 0xE2000002, 0x0104, 0xE3000002,
+    0xF0F0, 0xE5000002, 0x0401, 0xE7000002, 0x02FE, 0xF0000002, 0xFE02, 0xF1000002, 0xFE01, 0xF2000002,
+    0x01FE, 0xF4000002, 0xFF02, 0xF5000002, 0x02FF, 0xF6000002, 0x8003, 0xC2000002, 0x8004, 0x07000002,
+    0x8005, 0xD1000002, 0x8006, 0xC5000002, 0x8007, 0xE6000002, 0x8008, 0xE4000002, 0x8009, 0xF7000002,
+    0xFC02, 0x08000003, 0xFE04, 0x08100003, 0xFC00, 0x08200003, 0x02FC, 0x08300003, 0x1010, 0x08400003,
+    0x00FC, 0x08500003, 0x0004, 0x08600003, 0x0400, 0x08700003, 0xFFFC, 0x08800003, 0x1008, 0x08900003,
+    0x0810, 0x08A00003, 0x0802, 0x08B00003, 0x0208, 0x08C00003, 0xFEF8, 0x08D00003, 0xFC01, 0x08E00003,
+    0x04FF, 0x08F00003, 0xF8FE, 0x09000003, 0xFC04, 0x09100003, 0x04FC, 0x09200003, 0xFF04, 0x09300003,
+    0x01FC, 0x09400003, 0xF0F8, 0x09500003, 0xF8F0, 0x09600003, 0x04FE, 0x09700003, 0xF0FC, 0x09800003,
+    0x0008, 0x09900003, 0x08FE, 0x09A00003, 0x01F8, 0x09B00003, 0x0800, 0x09C00003, 0x08FC, 0x09D00003,
+    0xFE08, 0x09E00003, 0xFC08, 0x09F00003, 0xF800, 0x0A000003, 0x0108, 0x0A100003, 0xF802, 0x0A200003,
+    0x0801, 0x0A300003, 0x00F8, 0x0A400003, 0xF804, 0x0A500003, 0xF8FF, 0x0A600003, 0xFFF8, 0x0A700003,
+    0x04F8, 0x0A800003, 0x02F8, 0x0A900003, 0x1004, 0x0AA00003, 0x08F8, 0x0AB00003, 0xF808, 0x0AC00003,
+    0x0410, 0x0AD00003, 0xFF08, 0x0AE00003, 0x08FF, 0x0AF00003, 0xFCF0, 0x0B000003, 0xF801, 0x0B100003,
+    0xE0F0, 0x0B200003, 0xF3F3, 0x0B300003, 0xF0E0, 0x0B400003, 0xFAFA, 0x0B500003, 0xF7F7, 0x0B600003,
+    0xFEF0, 0x0B700003, 0xF0FE, 0x0B800003, 0xE9E9, 0x0B900003, 0xF9F9, 0x0BA00003, 0x2020, 0x0BB00003,
+    0xE0E0, 0x0BC00003, 0x02F0, 0x0BD00003, 0x04F0, 0x0BE00003, 0x2010, 0x0BF00003, 0xECEC, 0x0C000003,
+    0xEFEF, 0x0C100003, 0x1020, 0x0C200003, 0xF5F5, 0x0C300003, 0xF4F4, 0x0C400003, 0xEDED, 0x0C500003,
+    0xEAEA, 0x0C600003, 0xFBFB, 0x0C700003, 0x1002, 0x0C800003, 0xF2F2, 0x0C900003, 0xF6F6, 0x0CA00003,
+    0xF1F1, 0x0CB00003, 0xFDFD, 0x0CC00003, 0x0210, 0x0CD00003, 0x10FF, 0x0CE00003, 0xFDFE, 0x0CF00003,
+    0x10F8, 0x0D000003, 0x1000, 0x0D100003, 0xF001, 0x0D200003, 0x1001, 0x0D300003, 0x0010, 0x0D400003,
+    0x10FE, 0x0D500003, 0xEBEB, 0x0D600003, 0xFE10, 0x0D700003, 0x0110, 0x0D800003, 0xF000, 0x0D900003,
+    0x08F0, 0x0DA00003, 0x01F0, 0x0DB00003, 0x0303, 0x0DC00003, 0x00F0, 0x0DD00003, 0xF002, 0x0DE00003,
+    0x10FC, 0x0DF00003, 0xFC10, 0x0E000003, 0xF0FF, 0x0E100003, 0xEEEE, 0x0E200003, 0xF004, 0x0E300003,
+    0xFFF0, 0x0E400003, 0xF7F8, 0x0E500003, 0xF3F2, 0x0E600003, 0xF9FA, 0x0E700003, 0x0820, 0x0E800003,
+    0x0302, 0x0E900003, 0xE0F8, 0x0EA00003, 0x0505, 0x0EB00003, 0x2008, 0x0EC00003, 0xE8E8, 0x0ED00003,
+    0x0403, 0x0EE00003, 0xFBFC, 0x0EF00003, 0xFCFD, 0x0F000003, 0xFBFA, 0x0F100003, 0x0203, 0x0F200003,
+    0xFCFB, 0x0F300003, 0x0304, 0x0F400003, 0xF810, 0x0F500003, 0xFF10, 0x0F600003, 0xF008, 0x0F700003,
+    0xFEFD, 0x0F800003, 0xF7F6, 0x0F900003, 0xF2F1, 0x0FA00003, 0xF3F4, 0x0FB00003, 0xEDEC, 0x0FC00003,
+    0xF4F1, 0x0FD00003, 0xF5F6, 0x0FE00003, 0xF0F1, 0x0FF00003, 0xF9F8, 0xC8000003, 0x10F0, 0xC8100003,
+    0xF2F3, 0xC8200003, 0xF7F9, 0xC8300003, 0xF6F5, 0xC8400003, 0xF0EF, 0xC8500003, 0xF4F5, 0xC8600003,
+    0xF6F7, 0xC8700003, 0xFAF9, 0xC8800003, 0x0405, 0xC8900003, 0xF8F9, 0xC8A00003, 0xFAFB, 0xC8B00003,
+    0xF1F0, 0xC8C00003, 0xF4F3, 0xC8D00003, 0xF1F2, 0xC8E00003, 0xF8E0, 0xC8F00003, 0xF8F7, 0xC9000003,
+    0xFDFC, 0xC9100003, 0xF8FA, 0xC9200003, 0xFAF6, 0xC9300003, 0xEEEF, 0xC9400003, 0xF5F7, 0xC9500003,
+    0xFDFB, 0xC9600003, 0xF4F6, 0xC9700003, 0xFCFA, 0xC9800003, 0xECED, 0xC9900003, 0xF0F3, 0xC9A00003,
+    0xF3F1, 0xC9B00003, 0xECEB, 0xC9C00003, 0xEDEE, 0xC9D00003, 0xF9F7, 0xC9E00003, 0x0420, 0xC9F00003,
+    0xEBEA, 0xCA000003, 0xF0F4, 0xCA100003, 0xF3F5, 0xCA200003, 0xFAF7, 0xCA300003, 0x0301, 0xCA400003,
+    0xF3F7, 0xCA500003, 0xF7F3, 0xCA600003, 0xEFF0, 0xCA700003, 0xF9F6, 0xCA800003, 0xEFEE, 0xCA900003,
+    0xF4F7, 0xCAA00003, 0x0504, 0xCAB00003, 0xF5F4, 0xCAC00003, 0xF1F3, 0xCAD00003, 0xEBEE, 0xCAE00003,
+    0xF2F5, 0xCAF00003, 0xF3EF, 0xCB000003, 0xF5F1, 0xCB100003, 0xF9F3, 0xCB200003, 0xEDF0, 0xCB300003,
+    0xEEF1, 0xCB400003, 0xF6F9, 0xCB500003, 0xF8FB, 0xCB600003, 0xF010, 0xCB700003, 0xF2F6, 0xCB800003,
+    0xF4ED, 0xCB900003, 0xF7FB, 0xCBA00003, 0xF8F3, 0xCBB00003, 0xEDEB, 0xCBC00003, 0xF0F2, 0xCBD00003,
+    0xF2F9, 0xCBE00003, 0xF8F1, 0xCBF00003, 0xFAFC, 0xCC000003, 0xFBF8, 0xCC100003, 0xF6F0, 0xCC200003,
+    0xFAF8, 0xCC300003, 0x0103, 0xCC400003, 0xF3F6, 0xCC500003, 0xF4F9, 0xCC600003, 0xF7F2, 0xCC700003,
+    0x2004, 0xCC800003, 0xF2F0, 0xCC900003, 0xF4F2, 0xCCA00003, 0xEEED, 0xCCB00003, 0xFCE0, 0xCCC00003,
+    0xEAE9, 0xCCD00003, 0xEAEB, 0xCCE00003, 0xF6F4, 0xCCF00003, 0xFFFD, 0xCD000003, 0xE9EA, 0xCD100003,
+    0xF1F4, 0xCD200003, 0xF6EF, 0xCD300003, 0xF6F8, 0xCD400003, 0xF8F6, 0xCD500003, 0xEFF2, 0xCD600003,
+    0xEFF1, 0xCD700003, 0xF7F1, 0xCD800003, 0xFBFD, 0xCD900003, 0xFEF6, 0xCDA00003, 0xFFF7, 0xCDB00003,
+    0x0605, 0xCDC00003, 0xF0F5, 0xCDD00003, 0xF0FA, 0xCDE00003, 0xF1F9, 0xCDF00003, 0xF2FC, 0xCE000003,
+    0xF7EE, 0xCE100003, 0xF7F5, 0xCE200003, 0xF9FC, 0xCE300003, 0xFAF5, 0xCE400003, 0xFBF1, 0xCE500003,
+    0xF1EF, 0xCE600003, 0xF1FA, 0xCE700003, 0xF4F8, 0xCE800003, 0xF7F0, 0xCE900003, 0xF7F4, 0xCEA00003,
+    0xF7FC, 0xCEB00003, 0xF9FB, 0xCEC00003, 0xFAF1, 0xCED00003, 0xFBF9, 0xCEE00003, 0xFDFF, 0xCEF00003,
+    0xE0FC, 0xCF000003, 0xEBEC, 0xCF100003, 0xEDEF, 0xCF200003, 0xEFED, 0xCF300003, 0xF1F6, 0xCF400003,
+    0xF2F7, 0xCF500003, 0xF3EE, 0xCF600003, 0xF3F8, 0xCF700003, 0xF5F2, 0xCF800003, 0xF8F2, 0xCF900003,
+    0xF9F1, 0xCFA00003, 0xF9F2, 0xCFB00003, 0xFBEF, 0xCFC00003, 0x00FD, 0xCFD00003, 0xECEE, 0xCFE00003,
+    0xF2EF, 0xCFF00003, 0xF2F8, 0xD8000003, 0xF5F0, 0xD8100003, 0xF6F2, 0xD8200003, 0xFCF7, 0xD8300003,
+    0xFCF9, 0xD8400003, 0x0506, 0xD8500003, 0xEEEC, 0xD8600003, 0xF0F6, 0xD8700003, 0xF2F4, 0xD8800003,
+    0xF6F1, 0xD8900003, 0xF8F5, 0xD8A00003, 0xF9F4, 0xD8B00003, 0xFBF7, 0xD8C00003, 0x0503, 0xD8D00003,
+    0xEFEC, 0xD8E00003, 0xF3F0, 0xD8F00003, 0xF4F0, 0xD9000003, 0xF5F3, 0xD9100003, 0xF6F3, 0xD9200003,
+    0xF7FA, 0xD9300003, 0x800A, 0xD9400003, 0x800B, 0xD9500003, 0x800C, 0xD9600003, 0x800D, 0xD9700003,
+    0x800E, 0xD9800003, 0x800F, 0xD9900003, 0x8010, 0xD9A00003, 0x8011, 0xD9B00003, 0x8012, 0xD9C00003,
+    0x8013, 0xD9D00003, 0x8014, 0xD9E00003, 0x8015, 0xD9F00003, 0x8016, 0xDA000003, 0x8017, 0xDA100003,
+    0x8018, 0xDA200003, 0x8019, 0xDA300003, 0x801A, 0xDA400003, 0x801B, 0xDA500003, 0x801C, 0xDA600003,
+    0x801D, 0xDA700003, 0x801E, 0xDA800003, 0x801F, 0xDA900003, 0x8020, 0xDAA00003, 0x8021, 0xDAB00003,
+    0x8022, 0xDAC00003, 0x8023, 0xDAD00003, 0x8024, 0xDAE00003, 0x8025, 0xDAF00003, 0x8026, 0xDB000003,
+    0x8027, 0xDB100003, 0x8028, 0xDB200003, 0x8029, 0xDB300003, 0x802A, 0xDB400003, 0x802B, 0xDB500003,
+    0x802C, 0xDB600003, 0x802D, 0xDB700003, 0x802E, 0xDB800003, 0x802F, 0xDB900003, 0x80FF, 0xDBA00003,
+    0x0001
+};
+
+static void fill_elements(unsigned idx, unsigned shift, int size, unsigned *e0, unsigned *e1)
+{
+    unsigned a = 1, b, g = 1, h = idx << (32 - shift);
+    int i;
+
+    for (i = 0; i < size; i++) {
+        if (!a || !g)
+            break;
+        b = 4 * (table[2 * i + 1] & 0xF);
+        if (shift >= b && (h & (0xFFF00000u << (12 - b))) == (table[2 * i + 1] & 0xFFFF0000u)) {
+            if (table[2 * i] >> 8 == 0x80u) {
+                g = 0;
+            } else {
+                a = 0;
+                *e1 = table[2 * i];
+                *e0 = (*e0 & 0xFFFFFFu) | (((12 + b - shift) & 0xFFFFFFFCu | 0x40u) << 22);
+                shift -= b;
+                h <<= b;
+            }
+        }
+    }
+    a = 1;
+    for (i = 0; i < size; i++) {
+        if (!a || !g)
+            break;
+        b = 4 * (table[2 * i + 1] & 0xF);
+        if (shift >= b && (h & (0xFFF00000u << (12 - b))) == (table[2 * i + 1] & 0xFFFF0000u)) {
+            if ((table[2 * i] >> 8) == 0x80u) {
+                g = 0;
+            } else {
+                a = 0;
+                *e1 |= table[2 * i] << 16;
+                *e0 = (*e0 & 0xFFFFFFu) | (((12 + b - shift) & 0xFFFFFFFCu | 0x80u) << 22);
+            }
+        }
+    }
+}
+
+static void fill_lut(unsigned *lut)
+{
+    int i, j;
+
+    for (i = 1; i < FF_ARRAY_ELEMS(table); i += 2) {
+        unsigned a = table[i];
+        unsigned b = a & 0xFFu;
+        unsigned c, d, e;
+
+        if (b > 3)
+            continue;
+
+        c = (b << 16) | table[i-1];
+        d = 4 * (3 - b);
+        e = (((0xFFF00000u << d) & a) >> 20) & 0xFFF;
+        if (d <= 0) {
+            lut[2 * e] = c;
+            lut[2 * e + 1] = 0;
+        } else {
+            for (j = 0; j < 1 << d; j++) {
+                unsigned f = 0xFFFFFFFFu;
+                c &= 0xFFFFFFu;
+                if ((c & 0xFF00u) != 0x8000u)
+                    fill_elements(j, d, 365, &c, &f);
+                lut[2 * e + 2 * j] = c;
+                lut[2 * e + 2 * j + 1] = f;
+            }
+        }
+    }
+
+    for (i = 0; i < 32; i += 2) {
+        lut[i  ] = 0x68000;
+        lut[i+1] = 0;
+    }
+}
+
+static av_cold int decode_init(AVCodecContext *avctx)
+{
+    ProSumerContext *s = avctx->priv_data;
+
+    switch (avctx->bits_per_coded_sample) {
+    case 12:
+         s->stride = 3LL * FFALIGN(avctx->width, 8) >> 1;
+         break;
+    default:
+         return AVERROR_INVALIDDATA;
+    }
+    s->size = avctx->height * s->stride;
+
+    avctx->pix_fmt = AV_PIX_FMT_YUV411P;
+
+    s->table_b = av_malloc(s->stride);
+    s->decbuffer = av_malloc(s->size);
+    if (!s->table_b || !s->decbuffer)
+        return AVERROR(ENOMEM);
+    memset(s->table_b, 0x80u, s->stride);
+
+    fill_lut(s->lut);
+
+    return 0;
+}
+
+static av_cold int decode_close(AVCodecContext *avctx)
+{
+    ProSumerContext *s = avctx->priv_data;
+
+    av_freep(&s->table_b);
+    av_freep(&s->decbuffer);
+
+    return 0;
+}
+
+AVCodec ff_prosumer_decoder = {
+    .name           = "prosumer",
+    .long_name      = NULL_IF_CONFIG_SMALL("Brooktree ProSumer Video"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_PROSUMER,
+    .priv_data_size = sizeof(ProSumerContext),
+    .init           = decode_init,
+    .decode         = decode_frame,
+    .close          = decode_close,
+    .capabilities   = AV_CODEC_CAP_DR1,
+    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE |
+                      FF_CODEC_CAP_INIT_CLEANUP,
+};
diff --git a/libavformat/riff.c b/libavformat/riff.c
index 3e14644c46..cf27d0dfd5 100644
--- a/libavformat/riff.c
+++ b/libavformat/riff.c
@@ -471,6 +471,7 @@  const AVCodecTag ff_codec_bmp_tags[] = {
     { AV_CODEC_ID_MSCC,         MKTAG('M', 'S', 'C', 'C') },
     { AV_CODEC_ID_SRGC,         MKTAG('S', 'R', 'G', 'C') },
     { AV_CODEC_ID_IMM4,         MKTAG('I', 'M', 'M', '4') },
+    { AV_CODEC_ID_PROSUMER,     MKTAG('B', 'T', '2', '0') },
     { AV_CODEC_ID_NONE,         0 }
 };