1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-08-10 06:10:52 +02:00

lavc: add a ProRes RAW decoder

This commit is contained in:
Lynne
2025-06-28 04:51:23 +09:00
parent 5674879db5
commit 589b3ed943
5 changed files with 595 additions and 0 deletions

1
configure vendored
View File

@@ -3090,6 +3090,7 @@ prores_decoder_select="blockdsp idctdsp"
prores_encoder_select="fdctdsp"
prores_aw_encoder_select="fdctdsp"
prores_ks_encoder_select="fdctdsp"
prores_raw_decoder_select="blockdsp idctdsp"
qcelp_decoder_select="lsp"
qdm2_decoder_select="mpegaudiodsp"
ra_144_decoder_select="audiodsp"

View File

@@ -635,6 +635,7 @@ OBJS-$(CONFIG_PRORES_DECODER) += proresdec.o proresdsp.o proresdata.o
OBJS-$(CONFIG_PRORES_ENCODER) += proresenc_anatoliy.o proresdata.o
OBJS-$(CONFIG_PRORES_AW_ENCODER) += proresenc_anatoliy.o proresdata.o
OBJS-$(CONFIG_PRORES_KS_ENCODER) += proresenc_kostya.o proresdata.o
OBJS-$(CONFIG_PRORES_RAW_DECODER) += prores_raw.o
OBJS-$(CONFIG_PRORES_VIDEOTOOLBOX_ENCODER) += videotoolboxenc.o
OBJS-$(CONFIG_PROSUMER_DECODER) += prosumer.o
OBJS-$(CONFIG_PSD_DECODER) += psd.o

View File

@@ -269,6 +269,7 @@ extern const FFCodec ff_prores_encoder;
extern const FFCodec ff_prores_decoder;
extern const FFCodec ff_prores_aw_encoder;
extern const FFCodec ff_prores_ks_encoder;
extern const FFCodec ff_prores_raw_decoder;
extern const FFCodec ff_prosumer_decoder;
extern const FFCodec ff_psd_decoder;
extern const FFCodec ff_ptx_decoder;

531
libavcodec/prores_raw.c Normal file
View File

