From patchwork Thu Jun 30 12:42:42 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Tomas_H=C3=A4rdin?= X-Patchwork-Id: 36553 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp288464pzh; Thu, 30 Jun 2022 05:42:57 -0700 (PDT) X-Google-Smtp-Source: AGRyM1tH9Dw870IxnOBk7r1JJD7F7O2Sk/BIltx+2pAibMYYk1MPAnP808jdnHcOwtFFEukRJNq1 X-Received: by 2002:a17:907:2856:b0:727:9ea5:a5e2 with SMTP id el22-20020a170907285600b007279ea5a5e2mr8659526ejc.732.1656592976969; Thu, 30 Jun 2022 05:42:56 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656592976; cv=none; d=google.com; s=arc-20160816; b=GVevZAGiVcPPBkuTwC9He1Nn/psjkbKoC2xVxibR+dF9Ip7xVNdnrEcKqX5iIipFqU xGwcBKlTduEL0UyDloQuc4W1S9eBECis5nUDjKIb8zUzcWckVMagqIjOma6N4F5hE7L7 4LWNvM9CSwSd8Jc88q6CjkQzHV09HcNM4+F+rWnDICUYX+FUwkYuRqg/yKHcs/z7RgDT 4EFxKS9oGv/AtutaKvrUmvv3uhr9CA5iew7OZPzCyZPswvO2J1RrDHsZbDkpDAwNvMWU tkkjica9jZc+F6f70lpDo2Wj/qsVpmqVsl2FaT7xHXvyaT+oIAjLjH2TnmRCPV50yEYw TSEQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:subject :mime-version:user-agent:date:to:from:message-id:dkim-signature :dkim-signature:delivered-to; bh=0Fs7atsGEwAxVkrcUA35PnwVk70k3K8IM+O4UEU9XKA=; b=XywU8uuAO8Rwnp9iAqqTdJPxDbjmXTYVsHrsfcEaQ2vTNrFIsllpwK6LUWq1JM7Pz9 pMOi9+d/39oEtzlK24xBetqtEDtkL8hlqbI4vqUuWp858pHWoiMJ8B4zlnXFo7N+jT4x Fcy9tEGpfYaoXfdnfPknveIMXAvxBPJT039UDmikrhPXhyaitl2oojLITnEHfv/sqMHe dytRh6x5nqlmVcnAJoJtHWfxWADCs43BxgJZlmh8dQh+gRkRgoIgLN6nXZDHM0awxyca TMDHKeVKwd2XOIpyY9w6hkGw9HZ0LrffvX/Q35nkmWuBQQMLmMQdLju2p5YjgJYi7Prb 9FFw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@acc.umu.se header.s=mail1 header.b=JmZ09Tti; dkim=neutral (body hash did not verify) header.i=@acc.umu.se header.s=mail1 header.b=u1ZUYPMC; 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=acc.umu.se Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id mp3-20020a1709071b0300b0072a4d2a1a4dsi515417ejc.767.2022.06.30.05.42.56; Thu, 30 Jun 2022 05:42:56 -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=@acc.umu.se header.s=mail1 header.b=JmZ09Tti; dkim=neutral (body hash did not verify) header.i=@acc.umu.se header.s=mail1 header.b=u1ZUYPMC; 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=acc.umu.se Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 3471468B6F9; Thu, 30 Jun 2022 15:42:52 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail.acc.umu.se (mail.acc.umu.se [130.239.18.156]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 293A168B45D for ; Thu, 30 Jun 2022 15:42:45 +0300 (EEST) Received: from localhost (localhost.localdomain [127.0.0.1]) by amavisd-new (Postfix) with ESMTP id 8AA2C44B96 for ; Thu, 30 Jun 2022 14:42:44 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=acc.umu.se; s=mail1; t=1656592964; bh=gvrTpkOJYc4XojrPITm0T6+7lwmI8g3e4FuFbM0HYh0=; h=Subject:From:To:Date:From; b=JmZ09TtiJ/ft4jtft36xhDl+V1W7RTlJ9T8aztCToyOQ4fEUlbfcTNEI6chz5hryk ddUiZ4jDzc1Lhz43br6iGp7nkWNmyD813wL86zJJ9bHfGMWkxUCkCSedoD2nwNleE0 iz6aQRGpdm95Tzv36Q5tUaAIoF+8V2sqPlHCKahLVyKXTrDUaXiZQs9ibFc+ROtjU4 h3OwjkMBki165svBt/36EJbmvEdFB7r0wLDJ8nZ2uoACSoaUwweWmq7r1yalIeLXPp Ywrfjrix8Sr2SmaFY5llZpi7y6PU9wQZV0Vl5Yt7PLlTfIrwa/8zA7lFTBCjlNKkxh ipUkGo+hkcx+A== Received: from debian.lan (unknown [IPv6:2a00:66c0:a::72c]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: tjoppen) by mail.acc.umu.se (Postfix) with ESMTPSA id 8C56E44B93 for ; Thu, 30 Jun 2022 14:42:43 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=acc.umu.se; s=mail1; t=1656592963; bh=gvrTpkOJYc4XojrPITm0T6+7lwmI8g3e4FuFbM0HYh0=; h=Subject:From:To:Date:From; b=u1ZUYPMCyMS+zr7H1bc6x13KlY9kK2xlxNGiNDcL0iwUcg4R76Tw2R2Gy0snytWt7 Etjblyx7NaRgPXIfdJaOpb2ez/XWB+VjRcspXIBSlOfdcXo30GmFn1EQsgPI/hU12O kdbRp+urTttZsM4DktErug49m5Oewp/lFUDoYbcnVwL10Fd7NcVUy+RVp233I/BUeN onCzLLANGKqaYlFRdHm2bp+RiXGs7B1zX0/B7vaDse93H9C7X+4T0odckx2i01Mpgx zPQfacQAQk8STDt/4priZc/RDiqdXS/+ey9BbOXDPSKwTLz+JdKJ9f7ja0Z900lySS RBy5ogh9Yq2oA== Message-ID: <7c2bdda6042d62e21fff91407393e77e7e7fedc2.camel@acc.umu.se> From: Tomas =?iso-8859-1?q?H=E4rdin?= To: FFmpeg development discussions and patches Date: Thu, 30 Jun 2022 14:42:42 +0200 User-Agent: Evolution 3.38.3-1 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH] Make execute() and execute2() return FFMIN() of thread return codes 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: PP3pcOHow9BA Hi Previous version of this patch failed fate-fic-avi with THREAD_TYPE=slice THREADS=2 due to thread_execute() always returning 0. Fixed in this version. The fic sample appears to indeed be broken. Some packets are truncated, including one zero-length packet. /Tomas From 872354ff70f6e880d77e8bddd28bfa7bc6582ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Thu, 16 Jun 2022 12:16:44 +0200 Subject: [PATCH] Make execute() and execute2() return FFMIN() of thread return codes At the moment only fic.c actually checks return code of execute() hence the change to its FATE reference --- libavcodec/avcodec.c | 10 ++++++---- libavcodec/pthread_slice.c | 12 ++++++------ libavfilter/pthread.c | 3 ++- libavutil/slicethread.c | 37 ++++++++++++++++++++++------------- libavutil/slicethread.h | 6 +++--- libswscale/swscale.c | 5 +++-- libswscale/swscale_internal.h | 4 ++-- tests/ref/fate/fic-avi | 30 ++++++++++++---------------- 8 files changed, 58 insertions(+), 49 deletions(-) diff --git a/libavcodec/avcodec.c b/libavcodec/avcodec.c index 5f6e71a39e..49f0fd06fb 100644 --- a/libavcodec/avcodec.c +++ b/libavcodec/avcodec.c @@ -44,28 +44,30 @@ int avcodec_default_execute(AVCodecContext *c, int (*func)(AVCodecContext *c2, void *arg2), void *arg, int *ret, int count, int size) { - int i; + int i, rr = 0; for (i = 0; i < count; i++) { int r = func(c, (char *)arg + i * size); + rr = FFMIN(rr, r); if (ret) ret[i] = r; } emms_c(); - return 0; + return rr; } int avcodec_default_execute2(AVCodecContext *c, int (*func)(AVCodecContext *c2, void *arg2, int jobnr, int threadnr), void *arg, int *ret, int count) { - int i; + int i, rr = 0; for (i = 0; i < count; i++) { int r = func(c, arg, i, 0); + rr = FFMIN(rr, r); if (ret) ret[i] = r; } emms_c(); - return 0; + return rr; } static AVMutex codec_mutex = AV_MUTEX_INITIALIZER; diff --git a/libavcodec/pthread_slice.c b/libavcodec/pthread_slice.c index 0ad1965a22..e2903846eb 100644 --- a/libavcodec/pthread_slice.c +++ b/libavcodec/pthread_slice.c @@ -57,13 +57,13 @@ typedef struct SliceThreadContext { pthread_mutex_t *progress_mutex; } SliceThreadContext; -static void main_function(void *priv) { +static int main_function(void *priv) { AVCodecContext *avctx = priv; SliceThreadContext *c = avctx->internal->thread_ctx; - c->mainfunc(avctx); + return c->mainfunc(avctx); } -static void worker_func(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads) +static int worker_func(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads) { AVCodecContext *avctx = priv; SliceThreadContext *c = avctx->internal->thread_ctx; @@ -73,6 +73,7 @@ static void worker_func(void *priv, int jobnr, int threadnr, int nb_jobs, int nb : c->func2(avctx, c->args, jobnr, threadnr); if (c->rets) c->rets[jobnr] = ret; + return ret; } void ff_slice_thread_free(AVCodecContext *avctx) @@ -108,8 +109,7 @@ static int thread_execute(AVCodecContext *avctx, action_func* func, void *arg, i c->func = func; c->rets = ret; - avpriv_slicethread_execute(c->thread, job_count, !!c->mainfunc ); - return 0; + return avpriv_slicethread_execute(c->thread, job_count, !!c->mainfunc ); } static int thread_execute2(AVCodecContext *avctx, action_func2* func2, void *arg, int *ret, int job_count) @@ -131,7 +131,7 @@ int ff_slice_thread_init(AVCodecContext *avctx) { SliceThreadContext *c; int thread_count = avctx->thread_count; - void (*mainfunc)(void *); + int (*mainfunc)(void *); // We cannot do this in the encoder init as the threads are created before if (av_codec_is_encoder(avctx->codec) && diff --git a/libavfilter/pthread.c b/libavfilter/pthread.c index 1a063d3cc0..8cec278be0 100644 --- a/libavfilter/pthread.c +++ b/libavfilter/pthread.c @@ -43,12 +43,13 @@ typedef struct ThreadContext { int *rets; } ThreadContext; -static void worker_func(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads) +static int worker_func(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads) { ThreadContext *c = priv; int ret = c->func(c->ctx, c->arg, jobnr, nb_jobs); if (c->rets) c->rets[jobnr] = ret; + return ret; } static void slice_thread_uninit(ThreadContext *c) diff --git a/libavutil/slicethread.c b/libavutil/slicethread.c index ea1c9c8311..2e78d32ab8 100644 --- a/libavutil/slicethread.c +++ b/libavutil/slicethread.c @@ -32,6 +32,7 @@ typedef struct WorkerContext { pthread_cond_t cond; pthread_t thread; int done; + int ret; } WorkerContext; struct AVSliceThread { @@ -48,11 +49,11 @@ struct AVSliceThread { int finished; void *priv; - void (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads); - void (*main_func)(void *priv); + int (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads); + int (*main_func)(void *priv); }; -static int run_jobs(AVSliceThread *ctx) +static int run_jobs(AVSliceThread *ctx, int *ret_out) { unsigned nb_jobs = ctx->nb_jobs; unsigned nb_active_threads = ctx->nb_active_threads; @@ -60,7 +61,8 @@ static int run_jobs(AVSliceThread *ctx) unsigned current_job = first_job; do { - ctx->worker_func(ctx->priv, current_job, first_job, nb_jobs, nb_active_threads); + int ret = ctx->worker_func(ctx->priv, current_job, first_job, nb_jobs, nb_active_threads); + *ret_out = FFMIN(*ret_out, ret); } while ((current_job = atomic_fetch_add_explicit(&ctx->current_job, 1, memory_order_acq_rel)) < nb_jobs); return current_job == nb_jobs + nb_active_threads - 1; @@ -84,7 +86,7 @@ static void *attribute_align_arg thread_worker(void *v) return NULL; } - if (run_jobs(ctx)) { + if (run_jobs(ctx, &w->ret)) { pthread_mutex_lock(&ctx->done_mutex); ctx->done = 1; pthread_cond_signal(&ctx->done_cond); @@ -94,8 +96,8 @@ static void *attribute_align_arg thread_worker(void *v) } int avpriv_slicethread_create(AVSliceThread **pctx, void *priv, - void (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads), - void (*main_func)(void *priv), + int (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads), + int (*main_func)(void *priv), int nb_threads) { AVSliceThread *ctx; @@ -163,9 +165,9 @@ int avpriv_slicethread_create(AVSliceThread **pctx, void *priv, return nb_threads; } -void avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_main) +int avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_main) { - int nb_workers, i, is_last = 0; + int nb_workers, i, is_last = 0, ret = 0; av_assert0(nb_jobs > 0); ctx->nb_jobs = nb_jobs; @@ -180,14 +182,15 @@ void avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_mai WorkerContext *w = &ctx->workers[i]; pthread_mutex_lock(&w->mutex); w->done = 0; + w->ret = 0; pthread_cond_signal(&w->cond); pthread_mutex_unlock(&w->mutex); } if (ctx->main_func && execute_main) - ctx->main_func(ctx->priv); + ret = ctx->main_func(ctx->priv); else - is_last = run_jobs(ctx); + is_last = run_jobs(ctx, &ret); if (!is_last) { pthread_mutex_lock(&ctx->done_mutex); @@ -196,6 +199,11 @@ void avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_mai ctx->done = 0; pthread_mutex_unlock(&ctx->done_mutex); } + + for (i = 0; i < nb_workers; i++) + ret = FFMIN(ret, ctx->workers[i].ret); + + return ret; } void avpriv_slicethread_free(AVSliceThread **pctx) @@ -236,17 +244,18 @@ void avpriv_slicethread_free(AVSliceThread **pctx) #else /* HAVE_PTHREADS || HAVE_W32THREADS || HAVE_OS32THREADS */ int avpriv_slicethread_create(AVSliceThread **pctx, void *priv, - void (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads), - void (*main_func)(void *priv), + int (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads), + int (*main_func)(void *priv), int nb_threads) { *pctx = NULL; return AVERROR(ENOSYS); } -void avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_main) +int avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_main) { av_assert0(0); + return AVERROR(ENOSYS); } void avpriv_slicethread_free(AVSliceThread **pctx) diff --git a/libavutil/slicethread.h b/libavutil/slicethread.h index f6f6f302c4..5c8f197932 100644 --- a/libavutil/slicethread.h +++ b/libavutil/slicethread.h @@ -31,8 +31,8 @@ typedef struct AVSliceThread AVSliceThread; * @return return number of threads or negative AVERROR on failure */ int avpriv_slicethread_create(AVSliceThread **pctx, void *priv, - void (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads), - void (*main_func)(void *priv), + int (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads), + int (*main_func)(void *priv), int nb_threads); /** @@ -41,7 +41,7 @@ int avpriv_slicethread_create(AVSliceThread **pctx, void *priv, * @param nb_jobs number of jobs, must be > 0 * @param execute_main also execute main_func */ -void avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_main); +int avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_main); /** * Destroy slice threading context. diff --git a/libswscale/swscale.c b/libswscale/swscale.c index 7b40f49da4..2f9a0b5a7c 100644 --- a/libswscale/swscale.c +++ b/libswscale/swscale.c @@ -1211,8 +1211,8 @@ int attribute_align_arg sws_scale(struct SwsContext *c, dst, dstStride, 0, c->dstH); } -void ff_sws_slice_worker(void *priv, int jobnr, int threadnr, - int nb_jobs, int nb_threads) +int ff_sws_slice_worker(void *priv, int jobnr, int threadnr, + int nb_jobs, int nb_threads) { SwsContext *parent = priv; SwsContext *c = parent->slice_ctx[threadnr]; @@ -1241,4 +1241,5 @@ void ff_sws_slice_worker(void *priv, int jobnr, int threadnr, } parent->slice_err[threadnr] = err; + return err; } diff --git a/libswscale/swscale_internal.h b/libswscale/swscale_internal.h index e118b54457..eab3e26331 100644 --- a/libswscale/swscale_internal.h +++ b/libswscale/swscale_internal.h @@ -1135,8 +1135,8 @@ void ff_init_vscale_pfn(SwsContext *c, yuv2planar1_fn yuv2plane1, yuv2planarX_fn yuv2interleavedX_fn yuv2nv12cX, yuv2packed1_fn yuv2packed1, yuv2packed2_fn yuv2packed2, yuv2packedX_fn yuv2packedX, yuv2anyX_fn yuv2anyX, int use_mmx); -void ff_sws_slice_worker(void *priv, int jobnr, int threadnr, - int nb_jobs, int nb_threads); +int ff_sws_slice_worker(void *priv, int jobnr, int threadnr, + int nb_jobs, int nb_threads); //number of extra lines to process #define MAX_LINES_AHEAD 4 diff --git a/tests/ref/fate/fic-avi b/tests/ref/fate/fic-avi index df55789d54..4546f230b1 100644 --- a/tests/ref/fate/fic-avi +++ b/tests/ref/fate/fic-avi @@ -76,19 +76,18 @@ 0, 70, 70, 1, 1566720, 0x40f7d39a 0, 71, 71, 1, 1566720, 0x40f7d39a 0, 72, 72, 1, 1566720, 0x40f7d39a -0, 73, 73, 1, 1566720, 0xa7d6e25f -0, 74, 74, 1, 1566720, 0xa7d6e25f -0, 75, 75, 1, 1566720, 0xa7d6e25f -0, 76, 76, 1, 1566720, 0xa7d6e25f -0, 77, 77, 1, 1566720, 0xa7d6e25f -0, 78, 78, 1, 1566720, 0xa7d6e25f -0, 79, 79, 1, 1566720, 0xa7d6e25f -0, 80, 80, 1, 1566720, 0xa7d6e25f -0, 81, 81, 1, 1566720, 0xa7d6e25f -0, 82, 82, 1, 1566720, 0xa7d6e25f -0, 83, 83, 1, 1566720, 0xa7d6e25f -0, 84, 84, 1, 1566720, 0xa7d6e25f -0, 85, 85, 1, 1566720, 0xa7d6e25f +0, 74, 74, 1, 1566720, 0x40f7d39a +0, 75, 75, 1, 1566720, 0x40f7d39a +0, 76, 76, 1, 1566720, 0x40f7d39a +0, 77, 77, 1, 1566720, 0x40f7d39a +0, 78, 78, 1, 1566720, 0x40f7d39a +0, 79, 79, 1, 1566720, 0x40f7d39a +0, 80, 80, 1, 1566720, 0x40f7d39a +0, 81, 81, 1, 1566720, 0x40f7d39a +0, 82, 82, 1, 1566720, 0x40f7d39a +0, 83, 83, 1, 1566720, 0x40f7d39a +0, 84, 84, 1, 1566720, 0x40f7d39a +0, 85, 85, 1, 1566720, 0x40f7d39a 0, 86, 86, 1, 1566720, 0xa7d6e25f 0, 87, 87, 1, 1566720, 0xa7d6e25f 0, 88, 88, 1, 1566720, 0xa7d6e25f @@ -104,7 +103,6 @@ 0, 98, 98, 1, 1566720, 0xa7d6e25f 0, 99, 99, 1, 1566720, 0xa7d6e25f 0, 100, 100, 1, 1566720, 0xeaf8d207 -0, 101, 101, 1, 1566720, 0x6724983e 0, 102, 102, 1, 1566720, 0x0e95d209 0, 103, 103, 1, 1566720, 0x0e95d209 0, 104, 104, 1, 1566720, 0x0e95d209 @@ -121,6 +119,4 @@ 0, 115, 115, 1, 1566720, 0xfe83b964 0, 116, 116, 1, 1566720, 0xfe83b964 0, 117, 117, 1, 1566720, 0xfe83b964 -0, 118, 118, 1, 1566720, 0x25dc30a6 -0, 119, 119, 1, 1566720, 0x25dc30a6 -0, 120, 120, 1, 1566720, 0x25dc30a6 +0, 119, 119, 1, 1566720, 0xfe83b964 -- 2.30.2