From patchwork Tue Sep 1 11:29:44 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: 22047 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 436C744B8D6 for ; Tue, 1 Sep 2020 14:31:00 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 2558D68AB82; Tue, 1 Sep 2020 14:31:00 +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 5AD2D688337 for ; Tue, 1 Sep 2020 14:30:52 +0300 (EEST) Received: by mail-ej1-f68.google.com with SMTP id d26so1144265ejr.1 for ; Tue, 01 Sep 2020 04:30:52 -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=O+iqejblgGWcRAAjeONcerDRYE7xiu1UBozBl+/3YBQ=; b=SGBFhRLsk0pWAdUrVAR4exLzy+ZCyYxho/g2QOpZfvIdUIJafWSwZ+UH3stJZgzC+y ZNJ7UaD5eM5HjlVVPeINS/xvi/c/iy8vmc+BldJ/iMyH6TVgkDYAHJoAyiSFeMiVxKYM gBeKgog0dwz8A+uGfPNp55nNftH41R7M7ZUY+NPSleZjfevB4sNl5Gmv5mw4cv4rkTPS VhkJKzSloJT3nV6J4eMBUoGlKaFC7hFt8vv+RJQuCM17GZjomkHA/NO9A8zWi3XuYyUK uGlSzqQkjkMzAV38w/OsD1SJE+bjkZzfwlmBrcRI+Md6hwg7Tk/ljB+e6782Ft9LfwD9 Ftaw== 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=O+iqejblgGWcRAAjeONcerDRYE7xiu1UBozBl+/3YBQ=; b=ECEPLpPe4S68HxeoKelyJuqD1rl1szyM0RQexEqTqMPiYEYgYMwOhlLboxrCJ0ddUq rVxx6NmGQ/b4KylVTVYyy/5Q+vIW2chIvNPTlnsri2yYoYFOGjF5+dcCr4kcDNTd5sm4 BvcBXLtUuFoxlVuel5eP0+BchCCvP/+z+kJ3RB6iqdr6P0F/dxmBm+EdN7F3/OFbZt0h TKZMsQ0eiK7baocEn48b1ZMpJyG0kdP17UeeZsDY99QKeenNff5CCsxURe5fLOstYlYJ mp1Tf+lR5CzcnqZwLl4VQ+t4GnxCrmOKkc4ykb6+Qhj1qXBsR/mInLr6BTpGhQo5Xmq8 YBDQ== X-Gm-Message-State: AOAM531ymaZvMzP32S5IBfz2CYg0/qZX4RStGXOr3fv6tWAevVGbpLGI fsF+zwNQAQ0KS2Z8/UieyBaXzQeOieRftA== X-Google-Smtp-Source: ABdhPJx7mdy4aOFcZQjc34G/B9vYKuHhZX8U0ZhiuPu426Xo3LTrJ3hQXj9APICDcNIpXoAQcj2kNw== X-Received: by 2002:a17:906:2659:: with SMTP id i25mr1034902ejc.16.1598959849998; Tue, 01 Sep 2020 04:30:49 -0700 (PDT) Received: from localhost.localdomain ([31.45.254.223]) by smtp.gmail.com with ESMTPSA id b1sm351791eja.124.2020.09.01.04.30.48 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 01 Sep 2020 04:30:49 -0700 (PDT) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Tue, 1 Sep 2020 13:29:44 +0200 Message-Id: <20200901112946.3992-3-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200901112946.3992-1-onemda@gmail.com> References: <20200901112946.3992-1-onemda@gmail.com> Subject: [FFmpeg-devel] [PATCH 3/5] avcodec: add MobiClip 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/mobiclip.c | 1427 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 1437 insertions(+) create mode 100644 libavcodec/mobiclip.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 6f75f26c84..97fbe6b7a9 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -453,6 +453,7 @@ OBJS-$(CONFIG_MJPEG_VAAPI_ENCODER) += vaapi_encode_mjpeg.o OBJS-$(CONFIG_MLP_DECODER) += mlpdec.o mlpdsp.o OBJS-$(CONFIG_MLP_ENCODER) += mlpenc.o mlp.o OBJS-$(CONFIG_MMVIDEO_DECODER) += mmvideo.o +OBJS-$(CONFIG_MOBICLIP_DECODER) += mobiclip.o OBJS-$(CONFIG_MOTIONPIXELS_DECODER) += motionpixels.o OBJS-$(CONFIG_MOVTEXT_DECODER) += movtextdec.o ass.o OBJS-$(CONFIG_MOVTEXT_ENCODER) += movtextenc.o ass_split.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 3920eb37ce..8a4b3fb178 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -186,6 +186,7 @@ extern AVCodec ff_mjpeg_encoder; extern AVCodec ff_mjpeg_decoder; extern AVCodec ff_mjpegb_decoder; extern AVCodec ff_mmvideo_decoder; +extern AVCodec ff_mobiclip_decoder; extern AVCodec ff_motionpixels_decoder; extern AVCodec ff_mpeg1video_encoder; extern AVCodec ff_mpeg1video_decoder; diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 9a3eaf7d98..ceef244ebf 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -1784,6 +1784,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("PFM (Portable FloatMap) image"), .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, + { + .id = AV_CODEC_ID_MOBICLIP, + .type = AVMEDIA_TYPE_VIDEO, + .name = "mobiclip", + .long_name = NULL_IF_CONFIG_SMALL("MobiClip Video"), + .props = AV_CODEC_PROP_LOSSY, + }, /* various PCM "codecs" */ { diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h index aac7f63eb6..19d5014bb4 100644 --- a/libavcodec/codec_id.h +++ b/libavcodec/codec_id.h @@ -296,6 +296,7 @@ enum AVCodecID { AV_CODEC_ID_MV30, AV_CODEC_ID_NOTCHLC, AV_CODEC_ID_PFM, + AV_CODEC_ID_MOBICLIP, /* various PCM "codecs" */ AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs diff --git a/libavcodec/mobiclip.c b/libavcodec/mobiclip.c new file mode 100644 index 0000000000..175c5b86ae --- /dev/null +++ b/libavcodec/mobiclip.c @@ -0,0 +1,1427 @@ +/* + * MobiClip Video decoder + * Copyright (c) 2017 Adib Surani + * 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 "libavutil/avassert.h" + +#include "avcodec.h" +#include "bytestream.h" +#include "bswapdsp.h" +#include "get_bits.h" +#include "golomb.h" +#include "internal.h" + +static const uint8_t zigzag8x8_tab[] = +{ + 0x00, 0x01, 0x08, 0x10, 0x09, 0x02, 0x03, 0x0A, 0x11, 0x18, 0x20, 0x19, + 0x12, 0x0B, 0x04, 0x05, 0x0C, 0x13, 0x1A, 0x21, 0x28, 0x30, 0x29, 0x22, + 0x1B, 0x14, 0x0D, 0x06, 0x07, 0x0E, 0x15, 0x1C, 0x23, 0x2A, 0x31, 0x38, + 0x39, 0x32, 0x2B, 0x24, 0x1D, 0x16, 0x0F, 0x17, 0x1E, 0x25, 0x2C, 0x33, + 0x3A, 0x3B, 0x34, 0x2D, 0x26, 0x1F, 0x27, 0x2E, 0x35, 0x3C, 0x3D, 0x36, + 0x2F, 0x37, 0x3E, 0x3F +}; + +static const uint8_t zigzag4x4_tab[] = +{ + 0x00, 0x04, 0x01, 0x02, 0x05, 0x08, 0x0C, 0x09, 0x06, 0x03, 0x07, 0x0A, + 0x0D, 0x0E, 0x0B, 0x0F +}; + +static const uint8_t quant4x4_tab[][16] = +{ + { 10, 13, 13, 10, 16, 10, 13, 13, 13, 13, 16, 10, 16, 13, 13, 16 }, + { 11, 14, 14, 11, 18, 11, 14, 14, 14, 14, 18, 11, 18, 14, 14, 18 }, + { 13, 16, 16, 13, 20, 13, 16, 16, 16, 16, 20, 13, 20, 16, 16, 20 }, + { 14, 18, 18, 14, 23, 14, 18, 18, 18, 18, 23, 14, 23, 18, 18, 23 }, + { 16, 20, 20, 16, 25, 16, 20, 20, 20, 20, 25, 16, 25, 20, 20, 25 }, + { 18, 23, 23, 18, 29, 18, 23, 23, 23, 23, 29, 18, 29, 23, 23, 29 }, +}; + +static const uint8_t quant8x8_tab[][64] = +{ + { 20, 19, 19, 25, 18, 25, 19, 24, 24, 19, 20, 18, 32, 18, 20, 19, 19, 24, 24, 19, 19, 25, 18, 25, 18, 25, 18, 25, 19, 24, 24, 19, + 19, 24, 24, 19, 18, 32, 18, 20, 18, 32, 18, 24, 24, 19, 19, 24, 24, 18, 25, 18, 25, 18, 19, 24, 24, 19, 18, 32, 18, 24, 24, 18,}, + { 22, 21, 21, 28, 19, 28, 21, 26, 26, 21, 22, 19, 35, 19, 22, 21, 21, 26, 26, 21, 21, 28, 19, 28, 19, 28, 19, 28, 21, 26, 26, 21, + 21, 26, 26, 21, 19, 35, 19, 22, 19, 35, 19, 26, 26, 21, 21, 26, 26, 19, 28, 19, 28, 19, 21, 26, 26, 21, 19, 35, 19, 26, 26, 19,}, + { 26, 24, 24, 33, 23, 33, 24, 31, 31, 24, 26, 23, 42, 23, 26, 24, 24, 31, 31, 24, 24, 33, 23, 33, 23, 33, 23, 33, 24, 31, 31, 24, + 24, 31, 31, 24, 23, 42, 23, 26, 23, 42, 23, 31, 31, 24, 24, 31, 31, 23, 33, 23, 33, 23, 24, 31, 31, 24, 23, 42, 23, 31, 31, 23,}, + { 28, 26, 26, 35, 25, 35, 26, 33, 33, 26, 28, 25, 45, 25, 28, 26, 26, 33, 33, 26, 26, 35, 25, 35, 25, 35, 25, 35, 26, 33, 33, 26, + 26, 33, 33, 26, 25, 45, 25, 28, 25, 45, 25, 33, 33, 26, 26, 33, 33, 25, 35, 25, 35, 25, 26, 33, 33, 26, 25, 45, 25, 33, 33, 25,}, + { 32, 30, 30, 40, 28, 40, 30, 38, 38, 30, 32, 28, 51, 28, 32, 30, 30, 38, 38, 30, 30, 40, 28, 40, 28, 40, 28, 40, 30, 38, 38, 30, + 30, 38, 38, 30, 28, 51, 28, 32, 28, 51, 28, 38, 38, 30, 30, 38, 38, 28, 40, 28, 40, 28, 30, 38, 38, 30, 28, 51, 28, 38, 38, 28,}, + { 36, 34, 34, 46, 32, 46, 34, 43, 43, 34, 36, 32, 58, 32, 36, 34, 34, 43, 43, 34, 34, 46, 32, 46, 32, 46, 32, 46, 34, 43, 43, 34, + 34, 43, 43, 34, 32, 58, 32, 36, 32, 58, 32, 43, 43, 34, 34, 43, 43, 32, 46, 32, 46, 32, 34, 43, 43, 34, 32, 58, 32, 43, 43, 32,}, +}; + +static const uint8_t block4x4_coefficients_tab[] = +{ + 15, 0, 2, 1, 4, 8, 12, 3, 11, 13, 14, 7, 10, 5, 9, 6, +}; + +static const uint8_t pframe_block4x4_coefficients_tab[] = +{ + 0, 4, 1, 8, 2, 12, 3, 5, 10, 15, 7, 13, 14, 11, 9, 6, +}; + +static const uint8_t block8x8_coefficients_tab[] = +{ + 0x00, 0x1F, 0x3F, 0x0F, 0x08, 0x04, 0x02, 0x01, 0x0B, 0x0E, 0x1B, 0x0D, + 0x03, 0x07, 0x0C, 0x17, 0x1D, 0x0A, 0x1E, 0x05, 0x10, 0x2F, 0x37, 0x3B, + 0x13, 0x3D, 0x3E, 0x09, 0x1C, 0x06, 0x15, 0x1A, 0x33, 0x11, 0x12, 0x14, + 0x18, 0x20, 0x3C, 0x35, 0x19, 0x16, 0x3A, 0x30, 0x31, 0x32, 0x27, 0x34, + 0x2B, 0x2D, 0x39, 0x38, 0x23, 0x36, 0x2E, 0x21, 0x25, 0x22, 0x24, 0x2C, + 0x2A, 0x28, 0x29, 0x26, +}; + +static const uint8_t pframe_block8x8_coefficients_tab[] = +{ + 0x00, 0x0F, 0x04, 0x01, 0x08, 0x02, 0x0C, 0x03, 0x05, 0x0A, 0x0D, 0x07, 0x0E, 0x0B, 0x1F, 0x09, + 0x06, 0x10, 0x3F, 0x1E, 0x17, 0x1D, 0x1B, 0x1C, 0x13, 0x18, 0x1A, 0x12, 0x11, 0x14, 0x15, 0x20, + 0x2F, 0x16, 0x19, 0x37, 0x3D, 0x3E, 0x3B, 0x3C, 0x33, 0x35, 0x21, 0x24, 0x22, 0x28, 0x23, 0x2C, + 0x30, 0x27, 0x2D, 0x25, 0x3A, 0x2B, 0x2E, 0x2A, 0x31, 0x34, 0x38, 0x32, 0x29, 0x26, 0x39, 0x36 +}; + +static const uint8_t run_residue[2][256] = +{ + { + 12, 6, 4, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 27, 11, 7, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 41, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + }, + { + 27, 10, 5, 4, 3, 3, 3, 3, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 15, 10, 8, 4, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 21, 7, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + }, +}; + +static const uint8_t bits0[] = { + 9, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 7, 10, 10, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 5, 5, 5, 4, 2, 3, 4, 4, +}; + +static const uint16_t codes0[] = { + 0x0, 0x4, 0x5, 0x6, 0x7, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, + 0xB, 0xC, 0xD, 0xE, 0xF, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x3, 0x20, + 0x21, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0xB, 0xC, 0xD, 0x7, 0x2, 0x6, 0xE, 0xF, +}; + +static const uint16_t syms0[] = { + 0x0, 0x822, 0x803, 0xB, 0xA, 0xB81, 0xB61, 0xB41, 0xB21, 0x122, + 0x102, 0xE2, 0xC2, 0xA2, 0x63, 0x43, 0x24, 0xC, 0x25, 0x2E1, 0x301, + 0xBA1, 0xBC1, 0xBE1, 0xC01, 0x26, 0x44, 0x83, 0xA3, 0xC3, 0x142, + 0x321, 0x341, 0xC21, 0xC41, 0xC61, 0xC81, 0xCA1, 0xCC1, 0xCE1, 0xD01, + 0x0, 0x9, 0x8, 0xB01, 0xAE1, 0xAC1, 0xAA1, 0xA81, 0xA61, 0xA41, 0xA21, + 0x802, 0x2C1, 0x2A1, 0x281, 0x261, 0x241, 0x221, 0x201, 0x1E1, 0x82, + 0x62, 0x7, 0x6, 0xA01, 0x9E1, 0x9C1, 0x9A1, 0x981, 0x961, 0x941, 0x921, + 0x1C1, 0x1A1, 0x42, 0x23, 0x5, 0x901, 0x8E1, 0x8C1, 0x8A1, 0x181, 0x161, + 0x141, 0x4, 0x881, 0x861, 0x841, 0x821, 0x121, 0x101, 0xE1, 0xC1, 0x22, + 0x3, 0xA1, 0x81, 0x61, 0x801, 0x1, 0x21, 0x41, 0x2, +}; + +static const uint16_t syms1[] = { + 0x0, 0x807, 0x806, 0x16, 0x15, 0x842, 0x823, 0x805, 0x1A1, 0xA3, 0x102, 0x83, + 0x64, 0x44, 0x27, 0x14, 0x13, 0x17, 0x18, 0x28, 0x122, 0x862, 0x882, 0x9E1, 0xA01, + 0x19, 0x1A, 0x1B, 0x29, 0xC3, 0x2A, 0x45, 0xE3, 0x1C1, 0x808, 0x8A2, 0x8C2, 0xA21, + 0xA41, 0xA61, 0xA81, 0x0, 0x12, 0x11, 0x9C1, 0x9A1, 0x981, 0x961, 0x941, 0x822, 0x804, + 0x181, 0x161, 0xE2, 0xC2, 0xA2, 0x63, 0x43, 0x26, 0x25, 0x10, 0x82, 0xF, 0xE, 0xD, 0x901, + 0x8E1, 0x8C1, 0x803, 0x141, 0x121, 0x101, 0x921, 0x62, 0x24, 0xC, 0xB, 0xA, 0x881, 0x861, + 0xC1, 0x8A1, 0xE1, 0x42, 0x23, 0x9, 0x802, 0xA1, 0x841, 0x821, 0x81, 0x61, 0x8, 0x7, 0x22, + 0x6, 0x41, 0x5, 0x4, 0x801, 0x1, 0x2, 0x21, 0x3, +}; + +static const uint8_t mv_len[16] = +{ + 10, 8, 8, 7, 8, 8, 8, 7, 8, 8, 8, 7, 7, 7, 7, 6, +}; + +static const uint8_t mv_bits[16][10] = +{ + { 1, 3, 3, 4, 4, 5, 5, 5, 6, 6 }, + { 2, 2, 3, 3, 3, 4, 5, 5 }, + { 2, 2, 3, 3, 4, 4, 4, 4 }, + { 1, 3, 3, 4, 4, 4, 4 }, + { 2, 2, 3, 3, 3, 4, 5, 5 }, + { 2, 3, 3, 3, 3, 3, 4, 4 }, + { 1, 3, 3, 4, 4, 4, 5, 5 }, + { 1, 3, 3, 4, 4, 4, 4 }, + { 2, 2, 3, 3, 4, 4, 4, 4 }, + { 1, 3, 3, 4, 4, 4, 5, 5 }, + { 2, 2, 3, 3, 4, 4, 4, 4 }, + { 2, 2, 3, 3, 3, 4, 4 }, + { 1, 3, 3, 4, 4, 4, 4 }, + { 1, 3, 3, 4, 4, 4, 4 }, + { 2, 2, 3, 3, 3, 4, 4 }, + { 2, 2, 3, 3, 3, 3 }, +}; + +static const uint8_t mv_codes[16][10] = +{ + { 1, 0, 2, 2, 7, 6, 7, 12, 26, 27 }, + { 0, 2, 2, 6, 7, 6, 14, 15 }, + { 0, 3, 3, 4, 4, 5, 10, 11 }, + { 0, 5, 7, 8, 9, 12, 13 }, + { 1, 3, 0, 1, 5, 8, 18, 19 }, + { 3, 0, 2, 3, 4, 5, 2, 3 }, + { 0, 4, 5, 12, 13, 14, 30, 31 }, + { 0, 5, 6, 8, 9, 14, 15 }, + { 0, 3, 3, 4, 4, 5, 10, 11 }, + { 0, 4, 5, 12, 13, 14, 30, 31 }, + { 0, 3, 2, 5, 6, 7, 8, 9 }, + { 0, 3, 2, 3, 5, 8, 9 }, + { 0, 5, 6, 8, 9, 14, 15 }, + { 0, 5, 6, 8, 9, 14, 15 }, + { 0, 3, 2, 3, 5, 8, 9 }, + { 0, 3, 2, 3, 4, 5 }, +}; + +static const uint8_t mv_syms[16][10] = +{ + { 0, 8, 1, 2, 9, 3, 6, 7, 5, 4 }, + { 9, 1, 2, 8, 0, 3, 5, 4 }, + { 0, 1, 2, 9, 5, 4, 3, 8 }, + { 1, 2, 0, 5, 4, 8, 3 }, + { 8, 1, 2, 9, 0, 3, 5, 4 }, + { 1, 3, 2, 9, 8, 0, 5, 4 }, + { 1, 2, 0, 9, 8, 3, 5, 4 }, + { 1, 2, 0, 8, 5, 4, 3 }, + { 0, 1, 2, 8, 5, 4, 3, 9 }, + { 1, 2, 0, 9, 8, 3, 5, 4 }, + { 0, 1, 3, 2, 9, 8, 5, 4 }, + { 0, 1, 4, 3, 2, 8, 5 }, + { 1, 2, 0, 5, 4, 9, 3 }, + { 1, 2, 0, 9, 5, 4, 3 }, + { 0, 1, 5, 3, 2, 9, 4 }, + { 0, 1, 4, 5, 3, 2 }, +}; + +static const uint8_t mv_bits_mods[16][10] = +{ + { 2, 2, 3, 3, 4, 4, 5, 5, 5, 5 }, + { 2, 2, 3, 3, 4, 4, 4, 4 }, + { 2, 2, 3, 3, 4, 4, 4, 4 }, + { 1, 3, 3, 3, 4, 5, 5 }, + { 2, 2, 3, 3, 4, 4, 4, 4 }, + { 2, 2, 3, 3, 4, 4, 4, 4 }, + { 2, 2, 3, 3, 4, 4, 4, 4 }, + { 2, 2, 2, 3, 4, 5, 5 }, + { 2, 2, 3, 3, 4, 4, 4, 4 }, + { 2, 2, 3, 3, 4, 4, 4, 4 }, + { 2, 2, 3, 3, 3, 4, 5, 5 }, + { 2, 2, 3, 3, 3, 4, 4 }, + { 1, 3, 3, 4, 4, 4, 4 }, + { 2, 2, 3, 3, 3, 4, 4 }, + { 2, 2, 3, 3, 3, 4, 4 }, + { 2, 2, 3, 3, 3, 3 }, +}; + +static const uint8_t mv_codes_mods[16][10] = +{ + { 0, 3, 2, 3, 9, 10, 16, 17, 22, 23 }, + { 0, 3, 2, 4, 6, 7, 10, 11 }, + { 1, 3, 0, 5, 2, 3, 8, 9 }, + { 0, 4, 6, 7, 10, 22, 23 }, + { 0, 3, 3, 4, 4, 5, 10, 11 }, + { 0, 3, 2, 5, 6, 7, 8, 9 }, + { 0, 3, 2, 5, 6, 7, 8, 9 }, + { 0, 1, 3, 4, 10, 22, 23 }, + { 0, 3, 2, 4, 6, 7, 10, 11 }, + { 0, 3, 3, 5, 4, 5, 8, 9 }, + { 0, 3, 2, 3, 5, 9, 16, 17 }, + { 0, 3, 2, 4, 5, 6, 7 }, + { 0, 5, 6, 8, 9, 14, 15 }, + { 0, 3, 2, 4, 5, 6, 7 }, + { 0, 3, 2, 4, 5, 6, 7 }, + { 1, 2, 0, 1, 6, 7 }, +}; + +static const uint8_t mv_syms_mods[16][10] = +{ + { 1, 0, 8, 9, 2, 7, 4, 3, 5, 6 }, + { 0, 1, 9, 2, 5, 4, 3, 8 }, + { 0, 1, 3, 2, 9, 5, 4, 8 }, + { 1, 3, 2, 0, 4, 8, 5 }, + { 0, 1, 8, 2, 5, 4, 3, 9 }, + { 0, 1, 3, 2, 5, 9, 4, 8 }, + { 0, 1, 3, 2, 9, 5, 8, 4 }, + { 0, 2, 1, 3, 4, 8, 5 }, + { 0, 1, 3, 2, 8, 4, 5, 9 }, + { 2, 1, 3, 0, 8, 9, 5, 4 }, + { 0, 1, 4, 3, 2, 5, 8, 9 }, + { 0, 1, 4, 3, 2, 8, 5 }, + { 1, 2, 0, 9, 4, 5, 3 }, + { 2, 1, 4, 3, 0, 9, 5 }, + { 0, 1, 4, 3, 2, 9, 5 }, + { 1, 0, 5, 4, 3, 2 }, +}; + +typedef struct BlockXY { + int w, h; + int ax, ay; + int x, y; + int size; + uint8_t *block; + int linesize; +} BlockXY; + +typedef struct MotionXY { + int x, y; +} MotionXY; + +typedef struct MobiClipContext { + AVFrame *pic[6]; + + int current_pic; + int moflex; + int dct_tab_idx; + int quantizer; + + GetBitContext gb; + + uint8_t *bitstream; + int bitstream_size; + + VLC vlc[2]; + VLC mv_vlc[2][16]; + + int qtab[2][64]; + uint8_t pre[32]; + MotionXY *motion; + int motion_size; + + BswapDSPContext bdsp; +} MobiClipContext; + +static av_cold int mobiclip_init(AVCodecContext *avctx) +{ + MobiClipContext *s = avctx->priv_data; + int ret; + + if (avctx->width & 15 || avctx->height & 15) { + av_log(avctx, AV_LOG_ERROR, "width/height not multiple of 16\n"); + return AVERROR_INVALIDDATA; + } + + ff_bswapdsp_init(&s->bdsp); + + avctx->pix_fmt = AV_PIX_FMT_YUV420P; + + ret = ff_init_vlc_sparse(&s->vlc[0], 12, 104, + bits0, sizeof(*bits0), sizeof(*bits0), + codes0, sizeof(*codes0), sizeof(*codes0), + syms0, sizeof(*syms0), sizeof(*syms0), 0); + if (ret < 0) + return ret; + + ret = ff_init_vlc_sparse(&s->vlc[1], 12, 104, + bits0, sizeof(*bits0), sizeof(*bits0), + codes0, sizeof(*codes0), sizeof(*codes0), + syms1, sizeof(*syms1), sizeof(*syms1), 0); + if (ret < 0) + return ret; + + s->motion = av_calloc(avctx->width / 16 + 3, sizeof(MotionXY)); + if (!s->motion) + return AVERROR(ENOMEM); + s->motion_size = (avctx->width / 16 + 3) * sizeof(MotionXY); + + for (int i = 0; i < 6; i++) { + s->pic[i] = av_frame_alloc(); + if (!s->pic[i]) + return AVERROR(ENOMEM); + } + + for (int j = 0; j < 16; j++) { + ret = ff_init_vlc_sparse(&s->mv_vlc[0][j], 8, mv_len[j], + mv_bits_mods[j], sizeof(*mv_bits_mods[j]), sizeof(*mv_bits_mods[j]), + mv_codes_mods[j], sizeof(*mv_codes_mods[j]), sizeof(*mv_codes_mods[j]), + mv_syms_mods[j], sizeof(*mv_syms_mods[j]), sizeof(*mv_syms_mods[j]), 0); + if (ret < 0) + return ret; + + ret = ff_init_vlc_sparse(&s->mv_vlc[1][j], 8, mv_len[j], + mv_bits[j], sizeof(*mv_bits[j]), sizeof(*mv_bits[j]), + mv_codes[j], sizeof(*mv_codes[j]), sizeof(*mv_codes[j]), + mv_syms[j], sizeof(*mv_syms[j]), sizeof(*mv_syms[j]), 0); + if (ret < 0) + return ret; + } + + return 0; +} + +static void setup_qtables(AVCodecContext *avctx, int quantizer) +{ + MobiClipContext *s = avctx->priv_data; + int qx, qy; + + s->quantizer = quantizer; + + qx = quantizer % 6; + qy = quantizer / 6; + + for (int i = 0; i < 16; i++) + s->qtab[0][i] = quant4x4_tab[qx][i] << qy; + + for (int i = 0; i < 64; i++) + s->qtab[1][i] = quant8x8_tab[qx][i] << (qy - 2); + + for (int i = 0; i < 20; i++) + s->pre[i] = 9; +} + +static void inverse4(int *rs) +{ + int a = rs[0] + rs[2]; + int b = rs[0] - rs[2]; + int c = rs[1] + (rs[3] >> 1); + int d = (rs[1] >> 1) - rs[3]; + + rs[0] = a + c; + rs[1] = b + d; + rs[2] = b - d; + rs[3] = a - c; +} + +static void idct(int *arr, int size) +{ + int e, f, g, h, x3, x2, x1, x0; + int tmp[4]; + + if (size == 4) { + inverse4(arr); + return; + } + + tmp[0] = arr[0]; + tmp[1] = arr[2]; + tmp[2] = arr[4]; + tmp[3] = arr[6]; + + inverse4(tmp); + + e = arr[7] + arr[1] - arr[3] - (arr[3] >> 1); + f = arr[7] - arr[1] + arr[5] + (arr[5] >> 1); + g = arr[5] - arr[3] - arr[7] - (arr[7] >> 1); + h = arr[5] + arr[3] + arr[1] + (arr[1] >> 1); + x3 = g + (h >> 2); + x2 = e + (f >> 2); + x1 = (e >> 2) - f; + x0 = h - (g >> 2); + + arr[0] = tmp[0] + x0; + arr[1] = tmp[1] + x1; + arr[2] = tmp[2] + x2; + arr[3] = tmp[3] + x3; + arr[4] = tmp[3] - x3; + arr[5] = tmp[2] - x2; + arr[6] = tmp[1] - x1; + arr[7] = tmp[0] - x0; +} + +static int read_run_encoding(AVCodecContext *avctx, + int *last, int *run, int *level) +{ + MobiClipContext *s = avctx->priv_data; + GetBitContext *gb = &s->gb; + int n = get_vlc2(gb, s->vlc[s->dct_tab_idx].table, + s->vlc[s->dct_tab_idx].bits, 2); + + if (n < 0) + return AVERROR_INVALIDDATA; + + *last = (n >> 11) == 1; + *run = (n >> 5) & 0x3F; + *level = n & 0x1F; + + return 0; +} + +static int add_coefficients(AVCodecContext *avctx, AVFrame *frame, + int bx, int by, int size, int plane) +{ + MobiClipContext *s = avctx->priv_data; + GetBitContext *gb = &s->gb; + int mat[64] = { 0 }; + const uint8_t *ztab = size == 8 ? zigzag8x8_tab : zigzag4x4_tab; + const int *qtab = s->qtab[size == 8]; + uint8_t *dst = frame->data[plane] + by * frame->linesize[plane] + bx; + int ret = 0; + + for (int pos = 0; get_bits_left(gb) > 0; pos++) { + int qval, last, run, level; + + ret = read_run_encoding(avctx, &last, &run, &level); + if (ret < 0) + return ret; + + if (level) { + if (get_bits1(gb)) + level = -level; + } else if (!get_bits1(gb)) { + ret = read_run_encoding(avctx, &last, &run, &level); + if (ret < 0) + return ret; + level += run_residue[s->dct_tab_idx][(last ? 64 : 0) + run]; + if (get_bits1(gb)) + level = -level; + } else if (!get_bits1(gb)) { + ret = read_run_encoding(avctx, &last, &run, &level); + if (ret < 0) + return ret; + run += run_residue[s->dct_tab_idx][128 + (last ? 64 : 0) + level]; + if (get_bits1(gb)) + level = -level; + } else { + last = get_bits1(gb); + run = get_bits(gb, 6); + level = get_sbits(gb, 12); + } + + pos += run; + if (pos >= size * size) + return AVERROR_INVALIDDATA; + qval = qtab[pos]; + mat[ztab[pos]] = qval * level; + + if (last) + break; + } + + mat[0] += 32; + for (int y = 0; y < size; y++) + idct(&mat[y * size], size); + + for (int y = 0; y < size; y++) { + for (int x = y + 1; x < size; x++) { + int a = mat[x * size + y]; + int b = mat[y * size + x]; + + mat[y * size + x] = a; + mat[x * size + y] = b; + } + + idct(&mat[y * size], size); + for (int x = 0; x < size; x++) + dst[x] = av_clip_uint8(dst[x] + (mat[y * size + x] >> 6)); + dst += frame->linesize[plane]; + } + + return ret; +} + +static int add_pframe_coefficients(AVCodecContext *avctx, AVFrame *frame, + int bx, int by, int size, int plane) +{ + MobiClipContext *s = avctx->priv_data; + GetBitContext *gb = &s->gb; + int ret, tmp = get_ue_golomb(gb); + + if (tmp == 0) { + ret = add_coefficients(avctx, frame, bx, by, size, plane); + } else if (tmp < FF_ARRAY_ELEMS(pframe_block4x4_coefficients_tab)) { + int flags = pframe_block4x4_coefficients_tab[tmp]; + + for (int y = by; y < by + 8; y += 4) { + for (int x = bx; x < bx + 8; x += 4) { + if (flags & 1) { + ret = add_coefficients(avctx, frame, x, y, 4, plane); + if (ret < 0) + return ret; + } + flags >>= 1; + } + } + } else { + ret = AVERROR_INVALIDDATA; + } + + return ret; +} + +static int adjust(int x, int size) +{ + return size == 16 ? (x + 1) >> 1 : x; +} + +static uint8_t pget(BlockXY b) +{ + BlockXY ret = b; + int x, y; + + if (b.x == -1 && b.y >= b.size) { + ret.x = -1, ret.y = b.size - 1; + } else if (b.x >= -1 && b.y >= -1) { + ret.x = b.x, ret.y = b.y; + } else if (b.x == -1 && b.y == -2) { + ret.x = 0, ret.y = -1; + } else if (b.x == -2 && b.y == -1) { + ret.x = -1, ret.y = 0; + } + + y = av_clip(ret.ay + ret.y, 0, ret.h - 1); + x = av_clip(ret.ax + ret.x, 0, ret.w - 1); + + return ret.block[y * ret.linesize + x]; +} + +static uint8_t Half(int a, int b) +{ + return ((a + b) + 1) / 2; +} + +static uint8_t Half3(int a, int b, int c) +{ + return ((a + b + b + c) * 2 / 4 + 1) / 2;; +} + +static uint8_t pick_above(BlockXY bxy) +{ + bxy.y = bxy.y - 1; + + return pget(bxy); +} + +static uint8_t pick_left(BlockXY bxy) +{ + bxy.x = bxy.x - 1; + + return pget(bxy); +} + +static uint8_t HalfHorz(BlockXY bxy) +{ + BlockXY a = bxy, b = bxy, c = bxy; + + a.x -= 1; + c.x += 1; + + return Half3(pget(a), pget(b), pget(c)); +} + +static uint8_t HalfVert(BlockXY bxy) +{ + BlockXY a = bxy, b = bxy, c = bxy; + + a.y -= 1; + c.y += 1; + + return Half3(pget(a), pget(b), pget(c)); +} + +static uint8_t pick_4(BlockXY bxy) +{ + int val; + + if ((bxy.x % 2) == 0) { + BlockXY ba, bb; + int a, b; + + ba = bxy; + ba.x = -1; + ba.y = bxy.y + bxy.x / 2; + a = pget(ba); + + bb = bxy; + bb.x = -1; + bb.y = bxy.y + bxy.x / 2 + 1; + b = pget(bb); + + val = Half(a, b); + } else { + BlockXY ba; + + ba = bxy; + ba.x = -1; + ba.y = bxy.y + bxy.x / 2 + 1; + val = HalfVert(ba); + } + + return val; +} + +static uint8_t pick_5(BlockXY bxy) +{ + int val; + + if (bxy.x == 0) { + BlockXY a = bxy; + BlockXY b = bxy; + + a.x = -1; + a.y -= 1; + + b.x = -1; + + val = Half(pget(a), pget(b)); + } else if (bxy.y == 0) { + BlockXY a = bxy; + + a.x -= 2; + a.y -= 1; + + val = HalfHorz(a); + } else if (bxy.x == 1) { + BlockXY a = bxy; + + a.x -= 2; + a.y -= 1; + + val = HalfVert(a); + } else { + BlockXY a = bxy; + + a.x -= 2; + a.y -= 1; + + val = pget(a); + } + + return val; +} + +static uint8_t pick_6(BlockXY bxy) +{ + int val; + + if (bxy.y == 0) { + BlockXY a = bxy; + BlockXY b = bxy; + + a.x -= 1; + a.y = -1; + + b.y = -1; + + val = Half(pget(a), pget(b)); + } else if (bxy.x == 0) { + BlockXY a = bxy; + + a.x -= 1; + a.y -= 2; + + val = HalfVert(a); + } else if (bxy.y == 1) { + BlockXY a = bxy; + + a.x -= 1; + a.y -= 2; + + val = HalfHorz(a); + } else { + BlockXY a = bxy; + + a.x -= 1; + a.y -= 2; + + val = pget(a); + } + + return val; +} + +static uint8_t pick_7(BlockXY bxy) +{ + int clr, acc1, acc2; + BlockXY a = bxy; + + a.x -= 1; + a.y -= 1; + clr = pget(a); + if (bxy.x && bxy.y) + return clr; + + if (bxy.x == 0) { + a.x = -1; + a.y = bxy.y; + } else { + a.x = bxy.x - 2; + a.y = -1; + } + acc1 = pget(a); + + if (bxy.y == 0) { + a.x = bxy.x; + a.y = -1; + } else { + a.x = -1; + a.y = bxy.y - 2; + } + acc2 = pget(a); + + return Half3(acc1, clr, acc2); +} + +static uint8_t pick_8(BlockXY bxy) +{ + BlockXY ba = bxy; + BlockXY bb = bxy; + int val; + + if (bxy.y == 0) { + int a, b; + + ba.y = -1; + a = pget(ba); + + bb.x += 1; + bb.y = -1; + + b = pget(bb); + + val = Half(a, b); + } else if (bxy.y == 1) { + ba.x += 1; + ba.y -= 2; + + val = HalfHorz(ba); + } else if (bxy.x < bxy.size - 1) { + ba.x += 1; + ba.y -= 2; + + val = pget(ba); + } else if (bxy.y % 2 == 0) { + int a, b; + + ba.x = bxy.y / 2 + bxy.size - 1; + ba.y = -1; + a = pget(ba); + + bb.x = bxy.y / 2 + bxy.size; + bb.y = -1; + + b = pget(bb); + + val = Half(a, b); + } else { + ba.x = bxy.y / 2 + bxy.size; + ba.y = -1; + + val = HalfHorz(ba); + } + + return val; +} + +static void block_fill_simple(uint8_t *block, int size, int linesize, int fill) +{ + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + block[x] = fill; + } + block += linesize; + } +} + +static void block_fill(uint8_t *block, int size, int linesize, + int w, int h, int ax, int ay, + uint8_t (*pick)(BlockXY bxy)) +{ + BlockXY bxy; + + bxy.size = size; + bxy.block = block; + bxy.linesize = linesize; + bxy.w = w; + bxy.h = h; + bxy.ay = ay; + bxy.ax = ax; + + for (int y = 0; y < size; y++) { + bxy.y = y; + for (int x = 0; x < size; x++) { + uint8_t val; + + bxy.x = x; + + val = pick(bxy); + + block[ax + x + (ay + y) * linesize] = val; + } + } +} + +static int block_sum(const uint8_t *block, int w, int h, int linesize) +{ + int sum = 0; + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + sum += block[x]; + } + block += linesize; + } + + return sum; +} + +static int predict_intra(AVCodecContext *avctx, AVFrame *frame, int ax, int ay, + int pmode, int add_coeffs, int size, int plane) +{ + MobiClipContext *s = avctx->priv_data; + GetBitContext *gb = &s->gb; + int w = avctx->width >> !!plane, h = avctx->height >> !!plane; + int ret = 0; + + switch (pmode) { + case 0: + block_fill(frame->data[plane], size, frame->linesize[plane], w, h, ax, ay, pick_above); + break; + case 1: + block_fill(frame->data[plane], size, frame->linesize[plane], w, h, ax, ay, pick_left); + break; + case 2: + { + int arr1[16]; + int arr2[16]; + uint8_t *top = frame->data[plane] + (ay - 1) * frame->linesize[plane] + ax; + uint8_t *left = frame->data[plane] + ay * frame->linesize[plane] + ax - 1; + int bottommost = frame->data[plane][(ay + size - 1) * frame->linesize[plane] + ax - 1]; + int rightmost = frame->data[plane][(ay - 1) * frame->linesize[plane] + ax + size - 1]; + int avg = (bottommost + rightmost + 1) / 2 + 2 * get_se_golomb(gb); + int r6 = adjust(avg - bottommost, size); + int r9 = adjust(avg - rightmost, size); + int shift = adjust(size, size) == 8 ? 3 : 2; + uint8_t *block; + + for (int x = 0; x < size; x++) { + int val = top[x]; + arr1[x] = adjust(((bottommost - val) * (1 << shift)) + r6 * (x + 1), size); + } + + for (int y = 0; y < size; y++) { + int val = left[y * frame->linesize[plane]]; + arr2[y] = adjust(((rightmost - val) * (1 << shift)) + r9 * (y + 1), size); + } + + block = frame->data[plane] + ay * frame->linesize[plane] + ax; + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + block[x] = (((top[x] + left[0] + ((arr1[x] * (y + 1) + + arr2[y] * (x + 1)) >> 2 * shift)) + 1) / 2) & 0xFF; + } + block += frame->linesize[plane]; + left += frame->linesize[plane]; + } + } + break; + case 3: + { + uint8_t fill; + + if (ax == 0 && ay == 0) { + fill = 0x80; + } else if (ax >= 1 && ay >= 1) { + int left = block_sum(frame->data[plane] + ay * frame->linesize[plane] + ax - 1, + 1, size, frame->linesize[plane]); + int top = block_sum(frame->data[plane] + (ay - 1) * frame->linesize[plane] + ax, + size, 1, frame->linesize[plane]); + + fill = ((left + top) * 2 / (2 * size) + 1) / 2; + } else if (ax >= 1) { + fill = (block_sum(frame->data[plane] + ay * frame->linesize[plane] + ax - 1, + 1, size, frame->linesize[plane]) * 2 / size + 1) / 2; + } else if (ay >= 1) { + fill = (block_sum(frame->data[plane] + (ay - 1) * frame->linesize[plane] + ax, + size, 1, frame->linesize[plane]) * 2 / size + 1) / 2; + } else { + return -1; + } + + block_fill_simple(frame->data[plane] + ay * frame->linesize[plane] + ax, + size, frame->linesize[plane], fill); + } + break; + case 4: + block_fill(frame->data[plane], size, frame->linesize[plane], w, h, ax, ay, pick_4); + break; + case 5: + block_fill(frame->data[plane], size, frame->linesize[plane], w, h, ax, ay, pick_5); + break; + case 6: + block_fill(frame->data[plane], size, frame->linesize[plane], w, h, ax, ay, pick_6); + break; + case 7: + block_fill(frame->data[plane], size, frame->linesize[plane], w, h, ax, ay, pick_7); + break; + case 8: + block_fill(frame->data[plane], size, frame->linesize[plane], w, h, ax, ay, pick_8); + break; + } + + if (add_coeffs) + ret = add_coefficients(avctx, frame, ax, ay, size, plane); + + return ret; +} + +static int get_prediction(AVCodecContext *avctx, int x, int y, int size) +{ + MobiClipContext *s = avctx->priv_data; + GetBitContext *gb = &s->gb; + int index = (y & 0xC) | (x / 4 % 4); + + uint8_t val = FFMIN(s->pre[index], index % 4 == 0 ? 9 : s->pre[index + 3]); + if (val == 9) + val = 3; + + if (!get_bits1(gb)) { + int x = get_bits(gb, 3); + val = x + (x >= val ? 1 : 0); + } + + s->pre[index + 4] = val; + if (size == 8) + s->pre[index + 5] = s->pre[index + 8] = s->pre[index + 9] = val; + + return val; +} + +static int process_block(AVCodecContext *avctx, AVFrame *frame, + int x, int y, int pmode, int has_coeffs, int plane) +{ + MobiClipContext *s = avctx->priv_data; + GetBitContext *gb = &s->gb; + int tmp, ret; + + if (!has_coeffs) { + if (pmode < 0) + pmode = get_prediction(avctx, x, y, 8); + return predict_intra(avctx, frame, x, y, pmode, 0, 8, plane); + } + + tmp = get_ue_golomb(gb); + if (tmp < 0 || tmp > FF_ARRAY_ELEMS(block4x4_coefficients_tab)) + return AVERROR_INVALIDDATA; + + if (tmp == 0) { + if (pmode < 0) + pmode = get_prediction(avctx, x, y, 8); + ret = predict_intra(avctx, frame, x, y, pmode, 1, 8, plane); + } else { + int flags = block4x4_coefficients_tab[tmp - 1]; + + for (int by = y; by < y + 8; by += 4) { + for (int bx = x; bx < x + 8; bx += 4) { + int new_pmode = pmode; + + if (new_pmode < 0) + new_pmode = get_prediction(avctx, bx, by, 4); + ret = predict_intra(avctx, frame, bx, by, new_pmode, flags & 1, 4, plane); + if (ret < 0) + return ret; + flags >>= 1; + } + } + } + + return ret; +} + +static int decode_macroblock(AVCodecContext *avctx, AVFrame *frame, + int x, int y, int predict) +{ + MobiClipContext *s = avctx->priv_data; + GetBitContext *gb = &s->gb; + int flags, pmode_uv, idx = get_ue_golomb(gb); + int ret = 0; + + if (idx < 0 || idx >= FF_ARRAY_ELEMS(block8x8_coefficients_tab)) + return AVERROR_INVALIDDATA; + + flags = block8x8_coefficients_tab[idx]; + + if (predict) { + ret = process_block(avctx, frame, x, y, -1, flags & 1, 0); + if (ret < 0) + return ret; + flags >>= 1; + ret = process_block(avctx, frame, x + 8, y, -1, flags & 1, 0); + if (ret < 0) + return ret; + flags >>= 1; + ret = process_block(avctx, frame, x, y + 8, -1, flags & 1, 0); + if (ret < 0) + return ret; + flags >>= 1; + ret = process_block(avctx, frame, x + 8, y + 8, -1, flags & 1, 0); + if (ret < 0) + return ret; + flags >>= 1; + } else { + int pmode = get_bits(gb, 3); + + if (pmode == 2) { + ret = predict_intra(avctx, frame, x, y, pmode, 0, 16, 0); + if (ret < 0) + return ret; + pmode = 9; + } + + ret = process_block(avctx, frame, x, y, pmode, flags & 1, 0); + if (ret < 0) + return ret; + flags >>= 1; + ret = process_block(avctx, frame, x + 8, y, pmode, flags & 1, 0); + if (ret < 0) + return ret; + flags >>= 1; + ret = process_block(avctx, frame, x, y + 8, pmode, flags & 1, 0); + if (ret < 0) + return ret; + flags >>= 1; + ret = process_block(avctx, frame, x + 8, y + 8, pmode, flags & 1, 0); + if (ret < 0) + return ret; + flags >>= 1; + } + + pmode_uv = get_bits(gb, 3); + if (pmode_uv == 2) { + ret = predict_intra(avctx, frame, x >> 1, y >> 1, pmode_uv, 0, 8, 1); + if (ret < 0) + return ret; + ret = predict_intra(avctx, frame, x >> 1, y >> 1, pmode_uv, 0, 8, 2); + if (ret < 0) + return ret; + pmode_uv = 9; + } + + ret = process_block(avctx, frame, x >> 1, y >> 1, pmode_uv, flags & 1, 1); + if (ret < 0) + return ret; + flags >>= 1; + ret = process_block(avctx, frame, x >> 1, y >> 1, pmode_uv, flags & 1, 2); + if (ret < 0) + return ret; + + return 0; +} + +static int get_index(int x) +{ + return x == 16 ? 0 : x == 8 ? 1 : x == 4 ? 2 : x == 2 ? 3 : 0; +} + +static int predict_motion(AVCodecContext *avctx, + int width, int height, int index, + int offsetm, int offsetx, int offsety) +{ + MobiClipContext *s = avctx->priv_data; + MotionXY *motion = s->motion; + GetBitContext *gb = &s->gb; + int fheight = avctx->height; + int fwidth = avctx->width; + + if (index <= 5) { + int sidx = -FFMAX(1, index) + s->current_pic; + MotionXY mv = s->motion[0]; + + if (sidx < 0) + sidx += 6; + + if (index > 0) { + mv.x = mv.x + get_se_golomb(gb); + mv.y = mv.y + get_se_golomb(gb); + } + + motion[offsetm].x = mv.x; + motion[offsetm].y = mv.y; + + for (int i = 0; i < 3; i++) { + int method, src_linesize, dst_linesize; + uint8_t *src, *dst; + + if (i == 1) { + offsetx = offsetx >> 1; + offsety = offsety >> 1; + mv.x = mv.x >> 1; + mv.y = mv.y >> 1; + width = width >> 1; + height = height >> 1; + fwidth = fwidth >> 1; + fheight = fheight >> 1; + } + + av_assert0(s->pic[sidx]); + av_assert0(s->pic[s->current_pic]); + av_assert0(s->pic[s->current_pic]->data[i]); + if (!s->pic[sidx]->data[i]) + return AVERROR_INVALIDDATA; + + method = (mv.x & 1) | ((mv.y & 1) << 1); + src_linesize = s->pic[sidx]->linesize[i]; + src = s->pic[sidx]->data[i] + offsetx + (mv.x >> 1) + + (offsety + (mv.y >> 1)) * src_linesize; + dst_linesize = s->pic[s->current_pic]->linesize[i]; + dst = s->pic[s->current_pic]->data[i] + offsetx + offsety * dst_linesize; + + switch (method) { + case 0: + if (offsety + (mv.y >> 1) < 0 || + offsety + (mv.y >> 1) >= fheight || + offsetx + (mv.x >> 1) < 0 || + offsetx + (mv.x >> 1) >= fwidth) + return AVERROR_INVALIDDATA; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = src[x]; + dst += dst_linesize; + src += src_linesize; + } + break; + case 1: + if (offsety + (mv.y >> 1) < 0 || + offsety + (mv.y >> 1) >= fheight || + offsetx + (mv.x >> 1) < 0 || + offsetx + (mv.x >> 1) >= fwidth) + return AVERROR_INVALIDDATA; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + dst[x] = (uint8_t)((src[x] >> 1) + (src[x + 1] >> 1)); + } + + dst += dst_linesize; + src += src_linesize; + } + break; + case 2: + if (offsety + (mv.y >> 1) < 0 || + offsety + (mv.y >> 1) >= fheight || + offsetx + (mv.x >> 1) < 0 || + offsetx + (mv.x >> 1) >= fwidth) + return AVERROR_INVALIDDATA; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + dst[x] = (uint8_t)((src[x] >> 1) + (src[x + src_linesize] >> 1)); + } + + dst += dst_linesize; + src += src_linesize; + } + break; + case 3: + if (offsety + (mv.y >> 1) < 0 || + offsety + (mv.y >> 1) >= fheight || + offsetx + (mv.x >> 1) < 0 || + offsetx + (mv.x >> 1) >= fwidth) + return AVERROR_INVALIDDATA; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + dst[x] = (uint8_t)((((src[x] >> 1) + (src[x + 1] >> 1)) >> 1) + + (((src[x + src_linesize] >> 1) + (src[x + 1 + src_linesize] >> 1)) >> 1)); + } + + dst += dst_linesize; + src += src_linesize; + } + break; + } + } + } else { + int tidx; + int adjx = index == 8 ? 0 : width / 2; + int adjy = index == 8 ? height / 2 : 0; + + width = width - adjx; + height = height - adjy; + tidx = get_index(height) * 4 + get_index(width); + + for (int i = 0; i < 2; i++) { + int ret, idx2; + + idx2 = get_vlc2(gb, s->mv_vlc[s->moflex][tidx].table, + s->mv_vlc[s->moflex][tidx].bits, 1); + if (idx2 < 0) + return AVERROR_INVALIDDATA; + + ret = predict_motion(avctx, width, height, idx2, + offsetm, offsetx + i * adjx, offsety + i * adjy); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static int mobiclip_decode(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *pkt) +{ + MobiClipContext *s = avctx->priv_data; + GetBitContext *gb = &s->gb; + AVFrame *frame = s->pic[s->current_pic]; + int ret; + + av_fast_padded_malloc(&s->bitstream, &s->bitstream_size, + pkt->size); + + if ((ret = ff_reget_buffer(avctx, frame, 0)) < 0) + return ret; + + s->bdsp.bswap16_buf((uint16_t *)s->bitstream, + (uint16_t *)pkt->data, + (pkt->size + 1) >> 1); + + ret = init_get_bits8(gb, s->bitstream, s->bitstream_size); + if (ret < 0) + return ret; + + if (get_bits1(gb)) { + frame->pict_type = AV_PICTURE_TYPE_I; + frame->key_frame = 1; + s->moflex = get_bits1(gb); + s->dct_tab_idx = get_bits1(gb); + + setup_qtables(avctx, get_bits(gb, 6)); + for (int y = 0; y < avctx->height; y += 16) { + for (int x = 0; x < avctx->width; x += 16) { + ret = decode_macroblock(avctx, frame, x, y, get_bits1(gb)); + if (ret < 0) + return ret; + } + } + } else { + MotionXY *motion = s->motion; + + memset(motion, 0, s->motion_size); + + frame->pict_type = AV_PICTURE_TYPE_P; + frame->key_frame = 0; + s->dct_tab_idx = 0; + + setup_qtables(avctx, s->quantizer + get_se_golomb(gb)); + for (int y = 0; y < avctx->height; y += 16) { + for (int x = 0; x < avctx->width; x += 16) { + motion[0].x = mid_pred(motion[x / 16 + 1].x, motion[x / 16 + 2].x, motion[x / 16 + 3].x); + motion[0].y = mid_pred(motion[x / 16 + 1].y, motion[x / 16 + 2].y, motion[x / 16 + 3].y); + motion[x / 16 + 2].x = 0; + motion[x / 16 + 2].y = 0; + + int idx = get_vlc2(gb, s->mv_vlc[s->moflex][0].table, + s->mv_vlc[s->moflex][0].bits, 1); + if (idx < 0) + return AVERROR_INVALIDDATA; + + if (idx == 6 || idx == 7) { + ret = decode_macroblock(avctx, frame, x, y, idx == 7); + if (ret < 0) + return ret; + } else { + int flags, idx2; + ret = predict_motion(avctx, 16, 16, idx, x / 16 + 2, x, y); + if (ret < 0) + return ret; + idx2 = get_ue_golomb(gb); + if (idx2 >= FF_ARRAY_ELEMS(pframe_block8x8_coefficients_tab)) + return AVERROR_INVALIDDATA; + flags = pframe_block8x8_coefficients_tab[idx2]; + + for (int sy = y; sy < y + 16; sy += 8) { + for (int sx = x; sx < x + 16; sx += 8) { + if (flags & 1) + add_pframe_coefficients(avctx, frame, sx, sy, 8, 0); + flags >>= 1; + } + } + + if (flags & 1) + add_pframe_coefficients(avctx, frame, x >> 1, y >> 1, 8, 1); + flags >>= 1; + if (flags & 1) + add_pframe_coefficients(avctx, frame, x >> 1, y >> 1, 8, 2); + } + } + } + } + + s->current_pic = (s->current_pic + 1) % 6; + ret = av_frame_ref(data, frame); + if (ret < 0) + return ret; + *got_frame = 1; + + return 0; +} + +static av_cold int mobiclip_close(AVCodecContext *avctx) +{ + MobiClipContext *s = avctx->priv_data; + + ff_free_vlc(&s->vlc[0]); + ff_free_vlc(&s->vlc[1]); + + for (int i = 0; i < 16; i++) { + ff_free_vlc(&s->mv_vlc[0][i]); + ff_free_vlc(&s->mv_vlc[1][i]); + } + + av_freep(&s->bitstream); + s->bitstream_size = 0; + av_freep(&s->motion); + s->motion_size = 0; + + for (int i = 0; i < 6; i++) { + av_frame_free(&s->pic[i]); + } + + return 0; +} + +AVCodec ff_mobiclip_decoder = { + .name = "mobiclip", + .long_name = NULL_IF_CONFIG_SMALL("MobiClip Video"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_MOBICLIP, + .priv_data_size = sizeof(MobiClipContext), + .init = mobiclip_init, + .decode = mobiclip_decode, + .close = mobiclip_close, + .capabilities = AV_CODEC_CAP_DR1, +};