@@ -0,0 +1,531 @@
/*
* ProRes RAW decoder
* Copyright (c) 2023-2025 Paul B Mahol
* Copyright (c) 2025 Lynne
*
* 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 "libavutil/intreadwrite.h"
#include "libavutil/mem_internal.h"
#include "libavutil/mem.h"
#define CACHED_BITSTREAM_READER !ARCH_X86_32
#include "config_components.h"
#include "avcodec.h"
#include "bytestream.h"
#include "codec_internal.h"
#include "decode.h"
#include "get_bits.h"
#include "idctdsp.h"
#include "proresdata.h"
#include "thread.h"
#include "hwconfig.h"
#include "hwaccel_internal.h"
#include "prores_raw.h"
static av_cold int decode_init(AVCodecContext *avctx)
{
ProResRAWContext *s = avctx->priv_data;
uint8_t idct_permutation[64];
avctx->bits_per_raw_sample = 12;
avctx->color_primaries = AVCOL_PRI_UNSPECIFIED;
avctx->color_trc = AVCOL_TRC_UNSPECIFIED;
avctx->colorspace = AVCOL_SPC_UNSPECIFIED;
s->pix_fmt = AV_PIX_FMT_NONE;
ff_blockdsp_init(&s->bdsp);
ff_proresdsp_init(&s->prodsp, avctx->bits_per_raw_sample);
ff_init_scantable_permutation(idct_permutation,
s->prodsp.idct_permutation_type);
ff_permute_scantable(s->scan, ff_prores_interlaced_scan, idct_permutation);
return 0;
}
static int16_t get_value(GetBitContext *gb, int16_t codebook)
{
const int16_t switch_bits = codebook >> 8;
const int16_t rice_order = codebook & 0xf;
const int16_t exp_order = (codebook >> 4) & 0xf;
int16_t q, bits;
uint32_t b = show_bits_long(gb, 32);
if (!b)
return 0;
q = ff_clz(b);
if (b & 0x80000000) {
skip_bits_long(gb, 1 + rice_order);
return (b & 0x7FFFFFFF) >> (31 - rice_order);
}
if (q <= switch_bits) {
skip_bits_long(gb, 1 + rice_order + q);
return (q << rice_order) +
(((b << (q + 1)) >> 1) >> (31 - rice_order));
}
bits = exp_order + (q << 1) - switch_bits;
skip_bits_long(gb, bits);
return (b >> (32 - bits)) +
((switch_bits + 1) << rice_order) -
(1 << exp_order);
}
#define TODCCODEBOOK(x) ((x + 1) >> 1)
static const uint8_t align_tile_w[16] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
};
#define DC_CB_MAX 12
const uint8_t ff_prores_raw_dc_cb[DC_CB_MAX + 1] = {
16, 33, 50, 51, 51, 51, 68, 68, 68, 68, 68, 68, 118,
};
#define AC_CB_MAX 94
const int16_t ff_prores_raw_ac_cb[AC_CB_MAX + 1] = {
0, 529, 273, 273, 546, 546, 546, 290, 290, 290, 563, 563,
563, 563, 563, 563, 563, 563, 307, 307, 580, 580, 580, 580,
580, 580, 580, 580, 580, 580, 580, 580, 580, 580, 580, 580,
580, 580, 580, 580, 580, 580, 853, 853, 853, 853, 853, 853,
853, 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, 853,
853, 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, 853,
853, 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, 853,
853, 853, 853, 853, 853, 853, 853, 853, 853, 853, 358
};
#define RN_CB_MAX 27
const int16_t ff_prores_raw_rn_cb[RN_CB_MAX + 1] = {
512, 256, 0, 0, 529, 529, 273, 273, 17, 17, 33, 33, 546,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 50, 50, 68,
};
#define LN_CB_MAX 14
const int16_t ff_prores_raw_ln_cb[LN_CB_MAX + 1] = {
256, 273, 546, 546, 290, 290, 1075, 1075, 563, 563, 563, 563, 563, 563, 51
};
static int decode_comp(AVCodecContext *avctx, TileContext *tile,
AVFrame *frame, const uint8_t *data, int size,
int component, int16_t *qmat)
{
int ret;
ProResRAWContext *s = avctx->priv_data;
const ptrdiff_t linesize = frame->linesize[0] >> 1;
uint16_t *dst = (uint16_t *)(frame->data[0] + tile->y*frame->linesize[0] + 2*tile->x);
int idx;
const int w = FFMIN(s->tw, avctx->width - tile->x) / 2;
const int nb_blocks = w / 8;
const int log2_nb_blocks = 31 - ff_clz(nb_blocks);
const int block_mask = (1 << log2_nb_blocks) - 1;
const int nb_codes = 64 * nb_blocks;
LOCAL_ALIGNED_32(int16_t, block, [64*16]);
int16_t sign = 0;
int16_t dc_add = 0;
int16_t dc_codebook;
int16_t ac, rn, ln;
int16_t ac_codebook = 49;
int16_t rn_codebook = 0;
int16_t ln_codebook = 66;
const uint8_t *scan = s->scan;
GetBitContext gb;
if (component > 1)
dst += linesize;
dst += component & 1;
if ((ret = init_get_bits8(&gb, data, size)) < 0)
return ret;
for (int n = 0; n < nb_blocks; n++)
s->bdsp.clear_block(block + n*64);
/* Special handling for first block */
int dc = get_value(&gb, 700);
int prev_dc = (dc >> 1) ^ -(dc & 1);
block[0] = (((dc&1) + (dc>>1) ^ -(int)(dc & 1)) + (dc & 1)) + 1;
for (int n = 1; n < nb_blocks; n++) {
if (get_bits_left(&gb) <= 0)
break;
if ((n & 15) == 1)
dc_codebook = 100;
else
dc_codebook = ff_prores_raw_dc_cb[FFMIN(TODCCODEBOOK(dc), DC_CB_MAX)];
dc = get_value(&gb, dc_codebook);
sign = sign ^ dc & 1;
dc_add = (-sign ^ TODCCODEBOOK(dc)) + sign;
sign = dc_add < 0;
prev_dc += dc_add;
block[n*64] = prev_dc + 1;
}
for (int n = nb_blocks; n <= nb_codes;) {
if (get_bits_left(&gb) <= 0)
break;
ln = get_value(&gb, ln_codebook);
for (int i = 0; i < ln; i++) {
if (get_bits_left(&gb) <= 0)
break;
if ((n + i) >= nb_codes)
break;
ac = get_value(&gb, ac_codebook);
ac_codebook = ff_prores_raw_ac_cb[FFMIN(ac, AC_CB_MAX)];
sign = -get_bits1(&gb);
idx = scan[(n + i) >> log2_nb_blocks] + (((n + i) & block_mask) << 6);
block[idx] = ((ac + 1) ^ sign) - sign;
}
n += ln;
if (n >= nb_codes)
break;
rn = get_value(&gb, rn_codebook);
rn_codebook = ff_prores_raw_rn_cb[FFMIN(rn, RN_CB_MAX)];
n += rn + 1;
if (n >= nb_codes)
break;
if (get_bits_left(&gb) <= 0)
break;
ac = get_value(&gb, ac_codebook);
sign = -get_bits1(&gb);
idx = scan[n >> log2_nb_blocks] + ((n & block_mask) << 6);
block[idx] = ((ac + 1) ^ sign) - sign;
ac_codebook = ff_prores_raw_ac_cb[FFMIN(ac, AC_CB_MAX)];
ln_codebook = ff_prores_raw_ln_cb[FFMIN(ac, LN_CB_MAX)];
n++;
}
for (int n = 0; n < nb_blocks; n++) {
uint16_t *ptr = dst + n*16;
s->prodsp.idct_put_bayer(ptr, linesize, block + n*64, qmat);
}
return 0;
}
static int decode_tile(AVCodecContext *avctx, TileContext *tile,
AVFrame *frame)
{
int ret;
ProResRAWContext *s = avctx->priv_data;
GetByteContext *gb = &tile->gb;
LOCAL_ALIGNED_32(int16_t, qmat, [64]);
if (tile->x >= avctx->width)
return 0;
/* Tile header */
int header_len = bytestream2_get_byteu(gb) >> 3;
int16_t scale = bytestream2_get_byteu(gb);
int size[4];
size[0] = bytestream2_get_be16(gb);
size[1] = bytestream2_get_be16(gb);
size[2] = bytestream2_get_be16(gb);
size[3] = bytestream2_size(gb) - size[0] - size[1] - size[2] - header_len;
if (size[3] < 0)
return AVERROR_INVALIDDATA;
for (int i = 0; i < 64; i++)
qmat[i] = s->qmat[i] * scale >> 1;
const uint8_t *comp_start = gb->buffer_start + header_len;
ret = decode_comp(avctx, tile, frame, comp_start,
size[0], 2, qmat);
if (ret < 0)
goto fail;
ret = decode_comp(avctx, tile, frame, comp_start + size[0],
size[1], 1, qmat);
if (ret < 0)
goto fail;
ret = decode_comp(avctx, tile, frame, comp_start + size[0] + size[1],
size[2], 3, qmat);
if (ret < 0)
goto fail;
ret = decode_comp(avctx, tile, frame, comp_start + size[0] + size[1] + size[2],
size[3], 0, qmat);
if (ret < 0)
goto fail;
return 0;
fail:
av_log(avctx, AV_LOG_ERROR, "tile %d/%d decoding error\n", tile->x, tile->y);
return ret;
}
static int decode_tiles(AVCodecContext *avctx, void *arg,
int n, int thread_nb)
{
ProResRAWContext *s = avctx->priv_data;
TileContext *tile = &s->tiles[n];
AVFrame *frame = arg;
return decode_tile(avctx, tile, frame);
}
static enum AVPixelFormat get_pixel_format(AVCodecContext *avctx,
enum AVPixelFormat pix_fmt)
{
enum AVPixelFormat pix_fmts[] = {
pix_fmt,
AV_PIX_FMT_NONE,
};
return ff_get_format(avctx, pix_fmts);
}
static int decode_frame(AVCodecContext *avctx,
AVFrame *frame, int *got_frame_ptr,
AVPacket *avpkt)
{
int ret;
ProResRAWContext *s = avctx->priv_data;
DECLARE_ALIGNED(32, uint8_t, qmat)[64];
memset(qmat, 1, 64);
GetByteContext gb;
bytestream2_init(&gb, avpkt->data, avpkt->size);
if (bytestream2_get_be32(&gb) != avpkt->size)
return AVERROR_INVALIDDATA;
/* ProRes RAW frame */
if (bytestream2_get_le32(&gb) != MKTAG('p','r','r','f'))
return AVERROR_INVALIDDATA;
int header_len = bytestream2_get_be16(&gb);
if (header_len < 62)
return AVERROR_INVALIDDATA;
GetByteContext gb_hdr;
bytestream2_init(&gb_hdr, gb.buffer, header_len - 2);
bytestream2_skip(&gb, header_len - 2);
bytestream2_skip(&gb_hdr, 1);
s->version = bytestream2_get_byte(&gb_hdr);
if (s->version > 1) {
avpriv_request_sample(avctx, "Version %d", s->version);
return AVERROR_PATCHWELCOME;
}
/* Vendor header (e.g. "peac" for Panasonic or "atm0" for Atmos) */
bytestream2_skip(&gb_hdr, 4);
/* Width and height must always be even */
int w = bytestream2_get_be16(&gb_hdr);
int h = bytestream2_get_be16(&gb_hdr);
if ((w & 1) || (h & 1))
return AVERROR_INVALIDDATA;
avctx->coded_width = FFALIGN(w, 16);
avctx->coded_height = FFALIGN(h, 16);
if (w != avctx->width || h != avctx->height) {
av_log(avctx, AV_LOG_WARNING, "picture resolution change: %ix%i -> %ix%i\n",
avctx->width, avctx->height, w, h);
if ((ret = ff_set_dimensions(avctx, w, h)) < 0)
return ret;
}
enum AVPixelFormat pix_fmt = AV_PIX_FMT_BAYER_RGGB16;
if (pix_fmt != s->pix_fmt) {
s->pix_fmt = pix_fmt;
ret = get_pixel_format(avctx, pix_fmt);
if (ret < 0)
return ret;
avctx->pix_fmt = ret;
}
bytestream2_skip(&gb_hdr, 1 * 4);
bytestream2_skip(&gb_hdr, 2); /* & 0x3 */
bytestream2_skip(&gb_hdr, 2);
bytestream2_skip(&gb_hdr, 4);
bytestream2_skip(&gb_hdr, 4);
bytestream2_skip(&gb_hdr, 4 * 3 * 3);
bytestream2_skip(&gb_hdr, 4);
bytestream2_skip(&gb_hdr, 2);
/* Flags */
int flags = bytestream2_get_be16(&gb_hdr);
int align = (flags >> 1) & 0x7;
/* Quantization matrix */
if (flags & 1)
bytestream2_get_buffer(&gb_hdr, qmat, 64);
if ((flags >> 4) & 1) {
bytestream2_skip(&gb_hdr, 2);
bytestream2_skip(&gb_hdr, 2 * 7);
}
ff_permute_scantable(s->qmat, s->prodsp.idct_permutation, qmat);
s->nb_tw = (w + 15) >> 4;
s->nb_th = (h + 15) >> 4;
s->nb_tw = (s->nb_tw >> align) + align_tile_w[~(-1 * (1 << align)) & s->nb_tw];
s->nb_tiles = s->nb_tw * s->nb_th;
av_log(avctx, AV_LOG_DEBUG, "%dx%d | nb_tiles: %d\n", s->nb_tw, s->nb_th, s->nb_tiles);
s->tw = s->version == 0 ? 128 : 256;
s->th = 16;
av_log(avctx, AV_LOG_DEBUG, "tile_size: %dx%d\n", s->tw, s->th);
av_fast_mallocz(&s->tiles, &s->tiles_size, s->nb_tiles * sizeof(*s->tiles));
if (!s->tiles)
return AVERROR(ENOMEM);
if (bytestream2_get_bytes_left(&gb) < s->nb_tiles * 2)
return AVERROR_INVALIDDATA;
/* Read tile data offsets */
int offset = bytestream2_tell(&gb) + s->nb_tiles * 2;
for (int n = 0; n < s->nb_tiles; n++) {
TileContext *tile = &s->tiles[n];
int size = bytestream2_get_be16(&gb);
if (offset >= avpkt->size)
return AVERROR_INVALIDDATA;
if (size >= avpkt->size)
return AVERROR_INVALIDDATA;
if (offset > avpkt->size - size)
return AVERROR_INVALIDDATA;
bytestream2_init(&tile->gb, avpkt->data + offset, size);
tile->y = (n / s->nb_tw) * s->th;
tile->x = (n % s->nb_tw) * s->tw;
offset += size;
}
ret = ff_thread_get_buffer(avctx, frame, 0);
if (ret < 0)
return ret;
s->frame = frame;
/* Start */
if (avctx->hwaccel) {
const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel);
ret = ff_hwaccel_frame_priv_alloc(avctx, &s->hwaccel_picture_private);
if (ret < 0)
return ret;
ret = hwaccel->start_frame(avctx, avpkt->buf, avpkt->data, avpkt->size);
if (ret < 0)
return ret;
for (int n = 0; n < s->nb_tiles; n++) {
TileContext *tile = &s->tiles[n];
ret = hwaccel->decode_slice(avctx, tile->gb.buffer,
tile->gb.buffer_end - tile->gb.buffer);
if (ret < 0)
return ret;
}
ret = hwaccel->end_frame(avctx);
if (ret < 0)
return ret;
av_refstruct_unref(&s->hwaccel_picture_private);
} else {
avctx->execute2(avctx, decode_tiles, frame, NULL, s->nb_tiles);
}
frame->pict_type = AV_PICTURE_TYPE_I;
frame->flags |= AV_FRAME_FLAG_KEY;
*got_frame_ptr = 1;
return avpkt->size;
}
static av_cold int decode_end(AVCodecContext *avctx)
{
ProResRAWContext *s = avctx->priv_data;
av_refstruct_unref(&s->hwaccel_picture_private);
av_freep(&s->tiles);
return 0;
}
#if HAVE_THREADS
static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src)
{
ProResRAWContext *rsrc = src->priv_data;
ProResRAWContext *rdst = dst->priv_data;
rdst->pix_fmt = rsrc->pix_fmt;
return 0;
}
#endif
const FFCodec ff_prores_raw_decoder = {
.p.name = "prores_raw",
CODEC_LONG_NAME("Apple ProRes RAW"),
.p.type = AVMEDIA_TYPE_VIDEO,
.p.id = AV_CODEC_ID_PRORES_RAW,
.priv_data_size = sizeof(ProResRAWContext),
.init = decode_init,
.close = decode_end,
FF_CODEC_DECODE_CB(decode_frame),
UPDATE_THREAD_CONTEXT(update_thread_context),
.p.capabilities = AV_CODEC_CAP_DR1 |
AV_CODEC_CAP_FRAME_THREADS |
AV_CODEC_CAP_SLICE_THREADS,
.caps_internal = FF_CODEC_CAP_INIT_CLEANUP |
FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM,
.hw_configs = (const AVCodecHWConfigInternal *const []) {
NULL
},
};

