From patchwork Fri Feb 26 11:29:17 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 26008 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 0E21844A23F for ; Fri, 26 Feb 2021 13:57:46 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id CCFEE68A980; Fri, 26 Feb 2021 13:57:45 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-lj1-f173.google.com (mail-lj1-f173.google.com [209.85.208.173]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id CE8E86880FF for ; Fri, 26 Feb 2021 13:57:38 +0200 (EET) Received: by mail-lj1-f173.google.com with SMTP id h4so10105749ljl.0 for ; Fri, 26 Feb 2021 03:57:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id; bh=m4brqBxfQPyUG2S4VgyH2FCBngL1CxD+Oa7vBi97p1A=; b=SnsSi3Vz8PM+lJ7vh8QkzxAT+HRJ5rWXSZ+9pFfwG+uh8M0c2mpBBzZga92i+/pyVY 0Nfg140NfPefS8WZqi/CDFd8NGcIsj7v1qFCQg6etrf1fZOBhpwcooJ+SWHXGIVZjNtD gcDXSuNg2PBqzBQCuKBRVdqdJGkuJm9NJ6k24kTohRsU1TeVIgQBIHD37htr6EznIWw3 WYravO75V+CRU2AApMJhQhchyXFo1cL5lGo9aEsRiDfxwpbkpLyYksWVAtixmDiW8FBK gDDVUg48nEIox422iOtzVb9ushOOBEu0mX7ySJ5pcBOFJh3qYgO0jT00kp9pt9ZiuuZz kwsg== 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=m4brqBxfQPyUG2S4VgyH2FCBngL1CxD+Oa7vBi97p1A=; b=DyLZ+iS/dtIwf4Vh7/aHoBpxy51Mt6jCLJofmBox276PfdOqHpBTWM7UbKklmMHx/6 KCvceoLeDPtXoXoqbiTZxmaKCZ+hqs3/14VBW+oR6uVeTv3jg+LJqnNQga9u/ftsWrhD 9BS05KExeUibLUf9dyuyiYYL09OSOPHMhKb99D9sDMJy1LbxhhcS6kfoW/Anyy2UJOAN 9l+PcUxn0G7yWkv4aMb0JEEXtCfY7WzJp8w+zkghttVZ3nZ3tyoSQTbSrsOw04QKx+h2 5d7Z+Blpui4Qppba4xW62WSHHezmKVTJf9QSFZrv724dswFq05n/CasDliqrQKpmK111 ixHg== X-Gm-Message-State: AOAM5331y/WDgk/vfnWRyHMJWY+HaQgbdrywE67UNaJ9QUdr+ucAId6q y8x68kHMbX+6PmTlzKNsHRlE9sz3upU= X-Google-Smtp-Source: ABdhPJwICwpg8UKZ2hIESNAUk/NNc2Nrz4JmTIRHzrbt0dUwX7tiZEshHOwKiaga1XY+CaRbOgmPGQ== X-Received: by 2002:a50:aac8:: with SMTP id r8mr2662688edc.9.1614338969672; Fri, 26 Feb 2021 03:29:29 -0800 (PST) Received: from localhost.localdomain ([109.227.52.18]) by smtp.gmail.com with ESMTPSA id t8sm5348817edv.16.2021.02.26.03.29.28 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Feb 2021 03:29:29 -0800 (PST) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Fri, 26 Feb 2021 12:29:17 +0100 Message-Id: <20210226112919.20437-1-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 Subject: [FFmpeg-devel] [PATCH 1/3] avcodec: add SGA PCM 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/codec_desc.c | 7 +++++++ libavcodec/codec_id.h | 1 + libavcodec/pcm.c | 9 +++++++++ libavcodec/utils.c | 1 + 6 files changed, 20 insertions(+) diff --git a/libavcodec/Makefile b/libavcodec/Makefile index eb90841dfe..fe7026c1db 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -830,6 +830,7 @@ OBJS-$(CONFIG_PCM_S64BE_DECODER) += pcm.o OBJS-$(CONFIG_PCM_S64BE_ENCODER) += pcm.o OBJS-$(CONFIG_PCM_S64LE_DECODER) += pcm.o OBJS-$(CONFIG_PCM_S64LE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_SGA_DECODER) += pcm.o OBJS-$(CONFIG_PCM_U8_DECODER) += pcm.o OBJS-$(CONFIG_PCM_U8_ENCODER) += pcm.o OBJS-$(CONFIG_PCM_U16BE_DECODER) += pcm.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 354d146379..990998b64b 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -568,6 +568,7 @@ extern AVCodec ff_pcm_s64be_encoder; extern AVCodec ff_pcm_s64be_decoder; extern AVCodec ff_pcm_s64le_encoder; extern AVCodec ff_pcm_s64le_decoder; +extern AVCodec ff_pcm_sga_decoder; extern AVCodec ff_pcm_u8_encoder; extern AVCodec ff_pcm_u8_decoder; extern AVCodec ff_pcm_u16be_encoder; diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 8e695b11d2..f64ba488f2 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -2096,6 +2096,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("PCM Archimedes VIDC"), .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_PCM_SGA, + .type = AVMEDIA_TYPE_AUDIO, + .name = "pcm_sga", + .long_name = NULL_IF_CONFIG_SMALL("PCM SGA"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, + }, /* various ADPCM codecs */ { diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h index 56a69cf1c2..7dd316afd2 100644 --- a/libavcodec/codec_id.h +++ b/libavcodec/codec_id.h @@ -346,6 +346,7 @@ enum AVCodecID { AV_CODEC_ID_PCM_F16LE, AV_CODEC_ID_PCM_F24LE, AV_CODEC_ID_PCM_VIDC, + AV_CODEC_ID_PCM_SGA, /* various ADPCM codecs */ AV_CODEC_ID_ADPCM_IMA_QT = 0x11000, diff --git a/libavcodec/pcm.c b/libavcodec/pcm.c index 7f0af8564f..19d04e9181 100644 --- a/libavcodec/pcm.c +++ b/libavcodec/pcm.c @@ -419,6 +419,14 @@ static int pcm_decode_frame(AVCodecContext *avctx, void *data, for (; n > 0; n--) *samples++ = *src++ + 128; break; + case AV_CODEC_ID_PCM_SGA: + for (; n > 0; n--) { + int sign = *src >> 7; + int magn = *src & 0x7f; + *samples++ = sign ? 128 - magn : 128 + magn; + src++; + } + break; case AV_CODEC_ID_PCM_S8_PLANAR: n /= avctx->channels; for (c = 0; c < avctx->channels; c++) { @@ -622,3 +630,4 @@ PCM_CODEC (PCM_U32LE, AV_SAMPLE_FMT_S32, pcm_u32le, "PCM unsigned PCM_CODEC (PCM_S64BE, AV_SAMPLE_FMT_S64, pcm_s64be, "PCM signed 64-bit big-endian"); PCM_CODEC (PCM_S64LE, AV_SAMPLE_FMT_S64, pcm_s64le, "PCM signed 64-bit little-endian"); PCM_CODEC (PCM_VIDC, AV_SAMPLE_FMT_S16, pcm_vidc, "PCM Archimedes VIDC"); +PCM_DECODER(PCM_SGA, AV_SAMPLE_FMT_U8, pcm_sga, "PCM SGA"); diff --git a/libavcodec/utils.c b/libavcodec/utils.c index 4d1909b581..db6cd0cde8 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -1547,6 +1547,7 @@ int av_get_exact_bits_per_sample(enum AVCodecID codec_id) case AV_CODEC_ID_PCM_VIDC: case AV_CODEC_ID_PCM_S8: case AV_CODEC_ID_PCM_S8_PLANAR: + case AV_CODEC_ID_PCM_SGA: case AV_CODEC_ID_PCM_U8: case AV_CODEC_ID_SDX2_DPCM: case AV_CODEC_ID_DERF_DPCM: From patchwork Fri Feb 26 11:29:18 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 26007 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 ACC8B44B1B7 for ; Fri, 26 Feb 2021 13:55:28 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 897D268A8F0; Fri, 26 Feb 2021 13:55:28 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-ej1-f54.google.com (mail-ej1-f54.google.com [209.85.218.54]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 6714268A71F for ; Fri, 26 Feb 2021 13:55:21 +0200 (EET) Received: by mail-ej1-f54.google.com with SMTP id u20so14378218ejb.7 for ; Fri, 26 Feb 2021 03:55:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references; bh=E3rRI8MfPGitPxUVwXzPYs9e+0v6S1tXf2fP4uII0LY=; b=KQFMuSiqYCIOp1NWcFJAdGcGOMDrQgEqi2z/MYUeDr/iyQm0N8dCwvMK9qqbDeMOnO nZ6MNFbHp6E4MC1WC9LErleEpvjHQBEvX+ozIRJIxnRqWSKnDp0JxtUx9ke0yeXFEY7W x48X0B1ATZMux0YaDQ3saLPIP4NG+E5+W7/CDsAAwwOwwCcggFVI2tt0yK8Jsr7NYGxa wYXLvfCfBu6UB71VqsM4tM8GQ4TQ7zkw1wJ+QP/xG+/moZ45QDYmkSzb/dQjO0owt49c ZEjTBkBmmjm3Q0yuCV8qBp3NavQNDBbwCocjGqESS3nsOI1oSvJ0Gwp9G0B1n1UkS4Ob IPIQ== 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:in-reply-to :references; bh=E3rRI8MfPGitPxUVwXzPYs9e+0v6S1tXf2fP4uII0LY=; b=eSRuHp8z665xOE8ctmPx1TKIBkxhXo2/i2OSeeJ+2jVzoTPpUdUsy4ZsSv5lIjFBaW /LEKSa+ymDX6cjPxMGIulV91d8dNQxFc5VLm4m2eaL2wABEMILiexvIOYEuQM+QDB396 VCFNwDA+DbEbhU5NWIurAra0dzss0j2/GbHsEYjZyMUcaFWsItntADCU0/ZpXe1HGl3o cWoTioxsMOiQnZ2QGD/OqY8vIUBXsR72JTH7frZRj/vifhkULiMWlIU5cTBmrHXOVNdr C+k+Wnhh0Gf2Dy8DRFgt7TcWlNcLZoDTiAhJP8goHvJWJzzp4rEuirR9IGJcn8OvABpk ffkw== X-Gm-Message-State: AOAM530FxVE4ZrmE9662ACrzMkEPt6s1RDqDRoVZmjhKP/orwWwWg7mF ZltBTePLcsw2qtfP7CnUY7xkrSS4u1c= X-Google-Smtp-Source: ABdhPJwRYCpFaD/47GIh2XyOtQvut12vICuxTgZ/w7Tz/cKiGEjK1daHwjt9yZ5hBZwk+GdLlSMZ6w== X-Received: by 2002:a17:906:4002:: with SMTP id v2mr2780693ejj.135.1614338970704; Fri, 26 Feb 2021 03:29:30 -0800 (PST) Received: from localhost.localdomain ([109.227.52.18]) by smtp.gmail.com with ESMTPSA id t8sm5348817edv.16.2021.02.26.03.29.29 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Feb 2021 03:29:30 -0800 (PST) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Fri, 26 Feb 2021 12:29:18 +0100 Message-Id: <20210226112919.20437-2-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20210226112919.20437-1-onemda@gmail.com> References: <20210226112919.20437-1-onemda@gmail.com> Subject: [FFmpeg-devel] [PATCH 2/3] avcodec: add SGA 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/codec_desc.c | 7 + libavcodec/codec_id.h | 1 + libavcodec/sga.c | 534 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 544 insertions(+) create mode 100644 libavcodec/sga.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index fe7026c1db..850657ae3c 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -609,6 +609,7 @@ OBJS-$(CONFIG_SANM_DECODER) += sanm.o OBJS-$(CONFIG_SCPR_DECODER) += scpr.o OBJS-$(CONFIG_SCREENPRESSO_DECODER) += screenpresso.o OBJS-$(CONFIG_SDX2_DPCM_DECODER) += dpcm.o +OBJS-$(CONFIG_SGA_DECODER) += sga.o OBJS-$(CONFIG_SGI_DECODER) += sgidec.o OBJS-$(CONFIG_SGI_ENCODER) += sgienc.o rle.o OBJS-$(CONFIG_SGIRLE_DECODER) += sgirledec.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 990998b64b..a04faead16 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -289,6 +289,7 @@ extern AVCodec ff_s302m_decoder; extern AVCodec ff_sanm_decoder; extern AVCodec ff_scpr_decoder; extern AVCodec ff_screenpresso_decoder; +extern AVCodec ff_sga_decoder; extern AVCodec ff_sgi_encoder; extern AVCodec ff_sgi_decoder; extern AVCodec ff_sgirle_decoder; diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index f64ba488f2..17f8a14044 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -1849,6 +1849,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("Simbiosis Interactive IMX Video"), .props = AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_SGA_VIDEO, + .type = AVMEDIA_TYPE_VIDEO, + .name = "sga", + .long_name = NULL_IF_CONFIG_SMALL("Digital Pictures SGA Video"), + .props = AV_CODEC_PROP_LOSSY, + }, /* various PCM "codecs" */ { diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h index 7dd316afd2..ab7bc68ee2 100644 --- a/libavcodec/codec_id.h +++ b/libavcodec/codec_id.h @@ -306,6 +306,7 @@ enum AVCodecID { AV_CODEC_ID_ARGO, AV_CODEC_ID_CRI, AV_CODEC_ID_SIMBIOSIS_IMX, + AV_CODEC_ID_SGA_VIDEO, /* various PCM "codecs" */ AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs diff --git a/libavcodec/sga.c b/libavcodec/sga.c new file mode 100644 index 0000000000..00752a5843 --- /dev/null +++ b/libavcodec/sga.c @@ -0,0 +1,534 @@ +/* + * Copyright (c) 2021 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 "libavutil/avassert.h" +#include "libavutil/common.h" +#include "avcodec.h" +#include "get_bits.h" +#include "bytestream.h" +#include "internal.h" + +#define PALDATA_FOLLOWS_TILEDATA 4 +#define HAVE_COMPRESSED_TILEMAP 32 +#define HAVE_TILEMAP 128 + +typedef struct SGAVideoContext { + GetByteContext gb; + + int metadata_size; + int tiledata_size; + int tiledata_offset; + int tilemapdata_size; + int tilemapdata_offset; + int paldata_size; + int paldata_offset; + int palmapdata_offset; + int palmapdata_size; + + int flags; + int nb_pal; + int nb_tiles; + int tiles_w, tiles_h; + int shift; + int plus; + int swap; + + uint32_t pal[256]; + uint8_t *tileindex_data; + unsigned tileindex_size; + uint8_t *palmapindex_data; + unsigned palmapindex_size; + uint8_t uncompressed[65536]; +} SGAVideoContext; + +static av_cold int sga_decode_init(AVCodecContext *avctx) +{ + avctx->pix_fmt = AV_PIX_FMT_PAL8; + return 0; +} + +static int decode_palette(GetByteContext *gb, uint32_t *pal) +{ + GetBitContext gbit; + + if (bytestream2_get_bytes_left(gb) < 18) + return AVERROR_INVALIDDATA; + + memset(pal, 0, 16 * sizeof(*pal)); + init_get_bits8(&gbit, gb->buffer, 18); + + for (int RGBIndex = 0; RGBIndex < 3; RGBIndex++) { + for (int index = 0; index < 16; index++) { + unsigned color = get_bits1(&gbit) << RGBIndex; + pal[15 - index] |= color << (5 + 16); + } + } + + for (int RGBIndex = 0; RGBIndex < 3; RGBIndex++) { + for (int index = 0; index < 16; index++) { + unsigned color = get_bits1(&gbit) << RGBIndex; + pal[15 - index] |= color << (5 + 8); + } + } + + for (int RGBIndex = 0; RGBIndex < 3; RGBIndex++) { + for (int index = 0; index < 16; index++) { + unsigned color = get_bits1(&gbit) << RGBIndex; + pal[15 - index] |= color << (5 + 0); + } + } + + for (int index = 0; index < 16; index++) + pal[index] = (0xFFU << 24) | pal[index] | (pal[index] >> 3); + + bytestream2_skip(gb, 18); + + return 0; +} + +static int decode_index_palmap(SGAVideoContext *s, AVFrame *frame) +{ + const uint8_t *tt = s->tileindex_data; + + for (int y = 0; y < s->tiles_h; y++) { + for (int x = 0; x < s->tiles_w; x++) { + int pal_idx = s->palmapindex_data[y * s->tiles_w + x] * 16; + uint8_t *dst = frame->data[0] + y * 8 * frame->linesize[0] + x * 8; + + for (int yy = 0; yy < 8; yy++) { + for (int xx = 0; xx < 8; xx++) + dst[xx] = pal_idx + tt[xx]; + tt += 8; + + dst += frame->linesize[0]; + } + } + } + + return 0; +} + +static int decode_index_tilemap(SGAVideoContext *s, AVFrame *frame) +{ + GetByteContext *gb = &s->gb; + GetBitContext pm; + + bytestream2_seek(gb, s->tilemapdata_offset, SEEK_SET); + if (bytestream2_get_bytes_left(gb) < s->tilemapdata_size) + return AVERROR_INVALIDDATA; + + init_get_bits8(&pm, gb->buffer, s->tilemapdata_size); + + for (int y = 0; y < s->tiles_h; y++) { + for (int x = 0; x < s->tiles_w; x++) { + uint8_t tile[64]; + int tilemap = get_bits(&pm, 16); + int flip_x = (tilemap >> 11) & 1; + int flip_y = (tilemap >> 12) & 1; + int tindex = av_clip((tilemap & 511) - 1, 0, s->nb_tiles - 1); + const uint8_t *tt = s->tileindex_data + tindex * 64; + int pal_idx = ((tilemap >> 13) & 3) * 16; + uint8_t *dst = frame->data[0] + y * 8 * frame->linesize[0] + x * 8; + + if (!flip_x && !flip_y) { + memcpy(tile, tt, 64); + } else if (flip_x && flip_y) { + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 8; j++) + tile[i * 8 + j] = tt[(7 - i) * 8 + 7 - j]; + } + } else if (flip_x) { + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 8; j++) + tile[i * 8 + j] = tt[i * 8 + 7 - j]; + } + } else { + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 8; j++) + tile[i * 8 + j] = tt[(7 - i) * 8 + j]; + } + } + + for (int yy = 0; yy < 8; yy++) { + for (int xx = 0; xx < 8; xx++) + dst[xx] = pal_idx + tile[xx + yy * 8]; + + dst += frame->linesize[0]; + } + } + } + + return 0; +} + +static int decode_index(SGAVideoContext *s, AVFrame *frame) +{ + const uint8_t *src = s->tileindex_data; + uint8_t *dst = frame->data[0]; + + for (int y = 0; y < frame->height; y += 8) { + for (int x = 0; x < frame->width; x += 8) { + for (int yy = 0; yy < 8; yy++) { + for (int xx = 0; xx < 8; xx++) + dst[x + xx + yy * frame->linesize[0]] = src[xx]; + src += 8; + } + } + + dst += 8 * frame->linesize[0]; + } + + return 0; +} + +static int lzss_decompress(AVCodecContext *avctx, + GetByteContext *gb, uint8_t *dst, + int dst_size, int shift, int plus) +{ + int oi = 0; + + while (bytestream2_get_bytes_left(gb) > 0 && oi < dst_size) { + uint16_t displace, header = bytestream2_get_be16(gb); + int count, offset; + + for (int i = 0; i < 16; i++) { + switch (header >> 15) { + case 0: + if (oi + 2 < dst_size) { + dst[oi++] = bytestream2_get_byte(gb); + dst[oi++] = bytestream2_get_byte(gb); + } + break; + case 1: + displace = bytestream2_get_be16(gb); + count = displace >> shift; + offset = displace & ((1 << shift) - 1); + + if (displace == 0) { + while (bytestream2_get_bytes_left(gb) > 0 && + oi < dst_size) + dst[oi++] = bytestream2_get_byte(gb); + return oi; + } + + count += plus; + + if (offset <= 0) + offset = 1; + if (oi < offset) + return AVERROR_INVALIDDATA; + for (int j = 0; j < count * 2; j++) { + dst[oi] = dst[oi - offset]; + oi++; + } + break; + } + + header <<= 1; + } + } + + return AVERROR_INVALIDDATA; +} + +static int decode_palmapdata(AVCodecContext *avctx) +{ + SGAVideoContext *s = avctx->priv_data; + const int bits = (s->nb_pal + 1) / 2; + GetByteContext *gb = &s->gb; + GetBitContext pm; + + bytestream2_seek(gb, s->palmapdata_offset, SEEK_SET); + if (bytestream2_get_bytes_left(gb) < s->palmapdata_size) + return AVERROR_INVALIDDATA; + init_get_bits8(&pm, gb->buffer, s->palmapdata_size); + + for (int y = 0; y < s->tiles_h; y++) { + uint8_t *dst = s->palmapindex_data + y * s->tiles_w; + + for (int x = 0; x < s->tiles_w; x++) + dst[x] = get_bits(&pm, bits); + + dst += s->tiles_w; + } + + return 0; +} + +static int decode_tiledata(AVCodecContext *avctx) +{ + SGAVideoContext *s = avctx->priv_data; + GetByteContext *gb = &s->gb; + GetBitContext tm; + + bytestream2_seek(gb, s->tiledata_offset, SEEK_SET); + if (bytestream2_get_bytes_left(gb) < s->tiledata_size) + return AVERROR_INVALIDDATA; + init_get_bits8(&tm, gb->buffer, s->tiledata_size); + + for (int n = 0; n < s->nb_tiles; n++) { + uint8_t *dst = s->tileindex_data + n * 64; + + for (int yy = 0; yy < 8; yy++) { + for (int xx = 0; xx < 8; xx++) + dst[xx] = get_bits(&tm, 4); + + dst += 8; + } + } + + for (int i = 0; i < s->nb_tiles && s->swap; i++) { + uint8_t *dst = s->tileindex_data + i * 64; + + for (int j = 8; j < 64; j += 16) { + for (int k = 0; k < 8; k += 2) + FFSWAP(uint8_t, dst[j + k], dst[j+k+1]); + } + } + + return 0; +} + +static int sga_decode_frame(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *avpkt) +{ + SGAVideoContext *s = avctx->priv_data; + GetByteContext *gb = &s->gb; + AVFrame *frame = data; + int ret, type; + + if (avpkt->size <= 14) + return AVERROR_INVALIDDATA; + + s->flags = avpkt->data[8]; + s->nb_pal = avpkt->data[9]; + s->tiles_w = avpkt->data[10]; + s->tiles_h = avpkt->data[11]; + + if (s->nb_pal > 4) + return AVERROR_INVALIDDATA; + + if ((ret = ff_set_dimensions(avctx, + s->tiles_w * 8, + s->tiles_h * 8)) < 0) + return ret; + + av_fast_padded_malloc(&s->tileindex_data, &s->tileindex_size, + avctx->width * avctx->height); + if (!s->tileindex_data) + return AVERROR(ENOMEM); + + av_fast_padded_malloc(&s->palmapindex_data, &s->palmapindex_size, + s->tiles_w * s->tiles_h); + if (!s->palmapindex_data) + return AVERROR(ENOMEM); + + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) + return ret; + + bytestream2_init(gb, avpkt->data, avpkt->size); + + type = bytestream2_get_byte(gb); + s->metadata_size = 12 + ((!!(s->flags & HAVE_TILEMAP)) * 2); + s->nb_tiles = s->flags & HAVE_TILEMAP ? AV_RB16(avpkt->data + 12) : s->tiles_w * s->tiles_h; + if (s->nb_tiles > s->tiles_w * s->tiles_h) + return AVERROR_INVALIDDATA; + + av_log(avctx, AV_LOG_DEBUG, "type: %X flags: %X nb_tiles: %d\n", type, s->flags, s->nb_tiles); + + switch (type) { + case 0xE7: + case 0xCB: + case 0xCD: + s->swap = 1; + s->shift = 12; + s->plus = 1; + break; + case 0xC9: + s->swap = 1; + s->shift = 13; + s->plus = 1; + break; + case 0xC8: + s->swap = 1; + s->shift = 13; + s->plus = 0; + break; + case 0xC7: + s->swap = 0; + s->shift = 13; + s->plus = 1; + break; + case 0xC6: + s->swap = 0; + s->shift = 13; + s->plus = 0; + break; + } + + if (type == 0xE7) { + int offset = s->metadata_size, left; + int sizes[3]; + + bytestream2_seek(gb, s->metadata_size, SEEK_SET); + + for (int i = 0; i < 3; i++) + sizes[i] = bytestream2_get_be16(gb); + + for (int i = 0; i < 3; i++) { + int size = sizes[i]; + int raw = size >> 15; + + size &= (1 << 15) - 1; + + if (raw) { + if (bytestream2_get_bytes_left(gb) < size) + return AVERROR_INVALIDDATA; + + if (sizeof(s->uncompressed) - offset < size) + return AVERROR_INVALIDDATA; + + memcpy(s->uncompressed + offset, gb->buffer, size); + bytestream2_skip(gb, size); + } else { + GetByteContext gb2; + + if (bytestream2_get_bytes_left(gb) < size) + return AVERROR_INVALIDDATA; + + bytestream2_init(&gb2, gb->buffer, size); + ret = lzss_decompress(avctx, &gb2, s->uncompressed + offset, + sizeof(s->uncompressed) - offset, s->shift, s->plus); + if (ret < 0) + return ret; + bytestream2_skip(gb, size); + size = ret; + } + + offset += size; + } + + left = bytestream2_get_bytes_left(gb); + if (sizeof(s->uncompressed) - offset < left) + return AVERROR_INVALIDDATA; + + bytestream2_get_buffer(gb, s->uncompressed + offset, left); + + offset += left; + bytestream2_init(gb, s->uncompressed, offset); + } + + switch (type) { + case 0xCD: + case 0xCB: + case 0xC9: + case 0xC8: + case 0xC7: + case 0xC6: + bytestream2_seek(gb, s->metadata_size, SEEK_SET); + ret = lzss_decompress(avctx, gb, s->uncompressed + s->metadata_size, + sizeof(s->uncompressed) - s->metadata_size, s->shift, s->plus); + if (ret < 0) + return ret; + bytestream2_init(gb, s->uncompressed, ret + s->metadata_size); + case 0xE7: + case 0xC1: + s->tiledata_size = s->nb_tiles * 32; + s->paldata_size = s->nb_pal * 18; + s->tiledata_offset = s->flags & PALDATA_FOLLOWS_TILEDATA ? s->metadata_size : s->metadata_size + s->paldata_size; + s->paldata_offset = s->flags & PALDATA_FOLLOWS_TILEDATA ? s->metadata_size + s->tiledata_size : s->metadata_size; + s->palmapdata_offset = (s->flags & HAVE_TILEMAP) ? -1 : s->paldata_offset + s->paldata_size; + s->palmapdata_size = (s->flags & HAVE_TILEMAP) || s->nb_pal < 2 ? 0 : (s->tiles_w * s->tiles_h * ((s->nb_pal + 1) / 2) + 7) / 8; + s->tilemapdata_size = (s->flags & HAVE_TILEMAP) ? s->tiles_w * s->tiles_h * 2 : 0; + s->tilemapdata_offset = (s->flags & HAVE_TILEMAP) ? s->paldata_offset + s->paldata_size: -1; + + bytestream2_seek(gb, s->paldata_offset, SEEK_SET); + for (int n = 0; n < s->nb_pal; n++) { + ret = decode_palette(gb, s->pal + 16 * n); + if (ret < 0) + return ret; + } + + if (s->tiledata_size > 0) { + ret = decode_tiledata(avctx); + if (ret < 0) + return ret; + } + + if (s->palmapdata_size > 0) { + ret = decode_palmapdata(avctx); + if (ret < 0) + return ret; + } + + if (s->palmapdata_size > 0 && s->tiledata_size > 0) { + ret = decode_index_palmap(s, frame); + if (ret < 0) + return ret; + } else if (s->tilemapdata_size > 0 && s->tiledata_size > 0) { + ret = decode_index_tilemap(s, frame); + if (ret < 0) + return ret; + } else if (s->tiledata_size > 0) { + ret = decode_index(s, frame); + if (ret < 0) + return ret; + } + break; + default: + av_log(avctx, AV_LOG_ERROR, "Unknown type: %X\n", type); + return AVERROR_INVALIDDATA; + } + + memcpy(frame->data[1], s->pal, AVPALETTE_SIZE); + frame->palette_has_changed = 1; + frame->pict_type = AV_PICTURE_TYPE_I; + frame->key_frame = 1; + + *got_frame = 1; + + return avpkt->size; +} + +static av_cold int sga_decode_end(AVCodecContext *avctx) +{ + SGAVideoContext *s = avctx->priv_data; + + av_freep(&s->tileindex_data); + s->tileindex_size = 0; + + av_freep(&s->palmapindex_data); + s->palmapindex_size = 0; + + return 0; +} + +AVCodec ff_sga_decoder = { + .name = "sga", + .long_name = NULL_IF_CONFIG_SMALL("Digital Pictures SGA Video"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_SGA_VIDEO, + .priv_data_size = sizeof(SGAVideoContext), + .init = sga_decode_init, + .decode = sga_decode_frame, + .close = sga_decode_end, + .capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, +}; From patchwork Fri Feb 26 11:29:19 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 26006 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 3E5F744A7D5 for ; Fri, 26 Feb 2021 13:29:38 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 0D57E68A7B8; Fri, 26 Feb 2021 13:29:38 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-ej1-f41.google.com (mail-ej1-f41.google.com [209.85.218.41]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 30FF768A4A3 for ; Fri, 26 Feb 2021 13:29:32 +0200 (EET) Received: by mail-ej1-f41.google.com with SMTP id lr13so14210969ejb.8 for ; Fri, 26 Feb 2021 03:29:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references; bh=GLhuULIYQ1oj4WDaSdzb126AqzPu3h1EpJENfydDHaM=; b=YOliLMPR8p6VPu/ixqmjTnOQeg7a88D7hCN0CzI7cMSwYuzCUNPQ6e70uKLPMd1mtV hvAufhafg4UzfdZytO8yLb/SEeyqVw9zVxcJSeMezioofKjXkcDF0EobF1kQz5gdFWZK onR7mdyNNOB8ADfD9m2zgMbIO6gDXXyQbnuBQRgYMt1dPymjaT/doxER9MgjfNKOOz3C gd4L55/IVnWx5PkeY518N8jTbHFG/wfxKuCgXc8Fw43kcjbo9VSApgVa0pD5VPzalOy+ aprtGdk1qtHnbZnFlvZH+LgB9oYxNm/sfEdYVUCRFs2hYA/p1VVSq9KgS0c84GTpHnXb C84Q== 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:in-reply-to :references; bh=GLhuULIYQ1oj4WDaSdzb126AqzPu3h1EpJENfydDHaM=; b=haFmTo9Q6rXiGhlvc7XRWQ+tsCXUu+YDoLffUBabQ2obVNSUAHXYsOzTGmbFrjylA5 hKOZ1XDw1rF23pQ2R+E4bRS3Dv3d6ZPHyhE1NoVZOeQ3UkvzlA2ETsculU+uksHqnwpJ XxiuQBRLGevr8A4vp1NoaIV8zm/304IggQ6vYWRs05EfLdFkLeww4+ixlGX/5RDi7N63 cxFamYD2dNreLzuuRsFdc1kdGOND65SZgAXvH3bhfoYy0kwBuEKxtzn5MTOkNE7rcord M2Ug3QH2P0+weYXzFY2muGlt4xwfPaRoB2QseN+l9LvrZWS9DN5uV9/UjPYcybSbH028 bJMw== X-Gm-Message-State: AOAM531K2KALg8O9LGftt+5cZR0kikzyo3t8En2fWqXYavqbdySP+IiP Hw9foYL6FvPJ01Q2K8oEDXDJho1qvWY= X-Google-Smtp-Source: ABdhPJyuY1ubfDsJikarRu5MNGm4OuzFrcgnK50v2ZPkPUtlXdYBLVdSRk8OuEtVL59yh21woslyhQ== X-Received: by 2002:a17:906:6047:: with SMTP id p7mr2860389ejj.400.1614338971629; Fri, 26 Feb 2021 03:29:31 -0800 (PST) Received: from localhost.localdomain ([109.227.52.18]) by smtp.gmail.com with ESMTPSA id t8sm5348817edv.16.2021.02.26.03.29.30 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Feb 2021 03:29:31 -0800 (PST) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Fri, 26 Feb 2021 12:29:19 +0100 Message-Id: <20210226112919.20437-3-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20210226112919.20437-1-onemda@gmail.com> References: <20210226112919.20437-1-onemda@gmail.com> Subject: [FFmpeg-devel] [PATCH 3/3] avformat: add Digital Pictures SGA game demuxer 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 --- libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/sga.c | 473 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 475 insertions(+) create mode 100644 libavformat/sga.c diff --git a/libavformat/Makefile b/libavformat/Makefile index 95ed25e866..4b03e9fa92 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -502,6 +502,7 @@ OBJS-$(CONFIG_SEGAFILM_DEMUXER) += segafilm.o OBJS-$(CONFIG_SEGAFILM_MUXER) += segafilmenc.o OBJS-$(CONFIG_SEGMENT_MUXER) += segment.o OBJS-$(CONFIG_SER_DEMUXER) += serdec.o +OBJS-$(CONFIG_SGA_DEMUXER) += sga.o OBJS-$(CONFIG_SHORTEN_DEMUXER) += shortendec.o rawdec.o OBJS-$(CONFIG_SIFF_DEMUXER) += siff.o OBJS-$(CONFIG_SIMBIOSIS_IMX_DEMUXER) += imx.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 3b69423508..ade247640c 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -401,6 +401,7 @@ extern AVOutputFormat ff_segafilm_muxer; extern AVOutputFormat ff_segment_muxer; extern AVOutputFormat ff_stream_segment_muxer; extern AVInputFormat ff_ser_demuxer; +extern AVInputFormat ff_sga_demuxer; extern AVInputFormat ff_shorten_demuxer; extern AVInputFormat ff_siff_demuxer; extern AVInputFormat ff_simbiosis_imx_demuxer; diff --git a/libavformat/sga.c b/libavformat/sga.c new file mode 100644 index 0000000000..3dfce4e11e --- /dev/null +++ b/libavformat/sga.c @@ -0,0 +1,473 @@ +/* + * Digital Pictures SGA game demuxer + * + * Copyright (C) 2021 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 "libavutil/intreadwrite.h" +#include "libavutil/avassert.h" +#include "libavutil/internal.h" +#include "avformat.h" +#include "internal.h" +#include "avio_internal.h" + +#define SEGA_CD_PCM_NUM 12500000 +#define SEGA_CD_PCM_DEN 786432 + +typedef struct SGADemuxContext { + int video_stream_index; + int audio_stream_index; + + uint8_t sector[65536 * 2]; + int sector_headers; + int sample_rate; + int first_audio_size; + int payload_size; + int packet_type; + int flags; + int idx; + int left; + int64_t pkt_pos; +} SGADemuxContext; + +static int sga_probe(const AVProbeData *p) +{ + const uint8_t *src = p->buf; + int score = 0, sectors = 1; + int last_left = 0; + int sample_rate = -1;; + + if (p->buf_size < 2048) + return 0; + + for (int i = 0; i + 2 < p->buf_size; i += 2048) { + int header = AV_RB16(src + i); + + if ((header > 0x07FE && header < 0x8100) || + (header > 0x8200 && header < 0xA100) || + (header > 0xA200 && header < 0xC100)) { + sectors = 0; + break; + } + } + + for (int i = 0; i + 4 < p->buf_size;) { + int header = AV_RB16(src + i); + int left = AV_RB16(src + i + 2); + int offset, type, size; + + if (sectors && header && last_left <= 0) { + if (left <= 8) + return 0; + last_left = left; + } else if (sectors && header) { + left = header; + last_left -= left; + if (header != 0x7FE && left <= 8) + return 0; + } else if (sectors) { + if (left <= 8) + return 0; + i += sectors ? 2048 : left + 4; + last_left = 0; + continue; + } + + if (sectors && (i > 0 && left < 0x7fe) && + (i + left + 14 < p->buf_size)) { + offset = i + left + 2; + } else if (sectors && i > 0) { + i += 2048; + continue; + } else { + offset = 0; + last_left = left; + } + + header = AV_RB16(src + offset); + size = AV_RB16(src + offset + 2) + 4; + + while ((header & 0xFF00) == 0) { + offset++; + if (offset + 4 >= p->buf_size) + break; + header = AV_RB16(src + offset); + size = AV_RB16(src + offset + 2) + 4; + } + + if (offset + 12 >= p->buf_size) + break; + if ((header & 0xFF) > 1) + return 0; + type = header >> 8; + + if (type == 0xAA || + type == 0xA1 || + type == 0xA2 || + type == 0xA3) { + int new_rate; + + if (size <= 12) + return 0; + new_rate = AV_RB16(src + offset + 8); + if (sample_rate < 0) + sample_rate = new_rate; + if (sample_rate == 0 || new_rate != sample_rate) + return 0; + if (src[offset + 10] != 1) + return 0; + + score += 10; + } else if (type == 0xC1 || + type == 0xC6 || + type == 0xC7 || + type == 0xC8 || + type == 0xC9 || + type == 0xCB || + type == 0xCD || + type == 0xE7) { + int nb_pals = src[offset + 9]; + int tiles_w = src[offset + 10]; + int tiles_h = src[offset + 11]; + + if (size <= 12) + return 0; + if (nb_pals == 0 || nb_pals > 4) + return 0; + if (tiles_w == 0 || tiles_w > 80) + return 0; + if (tiles_h == 0 || tiles_h > 60) + return 0; + + score += 10; + } else if (header == 0x7FE) { + ; + } else { + return 0; + } + + i += sectors ? 2048 : size + 4; + + if (score < 0) + break; + } + + return av_clip(score, 0, AVPROBE_SCORE_MAX); +} + +static int sga_read_header(AVFormatContext *s) +{ + SGADemuxContext *sga = s->priv_data; + AVIOContext *pb = s->pb; + + sga->sector_headers = 1; + sga->first_audio_size = 0; + sga->video_stream_index = -1; + sga->audio_stream_index = -1; + sga->left = 2048; + sga->idx = 0; + + s->ctx_flags |= AVFMTCTX_NOHEADER; + + if (pb->seekable & AVIO_SEEKABLE_NORMAL) { + while (!avio_feof(pb)) { + int header = avio_rb16(pb); + int type = header >> 8; + int skip = 2046; + int clock; + + if (!sga->first_audio_size && + (type == 0xAA || + type == 0xA1 || + type == 0xA2 || + type == 0xA3)) { + sga->first_audio_size = avio_rb16(pb); + avio_skip(pb, 4); + clock = avio_rb16(pb); + sga->sample_rate = av_rescale(clock, + SEGA_CD_PCM_NUM, + SEGA_CD_PCM_DEN); + skip -= 8; + } + if ((header > 0x07FE && header < 0x8100) || + (header > 0x8200 && header < 0xA100) || + (header > 0xA200 && header < 0xC100)) { + sga->sector_headers = 0; + break; + } + + avio_skip(pb, skip); + } + + avio_seek(pb, 0, SEEK_SET); + } + + return 0; +} + +static void print_stats(AVFormatContext *s, const char *where) +{ + SGADemuxContext *sga = s->priv_data; + + av_log(s, AV_LOG_DEBUG, "START %s\n", where); + av_log(s, AV_LOG_DEBUG, "pos: %lX\n", avio_tell(s->pb)); + av_log(s, AV_LOG_DEBUG, "idx: %X\n", sga->idx); + av_log(s, AV_LOG_DEBUG, "packet_type: %X\n", sga->packet_type); + av_log(s, AV_LOG_DEBUG, "payload_size: %X\n", sga->payload_size); + av_log(s, AV_LOG_DEBUG, "SECTOR: %016lX\n", AV_RB64(sga->sector)); + av_log(s, AV_LOG_DEBUG, "stream: %X\n", sga->sector[1]); + av_log(s, AV_LOG_DEBUG, "END %s\n", where); +} + +static void update_type_size(AVFormatContext *s) +{ + SGADemuxContext *sga = s->priv_data; + + if (sga->idx >= 4) { + sga->packet_type = sga->sector[0]; + sga->payload_size = AV_RB16(sga->sector + 2); + } else { + sga->packet_type = 0; + sga->payload_size = 0; + } +} + +static int sga_video_packet(AVFormatContext *s, AVPacket *pkt) +{ + SGADemuxContext *sga = s->priv_data; + int ret; + + if (sga->payload_size <= 8) + return AVERROR_INVALIDDATA; + + if (sga->video_stream_index == -1) { + AVRational frame_rate; + + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->start_time = 0; + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + st->codecpar->codec_tag = 0; + st->codecpar->codec_id = AV_CODEC_ID_SGA_VIDEO; + sga->video_stream_index = st->index; + + if (sga->first_audio_size > 0 && sga->sample_rate > 0) { + frame_rate.num = sga->sample_rate; + frame_rate.den = sga->first_audio_size; + } else { + frame_rate.num = 15; + frame_rate.den = 1; + } + avpriv_set_pts_info(st, 64, frame_rate.den, frame_rate.num); + } + + ret = av_new_packet(pkt, sga->payload_size + 4); + if (ret < 0) + return AVERROR(ENOMEM); + memcpy(pkt->data, sga->sector, sga->payload_size + 4); + av_assert0(sga->idx >= sga->payload_size + 4); + memmove(sga->sector, sga->sector + sga->payload_size + 4, sga->idx - sga->payload_size - 4); + + pkt->stream_index = sga->video_stream_index; + pkt->duration = 1; + pkt->pos = sga->pkt_pos; + pkt->flags |= sga->flags; + sga->idx -= sga->payload_size + 4; + sga->flags = 0; + update_type_size(s); + + av_log(s, AV_LOG_DEBUG, "VIDEO PACKET: %d:%016lX i:%X\n", pkt->size, AV_RB64(sga->sector), sga->idx); + + return 0; +} + +static int sga_audio_packet(AVFormatContext *s, AVPacket *pkt) +{ + SGADemuxContext *sga = s->priv_data; + int ret; + + if (sga->payload_size <= 8) + return AVERROR_INVALIDDATA; + + if (sga->audio_stream_index == -1) { + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->start_time = 0; + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + st->codecpar->codec_tag = 0; + st->codecpar->codec_id = AV_CODEC_ID_PCM_SGA; + st->codecpar->channels = 1; + st->codecpar->channel_layout= AV_CH_LAYOUT_MONO; + st->codecpar->sample_rate = av_rescale(AV_RB16(sga->sector + 8), + SEGA_CD_PCM_NUM, + SEGA_CD_PCM_DEN); + sga->audio_stream_index = st->index; + + avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); + } + + ret = av_new_packet(pkt, sga->payload_size - 8); + if (ret < 0) + return AVERROR(ENOMEM); + memcpy(pkt->data, sga->sector + 12, sga->payload_size - 8); + av_assert0(sga->idx >= sga->payload_size + 4); + memmove(sga->sector, sga->sector + sga->payload_size + 4, sga->idx - sga->payload_size - 4); + + pkt->stream_index = sga->audio_stream_index; + pkt->duration = pkt->size; + pkt->pos = sga->pkt_pos; + pkt->flags |= sga->flags; + sga->idx -= sga->payload_size + 4; + sga->flags = 0; + update_type_size(s); + + av_log(s, AV_LOG_DEBUG, "AUDIO PACKET: %d:%016lX i:%X\n", pkt->size, AV_RB64(sga->sector), sga->idx); + + return 0; +} + +static int sga_packet(AVFormatContext *s, AVPacket *pkt) +{ + SGADemuxContext *sga = s->priv_data; + int ret = 0; + + if (sga->packet_type == 0xCD || + sga->packet_type == 0xCB || + sga->packet_type == 0xC9 || + sga->packet_type == 0xC8 || + sga->packet_type == 0xC7 || + sga->packet_type == 0xC6 || + sga->packet_type == 0xC1 || + sga->packet_type == 0xE7) { + ret = sga_video_packet(s, pkt); + } else if (sga->packet_type == 0xA1 || + sga->packet_type == 0xA2 || + sga->packet_type == 0xA3 || + sga->packet_type == 0xAA) { + ret = sga_audio_packet(s, pkt); + } else { + if (sga->idx == 0) + return AVERROR_EOF; + if (sga->sector[0]) + return AVERROR_INVALIDDATA; + memmove(sga->sector, sga->sector + 1, sga->idx - 1); + sga->idx--; + return AVERROR(EAGAIN); + } + + return ret; +} + +static int try_packet(AVFormatContext *s, AVPacket *pkt) +{ + SGADemuxContext *sga = s->priv_data; + int ret = AVERROR(EAGAIN); + + update_type_size(s); + if (sga->idx >= sga->payload_size + 4) { + print_stats(s, "before sga_packet"); + ret = sga_packet(s, pkt); + print_stats(s, "after sga_packet"); + if (ret != AVERROR(EAGAIN)) + return ret; + } + + return sga->idx < sga->payload_size + 4 ? AVERROR(EAGAIN) : ret; +} + +static int sga_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + SGADemuxContext *sga = s->priv_data; + AVIOContext *pb = s->pb; + int header, ret = 0; + + sga->pkt_pos = avio_tell(pb); + +retry: + update_type_size(s); + + print_stats(s, "start"); + if (avio_feof(pb) && + (!sga->payload_size || sga->idx < sga->payload_size + 4)) + return AVERROR_EOF; + + if (sga->idx < sga->payload_size + 4) { + ret = ffio_ensure_seekback(pb, 2); + if (ret < 0) + return ret; + + print_stats(s, "before read header"); + header = avio_rb16(pb); + if (!header) { + avio_skip(pb, 2046); + sga->left = 0; + } else if (!avio_feof(pb) && + ((header >> 15) || + !sga->sector_headers)) { + avio_seek(pb, -2, SEEK_CUR); + sga->flags = AV_PKT_FLAG_KEY; + sga->left = 2048; + } else { + sga->left = 2046; + } + + av_assert0(sga->idx + sga->left < sizeof(sga->sector)); + ret = avio_read(pb, sga->sector + sga->idx, sga->left); + if (ret > 0) + sga->idx += ret; + else if (ret != AVERROR_EOF && ret) + return ret; + print_stats(s, "after read header"); + + update_type_size(s); + } + + ret = try_packet(s, pkt); + if (ret == AVERROR(EAGAIN)) + goto retry; + + return ret; +} + +static int sga_seek(AVFormatContext *s, int stream_index, + int64_t timestamp, int flags) +{ + SGADemuxContext *sga = s->priv_data; + + sga->packet_type = sga->payload_size = sga->idx = 0; + memset(sga->sector, 0, sizeof(sga->sector)); + + return -1; +} + +AVInputFormat ff_sga_demuxer = { + .name = "sga", + .long_name = NULL_IF_CONFIG_SMALL("Digital Pictures SGA"), + .priv_data_size = sizeof(SGADemuxContext), + .read_probe = sga_probe, + .read_header = sga_read_header, + .read_packet = sga_read_packet, + .read_seek = sga_seek, + .extensions = "sga", + .flags = AVFMT_GENERIC_INDEX, +};