mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2026-10-24 02:09:03 +03:00
Supports RawFormat 8, used by the S1II, S5II and GH6. Outputs the Bayer mosaic, with the sensor borders as cropping and the black and white levels as raw color params side data. The as-shot white balance (tags 0x24 - 0x26) is exported as AVRW2RawColorParams, and the TIFF and EXIF tags of IFD0 and its sub-IFDs as EXIF side data, gathered during the single header walk and attached without re-parsing. The orientation becomes a display matrix; Panasonic's raw parameters, the previews and the strip pointers are left out. The whole file is given as one packet. Bit-exact with LibRaw on 801 S1II files, at 3x its speed per thread.
625 lines
20 KiB
C
625 lines
20 KiB
C
/*
|
|
* Panasonic RW2 raw image decoder
|
|
* Copyright (c) 2026 Lynne <dev@lynne.ee>
|
|
*
|
|
* This file is part of FFmpeg.
|
|
*
|
|
* FFmpeg is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* FFmpeg is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with FFmpeg; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* Panasonic RW2 decoder, RawFormat 8 only (S1II, S5II, GH6).
|
|
* Bit-exact with LibRaw's pana8.cpp, quirks included.
|
|
*
|
|
* Input is the whole file as one packet.
|
|
*/
|
|
|
|
#include "libavutil/internal.h"
|
|
#include "libavutil/buffer.h"
|
|
#include "libavutil/mem.h"
|
|
#include "libavutil/raw_color_params.h"
|
|
#include "libavutil/reverse.h"
|
|
|
|
#define BITSTREAM_READER_LE
|
|
#define CACHED_BITSTREAM_READER 1
|
|
#include "avcodec.h"
|
|
#include "bytestream.h"
|
|
#include "codec_internal.h"
|
|
#include "decode.h"
|
|
#include "exif.h"
|
|
#include "exif_internal.h"
|
|
#include "get_bits.h"
|
|
#include "hwaccel_internal.h"
|
|
#include "thread.h"
|
|
#include "tiff_common.h"
|
|
|
|
#include "rw2.h"
|
|
|
|
#define RW2_VLC_BITS 11
|
|
#define RW2_JOINT_BITS 16 /* longest code with its magnitude folded in */
|
|
#define RW2_SYM_DELTA 0x2000 /* joint symbols: delta + RW2_SYM_DELTA */
|
|
#define RW2_SYM_ESC 0x4000 /* other categories: RW2_SYM_ESC + category */
|
|
|
|
static av_always_inline int get_delta(const RW2DecContext *s,
|
|
GetBitContext *bc, int *delta)
|
|
{
|
|
int sym = get_vlc2(bc, s->vlc.table, RW2_VLC_BITS, 2);
|
|
if (sym < RW2_SYM_ESC) {
|
|
if (sym < 0)
|
|
return AVERROR_INVALIDDATA;
|
|
*delta = sym - RW2_SYM_DELTA;
|
|
return 0;
|
|
}
|
|
|
|
/* Escapes read k - shift magnitude bits, MSB-first, the sign leading */
|
|
int k = sym - RW2_SYM_ESC;
|
|
int shift = s->h.c.code_shift[k], extra = k - shift;
|
|
int d = 0;
|
|
if (extra > 0) {
|
|
unsigned v = get_bits(bc, extra);
|
|
unsigned m = (ff_reverse[v & 0xFF] << 8 | ff_reverse[v >> 8]) >> (16 - extra);
|
|
d = (m << shift) & 0xFFFF;
|
|
if (!(v & 1))
|
|
d += !shift - (1 << k);
|
|
} else if (k && !show_bits(bc, 1)) {
|
|
/* No magnitude, but LibRaw still tests the next bit for the sign */
|
|
d = !shift - (1 << k);
|
|
}
|
|
|
|
*delta = d + (shift ? 1 << (shift - 1) : 0);
|
|
return 0;
|
|
}
|
|
|
|
static int gamma_curve(const RW2Coding *c, uint32_t v)
|
|
{
|
|
int seg = 0;
|
|
while (seg < 5 && v >= c->gamma_point[seg + 1])
|
|
seg++;
|
|
|
|
/* LibRaw only keeps the second half of each 0x3A pair, which makes the
|
|
* output offset of every segment 0 */
|
|
uint32_t sh = c->gamma_shift[seg];
|
|
uint32_t r = v - c->gamma_point[seg];
|
|
if ((sh & 0x1F) == 31)
|
|
r = seg == 5 ? 0xFFFF : 0;
|
|
else if (sh & 0x10)
|
|
r <<= sh & 0xF;
|
|
else if ((sh & 0x1F) == 15)
|
|
r = 0;
|
|
else if (sh & 0x1F)
|
|
r = (r + (1 << ((sh & 0x1F) - 1))) >> (sh & 0x1F);
|
|
|
|
return FFMIN(r, c->datamax);
|
|
}
|
|
|
|
static int build_tables(RW2DecContext *s)
|
|
{
|
|
const RW2Coding *c = &s->h.c;
|
|
int nb = 0;
|
|
|
|
/* A code per magnitude where both fit and nothing is shifted, the rest escape */
|
|
for (int k = 0; k < 17; k++) {
|
|
int len = c->code_len[k], code = c->code_val[k];
|
|
if (!len)
|
|
continue;
|
|
if (c->code_shift[k] || k >= RW2_JOINT_CATS || len + k > RW2_JOINT_BITS) {
|
|
s->codes[nb++] = (RW2Code){ len, code, RW2_SYM_ESC + k };
|
|
continue;
|
|
}
|
|
for (int m = 0; m < (1 << k); m++) {
|
|
int d = k && !(m >> (k - 1)) ? m + 1 - (1 << k) : m;
|
|
s->codes[nb++] = (RW2Code){ len + k, code << k | m, RW2_SYM_DELTA + d };
|
|
}
|
|
}
|
|
|
|
ff_vlc_free(&s->vlc);
|
|
int ret = ff_vlc_init_sparse(&s->vlc, RW2_VLC_BITS, nb,
|
|
&s->codes[0].len, sizeof(*s->codes), 1,
|
|
&s->codes[0].code, sizeof(*s->codes), 2,
|
|
&s->codes[0].sym, sizeof(*s->codes), 2,
|
|
VLC_INIT_OUTPUT_LE);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* The gamma LUT, an identity on every file seen so far */
|
|
s->use_gamma = 0;
|
|
for (int i = 0; i <= c->datamax; i++) {
|
|
s->gamma[i] = gamma_curve(c, i);
|
|
s->use_gamma |= s->gamma[i] != i;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int decode_stripe(AVCodecContext *avctx, void *tdata,
|
|
int jobnr, int threadnr)
|
|
{
|
|
RW2DecContext *s = avctx->priv_data;
|
|
AVFrame *frame = tdata;
|
|
const RW2Stripe *st = &s->h.stripes[jobnr];
|
|
const ptrdiff_t stride = frame->linesize[0];
|
|
const uint16_t *gamma = s->use_gamma ? s->gamma : NULL;
|
|
const int datamax = s->h.c.datamax;
|
|
int line_base[4], cur[4];
|
|
GetBitContext bc;
|
|
int ret;
|
|
|
|
ret = init_get_bits(&bc, s->payload + st->offset, st->size_bits);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
line_base[i] = s->h.c.initial[i];
|
|
|
|
for (int y = 0; y < st->height; y += 2) {
|
|
uint16_t *dst0 = (uint16_t *)(frame->data[0] + y*stride) + st->left;
|
|
uint16_t *dst1 = (uint16_t *)((uint8_t *)dst0 + stride);
|
|
|
|
/* Each sample predicts from the same position in the previous 2x2 */
|
|
memcpy(cur, line_base, sizeof(cur));
|
|
for (int x = 0; x < st->width; x += 2) {
|
|
/* Coding order: top-left, bottom-left, top-right, bottom-right */
|
|
static const uint8_t slot[4] = { 0, 2, 1, 3 };
|
|
for (int i = 0; i < 4; i++) {
|
|
int delta, j = slot[i];
|
|
if ((ret = get_delta(s, &bc, &delta)) < 0)
|
|
return ret;
|
|
cur[j] = av_clip(cur[j] + delta, 0, datamax);
|
|
}
|
|
|
|
if (gamma) {
|
|
dst0[x] = gamma[cur[0]]; dst0[x + 1] = gamma[cur[1]];
|
|
dst1[x] = gamma[cur[2]]; dst1[x + 1] = gamma[cur[3]];
|
|
} else {
|
|
dst0[x] = cur[0]; dst0[x + 1] = cur[1];
|
|
dst1[x] = cur[2]; dst1[x + 1] = cur[3];
|
|
}
|
|
|
|
if (!x)
|
|
memcpy(line_base, cur, sizeof(line_base));
|
|
}
|
|
|
|
if (get_bits_left(&bc) < 0)
|
|
return AVERROR_INVALIDDATA;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int read_array_count(GetByteContext *gb, unsigned type, unsigned count,
|
|
unsigned expected, int max)
|
|
{
|
|
int n;
|
|
if (type != AV_TIFF_UNDEFINED || count != expected)
|
|
return 0;
|
|
n = bytestream2_get_le16(gb);
|
|
return FFMIN(n, max);
|
|
}
|
|
|
|
/* Tag layouts and clamps follow LibRaw */
|
|
static int parse_header(RW2Header *h, const uint8_t *data, int size)
|
|
{
|
|
RW2Coding *c = &h->c;
|
|
GetByteContext gb;
|
|
|
|
memset(h, 0, sizeof(*h));
|
|
|
|
bytestream2_init(&gb, data, size);
|
|
if (bytestream2_get_le32(&gb) != MKTAG('I', 'I', 'U', '\0'))
|
|
return AVERROR_INVALIDDATA;
|
|
int64_t ifd0 = bytestream2_get_le32(&gb);
|
|
if (ifd0 < 8 || ifd0 + 2 > size)
|
|
return AVERROR_INVALIDDATA;
|
|
bytestream2_seek(&gb, ifd0, SEEK_SET);
|
|
|
|
int nb_entries = bytestream2_get_le16(&gb);
|
|
if (nb_entries > RW2_MAX_IFD0_ENTRIES || bytestream2_get_bytes_left(&gb) < nb_entries*12)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
int64_t ifd_end = ifd0 + 2 + 12*(int64_t)nb_entries + 4;
|
|
h->exif_off = ifd0;
|
|
h->exif_end = size;
|
|
|
|
for (int i = 0; i < nb_entries; i++) {
|
|
unsigned tag, type, count, n, v;
|
|
int next;
|
|
|
|
if (ff_tread_tag(&gb, 1, &tag, &type, &count, &next) < 0)
|
|
goto next;
|
|
|
|
/* Keep the standard tags verbatim for EXIF in this same walk, leaving
|
|
* out the Panasonic-private ones and the orientation that is exported
|
|
* as a display matrix; the previews mark where the metadata ends */
|
|
const uint8_t *rec = data + ifd0 + 2 + 12*i;
|
|
if ((tag == 0x2E || tag == 0x111 || tag == 0x127) && AV_RL32(rec + 8) >= ifd_end)
|
|
h->exif_end = FFMIN(h->exif_end, (int64_t)AV_RL32(rec + 8));
|
|
if (tag >= 0x100 && tag != 0x111 && tag != 0x112 && tag != 0x117 &&
|
|
!(tag >= 0x118 && tag <= 0x127)) {
|
|
memcpy(h->exif + h->exif_count*12, rec, 12);
|
|
h->exif_count++;
|
|
}
|
|
|
|
switch (tag) {
|
|
case 0x02: h->raw_width = ff_tget(&gb, type, 1); break;
|
|
case 0x03: h->raw_height = ff_tget(&gb, type, 1); break;
|
|
case 0x04: case 0x05: case 0x06: case 0x07:
|
|
h->border[tag - 0x04] = ff_tget(&gb, type, 1);
|
|
break;
|
|
case 0x09: h->cfa = ff_tget(&gb, type, 1); break;
|
|
case 0x0A: h->bpp = ff_tget(&gb, type, 1); break;
|
|
case 0x0E: case 0x0F: case 0x10:
|
|
h->linearity[tag - 0x0E] = ff_tget(&gb, type, 1);
|
|
break;
|
|
case 0x1C: case 0x1D: case 0x1E:
|
|
h->black[tag - 0x1C] = ff_tget(&gb, type, 1);
|
|
break;
|
|
case 0x24: case 0x25: case 0x26:
|
|
h->wb[tag - 0x24] = ff_tget(&gb, type, 1);
|
|
break;
|
|
case 0x2D: h->raw_format = ff_tget(&gb, type, 1); break;
|
|
case 0x39:
|
|
n = read_array_count(&gb, type, count, 26, 6);
|
|
for (int j = 0; j < n; j++)
|
|
c->gamma_shift[j] = bytestream2_get_le32(&gb);
|
|
break;
|
|
case 0x3A:
|
|
n = read_array_count(&gb, type, count, 26, 6);
|
|
for (int j = 0; j < n; j++) {
|
|
bytestream2_skip(&gb, 2);
|
|
c->gamma_point[j] = bytestream2_get_le16(&gb);
|
|
}
|
|
break;
|
|
case 0x3B: c->datamax = ff_tget(&gb, type, 1) & 0xFFFF; break;
|
|
case 0x3C: case 0x3D: case 0x3E: case 0x3F:
|
|
c->initial[tag - 0x3C] = ff_tget(&gb, type, 1) & 0xFFFF;
|
|
break;
|
|
case 0x40:
|
|
n = read_array_count(&gb, type, count, 70, 17);
|
|
for (int k = 0; k < n; k++) {
|
|
v = bytestream2_get_le16(&gb);
|
|
int len = FFMIN(v, 16);
|
|
v = bytestream2_get_le16(&gb);
|
|
int code = FFMIN(v, 0xFFF) & ((1 << len) - 1);
|
|
|
|
/* LibRaw takes the first match, drop the codes it never reaches */
|
|
for (int j = 0; j < k && len; j++)
|
|
if (c->code_len[j] && c->code_len[j] <= len &&
|
|
code >> (len - c->code_len[j]) == c->code_val[j])
|
|
len = 0;
|
|
c->code_len[k] = len;
|
|
c->code_val[k] = code;
|
|
}
|
|
break;
|
|
case 0x41:
|
|
n = read_array_count(&gb, type, count, 36, 17);
|
|
for (int j = 0; j < n; j++) {
|
|
v = bytestream2_get_le16(&gb);
|
|
c->code_shift[j] = FFMIN(v, 64) & 0x1F;
|
|
}
|
|
break;
|
|
case 0x42:
|
|
v = ff_tget(&gb, type, 1);
|
|
h->nb_stripes = FFMIN(v, RW2_MAX_STRIPES);
|
|
break;
|
|
case 0x44: case 0x45: case 0x46:
|
|
n = read_array_count(&gb, type, count, 50, RW2_MAX_STRIPES);
|
|
for (int j = 0; j < n; j++) {
|
|
v = bytestream2_get_le32(&gb);
|
|
if (tag == 0x44)
|
|
h->stripes[j].offset = v;
|
|
else if (tag == 0x45)
|
|
h->stripes[j].left = v;
|
|
else
|
|
h->stripes[j].size_bits = v;
|
|
}
|
|
break;
|
|
case 0x47: case 0x48:
|
|
n = read_array_count(&gb, type, count, 26, RW2_MAX_STRIPES);
|
|
for (int j = 0; j < n; j++) {
|
|
v = bytestream2_get_le16(&gb);
|
|
if (tag == 0x47)
|
|
h->stripes[j].width = v;
|
|
else
|
|
h->stripes[j].height = v;
|
|
}
|
|
break;
|
|
case 0x112: h->orientation = ff_tget(&gb, type, 1); break;
|
|
}
|
|
|
|
next:
|
|
bytestream2_seek(&gb, next, SEEK_SET);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int check_stripes(AVCodecContext *avctx, const RW2Header *h,
|
|
int payload_size)
|
|
{
|
|
uint32_t left = 0;
|
|
|
|
if (h->nb_stripes < 1)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
/* Stripes must tile the image exactly */
|
|
for (int i = 0; i < h->nb_stripes; i++) {
|
|
const RW2Stripe *st = &h->stripes[i];
|
|
if (st->left != left || st->height != h->raw_height ||
|
|
!st->width || (st->width & 1) || st->width > h->raw_width - left ||
|
|
!st->size_bits || st->size_bits > INT_MAX - 7 ||
|
|
st->offset > payload_size ||
|
|
(st->size_bits + 7) >> 3 > payload_size - st->offset) {
|
|
av_log(avctx, AV_LOG_ERROR, "Invalid stripe %d\n", i);
|
|
return AVERROR_INVALIDDATA;
|
|
}
|
|
left += st->width;
|
|
}
|
|
|
|
if (left != h->raw_width)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
return 0;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/* The TIFF and EXIF tags of the file. Panasonic's own tags, below 0x100 and
|
|
* at 0x118 - 0x127, hold the raw parameters exported as side data and the
|
|
* previews; the strips point into the file. */
|
|
static int export_exif(AVCodecContext *avctx, AVFrame *frame,
|
|
const uint8_t *data, int size)
|
|
{
|
|
const RW2Header *h = &((RW2DecContext *)avctx->priv_data)->h;
|
|
int64_t off = h->exif_off, keep = FFMIN(h->exif_end, size);
|
|
|
|
if (!h->exif_count || off + 2 + (int64_t)h->exif_count*12 + 4 > keep)
|
|
return 0;
|
|
|
|
/* The metadata precedes the embedded previews, so attach it whole, pointing
|
|
* IFD0 at the standard tags gathered during the header walk */
|
|
uint8_t *buf = av_memdup(data, keep);
|
|
if (!buf)
|
|
return AVERROR(ENOMEM);
|
|
|
|
AV_WL16(buf + 2, 42); /* "IIU\0" -> TIFF */
|
|
AV_WL16(buf + off, h->exif_count);
|
|
memcpy(buf + off + 2, h->exif, h->exif_count * 12);
|
|
AV_WL32(buf + off + 2 + h->exif_count*12, 0); /* no next IFD */
|
|
|
|
AVBufferRef *bref = av_buffer_create(buf, keep, NULL, NULL, 0);
|
|
if (!bref) {
|
|
av_free(buf);
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
|
|
return ff_frame_new_side_data_from_buf(avctx, frame, AV_FRAME_DATA_EXIF, &bref);
|
|
}
|
|
|
|
static int export_metadata(AVCodecContext *avctx, AVFrame *frame,
|
|
const uint8_t *hdr, int hdr_size)
|
|
{
|
|
RW2DecContext *s = avctx->priv_data;
|
|
const RW2Header *h = &s->h;
|
|
int ret;
|
|
|
|
/* Even crops keep the CFA phase */
|
|
if (h->border[0] >= 0 && h->border[1] >= 0 &&
|
|
h->border[0] < h->border[2] && h->border[2] <= h->raw_height &&
|
|
h->border[1] < h->border[3] && h->border[3] <= h->raw_width) {
|
|
frame->crop_top = h->border[0] & ~1;
|
|
frame->crop_left = h->border[1] & ~1;
|
|
frame->crop_bottom = (h->raw_height - h->border[2]) & ~1;
|
|
frame->crop_right = (h->raw_width - h->border[3]) & ~1;
|
|
}
|
|
|
|
if (h->orientation > 1 && h->orientation <= 8) {
|
|
AVFrameSideData *sd;
|
|
ret = ff_frame_new_side_data(avctx, frame, AV_FRAME_DATA_DISPLAYMATRIX,
|
|
sizeof(int32_t) * 9, &sd);
|
|
if (ret < 0)
|
|
return ret;
|
|
if (sd)
|
|
av_exif_orientation_to_matrix((int32_t *)sd->data, h->orientation);
|
|
}
|
|
|
|
/* Per-channel black levels are not representable, use green's */
|
|
int white = FFMAX3(h->linearity[0], h->linearity[1], h->linearity[2]);
|
|
if (!white)
|
|
white = (1 << h->bpp) - 1;
|
|
|
|
AVRawColorParams *rcp = av_raw_color_params_create_side_data(frame);
|
|
if (!rcp)
|
|
return AVERROR(ENOMEM);
|
|
rcp->black_level = av_make_q(h->black[1], 65535);
|
|
rcp->white_level = av_make_q(white, 65535);
|
|
|
|
if (h->wb[1]) {
|
|
rcp->type = AV_RAW_COLOR_PARAMS_RW2;
|
|
rcp->codec.rw2.wb_red = av_make_q(h->wb[0], h->wb[1]);
|
|
rcp->codec.rw2.wb_blue = av_make_q(h->wb[2], h->wb[1]);
|
|
}
|
|
|
|
return export_exif(avctx, frame, hdr, hdr_size);
|
|
}
|
|
|
|
static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
|
|
int *got_frame, AVPacket *avpkt)
|
|
{
|
|
RW2DecContext *s = avctx->priv_data;
|
|
RW2Header *h = &s->h;
|
|
int ret;
|
|
|
|
static const enum AVPixelFormat cfa_fmts[] = {
|
|
AV_PIX_FMT_BAYER_RGGB16, AV_PIX_FMT_BAYER_GRBG16,
|
|
AV_PIX_FMT_BAYER_GBRG16, AV_PIX_FMT_BAYER_BGGR16,
|
|
};
|
|
|
|
ret = parse_header(h, avpkt->data, avpkt->size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (h->raw_format != 8) {
|
|
if (!h->raw_format)
|
|
return AVERROR_INVALIDDATA;
|
|
avpriv_request_sample(avctx, "RawFormat %d", h->raw_format);
|
|
return AVERROR_PATCHWELCOME;
|
|
}
|
|
|
|
if (h->cfa < 1 || h->cfa > 4 || h->bpp < 8 || h->bpp > 16 ||
|
|
(h->raw_width & 1) || (h->raw_height & 1))
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
ret = ff_set_dimensions(avctx, h->raw_width, h->raw_height);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
s->payload = avpkt->data;
|
|
|
|
ret = check_stripes(avctx, h, avpkt->size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (cfa_fmts[h->cfa - 1] != s->pix_fmt) {
|
|
s->pix_fmt = cfa_fmts[h->cfa - 1];
|
|
ret = get_pixel_format(avctx, s->pix_fmt);
|
|
if (ret < 0)
|
|
return ret;
|
|
avctx->pix_fmt = ret;
|
|
}
|
|
avctx->bits_per_raw_sample = h->bpp;
|
|
|
|
if (avctx->skip_frame >= AVDISCARD_ALL)
|
|
return avpkt->size;
|
|
|
|
ret = ff_thread_get_buffer(avctx, frame, 0);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
s->frame = frame;
|
|
|
|
ret = ff_hwaccel_frame_priv_alloc(avctx, &s->hwaccel_picture_private);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ff_thread_finish_setup(avctx);
|
|
|
|
if (avctx->hwaccel) {
|
|
const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel);
|
|
|
|
ret = hwaccel->start_frame(avctx, avpkt->buf, avpkt->data, avpkt->size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
for (int i = 0; i < h->nb_stripes; i++) {
|
|
const RW2Stripe *st = &h->stripes[i];
|
|
ret = hwaccel->decode_slice(avctx, s->payload + st->offset,
|
|
(st->size_bits + 7) >> 3);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
ret = hwaccel->end_frame(avctx);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
av_refstruct_unref(&s->hwaccel_picture_private);
|
|
} else {
|
|
int rets[RW2_MAX_STRIPES];
|
|
|
|
ret = build_tables(s);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
avctx->execute2(avctx, decode_stripe, frame, rets, h->nb_stripes);
|
|
for (int i = 0; i < h->nb_stripes; i++) {
|
|
if (rets[i] < 0) {
|
|
av_log(avctx, AV_LOG_ERROR, "Error decoding stripe %d\n", i);
|
|
return rets[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
frame->pict_type = AV_PICTURE_TYPE_I;
|
|
frame->flags |= AV_FRAME_FLAG_KEY;
|
|
|
|
ret = export_metadata(avctx, frame, avpkt->data, avpkt->size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
*got_frame = 1;
|
|
|
|
return avpkt->size;
|
|
}
|
|
|
|
#if HAVE_THREADS
|
|
static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src)
|
|
{
|
|
RW2DecContext *ssrc = src->priv_data;
|
|
RW2DecContext *sdst = dst->priv_data;
|
|
|
|
sdst->pix_fmt = ssrc->pix_fmt;
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static av_cold int decode_init(AVCodecContext *avctx)
|
|
{
|
|
RW2DecContext *s = avctx->priv_data;
|
|
|
|
avctx->color_trc = AVCOL_TRC_LINEAR;
|
|
avctx->color_primaries = AVCOL_PRI_UNSPECIFIED;
|
|
avctx->colorspace = AVCOL_SPC_UNSPECIFIED;
|
|
|
|
s->pix_fmt = AV_PIX_FMT_NONE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static av_cold int decode_end(AVCodecContext *avctx)
|
|
{
|
|
RW2DecContext *s = avctx->priv_data;
|
|
av_refstruct_unref(&s->hwaccel_picture_private);
|
|
ff_vlc_free(&s->vlc);
|
|
return 0;
|
|
}
|
|
|
|
const FFCodec ff_rw2_decoder = {
|
|
.p.name = "rw2",
|
|
CODEC_LONG_NAME("Panasonic RW2"),
|
|
.p.type = AVMEDIA_TYPE_VIDEO,
|
|
.p.id = AV_CODEC_ID_RW2,
|
|
.priv_data_size = sizeof(RW2DecContext),
|
|
.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,
|
|
};
|