61
libavcodec/prores_raw.h Normal file
View File

@@ -0,0 +1,61 @@
/*
* ProRes RAW decoder
* Copyright (c) 2025 Lynne
*
* 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
*/
#ifndef AVCODEC_PRORES_RAW_H
#define AVCODEC_PRORES_RAW_H
#include "libavutil/mem_internal.h"
#include "bytestream.h"
#include "blockdsp.h"
#include "proresdsp.h"
typedef struct TileContext {
GetByteContext gb;
unsigned x, y;
} TileContext;
typedef struct ProResRAWContext {
ProresDSPContext prodsp;
BlockDSPContext bdsp;
TileContext *tiles;
unsigned int tiles_size;
int nb_tiles;
int tw, th;
int nb_tw, nb_th;
enum AVPixelFormat pix_fmt;
AVFrame *frame;
void *hwaccel_picture_private;
int version;
DECLARE_ALIGNED(32, uint8_t, scan)[64];
DECLARE_ALIGNED(32, uint8_t, qmat)[64];
} ProResRAWContext;
extern const uint8_t ff_prores_raw_dc_cb[13];
extern const int16_t ff_prores_raw_ac_cb[95];
extern const int16_t ff_prores_raw_rn_cb[28];
extern const int16_t ff_prores_raw_ln_cb[15];
#endif /* AVCODEC_PRORES_RAW_H */