From patchwork Fri Feb 10 15:14:10 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 2482 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.89.21 with SMTP id n21csp544628vsb; Fri, 10 Feb 2017 07:14:48 -0800 (PST) X-Received: by 10.28.107.141 with SMTP id a13mr8464210wmi.61.1486739688176; Fri, 10 Feb 2017 07:14:48 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id c63si1548293wmf.132.2017.02.10.07.14.46; Fri, 10 Feb 2017 07:14:48 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id DBE95689DA1; Fri, 10 Feb 2017 17:14:39 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm0-f68.google.com (mail-wm0-f68.google.com [74.125.82.68]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id F16EE689D88 for ; Fri, 10 Feb 2017 17:14:33 +0200 (EET) Received: by mail-wm0-f68.google.com with SMTP id u63so7805758wmu.2 for ; Fri, 10 Feb 2017 07:14: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=m+k4227mjRb+hyaOcml9eFf/jWGLzjU8bpagzN/OsbU=; b=eeHt+ri3O3I0Xj2glPbsVA9BpqBf2zNBO1OxQThBxpd6+/bwuQSC7+3t16Aq9Y82l6 mX0yNugfm0i6ab3rNu5YtsR3Mp6Cs+MiABJMJDnIvJcUx1lfEeGztgp0gy5EQaN20CdX Dd/gggCBo55+/WXftAXwJ5HIiGxG0J3D+NRRnfiuwmeNXpqRGGwvrzTcwxVN+Dc67+aZ 5g+fxQ+SXaOVxbWZsNzbSdUE2ZQZuXQkgEd+X/z/WDIhYdLtVeNslKRH5Li6N1EzA1FX EzgQjvI1Q3xyNtarzMaCnezanuSAKTAJZJr0hBYdFVBXTwHJ/nV5171iu1UVpGDgV3ds 2V8A== 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=m+k4227mjRb+hyaOcml9eFf/jWGLzjU8bpagzN/OsbU=; b=mABDVjkpcnB8Ten4j/o8WK8ekdL4SLshOR/FBJVJrTtmw4jEBziGMbSGr2XWp9qqT1 Y5Y7URcoYBZ9yePa/dMeohaO9004E8yqyVZxZAg+umNsWE3WJgbdqCxWTdCsF0HlfIpJ NNacscLU1q9oVTpdZaRkIICcCrquHCN5Gz3kaksmtPaCdYft12/ijq3ECttygbOpveh0 GdHb2EfciEDZGH72YnF8aguxOP+s77IhOvzl+pWW7zyPGyJwjaxia55loyE6nnPVrw6m Ja8StEefHODFmzOWbP4ylJpseLGcX1Nt4Iy12R9vcP/kEuQ+CdPp4+ajTUe3oFpuH2Rg ZglA== X-Gm-Message-State: AMke39lnklJAKiRA49PexKfIc3xvMqLwmmJQNbW9V74mF4lHSjtgWf0dvtltGDjaep8ONg== X-Received: by 10.28.217.136 with SMTP id q130mr8266413wmg.13.1486739676740; Fri, 10 Feb 2017 07:14:36 -0800 (PST) Received: from localhost.localdomain ([94.250.174.60]) by smtp.gmail.com with ESMTPSA id 65sm79275wri.53.2017.02.10.07.14.32 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 10 Feb 2017 07:14:34 -0800 (PST) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Fri, 10 Feb 2017 16:14:10 +0100 Message-Id: <20170210151410.22141-1-onemda@gmail.com> X-Mailer: git-send-email 2.9.3 Subject: [FFmpeg-devel] [PATCH] avcodec: add FM Screen Capture Codec 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/avcodec.h | 1 + libavcodec/codec_desc.c | 7 + libavcodec/fmvc.c | 629 ++++++++++++++++++++++++++++++++++++++++++++++++ libavformat/riff.c | 1 + 6 files changed, 640 insertions(+) create mode 100644 libavcodec/fmvc.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 4765e9c..f0b2aa3 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -290,6 +290,7 @@ OBJS-$(CONFIG_FLASHSV_ENCODER) += flashsvenc.o OBJS-$(CONFIG_FLASHSV2_ENCODER) += flashsv2enc.o OBJS-$(CONFIG_FLASHSV2_DECODER) += flashsv.o OBJS-$(CONFIG_FLIC_DECODER) += flicvideo.o +OBJS-$(CONFIG_FMVC_DECODER) += fmvc.o OBJS-$(CONFIG_FOURXM_DECODER) += 4xm.o OBJS-$(CONFIG_FRAPS_DECODER) += fraps.o OBJS-$(CONFIG_FRWU_DECODER) += frwu.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index f92b2b7..10fd61c 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -192,6 +192,7 @@ void avcodec_register_all(void) REGISTER_ENCDEC (FLASHSV2, flashsv2); REGISTER_DECODER(FLIC, flic); REGISTER_ENCDEC (FLV, flv); + REGISTER_DECODER(FMVC, fmvc); REGISTER_DECODER(FOURXM, fourxm); REGISTER_DECODER(FRAPS, fraps); REGISTER_DECODER(FRWU, frwu); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 1e681e9..724e246 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -414,6 +414,7 @@ enum AVCodecID { AV_CODEC_ID_PSD, AV_CODEC_ID_PIXLET, AV_CODEC_ID_SPEEDHQ, + AV_CODEC_ID_FMVC, /* various PCM "codecs" */ AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 09d3c48..cb3debd 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -1353,6 +1353,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("Apple Pixlet"), .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_FMVC, + .type = AVMEDIA_TYPE_VIDEO, + .name = "fmvc", + .long_name = NULL_IF_CONFIG_SMALL("FM Screen Capture Codec"), + .props = AV_CODEC_PROP_LOSSLESS, + }, /* image codecs */ { diff --git a/libavcodec/fmvc.c b/libavcodec/fmvc.c new file mode 100644 index 0000000..1c6d9ae --- /dev/null +++ b/libavcodec/fmvc.c @@ -0,0 +1,629 @@ +/* + * FM Screen Capture Codec decoder + * + * Copyright (c) 2017 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 "avcodec.h" +#include "bytestream.h" +#include "internal.h" + +#define BLOCK_HEIGHT 112u +#define BLOCK_WIDTH 84u + +typedef struct InterBlock { + int w, h; + int size; + int xor; +} InterBlock; + +typedef struct FMVCContext { + GetByteContext gb; + PutByteContext pb; + uint8_t *buffer; + size_t buffer_size; + uint8_t *pbuffer; + size_t pbuffer_size; + int stride; + int bpp; + int yb, xb; + InterBlock *blocks; + int nb_blocks; +} FMVCContext; + +static int decode_type2(GetByteContext *gb, PutByteContext *pb) +{ + unsigned repeat = 0, first = 1, opcode; + int i, len, pos; + + while (bytestream2_get_bytes_left(gb) > 0) { + GetByteContext gbc; + + while (bytestream2_get_bytes_left(gb) > 0) { + if (first) { + first = 0; + if (bytestream2_peek_byte(gb) > 17) { + len = bytestream2_get_byte(gb) - 17; + if (len < 4) { + do { + bytestream2_put_byte(pb, bytestream2_get_byte(gb)); + --len; + } while (len); + opcode = bytestream2_peek_byte(gb); + continue; + } else { + do { + bytestream2_put_byte(pb, bytestream2_get_byte(gb)); + --len; + } while (len); + opcode = bytestream2_peek_byte(gb); + if (opcode < 0x10) { + bytestream2_skip(gb, 1); + pos = - (opcode >> 2) - 4 * bytestream2_get_byte(gb) - 2049; + + bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start); + bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET); + + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + len = opcode & 3; + if (!len) { + repeat = 1; + } else { + do { + bytestream2_put_byte(pb, bytestream2_get_byte(gb)); + --len; + } while (len); + opcode = bytestream2_peek_byte(gb); + } + continue; + } + } + repeat = 0; + } + repeat = 1; + } + if (repeat) { + repeat = 0; + opcode = bytestream2_peek_byte(gb); + if (opcode < 0x10) { + bytestream2_skip(gb, 1); + if (!opcode) { + if (!bytestream2_peek_byte(gb)) { + do { + opcode += 255; + bytestream2_skip(gb, 1); + } while (!bytestream2_peek_byte(gb)); + } + opcode += bytestream2_get_byte(gb) + 15; + } + bytestream2_put_le32(pb, bytestream2_get_le32(gb)); + for (i = opcode - 1; i > 0; --i) + bytestream2_put_byte(pb, bytestream2_get_byte(gb)); + opcode = bytestream2_peek_byte(gb); + if (opcode < 0x10) { + bytestream2_skip(gb, 1); + pos = - (opcode >> 2) - 4 * bytestream2_get_byte(gb) - 2049; + + bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start); + bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET); + + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + len = opcode & 3; + if (!len) { + repeat = 1; + } else { + do { + bytestream2_put_byte(pb, bytestream2_get_byte(gb)); + --len; + } while (len); + opcode = bytestream2_peek_byte(gb); + } + continue; + } + } + } + + if (opcode >= 0x40) { + bytestream2_skip(gb, 1); + pos = - ((opcode >> 2) & 7) - 1 - 8 * bytestream2_get_byte(gb); + len = (opcode >> 5) - 1; + + bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start); + bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET); + + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + do { + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + --len; + } while (len); + + len = opcode & 3; + + if (!len) { + repeat = 1; + } else { + do { + bytestream2_put_byte(pb, bytestream2_get_byte(gb)); + --len; + } while (len); + opcode = bytestream2_peek_byte(gb); + } + continue; + } else if (opcode < 0x20) { + break; + } + len = opcode & 0x1F; + bytestream2_skip(gb, 1); + if (!len) { + if (!bytestream2_peek_byte(gb)) { + do { + len += 255; + bytestream2_skip(gb, 1); + } while (!bytestream2_peek_byte(gb)); + } + len += bytestream2_get_byte(gb) + 31; + } + i = bytestream2_get_le16(gb); + pos = - (i >> 2) - 1; + + bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start); + bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET); + + if (len < 6 || bytestream2_tell_p(pb) - bytestream2_tell(&gbc) < 4) { + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + do { + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + --len; + } while (len); + } else { + bytestream2_put_le32(pb, bytestream2_get_le32(&gbc)); + for (len = len - 2; len; --len) + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + } + len = i & 3; + if (!len) { + repeat = 1; + } else { + do { + bytestream2_put_byte(pb, bytestream2_get_byte(gb)); + --len; + } while (len); + opcode = bytestream2_peek_byte(gb); + } + } + bytestream2_skip(gb, 1); + if (opcode < 0x10) { + pos = -(opcode >> 2) - 1 - 4 * bytestream2_get_byte(gb); + + bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start); + bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET); + + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + len = opcode & 3; + if (!len) { + repeat = 1; + } else { + do { + bytestream2_put_byte(pb, bytestream2_get_byte(gb)); + --len; + } while (len); + opcode = bytestream2_peek_byte(gb); + } + continue; + } + len = opcode & 7; + if (!len) { + if (!bytestream2_peek_byte(gb)) { + do { + bytestream2_skip(gb, 1); + len += 255; + } while (!bytestream2_peek_byte(gb)); + } + len += bytestream2_get_byte(gb) + 7; + } + i = bytestream2_get_le16(gb); + pos = bytestream2_tell_p(pb) - 2048 * (opcode & 8); + pos = pos - (i >> 2); + if (pos != bytestream2_tell_p(pb)) { + pos = pos - 0x4000; + + bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start); + bytestream2_seek(&gbc, pos, SEEK_SET); + + if (len < 6 || bytestream2_tell_p(pb) - bytestream2_tell(&gbc) < 4) { + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + do { + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + --len; + } while (len); + } else { + bytestream2_put_le32(pb, bytestream2_get_le32(&gbc)); + for (len = len - 2; len; --len) + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + } + + len = i & 3; + if (!len) { + repeat = 1; + } else { + do { + bytestream2_put_byte(pb, bytestream2_get_byte(gb)); + --len; + } while (len); + opcode = bytestream2_peek_byte(gb); + } + continue; + } + break; + } + + return 0; +} + +static int decode_type1(GetByteContext *gb, PutByteContext *pb) +{ + unsigned opcode, len; + int high = 0; + int i, pos; + + while (bytestream2_get_bytes_left(gb) > 0) { + GetByteContext gbc; + + while (bytestream2_get_bytes_left(gb) > 0) { + while (bytestream2_get_bytes_left(gb) > 0) { + opcode = bytestream2_get_byte(gb); + high = opcode >= 0x20; + if (high) + break; + if (opcode) + break; + opcode = bytestream2_get_byte(gb); + if (opcode < 0xF8) { + opcode = opcode + 32; + break; + } + i = opcode - 0xF8; + if (i) { + len = 256; + do { + len *= 2; + --i; + } while (i); + } else { + len = 280; + } + do { + bytestream2_put_le32(pb, bytestream2_get_le32(gb)); + bytestream2_put_le32(pb, bytestream2_get_le32(gb)); + len -= 8; + } while (len && bytestream2_get_bytes_left(gb) > 0); + } + + if (!high) { + do { + bytestream2_put_byte(pb, bytestream2_get_byte(gb)); + --opcode; + } while (opcode && bytestream2_get_bytes_left(gb) > 0); + + while (bytestream2_get_bytes_left(gb) > 0) { + GetByteContext gbc; + + opcode = bytestream2_get_byte(gb); + if (opcode >= 0x20) + break; + bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start); + + pos = -(opcode | 32 * bytestream2_get_byte(gb)) - 1; + bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET); + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + bytestream2_put_byte(pb, bytestream2_get_byte(gb)); + } + } + high = 0; + if (opcode < 0x40) + break; + bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start); + pos = (-((opcode & 0x1F) | 32 * bytestream2_get_byte(gb)) - 1); + bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET); + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + len = (opcode >> 5) - 1; + do { + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + --len; + } while (len && bytestream2_get_bytes_left(&gbc) > 0); + } + len = opcode & 0x1F; + if (!len) { + if (!bytestream2_peek_byte(gb)) { + do { + bytestream2_skip(gb, 1); + len += 255; + } while (!bytestream2_peek_byte(gb) && bytestream2_get_bytes_left(gb) > 0); + } + len += bytestream2_get_byte(gb) + 31; + } + pos = -bytestream2_get_byte(gb); + bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start); + bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos - (bytestream2_get_byte(gb) << 8), SEEK_SET); + if (bytestream2_tell_p(pb) == bytestream2_tell(&gbc)) + break; + if (len < 5 || bytestream2_tell_p(pb) - bytestream2_tell(&gbc) < 4) { + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + do { + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + --len; + } while (len && bytestream2_get_bytes_left(&gbc) > 0); + } else { + bytestream2_put_le32(pb, bytestream2_get_le32(&gbc)); + len--; + do { + bytestream2_put_byte(pb, bytestream2_get_byte(&gbc)); + len--; + } while (len && bytestream2_get_bytes_left(&gbc) > 0); + } + } + + return 0; +} + +static int decode_frame(AVCodecContext *avctx, + void *data, int *got_frame, + AVPacket *avpkt) +{ + FMVCContext *s = avctx->priv_data; + GetByteContext *gb = &s->gb; + PutByteContext *pb = &s->pb; + AVFrame *frame = data; + int ret, y, x; + + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) + return ret; + + bytestream2_init(gb, avpkt->data, avpkt->size); + bytestream2_skip(gb, 2); + + frame->key_frame = !!bytestream2_get_le16(gb); + frame->pict_type = frame->key_frame ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + + if (frame->key_frame) { + uint8_t *src, *dst; + int type, size; + + type = bytestream2_get_le16(gb); + size = bytestream2_get_le16(gb); + if (size > bytestream2_get_bytes_left(gb)) + return AVERROR_INVALIDDATA; + + bytestream2_init_writer(pb, s->buffer, s->buffer_size); + if (type == 1) { + decode_type1(gb, pb); + } else if (type == 2){ + decode_type2(gb, pb); + } else { + avpriv_report_missing_feature(avctx, "compression %d", type); + return AVERROR_PATCHWELCOME; + } + + src = s->buffer; + dst = frame->data[0] + (avctx->height - 1) * frame->linesize[0]; + for (y = 0; y < avctx->height; y++) { + memcpy(dst, src, avctx->width * s->bpp); + dst -= frame->linesize[0]; + src += avctx->width * s->bpp; + } + } else { + int block, nb_blocks, type, k, l; + uint32_t *src, *dst; + uint8_t *ssrc, *ddst; + + for (block = 0; block < s->nb_blocks; block++) + s->blocks[block].xor = 0; + + nb_blocks = bytestream2_get_le16(gb); + if (nb_blocks > s->nb_blocks) + return AVERROR_INVALIDDATA; + + bytestream2_init_writer(pb, s->pbuffer, s->pbuffer_size); + + type = bytestream2_get_le16(gb); + for (block = 0; block < nb_blocks; block++) { + int size, offset, start = 0; + + offset = bytestream2_get_le16(gb); + if (offset > s->nb_blocks) + return AVERROR_INVALIDDATA; + + size = bytestream2_get_le16(gb); + if (size > bytestream2_get_bytes_left(gb)) + return AVERROR_INVALIDDATA; + + start = bytestream2_tell_p(pb); + if (type == 1) { + decode_type1(gb, pb); + } else if (type == 2){ + decode_type2(gb, pb); + } else { + avpriv_report_missing_feature(avctx, "compression %d", type); + return AVERROR_PATCHWELCOME; + } + + if (s->blocks[offset].size * 4 != bytestream2_tell_p(pb) - start) + return AVERROR_INVALIDDATA; + + s->blocks[offset].xor = 1; + } + + src = (uint32_t *)s->pbuffer; + dst = (uint32_t *)s->buffer; + + for (block = 0, y = 0; y < s->yb; y++) { + int block_h = s->blocks[block].h; + uint32_t *output3 = dst; + + for (x = 0; x < s->xb; x++) { + int block_w = s->blocks[block].w; + uint32_t *output4 = dst; + + block_h = s->blocks[block].h; + if (s->blocks[block].xor) { + for (k = 0; k < block_h; k++) { + uint32_t *output5 = dst; + for (l = 0; l < block_w; l++) { + *dst++ ^= *src++; + } + dst = &output5[s->stride]; + } + } + dst = &output4[block_w]; + ++block; + } + dst = &output3[block_h * s->stride]; + } + + ssrc = s->buffer; + ddst = frame->data[0] + (avctx->height - 1) * frame->linesize[0]; + for (y = 0; y < avctx->height; y++) { + memcpy(ddst, ssrc, avctx->width * s->bpp); + ddst -= frame->linesize[0]; + ssrc += avctx->width * s->bpp; + } + } + + *got_frame = 1; + + return avpkt->size; +} + +static av_cold int decode_init(AVCodecContext *avctx) +{ + FMVCContext *s = avctx->priv_data; + int i, j, m, block = 0, h = BLOCK_HEIGHT, w = BLOCK_WIDTH; + + switch (avctx->bits_per_coded_sample) { + case 16: avctx->pix_fmt = AV_PIX_FMT_RGB555; break; + case 24: avctx->pix_fmt = AV_PIX_FMT_BGR24; break; + case 32: avctx->pix_fmt = AV_PIX_FMT_BGRA; break; + default: + av_log(avctx, AV_LOG_ERROR, "Unsupported bitdepth %i\n", avctx->bits_per_coded_sample); + return AVERROR_INVALIDDATA; + } + + s->stride = (avctx->width * avctx->bits_per_coded_sample + 31) / 32; + s->xb = s->stride / BLOCK_WIDTH; + m = s->stride % BLOCK_WIDTH; + if (m) { + if (m < 37) { + w = m + BLOCK_WIDTH; + } else { + w = m; + s->xb++; + } + } + + s->yb = avctx->height / BLOCK_HEIGHT; + m = avctx->height % BLOCK_HEIGHT; + if (m) { + if (m < 49) { + h = m + BLOCK_HEIGHT; + } else { + h = m; + s->yb++; + } + } + + s->nb_blocks = s->xb * s->yb; + s->blocks = av_calloc(s->nb_blocks, sizeof(*s->blocks)); + if (!s->blocks) + return AVERROR(ENOMEM); + + for (i = 0; i < s->yb; i++) { + for (j = 0; j < s->xb; j++) { + if (i != (s->yb - 1) || j != (s->xb - 1)) { + if (i == s->yb - 1) { + s->blocks[block].w = BLOCK_WIDTH; + s->blocks[block].h = h; + s->blocks[block].size = BLOCK_WIDTH * h; + } else if (j == s->xb - 1) { + s->blocks[block].w = w; + s->blocks[block].h = BLOCK_HEIGHT; + s->blocks[block].size = BLOCK_HEIGHT * w; + } else { + s->blocks[block].w = BLOCK_WIDTH; + s->blocks[block].h = BLOCK_HEIGHT; + s->blocks[block].size = BLOCK_WIDTH * BLOCK_HEIGHT; + } + } else { + s->blocks[block].w = w; + s->blocks[block].h = h; + s->blocks[block].size = w * h; + } + block++; + } + } + + s->bpp = avctx->bits_per_coded_sample >> 3; + s->buffer_size = avctx->width * avctx->height * 4; + s->pbuffer_size = avctx->width * avctx->height * 4; + s->buffer = av_malloc(s->buffer_size); + s->pbuffer = av_malloc(s->pbuffer_size); + if (!s->buffer || !s->pbuffer) + return AVERROR(ENOMEM); + + return 0; +} + +static av_cold int decode_end(AVCodecContext *avctx) +{ + FMVCContext *s = avctx->priv_data; + + av_freep(&s->buffer); + av_freep(&s->pbuffer); + av_freep(&s->blocks); + + return 0; +} + +AVCodec ff_fmvc_decoder = { + .name = "fmvc", + .long_name = NULL_IF_CONFIG_SMALL("FM Screen Capture Codec"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_FMVC, + .priv_data_size = sizeof(FMVCContext), + .init = decode_init, + .close = decode_end, + .decode = decode_frame, + .capabilities = AV_CODEC_CAP_DR1, +}; diff --git a/libavformat/riff.c b/libavformat/riff.c index d44b908..7be50c3 100644 --- a/libavformat/riff.c +++ b/libavformat/riff.c @@ -448,6 +448,7 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '5') }, { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '7') }, { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '9') }, + { AV_CODEC_ID_FMVC, MKTAG('F', 'M', 'V', 'C') }, { AV_CODEC_ID_NONE, 0 } };