From patchwork Thu Sep 24 11:59:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 22569 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 600F344A6E8 for ; Thu, 24 Sep 2020 14:59:23 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 36BFB68B8C5; Thu, 24 Sep 2020 14:59:23 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-ej1-f68.google.com (mail-ej1-f68.google.com [209.85.218.68]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 0D79D68B3F2 for ; Thu, 24 Sep 2020 14:59:17 +0300 (EEST) Received: by mail-ej1-f68.google.com with SMTP id gx22so4093713ejb.5 for ; Thu, 24 Sep 2020 04:59:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id; bh=iHrOLnWxmtveJOBSCsSHT9Uw27nMnNTPZ4M4JP78UGM=; b=HQ7pnRuGWQgaJ//SBLemyhl0R2VNiwFOJ0i9UdjIqwtpp/Zt2o8kcCUclFqCT3+zU6 C7Hv1Ao8iiiP4b+zaM1MJdv79rl0X967rgq9oMw73Pzib2gZPUVQXM221jFYlR+5h3/m 3eCg/QU9f36txhUCXMkY01i8MXr8wqdico78OqaAu+J9cCpQQPHIm7sd6mg2zU4Tv0oB FvPFknh28AMRfXbuBzpY/vxv6nL3lf/LZCM1GCIcw+PSPmjuOqprH/PzxvMRYPNNGCDn E8It/TuVyKu3hSnaHxGM9ImiQAtxQMY5U6MXxXxyFypJms5w9QsRYhV1Zu/n6ORF16P+ d+Vw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id; bh=iHrOLnWxmtveJOBSCsSHT9Uw27nMnNTPZ4M4JP78UGM=; b=JMIwdyx39gLeFsXGvg4f3qNlctSN62ZZjzr8xNVOJuBOUS7yt1SaUJ+Fh+M1a6ZMhq tgoHz6IAI2t+Jc5e9QYjvxrGcvDxWgwUqmiL2dR5rNJkW3B95FUxwTQFsUl9wrCGEeWs zfDlvPdslPsBc4M7SG2Ryb4aQYqrqvRuovN3ysN4zVafSfbpz7NqVOOIkdIntQJCDAAI gl6i5S04dwJNwzXi/Z8vQ1940D2yyV6MLN6lu3XdsUraKnc++Y900heOrzKljW+xMKJP m4VdVvit5f3S9Vnp1YvTegFKYaVhyJxuRzYYBidhchJPfjuA3oBKDSIH/D5vn+y2lZcD w0/w== X-Gm-Message-State: AOAM531G+Z9DQQsAx6WUPUotkiHl8ySaDpeEXir1gty3U5tBktVV9M1E cnUlSpPmIMdBfhhs5UgitQ3JDph2KdpBag== X-Google-Smtp-Source: ABdhPJww08Ha0Uu4g+mhvGKGpFjx9qraRNIo21cwzcicalmuNugAuKRsbBFWYZYgzwH9IJSrA0k+sw== X-Received: by 2002:a17:906:7f06:: with SMTP id d6mr563082ejr.553.1600948755815; Thu, 24 Sep 2020 04:59:15 -0700 (PDT) Received: from localhost.localdomain ([94.250.162.52]) by smtp.gmail.com with ESMTPSA id w5sm1635302eju.41.2020.09.24.04.59.14 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Sep 2020 04:59:15 -0700 (PDT) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Thu, 24 Sep 2020 13:59:06 +0200 Message-Id: <20200924115906.973-1-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 Subject: [FFmpeg-devel] [PATCH] avcodec: add Argonaut Games Video decoder X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Paul B Mahol --- libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/argo.c | 739 ++++++++++++++++++++++++++++++++++++++++ libavcodec/codec_desc.c | 7 + libavcodec/codec_id.h | 1 + libavformat/argo_brp.c | 14 +- 6 files changed, 751 insertions(+), 12 deletions(-) create mode 100644 libavcodec/argo.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 9b120a6f64..2f567c5074 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -210,6 +210,7 @@ OBJS-$(CONFIG_APTX_HD_ENCODER) += aptxenc.o aptx.o OBJS-$(CONFIG_APNG_DECODER) += png.o pngdec.o pngdsp.o OBJS-$(CONFIG_APNG_ENCODER) += png.o pngenc.o OBJS-$(CONFIG_ARBC_DECODER) += arbc.o +OBJS-$(CONFIG_ARGO_DECODER) += argo.o OBJS-$(CONFIG_SSA_DECODER) += assdec.o ass.o OBJS-$(CONFIG_SSA_ENCODER) += assenc.o ass.o OBJS-$(CONFIG_ASS_DECODER) += assdec.o ass.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 17c88d6290..1d1416fa8d 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -43,6 +43,7 @@ extern AVCodec ff_ansi_decoder; extern AVCodec ff_apng_encoder; extern AVCodec ff_apng_decoder; extern AVCodec ff_arbc_decoder; +extern AVCodec ff_argo_decoder; extern AVCodec ff_asv1_encoder; extern AVCodec ff_asv1_decoder; extern AVCodec ff_asv2_encoder; diff --git a/libavcodec/argo.c b/libavcodec/argo.c new file mode 100644 index 0000000000..8794095484 --- /dev/null +++ b/libavcodec/argo.c @@ -0,0 +1,739 @@ +/* + * Argonaut Games Video decoder + * Copyright (c) 2020 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 +#include +#include + +#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 ArgoContext { + GetByteContext gb; + + int bpp; + int key; + int mv0[128][2]; + int mv1[16][2]; + uint32_t pal[256]; + AVFrame *frame; +} ArgoContext; + +static int decode_pal8(AVCodecContext *avctx, uint32_t *pal) +{ + ArgoContext *s = avctx->priv_data; + GetByteContext *gb = &s->gb; + int start, count; + + start = bytestream2_get_le16(gb); + count = bytestream2_get_le16(gb); + + if (start + count > 256) + return AVERROR_INVALIDDATA; + + if (bytestream2_get_bytes_left(gb) < 3 * count) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < count; i++) + pal[start + i] = (0xFF << 24U) | bytestream2_get_be24u(gb); + + return 0; +} + +static int decode_avcf(AVCodecContext *avctx, AVFrame *frame) +{ + ArgoContext *s = avctx->priv_data; + GetByteContext *gb = &s->gb; + const int l = frame->linesize[0]; + const uint8_t *map = gb->buffer; + uint8_t *dst = frame->data[0]; + + if (bytestream2_get_bytes_left(gb) < 1024 + (frame->width / 2) * (frame->height / 2)) + return AVERROR_INVALIDDATA; + + bytestream2_skipu(gb, 1024); + for (int y = 0; y < frame->height; y += 2) { + for (int x = 0; x < frame->width; x += 2) { + int index = bytestream2_get_byteu(gb); + const uint8_t *block = map + index * 4; + + dst[x+0] = block[0]; + dst[x+1] = block[1]; + dst[x+l] = block[2]; + dst[x+l+1] = block[3]; + } + + dst += frame->linesize[0] * 2; + } + + return 0; +} + +static int decode_alcd(AVCodecContext *avctx, AVFrame *frame) +{ + ArgoContext *s = avctx->priv_data; + GetByteContext *gb = &s->gb; + GetByteContext sb; + const int l = frame->linesize[0]; + const uint8_t *map = gb->buffer; + uint8_t *dst = frame->data[0]; + uint8_t codes = 0; + int count = 0; + + if (bytestream2_get_bytes_left(gb) < 1024 + (((frame->width / 2) * (frame->height / 2) + 7) >> 3)) + return AVERROR_INVALIDDATA; + + bytestream2_skipu(gb, 1024); + sb = *gb; + bytestream2_skipu(gb, ((frame->width / 2) * (frame->height / 2) + 7) >> 3); + + for (int y = 0; y < frame->height; y += 2) { + for (int x = 0; x < frame->width; x += 2) { + const uint8_t *block; + int index; + + if (count == 0) { + codes = bytestream2_get_byteu(&sb); + count = 8; + } + + if (codes & 0x80) { + index = bytestream2_get_byte(gb); + block = map + index * 4; + + dst[x+0] = block[0]; + dst[x+1] = block[1]; + dst[x+l] = block[2]; + dst[x+l+1] = block[3]; + } + + codes <<= 1; + count--; + } + + dst += frame->linesize[0] * 2; + } + + return 0; +} + +static int decode_mad1(AVCodecContext *avctx, AVFrame *frame) +{ + ArgoContext *s = avctx->priv_data; + GetByteContext *gb = &s->gb; + const int w = frame->width; + const int h = frame->height; + const int l = frame->linesize[0]; + + while (bytestream2_get_bytes_left(gb) > 0) { + int size, type, pos, dy; + uint8_t *dst; + + type = bytestream2_get_byte(gb); + if (type == 0xFF) + break; + + switch (type) { + case 8: + dst = frame->data[0]; + for (int y = 0; y < h; y += 8) { + for (int x = 0; x < w; x += 8) { + int fill = bytestream2_get_byte(gb); + uint8_t *ddst = dst + x; + + for (int by = 0; by < 8; by++) { + memset(ddst, fill, 8); + ddst += l; + } + } + + dst += 8 * l; + } + break; + case 7: + while (bytestream2_get_bytes_left(gb) > 0) { + int bsize = bytestream2_get_byte(gb); + uint8_t *src; + int count; + + if (!bsize) + break; + + count = bytestream2_get_be16(gb); + while (count > 0) { + int mvx, mvy, a, b, c, mx, my; + int bsize_w, bsize_h; + + bsize_w = bsize_h = bsize; + if (bytestream2_get_bytes_left(gb) < 4) + return AVERROR_INVALIDDATA; + mvx = bytestream2_get_byte(gb) * bsize; + mvy = bytestream2_get_byte(gb) * bsize; + a = bytestream2_get_byte(gb); + b = bytestream2_get_byte(gb); + c = ((a & 0x3F) << 8) + b; + mx = mvx + (c & 0x7F) - 64; + my = mvy + (c >> 7) - 64; + + if (mvy < 0 || mvy >= h) + return AVERROR_INVALIDDATA; + + if (mvx < 0 || mvx >= w) + return AVERROR_INVALIDDATA; + + if (my < 0 || my >= h) + return AVERROR_INVALIDDATA; + + if (mx < 0 || mx >= w) + return AVERROR_INVALIDDATA; + + dst = frame->data[0] + mvx + l * mvy; + src = frame->data[0] + mx + l * my; + + bsize_w = FFMIN3(bsize_w, w - mvx, w - mx); + bsize_h = FFMIN3(bsize_h, h - mvy, h - my); + + if (mvy >= my && (mvy != my || mvx >= mx)) { + src += (bsize_h - 1) * l; + dst += (bsize_h - 1) * l; + for (int by = 0; by < bsize_h; by++) { + memmove(dst, src, bsize_w); + src -= l; + dst -= l; + } + } else { + for (int by = 0; by < bsize_h; by++) { + memmove(dst, src, bsize_w); + src += l; + dst += l; + } + } + + count--; + } + } + break; + case 6: + dst = frame->data[0]; + if (bytestream2_get_bytes_left(gb) < w * h) + return AVERROR_INVALIDDATA; + for (int y = 0; y < h; y++) { + bytestream2_get_bufferu(gb, dst, w); + dst += l; + } + break; + case 5: + dst = frame->data[0]; + for (int y = 0; y < h; y += 2) { + for (int x = 0; x < w; x += 2) { + int fill = bytestream2_get_byte(gb); + uint8_t *ddst = dst + x; + + fill = (fill << 8) | fill; + for (int by = 0; by < 2; by++) { + AV_WN16(ddst, fill); + + ddst += l; + } + } + + dst += 2 * l; + } + break; + case 3: + size = bytestream2_get_le16(gb); + if (size > 0) { + int x = bytestream2_get_byte(gb) * 4; + int y = bytestream2_get_byte(gb) * 4; + int count = bytestream2_get_byte(gb); + int fill = bytestream2_get_byte(gb); + + av_log(avctx, AV_LOG_DEBUG, "%d %d %d %d\n", x, y, count, fill); + for (int i = 0; i < count; i++) + ; + return AVERROR_PATCHWELCOME; + } + break; + case 2: + dst = frame->data[0]; + pos = 0; + dy = 0; + while (bytestream2_get_bytes_left(gb) > 0) { + int count = bytestream2_get_byteu(gb); + int skip = count & 0x3F; + + count = count >> 6; + if (skip == 0x3F) { + pos += 0x3E; + while (pos >= w) { + pos -= w; + dst += l; + dy++; + if (dy >= h) + return 0; + } + } else { + pos += skip; + while (pos >= w) { + pos -= w; + dst += l; + dy++; + if (dy >= h) + return 0; + } + while (count >= 0) { + int bits = bytestream2_get_byte(gb); + + for (int i = 0; i < 4; i++) { + switch (bits & 3) { + case 0: + break; + case 1: + if (dy < 1 && !pos) + return AVERROR_INVALIDDATA; + else + dst[pos] = pos ? dst[pos - 1] : dst[-l + w - 1]; + break; + case 2: + if (dy < 1) + return AVERROR_INVALIDDATA; + dst[pos] = dst[pos - l]; + break; + case 3: + dst[pos] = bytestream2_get_byte(gb); + break; + } + + pos++; + if (pos >= w) { + pos -= w; + dst += l; + dy++; + if (dy >= h) + return 0; + } + bits >>= 2; + } + count--; + } + } + } + break; + default: + return AVERROR_INVALIDDATA; + } + } + + return 0; +} + +static int decode_mad1_24(AVCodecContext *avctx, AVFrame *frame) +{ + ArgoContext *s = avctx->priv_data; + GetByteContext *gb = &s->gb; + const int w = frame->width; + const int h = frame->height; + const int l = frame->linesize[0] / 4; + + while (bytestream2_get_bytes_left(gb) > 0) { + int osize, type, pos, dy, di, bcode, value, v14; + const uint8_t *bits; + uint32_t *dst; + + type = bytestream2_get_byte(gb); + if (type == 0xFF) + return 0; + + switch (type) { + case 8: + dst = (uint32_t *)frame->data[0]; + for (int y = 0; y + 12 <= h; y += 12) { + for (int x = 0; x + 12 <= w; x += 12) { + int fill = bytestream2_get_be24(gb); + uint32_t *dstp = dst + x; + + for (int by = 0; by < 12; by++) { + for (int bx = 0; bx < 12; bx++) + dstp[bx] = fill; + + dstp += l; + } + } + + dst += 12 * l; + } + break; + case 7: + while (bytestream2_get_bytes_left(gb) > 0) { + int bsize = bytestream2_get_byte(gb); + uint32_t *src; + int count; + + if (!bsize) + break; + + count = bytestream2_get_be16(gb); + while (count > 0) { + int mvx, mvy, a, b, c, mx, my; + int bsize_w, bsize_h; + + bsize_w = bsize_h = bsize; + if (bytestream2_get_bytes_left(gb) < 4) + return AVERROR_INVALIDDATA; + mvx = bytestream2_get_byte(gb) * bsize; + mvy = bytestream2_get_byte(gb) * bsize; + a = bytestream2_get_byte(gb); + b = bytestream2_get_byte(gb); + c = ((a & 0x3F) << 8) + b; + mx = mvx + (c & 0x7F) - 64; + my = mvy + (c >> 7) - 64; + + if (mvy < 0 || mvy >= h) + return AVERROR_INVALIDDATA; + + if (mvx < 0 || mvx >= w) + return AVERROR_INVALIDDATA; + + if (my < 0 || my >= h) + return AVERROR_INVALIDDATA; + + if (mx < 0 || mx >= w) + return AVERROR_INVALIDDATA; + + dst = (uint32_t *)frame->data[0] + mvx + l * mvy; + src = (uint32_t *)frame->data[0] + mx + l * my; + + bsize_w = FFMIN3(bsize_w, w - mvx, w - mx); + bsize_h = FFMIN3(bsize_h, h - mvy, h - my); + + if (mvy >= my && (mvy != my || mvx >= mx)) { + src += (bsize_h - 1) * l; + dst += (bsize_h - 1) * l; + for (int by = 0; by < bsize_h; by++) { + memmove(dst, src, bsize_w * 4); + src -= l; + dst -= l; + } + } else { + for (int by = 0; by < bsize_h; by++) { + memmove(dst, src, bsize_w * 4); + src += l; + dst += l; + } + } + + count--; + } + } + break; + case 12: + osize = ((h + 3) / 4) * ((w + 3) / 4) + 7; + bits = gb->buffer; + di = 0; + bcode = v14 = 0; + if (bytestream2_get_bytes_left(gb) < osize >> 3) + return AVERROR_INVALIDDATA; + bytestream2_skip(gb, osize >> 3); + for (int x = 0; x < w; x += 4) { + for (int y = 0; y < h; y += 4) { + int astate = 0; + + if (bits[di >> 3] & (1 << (di & 7))) { + int codes = bytestream2_get_byte(gb); + + for (int count = 0; count < 4; count++) { + uint32_t *src = (uint32_t *)frame->data[0]; + int nv, v, code = codes & 3; + + pos = x; + dy = y + count; + dst = (uint32_t *)frame->data[0] + pos + dy * l; + if (code & 1) + bcode = bytestream2_get_byte(gb); + if (code == 3) { + for (int j = 0; j < 4; j++) { + switch (bcode & 3) { + case 0: + break; + case 1: + if (dy < 1 && !pos) + return AVERROR_INVALIDDATA; + dst[0] = dst[-1]; + break; + case 2: + if (dy < 1) + return AVERROR_INVALIDDATA; + dst[0] = dst[-l]; + break; + case 3: + if (astate) { + nv = value >> 4; + } else { + value = bytestream2_get_byte(gb); + nv = value & 0xF; + } + astate ^= 1; + dst[0] = src[l * (av_clip(dy + s->mv1[nv][1], 0, h - 1)) + + av_clip(pos + s->mv1[nv][0], 0, w - 1)]; + break; + } + + bcode >>= 2; + dst++; + pos++; + } + } else if (code) { + if (code == 1) + v14 = bcode; + else + bcode = v14; + for (int j = 0; j < 4; j++) { + switch (bcode & 3) { + case 0: + break; + case 1: + if (dy < 1 && !pos) + return AVERROR_INVALIDDATA; + dst[0] = dst[-1]; + break; + case 2: + if (dy < 1) + return AVERROR_INVALIDDATA; + dst[0] = dst[-l]; + break; + case 3: + v = bytestream2_get_byte(gb); + if (v < 128) { + dst[0] = src[l * (av_clip(dy + s->mv0[v][1], 0, h - 1)) + + av_clip(pos + s->mv0[v][0], 0, w - 1)]; + } else { + dst[0] = ((v & 0x7F) << 17) | bytestream2_get_be16(gb); + } + break; + } + + bcode >>= 2; + dst++; + pos++; + } + } + + codes >>= 2; + } + } + + di++; + } + } + break; + default: + return AVERROR_INVALIDDATA; + } + } + + return AVERROR_INVALIDDATA; +} + +static int decode_rle(AVCodecContext *avctx, AVFrame *frame) +{ + ArgoContext *s = avctx->priv_data; + GetByteContext *gb = &s->gb; + const int w = frame->width; + const int h = frame->height; + const int l = frame->linesize[0] / 4; + uint8_t *dst = frame->data[0]; + int pos = 0, y = 0; + + while (bytestream2_get_bytes_left(gb) > 0) { + int count = bytestream2_get_byte(gb); + int pixel = bytestream2_get_byte(gb); + + if (!count) { + pos += pixel; + while (pos >= w) { + pos -= w; + y++; + if (y >= h) + return 0; + } + } else { + while (count > 0) { + dst[pos + y * l] = pixel; + count--; + pos++; + if (pos >= w) { + pos = 0; + y++; + if (y >= h) + return 0; + } + } + } + } + + return 0; +} + +static int decode_frame(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *avpkt) +{ + ArgoContext *s = avctx->priv_data; + GetByteContext *gb = &s->gb; + AVFrame *frame = s->frame; + uint32_t chunk; + int ret; + + bytestream2_init(gb, avpkt->data, avpkt->size); + + if ((ret = ff_reget_buffer(avctx, frame, 0)) < 0) + return ret; + + chunk = bytestream2_get_be32(gb); + switch (chunk) { + case MKBETAG('P', 'A', 'L', '8'): + for (int y = 0; y < frame->height; y++) + memset(frame->data[0] + y * frame->linesize[0], 0, frame->width * s->bpp); + if (avctx->pix_fmt == AV_PIX_FMT_PAL8) + memset(frame->data[1], 0, AVPALETTE_SIZE); + return decode_pal8(avctx, s->pal); + case MKBETAG('M', 'A', 'D', '1'): + if (avctx->pix_fmt == AV_PIX_FMT_PAL8) + ret = decode_mad1(avctx, frame); + else + ret = decode_mad1_24(avctx, frame); + break; + case MKBETAG('A', 'V', 'C', 'F'): + if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { + s->key = 1; + ret = decode_avcf(avctx, frame); + break; + } + case MKBETAG('A', 'L', 'C', 'D'): + if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { + s->key = 0; + ret = decode_alcd(avctx, frame); + break; + } + case MKBETAG('R', 'L', 'E', 'F'): + if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { + s->key = 1; + ret = decode_rle(avctx, frame); + break; + } + case MKBETAG('R', 'L', 'E', 'D'): + if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { + s->key = 0; + ret = decode_rle(avctx, frame); + break; + } + default: + av_log(avctx, AV_LOG_DEBUG, "unknown chunk 0x%X\n", chunk); + break; + } + + if (ret < 0) + return ret; + + if (avctx->pix_fmt == AV_PIX_FMT_PAL8) + memcpy(frame->data[1], s->pal, AVPALETTE_SIZE); + + if ((ret = av_frame_ref(data, s->frame)) < 0) + return ret; + + frame->pict_type = s->key ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + frame->key_frame = s->key; + *got_frame = 1; + + return avpkt->size; +} + +static av_cold int decode_init(AVCodecContext *avctx) +{ + ArgoContext *s = avctx->priv_data; + + switch (avctx->bits_per_raw_sample) { + case 8: s->bpp = 1; + avctx->pix_fmt = AV_PIX_FMT_PAL8; break; + case 24: s->bpp = 4; + avctx->pix_fmt = AV_PIX_FMT_BGR0; break; + default: avpriv_request_sample(s, "depth == %u", avctx->bits_per_raw_sample); + return AVERROR_PATCHWELCOME; + } + + s->frame = av_frame_alloc(); + if (!s->frame) + return AVERROR(ENOMEM); + + for (int n = 0, i = -4; i < 4; i++) { + for (int j = -14; j < 2; j++) { + s->mv0[n][0] = j; + s->mv0[n++][1] = i; + } + } + + for (int n = 0, i = -5; i <= 1; i += 2) { + int j = -5; + + while (j <= 1) { + s->mv1[n][0] = j; + s->mv1[n++][1] = i; + j += 2; + } + } + + return 0; +} + +static void decode_flush(AVCodecContext *avctx) +{ + ArgoContext *s = avctx->priv_data; + + av_frame_unref(s->frame); +} + +static av_cold int decode_close(AVCodecContext *avctx) +{ + ArgoContext *s = avctx->priv_data; + + av_frame_free(&s->frame); + + return 0; +} + +AVCodec ff_argo_decoder = { + .name = "argo", + .long_name = NULL_IF_CONFIG_SMALL("Argonaut Games Video"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_ARGO, + .priv_data_size = sizeof(ArgoContext), + .init = decode_init, + .decode = decode_frame, + .flush = decode_flush, + .close = decode_close, + .capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, +}; diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 002ec86fa6..1246dc2b96 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -1805,6 +1805,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("IPU Video"), .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_ARGO, + .type = AVMEDIA_TYPE_VIDEO, + .name = "argo", + .long_name = NULL_IF_CONFIG_SMALL("Argonaut Games Video"), + .props = AV_CODEC_PROP_LOSSY, + }, /* various PCM "codecs" */ { diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h index e49ba1b28a..21444f9ce3 100644 --- a/libavcodec/codec_id.h +++ b/libavcodec/codec_id.h @@ -299,6 +299,7 @@ enum AVCodecID { AV_CODEC_ID_MOBICLIP, AV_CODEC_ID_PHOTOCD, AV_CODEC_ID_IPU, + AV_CODEC_ID_ARGO, /* various PCM "codecs" */ AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs diff --git a/libavformat/argo_brp.c b/libavformat/argo_brp.c index c417818639..3110dbc343 100644 --- a/libavformat/argo_brp.c +++ b/libavformat/argo_brp.c @@ -196,9 +196,7 @@ static int argo_brp_read_header(AVFormatContext *s) ArgoBVIDHeader *bvid = &hdr->extradata.bvid; st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; - - /* No decoder for this yet. */ - st->codecpar->codec_id = AV_CODEC_ID_NONE; + st->codecpar->codec_id = AV_CODEC_ID_ARGO; bvid->num_frames = AV_RL32(buf + 0); bvid->width = AV_RL32(buf + 4); @@ -213,15 +211,7 @@ static int argo_brp_read_header(AVFormatContext *s) st->codecpar->width = bvid->width; st->codecpar->height = bvid->height; - - if (bvid->depth == 8) { - st->codecpar->format = AV_PIX_FMT_PAL8; - } else if (bvid->depth == 24) { - st->codecpar->format = AV_PIX_FMT_RGB24; - } else { - avpriv_request_sample(s, "depth == %u", bvid->depth); - return AVERROR_PATCHWELCOME; - } + st->codecpar->bits_per_raw_sample = bvid->depth; } else if (hdr->codec_id == BRP_CODEC_ID_BASF) { /* * It would make the demuxer significantly more complicated