From patchwork Sat Aug 14 08:42:37 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: 29502 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2a4a:0:0:0:0 with SMTP id k10csp287424iov; Sat, 14 Aug 2021 01:42:58 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxOxCRGUmdj8BrM9fRQGGQXglJey1TC6XRGZraBCtXj7e7RdLPq/PG8r9ZvhxtwQZWzTKwC X-Received: by 2002:a17:906:dd9:: with SMTP id p25mr6602547eji.144.1628930577929; Sat, 14 Aug 2021 01:42:57 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1628930577; cv=none; d=google.com; s=arc-20160816; b=tTwoknEzSXqHb5/mHX/UluyG2wxQOvsfZdfGunDlzpmlSc6+1lq9h3nw5+t6ro+GOm 0uPHL8k/hxLfrEw7Go5FOv6gm+TRxI3/V9wk4pBwmc6E+EZ0HnjCcohy76tndX6+py3L jJGRHu9GIW30ctaLS4I8IMZ/vuJviXdJ1wiEROfsKgkIgrUcTykMi6HT+84o2qB33nOt vIlLCI5NviyTnSrntrpO/0mFFd8nIxdeXsOn8JEkgoAeh+o/TkQ5C/Wv/88pnFCBJLuK C5VqFRAnNpVc++DX3W+V1aE/gbP7MjSkHROAeucioyx808xIBlNzv921PsWHEXWsXQJI LwZA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:message-id:date:to:from:dkim-signature :delivered-to; bh=QKVUhe0ozZ+kTK7kwR7Aa69DTrPQnfYkJPdhtfmlyY4=; b=fmDfyE8tE3Yzcvd+H86OrJ43SYF5nuy6sJD15hkkXvqbJyUi3Qvvetr+lQnPexXOVA 9vhEMwOg3KpOM9y4h5HU3N41N2bRBQUNVXTak+YkmzYWnZ2JWjZGfRIlDNkjikxeg1A9 TBGkveCWcApjPvZ05RoT44Ppevdz/hclO7UYIQRO9zN98qWqCbsIqW8X1LNjhkilrY1r OZR06KUPiVNLKuijbUMyhBGv+z2f/JT369h+J+gNK5Bzk3MnaMtOuQgW/zZEPzjay5d8 vWcIODrMmlMin520aUOp0kJ904MpjWPe/zx3hqGuOoVXSY/Mvh3ptLLn+Q3uLxOX/Kzq fnkQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=r8MX5qAV; 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=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id p17si5262082edq.486.2021.08.14.01.42.57; Sat, 14 Aug 2021 01:42:57 -0700 (PDT) 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 header.s=20161025 header.b=r8MX5qAV; 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=QUARANTINE 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 032D968A5E7; Sat, 14 Aug 2021 11:42:54 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-ed1-f52.google.com (mail-ed1-f52.google.com [209.85.208.52]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 2701368041C for ; Sat, 14 Aug 2021 11:42:48 +0300 (EEST) Received: by mail-ed1-f52.google.com with SMTP id q3so14774379edt.5 for ; Sat, 14 Aug 2021 01:42:48 -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=t1Y53XuBuJDDjRWd6P4qCCX2JUL6pmnPo68Z4Kh6NPY=; b=r8MX5qAVhK2DQ9zx5zmgtiC5pCnK2faN3b1HEuGmH/GsSoKcB1/Z6mS6nBFknplgcN ondxmeegb5Xpwr4yyDphJW/7m25KTgYU0CBUMf+L6uA0CKiB2U9PaMH6FN3cyX481faA DF7o77hl4cagZnrvlT9ADa2fIfUlDtLFys0OZ1OxzKTZnW8ObvNZBAcPSsjtGnA7snVQ 4DB0uCw5t13I4buoVicqqWCAv9YkgVVxrqcqu8R/4O/XjvJkcEhO7kg66ZbBQgxDDX+1 jqyOGJYuJwAOtAgVROb7dqzRBYH9M/qFma3DCeNYoUQ7gKTT3WcKi/r0SoeIodI3cFwJ APng== 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=t1Y53XuBuJDDjRWd6P4qCCX2JUL6pmnPo68Z4Kh6NPY=; b=sjdDQraViT+0jA/rXKFHiY8vpfdfOP282+r2OZsSuediJT52/G3NtPlkwgpJ6kjmxM D0YXmZekUNLwLlLRIsve9POaylTj4b87qFp91RTvnJ8lrjvqNBsC7TT5ZveG5i0dYus+ ttSoT1UQJ9J5nxlbhGvGAMmbbLVB1R6FO7vS8K42ml4Wyah5TFjU2QrZsFV2pdnG8PUi ZF7DTvUKxzTFD85YKMFygAtUTVVh0kBh3+eEYKTM1hOeDbAR+BzlhI0AQPix1Bq5RJVf BhCVu637Hnf4fDMkVp2f7YcKuMrkKkb27TCm9oRzfFvxA8S7A/pG5WNTl4iMdyv4iECz FzZw== X-Gm-Message-State: AOAM532aKyXs75yYECRkQxrP2K6MzCG1NgHyaYX6LW7twu49+Uewmgg3 PR1jHah0ODdoi33g9IQv2zJWu8EwZlE= X-Received: by 2002:aa7:da52:: with SMTP id w18mr8126740eds.48.1628930567476; Sat, 14 Aug 2021 01:42:47 -0700 (PDT) Received: from localhost.localdomain ([95.168.116.124]) by smtp.gmail.com with ESMTPSA id ck17sm1976844edb.88.2021.08.14.01.42.46 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 14 Aug 2021 01:42:47 -0700 (PDT) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Sat, 14 Aug 2021 10:42:37 +0200 Message-Id: <20210814084238.17024-1-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 Subject: [FFmpeg-devel] [PATCH 1/2] avcodec/smc: add flush support X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 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" X-TUID: RxtwSbtsk9i/ Signed-off-by: Paul B Mahol --- libavcodec/smc.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libavcodec/smc.c b/libavcodec/smc.c index 9cd86216a2..2f43984b99 100644 --- a/libavcodec/smc.c +++ b/libavcodec/smc.c @@ -459,6 +459,17 @@ static int smc_decode_frame(AVCodecContext *avctx, return buf_size; } +static void smc_decode_flush(AVCodecContext *avctx) +{ + SmcContext *s = avctx->priv_data; + + memset(s->color_pairs, 0, sizeof(s->color_pairs)); + memset(s->color_quads, 0, sizeof(s->color_quads)); + memset(s->color_octets, 0, sizeof(s->color_octets)); + + av_frame_unref(s->frame); +} + static av_cold int smc_decode_end(AVCodecContext *avctx) { SmcContext *s = avctx->priv_data; @@ -477,6 +488,7 @@ const AVCodec ff_smc_decoder = { .init = smc_decode_init, .close = smc_decode_end, .decode = smc_decode_frame, + .flush = smc_decode_flush, .capabilities = AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; From patchwork Sat Aug 14 08:42:38 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: 29501 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2a4a:0:0:0:0 with SMTP id k10csp287481iov; Sat, 14 Aug 2021 01:43:06 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzTSC0ZC989Jq7h6+qkAiENvfSSS7R0l3XKbR7yE0SJXItIE9bzfH596IA52P5WyYkCeKF8 X-Received: by 2002:a17:906:6d11:: with SMTP id m17mr6609784ejr.325.1628930586703; Sat, 14 Aug 2021 01:43:06 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1628930586; cv=none; d=google.com; s=arc-20160816; b=c1W83oDvSs4/wNhpZp3/e4knNxqsZ+ODlYhgCf44zZoZ5SUjrSPySCNTvgi5/UQgeO 7pu4+lSi/I7ViQBhJKlyah/PqhYCjzP5auR0PmPxGZ09N8RGewExwdnjMqa3WTkoxgK0 2Ur9pbUHd/IR3/FyIJY1PFKbrLvaHE8ggAUBfafIiRP97ay3meUF+2u9ubZlCZFAuHUu qCrZnPB2uMy8mt37xXOuGT3ItrizQ5OfVsCsSQXco9jrs92/X6Pr4/b7bhswiGdUNxgx IUKR+SW4hV1sJEsx0rY5OaxnQ69VKe/mU2HqN0ZXGtb79G133uEu/25ljZQclC9LQdo4 cK/w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:dkim-signature:delivered-to; bh=9EPLdUhvpDreMXBI3cc+aCHyKWeYqhBexKbjz4sPgT8=; b=okOxa+yJC05rGJniZ58M/ZEbsh5Xho/lNatfsrcJOLYNrE2aWA3xP/g25C6o+9uBg7 GLbouqnZmRqK1V+gEFkb8U4cfDz/5yCtFFT3KchLdfoSUz38WttikoJOomtcHy/eZoM+ 4QciKcfGLmrKSZP8o0VpD4Rh34JSnB2MPvrmGo/Oc9ceSDmF3eAFEhqKkHY5pG8lY0WF 9KZpf9KMpA2T00qZxGwc18Il4Hy2+P9RuG/JnrIbnDeL7ZzAlxzZefrkZUsI1xFaD1T1 pYhxxT5AoHJ5SFBm+tQt1kLruOg13z4fS9nPSFKEjRA6xZ4FbCX/OCK/xEXbuBnmewzr C2dg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b="S/FEs3Rr"; 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=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id q24si2403877eju.305.2021.08.14.01.43.06; Sat, 14 Aug 2021 01:43:06 -0700 (PDT) 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 header.s=20161025 header.b="S/FEs3Rr"; 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=QUARANTINE 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 1365368A603; Sat, 14 Aug 2021 11:42:57 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-ej1-f45.google.com (mail-ej1-f45.google.com [209.85.218.45]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 12A4468A5F0 for ; Sat, 14 Aug 2021 11:42:49 +0300 (EEST) Received: by mail-ej1-f45.google.com with SMTP id qk33so22580852ejc.12 for ; Sat, 14 Aug 2021 01:42:49 -0700 (PDT) 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=PWZ7KUcCSPHYzxOtvbBpi2x/wwgRsE3X52F1i9+K6qI=; b=S/FEs3RrRDRczEWfyGou5UZ04bE+crFxHoJWBlmceHIRg6aVy/nZ8uyI+lSrm3dhQM 7qG/6WwswYdOqPkrklMaPXkfelr8BCpctuILYWX5fllonPk+LWJnc/zJ8Zd6a+WpVZuA 6KKT6p9AKRefSe0BaqYQTlYToC+gqcP3pWG0muTxf7HlsOMymjbEfPaoGbXYq7qHpaHe 5yNH7yo589AFqbgF4/L6CBTuT5n9o/wigbJtWUJLXUR/6BgBSiwFzvdVoArOI2v6Z/Sq W81YdnaI712bGvvu+NFpeETg89GYH1zC3ArZgbKiHAv89sqEav7dcWM6HSB6dAFfjgx0 eZPg== 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=PWZ7KUcCSPHYzxOtvbBpi2x/wwgRsE3X52F1i9+K6qI=; b=sVS8zb+2N0UVf+P5iz1r2pSJguFI8ANx7hMIMPxFXPIdwyJqFBZG9ZqIMvLSaBhLmm dNfgnxwuGpcijIT6z8BXnenUjLMU4jSJpoO7wwYjvUX3zhd/VDndBRRYCt3U6t6pRvH8 NKDrFCrHRWju3iZCHpEYYVGoD79mFDEyxNHtkm0/2voYYL6oqra1NyktYa+5hQDvFnB+ I++SKd3E5ghTVn8XNHTC/m3hqeCL2GnKM6OMZ4YhbYC/vY7PVULZS39gydwoy1ntFUK3 0UbLWJNYDhNq+ck/Fg3tSlu7Grd95JNlMpUURUhd8R0i3XQXpnOorr/NEHCSurNuIQo2 yxUg== X-Gm-Message-State: AOAM530gthZo/HiK4tOC02isK42QMvq8y/mxzMb3jfiReXAKegB4PvQZ X8qBAcWzYIXhpoXKpiwvQshvtoEekNo= X-Received: by 2002:a17:906:2809:: with SMTP id r9mr6463055ejc.463.1628930568395; Sat, 14 Aug 2021 01:42:48 -0700 (PDT) Received: from localhost.localdomain ([95.168.116.124]) by smtp.gmail.com with ESMTPSA id ck17sm1976844edb.88.2021.08.14.01.42.47 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 14 Aug 2021 01:42:48 -0700 (PDT) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Sat, 14 Aug 2021 10:42:38 +0200 Message-Id: <20210814084238.17024-2-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20210814084238.17024-1-onemda@gmail.com> References: <20210814084238.17024-1-onemda@gmail.com> Subject: [FFmpeg-devel] [PATCH 2/2] avcodec: add SMC encoder X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 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" X-TUID: 2kqdX6d93oRp Signed-off-by: Paul B Mahol --- libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/smcenc.c | 557 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 559 insertions(+) create mode 100644 libavcodec/smcenc.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 9a6adb9903..0dc564f5dc 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -621,6 +621,7 @@ OBJS-$(CONFIG_SIMBIOSIS_IMX_DECODER) += imx.o OBJS-$(CONFIG_SMACKAUD_DECODER) += smacker.o OBJS-$(CONFIG_SMACKER_DECODER) += smacker.o OBJS-$(CONFIG_SMC_DECODER) += smc.o +OBJS-$(CONFIG_SMC_ENCODER) += smcenc.o OBJS-$(CONFIG_SNOW_DECODER) += snowdec.o snow.o snow_dwt.o OBJS-$(CONFIG_SNOW_ENCODER) += snowenc.o snow.o snow_dwt.o \ h263.o h263data.o ituh263enc.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 71bc21aa05..c087b91148 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -299,6 +299,7 @@ extern const AVCodec ff_sgirle_decoder; extern const AVCodec ff_sheervideo_decoder; extern const AVCodec ff_simbiosis_imx_decoder; extern const AVCodec ff_smacker_decoder; +extern const AVCodec ff_smc_encoder; extern const AVCodec ff_smc_decoder; extern const AVCodec ff_smvjpeg_decoder; extern const AVCodec ff_snow_encoder; diff --git a/libavcodec/smcenc.c b/libavcodec/smcenc.c new file mode 100644 index 0000000000..4d70c76bad --- /dev/null +++ b/libavcodec/smcenc.c @@ -0,0 +1,557 @@ +/* + * QuickTime Graphics (SMC) Video Encoder + * Copyright (c) 2021 The FFmpeg project + * + * 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 smcenc.c + * QT SMC Video Encoder by Paul B. Mahol + */ + +#include "libavutil/avassert.h" +#include "libavutil/common.h" + +#include "avcodec.h" +#include "encode.h" +#include "internal.h" +#include "bytestream.h" + +#define CPAIR 2 +#define CQUAD 4 +#define COCTET 8 + +#define COLORS_PER_TABLE 256 + +typedef struct SMCContext { + AVFrame *prev_frame; // buffer for previous source frame + PutByteContext pb; + + uint8_t mono_value; + int nb_distinct; + int next_nb_distinct; + uint8_t distinct_values[16]; + uint8_t next_distinct_values[16]; + + uint16_t color_pairs[COLORS_PER_TABLE]; + uint32_t color_quads[COLORS_PER_TABLE]; + uint64_t color_octets[COLORS_PER_TABLE]; + + int first_frame; // flag set to one when the first frame is being processed + // so that comparisons with previous frame data in not attempted + int key_frame; +} SMCContext; + +#define ADVANCE_BLOCK(pixel_ptr, row_ptr, nb_blocks) \ +{ \ + for (int block = 0; block < nb_blocks && pixel_ptr && row_ptr; block++) { \ + pixel_ptr += 4; \ + if (pixel_ptr - row_ptr >= width) \ + { \ + row_ptr += stride * 4; \ + pixel_ptr = row_ptr; \ + } \ + } \ +} + +static int smc_cmp_values(const void *a, const void *b) +{ + const uint8_t *aa = a, *bb = b; + + return FFDIFFSIGN(aa[0], bb[0]); +} + +static int count_distinct_items(const uint8_t *block_values, + uint8_t *distinct_values, + int size) +{ + int n = 1; + + distinct_values[0] = block_values[0]; + for (int i = 1; i < size; i++) { + if (block_values[i] != block_values[i-1]) { + distinct_values[n] = block_values[i]; + n++; + } + } + + return n; +} + +static void smc_encode_stream(SMCContext *s, const AVFrame *frame) +{ + PutByteContext *pb = &s->pb; + const uint8_t *src_pixels = (const uint8_t *)frame->data[0]; + const int stride = frame->linesize[0]; + const uint8_t *prev_pixels = (const uint8_t *)s->prev_frame->data[0]; + const uint8_t *pixel_ptr, *row_ptr; + const int width = frame->width; + const uint8_t *old_pixel_ptr = NULL; + const uint8_t *old_row_ptr = NULL; + uint8_t block_values[16]; + int block_counter = 0; + int color_pair_index = 0; + int color_quad_index = 0; + int color_octet_index = 0; + int color_table_index; /* indexes to color pair, quad, or octet tables */ + int total_blocks; + + memset(s->color_pairs, 0, sizeof(s->color_pairs)); + memset(s->color_quads, 0, sizeof(s->color_quads)); + memset(s->color_octets, 0, sizeof(s->color_octets)); + + /* Number of 4x4 blocks in frame. */ + total_blocks = ((frame->width + 3) / 4) * ((frame->height + 3) / 4); + + pixel_ptr = row_ptr = src_pixels; + + while (block_counter < total_blocks) { + const uint8_t *xpixel_ptr = pixel_ptr; + const uint8_t *xrow_ptr = row_ptr; + int intra_skip_blocks = 0; + int inter_skip_blocks = 0; + uint16_t new_pair; + uint32_t new_quad; + uint64_t new_octet; + int cache_index; + int distinct = 0; + int blocks = 0; + + while (s->first_frame == 0 && s->key_frame == 0 && block_counter + inter_skip_blocks < total_blocks) { + int compare = 0; + + for (int y = 0; y < 4; y++) { + const ptrdiff_t offset = pixel_ptr - src_pixels; + const uint8_t *prev_pixel_ptr = prev_pixels + offset; + + compare |= memcmp(prev_pixel_ptr + y * stride, pixel_ptr + y * stride, 4); + if (compare) + break; + } + + if (compare) + break; + + if (inter_skip_blocks >= 256) + break; + inter_skip_blocks++; + + ADVANCE_BLOCK(pixel_ptr, row_ptr, 1) + } + + pixel_ptr = xpixel_ptr; + row_ptr = xrow_ptr; + + while (block_counter > 0 && block_counter + intra_skip_blocks < total_blocks && + old_pixel_ptr != NULL) { + int compare = 0; + + for (int y = 0; y < 4; y++) { + compare |= memcmp(old_pixel_ptr + y * stride, pixel_ptr + y * stride, 4); + if (compare) + break; + } + + if (compare) + break; + + if (intra_skip_blocks >= 256) + break; + intra_skip_blocks++; + ADVANCE_BLOCK(pixel_ptr, row_ptr, 1) + } + + pixel_ptr = xpixel_ptr; + row_ptr = xrow_ptr; + + while (block_counter + blocks < total_blocks && blocks < 256) { + for (int y = 0; y < 4; y++) + memcpy(block_values + y * 4, pixel_ptr + y * stride, 4); + + qsort(block_values, 16, sizeof(block_values[0]), smc_cmp_values); + s->next_nb_distinct = count_distinct_items(block_values, s->next_distinct_values, 16); + if (blocks == 0) { + memcpy(s->distinct_values, s->next_distinct_values, sizeof(s->distinct_values)); + s->nb_distinct = s->next_nb_distinct; + } else { + if (s->next_nb_distinct != s->nb_distinct || + memcmp(s->distinct_values, s->next_distinct_values, s->nb_distinct)) { + break; + } + } + s->mono_value = block_values[0]; + + distinct = s->nb_distinct; + ADVANCE_BLOCK(pixel_ptr, row_ptr, 1) + blocks++; + if (distinct > 1 && blocks >= 16) + break; + } + + pixel_ptr = xpixel_ptr; + row_ptr = xrow_ptr; + + if (intra_skip_blocks > 0 && intra_skip_blocks >= inter_skip_blocks) { + distinct = 17; + blocks = intra_skip_blocks; + } + + if (intra_skip_blocks > 16 && intra_skip_blocks >= inter_skip_blocks) { + distinct = 18; + blocks = intra_skip_blocks; + } + + if (inter_skip_blocks > 0 && inter_skip_blocks > intra_skip_blocks) { + distinct = 19; + blocks = inter_skip_blocks; + } + + if (inter_skip_blocks > 16 && inter_skip_blocks > intra_skip_blocks) { + distinct = 20; + blocks = inter_skip_blocks; + } + + if (intra_skip_blocks > 0) { + old_row_ptr = row_ptr; + old_pixel_ptr = pixel_ptr; + } + + switch (distinct) { + case 1: + if (blocks <= 16) { + bytestream2_put_byte(pb, 0x60 | (blocks - 1)); + } else { + bytestream2_put_byte(pb, 0x70); + bytestream2_put_byte(pb, blocks - 1); + } + bytestream2_put_byte(pb, s->mono_value); + ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks) + break; + case 2: + new_pair = 0; + for (int i = 0; i < CPAIR; i++) + new_pair |= s->distinct_values[i] << (i * 8); + + cache_index = -1; + for (int i = 0; i < COLORS_PER_TABLE; i++) { + if (s->color_pairs[i] == new_pair) { + cache_index = i; + break; + } + } + + if (cache_index >= 0) { + bytestream2_put_byte(pb, 0x90 | (blocks - 1)); + bytestream2_put_byte(pb, cache_index); + color_table_index = cache_index; + } else { + bytestream2_put_byte(pb, 0x80 | (blocks - 1)); + + color_table_index = color_pair_index; + s->color_pairs[color_table_index] = new_pair; + for (int i = 0; i < CPAIR; i++) + bytestream2_put_byte(pb, s->distinct_values[i]); + + color_pair_index++; + if (color_pair_index == COLORS_PER_TABLE) + color_pair_index = 0; + } + + for (int i = 0; i < blocks; i++) { + uint16_t pair = s->color_pairs[color_table_index] >> 8; + uint16_t flags = 0; + int shift = 15; + + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + flags |= (pair == pixel_ptr[x + y * stride]) << shift; + shift--; + } + } + + bytestream2_put_be16(pb, flags); + + ADVANCE_BLOCK(pixel_ptr, row_ptr, 1) + } + break; + case 3: + case 4: + new_quad = 0; + for (int i = 0; i < CQUAD; i++) + new_quad |= ((uint32_t)(s->distinct_values[i])) << (i * 8); + + cache_index = -1; + for (int i = 0; i < COLORS_PER_TABLE; i++) { + if (s->color_quads[i] == new_quad) { + cache_index = i; + break; + } + } + + if (cache_index >= 0) { + bytestream2_put_byte(pb, 0xB0 | (blocks - 1)); + bytestream2_put_byte(pb, cache_index); + color_table_index = cache_index; + } else { + bytestream2_put_byte(pb, 0xA0 | (blocks - 1)); + + color_table_index = color_quad_index; + for (int i = 0; i < CQUAD; i++) + bytestream2_put_byte(pb, s->distinct_values[i]); + s->color_quads[color_table_index] = new_quad; + + color_quad_index++; + if (color_quad_index == COLORS_PER_TABLE) + color_quad_index = 0; + } + + for (int i = 0; i < blocks; i++) { + uint32_t flags = 0; + uint8_t quad[4]; + int shift = 30; + + quad[0] = (s->color_quads[color_table_index] >> 0) & 0xFFu; + quad[1] = (s->color_quads[color_table_index] >> 8) & 0xFFu; + quad[2] = (s->color_quads[color_table_index] >> 16) & 0xFFu; + quad[3] = (s->color_quads[color_table_index] >> 24) & 0xFFu; + + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + int pixel = pixel_ptr[x + y * stride]; + uint32_t idx = 0; + + for (int w = 0; w < CQUAD; w++) { + if (quad[w] == pixel) { + idx = w; + break; + } + } + + flags |= idx << shift; + shift -= 2; + } + } + + bytestream2_put_be32(pb, flags); + + ADVANCE_BLOCK(pixel_ptr, row_ptr, 1) + } + break; + case 5: + case 6: + case 7: + case 8: + new_octet = 0; + for (int i = 0; i < COCTET; i++) + new_octet |= ((uint64_t)(s->distinct_values[i])) << (i * 8); + + cache_index = -1; + for (int i = 0; i < COLORS_PER_TABLE; i++) { + if (s->color_octets[i] == new_octet) { + cache_index = i; + break; + } + } + + if (cache_index >= 0) { + bytestream2_put_byte(pb, 0xD0 | (blocks - 1)); + bytestream2_put_byte(pb, cache_index); + color_table_index = cache_index; + } else { + bytestream2_put_byte(pb, 0xC0 | (blocks - 1)); + + color_table_index = color_octet_index; + for (int i = 0; i < COCTET; i++) + bytestream2_put_byte(pb, s->distinct_values[i]); + s->color_octets[color_table_index] = new_octet; + + color_octet_index++; + if (color_octet_index == COLORS_PER_TABLE) + color_octet_index = 0; + } + + for (int i = 0; i < blocks; i++) { + uint64_t flags = 0; + uint8_t octet[8]; + int shift = 45; + + octet[0] = (s->color_octets[color_table_index] >> 0) & 0xFFu; + octet[1] = (s->color_octets[color_table_index] >> 8) & 0xFFu; + octet[2] = (s->color_octets[color_table_index] >> 16) & 0xFFu; + octet[3] = (s->color_octets[color_table_index] >> 24) & 0xFFu; + octet[4] = (s->color_octets[color_table_index] >> 32) & 0xFFu; + octet[5] = (s->color_octets[color_table_index] >> 40) & 0xFFu; + octet[6] = (s->color_octets[color_table_index] >> 48) & 0xFFu; + octet[7] = (s->color_octets[color_table_index] >> 56) & 0xFFu; + + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + int pixel = pixel_ptr[x + y * stride]; + uint64_t idx = 0; + + for (int w = 0; w < COCTET; w++) { + if (octet[w] == pixel) { + idx = w; + break; + } + } + + flags |= idx << shift; + shift -= 3; + } + } + + bytestream2_put_be16(pb, ((flags >> 32) & 0xFFF0) | ((flags >> 8) & 0xF)); + bytestream2_put_be16(pb, ((flags >> 20) & 0xFFF0) | ((flags >> 4) & 0xF)); + bytestream2_put_be16(pb, ((flags >> 8) & 0xFFF0) | ((flags >> 0) & 0xF)); + + ADVANCE_BLOCK(pixel_ptr, row_ptr, 1) + } + break; + default: + bytestream2_put_byte(pb, 0xE0 | (blocks - 1)); + for (int i = 0; i < blocks; i++) { + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) + bytestream2_put_byte(pb, pixel_ptr[x + y * stride]); + } + + ADVANCE_BLOCK(pixel_ptr, row_ptr, 1) + } + break; + case 17: + bytestream2_put_byte(pb, 0x20 | (blocks - 1)); + ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks) + break; + case 18: + bytestream2_put_byte(pb, 0x30); + bytestream2_put_byte(pb, blocks - 1); + ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks) + break; + case 19: + bytestream2_put_byte(pb, 0x00 | (blocks - 1)); + ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks) + break; + case 20: + bytestream2_put_byte(pb, 0x10); + bytestream2_put_byte(pb, blocks - 1); + ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks) + break; + } + + block_counter += blocks; + } +} + +static int smc_encode_init(AVCodecContext *avctx) +{ + SMCContext *s = avctx->priv_data; + + avctx->bits_per_coded_sample = 8; + + s->prev_frame = av_frame_alloc(); + if (!s->prev_frame) + return AVERROR(ENOMEM); + + return 0; +} + +static int smc_encode_frame(AVCodecContext *avctx, AVPacket *pkt, + const AVFrame *frame, int *got_packet) +{ + SMCContext *s = avctx->priv_data; + const AVFrame *pict = frame; + uint8_t *pal; + int ret; + + ret = ff_alloc_packet(avctx, pkt, 8LL * avctx->height * avctx->width); + if (ret < 0) + return ret; + + if (avctx->gop_size == 0 || !s->prev_frame->data[0] || + (avctx->frame_number % avctx->gop_size) == 0) { + s->key_frame = 1; + } else { + s->key_frame = 0; + } + + bytestream2_init_writer(&s->pb, pkt->data, pkt->size); + + bytestream2_put_be32(&s->pb, 0x00); + + if (!s->prev_frame->data[0]) { + s->first_frame = 1; + s->prev_frame->format = pict->format; + s->prev_frame->width = pict->width; + s->prev_frame->height = pict->height; + ret = av_frame_get_buffer(s->prev_frame, 0); + if (ret < 0) + return ret; + } else { + s->first_frame = 0; + } + + pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); + memcpy(pal, frame->data[1], AVPALETTE_SIZE); + + smc_encode_stream(s, pict); + + av_shrink_packet(pkt, bytestream2_tell_p(&s->pb)); + + pkt->data[0] = 0x0; + + // write chunk length + AV_WB24(pkt->data + 1, pkt->size); + + av_frame_unref(s->prev_frame); + ret = av_frame_ref(s->prev_frame, frame); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "cannot add reference\n"); + return ret; + } + + if (s->key_frame) + pkt->flags |= AV_PKT_FLAG_KEY; + + *got_packet = 1; + + return 0; +} + +static int smc_encode_end(AVCodecContext *avctx) +{ + SMCContext *s = (SMCContext *)avctx->priv_data; + + av_frame_free(&s->prev_frame); + + return 0; +} + +const AVCodec ff_smc_encoder = { + .name = "smc", + .long_name = NULL_IF_CONFIG_SMALL("QuickTime Graphics (SMC)"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_SMC, + .priv_data_size = sizeof(SMCContext), + .init = smc_encode_init, + .encode2 = smc_encode_frame, + .close = smc_encode_end, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, + .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_PAL8, + AV_PIX_FMT_NONE}, +};