From patchwork Fri Jan 14 01:13:10 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33563 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp984815iog; Thu, 13 Jan 2022 17:14:01 -0800 (PST) X-Google-Smtp-Source: ABdhPJwK9OxQd2X5Iu8UXD5kye1EN+Z3yApBGQuxtwuhzgrh7N/81gUXV/6ea6QW0fPzMJ5W8X85 X-Received: by 2002:a17:906:c156:: with SMTP id dp22mr5751195ejc.109.1642122840939; Thu, 13 Jan 2022 17:14:00 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642122840; cv=none; d=google.com; s=arc-20160816; b=P40oeoMWe7gt6W48mnZAuXOWpviyXf6FoR7xGwK06A5bA+cDn3xs48/M3SK9eEuVEU TsZ31dJfTehfSk+OOTq3GOQp/h6wM65W587d0qV9+F2CzXM7lAaCoWGqvBEieqWuiKXj NcyvuEoW2Li37vq6/k/d/JHNaHNNGweGRXb/a5W9fkHusQcab2FLntCkHc8uUNMNzV4Q ++Fz8OcdBIxze1FAloH6HgOjRKaB/0PkfZ9fT+kqAAXj6d3gz9fRcOTFOiMelTh85rV1 nPG5BF/boSrP8d+EgWcOUDjUvDoDY/LLVBbAPhfMhUiDCp1g9Vd6QCyPie6f+OSzPP6/ ccnA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=TQnAen+DV7aFtFioRK0/NUkuJCcuI8VKgLfAztqjzmg=; b=sAbrnWI2+K4DDufAgMcTF1rq1NmJPufV5BKBl7A1OOMkqHbQ6KpAC+NqGszPL/2ycN WVBjFKp+cKy0RdCmrT63yTxwWX/B9oCkHtcG3Fv5XNjYjYFgNuFs4VkpAJZnzCOln4Va z3KsXZgSk1pdWE43oyQBWWjvoSSh8qMxMe15e0NrXEElq7R5UJR5/isIeVtWphAKayUx GHbIOJ/+Yn/Pbt44Jxr5hzioynf2/YI9AocK20LW6u8G3pOjMV27fjWKGTDVzlVoDu9u 6NcNiVN6PLWhwjTcUzzyGhOqh8z7EWY/EJ7PF2bSj9pm8n+FpyjKja3/Udf5nWskONGz EWHw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=UVqhyVxR; 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 ay21si2099266ejb.502.2022.01.13.17.14.00; Thu, 13 Jan 2022 17:14:00 -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 header.s=20210112 header.b=UVqhyVxR; 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 EEB7168B510; Fri, 14 Jan 2022 03:13:45 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pj1-f44.google.com (mail-pj1-f44.google.com [209.85.216.44]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 977EF68A86C for ; Fri, 14 Jan 2022 03:13:38 +0200 (EET) Received: by mail-pj1-f44.google.com with SMTP id i8-20020a17090a138800b001b3936fb375so20561473pja.1 for ; Thu, 13 Jan 2022 17:13:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=TpUPukYaZVM59oxufUEiMOrqpC19LS0P3MvaA/WmbiM=; b=UVqhyVxRNuP//ltywCEJDclI5sKghUH0Yc0i7wm5VOvmMwFaBbI4KIxxBJ0I0rvm3g bX+Bu+0fs+y7KXjomsSvgnnTASgdw5x8wGT9oOR9HgNHq21OLNoQw+4SYq8329is+MhC 05v2J/nNUt6lVH255LRB23CEpoBxOQJUwnw7MsKS1j5qZoMPuri6qsxOWGt16SyDHIiV 1/1K8v4jVWY5XfRR91mdMhlqAAQVy8Cm3aJwV7Sn5QfxeQHytMruI4pIMzG9qdbZg5xq Ld9nhPffw4ZwKscPMI4NHI+S/gjKDYq2IS+prPG7JmbJhnfg2mWT38n42/gffgzBvdrG JGEg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=TpUPukYaZVM59oxufUEiMOrqpC19LS0P3MvaA/WmbiM=; b=Me4DkLI3/4eKTzJ/KbE/vgsPhHgrt5SlSQ4UnBV4Nl5JAS2+HgK8XsfOWbXAuBi4lp SjIRnlM1JS+yUelAz2uY/opzN0mFMrx3pUKdPIuqW/mhhPQBof2qPrNoyjOMJ3eGQTNH l3X1xO5STNw2xGh6MjhERbpj5IVnfax6Pf0ymzXuszxEJdZzOxO1fXwbtMdSyiMNrqec DNg9c47m5CtKisZoe9DF/sJRYndtMKFmR9R9MYPGXgRMFR5NrodvcX+Udf6kOPHgPZWv uMb6pI2d8qld4Bq5qfTR7eJWG57WdEu70YBVJOCJINBlnRMidTY6e3cUnEf8G9Zs+r2f Q4iw== X-Gm-Message-State: AOAM531Bj6MmdVLa0FSurjO3U3fNxFJ2j3Sg3R8T7ojHZ15x+n4324JP djXWtTR45qVWhOMBjDUzD8GqQFUMkW4nBA== X-Received: by 2002:a17:902:820a:b0:14a:171d:8e35 with SMTP id x10-20020a170902820a00b0014a171d8e35mr6976635pln.136.1642122816868; Thu, 13 Jan 2022 17:13:36 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id p10sm3909456pfw.69.2022.01.13.17.13.36 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:36 -0800 (PST) Message-Id: <13b9a26b25af71def101a29c2c079fb5bd37dbff.1642122814.git.ffmpegagent@gmail.com> In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:10 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 01/24] avcodec, avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 4CpV+9wSxXyl From: softworkz Signed-off-by: softworkz --- libavcodec/avcodec.h | 19 +------------ libavutil/Makefile | 1 + libavutil/subfmt.h | 68 ++++++++++++++++++++++++++++++++++++++++++++ libavutil/version.h | 1 + 4 files changed, 71 insertions(+), 18 deletions(-) create mode 100644 libavutil/subfmt.h diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index ec1a0566a4..fe5a83cf85 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -35,6 +35,7 @@ #include "libavutil/frame.h" #include "libavutil/log.h" #include "libavutil/pixfmt.h" +#include "libavutil/subfmt.h" #include "libavutil/rational.h" #include "codec.h" @@ -2238,24 +2239,6 @@ typedef struct AVHWAccel { * @} */ -enum AVSubtitleType { - SUBTITLE_NONE, - - SUBTITLE_BITMAP, ///< A bitmap, pict will be set - - /** - * Plain text, the text field must be set by the decoder and is - * authoritative. ass and pict fields may contain approximations. - */ - SUBTITLE_TEXT, - - /** - * Formatted text, the ass field must be set by the decoder and is - * authoritative. pict and text fields may contain approximations. - */ - SUBTITLE_ASS, -}; - #define AV_SUBTITLE_FLAG_FORCED 0x00000001 typedef struct AVSubtitleRect { diff --git a/libavutil/Makefile b/libavutil/Makefile index d17876df1a..ce644f4d48 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -74,6 +74,7 @@ HEADERS = adler32.h \ sha512.h \ spherical.h \ stereo3d.h \ + subfmt.h \ threadmessage.h \ time.h \ timecode.h \ diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h new file mode 100644 index 0000000000..791b45519f --- /dev/null +++ b/libavutil/subfmt.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 softworkz + * + * 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 + */ + +#ifndef AVUTIL_SUBFMT_H +#define AVUTIL_SUBFMT_H + +#include "version.h" + +enum AVSubtitleType { + + /** + * Subtitle format unknown. + */ + AV_SUBTITLE_FMT_NONE = -1, + + /** + * Subtitle format unknown. + */ + AV_SUBTITLE_FMT_UNKNOWN = 0, +#if FF_API_OLD_SUBTITLES + SUBTITLE_NONE = 0, ///< Deprecated, use AV_SUBTITLE_FMT_NONE instead. +#endif + + /** + * Bitmap area in AVSubtitleRect.data, pixfmt AV_PIX_FMT_PAL8. + */ + AV_SUBTITLE_FMT_BITMAP = 1, +#if FF_API_OLD_SUBTITLES + SUBTITLE_BITMAP = 1, ///< Deprecated, use AV_SUBTITLE_FMT_BITMAP instead. +#endif + + /** + * Plain text in AVSubtitleRect.text. + */ + AV_SUBTITLE_FMT_TEXT = 2, +#if FF_API_OLD_SUBTITLES + SUBTITLE_TEXT = 2, ///< Deprecated, use AV_SUBTITLE_FMT_TEXT instead. +#endif + + /** + * Text Formatted as per ASS specification, contained AVSubtitleRect.ass. + */ + AV_SUBTITLE_FMT_ASS = 3, +#if FF_API_OLD_SUBTITLES + SUBTITLE_ASS = 3, ///< Deprecated, use AV_SUBTITLE_FMT_ASS instead. +#endif + + AV_SUBTITLE_FMT_NB, ///< number of subtitle formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions. +}; + +#endif /* AVUTIL_SUBFMT_H */ diff --git a/libavutil/version.h b/libavutil/version.h index 953aac9d94..5bf48f6304 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -110,6 +110,7 @@ #define FF_API_COLORSPACE_NAME (LIBAVUTIL_VERSION_MAJOR < 58) #define FF_API_AV_MALLOCZ_ARRAY (LIBAVUTIL_VERSION_MAJOR < 58) #define FF_API_FIFO_PEEK2 (LIBAVUTIL_VERSION_MAJOR < 58) +#define FF_API_OLD_SUBTITLES (LIBAVUTIL_VERSION_MAJOR < 58) /** * @} From patchwork Fri Jan 14 01:13:11 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33564 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp984922iog; Thu, 13 Jan 2022 17:14:12 -0800 (PST) X-Google-Smtp-Source: ABdhPJwUumEd+nSSYBCbiwTZUur2sFCw6ZuJdauca0NPEPIYyxRBfAUYJIWhnZ+sfX1dzMKbmaLk X-Received: by 2002:a05:6402:520b:: with SMTP id s11mr6603453edd.27.1642122852254; Thu, 13 Jan 2022 17:14:12 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642122852; cv=none; d=google.com; s=arc-20160816; b=sz1LkhEXbyaiFTDef4XOg493ZjXkCgSH4LdCdKkMEDiNWDotj/LruieInlIkHQdq2y iNrhHwMq2wwjXmJwPWJQH+Lqjb0WK1eDPtNLQ/PIpO/XAL+hYqcI5jWb6lKTPepGoftt 7ouOqi/cG5zXyST3f4datYwkrwKP4HlJ93erx2wgunTpSohCiJM3eywL4sXlGAJlRD5r rZb+UEcm5C2KJFFFyCRJ0xIZjhRzmE1ZJZIozSoJ0ctiMda826lRgW6getlttYmy/i0l W+xlJEK3lIpMvEeKG6oHk/Hoab6zyk1Ok25HBN0PityYOlFm6lSFNB+TuMcowMtTZheu esVw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=ccBTjWjFlltRIm87Pn4eJOiDk/kiNON55xgW8t/DmTo=; b=MbUKaRORBYAdDLh31zCUBmW5qBCff8ERtRRUaonhdK72FwOlC4bK/V2Uh423ttZ6iC bN7paR0UOXo95Cx6MwPlHY1MN3Oqg8s4H7lCxorVh+s8uOCY9uKrRRlcY3UbGm649vdU RuD95O6anCP0Z+Wgi4CClSNS2vkSKoNd5RAsoN66FU/g8iK3BvhEHq1YOo0LvTZ90GQI tjo9IKuVnGQxFqW6905puk0LZQyfq5iaAXBk019UA4Smr5AVX3BEHDnPyWprAec/BURV DbWnX75oyp6onms9surHeZMnQcuStZZvQdF2dtF2bk5+jYD12/ph/XRvf7Dljy69cQo9 tp4A== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=YPR2soTh; 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 l6si2325316ejo.274.2022.01.13.17.14.11; Thu, 13 Jan 2022 17:14:12 -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 header.s=20210112 header.b=YPR2soTh; 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 1F81168B570; Fri, 14 Jan 2022 03:13:47 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f179.google.com (mail-pf1-f179.google.com [209.85.210.179]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D522568B51B for ; Fri, 14 Jan 2022 03:13:39 +0200 (EET) Received: by mail-pf1-f179.google.com with SMTP id 78so1400880pfu.10 for ; Thu, 13 Jan 2022 17:13:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=vfHKooxDg5ACms7yWQwDBzpnmWyA+yDPx8Ij7IunoKI=; b=YPR2soThgms42XNAE9l3Y2n6NgkomL/3pN6iipkz22P3i+FhPhi27DQocxjaeUZ71Y wjjgop90iS3+TtLCnFlk4s0gXIcpzYhj9e6zi0D1ZN0amKmJAxEBCyEBxxEx9CiLFYVy gZmXYtiWcLKMDuqw0WOmR7v0tMDqWBsoGsHoC09owErC5RqzDYr5glBwojSbMShyaIFh qCZvmlhh6XsBM/Fy33RToJfkVxjtiQPWJzphO9qwZh3PpjG9IdQb5NMKH23VT5FVougx 4PvwqWVIEGtS4VP7fpSlhBVIWzGiZusHgGlavaTJzWKZwZ4Rn/LgZKKsQK1fgOy2NQrK 8yDg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=vfHKooxDg5ACms7yWQwDBzpnmWyA+yDPx8Ij7IunoKI=; b=g6F/JLS/KE3XSkL5ps0kHM98Hss+UEwtnAI74+IZj4v07Plp7bmM4Ze2bjQvZLCKxg EMoOZhujeLM0BoVLhthzRTTJqYIGZQiCmVGyCFq2803bdidR0867auUfsvAL162t83SM nc7tFiXTYnyLQIx+0BeeD3pEC81Ug4bvDQZOynezqy/89jwJQYz/nBslOEXBTdvnbf2b 5njflDMoriNjDv276FVI7x5MG0fWep+oqvHM5yZTMpxINk4roDOu8d3X1Ce5aCBJDP3w WrRs21OXI1qEJAVP3pGfx8W5VsV7GgnNwXMYqXjNuuZtEtdDzssiQ2OSj6D8oGzSorse eVdA== X-Gm-Message-State: AOAM531tq5NnTfYytk6wEa2d0tTkRnJ4yvM9OYIJNvUiLdevdJyNZyED OGwURPOnitXma4LAgfYR4pQr4a1EiHdN7Q== X-Received: by 2002:a05:6a00:16c7:b0:4a5:d9c1:89da with SMTP id l7-20020a056a0016c700b004a5d9c189damr6698111pfc.34.1642122817924; Thu, 13 Jan 2022 17:13:37 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id c20sm3191569pgk.75.2022.01.13.17.13.37 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:37 -0800 (PST) Message-Id: <54ed1290d25fc43dae1ffb8e2221b9067d68d110.1642122814.git.ffmpegagent@gmail.com> In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:11 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 02/24] avutil/frame: Prepare AVFrame for subtitle handling 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: fpcA8EFlMyWk From: softworkz Root commit for adding subtitle filtering capabilities. In detail: - Add type (AVMediaType) field to AVFrame Replaces previous way of distinction which was based on checking width and height to determine whether a frame is audio or video - Add subtitle fields to AVFrame - Add new struct AVSubtitleArea, similar to AVSubtitleRect, but different allocation logic. Cannot and must not be used interchangeably, hence the new struct Signed-off-by: softworkz --- libavutil/Makefile | 1 + libavutil/frame.c | 211 ++++++++++++++++++++++++++++++++++++++++----- libavutil/frame.h | 85 +++++++++++++++++- libavutil/subfmt.c | 45 ++++++++++ libavutil/subfmt.h | 47 ++++++++++ 5 files changed, 364 insertions(+), 25 deletions(-) create mode 100644 libavutil/subfmt.c diff --git a/libavutil/Makefile b/libavutil/Makefile index ce644f4d48..8bc0a14942 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -160,6 +160,7 @@ OBJS = adler32.o \ slicethread.o \ spherical.o \ stereo3d.o \ + subfmt.o \ threadmessage.o \ time.o \ timecode.o \ diff --git a/libavutil/frame.c b/libavutil/frame.c index 8997c85e35..2b95830b6f 100644 --- a/libavutil/frame.c +++ b/libavutil/frame.c @@ -26,6 +26,7 @@ #include "imgutils.h" #include "mem.h" #include "samplefmt.h" +#include "subfmt.h" #include "hwcontext.h" #define CHECK_CHANNELS_CONSISTENCY(frame) \ @@ -50,6 +51,9 @@ const char *av_get_colorspace_name(enum AVColorSpace val) return name[val]; } #endif + +static int frame_copy_subtitles(AVFrame *dst, const AVFrame *src, int copy_data); + static void get_frame_defaults(AVFrame *frame) { memset(frame, 0, sizeof(*frame)); @@ -70,7 +74,12 @@ static void get_frame_defaults(AVFrame *frame) frame->colorspace = AVCOL_SPC_UNSPECIFIED; frame->color_range = AVCOL_RANGE_UNSPECIFIED; frame->chroma_location = AVCHROMA_LOC_UNSPECIFIED; - frame->flags = 0; + frame->num_subtitle_areas = 0; + frame->subtitle_areas = NULL; + frame->subtitle_header = NULL; + frame->repeat_sub = 0; + frame->subtitle_timing.start_pts = 0; + frame->subtitle_timing.duration = 0; } static void free_side_data(AVFrameSideData **ptr_sd) @@ -240,23 +249,55 @@ static int get_audio_buffer(AVFrame *frame, int align) } +static int get_subtitle_buffer(AVFrame *frame) +{ + // Buffers in AVFrame->buf[] are not used in case of subtitle frames. + // To accomodate with existing code, checking ->buf[0] to determine + // whether a frame is ref-counted or has data, we're adding a 1-byte + // buffer here, which marks the subtitle frame to contain data. + frame->buf[0] = av_buffer_alloc(1); + if (!frame->buf[0]) { + av_frame_unref(frame); + return AVERROR(ENOMEM); + } + + frame->extended_data = frame->data; + + return 0; +} + int av_frame_get_buffer(AVFrame *frame, int align) +{ + if (frame->width > 0 && frame->height > 0) + frame->type = AVMEDIA_TYPE_VIDEO; + else if (frame->nb_samples > 0 && (frame->channel_layout || frame->channels > 0)) + frame->type = AVMEDIA_TYPE_AUDIO; + + return av_frame_get_buffer2(frame, align); +} + +int av_frame_get_buffer2(AVFrame *frame, int align) { if (frame->format < 0) return AVERROR(EINVAL); - if (frame->width > 0 && frame->height > 0) + switch(frame->type) { + case AVMEDIA_TYPE_VIDEO: return get_video_buffer(frame, align); - else if (frame->nb_samples > 0 && (frame->channel_layout || frame->channels > 0)) + case AVMEDIA_TYPE_AUDIO: return get_audio_buffer(frame, align); - - return AVERROR(EINVAL); + case AVMEDIA_TYPE_SUBTITLE: + return get_subtitle_buffer(frame); + default: + return AVERROR(EINVAL); + } } static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy) { int ret, i; + dst->type = src->type; dst->key_frame = src->key_frame; dst->pict_type = src->pict_type; dst->sample_aspect_ratio = src->sample_aspect_ratio; @@ -288,6 +329,12 @@ static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy) dst->colorspace = src->colorspace; dst->color_range = src->color_range; dst->chroma_location = src->chroma_location; + dst->repeat_sub = src->repeat_sub; + dst->subtitle_timing.start_pts = src->subtitle_timing.start_pts; + dst->subtitle_timing.duration = src->subtitle_timing.duration; + + if ((ret = av_buffer_replace(&dst->subtitle_header, src->subtitle_header)) < 0) + return ret; av_dict_copy(&dst->metadata, src->metadata, 0); @@ -329,6 +376,7 @@ int av_frame_ref(AVFrame *dst, const AVFrame *src) av_assert1(dst->width == 0 && dst->height == 0); av_assert1(dst->channels == 0); + dst->type = src->type; dst->format = src->format; dst->width = src->width; dst->height = src->height; @@ -342,7 +390,7 @@ int av_frame_ref(AVFrame *dst, const AVFrame *src) /* duplicate the frame data if it's not refcounted */ if (!src->buf[0]) { - ret = av_frame_get_buffer(dst, 0); + ret = av_frame_get_buffer2(dst, 0); if (ret < 0) goto fail; @@ -364,6 +412,10 @@ int av_frame_ref(AVFrame *dst, const AVFrame *src) } } + /* copy subtitle areas */ + if (src->type == AVMEDIA_TYPE_SUBTITLE) + frame_copy_subtitles(dst, src, 0); + if (src->extended_buf) { dst->extended_buf = av_calloc(src->nb_extended_buf, sizeof(*dst->extended_buf)); @@ -434,7 +486,7 @@ AVFrame *av_frame_clone(const AVFrame *src) void av_frame_unref(AVFrame *frame) { - int i; + unsigned i, n; if (!frame) return; @@ -453,6 +505,21 @@ void av_frame_unref(AVFrame *frame) av_buffer_unref(&frame->opaque_ref); av_buffer_unref(&frame->private_ref); + av_buffer_unref(&frame->subtitle_header); + + for (i = 0; i < frame->num_subtitle_areas; i++) { + AVSubtitleArea *area = frame->subtitle_areas[i]; + + for (n = 0; n < FF_ARRAY_ELEMS(area->buf); n++) + av_buffer_unref(&area->buf[n]); + + av_freep(&area->text); + av_freep(&area->ass); + av_free(area); + } + + av_freep(&frame->subtitle_areas); + if (frame->extended_data != frame->data) av_freep(&frame->extended_data); @@ -472,18 +539,28 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src) int av_frame_is_writable(AVFrame *frame) { - int i, ret = 1; + AVSubtitleArea *area; + int ret = 1; /* assume non-refcounted frames are not writable */ if (!frame->buf[0]) return 0; - for (i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++) + for (unsigned i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++) if (frame->buf[i]) ret &= !!av_buffer_is_writable(frame->buf[i]); - for (i = 0; i < frame->nb_extended_buf; i++) + for (unsigned i = 0; i < frame->nb_extended_buf; i++) ret &= !!av_buffer_is_writable(frame->extended_buf[i]); + for (unsigned i = 0; i < frame->num_subtitle_areas; i++) { + area = frame->subtitle_areas[i]; + if (area) { + for (unsigned n = 0; n < FF_ARRAY_ELEMS(area->buf); n++) + if (area->buf[n]) + ret &= !!av_buffer_is_writable(area->buf[n]); + } + } + return ret; } @@ -499,6 +576,7 @@ int av_frame_make_writable(AVFrame *frame) return 0; memset(&tmp, 0, sizeof(tmp)); + tmp.type = frame->type; tmp.format = frame->format; tmp.width = frame->width; tmp.height = frame->height; @@ -509,7 +587,7 @@ int av_frame_make_writable(AVFrame *frame) if (frame->hw_frames_ctx) ret = av_hwframe_get_buffer(frame->hw_frames_ctx, &tmp, 0); else - ret = av_frame_get_buffer(&tmp, 0); + ret = av_frame_get_buffer2(&tmp, 0); if (ret < 0) return ret; @@ -544,14 +622,22 @@ AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int plane) uint8_t *data; int planes, i; - if (frame->nb_samples) { - int channels = frame->channels; - if (!channels) - return NULL; - CHECK_CHANNELS_CONSISTENCY(frame); - planes = av_sample_fmt_is_planar(frame->format) ? channels : 1; - } else + switch(frame->type) { + case AVMEDIA_TYPE_VIDEO: planes = 4; + break; + case AVMEDIA_TYPE_AUDIO: + { + int channels = frame->channels; + if (!channels) + return NULL; + CHECK_CHANNELS_CONSISTENCY(frame); + planes = av_sample_fmt_is_planar(frame->format) ? channels : 1; + break; + } + default: + return NULL; + } if (plane < 0 || plane >= planes || !frame->extended_data[plane]) return NULL; @@ -675,17 +761,98 @@ static int frame_copy_audio(AVFrame *dst, const AVFrame *src) return 0; } +static int av_subtitle_area2area(AVSubtitleArea *dst, const AVSubtitleArea *src, int copy_data) +{ + dst->x = src->x; + dst->y = src->y; + dst->w = src->w; + dst->h = src->h; + dst->nb_colors = src->nb_colors; + dst->type = src->type; + dst->flags = src->flags; + + switch (dst->type) { + case AV_SUBTITLE_FMT_BITMAP: + + for (unsigned i = 0; i < AV_NUM_BUFFER_POINTERS; i++) { + if (src->h > 0 && src->w > 0 && src->buf[i]) { + dst->buf[0] = av_buffer_ref(src->buf[i]); + if (!dst->buf[i]) + return AVERROR(ENOMEM); + + if (copy_data) { + const int ret = av_buffer_make_writable(&dst->buf[i]); + if (ret < 0) + return ret; + } + + dst->linesize[i] = src->linesize[i]; + } + } + + memcpy(&dst->pal[0], &src->pal[0], sizeof(src->pal[0]) * 256); + + break; + case AV_SUBTITLE_FMT_TEXT: + + if (src->text) { + dst->text = av_strdup(src->text); + if (!dst->text) + return AVERROR(ENOMEM); + } + + break; + case AV_SUBTITLE_FMT_ASS: + + if (src->ass) { + dst->ass = av_strdup(src->ass); + if (!dst->ass) + return AVERROR(ENOMEM); + } + + break; + default: + + av_log(NULL, AV_LOG_ERROR, "Subtitle area has invalid format: %d", dst->type); + return AVERROR(ENOMEM); + } + + return 0; +} + +static int frame_copy_subtitles(AVFrame *dst, const AVFrame *src, int copy_data) +{ + dst->format = src->format; + + dst->num_subtitle_areas = src->num_subtitle_areas; + + if (src->num_subtitle_areas) { + dst->subtitle_areas = av_malloc_array(src->num_subtitle_areas, sizeof(AVSubtitleArea *)); + + for (unsigned i = 0; i < src->num_subtitle_areas; i++) { + dst->subtitle_areas[i] = av_mallocz(sizeof(AVSubtitleArea)); + av_subtitle_area2area(dst->subtitle_areas[i], src->subtitle_areas[i], copy_data); + } + } + + return 0; +} + int av_frame_copy(AVFrame *dst, const AVFrame *src) { if (dst->format != src->format || dst->format < 0) return AVERROR(EINVAL); - if (dst->width > 0 && dst->height > 0) + switch(dst->type) { + case AVMEDIA_TYPE_VIDEO: return frame_copy_video(dst, src); - else if (dst->nb_samples > 0 && dst->channels > 0) + case AVMEDIA_TYPE_AUDIO: return frame_copy_audio(dst, src); - - return AVERROR(EINVAL); + case AVMEDIA_TYPE_SUBTITLE: + return frame_copy_subtitles(dst, src, 1); + default: + return AVERROR(EINVAL); + } } void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type) diff --git a/libavutil/frame.h b/libavutil/frame.h index 18e239f870..ed519c5e2b 100644 --- a/libavutil/frame.h +++ b/libavutil/frame.h @@ -25,7 +25,6 @@ #ifndef AVUTIL_FRAME_H #define AVUTIL_FRAME_H -#include #include #include "avutil.h" @@ -34,6 +33,7 @@ #include "rational.h" #include "samplefmt.h" #include "pixfmt.h" +#include "subfmt.h" #include "version.h" @@ -285,7 +285,7 @@ typedef struct AVRegionOfInterest { } AVRegionOfInterest; /** - * This structure describes decoded (raw) audio or video data. + * This structure describes decoded (raw) audio, video or subtitle data. * * AVFrame must be allocated using av_frame_alloc(). Note that this only * allocates the AVFrame itself, the buffers for the data must be managed @@ -399,7 +399,7 @@ typedef struct AVFrame { /** * format of the frame, -1 if unknown or unset * Values correspond to enum AVPixelFormat for video frames, - * enum AVSampleFormat for audio) + * enum AVSampleFormat for audio, AVSubtitleType for subtitles) */ int format; @@ -681,6 +681,53 @@ typedef struct AVFrame { * for the target frame's private_ref field. */ AVBufferRef *private_ref; + + /** + * Media type of the frame (audio, video, subtitles..) + * + * See AVMEDIA_TYPE_xxx + */ + enum AVMediaType type; + + /** + * Number of items in the @ref subtitle_areas array. + */ + unsigned num_subtitle_areas; + + /** + * Array of subtitle areas, may be empty. + */ + AVSubtitleArea **subtitle_areas; + + /** + * Header containing style information for text subtitles. + */ + AVBufferRef *subtitle_header; + + /** + * Indicates that a subtitle frame is a repeated frame for arbitrating flow + * in a filter graph. + * The field subtitle_timing.start_pts always indicates the original presentation + * time, while the frame's pts field may be different. + */ + int repeat_sub; + + struct SubtitleTiming + { + /** + * The display start time, in AV_TIME_BASE. + * + * For subtitle frames, AVFrame.pts is populated from the packet pts value, + * which is not always the same as this value. + */ + int64_t start_pts; + + /** + * Display duration, in AV_TIME_BASE. + */ + int64_t duration; + + } subtitle_timing; } AVFrame; @@ -757,6 +804,8 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src); /** * Allocate new buffer(s) for audio or video data. * + * Note: For subtitle data, use av_frame_get_buffer2 + * * The following fields must be set on frame before calling this function: * - format (pixel format for video, sample format for audio) * - width and height for video @@ -776,9 +825,39 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src); * recommended to pass 0 here unless you know what you are doing. * * @return 0 on success, a negative AVERROR on error. + * + * @deprecated Use @ref av_frame_get_buffer2 instead and set @ref AVFrame.type + * before calling. */ +attribute_deprecated int av_frame_get_buffer(AVFrame *frame, int align); +/** + * Allocate new buffer(s) for audio, video or subtitle data. + * + * The following fields must be set on frame before calling this function: + * - format (pixel format for video, sample format for audio) + * - width and height for video + * - nb_samples and channel_layout for audio + * - type (AVMediaType) + * + * This function will fill AVFrame.data and AVFrame.buf arrays and, if + * necessary, allocate and fill AVFrame.extended_data and AVFrame.extended_buf. + * For planar formats, one buffer will be allocated for each plane. + * + * @warning: if frame already has been allocated, calling this function will + * leak memory. In addition, undefined behavior can occur in certain + * cases. + * + * @param frame frame in which to store the new buffers. + * @param align Required buffer size alignment. If equal to 0, alignment will be + * chosen automatically for the current CPU. It is highly + * recommended to pass 0 here unless you know what you are doing. + * + * @return 0 on success, a negative AVERROR on error. + */ +int av_frame_get_buffer2(AVFrame *frame, int align); + /** * Check if the frame data is writable. * diff --git a/libavutil/subfmt.c b/libavutil/subfmt.c new file mode 100644 index 0000000000..c72ebe2a43 --- /dev/null +++ b/libavutil/subfmt.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 softworkz + * + * 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 "common.h" +#include "subfmt.h" + +static const char sub_fmt_info[AV_SUBTITLE_FMT_NB][24] = { + [AV_SUBTITLE_FMT_UNKNOWN] = "Unknown subtitle format", + [AV_SUBTITLE_FMT_BITMAP] = "Graphical subtitles", + [AV_SUBTITLE_FMT_TEXT] = "Text subtitles (plain)", + [AV_SUBTITLE_FMT_ASS] = "Text subtitles (ass)", +}; + +const char *av_get_subtitle_fmt_name(enum AVSubtitleType sub_fmt) +{ + if (sub_fmt < 0 || sub_fmt >= AV_SUBTITLE_FMT_NB) + return NULL; + return sub_fmt_info[sub_fmt]; +} + +enum AVSubtitleType av_get_subtitle_fmt(const char *name) +{ + for (int i = 0; i < AV_SUBTITLE_FMT_NB; i++) + if (!strcmp(sub_fmt_info[i], name)) + return i; + return AV_SUBTITLE_FMT_NONE; +} diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h index 791b45519f..3b8a0613b2 100644 --- a/libavutil/subfmt.h +++ b/libavutil/subfmt.h @@ -21,6 +21,9 @@ #ifndef AVUTIL_SUBFMT_H #define AVUTIL_SUBFMT_H +#include + +#include "buffer.h" #include "version.h" enum AVSubtitleType { @@ -65,4 +68,48 @@ enum AVSubtitleType { AV_SUBTITLE_FMT_NB, ///< number of subtitle formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions. }; +typedef struct AVSubtitleArea { +#define AV_NUM_BUFFER_POINTERS 1 + + enum AVSubtitleType type; + int flags; + + int x; ///< top left corner of area. + int y; ///< top left corner of area. + int w; ///< width of area. + int h; ///< height of area. + int nb_colors; ///< number of colors in bitmap palette (@ref pal). + + /** + * Buffers and line sizes for the bitmap of this subtitle. + * + * @{ + */ + AVBufferRef *buf[AV_NUM_BUFFER_POINTERS]; + int linesize[AV_NUM_BUFFER_POINTERS]; + /** + * @} + */ + + uint32_t pal[256]; ///< RGBA palette for the bitmap. + + char *text; ///< 0-terminated plain UTF-8 text + char *ass; ///< 0-terminated ASS/SSA compatible event line. + +} AVSubtitleArea; + +/** + * Return the name of sub_fmt, or NULL if sub_fmt is not + * recognized. + */ +const char *av_get_subtitle_fmt_name(enum AVSubtitleType sub_fmt); + +/** + * Return a subtitle format corresponding to name, or AV_SUBTITLE_FMT_NONE + * on error. + * + * @param name Subtitle format name. + */ +enum AVSubtitleType av_get_subtitle_fmt(const char *name); + #endif /* AVUTIL_SUBFMT_H */ From patchwork Fri Jan 14 01:13:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33565 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp985030iog; Thu, 13 Jan 2022 17:14:23 -0800 (PST) X-Google-Smtp-Source: ABdhPJz9giWP+GZxzQaDwAM0AqDg6xOEZWr5vzoiYrrJ1SAMfa41G5K7TvfQ4rTS+Vi41LDyoC2+ X-Received: by 2002:a17:906:74db:: with SMTP id z27mr5372286ejl.110.1642122863747; Thu, 13 Jan 2022 17:14:23 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642122863; cv=none; d=google.com; s=arc-20160816; b=s2N0NYBvgyklKKRHfWv8SJ2eyEkzBCjds31xaHKNmQBsVKAli1Sf4yeaEBFwkp8wmP RkLvkqCd/dFU1JiNVuiOiwXIpALAN0mLdAlvfnAL9FJUyM43j3kQUTIS25e1uypyYDTV sobAlM74RggrbsWKYWgvg2MRiBDfD3UcqQzukOJIhTkwXG19FeHN5DiNdnSrhQsiiTLr 6ImEvsZyYl4h+PlwWl1s3gDIG+649OroRw65ap+SxLDsC7Mp0lLJM3wjg5x8KoJIIt4w MUDX0ae0RIXTQtyyJNSaKik1Gn/FLUK8ejNlXUcxGhkh94Kkg606h8YWX9cEGWDqnAFB mcMw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=nLeJh/15ywKcIOT4gmV+vjrhSz6GSVgJW7jrYqiqjIg=; b=hO3FHGqSi8WrQb+mDkTloxPWoh/gSTRuVH4eCXwPqG+HlxdskC0kSNQjbxznRc18T4 IRqrDXXLGZUeN6qsZ8fsXw+GLeDPDlalFIlHHh3Su8FGGjt+P2eyB0BkQSu7Ec4R6A3R Vw1a+kqrRE8yu3/osz6hZD1lI6gMpEiyB88ESFO5gr029gTJ1bI6coC5PkAuoNYX7Sk1 jH38qtx4M8a6C4lu56sFCSQA9fmCGDLM0t8frAS2tpMkE5QrupmYSdTdooGTQtjUmLVC IZjkc8wHQJsKNWaLLpH9prHCy7EN99t6qCqvjqVu2ujjvfBm2DO3gup/Hz0z13PbWy71 3feA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=W8NyMegI; 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 m13si2284320edi.185.2022.01.13.17.14.23; Thu, 13 Jan 2022 17:14:23 -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 header.s=20210112 header.b=W8NyMegI; 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 35D5B68B5A0; Fri, 14 Jan 2022 03:13:49 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pg1-f169.google.com (mail-pg1-f169.google.com [209.85.215.169]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 9BBBD68B510 for ; Fri, 14 Jan 2022 03:13:40 +0200 (EET) Received: by mail-pg1-f169.google.com with SMTP id g2so1435461pgo.9 for ; Thu, 13 Jan 2022 17:13:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=ZGdgkigsZaZjPe6KxA0rVIUUJ3kWyVvrDk/WB4wcTsU=; b=W8NyMegI47wuSTs39sXSEJIZigBsugN5x7wDHVBVC140WOCFUv79gZV4gWeP8yWAlr fxmIPIIR8/ORgE6B1XlLaKCOd/8LiqNQYGzdSx7H4KzoKN2YeYa0as18yA4fhhWlRuBm m/sHKNAU1vSf0Ga/xJdA9o5zfG96U892DsAcFJ1Eve+DH54HiVzgX0wQYXTAAkVza16e F7A+ms9D0AApNdIWw7bOcGPyp1S1VP+i5M9I53dCupaOZqJ5cOMzrtmH2/+A4IOsFVm9 3lH01WjSI6loiLFexfCp3otAHQulFecAv6siUYuBWJ7+MsxYqILuX/t1rUaOYVNYVU5i s1gw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=ZGdgkigsZaZjPe6KxA0rVIUUJ3kWyVvrDk/WB4wcTsU=; b=nN9XoDpDlLr0D71VM9whMFxcEzJaH6qDbLCfuZKyxvhZ1Qz5zWzdGgUozz3+fX3F7n 52TlCTihYcCiMYsbtPqdXdZvuPgpqqEtpKeEp9leBxfeKoAVbs9KGB4wi5J1nlTKDr8P BNOkxlk3xt3HXyPF+6PgIj0eGPw8E1ll4hnZQT/mqOSeR9sCTML6/OMLdDjHH4whvw7J pkQteZkjx8fK7pYmX0SgtrEcwlg2iHGOsIKMfn29eE1GnwZsvAnfBFUhiHa/Vh0vG0Sj Sb4OQvgX6TIhPRLhNdm6k2ikQo0mzScJbljdXKihQVfwjXis+GVutN+efpsM3wB0NljC lPTQ== X-Gm-Message-State: AOAM530Mw9j37VPrj7w46s1BysfM3lmSJ1JPXih2Z4BeOk412Nuyhgsh sLtEvQIt7v/ZdnXc53Wm/j+6tgiO3fki+g== X-Received: by 2002:aa7:918e:0:b0:4bb:793:b7a7 with SMTP id x14-20020aa7918e000000b004bb0793b7a7mr6702616pfa.71.1642122818944; Thu, 13 Jan 2022 17:13:38 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id x23sm3506600pjd.40.2022.01.13.17.13.38 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:38 -0800 (PST) Message-Id: In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:12 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 03/24] avcodec/subtitles: Introduce new frame-based subtitle decoding API 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: VdTWauzE8vO5 From: softworkz - Add avcodec_decode_subtitle3 which takes subtitle frames, serving as compatibility shim to legacy subtitle decoding - Add additional methods for conversion between old and new API Signed-off-by: softworkz --- libavcodec/avcodec.h | 8 +- libavcodec/codec_desc.c | 11 +++ libavcodec/codec_desc.h | 8 ++ libavcodec/decode.c | 56 ++++++++++-- libavcodec/internal.h | 22 +++++ libavcodec/utils.c | 184 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 280 insertions(+), 9 deletions(-) diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index fe5a83cf85..9d59f6e840 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -1675,7 +1675,7 @@ typedef struct AVCodecContext { /** * Header containing style information for text subtitles. - * For SUBTITLE_ASS subtitle type, it should contain the whole ASS + * For AV_SUBTITLE_FMT_ASS subtitle type, it should contain the whole ASS * [Script Info] and [V4+ Styles] section, plus the [Events] line and * the Format line following. It shouldn't include any Dialogue line. * - encoding: Set/allocated/freed by user (before avcodec_open2()) @@ -2415,7 +2415,10 @@ int avcodec_close(AVCodecContext *avctx); * Free all allocated data in the given subtitle struct. * * @param sub AVSubtitle to free. + * + * @deprecated Use the regular frame based encode and decode APIs instead. */ +attribute_deprecated void avsubtitle_free(AVSubtitle *sub); /** @@ -2508,7 +2511,10 @@ enum AVChromaLocation avcodec_chroma_pos_to_enum(int xpos, int ypos); * must be freed with avsubtitle_free if *got_sub_ptr is set. * @param[in,out] got_sub_ptr Zero if no subtitle could be decompressed, otherwise, it is nonzero. * @param[in] avpkt The input AVPacket containing the input buffer. + * + * @deprecated Use the new decode API (avcodec_send_packet, avcodec_receive_frame) instead. */ +attribute_deprecated int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, int *got_sub_ptr, AVPacket *avpkt); diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 0974ee03de..e48e4532ba 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -3548,3 +3548,14 @@ enum AVMediaType avcodec_get_type(enum AVCodecID codec_id) const AVCodecDescriptor *desc = avcodec_descriptor_get(codec_id); return desc ? desc->type : AVMEDIA_TYPE_UNKNOWN; } + +enum AVSubtitleType avcodec_descriptor_get_subtitle_format(const AVCodecDescriptor *codec_descriptor) +{ + if(codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB) + return AV_SUBTITLE_FMT_BITMAP; + + if(codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB) + return AV_SUBTITLE_FMT_ASS; + + return AV_SUBTITLE_FMT_UNKNOWN; +} diff --git a/libavcodec/codec_desc.h b/libavcodec/codec_desc.h index 126b52df47..ba68d24e0e 100644 --- a/libavcodec/codec_desc.h +++ b/libavcodec/codec_desc.h @@ -121,6 +121,14 @@ const AVCodecDescriptor *avcodec_descriptor_next(const AVCodecDescriptor *prev); */ const AVCodecDescriptor *avcodec_descriptor_get_by_name(const char *name); +/** + * Return subtitle format from a codec descriptor + * + * @param codec_descriptor codec descriptor + * @return the subtitle type (e.g. bitmap, text) + */ +enum AVSubtitleType avcodec_descriptor_get_subtitle_format(const AVCodecDescriptor *codec_descriptor); + /** * @} */ diff --git a/libavcodec/decode.c b/libavcodec/decode.c index 0912f86a14..ab8a6ea6ff 100644 --- a/libavcodec/decode.c +++ b/libavcodec/decode.c @@ -576,6 +576,39 @@ static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) return ret; } +static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub, + int *got_sub_ptr, AVPacket *avpkt); + +static int decode_subtitle_shim(AVCodecContext *avctx, AVFrame *frame, AVPacket *avpkt) +{ + int ret, got_sub_ptr = 0; + AVSubtitle subtitle = { 0 }; + + if (frame->buf[0]) + return AVERROR(EAGAIN); + + av_frame_unref(frame); + + ret = decode_subtitle2_priv(avctx, &subtitle, &got_sub_ptr, avpkt); + + if (ret >= 0 && got_sub_ptr) { + frame->type = AVMEDIA_TYPE_SUBTITLE; + frame->format = subtitle.format; + ret = av_frame_get_buffer2(frame, 0); + + if (ret >= 0) + ret = ff_frame_put_subtitle(frame, &subtitle); + + frame->width = avctx->width; + frame->height = avctx->height; + frame->pkt_dts = avpkt->dts; + } + + avsubtitle_free(&subtitle); + + return ret; +} + int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt) { AVCodecInternal *avci = avctx->internal; @@ -590,6 +623,9 @@ int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacke if (avpkt && !avpkt->size && avpkt->data) return AVERROR(EINVAL); + if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) + return decode_subtitle_shim(avctx, avci->buffer_frame, avpkt); + av_packet_unref(avci->buffer_pkt); if (avpkt && (avpkt->data || avpkt->side_data_elems)) { ret = av_packet_ref(avci->buffer_pkt, avpkt); @@ -651,7 +687,9 @@ int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *fr if (avci->buffer_frame->buf[0]) { av_frame_move_ref(frame, avci->buffer_frame); - } else { + } else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) + return AVERROR(EAGAIN); + else { ret = decode_receive_frame_internal(avctx, frame); if (ret < 0) return ret; @@ -802,9 +840,8 @@ static int utf8_check(const uint8_t *str) return 1; } -int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, - int *got_sub_ptr, - AVPacket *avpkt) +static int decode_subtitle2_priv(AVCodecContext *avctx, AVSubtitle *sub, + int *got_sub_ptr, AVPacket *avpkt) { int ret = 0; @@ -850,10 +887,7 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, avctx->pkt_timebase, ms); } - if (avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB) - sub->format = 0; - else if (avctx->codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB) - sub->format = 1; + sub->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor); for (unsigned i = 0; i < sub->num_rects; i++) { if (avctx->sub_charenc_mode != FF_SUB_CHARENC_MODE_IGNORE && @@ -874,6 +908,12 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, return ret; } +int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, + int *got_sub_ptr, AVPacket *avpkt) +{ + return decode_subtitle2_priv(avctx, sub, got_sub_ptr, avpkt); +} + enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *avctx, const enum AVPixelFormat *fmt) { diff --git a/libavcodec/internal.h b/libavcodec/internal.h index 72ca1553f6..ff63974f7f 100644 --- a/libavcodec/internal.h +++ b/libavcodec/internal.h @@ -363,4 +363,26 @@ int ff_int_from_list_or_default(void *ctx, const char * val_name, int val, void ff_dvdsub_parse_palette(uint32_t *palette, const char *p); +/** + * Copies subtitle data from AVSubtitle to AVFrame. + * + * @deprecated This is a compatibility method for interoperability with + * the legacy subtitle API. + */ +int ff_frame_put_subtitle(AVFrame* frame, const AVSubtitle* sub); + +/** + * Copies subtitle data from AVFrame to AVSubtitle. + * + * @deprecated This is a compatibility method for interoperability with + * the legacy subtitle API. + */ +int ff_frame_get_subtitle(AVSubtitle* sub, AVFrame* frame); + +#if defined(_WIN32) && CONFIG_SHARED && !defined(BUILDING_avcodec) +# define av_export_avcodec __declspec(dllimport) +#else +# define av_export_avcodec +#endif + #endif /* AVCODEC_INTERNAL_H */ diff --git a/libavcodec/utils.c b/libavcodec/utils.c index b19befef21..72c742c176 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -813,6 +813,190 @@ int av_get_audio_frame_duration(AVCodecContext *avctx, int frame_bytes) return FFMAX(0, duration); } +static int subtitle_area2rect(AVSubtitleRect *dst, const AVSubtitleArea *src) +{ + dst->x = src->x; + dst->y = src->y; + dst->w = src->w; + dst->h = src->h; + dst->nb_colors = src->nb_colors; + dst->type = src->type; + dst->flags = src->flags; + + switch (dst->type) { + case AV_SUBTITLE_FMT_BITMAP: + + if (src->h > 0 && src->w > 0 && src->buf[0]) { + uint32_t *pal; + AVBufferRef *buf = src->buf[0]; + dst->data[0] = av_mallocz(buf->size); + memcpy(dst->data[0], buf->data, buf->size); + dst->linesize[0] = src->linesize[0]; + + dst->data[1] = av_mallocz(256 * 4); + pal = (uint32_t *)dst->data[1]; + + for (unsigned i = 0; i < 256; i++) { + pal[i] = src->pal[i]; + } + } + + break; + case AV_SUBTITLE_FMT_TEXT: + + if (src->text) + dst->text = av_strdup(src->text); + else + dst->text = av_strdup(""); + + if (!dst->text) + return AVERROR(ENOMEM); + + break; + case AV_SUBTITLE_FMT_ASS: + + if (src->ass) + dst->ass = av_strdup(src->ass); + else + dst->ass = av_strdup(""); + + if (!dst->ass) + return AVERROR(ENOMEM); + + break; + default: + + av_log(NULL, AV_LOG_ERROR, "Subtitle rect has invalid format: %d", dst->type); + return AVERROR(EINVAL); + } + + return 0; +} + +static int subtitle_rect2area(AVSubtitleArea *dst, const AVSubtitleRect *src) +{ + dst->x = src->x; + dst->y = src->y; + dst->w = src->w; + dst->h = src->h; + dst->nb_colors = src->nb_colors; + dst->type = src->type; + dst->flags = src->flags; + + switch (dst->type) { + case AV_SUBTITLE_FMT_BITMAP: + + if (src->h > 0 && src->w > 0 && src->data[0]) { + AVBufferRef *buf = av_buffer_allocz(src->h * src->linesize[0]); + memcpy(buf->data, src->data[0], buf->size); + + dst->buf[0] = buf; + dst->linesize[0] = src->linesize[0]; + } + + if (src->data[1]) { + uint32_t *pal = (uint32_t *)src->data[1]; + + for (unsigned i = 0; i < 256; i++) { + dst->pal[i] = pal[i]; + } + } + + break; + case AV_SUBTITLE_FMT_TEXT: + + if (src->text) { + dst->text = av_strdup(src->text); + if (!dst->text) + return AVERROR(ENOMEM); + } + + break; + case AV_SUBTITLE_FMT_ASS: + + if (src->ass) { + dst->ass = av_strdup(src->ass); + if (!dst->ass) + return AVERROR(ENOMEM); + } + + break; + default: + + av_log(NULL, AV_LOG_ERROR, "Subtitle area has invalid format: %d", dst->type); + return AVERROR(EINVAL); + } + + return 0; +} + +/** + * Copies subtitle data from AVSubtitle (deprecated) to AVFrame + * + * @note This is a compatibility method for conversion to the legacy API + */ +int ff_frame_put_subtitle(AVFrame *frame, const AVSubtitle *sub) +{ + frame->format = sub->format; + frame->subtitle_timing.start_pts = sub->pts; + frame->subtitle_timing.start_pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q); + frame->subtitle_timing.duration = av_rescale_q(sub->end_display_time - sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q); + + if (sub->num_rects) { + frame->subtitle_areas = av_malloc_array(sub->num_rects, sizeof(AVSubtitleArea*)); + if (!frame->subtitle_areas) + return AVERROR(ENOMEM); + + for (unsigned i = 0; i < sub->num_rects; i++) { + int ret; + frame->subtitle_areas[i] = av_mallocz(sizeof(AVSubtitleArea)); + if (!frame->subtitle_areas[i]) + return AVERROR(ENOMEM); + ret = subtitle_rect2area(frame->subtitle_areas[i], sub->rects[i]); + if (ret < 0) { + frame->num_subtitle_areas = i; + return ret; + } + } + } + + frame->num_subtitle_areas = sub->num_rects; + return 0; +} + +/** + * Copies subtitle data from AVFrame to AVSubtitle (deprecated) + * + * @note This is a compatibility method for conversion to the legacy API + */ +int ff_frame_get_subtitle(AVSubtitle *sub, AVFrame *frame) +{ + const int64_t duration_ms = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, (AVRational){ 1, 1000 }); + + sub->start_display_time = 0; + sub->end_display_time = (int32_t)duration_ms; + sub->pts = frame->subtitle_timing.start_pts; + + if (frame->num_subtitle_areas) { + sub->rects = av_malloc_array(frame->num_subtitle_areas, sizeof(AVSubtitleRect*)); + if (!sub->rects) + return AVERROR(ENOMEM); + + for (unsigned i = 0; i < frame->num_subtitle_areas; i++) { + int ret; + sub->rects[i] = av_mallocz(sizeof(AVSubtitleRect)); + ret = subtitle_area2rect(sub->rects[i], frame->subtitle_areas[i]); + if (ret < 0) { + sub->num_rects = i; + return ret; + } + } + } + + sub->num_rects = frame->num_subtitle_areas; + return 0; +} + int av_get_audio_frame_duration2(AVCodecParameters *par, int frame_bytes) { int duration = get_audio_frame_duration(par->codec_id, par->sample_rate, From patchwork Fri Jan 14 01:13:13 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33566 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp985156iog; Thu, 13 Jan 2022 17:14:33 -0800 (PST) X-Google-Smtp-Source: ABdhPJyh7v3CI6DU8QRQ9ncvZXae8UxRLQNt5zOWwUgYEwJPS4nKzAtMRwfQb8JPBCP+us/fn4h1 X-Received: by 2002:a17:906:9b87:: with SMTP id dd7mr5388174ejc.126.1642122873837; Thu, 13 Jan 2022 17:14:33 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642122873; cv=none; d=google.com; s=arc-20160816; b=IbRIHkzC7fuO8cYLPSxOtWs5sk+nDLoJXC9SbqRzJukn8aVZOGG6+tvAEH2Ifjq1ei MwYsGaCJQzoHYOUi/E1+4OhdKuH6LBSSSwLDpX2wFxJFg91aDSf4XJgje5P5lax5s1j0 0FGh2s1a0H7V8qotNeEpJn9c6T6gTBhkgToNHDfaVPlm7cfNbv2AcF1IawWJpRHWtYdr D9aNGZ8jC8tFxLPRn6PrP2k9OJ6YZPLY6ZP2M3UzmgCZ1HOI3eEMv4P5oJ3TcNPWDuab nldX4oqgvIAgr1IyQJlI2t982gIL0dBAur3QC7jf40ssKNFbj/mpVVMo6g1jsFb0zx9b XdEg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=Gz/Qn0ewDhpg00ofGNme9RfvJONnDA1Xg0V9usnPau0=; b=r8ibkPLpueec/DF00f0Mx9ijOE4UZ4PW7/P+O153zaIMr9DwwGL7VZPe52FCjLxI+5 m4+TaUz1dygkQhHBKKIOBxJI376/LY4vwnghCs+8uWNuV5GGsxo7NhAEPcjt6lvdaNAz u28U9uBpNKZy1eIgS4YHPQkekK7TXg+D5gIb+x97uEZJRthn1kPXjdH010ZLutz/RSb0 Te+KBBzoIS4sAId63f2JX+07LWFUlcA5xC8lBu6qC8Vvf16Ag+qRRaKlsj/6lS/xeBdL xWESYk8M9oEZSj3DjdsWW6MgYjUvFECjeaSmKXr71DuDPHMmPwdAgsqixsQ4ynCUTwOD MUTA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=mKANRdJQ; 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 p4si2747402edj.354.2022.01.13.17.14.33; Thu, 13 Jan 2022 17:14:33 -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 header.s=20210112 header.b=mKANRdJQ; 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 3BC9568B562; Fri, 14 Jan 2022 03:13:50 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pg1-f171.google.com (mail-pg1-f171.google.com [209.85.215.171]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 8523668B51B for ; Fri, 14 Jan 2022 03:13:41 +0200 (EET) Received: by mail-pg1-f171.google.com with SMTP id v25so1433252pge.2 for ; Thu, 13 Jan 2022 17:13:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=5bf17VR0/ZJjwZK6zV/qDNkrzv1wJQUHu4/lfHYjj/M=; b=mKANRdJQxcRQMzOAtoxkvaJvgL9s8xj6nvlbXfvUtvkv/yotLMKbNEj5PJS8AdnWL8 YTYgxloYcXi3YcSpIRVFlKsBnz4MStLmU6zyG6M1nLumfOTom3UJvvJWpZCupa90F2vo uXzEN+lhAg4YV5UxGa1aET1gmh8vpwgR4bA35+6K759Shr17uvvNDzn734RnCeQcYrea YoUGIep5NKLvg+X34/VR7x4Kkzv3hWRwOVp4bXEyD3NLTu/Sq0xN3go8ge0SgiuDFUv2 cmMcbyaB74YStECw/DFMUsCnoLpkrhgg1n7AT4c+g2bv/kqvoPS9lBhtq3kNvZBePQgi 9wog== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=5bf17VR0/ZJjwZK6zV/qDNkrzv1wJQUHu4/lfHYjj/M=; b=UUIK2Hz5h+rWS3xr5ogL8ZRapSzx9Cui2/Rpwn9l73OSk+Lpfj9CGH95j8hXqUR0Eg dy1E0gPNbCor3I38m5N00a122FD1QIV/zZ+flvznPtsrjc1cvkNsAvYgkP7k8eXchh1G nD76FZRuo2iJLiinrXxHrzezNb4dLd04KAcpC77d14s1F0xd/fbCNC3pi+TNa2osY+tY x9u8j++IPt1nVEr+7fTelf5BgC6zTVBHyNi/8c0+eE5qxaM691Vn1Fl8qY0X+IIG99Rq /VfEU1eyAzU5oxjBfFLVru/UxSeiaE5/FyzfYxLoQG75gXvi9ZFHrhUgAQR9cCEhwpmp dCuA== X-Gm-Message-State: AOAM532oarrC73MZzg/lNkLc46IcWWIZ/zOzUTxGQFNApmSFqs74bTig lLWxxknV0+iRmQHyFT91UUReqDEr8+7cGw== X-Received: by 2002:a63:7e48:: with SMTP id o8mr6123482pgn.133.1642122819910; Thu, 13 Jan 2022 17:13:39 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id ge5sm9390381pjb.45.2022.01.13.17.13.39 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:39 -0800 (PST) Message-Id: <24d5ed19fee5c38742bfabca5ee1a18a6cbabacc.1642122814.git.ffmpegagent@gmail.com> In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:13 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 04/24] avfilter/subtitles: Update vf_subtitles to use new decoding api 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: +m6ygbWQ1RcL From: softworkz Signed-off-by: softworkz --- libavfilter/vf_subtitles.c | 56 +++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/libavfilter/vf_subtitles.c b/libavfilter/vf_subtitles.c index 3fc4eeb63d..25e217e845 100644 --- a/libavfilter/vf_subtitles.c +++ b/libavfilter/vf_subtitles.c @@ -35,14 +35,12 @@ # include "libavformat/avformat.h" #endif #include "libavutil/avstring.h" -#include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "drawutils.h" #include "avfilter.h" #include "internal.h" #include "formats.h" -#include "video.h" typedef struct AssContext { const AVClass *class; @@ -292,6 +290,29 @@ static int attachment_is_font(AVStream * st) return 0; } +static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt) +{ + int ret; + + *got_frame = 0; + + if (pkt) { + ret = avcodec_send_packet(avctx, pkt); + // In particular, we don't expect AVERROR(EAGAIN), because we read all + // decoded frames with avcodec_receive_frame() until done. + if (ret < 0 && ret != AVERROR_EOF) + return ret; + } + + ret = avcodec_receive_frame(avctx, frame); + if (ret < 0 && ret != AVERROR(EAGAIN)) + return ret; + if (ret >= 0) + *got_frame = 1; + + return 0; +} + AVFILTER_DEFINE_CLASS(subtitles); static av_cold int init_subtitles(AVFilterContext *ctx) @@ -306,6 +327,7 @@ static av_cold int init_subtitles(AVFilterContext *ctx) AVStream *st; AVPacket pkt; AssContext *ass = ctx->priv; + enum AVSubtitleType subtitle_format; /* Init libass */ ret = init(ctx); @@ -386,13 +408,17 @@ static av_cold int init_subtitles(AVFilterContext *ctx) ret = AVERROR_DECODER_NOT_FOUND; goto end; } + dec_desc = avcodec_descriptor_get(st->codecpar->codec_id); - if (dec_desc && !(dec_desc->props & AV_CODEC_PROP_TEXT_SUB)) { + subtitle_format = avcodec_descriptor_get_subtitle_format(dec_desc); + + if (subtitle_format != AV_SUBTITLE_FMT_ASS) { av_log(ctx, AV_LOG_ERROR, - "Only text based subtitles are currently supported\n"); - ret = AVERROR_PATCHWELCOME; + "Only text based subtitles are supported by this filter\n"); + ret = AVERROR_INVALIDDATA; goto end; } + if (ass->charenc) av_dict_set(&codec_opts, "sub_charenc", ass->charenc, 0); @@ -448,27 +474,31 @@ static av_cold int init_subtitles(AVFilterContext *ctx) dec_ctx->subtitle_header_size); while (av_read_frame(fmt, &pkt) >= 0) { int i, got_subtitle; - AVSubtitle sub = {0}; + AVFrame *sub = av_frame_alloc(); + if (!sub) { + ret = AVERROR(ENOMEM); + goto end; + } if (pkt.stream_index == sid) { - ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_subtitle, &pkt); + ret = decode(dec_ctx, sub, &got_subtitle, &pkt); if (ret < 0) { av_log(ctx, AV_LOG_WARNING, "Error decoding: %s (ignored)\n", av_err2str(ret)); } else if (got_subtitle) { - const int64_t start_time = av_rescale_q(sub.pts, AV_TIME_BASE_Q, av_make_q(1, 1000)); - const int64_t duration = sub.end_display_time; - for (i = 0; i < sub.num_rects; i++) { - char *ass_line = sub.rects[i]->ass; + const int64_t start_time = av_rescale_q(sub->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000)); + const int64_t duration = av_rescale_q(sub->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000)); + for (i = 0; i < sub->num_subtitle_areas; i++) { + char *ass_line = sub->subtitle_areas[i]->ass; if (!ass_line) - break; + continue; ass_process_chunk(ass->track, ass_line, strlen(ass_line), start_time, duration); } } } av_packet_unref(&pkt); - avsubtitle_free(&sub); + av_frame_free(&sub); } end: From patchwork Fri Jan 14 01:13:14 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33569 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp985480iog; Thu, 13 Jan 2022 17:15:02 -0800 (PST) X-Google-Smtp-Source: ABdhPJzqC5vtMWJHib+96KyJzBwNFZVWC1UTiOR6FLdoMTrOepmqpWqNre1Efeeu/h37x/XfPPAk X-Received: by 2002:a17:907:7858:: with SMTP id lb24mr5504905ejc.629.1642122901815; Thu, 13 Jan 2022 17:15:01 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642122901; cv=none; d=google.com; s=arc-20160816; b=U1DMvVjC4nWuSQXbZc/20i00ODvi6J/H1GZcLOGhc3VH/blChCyI6mwoxfnIj37p0h xm+842TTn3aTBONsnHcuvqx13IXna2TXAfRGXqAagAQ8wn9hRfENU/k6K+HeO4vBc+S7 JV2UbZ9BBde/IM6uhI3l875q40YWpGPf0I9U3aYkb/Ypcnnsvw81j1b2fNAPc/qw2ExR c2wawislYqhLWSOZp3mHjkhrst0bCt9H5Kjfs9VrQPye1xWZtbJ65evaZK4+6BlpTBmr 1i0PGqHZAYzHbbuj7ts3wJ/pLF9xL+ISdvdiK/TjjJibgvW76F+t6ct1SmiANIj5iz4D f3xg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=vhHQ3HeBe8Lsjo+33lQc7sWAAUYGZnukJZqB2JS4jSU=; b=DoSzXhuPakirNwZUFzCFEeDl2RdMz0casdF3r5kidJiv2vExY0xjFLBiPWQNJ/Vgkn hugVKFs0vogJABr4B/whAn14vXsWHehDJ6FmMK+k5XLVmrXsT7DRJs+9Jy43CdCbwAGN eEq2+yPZg6JNGlbQEtJmvchCxSKEkBCDqn/mtRM+TupkRUZYWRxr6ZzyXqVyAI31X1jJ pnYSctrSaLtwxt4mODPCmO9l2RzyTqpe07qe7ioRPbie6SoTnNzylZPugTYr+Ot22nro yMY5wyICBr5vDp1WAkIJgfSXLaIWe8dXLf5mSAw5+56yKm2KWAHznNJpe7pBNrVxeL77 DqzQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=SEjSsmEC; 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 i14si2284781ejr.828.2022.01.13.17.15.01; Thu, 13 Jan 2022 17:15:01 -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 header.s=20210112 header.b=SEjSsmEC; 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 B33E368B5EE; Fri, 14 Jan 2022 03:13:53 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f174.google.com (mail-pf1-f174.google.com [209.85.210.174]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 3607F68B591 for ; Fri, 14 Jan 2022 03:13:43 +0200 (EET) Received: by mail-pf1-f174.google.com with SMTP id 128so1400739pfe.12 for ; Thu, 13 Jan 2022 17:13:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=nXY8yJq+axUq0qk0eYOrLwZ69WJLswhW7rJ7ecSp2HA=; b=SEjSsmEC59WGePmHYQEXKRrrHJo4PL/EERChOAAuFhDzzx0ITS2M2vNugm4MWVf1kJ wPTyg4ePRgzMV1LjAJLMlICZeErvtyCxB1iNGbs32FQGHQo2GLqa7sFzwpnpsNNuakTW PWawY1Caxom9e+Dp0uLNs3RZGQeLKZBnBT7/fkMQWk6fupjJaOBzJHYbZAwOvj4sttRF cFGOJpwb+SuIa4tsfSOCJ95zhHvDJKn8Jk744aXRKsjIza9twddHysdhqqm/A7qPzb3N 4a/jmLquigVORL2kkry21AEvRxsLjBDrwBbfa+uIXZryZGcyyRbNY0qVVznXdDRLkiFx YOPA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=nXY8yJq+axUq0qk0eYOrLwZ69WJLswhW7rJ7ecSp2HA=; b=CO+8MlfayoORargsHNHLr58neT+A7vddcEXdeyV6ynbDsZ3FEbcrv7gfECoY5mt4Wt NVTlZb7OQENeYKQ2T6QpYnSx9rPqHDnAbM6XxHCQyB2RIWy+141FVcNs+51pWUNResBj VaAzaS50Nau7qNjJvHJ46nrX5Ea9NEZMLqxpT8/x8MKjXhlOTJ1C6/awPTOhuZY57ix8 eWbBB5dM/otzilRMj3I5x+lg0LMNAPJpSzXSHRJOMBGLF9N5htnHwlaf65BltdCAZf0s P4Ze3w9Zf2LgWUbSWGo7SALzh+1gLTZMeTIoLg3TbFEa5gCm8ZcBUzrYCODd5vxjLqOa 4Szw== X-Gm-Message-State: AOAM531v/XOb3Eu+mcR+76iBz3S5QOQcQVajqbz8ajzAec0KYUT/oa1U qWGDzq2lYeTHhfL9KzFNt3MyXG2qzid27A== X-Received: by 2002:a63:6c01:: with SMTP id h1mr6199607pgc.233.1642122820973; Thu, 13 Jan 2022 17:13:40 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id e7sm3735081pfc.106.2022.01.13.17.13.40 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:40 -0800 (PST) Message-Id: In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:14 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 05/24] avcodec, avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: J8Br4KkUqWl3 From: softworkz Also add - hard_space callback (for upcoming fix) - extensible callback (for future extension) Signed-off-by: softworkz --- libavcodec/Makefile | 56 +++---- libavcodec/ass.h | 144 ++++++------------ libavcodec/assdec.c | 2 +- libavcodec/assenc.c | 2 +- libavcodec/ccaption_dec.c | 19 +-- libavcodec/jacosubdec.c | 2 +- libavcodec/libaribb24.c | 2 +- libavcodec/libzvbi-teletextdec.c | 14 +- libavcodec/microdvddec.c | 7 +- libavcodec/movtextdec.c | 3 +- libavcodec/movtextenc.c | 20 +-- libavcodec/mpl2dec.c | 2 +- libavcodec/realtextdec.c | 2 +- libavcodec/samidec.c | 2 +- libavcodec/srtdec.c | 2 +- libavcodec/srtenc.c | 16 +- libavcodec/subviewerdec.c | 2 +- libavcodec/textdec.c | 4 +- libavcodec/ttmlenc.c | 15 +- libavcodec/webvttdec.c | 2 +- libavcodec/webvttenc.c | 16 +- libavutil/Makefile | 2 + {libavcodec => libavutil}/ass.c | 91 +++++------ libavutil/ass_internal.h | 135 ++++++++++++++++ {libavcodec => libavutil}/ass_split.c | 30 ++-- .../ass_split_internal.h | 32 ++-- 26 files changed, 355 insertions(+), 269 deletions(-) rename {libavcodec => libavutil}/ass.c (65%) create mode 100644 libavutil/ass_internal.h rename {libavcodec => libavutil}/ass_split.c (94%) rename libavcodec/ass_split.h => libavutil/ass_split_internal.h (86%) diff --git a/libavcodec/Makefile b/libavcodec/Makefile index cfc70a3eaf..80bf8ff2d2 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -215,10 +215,10 @@ OBJS-$(CONFIG_APNG_DECODER) += png.o pngdec.o pngdsp.o OBJS-$(CONFIG_APNG_ENCODER) += png.o pngenc.o OBJS-$(CONFIG_ARBC_DECODER) += arbc.o OBJS-$(CONFIG_ARGO_DECODER) += argo.o -OBJS-$(CONFIG_SSA_DECODER) += assdec.o ass.o -OBJS-$(CONFIG_SSA_ENCODER) += assenc.o ass.o -OBJS-$(CONFIG_ASS_DECODER) += assdec.o ass.o -OBJS-$(CONFIG_ASS_ENCODER) += assenc.o ass.o +OBJS-$(CONFIG_SSA_DECODER) += assdec.o +OBJS-$(CONFIG_SSA_ENCODER) += assenc.o +OBJS-$(CONFIG_ASS_DECODER) += assdec.o +OBJS-$(CONFIG_ASS_ENCODER) += assenc.o OBJS-$(CONFIG_ASV1_DECODER) += asvdec.o asv.o mpeg12data.o OBJS-$(CONFIG_ASV1_ENCODER) += asvenc.o asv.o mpeg12data.o OBJS-$(CONFIG_ASV2_DECODER) += asvdec.o asv.o mpeg12data.o @@ -259,7 +259,7 @@ OBJS-$(CONFIG_BRENDER_PIX_DECODER) += brenderpix.o OBJS-$(CONFIG_C93_DECODER) += c93.o OBJS-$(CONFIG_CAVS_DECODER) += cavs.o cavsdec.o cavsdsp.o \ cavsdata.o -OBJS-$(CONFIG_CCAPTION_DECODER) += ccaption_dec.o ass.o +OBJS-$(CONFIG_CCAPTION_DECODER) += ccaption_dec.o OBJS-$(CONFIG_CDGRAPHICS_DECODER) += cdgraphics.o OBJS-$(CONFIG_CDTOONS_DECODER) += cdtoons.o OBJS-$(CONFIG_CDXL_DECODER) += cdxl.o @@ -434,7 +434,7 @@ OBJS-$(CONFIG_INTERPLAY_ACM_DECODER) += interplayacm.o OBJS-$(CONFIG_INTERPLAY_DPCM_DECODER) += dpcm.o OBJS-$(CONFIG_INTERPLAY_VIDEO_DECODER) += interplayvideo.o OBJS-$(CONFIG_IPU_DECODER) += mpeg12dec.o mpeg12.o mpeg12data.o -OBJS-$(CONFIG_JACOSUB_DECODER) += jacosubdec.o ass.o +OBJS-$(CONFIG_JACOSUB_DECODER) += jacosubdec.o OBJS-$(CONFIG_JPEG2000_ENCODER) += j2kenc.o mqcenc.o mqc.o jpeg2000.o \ jpeg2000dwt.o OBJS-$(CONFIG_JPEG2000_DECODER) += jpeg2000dec.o jpeg2000.o jpeg2000dsp.o \ @@ -456,7 +456,7 @@ OBJS-$(CONFIG_MAGICYUV_ENCODER) += magicyuvenc.o OBJS-$(CONFIG_MDEC_DECODER) += mdec.o mpeg12.o mpeg12data.o OBJS-$(CONFIG_METASOUND_DECODER) += metasound.o metasound_data.o \ twinvq.o -OBJS-$(CONFIG_MICRODVD_DECODER) += microdvddec.o ass.o +OBJS-$(CONFIG_MICRODVD_DECODER) += microdvddec.o OBJS-$(CONFIG_MIMIC_DECODER) += mimic.o OBJS-$(CONFIG_MJPEG_DECODER) += mjpegdec.o mjpegdec_common.o OBJS-$(CONFIG_MJPEG_QSV_DECODER) += qsvdec.o @@ -471,8 +471,8 @@ 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 +OBJS-$(CONFIG_MOVTEXT_DECODER) += movtextdec.o +OBJS-$(CONFIG_MOVTEXT_ENCODER) += movtextenc.o OBJS-$(CONFIG_MP1_DECODER) += mpegaudiodec_fixed.o OBJS-$(CONFIG_MP1FLOAT_DECODER) += mpegaudiodec_float.o OBJS-$(CONFIG_MP2_DECODER) += mpegaudiodec_fixed.o @@ -513,7 +513,7 @@ OBJS-$(CONFIG_MPEG4_MEDIACODEC_DECODER) += mediacodecdec.o OBJS-$(CONFIG_MPEG4_OMX_ENCODER) += omx.o OBJS-$(CONFIG_MPEG4_V4L2M2M_DECODER) += v4l2_m2m_dec.o OBJS-$(CONFIG_MPEG4_V4L2M2M_ENCODER) += v4l2_m2m_enc.o -OBJS-$(CONFIG_MPL2_DECODER) += mpl2dec.o ass.o +OBJS-$(CONFIG_MPL2_DECODER) += mpl2dec.o OBJS-$(CONFIG_MSA1_DECODER) += mss3.o OBJS-$(CONFIG_MSCC_DECODER) += mscc.o OBJS-$(CONFIG_MSMPEG4V1_DECODER) += msmpeg4dec.o msmpeg4.o msmpeg4data.o @@ -566,7 +566,7 @@ OBJS-$(CONFIG_PGX_DECODER) += pgxdec.o OBJS-$(CONFIG_PHOTOCD_DECODER) += photocd.o OBJS-$(CONFIG_PICTOR_DECODER) += pictordec.o cga_data.o OBJS-$(CONFIG_PIXLET_DECODER) += pixlet.o -OBJS-$(CONFIG_PJS_DECODER) += textdec.o ass.o +OBJS-$(CONFIG_PJS_DECODER) += textdec.o OBJS-$(CONFIG_PNG_DECODER) += png.o pngdec.o pngdsp.o OBJS-$(CONFIG_PNG_ENCODER) += png.o pngenc.o OBJS-$(CONFIG_PPM_DECODER) += pnmdec.o pnm.o @@ -599,7 +599,7 @@ OBJS-$(CONFIG_RALF_DECODER) += ralf.o OBJS-$(CONFIG_RASC_DECODER) += rasc.o OBJS-$(CONFIG_RAWVIDEO_DECODER) += rawdec.o OBJS-$(CONFIG_RAWVIDEO_ENCODER) += rawenc.o -OBJS-$(CONFIG_REALTEXT_DECODER) += realtextdec.o ass.o +OBJS-$(CONFIG_REALTEXT_DECODER) += realtextdec.o OBJS-$(CONFIG_RL2_DECODER) += rl2.o OBJS-$(CONFIG_ROQ_DECODER) += roqvideodec.o roqvideo.o OBJS-$(CONFIG_ROQ_ENCODER) += roqvideoenc.o roqvideo.o elbg.o @@ -614,7 +614,7 @@ OBJS-$(CONFIG_RV20_DECODER) += rv10.o OBJS-$(CONFIG_RV20_ENCODER) += rv20enc.o OBJS-$(CONFIG_RV30_DECODER) += rv30.o rv34.o rv30dsp.o OBJS-$(CONFIG_RV40_DECODER) += rv40.o rv34.o rv40dsp.o -OBJS-$(CONFIG_SAMI_DECODER) += samidec.o ass.o htmlsubtitles.o +OBJS-$(CONFIG_SAMI_DECODER) += samidec.o htmlsubtitles.o OBJS-$(CONFIG_S302M_DECODER) += s302m.o OBJS-$(CONFIG_S302M_ENCODER) += s302menc.o OBJS-$(CONFIG_SANM_DECODER) += sanm.o @@ -649,13 +649,13 @@ OBJS-$(CONFIG_SPEEDHQ_ENCODER) += speedhq.o mpeg12data.o mpeg12enc.o spe OBJS-$(CONFIG_SPEEX_DECODER) += speexdec.o OBJS-$(CONFIG_SP5X_DECODER) += sp5xdec.o OBJS-$(CONFIG_SRGC_DECODER) += mscc.o -OBJS-$(CONFIG_SRT_DECODER) += srtdec.o ass.o htmlsubtitles.o -OBJS-$(CONFIG_SRT_ENCODER) += srtenc.o ass_split.o -OBJS-$(CONFIG_STL_DECODER) += textdec.o ass.o -OBJS-$(CONFIG_SUBRIP_DECODER) += srtdec.o ass.o htmlsubtitles.o -OBJS-$(CONFIG_SUBRIP_ENCODER) += srtenc.o ass_split.o -OBJS-$(CONFIG_SUBVIEWER1_DECODER) += textdec.o ass.o -OBJS-$(CONFIG_SUBVIEWER_DECODER) += subviewerdec.o ass.o +OBJS-$(CONFIG_SRT_DECODER) += srtdec.o htmlsubtitles.o +OBJS-$(CONFIG_SRT_ENCODER) += srtenc.o +OBJS-$(CONFIG_STL_DECODER) += textdec.o +OBJS-$(CONFIG_SUBRIP_DECODER) += srtdec.o htmlsubtitles.o +OBJS-$(CONFIG_SUBRIP_ENCODER) += srtenc.o +OBJS-$(CONFIG_SUBVIEWER1_DECODER) += textdec.o +OBJS-$(CONFIG_SUBVIEWER_DECODER) += subviewerdec.o OBJS-$(CONFIG_SUNRAST_DECODER) += sunrast.o OBJS-$(CONFIG_SUNRAST_ENCODER) += sunrastenc.o OBJS-$(CONFIG_LIBRSVG_DECODER) += librsvgdec.o @@ -665,8 +665,8 @@ OBJS-$(CONFIG_SVQ1_DECODER) += svq1dec.o svq1.o h263data.o OBJS-$(CONFIG_SVQ1_ENCODER) += svq1enc.o svq1.o h263data.o \ h263.o ituh263enc.o OBJS-$(CONFIG_SVQ3_DECODER) += svq3.o mpegutils.o h264data.o -OBJS-$(CONFIG_TEXT_DECODER) += textdec.o ass.o -OBJS-$(CONFIG_TEXT_ENCODER) += srtenc.o ass_split.o +OBJS-$(CONFIG_TEXT_DECODER) += textdec.o +OBJS-$(CONFIG_TEXT_ENCODER) += srtenc.o OBJS-$(CONFIG_TAK_DECODER) += takdec.o tak.o takdsp.o OBJS-$(CONFIG_TARGA_DECODER) += targa.o OBJS-$(CONFIG_TARGA_ENCODER) += targaenc.o rle.o @@ -686,7 +686,7 @@ OBJS-$(CONFIG_TSCC_DECODER) += tscc.o msrledec.o OBJS-$(CONFIG_TSCC2_DECODER) += tscc2.o OBJS-$(CONFIG_TTA_DECODER) += tta.o ttadata.o ttadsp.o OBJS-$(CONFIG_TTA_ENCODER) += ttaenc.o ttaencdsp.o ttadata.o -OBJS-$(CONFIG_TTML_ENCODER) += ttmlenc.o ass_split.o +OBJS-$(CONFIG_TTML_ENCODER) += ttmlenc.o OBJS-$(CONFIG_TWINVQ_DECODER) += twinvqdec.o twinvq.o metasound_data.o OBJS-$(CONFIG_TXD_DECODER) += txd.o OBJS-$(CONFIG_ULTI_DECODER) += ulti.o @@ -741,15 +741,15 @@ OBJS-$(CONFIG_VP9_MEDIACODEC_DECODER) += mediacodecdec.o OBJS-$(CONFIG_VP9_RKMPP_DECODER) += rkmppdec.o OBJS-$(CONFIG_VP9_VAAPI_ENCODER) += vaapi_encode_vp9.o OBJS-$(CONFIG_VP9_QSV_ENCODER) += qsvenc_vp9.o -OBJS-$(CONFIG_VPLAYER_DECODER) += textdec.o ass.o +OBJS-$(CONFIG_VPLAYER_DECODER) += textdec.o OBJS-$(CONFIG_VP9_V4L2M2M_DECODER) += v4l2_m2m_dec.o OBJS-$(CONFIG_VQA_DECODER) += vqavideo.o OBJS-$(CONFIG_WAVPACK_DECODER) += wavpack.o wavpackdata.o dsd.o OBJS-$(CONFIG_WAVPACK_ENCODER) += wavpackdata.o wavpackenc.o OBJS-$(CONFIG_WCMV_DECODER) += wcmv.o OBJS-$(CONFIG_WEBP_DECODER) += webp.o -OBJS-$(CONFIG_WEBVTT_DECODER) += webvttdec.o ass.o -OBJS-$(CONFIG_WEBVTT_ENCODER) += webvttenc.o ass_split.o +OBJS-$(CONFIG_WEBVTT_DECODER) += webvttdec.o +OBJS-$(CONFIG_WEBVTT_ENCODER) += webvttenc.o OBJS-$(CONFIG_WMALOSSLESS_DECODER) += wmalosslessdec.o wma_common.o OBJS-$(CONFIG_WMAPRO_DECODER) += wmaprodec.o wma.o wma_common.o OBJS-$(CONFIG_WMAV1_DECODER) += wmadec.o wma.o wma_common.o aactab.o @@ -1040,7 +1040,7 @@ OBJS-$(CONFIG_PCM_ALAW_AT_ENCODER) += audiotoolboxenc.o OBJS-$(CONFIG_PCM_MULAW_AT_ENCODER) += audiotoolboxenc.o OBJS-$(CONFIG_LIBAOM_AV1_DECODER) += libaomdec.o OBJS-$(CONFIG_LIBAOM_AV1_ENCODER) += libaomenc.o -OBJS-$(CONFIG_LIBARIBB24_DECODER) += libaribb24.o ass.o +OBJS-$(CONFIG_LIBARIBB24_DECODER) += libaribb24.o OBJS-$(CONFIG_LIBCELT_DECODER) += libcelt_dec.o OBJS-$(CONFIG_LIBCODEC2_DECODER) += libcodec2.o OBJS-$(CONFIG_LIBCODEC2_ENCODER) += libcodec2.o @@ -1091,7 +1091,7 @@ OBJS-$(CONFIG_LIBX265_ENCODER) += libx265.o OBJS-$(CONFIG_LIBXAVS_ENCODER) += libxavs.o OBJS-$(CONFIG_LIBXAVS2_ENCODER) += libxavs2.o OBJS-$(CONFIG_LIBXVID_ENCODER) += libxvid.o -OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER) += libzvbi-teletextdec.o ass.o +OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER) += libzvbi-teletextdec.o # parsers OBJS-$(CONFIG_AAC_LATM_PARSER) += latm_parser.o diff --git a/libavcodec/ass.h b/libavcodec/ass.h index 2c260e4e78..8bc13d7ab8 100644 --- a/libavcodec/ass.h +++ b/libavcodec/ass.h @@ -23,117 +23,73 @@ #define AVCODEC_ASS_H #include "avcodec.h" -#include "libavutil/bprint.h" - -#define ASS_DEFAULT_PLAYRESX 384 -#define ASS_DEFAULT_PLAYRESY 288 - -/** - * @name Default values for ASS style - * @{ - */ -#define ASS_DEFAULT_FONT "Arial" -#define ASS_DEFAULT_FONT_SIZE 16 -#define ASS_DEFAULT_COLOR 0xffffff -#define ASS_DEFAULT_BACK_COLOR 0 -#define ASS_DEFAULT_BOLD 0 -#define ASS_DEFAULT_ITALIC 0 -#define ASS_DEFAULT_UNDERLINE 0 -#define ASS_DEFAULT_ALIGNMENT 2 -#define ASS_DEFAULT_BORDERSTYLE 1 -/** @} */ +#include "libavutil/ass_internal.h" typedef struct FFASSDecoderContext { int readorder; } FFASSDecoderContext; -/** - * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS. - * Can specify all fields explicitly - * - * @param avctx pointer to the AVCodecContext - * @param play_res_x subtitle frame width - * @param play_res_y subtitle frame height - * @param font name of the default font face to use - * @param font_size default font size to use - * @param primary_color default text color to use (ABGR) - * @param secondary_color default secondary text color to use (ABGR) - * @param outline_color default outline color to use (ABGR) - * @param back_color default background color to use (ABGR) - * @param bold 1 for bold text, 0 for normal text - * @param italic 1 for italic text, 0 for normal text - * @param underline 1 for underline text, 0 for normal text - * @param border_style 1 for outline, 3 for opaque box - * @param alignment position of the text (left, center, top...), defined after - * the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top) - * @return >= 0 on success otherwise an error code <0 - */ -int ff_ass_subtitle_header_full(AVCodecContext *avctx, +static inline int ff_ass_subtitle_header_full(AVCodecContext *avctx, int play_res_x, int play_res_y, const char *font, int font_size, int primary_color, int secondary_color, int outline_color, int back_color, int bold, int italic, int underline, - int border_style, int alignment); -/** - * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS. - * - * @param avctx pointer to the AVCodecContext - * @param font name of the default font face to use - * @param font_size default font size to use - * @param color default text color to use (ABGR) - * @param back_color default background color to use (ABGR) - * @param bold 1 for bold text, 0 for normal text - * @param italic 1 for italic text, 0 for normal text - * @param underline 1 for underline text, 0 for normal text - * @param alignment position of the text (left, center, top...), defined after - * the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top) - * @return >= 0 on success otherwise an error code <0 - */ -int ff_ass_subtitle_header(AVCodecContext *avctx, - const char *font, int font_size, - int color, int back_color, - int bold, int italic, int underline, - int border_style, int alignment); + int border_style, int alignment) +{ + avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_full( + play_res_x, play_res_y, font, font_size, + primary_color, secondary_color, outline_color, + back_color, bold,italic,underline,border_style,alignment, + !(avctx->flags & AV_CODEC_FLAG_BITEXACT)); -/** - * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS - * with default style. - * - * @param avctx pointer to the AVCodecContext - * @return >= 0 on success otherwise an error code <0 - */ -int ff_ass_subtitle_header_default(AVCodecContext *avctx); + if (!avctx->subtitle_header) + return AVERROR(ENOMEM); + avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header); + return 0; +} -/** - * Craft an ASS dialog string. - */ -char *ff_ass_get_dialog(int readorder, int layer, const char *style, - const char *speaker, const char *text); +static inline int ff_ass_subtitle_header_default(AVCodecContext *avctx) +{ + avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_default(!(avctx->flags & AV_CODEC_FLAG_BITEXACT)); + + if (!avctx->subtitle_header) + return AVERROR(ENOMEM); + avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header); + return 0; +} + +static inline void ff_ass_decoder_flush(AVCodecContext *avctx) +{ + FFASSDecoderContext *s = avctx->priv_data; + if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP)) + s->readorder = 0; +} /** * Add an ASS dialog to a subtitle. */ -int ff_ass_add_rect(AVSubtitle *sub, const char *dialog, +static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog, int readorder, int layer, const char *style, - const char *speaker); + const char *speaker) +{ + char *ass_str; + AVSubtitleRect **rects; -/** - * Helper to flush a text subtitles decoder making use of the - * FFASSDecoderContext. - */ -void ff_ass_decoder_flush(AVCodecContext *avctx); + rects = av_realloc_array(sub->rects, sub->num_rects+1, sizeof(*sub->rects)); + if (!rects) + return AVERROR(ENOMEM); + sub->rects = rects; + rects[sub->num_rects] = av_mallocz(sizeof(*rects[0])); + if (!rects[sub->num_rects]) + return AVERROR(ENOMEM); + rects[sub->num_rects]->type = SUBTITLE_ASS; + ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog); + if (!ass_str) + return AVERROR(ENOMEM); + rects[sub->num_rects]->ass = ass_str; + sub->num_rects++; + return 0; +} -/** - * Escape a text subtitle using ASS syntax into an AVBPrint buffer. - * Newline characters will be escaped to \N. - * - * @param buf pointer to an initialized AVBPrint buffer - * @param p source text - * @param size size of the source text - * @param linebreaks additional newline chars, which will be escaped to \N - * @param keep_ass_markup braces and backslash will not be escaped if set - */ -void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size, - const char *linebreaks, int keep_ass_markup); #endif /* AVCODEC_ASS_H */ diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c index 319279490c..7802a44e71 100644 --- a/libavcodec/assdec.c +++ b/libavcodec/assdec.c @@ -22,7 +22,7 @@ #include #include "avcodec.h" -#include "ass.h" +#include "libavutil/ass_internal.h" #include "internal.h" #include "libavutil/internal.h" #include "libavutil/mem.h" diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c index a6d107ded2..b0e475834b 100644 --- a/libavcodec/assenc.c +++ b/libavcodec/assenc.c @@ -22,7 +22,7 @@ #include #include "avcodec.h" -#include "ass.h" +#include "libavutil/ass_internal.h" #include "internal.h" #include "libavutil/avstring.h" #include "libavutil/internal.h" diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c index 27c61527f6..27eef75657 100644 --- a/libavcodec/ccaption_dec.c +++ b/libavcodec/ccaption_dec.c @@ -272,15 +272,12 @@ static av_cold int init_decoder(AVCodecContext *avctx) ctx->bg_color = CCCOL_BLACK; ctx->rollup = 2; ctx->cursor_row = 10; - ret = ff_ass_subtitle_header(avctx, "Monospace", + ret = ff_ass_subtitle_header_full(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, "Monospace", ASS_DEFAULT_FONT_SIZE, - ASS_DEFAULT_COLOR, - ASS_DEFAULT_BACK_COLOR, - ASS_DEFAULT_BOLD, - ASS_DEFAULT_ITALIC, - ASS_DEFAULT_UNDERLINE, - 3, - ASS_DEFAULT_ALIGNMENT); + ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR, + ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, + ASS_DEFAULT_BOLD, ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE, + 3, ASS_DEFAULT_ALIGNMENT); if (ret < 0) { return ret; } @@ -886,7 +883,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp AV_TIME_BASE_Q, ms_tb); else sub->end_display_time = -1; - ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL); if (ret < 0) return ret; ctx->last_real_time = sub->pts; @@ -896,7 +893,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp if (!bptr && !ctx->real_time && ctx->buffer[!ctx->buffer_index].str[0]) { bidx = !ctx->buffer_index; - ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL); if (ret < 0) return ret; sub->pts = ctx->buffer_time[1]; @@ -914,7 +911,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp capture_screen(ctx); ctx->buffer_changed = 0; - ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL); if (ret < 0) return ret; sub->end_display_time = -1; diff --git a/libavcodec/jacosubdec.c b/libavcodec/jacosubdec.c index 698895a86b..6a53ec3e34 100644 --- a/libavcodec/jacosubdec.c +++ b/libavcodec/jacosubdec.c @@ -183,7 +183,7 @@ static int jacosub_decode_frame(AVCodecContext *avctx, av_bprint_init(&buffer, JSS_MAX_LINESIZE, JSS_MAX_LINESIZE); jacosub_to_ass(avctx, &buffer, ptr); - ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buffer, NULL); if (ret < 0) return ret; diff --git a/libavcodec/libaribb24.c b/libavcodec/libaribb24.c index 0766c0079d..3fb7e5f16e 100644 --- a/libavcodec/libaribb24.c +++ b/libavcodec/libaribb24.c @@ -273,7 +273,7 @@ next_region: av_log(avctx, AV_LOG_DEBUG, "Styled ASS line: %s\n", buf.str); - ret = ff_ass_add_rect(sub, buf.str, b24->read_order++, + ret = avpriv_ass_add_rect(sub, buf.str, b24->read_order++, 0, NULL, NULL); } diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c index 1073d6a0bd..bd9edc34d7 100644 --- a/libavcodec/libzvbi-teletextdec.c +++ b/libavcodec/libzvbi-teletextdec.c @@ -152,12 +152,12 @@ static char *create_ass_text(TeletextContext *ctx, const char *text) AVBPrint buf; av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); - ff_ass_bprint_text_event(&buf, text, strlen(text), "", 0); + avpriv_ass_bprint_text_event(&buf, text, strlen(text), "", 0); if (!av_bprint_is_complete(&buf)) { av_bprint_finalize(&buf, NULL); return NULL; } - dialog = ff_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str); + dialog = avpriv_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str); av_bprint_finalize(&buf, NULL); return dialog; } @@ -224,7 +224,7 @@ static int gen_sub_text(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page } av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass); } else { - sub_rect->type = SUBTITLE_NONE; + sub_rect->type = AV_SUBTITLE_FMT_NONE; } av_bprint_finalize(&buf, NULL); return 0; @@ -394,7 +394,7 @@ static int gen_sub_ass(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page if (buf.len) { sub_rect->type = SUBTITLE_ASS; - sub_rect->ass = ff_ass_get_dialog(ctx->readorder++, 0, is_subtitle_page ? "Subtitle" : "Teletext", NULL, buf.str); + sub_rect->ass = avpriv_ass_get_dialog(ctx->readorder++, 0, is_subtitle_page ? "Subtitle" : "Teletext", NULL, buf.str); if (!sub_rect->ass) { av_bprint_finalize(&buf, NULL); @@ -402,7 +402,7 @@ static int gen_sub_ass(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page } av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass); } else { - sub_rect->type = SUBTITLE_NONE; + sub_rect->type = AV_SUBTITLE_FMT_NONE; } av_bprint_finalize(&buf, NULL); return 0; @@ -462,7 +462,7 @@ static int gen_sub_bitmap(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_pa if (vc >= vcend) { av_log(ctx, AV_LOG_DEBUG, "dropping empty page %3x\n", page->pgno); - sub_rect->type = SUBTITLE_NONE; + sub_rect->type = AV_SUBTITLE_FMT_NONE; return 0; } @@ -695,7 +695,7 @@ static int teletext_decode_frame(AVCodecContext *avctx, void *data, int *got_sub sub->num_rects = 0; sub->pts = ctx->pages->pts; - if (ctx->pages->sub_rect->type != SUBTITLE_NONE) { + if (ctx->pages->sub_rect->type != AV_SUBTITLE_FMT_NONE) { sub->rects = av_malloc(sizeof(*sub->rects)); if (sub->rects) { sub->num_rects = 1; diff --git a/libavcodec/microdvddec.c b/libavcodec/microdvddec.c index c45fe043bf..fc09db8997 100644 --- a/libavcodec/microdvddec.c +++ b/libavcodec/microdvddec.c @@ -310,7 +310,7 @@ static int microdvd_decode_frame(AVCodecContext *avctx, } } if (new_line.len) { - int ret = ff_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL); + int ret = avpriv_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&new_line, NULL); if (ret < 0) return ret; @@ -363,8 +363,9 @@ static int microdvd_init(AVCodecContext *avctx) } } } - return ff_ass_subtitle_header(avctx, font_buf.str, font_size, color, - ASS_DEFAULT_BACK_COLOR, bold, italic, + return ff_ass_subtitle_header_full(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, + font_buf.str, font_size, color, color, + ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, bold, italic, underline, ASS_DEFAULT_BORDERSTYLE, alignment); } diff --git a/libavcodec/movtextdec.c b/libavcodec/movtextdec.c index 825632ca9b..9b17bac9ea 100644 --- a/libavcodec/movtextdec.c +++ b/libavcodec/movtextdec.c @@ -22,7 +22,6 @@ #include "avcodec.h" #include "ass.h" #include "libavutil/opt.h" -#include "libavutil/avstring.h" #include "libavutil/common.h" #include "libavutil/bprint.h" #include "libavutil/intreadwrite.h" @@ -554,7 +553,7 @@ static int mov_text_decode_frame(AVCodecContext *avctx, } else text_to_ass(&buf, ptr, end, avctx); - ret = ff_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL); av_bprint_finalize(&buf, NULL); if (ret < 0) return ret; diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c index 221cd76fea..d506ed5c37 100644 --- a/libavcodec/movtextenc.c +++ b/libavcodec/movtextenc.c @@ -26,8 +26,8 @@ #include "libavutil/intreadwrite.h" #include "libavutil/mem.h" #include "libavutil/common.h" -#include "ass_split.h" -#include "ass.h" +#include "libavutil/ass_split_internal.h" +#include "libavutil/ass_internal.h" #include "bytestream.h" #include "internal.h" @@ -167,7 +167,7 @@ static int mov_text_encode_close(AVCodecContext *avctx) { MovTextContext *s = avctx->priv_data; - ff_ass_split_free(s->ass_ctx); + avpriv_ass_split_free(s->ass_ctx); av_freep(&s->style_attributes); av_freep(&s->fonts); av_bprint_finalize(&s->buffer, NULL); @@ -222,7 +222,7 @@ static int encode_sample_description(AVCodecContext *avctx) else s->font_scale_factor = 1; - style = ff_ass_style_get(s->ass_ctx, "Default"); + style = avpriv_ass_style_get(s->ass_ctx, "Default"); if (!style && ass->styles_count) { style = &ass->styles[0]; } @@ -329,7 +329,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx) av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); - s->ass_ctx = ff_ass_split(avctx->subtitle_header); + s->ass_ctx = avpriv_ass_split(avctx->subtitle_header); if (!s->ass_ctx) return AVERROR_INVALIDDATA; ret = encode_sample_description(avctx); @@ -566,7 +566,7 @@ static void mov_text_ass_style_set(MovTextContext *s, ASSStyle *style) static void mov_text_dialog(MovTextContext *s, ASSDialog *dialog) { - ASSStyle *style = ff_ass_style_get(s->ass_ctx, dialog->style); + ASSStyle *style = avpriv_ass_style_get(s->ass_ctx, dialog->style); s->ass_dialog_style = style; mov_text_ass_style_set(s, style); @@ -580,7 +580,7 @@ static void mov_text_cancel_overrides_cb(void *priv, const char *style_name) if (!style_name || !*style_name) style = s->ass_dialog_style; else - style= ff_ass_style_get(s->ass_ctx, style_name); + style= avpriv_ass_style_get(s->ass_ctx, style_name); mov_text_ass_style_set(s, style); } @@ -652,12 +652,12 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, return AVERROR(EINVAL); } - dialog = ff_ass_split_dialog(s->ass_ctx, ass); + dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); if (!dialog) return AVERROR(ENOMEM); mov_text_dialog(s, dialog); - ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text); - ff_ass_free_dialog(&dialog); + avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text); + avpriv_ass_free_dialog(&dialog); } if (s->buffer.len > UINT16_MAX) diff --git a/libavcodec/mpl2dec.c b/libavcodec/mpl2dec.c index 61e47050ec..fe806b4927 100644 --- a/libavcodec/mpl2dec.c +++ b/libavcodec/mpl2dec.c @@ -74,7 +74,7 @@ static int mpl2_decode_frame(AVCodecContext *avctx, void *data, av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); if (ptr && avpkt->size > 0 && *ptr && !mpl2_event_to_ass(&buf, ptr)) - ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buf, NULL); if (ret < 0) return ret; diff --git a/libavcodec/realtextdec.c b/libavcodec/realtextdec.c index 11b586d493..acd548b6b5 100644 --- a/libavcodec/realtextdec.c +++ b/libavcodec/realtextdec.c @@ -67,7 +67,7 @@ static int realtext_decode_frame(AVCodecContext *avctx, av_bprint_init(&buf, 0, 4096); if (ptr && avpkt->size > 0 && !rt_event_to_ass(&buf, ptr)) - ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buf, NULL); if (ret < 0) return ret; diff --git a/libavcodec/samidec.c b/libavcodec/samidec.c index 32d07447b4..1249b629d6 100644 --- a/libavcodec/samidec.c +++ b/libavcodec/samidec.c @@ -144,7 +144,7 @@ static int sami_decode_frame(AVCodecContext *avctx, if (ret < 0) return ret; // TODO: pass escaped sami->encoded_source.str as source - ret = ff_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL); if (ret < 0) return ret; } diff --git a/libavcodec/srtdec.c b/libavcodec/srtdec.c index 4f16226b83..847352eb37 100644 --- a/libavcodec/srtdec.c +++ b/libavcodec/srtdec.c @@ -78,7 +78,7 @@ static int srt_decode_frame(AVCodecContext *avctx, ret = srt_to_ass(avctx, &buffer, avpkt->data, x1, y1, x2, y2); if (ret >= 0) - ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buffer, NULL); if (ret < 0) return ret; diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c index 2e3ac55770..a7c5fccefe 100644 --- a/libavcodec/srtenc.c +++ b/libavcodec/srtenc.c @@ -23,8 +23,8 @@ #include "avcodec.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" -#include "ass_split.h" -#include "ass.h" +#include "libavutil/ass_split_internal.h" +#include "libavutil/ass_internal.h" #include "internal.h" @@ -94,7 +94,7 @@ static void srt_stack_push_pop(SRTContext *s, const char c, int close) static void srt_style_apply(SRTContext *s, const char *style) { - ASSStyle *st = ff_ass_style_get(s->ass_ctx, style); + ASSStyle *st = avpriv_ass_style_get(s->ass_ctx, style); if (st) { int c = st->primary_color & 0xFFFFFF; if (st->font_name && strcmp(st->font_name, ASS_DEFAULT_FONT) || @@ -135,7 +135,7 @@ static av_cold int srt_encode_init(AVCodecContext *avctx) { SRTContext *s = avctx->priv_data; s->avctx = avctx; - s->ass_ctx = ff_ass_split(avctx->subtitle_header); + s->ass_ctx = avpriv_ass_split(avctx->subtitle_header); av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); return s->ass_ctx ? 0 : AVERROR_INVALIDDATA; } @@ -245,14 +245,14 @@ static int encode_frame(AVCodecContext *avctx, return AVERROR(EINVAL); } - dialog = ff_ass_split_dialog(s->ass_ctx, ass); + dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); if (!dialog) return AVERROR(ENOMEM); s->alignment_applied = 0; if (avctx->codec_id == AV_CODEC_ID_SUBRIP) srt_style_apply(s, dialog->style); - ff_ass_split_override_codes(cb, s, dialog->text); - ff_ass_free_dialog(&dialog); + avpriv_ass_split_override_codes(cb, s, dialog->text); + avpriv_ass_free_dialog(&dialog); } if (!av_bprint_is_complete(&s->buffer)) @@ -284,7 +284,7 @@ static int text_encode_frame(AVCodecContext *avctx, static int srt_encode_close(AVCodecContext *avctx) { SRTContext *s = avctx->priv_data; - ff_ass_split_free(s->ass_ctx); + avpriv_ass_split_free(s->ass_ctx); av_bprint_finalize(&s->buffer, NULL); return 0; } diff --git a/libavcodec/subviewerdec.c b/libavcodec/subviewerdec.c index 5c650d0cde..04e9ae7c09 100644 --- a/libavcodec/subviewerdec.c +++ b/libavcodec/subviewerdec.c @@ -58,7 +58,7 @@ static int subviewer_decode_frame(AVCodecContext *avctx, av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); if (ptr && avpkt->size > 0 && !subviewer_event_to_ass(&buf, ptr)) - ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buf, NULL); if (ret < 0) return ret; diff --git a/libavcodec/textdec.c b/libavcodec/textdec.c index 308553660a..7556deefe8 100644 --- a/libavcodec/textdec.c +++ b/libavcodec/textdec.c @@ -54,8 +54,8 @@ static int text_decode_frame(AVCodecContext *avctx, void *data, av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); if (ptr && avpkt->size > 0 && *ptr) { - ff_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup); - ret = ff_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL); + avpriv_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup); + ret = avpriv_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL); } av_bprint_finalize(&buf, NULL); if (ret < 0) diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c index ad2eddfdd5..083f2dd67a 100644 --- a/libavcodec/ttmlenc.c +++ b/libavcodec/ttmlenc.c @@ -32,8 +32,7 @@ #include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/internal.h" -#include "ass_split.h" -#include "ass.h" +#include "libavutil/ass_split_internal.h" #include "ttmlenc.h" typedef struct { @@ -95,7 +94,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, return AVERROR(EINVAL); } - dialog = ff_ass_split_dialog(s->ass_ctx, ass); + dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); if (!dialog) return AVERROR(ENOMEM); @@ -107,7 +106,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, av_bprintf(&s->buffer, "\">"); } - ret = ff_ass_split_override_codes(&ttml_callbacks, s, dialog->text); + ret = avpriv_ass_split_override_codes(&ttml_callbacks, s, dialog->text); if (ret < 0) { int log_level = (ret != AVERROR_INVALIDDATA || avctx->err_recognition & AV_EF_EXPLODE) ? @@ -118,7 +117,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, av_err2str(ret)); if (log_level == AV_LOG_ERROR) { - ff_ass_free_dialog(&dialog); + avpriv_ass_free_dialog(&dialog); return ret; } } @@ -126,7 +125,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, if (dialog->style) av_bprintf(&s->buffer, ""); - ff_ass_free_dialog(&dialog); + avpriv_ass_free_dialog(&dialog); } if (!av_bprint_is_complete(&s->buffer)) @@ -148,7 +147,7 @@ static av_cold int ttml_encode_close(AVCodecContext *avctx) { TTMLContext *s = avctx->priv_data; - ff_ass_split_free(s->ass_ctx); + avpriv_ass_split_free(s->ass_ctx); av_bprint_finalize(&s->buffer, NULL); @@ -372,7 +371,7 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx) av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); - if (!(s->ass_ctx = ff_ass_split(avctx->subtitle_header))) { + if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) { return AVERROR_INVALIDDATA; } diff --git a/libavcodec/webvttdec.c b/libavcodec/webvttdec.c index 0093f328fa..d304edc705 100644 --- a/libavcodec/webvttdec.c +++ b/libavcodec/webvttdec.c @@ -91,7 +91,7 @@ static int webvtt_decode_frame(AVCodecContext *avctx, av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); if (ptr && avpkt->size > 0 && !webvtt_event_to_ass(&buf, ptr)) - ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buf, NULL); if (ret < 0) return ret; diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c index 89b49e42bf..761099b69a 100644 --- a/libavcodec/webvttenc.c +++ b/libavcodec/webvttenc.c @@ -24,8 +24,8 @@ #include "avcodec.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" -#include "ass_split.h" -#include "ass.h" +#include "libavutil/ass_split_internal.h" +#include "libavutil/ass_internal.h" #include "internal.h" #define WEBVTT_STACK_SIZE 64 @@ -93,7 +93,7 @@ static void webvtt_stack_push_pop(WebVTTContext *s, const char c, int close) static void webvtt_style_apply(WebVTTContext *s, const char *style) { - ASSStyle *st = ff_ass_style_get(s->ass_ctx, style); + ASSStyle *st = avpriv_ass_style_get(s->ass_ctx, style); if (st) { if (st->bold != ASS_DEFAULT_BOLD) { webvtt_print(s, ""); @@ -172,12 +172,12 @@ static int webvtt_encode_frame(AVCodecContext *avctx, return AVERROR(EINVAL); } - dialog = ff_ass_split_dialog(s->ass_ctx, ass); + dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); if (!dialog) return AVERROR(ENOMEM); webvtt_style_apply(s, dialog->style); - ff_ass_split_override_codes(&webvtt_callbacks, s, dialog->text); - ff_ass_free_dialog(&dialog); + avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text); + avpriv_ass_free_dialog(&dialog); } if (!av_bprint_is_complete(&s->buffer)) @@ -197,7 +197,7 @@ static int webvtt_encode_frame(AVCodecContext *avctx, static int webvtt_encode_close(AVCodecContext *avctx) { WebVTTContext *s = avctx->priv_data; - ff_ass_split_free(s->ass_ctx); + avpriv_ass_split_free(s->ass_ctx); av_bprint_finalize(&s->buffer, NULL); return 0; } @@ -206,7 +206,7 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx) { WebVTTContext *s = avctx->priv_data; s->avctx = avctx; - s->ass_ctx = ff_ass_split(avctx->subtitle_header); + s->ass_ctx = avpriv_ass_split(avctx->subtitle_header); av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); return s->ass_ctx ? 0 : AVERROR_INVALIDDATA; } diff --git a/libavutil/Makefile b/libavutil/Makefile index 8bc0a14942..7d4c4793b1 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -101,6 +101,8 @@ BUILT_HEADERS = avconfig.h \ OBJS = adler32.o \ aes.o \ aes_ctr.o \ + ass.o \ + ass_split.o \ audio_fifo.o \ avstring.o \ avsscanf.o \ diff --git a/libavcodec/ass.c b/libavutil/ass.c similarity index 65% rename from libavcodec/ass.c rename to libavutil/ass.c index 725e4d42ba..9eeaa38ba9 100644 --- a/libavcodec/ass.c +++ b/libavutil/ass.c @@ -19,21 +19,22 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "avcodec.h" -#include "ass.h" +#include "ass_internal.h" + +#include "subfmt.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/common.h" -int ff_ass_subtitle_header_full(AVCodecContext *avctx, - int play_res_x, int play_res_y, - const char *font, int font_size, - int primary_color, int secondary_color, - int outline_color, int back_color, - int bold, int italic, int underline, - int border_style, int alignment) +char* avpriv_ass_get_subtitle_header_full(int play_res_x, int play_res_y, + const char *font, int font_size, + int primary_color, int secondary_color, + int outline_color, int back_color, + int bold, int italic, int underline, + int border_style, int alignment, + int print_av_version) { - avctx->subtitle_header = av_asprintf( + char* header = av_asprintf( "[Script Info]\r\n" "; Script generated by FFmpeg/Lavc%s\r\n" "ScriptType: v4.00+\r\n" @@ -68,34 +69,31 @@ int ff_ass_subtitle_header_full(AVCodecContext *avctx, "\r\n" "[Events]\r\n" "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n", - !(avctx->flags & AV_CODEC_FLAG_BITEXACT) ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "", + print_av_version ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "", play_res_x, play_res_y, font, font_size, primary_color, secondary_color, outline_color, back_color, -bold, -italic, -underline, border_style, alignment); - if (!avctx->subtitle_header) - return AVERROR(ENOMEM); - avctx->subtitle_header_size = strlen(avctx->subtitle_header); - return 0; + return header; } -int ff_ass_subtitle_header(AVCodecContext *avctx, - const char *font, int font_size, +char* avpriv_ass_get_subtitle_header(const char *font, int font_size, int color, int back_color, int bold, int italic, int underline, - int border_style, int alignment) + int border_style, int alignment, + int print_av_version) { - return ff_ass_subtitle_header_full(avctx, - ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, - font, font_size, color, color, - back_color, back_color, - bold, italic, underline, - border_style, alignment); + return avpriv_ass_get_subtitle_header_full(ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, + font, font_size, color, color, + back_color, back_color, + bold, italic, underline, + border_style, alignment, + print_av_version); } -int ff_ass_subtitle_header_default(AVCodecContext *avctx) +char* avpriv_ass_get_subtitle_header_default(int print_av_version) { - return ff_ass_subtitle_header(avctx, ASS_DEFAULT_FONT, + return avpriv_ass_get_subtitle_header(ASS_DEFAULT_FONT, ASS_DEFAULT_FONT_SIZE, ASS_DEFAULT_COLOR, ASS_DEFAULT_BACK_COLOR, @@ -103,10 +101,11 @@ int ff_ass_subtitle_header_default(AVCodecContext *avctx) ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE, ASS_DEFAULT_BORDERSTYLE, - ASS_DEFAULT_ALIGNMENT); + ASS_DEFAULT_ALIGNMENT, + print_av_version); } -char *ff_ass_get_dialog(int readorder, int layer, const char *style, +char *avpriv_ass_get_dialog(int readorder, int layer, const char *style, const char *speaker, const char *text) { return av_asprintf("%d,%d,%s,%s,0,0,0,,%s", @@ -114,37 +113,17 @@ char *ff_ass_get_dialog(int readorder, int layer, const char *style, speaker ? speaker : "", text); } -int ff_ass_add_rect(AVSubtitle *sub, const char *dialog, - int readorder, int layer, const char *style, - const char *speaker) +char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style, + const char *speaker, int margin_l, int margin_r, + int margin_v, const char *text) { - AVSubtitleRect **rects, *rect; - char *ass_str; - - rects = av_realloc_array(sub->rects, sub->num_rects+1, sizeof(*sub->rects)); - if (!rects) - return AVERROR(ENOMEM); - sub->rects = rects; - rect = av_mallocz(sizeof(*rect)); - if (!rect) - return AVERROR(ENOMEM); - rects[sub->num_rects++] = rect; - rect->type = SUBTITLE_ASS; - ass_str = ff_ass_get_dialog(readorder, layer, style, speaker, dialog); - if (!ass_str) - return AVERROR(ENOMEM); - rect->ass = ass_str; - return 0; -} - -void ff_ass_decoder_flush(AVCodecContext *avctx) -{ - FFASSDecoderContext *s = avctx->priv_data; - if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP)) - s->readorder = 0; + return av_asprintf("%d,%d,%s,%s,%d,%d,%d,,%s", + readorder, layer, style ? style : "Default", + speaker ? speaker : "", margin_l, margin_r, + margin_v, text); } -void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size, +void avpriv_ass_bprint_text_event(AVBPrint *buf, const char *p, int size, const char *linebreaks, int keep_ass_markup) { const char *p_end = p + size; diff --git a/libavutil/ass_internal.h b/libavutil/ass_internal.h new file mode 100644 index 0000000000..cde5561cd3 --- /dev/null +++ b/libavutil/ass_internal.h @@ -0,0 +1,135 @@ +/* + * SSA/ASS common functions + * Copyright (c) 2010 Aurelien Jacobs + * + * 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 + */ + +#ifndef AVUTIL_ASS_INTERNAL_H +#define AVUTIL_ASS_INTERNAL_H + +#include "subfmt.h" +#include "libavutil/bprint.h" + +#define ASS_DEFAULT_PLAYRESX 384 +#define ASS_DEFAULT_PLAYRESY 288 + +/** + * @name Default values for ASS style + * @{ + */ +#define ASS_DEFAULT_FONT "Arial" +#define ASS_DEFAULT_FONT_SIZE 16 +#define ASS_DEFAULT_COLOR 0xffffff +#define ASS_DEFAULT_BACK_COLOR 0 +#define ASS_DEFAULT_BOLD 0 +#define ASS_DEFAULT_ITALIC 0 +#define ASS_DEFAULT_UNDERLINE 0 +#define ASS_DEFAULT_ALIGNMENT 2 +#define ASS_DEFAULT_BORDERSTYLE 1 +/** @} */ + +/** + * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS. + * Can specify all fields explicitly + * + * @param play_res_x subtitle frame width + * @param play_res_y subtitle frame height + * @param font name of the default font face to use + * @param font_size default font size to use + * @param primary_color default text color to use (ABGR) + * @param secondary_color default secondary text color to use (ABGR) + * @param outline_color default outline color to use (ABGR) + * @param back_color default background color to use (ABGR) + * @param bold 1 for bold text, 0 for normal text + * @param italic 1 for italic text, 0 for normal text + * @param underline 1 for underline text, 0 for normal text + * @param border_style 1 for outline, 3 for opaque box + * @param alignment position of the text (left, center, top...), defined after + * the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top) + * @param print_av_version include library version in header + * @return a string containing the subtitle header that needs + * to be released via av_free() + */ +char* avpriv_ass_get_subtitle_header_full(int play_res_x, int play_res_y, + const char *font, int font_size, + int primary_color, int secondary_color, + int outline_color, int back_color, + int bold, int italic, int underline, + int border_style, int alignment, + int print_av_version); + +/** + * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS. + * + * @param font name of the default font face to use + * @param font_size default font size to use + * @param color default text color to use (ABGR) + * @param back_color default background color to use (ABGR) + * @param bold 1 for bold text, 0 for normal text + * @param italic 1 for italic text, 0 for normal text + * @param underline 1 for underline text, 0 for normal text + * @param border_style 1 for outline, 3 for opaque box + * @param alignment position of the text (left, center, top...), defined after + * the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top) + * @param print_av_version include library version in header + * @return a string containing the subtitle header that needs + * to be released via av_free() + */ +char* avpriv_ass_get_subtitle_header(const char *font, int font_size, + int color, int back_color, + int bold, int italic, int underline, + int border_style, int alignment, + int print_av_version); + +/** + * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS + * with default style. + * + * @param print_av_version include library version in header + * @return a string containing the subtitle header that needs + * to be released via av_free() + */ +char* avpriv_ass_get_subtitle_header_default(int print_av_version); + +/** + * Craft an ASS dialog string. + */ +char *avpriv_ass_get_dialog(int readorder, int layer, const char *style, + const char *speaker, const char *text); + +/** + * Craft an ASS dialog string. + */ +char *avpriv_ass_get_dialog_ex(int readorder, int layer, const char *style, + const char *speaker, int margin_l, int margin_r, + int margin_v, const char *text); + +/** + * Escape a text subtitle using ASS syntax into an AVBPrint buffer. + * Newline characters will be escaped to \N. + * + * @param buf pointer to an initialized AVBPrint buffer + * @param p source text + * @param size size of the source text + * @param linebreaks additional newline chars, which will be escaped to \N + * @param keep_ass_markup braces and backslash will not be escaped if set + */ +void avpriv_ass_bprint_text_event(AVBPrint *buf, const char *p, int size, + const char *linebreaks, int keep_ass_markup); + +#endif /* AVUTIL_ASS_INTERNAL_H */ diff --git a/libavcodec/ass_split.c b/libavutil/ass_split.c similarity index 94% rename from libavcodec/ass_split.c rename to libavutil/ass_split.c index 05c5453e53..c5963351fc 100644 --- a/libavcodec/ass_split.c +++ b/libavutil/ass_split.c @@ -22,7 +22,7 @@ #include "libavutil/common.h" #include "libavutil/error.h" #include "libavutil/mem.h" -#include "ass_split.h" +#include "ass_split_internal.h" typedef enum { ASS_STR, @@ -373,7 +373,7 @@ static int ass_split(ASSSplitContext *ctx, const char *buf) return buf ? 0 : AVERROR_INVALIDDATA; } -ASSSplitContext *ff_ass_split(const char *buf) +ASSSplitContext *avpriv_ass_split(const char *buf) { ASSSplitContext *ctx = av_mallocz(sizeof(*ctx)); if (!ctx) @@ -382,7 +382,7 @@ ASSSplitContext *ff_ass_split(const char *buf) buf += 3; ctx->current_section = -1; if (ass_split(ctx, buf) < 0) { - ff_ass_split_free(ctx); + avpriv_ass_split_free(ctx); return NULL; } return ctx; @@ -412,7 +412,7 @@ static void free_section(ASSSplitContext *ctx, const ASSSection *section) av_freep((uint8_t *)&ctx->ass + section->offset); } -void ff_ass_free_dialog(ASSDialog **dialogp) +void avpriv_ass_free_dialog(ASSDialog **dialogp) { ASSDialog *dialog = *dialogp; if (!dialog) @@ -424,7 +424,7 @@ void ff_ass_free_dialog(ASSDialog **dialogp) av_freep(dialogp); } -ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf) +ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf) { int i; static const ASSFields fields[] = { @@ -451,7 +451,7 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf) buf = skip_space(buf); len = last ? strlen(buf) : strcspn(buf, ","); if (len >= INT_MAX) { - ff_ass_free_dialog(&dialog); + avpriv_ass_free_dialog(&dialog); return NULL; } convert_func[type](ptr, buf, len); @@ -461,7 +461,7 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf) return dialog; } -void ff_ass_split_free(ASSSplitContext *ctx) +void avpriv_ass_split_free(ASSSplitContext *ctx) { if (ctx) { int i; @@ -474,7 +474,7 @@ void ff_ass_split_free(ASSSplitContext *ctx) } -int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, +int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, const char *buf) { const char *text = NULL; @@ -497,8 +497,8 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, while (*buf == '\\') { char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0}; unsigned int color = 0xFFFFFFFF; - int len, size = -1, an = -1, alpha = -1; - int x1, y1, x2, y2, t1 = -1, t2 = -1; + int len, size = -1, an = -1, alpha = -1, scale = 0; + int x1, y1, x2, y2, t1 = -1, t2 = -1, accel = 1; if (sscanf(buf, "\\%1[bisu]%1[01\\}]%n", style, c, &len) > 1) { int close = c[0] == '0' ? 1 : c[0] == '1' ? 0 : -1; len += close != -1; @@ -546,6 +546,14 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, } else if (sscanf(buf, "\\org(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) { if (callbacks->origin) callbacks->origin(priv, x1, y1); + } else if (sscanf(buf, "\\t(%d,%d,%1[\\}]%n", &t1, &t2, sep, &len) > 2 || + sscanf(buf, "\\t(%d,%d,%d,%1[\\}]%n", &t1, &t2, &accel, sep, &len) > 3) { + if (callbacks->animate) + callbacks->animate(priv, t1, t2, accel, tmp); + } else if (sscanf(buf, "\\p%1[\\}]%n", sep, &len) > 0 || + sscanf(buf, "\\p%u%1[\\}]%n", &scale, sep, &len) > 1) { + if (callbacks->drawing_mode) + callbacks->drawing_mode(priv, scale); } else { len = strcspn(buf+1, "\\}") + 2; /* skip unknown code */ } @@ -569,7 +577,7 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, return 0; } -ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style) +ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style) { ASS *ass = &ctx->ass; int i; diff --git a/libavcodec/ass_split.h b/libavutil/ass_split_internal.h similarity index 86% rename from libavcodec/ass_split.h rename to libavutil/ass_split_internal.h index a45fb9b8a1..eee49ef0f5 100644 --- a/libavcodec/ass_split.h +++ b/libavutil/ass_split_internal.h @@ -19,8 +19,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef AVCODEC_ASS_SPLIT_H -#define AVCODEC_ASS_SPLIT_H +#ifndef AVUTIL_ASS_SPLIT_INTERNAL_H +#define AVUTIL_ASS_SPLIT_INTERNAL_H /** * fields extracted from the [Script Info] section @@ -81,7 +81,7 @@ typedef struct { char *effect; char *text; /**< actual text which will be displayed as a subtitle, can include style override control codes (see - ff_ass_split_override_codes()) */ + avpriv_ass_split_override_codes()) */ } ASSDialog; /** @@ -107,12 +107,12 @@ typedef struct ASSSplitContext ASSSplitContext; * @param buf String containing the ASS formatted data. * @return Newly allocated struct containing split data. */ -ASSSplitContext *ff_ass_split(const char *buf); +ASSSplitContext *avpriv_ass_split(const char *buf); /** - * Free a dialogue obtained from ff_ass_split_dialog(). + * Free a dialogue obtained from avpriv_ass_split_dialog(). */ -void ff_ass_free_dialog(ASSDialog **dialogp); +void avpriv_ass_free_dialog(ASSDialog **dialogp); /** * Split one ASS Dialogue line from a string buffer. @@ -121,14 +121,14 @@ void ff_ass_free_dialog(ASSDialog **dialogp); * @param buf String containing the ASS "Dialogue" line. * @return Pointer to the split ASSDialog. Must be freed with ff_ass_free_dialog() */ -ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf); +ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf); /** * Free all the memory allocated for an ASSSplitContext. * * @param ctx Context previously initialized by ff_ass_split(). */ -void ff_ass_split_free(ASSSplitContext *ctx); +void avpriv_ass_split_free(ASSSplitContext *ctx); /** @@ -141,6 +141,7 @@ typedef struct { * @{ */ void (*text)(void *priv, const char *text, int len); + void (*hard_space)(void *priv); void (*new_line)(void *priv, int forced); void (*style)(void *priv, char style, int close); void (*color)(void *priv, unsigned int /* color */, unsigned int color_id); @@ -156,7 +157,16 @@ typedef struct { * @{ */ void (*move)(void *priv, int x1, int y1, int x2, int y2, int t1, int t2); + void (*animate)(void *priv, int t1, int t2, int accel, char *style); void (*origin)(void *priv, int x, int y); + void (*drawing_mode)(void *priv, int scale); + /** @} */ + + /** + * @defgroup ass_ext ASS extensible parsing callback + * @{ + */ + void (*ext)(void *priv, int ext_id, const char *text, int p1, int p2); /** @} */ /** @@ -176,7 +186,7 @@ typedef struct { * @param buf The ASS "Dialogue" Text field to split. * @return >= 0 on success otherwise an error code <0 */ -int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, +int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, const char *buf); /** @@ -186,6 +196,6 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, * @param style name of the style to search for. * @return the ASSStyle corresponding to style, or NULL if style can't be found */ -ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style); +ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style); -#endif /* AVCODEC_ASS_SPLIT_H */ +#endif /* AVUTIL_ASS_SPLIT_INTERNAL_H */ From patchwork Fri Jan 14 01:13:15 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33572 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp985869iog; Thu, 13 Jan 2022 17:15:31 -0800 (PST) X-Google-Smtp-Source: ABdhPJzQ56J3n5KXw9AE7l5YHJ1cRb1fMVAY1w7SX1AhBbXHXJclnIKeUH/RK85eEP4Es44E1f4t X-Received: by 2002:a17:906:31c4:: with SMTP id f4mr5491858ejf.6.1642122931391; Thu, 13 Jan 2022 17:15:31 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642122931; cv=none; d=google.com; s=arc-20160816; b=YL40HyXHeBHMtBYLJ9Ab7LGGoP4REmonRSTAAF8Gs9yayyzRl0/moelU15RX1vv9DX qgIy0MrfNdfk5WODsYCEG1gIilaFnqgU8qqqZT/SPHlf9peGT/X6BNR5J4WRAzdhLp2/ nyL20rqQrbWmJG9L0CNzVxrJH9gc5RQ0BacPEEvU45eCmBXzpKWSZsKLezal5MxWGHaJ VnIsRroTH0O2MchgVpXCRTmpwtUPElDywvmTeRus4zfMWri8bu5MCzIwHXHxpAfCB9+a w95421ekhZJ9UXERttIz+EvUsstTyzvAllz6UFalyNUJDcTzjnH8xP8B+Qf+wsMWl1A1 6zxA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=m1rpczFW4c+4scoqBF0wi3Hg45dKRCPhQ2cAoPYK8/8=; b=o0iGIcSIcsSfsZDW+lc0W8pEmjr+RDvc4mg5q3W2fbH7P2jkbSIVxAhQH7/C2J5cAH oi7FhFZ4xP7gDehNmNY2VloI8KIZG9TALz78FIOqjfd/nzEYw2d5S/IWI2B9VX9Imn/x XV9e7MEAqtKQxr3yegmnlbYSA0PcDfAjSWulI1xd5p/V0uyk/r0Gb9bRES0CnXN7Cy6V 5vN6h/okp06N2N+bONQy22R+1NoPPha6yVa7qRbkEKm/dxryj9pKRQHdZOhnhvK7Wjac NqgcgpSpYd3UIAXsBuYD/3hB8wmqdPKkFhjXHJ+DOwLwLa5XkDhEOXpERtEeAgGiJZQd eQEg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=gmXV3+LZ; 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 i14si2443993edj.381.2022.01.13.17.15.31; Thu, 13 Jan 2022 17:15:31 -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 header.s=20210112 header.b=gmXV3+LZ; 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 D574668B638; Fri, 14 Jan 2022 03:13:56 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pg1-f173.google.com (mail-pg1-f173.google.com [209.85.215.173]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 3EF1A68B5A2 for ; Fri, 14 Jan 2022 03:13:44 +0200 (EET) Received: by mail-pg1-f173.google.com with SMTP id i8so1432201pgt.13 for ; Thu, 13 Jan 2022 17:13:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=cpfGRJcv7fLU9ZIX42talxFREb70cuzl7puUbNY8Ops=; b=gmXV3+LZHLsX5Ty6xemg3m78nkQ2DEbFHWlsIHi9LhARYq+c6hCY/cqkFzHhzZH5v8 ENYKhB0Ip/gKHEgvyLOH2s2mio9iXws+GBaLs4+b4ocdUkQzavhYSv7vjsApaWjjmEiZ WoLv6ssAmotvKAkkLhyQtqrjSaUBj/adZfwEITB72G/VPUKYeBaDW5VCT9ot6vjlfbLN IFS9f2srCg2HrOz5t6hPmAdNxvjTLNhZ4h1EFJeJXHFbGk7/e6n6zVsdaJbEo31JKyYg EF+5/8LUNm0QGAniyrW8Vfo39VeG1l56f4XRMXWqQyC30FYQsGiL9vmEuucjo+A3X9KN 2aeQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=cpfGRJcv7fLU9ZIX42talxFREb70cuzl7puUbNY8Ops=; b=mTWVg5C/xKDu16v6Yay5AHkCLvTX07bsY/uRM/uECX0mpLsraAix4td4xLdoHJBiyf dlqHBfxVe+z5Fnlm9y/SCAXrwY8aqg8aHSbs7fh1Wi68yhdi49NDLEDyO/w13KQicRUS zzQ2VipPlalZ/AivFKhacsXl+MXt8PfSgiuma5U259ER62QJ5LR+9oroeE7fYqHhshFj lJ59dvqNhvSgFRJnUKW6Zz+Vex5zuW41yzkWOOcGeANFkktZ4qo79nPWI5lObjZ/WaOa UxfrqYQB125wO4ymd1+2un0LyhF1SYVsSKxBiprwlKfcHUkWHdgW9g+oDZgt6QMRfkCl GayQ== X-Gm-Message-State: AOAM531bXaW48ohJvep0b2P5VwW+Y8LW+CGq1BMvsVg16eUPUG1M/vWm gxcj3CzAwHMowpQnYupcVziQ9588U5QQzg== X-Received: by 2002:aa7:8c41:0:b0:4bc:2888:6951 with SMTP id e1-20020aa78c41000000b004bc28886951mr6740222pfd.16.1642122822068; Thu, 13 Jan 2022 17:13:42 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id f20sm4181557pfc.108.2022.01.13.17.13.41 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:41 -0800 (PST) Message-Id: <808f5894884f0ef73365674ce7a697e5ac46421a.1642122814.git.ffmpegagent@gmail.com> In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:15 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 06/24] avcodec/subtitles: Migrate subtitle encoders to frame-based API 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: f1sVKxnZT5cp From: softworkz and provide a compatibility shim for the legacy api Signed-off-by: softworkz --- libavcodec/assenc.c | 189 ++++++++++++++++++++++++++++++------- libavcodec/avcodec.h | 5 +- libavcodec/dvbsubenc.c | 96 ++++++++++--------- libavcodec/dvdsubenc.c | 102 ++++++++++++-------- libavcodec/encode.c | 57 ++++++++++- libavcodec/movtextenc.c | 114 ++++++++++++++++------ libavcodec/srtenc.c | 108 ++++++++++++++------- libavcodec/tests/avcodec.c | 2 - libavcodec/ttmlenc.c | 101 +++++++++++++++----- libavcodec/webvttenc.c | 86 ++++++++++++----- libavcodec/xsubenc.c | 88 ++++++++++------- 11 files changed, 685 insertions(+), 263 deletions(-) diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c index b0e475834b..e1401b1ac5 100644 --- a/libavcodec/assenc.c +++ b/libavcodec/assenc.c @@ -22,70 +22,195 @@ #include #include "avcodec.h" +#include "encode.h" #include "libavutil/ass_internal.h" #include "internal.h" #include "libavutil/avstring.h" #include "libavutil/internal.h" #include "libavutil/mem.h" +typedef struct { + AVCodecContext *avctx; + AVFrame* current_frame; + int have_frame; + int current_area; +} AssEncContext; + +static void check_write_header(AVCodecContext* avctx, const AVFrame* frame) +{ + if (avctx->extradata_size) + return; + + if (frame->subtitle_header && frame->subtitle_header->size > 0) { + const char* subtitle_header = (char*)frame->subtitle_header->data; + avctx->extradata_size = strlen(subtitle_header); + avctx->extradata = av_mallocz(frame->subtitle_header->size + 1); + memcpy(avctx->extradata, subtitle_header, avctx->extradata_size); + avctx->extradata[avctx->extradata_size] = 0; + } + + if (!avctx->extradata_size) { + const char* subtitle_header = avpriv_ass_get_subtitle_header_default(0); + if (!subtitle_header) + return; + + avctx->extradata_size = strlen(subtitle_header); + avctx->extradata = av_mallocz(avctx->extradata_size + 1); + memcpy(avctx->extradata, subtitle_header, avctx->extradata_size); + avctx->extradata[avctx->extradata_size] = 0; + av_freep(&subtitle_header); + } +} + static av_cold int ass_encode_init(AVCodecContext *avctx) { - avctx->extradata = av_malloc(avctx->subtitle_header_size + 1); - if (!avctx->extradata) - return AVERROR(ENOMEM); - memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size); - avctx->extradata_size = avctx->subtitle_header_size; - avctx->extradata[avctx->extradata_size] = 0; + AssEncContext *s = avctx->priv_data; + + if (avctx->subtitle_header_size) { + avctx->extradata = av_malloc(avctx->subtitle_header_size + 1); + if (!avctx->extradata) + return AVERROR(ENOMEM); + memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size); + avctx->extradata_size = avctx->subtitle_header_size; + avctx->extradata[avctx->extradata_size] = 0; + } + + s->current_frame = av_frame_alloc(); + return 0; +} + +static av_cold int ass_encode_close(AVCodecContext *avctx) +{ + AssEncContext *s = avctx->priv_data; + av_frame_free(&s->current_frame); return 0; } -static int ass_encode_frame(AVCodecContext *avctx, - unsigned char *buf, int bufsize, - const AVSubtitle *sub) +////static int ass_encode_frame(AVCodecContext* avctx, AVPacket* avpkt, +//// const AVFrame* frame, int* got_packet) +////{ +//// int ret; +//// size_t req_len = 0, total_len = 0; +//// +//// check_write_header(avctx, frame); +//// +//// for (unsigned i = 0; i < frame->num_subtitle_areas; i++) { +//// const char *ass = frame->subtitle_areas[i]->ass; +//// +//// if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) { +//// av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n"); +//// return AVERROR(EINVAL); +//// } +//// +//// if (ass) +//// req_len += strlen(ass); +//// } +//// +//// ret = ff_get_encode_buffer(avctx, avpkt, req_len + 1, 0); +//// if (ret < 0) { +//// av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); +//// return ret; +//// } +//// +//// for (unsigned i = 0; i < frame->num_subtitle_areas; i++) { +//// const char *ass = frame->subtitle_areas[i]->ass; +//// +//// if (ass) { +//// size_t len = av_strlcpy((char *)avpkt->data + total_len, ass, avpkt->size - total_len); +//// total_len += len; +//// } +//// } +//// +//// avpkt->size = total_len; +//// *got_packet = total_len > 0; +//// +//// return 0; +////} + +static int ass_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) { - int i, len, total_len = 0; + AssEncContext *s = avctx->priv_data; + int ret; + + if (!s->have_frame) { + s->current_area = 0; + ret = ff_encode_get_frame(avctx, s->current_frame); + + if (ret < 0) { + av_frame_unref(s->current_frame); + return ret; + } + + s->have_frame = 1; + } - for (i=0; inum_rects; i++) { - const char *ass = sub->rects[i]->ass; + check_write_header(avctx, s->current_frame); - if (sub->rects[i]->type != SUBTITLE_ASS) { - av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); + if (s->current_frame->repeat_sub) { + av_frame_unref(s->current_frame); + s->have_frame = 0; + return AVERROR(EAGAIN); + } + + if (s->current_area < s->current_frame->num_subtitle_areas) { + const AVSubtitleArea *area = s->current_frame->subtitle_areas[s->current_area]; + const char *ass = area->ass; + + if (area->type != AV_SUBTITLE_FMT_ASS) { + av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n"); return AVERROR(EINVAL); } - len = av_strlcpy(buf+total_len, ass, bufsize-total_len); + if (ass) { + size_t len = strlen(ass); + + ret = ff_get_encode_buffer(avctx, avpkt, len + 1, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); + return ret; + } + + len = av_strlcpy((char *)avpkt->data, ass, avpkt->size); - if (len > bufsize-total_len-1) { - av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); - return AVERROR_BUFFER_TOO_SMALL; + avpkt->size = len; } - total_len += len; + s->current_area++; } - return total_len; + if (s->current_area < s->current_frame->num_subtitle_areas) + return 0; + + av_frame_unref(s->current_frame); + s->have_frame = 0; + + return 0; } #if CONFIG_SSA_ENCODER const AVCodec ff_ssa_encoder = { - .name = "ssa", - .long_name = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"), - .type = AVMEDIA_TYPE_SUBTITLE, - .id = AV_CODEC_ID_ASS, - .init = ass_encode_init, - .encode_sub = ass_encode_frame, + .name = "ssa", + .long_name = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"), + .type = AVMEDIA_TYPE_SUBTITLE, + .id = AV_CODEC_ID_ASS, + .priv_data_size = sizeof(AssEncContext), + .init = ass_encode_init, + .close = ass_encode_close, + .receive_packet = ass_receive_packet, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; #endif #if CONFIG_ASS_ENCODER const AVCodec ff_ass_encoder = { - .name = "ass", - .long_name = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"), - .type = AVMEDIA_TYPE_SUBTITLE, - .id = AV_CODEC_ID_ASS, - .init = ass_encode_init, - .encode_sub = ass_encode_frame, + .name = "ass", + .long_name = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"), + .type = AVMEDIA_TYPE_SUBTITLE, + .priv_data_size = sizeof(AssEncContext), + .id = AV_CODEC_ID_ASS, + .init = ass_encode_init, + .close = ass_encode_close, + .receive_packet = ass_receive_packet, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; #endif diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 9d59f6e840..93063dc6e9 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -2995,10 +2995,13 @@ void av_parser_close(AVCodecParserContext *s); * @{ */ + /** + * @deprecated Use @ref avcodec_encode_subtitle2() instead. + */ +attribute_deprecated int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, const AVSubtitle *sub); - /** * @} */ diff --git a/libavcodec/dvbsubenc.c b/libavcodec/dvbsubenc.c index 322fc27cb4..3b5a76daa7 100644 --- a/libavcodec/dvbsubenc.c +++ b/libavcodec/dvbsubenc.c @@ -20,6 +20,7 @@ */ #include "avcodec.h" #include "bytestream.h" +#include "encode.h" #include "libavutil/colorspace.h" typedef struct DVBSubtitleContext { @@ -268,21 +269,29 @@ static int dvb_encode_rle8(uint8_t **pq, int buf_size, return len; } -static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, - const AVSubtitle *h) +static int dvbsub_encode(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet) { DVBSubtitleContext *s = avctx->priv_data; uint8_t *q, *pseg_len; int page_id, region_id, clut_id, object_id, i, bpp_index, page_state; - - - q = outbuf; + size_t buf_size; + int ret; page_id = 1; - if (h->num_rects && !h->rects) + if (frame->num_subtitle_areas && !frame->subtitle_areas) return AVERROR(EINVAL); + ret = ff_get_encode_buffer(avctx, avpkt, 1024 * 1024, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); + return ret; + } + + buf_size = avpkt->size; + q = avpkt->data; + if (avctx->width > 0 && avctx->height > 0) { if (buf_size < 11) return AVERROR_BUFFER_TOO_SMALL; @@ -301,7 +310,7 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, /* page composition segment */ - if (buf_size < 8 + h->num_rects * 6) + if (buf_size < 8 + frame->num_subtitle_areas * 6) return AVERROR_BUFFER_TOO_SMALL; *q++ = 0x0f; /* sync_byte */ *q++ = 0x10; /* segment_type */ @@ -313,30 +322,30 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, /* page_version = 0 + page_state */ *q++ = (s->object_version << 4) | (page_state << 2) | 3; - for (region_id = 0; region_id < h->num_rects; region_id++) { + for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) { *q++ = region_id; *q++ = 0xff; /* reserved */ - bytestream_put_be16(&q, h->rects[region_id]->x); /* left pos */ - bytestream_put_be16(&q, h->rects[region_id]->y); /* top pos */ + bytestream_put_be16(&q, frame->subtitle_areas[region_id]->x); /* left pos */ + bytestream_put_be16(&q, frame->subtitle_areas[region_id]->y); /* top pos */ } bytestream_put_be16(&pseg_len, q - pseg_len - 2); - buf_size -= 8 + h->num_rects * 6; + buf_size -= 8 + frame->num_subtitle_areas * 6; - if (h->num_rects) { - for (clut_id = 0; clut_id < h->num_rects; clut_id++) { - if (buf_size < 6 + h->rects[clut_id]->nb_colors * 6) + if (frame->num_subtitle_areas) { + for (clut_id = 0; clut_id < frame->num_subtitle_areas; clut_id++) { + if (buf_size < 6 + frame->subtitle_areas[clut_id]->nb_colors * 6) return AVERROR_BUFFER_TOO_SMALL; /* CLUT segment */ - if (h->rects[clut_id]->nb_colors <= 4) { + if (frame->subtitle_areas[clut_id]->nb_colors <= 4) { /* 2 bpp, some decoders do not support it correctly */ bpp_index = 0; - } else if (h->rects[clut_id]->nb_colors <= 16) { + } else if (frame->subtitle_areas[clut_id]->nb_colors <= 16) { /* 4 bpp, standard encoding */ bpp_index = 1; - } else if (h->rects[clut_id]->nb_colors <= 256) { + } else if (frame->subtitle_areas[clut_id]->nb_colors <= 256) { /* 8 bpp, standard encoding */ bpp_index = 2; } else { @@ -353,12 +362,12 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, *q++ = clut_id; *q++ = (0 << 4) | 0xf; /* version = 0 */ - for(i = 0; i < h->rects[clut_id]->nb_colors; i++) { + for(i = 0; i < frame->subtitle_areas[clut_id]->nb_colors; i++) { *q++ = i; /* clut_entry_id */ *q++ = (1 << (7 - bpp_index)) | (0xf << 1) | 1; /* 2 bits/pixel full range */ { int a, r, g, b; - uint32_t x= ((uint32_t*)h->rects[clut_id]->data[1])[i]; + uint32_t x= ((uint32_t*)frame->subtitle_areas[clut_id]->pal)[i]; a = (x >> 24) & 0xff; r = (x >> 16) & 0xff; g = (x >> 8) & 0xff; @@ -372,22 +381,22 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, } bytestream_put_be16(&pseg_len, q - pseg_len - 2); - buf_size -= 6 + h->rects[clut_id]->nb_colors * 6; + buf_size -= 6 + frame->subtitle_areas[clut_id]->nb_colors * 6; } - if (buf_size < h->num_rects * 22) + if (buf_size < frame->num_subtitle_areas * 22) return AVERROR_BUFFER_TOO_SMALL; - for (region_id = 0; region_id < h->num_rects; region_id++) { + for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) { /* region composition segment */ - if (h->rects[region_id]->nb_colors <= 4) { + if (frame->subtitle_areas[region_id]->nb_colors <= 4) { /* 2 bpp, some decoders do not support it correctly */ bpp_index = 0; - } else if (h->rects[region_id]->nb_colors <= 16) { + } else if (frame->subtitle_areas[region_id]->nb_colors <= 16) { /* 4 bpp, standard encoding */ bpp_index = 1; - } else if (h->rects[region_id]->nb_colors <= 256) { + } else if (frame->subtitle_areas[region_id]->nb_colors <= 256) { /* 8 bpp, standard encoding */ bpp_index = 2; } else { @@ -401,8 +410,8 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, q += 2; /* segment length */ *q++ = region_id; *q++ = (s->object_version << 4) | (0 << 3) | 0x07; /* version , no fill */ - bytestream_put_be16(&q, h->rects[region_id]->w); /* region width */ - bytestream_put_be16(&q, h->rects[region_id]->h); /* region height */ + bytestream_put_be16(&q, frame->subtitle_areas[region_id]->w); /* region width */ + bytestream_put_be16(&q, frame->subtitle_areas[region_id]->h); /* region height */ *q++ = ((1 + bpp_index) << 5) | ((1 + bpp_index) << 2) | 0x03; *q++ = region_id; /* clut_id == region_id */ *q++ = 0; /* 8 bit fill colors */ @@ -416,9 +425,9 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, bytestream_put_be16(&pseg_len, q - pseg_len - 2); } - buf_size -= h->num_rects * 22; + buf_size -= frame->num_subtitle_areas * 22; - for (object_id = 0; object_id < h->num_rects; object_id++) { + for (object_id = 0; object_id < frame->num_subtitle_areas; object_id++) { int (*dvb_encode_rle)(uint8_t **pq, int buf_size, const uint8_t *bitmap, int linesize, int w, int h); @@ -427,13 +436,13 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, return AVERROR_BUFFER_TOO_SMALL; /* bpp_index maths */ - if (h->rects[object_id]->nb_colors <= 4) { + if (frame->subtitle_areas[object_id]->nb_colors <= 4) { /* 2 bpp, some decoders do not support it correctly */ dvb_encode_rle = dvb_encode_rle2; - } else if (h->rects[object_id]->nb_colors <= 16) { + } else if (frame->subtitle_areas[object_id]->nb_colors <= 16) { /* 4 bpp, standard encoding */ dvb_encode_rle = dvb_encode_rle4; - } else if (h->rects[object_id]->nb_colors <= 256) { + } else if (frame->subtitle_areas[object_id]->nb_colors <= 256) { /* 8 bpp, standard encoding */ dvb_encode_rle = dvb_encode_rle8; } else { @@ -463,19 +472,19 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, top_ptr = q; ret = dvb_encode_rle(&q, buf_size, - h->rects[object_id]->data[0], - h->rects[object_id]->w * 2, - h->rects[object_id]->w, - h->rects[object_id]->h >> 1); + frame->subtitle_areas[object_id]->buf[0]->data, + frame->subtitle_areas[object_id]->w * 2, + frame->subtitle_areas[object_id]->w, + frame->subtitle_areas[object_id]->h >> 1); if (ret < 0) return ret; buf_size -= ret; bottom_ptr = q; ret = dvb_encode_rle(&q, buf_size, - h->rects[object_id]->data[0] + h->rects[object_id]->w, - h->rects[object_id]->w * 2, - h->rects[object_id]->w, - h->rects[object_id]->h >> 1); + frame->subtitle_areas[object_id]->buf[0]->data + frame->subtitle_areas[object_id]->w, + frame->subtitle_areas[object_id]->w * 2, + frame->subtitle_areas[object_id]->w, + frame->subtitle_areas[object_id]->h >> 1); if (ret < 0) return ret; buf_size -= ret; @@ -502,7 +511,10 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, buf_size -= 6; s->object_version = (s->object_version + 1) & 0xf; - return q - outbuf; + avpkt->size = q - avpkt->data; + *got_packet = 1; + + return 0; } const AVCodec ff_dvbsub_encoder = { @@ -511,5 +523,5 @@ const AVCodec ff_dvbsub_encoder = { .type = AVMEDIA_TYPE_SUBTITLE, .id = AV_CODEC_ID_DVB_SUBTITLE, .priv_data_size = sizeof(DVBSubtitleContext), - .encode_sub = dvbsub_encode, + .encode2 = dvbsub_encode, }; diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c index ff4fbed39d..84f2a544f0 100644 --- a/libavcodec/dvdsubenc.c +++ b/libavcodec/dvdsubenc.c @@ -20,6 +20,7 @@ */ #include "avcodec.h" #include "bytestream.h" +#include "encode.h" #include "internal.h" #include "libavutil/avassert.h" #include "libavutil/bprint.h" @@ -114,15 +115,14 @@ static int color_distance(uint32_t a, uint32_t b) * Count colors used in a rectangle, quantizing alpha and grouping by * nearest global palette entry. */ -static void count_colors(AVCodecContext *avctx, unsigned hits[33], - const AVSubtitleRect *r) +static void count_colors(const AVCodecContext *avctx, unsigned hits[33], + const AVSubtitleArea *r) { DVDSubtitleContext *dvdc = avctx->priv_data; unsigned count[256] = { 0 }; - uint32_t *palette = (uint32_t *)r->data[1]; uint32_t color; int x, y, i, j, match, d, best_d, av_uninit(best_j); - uint8_t *p = r->data[0]; + uint8_t *p = r->buf[0]->data; for (y = 0; y < r->h; y++) { for (x = 0; x < r->w; x++) @@ -132,7 +132,7 @@ static void count_colors(AVCodecContext *avctx, unsigned hits[33], for (i = 0; i < 256; i++) { if (!count[i]) /* avoid useless search */ continue; - color = palette[i]; + color = r->pal[i]; /* 0: transparent, 1-16: semi-transparent, 17-33 opaque */ match = color < 0x33000000 ? 0 : color < 0xCC000000 ? 1 : 17; if (match) { @@ -232,13 +232,13 @@ static void build_color_map(AVCodecContext *avctx, int cmap[], } } -static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[]) +static void copy_rectangle(AVSubtitleArea*dst, AVSubtitleArea *src, int cmap[]) { int x, y; uint8_t *p, *q; - p = src->data[0]; - q = dst->data[0] + (src->x - dst->x) + + p = src->buf[0]->data; + q = dst->buf[0]->data + (src->x - dst->x) + (src->y - dst->y) * dst->linesize[0]; for (y = 0; y < src->h; y++) { for (x = 0; x < src->w; x++) @@ -248,51 +248,57 @@ static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[]) } } -static int encode_dvd_subtitles(AVCodecContext *avctx, - uint8_t *outbuf, int outbuf_size, - const AVSubtitle *h) +static int encode_dvd_subtitles(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet) { DVDSubtitleContext *dvdc = avctx->priv_data; uint8_t *q, *qq; int offset1, offset2; - int i, rects = h->num_rects, ret; + int ret = 0; + unsigned i, rects = frame->num_subtitle_areas; unsigned global_palette_hits[33] = { 0 }; int cmap[256]; int out_palette[4]; int out_alpha[4]; - AVSubtitleRect vrect; - uint8_t *vrect_data = NULL; + AVSubtitleArea vrect; + uint8_t* vrect_data = NULL, *outbuf; int x2, y2; int forced = 0; + int outbuf_size; + int64_t duration_ms; - if (rects == 0 || !h->rects) + if (rects == 0) + return 0; + + if (!frame->subtitle_areas) return AVERROR(EINVAL); + for (i = 0; i < rects; i++) - if (h->rects[i]->type != SUBTITLE_BITMAP) { + if (frame->subtitle_areas[i]->type != SUBTITLE_BITMAP) { av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n"); return AVERROR(EINVAL); } /* Mark this subtitle forced if any of the rectangles is forced. */ for (i = 0; i < rects; i++) - if ((h->rects[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) { + if ((frame->subtitle_areas[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) { forced = 1; break; } - vrect = *h->rects[0]; + vrect = *frame->subtitle_areas[0]; if (rects > 1) { /* DVD subtitles can have only one rectangle: build a virtual rectangle containing all actual rectangles. The data of the rectangles will be copied later, when the palette is decided, because the rectangles may have different palettes. */ - int xmin = h->rects[0]->x, xmax = xmin + h->rects[0]->w; - int ymin = h->rects[0]->y, ymax = ymin + h->rects[0]->h; + int xmin = frame->subtitle_areas[0]->x, xmax = xmin + frame->subtitle_areas[0]->w; + int ymin = frame->subtitle_areas[0]->y, ymax = ymin + frame->subtitle_areas[0]->h; for (i = 1; i < rects; i++) { - xmin = FFMIN(xmin, h->rects[i]->x); - ymin = FFMIN(ymin, h->rects[i]->y); - xmax = FFMAX(xmax, h->rects[i]->x + h->rects[i]->w); - ymax = FFMAX(ymax, h->rects[i]->y + h->rects[i]->h); + xmin = FFMIN(xmin, frame->subtitle_areas[i]->x); + ymin = FFMIN(ymin, frame->subtitle_areas[i]->y); + xmax = FFMAX(xmax, frame->subtitle_areas[i]->x + frame->subtitle_areas[i]->w); + ymax = FFMAX(ymax, frame->subtitle_areas[i]->y + frame->subtitle_areas[i]->h); } vrect.x = xmin; vrect.y = ymin; @@ -304,27 +310,29 @@ static int encode_dvd_subtitles(AVCodecContext *avctx, /* Count pixels outside the virtual rectangle as transparent */ global_palette_hits[0] = vrect.w * vrect.h; for (i = 0; i < rects; i++) - global_palette_hits[0] -= h->rects[i]->w * h->rects[i]->h; + global_palette_hits[0] -= frame->subtitle_areas[i]->w * frame->subtitle_areas[i]->h; } for (i = 0; i < rects; i++) - count_colors(avctx, global_palette_hits, h->rects[i]); + count_colors(avctx, global_palette_hits, frame->subtitle_areas[i]); select_palette(avctx, out_palette, out_alpha, global_palette_hits); if (rects > 1) { - if (!(vrect_data = av_calloc(vrect.w, vrect.h))) + + vrect.buf[0] = av_buffer_allocz((size_t)vrect.w * vrect.h); + if (!vrect.buf[0]) return AVERROR(ENOMEM); - vrect.data [0] = vrect_data; + vrect.linesize[0] = vrect.w; for (i = 0; i < rects; i++) { - build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->data[1], + build_color_map(avctx, cmap, frame->subtitle_areas[i]->pal, out_palette, out_alpha); - copy_rectangle(&vrect, h->rects[i], cmap); + copy_rectangle(&vrect, frame->subtitle_areas[i], cmap); } for (i = 0; i < 4; i++) cmap[i] = i; } else { - build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->data[1], + build_color_map(avctx, cmap, frame->subtitle_areas[0]->pal, out_palette, out_alpha); } @@ -335,6 +343,16 @@ static int encode_dvd_subtitles(AVCodecContext *avctx, out_palette[i], out_alpha[i] >> 4); av_log(avctx, AV_LOG_DEBUG, "\n"); + + ret = ff_get_encode_buffer(avctx, avpkt, 4 + vrect.w * vrect.h / 2 + 17 + 21, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); + return ret; + } + + outbuf_size = avpkt->size; + outbuf = avpkt->data; + // encode data block q = outbuf + 4; offset1 = q - outbuf; @@ -344,10 +362,10 @@ static int encode_dvd_subtitles(AVCodecContext *avctx, ret = AVERROR_BUFFER_TOO_SMALL; goto fail; } - dvd_encode_rle(&q, vrect.data[0], vrect.w * 2, + dvd_encode_rle(&q, vrect.buf[0]->data, vrect.w * 2, vrect.w, (vrect.h + 1) >> 1, cmap); offset2 = q - outbuf; - dvd_encode_rle(&q, vrect.data[0] + vrect.w, vrect.w * 2, + dvd_encode_rle(&q, vrect.buf[0]->data + vrect.w, vrect.w * 2, vrect.w, vrect.h >> 1, cmap); if (dvdc->even_rows_fix && (vrect.h & 1)) { @@ -362,7 +380,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx, bytestream_put_be16(&qq, q - outbuf); // send start display command - bytestream_put_be16(&q, (h->start_display_time*90) >> 10); + bytestream_put_be16(&q, 0); bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12 + 2); *q++ = 0x03; // palette - 4 nibbles *q++ = (out_palette[3] << 4) | out_palette[2]; @@ -394,7 +412,8 @@ static int encode_dvd_subtitles(AVCodecContext *avctx, *q++ = 0xff; // terminating command // send stop display command last - bytestream_put_be16(&q, (h->end_display_time*90) >> 10); + duration_ms = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, (AVRational){ 1, 1000 }); + bytestream_put_be16(&q, (duration_ms*90) >> 10); bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/); *q++ = 0x02; // set end *q++ = 0xff; // terminating command @@ -403,7 +422,9 @@ static int encode_dvd_subtitles(AVCodecContext *avctx, bytestream_put_be16(&qq, q - outbuf); av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%"PTRDIFF_SPECIFIER"\n", q - outbuf); - ret = q - outbuf; + avpkt->size = q - outbuf; + ret = 0; + *got_packet = 1; fail: av_free(vrect_data); @@ -467,14 +488,13 @@ static int dvdsub_init(AVCodecContext *avctx) return 0; } -static int dvdsub_encode(AVCodecContext *avctx, - unsigned char *buf, int buf_size, - const AVSubtitle *sub) +static int dvdsub_encode(struct AVCodecContext* avctx, struct AVPacket* avpkt, + const struct AVFrame* frame, int* got_packet) { //DVDSubtitleContext *s = avctx->priv_data; int ret; - ret = encode_dvd_subtitles(avctx, buf, buf_size, sub); + ret = encode_dvd_subtitles(avctx, avpkt, frame, got_packet); return ret; } @@ -499,7 +519,7 @@ const AVCodec ff_dvdsub_encoder = { .type = AVMEDIA_TYPE_SUBTITLE, .id = AV_CODEC_ID_DVD_SUBTITLE, .init = dvdsub_init, - .encode_sub = dvdsub_encode, + .encode2 = dvdsub_encode, .priv_class = &dvdsubenc_class, .priv_data_size = sizeof(DVDSubtitleContext), .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, diff --git a/libavcodec/encode.c b/libavcodec/encode.c index 618be0573d..982ee2de08 100644 --- a/libavcodec/encode.c +++ b/libavcodec/encode.c @@ -140,17 +140,70 @@ fail: return ret; } +/** + * \brief + * \param avctx + * \param buf q + * \param buf_size + * \param sub + * \return + */ int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, const AVSubtitle *sub) { - int ret; + int ret = 0, got_packet = 0; + AVFrame *frame = NULL; + AVPacket* avpkt = NULL; + if (sub->start_display_time) { av_log(avctx, AV_LOG_ERROR, "start_display_time must be 0.\n"); return -1; } - ret = avctx->codec->encode_sub(avctx, buf, buf_size, sub); + memset(buf, 0, buf_size); + // Create a temporary frame for calling the regular api: + frame = av_frame_alloc(); + if (!frame) { + ret = AVERROR(ENOMEM); + goto exit; + } + + frame->format = sub->format; + frame->type = AVMEDIA_TYPE_SUBTITLE; + ret = av_frame_get_buffer2(frame, 0); + if (ret < 0) + goto exit; + + // Create a temporary packet + avpkt = av_packet_alloc(); + if (!avpkt) { + ret = AVERROR(ENOMEM); + goto exit; + } + + // Copy legacy subtitle data to temp frame + ret = ff_frame_put_subtitle(frame, sub); + if (ret < 0) + goto exit; + + ret = avctx->codec->encode2(avctx, avpkt, frame, &got_packet); + avctx->frame_number++; + + if (got_packet) { + if (avpkt->size > buf_size) { + ret = AVERROR_BUFFER_TOO_SMALL; + goto exit; + } + + memcpy(buf, avpkt->data, avpkt->size); + ret = avpkt->size; + } + +exit: + + av_packet_free(&avpkt); + av_frame_free(&frame); return ret; } diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c index d506ed5c37..6383d694a8 100644 --- a/libavcodec/movtextenc.c +++ b/libavcodec/movtextenc.c @@ -29,6 +29,7 @@ #include "libavutil/ass_split_internal.h" #include "libavutil/ass_internal.h" #include "bytestream.h" +#include "encode.h" #include "internal.h" #define STYLE_FLAG_BOLD (1<<0) @@ -73,6 +74,7 @@ typedef struct { AVCodecContext *avctx; ASSSplitContext *ass_ctx; + int is_default_ass_context; ASSStyle *ass_dialog_style; StyleBox *style_attributes; unsigned count; @@ -329,7 +331,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx) av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); - s->ass_ctx = avpriv_ass_split(avctx->subtitle_header); + s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header); if (!s->ass_ctx) return AVERROR_INVALIDDATA; ret = encode_sample_description(avctx); @@ -633,56 +635,112 @@ static const ASSCodesCallbacks mov_text_callbacks = { .end = mov_text_end_cb, }; -static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, - int bufsize, const AVSubtitle *sub) +static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame) +{ + MovTextContext* s = avctx->priv_data; + int ret; + + if (s->ass_ctx && !s->is_default_ass_context) + // We already have a (non-default context) + return; + + if (!frame->num_subtitle_areas) + // Don't need ass context for processing empty subtitle frames + return; + + // The frame has content, so we need to set up a context + if (frame->subtitle_header && frame->subtitle_header->size > 0) { + const char* subtitle_header = (char*)frame->subtitle_header->data; + avpriv_ass_split_free(s->ass_ctx); + s->ass_ctx = avpriv_ass_split(subtitle_header); + s->is_default_ass_context = 0; + } + else if (!s->ass_ctx) { + char* subtitle_header = avpriv_ass_get_subtitle_header_default(0); + if (!subtitle_header) + return; + + s->ass_ctx = avpriv_ass_split(subtitle_header); + s->is_default_ass_context = 1; + av_free(subtitle_header); + } + + if (s->ass_ctx && !avctx->extradata_size) { + ret = encode_sample_description(avctx); + if (ret < 0) + av_log(avctx, AV_LOG_ERROR, "Error during encode_sample_description().\n"); + } +} + +static int mov_text_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, + const AVFrame *frame, int *got_packet) { MovTextContext *s = avctx->priv_data; ASSDialog *dialog; - int i, length; + int i, ret = 0; + size_t j; + uint8_t* buf; + + ensure_ass_context(avctx, frame); s->text_pos = 0; s->count = 0; s->box_flags = 0; av_bprint_clear(&s->buffer); - for (i = 0; i < sub->num_rects; i++) { - const char *ass = sub->rects[i]->ass; + for (i = 0; i < frame->num_subtitle_areas; i++) { + const char *ass = frame->subtitle_areas[i]->ass; - if (sub->rects[i]->type != SUBTITLE_ASS) { - av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); + if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) { + av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n"); return AVERROR(EINVAL); } - dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); - if (!dialog) - return AVERROR(ENOMEM); - mov_text_dialog(s, dialog); - avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text); - avpriv_ass_free_dialog(&dialog); + if (ass) { + + if (i > 0) + mov_text_new_line_cb(s, 0); + + dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); + if (!dialog) + return AVERROR(ENOMEM); + mov_text_dialog(s, dialog); + avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text); + avpriv_ass_free_dialog(&dialog); + + } } - if (s->buffer.len > UINT16_MAX) - return AVERROR(ERANGE); - AV_WB16(buf, s->buffer.len); - buf += 2; + if (!av_bprint_is_complete(&s->buffer)) { + return AVERROR(ENOMEM); + } - for (size_t j = 0; j < box_count; j++) + for (j = 0; j < box_count; j++) { box_types[j].encode(s); + } - if (!av_bprint_is_complete(&s->buffer)) - return AVERROR(ENOMEM); + ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); + return ret; + } - if (!s->buffer.len) - return 0; + buf = avpkt->data; - if (s->buffer.len > bufsize - 3) { + AV_WB16(buf, s->buffer.len); + buf += 2; + + if (s->buffer.len > avpkt->size - 3) { av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); - return AVERROR_BUFFER_TOO_SMALL; + ret = AVERROR_BUFFER_TOO_SMALL; + goto exit; } memcpy(buf, s->buffer.str, s->buffer.len); - length = s->buffer.len + 2; + avpkt->size = s->buffer.len + 2; + *got_packet = 1; - return length; +exit: + return ret; } #define OFFSET(x) offsetof(MovTextContext, x) @@ -707,7 +765,7 @@ const AVCodec ff_movtext_encoder = { .priv_data_size = sizeof(MovTextContext), .priv_class = &mov_text_encoder_class, .init = mov_text_encode_init, - .encode_sub = mov_text_encode_frame, + .encode2 = mov_text_encode_frame, .close = mov_text_encode_close, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c index a7c5fccefe..ebe42ef817 100644 --- a/libavcodec/srtenc.c +++ b/libavcodec/srtenc.c @@ -21,6 +21,7 @@ #include #include "avcodec.h" +#include "encode.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/ass_split_internal.h" @@ -33,6 +34,7 @@ typedef struct { AVCodecContext *avctx; ASSSplitContext *ass_ctx; + int is_default_ass_context; AVBPrint buffer; char stack[SRT_STACK_SIZE]; int stack_ptr; @@ -130,14 +132,13 @@ static void srt_style_apply(SRTContext *s, const char *style) } } - static av_cold int srt_encode_init(AVCodecContext *avctx) { SRTContext *s = avctx->priv_data; s->avctx = avctx; - s->ass_ctx = avpriv_ass_split(avctx->subtitle_header); + s->ass_ctx = avpriv_ass_split((char *)avctx->subtitle_header); av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); - return s->ass_ctx ? 0 : AVERROR_INVALIDDATA; + return 0; } static void srt_text_cb(void *priv, const char *text, int len) @@ -227,58 +228,95 @@ static const ASSCodesCallbacks text_callbacks = { .new_line = srt_new_line_cb, }; -static int encode_frame(AVCodecContext *avctx, - unsigned char *buf, int bufsize, const AVSubtitle *sub, - const ASSCodesCallbacks *cb) +static void ensure_ass_context(SRTContext* s, const AVFrame* frame) +{ + if (s->ass_ctx && !s->is_default_ass_context) + // We already have a (non-default context) + return; + + if (!frame->num_subtitle_areas) + // Don't need ass context for processing empty subtitle frames + return; + + // The frame has content, so we need to set up a context + if (frame->subtitle_header && frame->subtitle_header->size > 0) { + const char* subtitle_header = (char*)frame->subtitle_header->data; + avpriv_ass_split_free(s->ass_ctx); + s->ass_ctx = avpriv_ass_split(subtitle_header); + s->is_default_ass_context = 0; + } + else if (!s->ass_ctx) { + char* subtitle_header = avpriv_ass_get_subtitle_header_default(0); + if (!subtitle_header) + return; + + s->ass_ctx = avpriv_ass_split(subtitle_header); + s->is_default_ass_context = 1; + av_free(subtitle_header); + } +} + +static int encode_frame(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet, const ASSCodesCallbacks* cb) { SRTContext *s = avctx->priv_data; ASSDialog *dialog; - int i; + int i, ret; + + ensure_ass_context(s, frame); av_bprint_clear(&s->buffer); - for (i=0; inum_rects; i++) { - const char *ass = sub->rects[i]->ass; + for (i=0; i< frame->num_subtitle_areas; i++) { + const char *ass = frame->subtitle_areas[i]->ass; - if (sub->rects[i]->type != SUBTITLE_ASS) { - av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); + if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) { + av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n"); return AVERROR(EINVAL); } - dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); - if (!dialog) - return AVERROR(ENOMEM); - s->alignment_applied = 0; - if (avctx->codec_id == AV_CODEC_ID_SUBRIP) - srt_style_apply(s, dialog->style); - avpriv_ass_split_override_codes(cb, s, dialog->text); - avpriv_ass_free_dialog(&dialog); + if (ass) { + + if (i > 0) + srt_new_line_cb(s, 0); + + dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); + if (!dialog) + return AVERROR(ENOMEM); + s->alignment_applied = 0; + if (avctx->codec_id == AV_CODEC_ID_SUBRIP) + srt_style_apply(s, dialog->style); + avpriv_ass_split_override_codes(cb, s, dialog->text); + avpriv_ass_free_dialog(&dialog); + } } if (!av_bprint_is_complete(&s->buffer)) return AVERROR(ENOMEM); - if (!s->buffer.len) - return 0; - if (s->buffer.len > bufsize) { - av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); - return AVERROR_BUFFER_TOO_SMALL; + ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); + return ret; } - memcpy(buf, s->buffer.str, s->buffer.len); - return s->buffer.len; + memcpy(avpkt->data, s->buffer.str, s->buffer.len); + avpkt->size = s->buffer.len; + *got_packet = 1; + + return 0; } -static int srt_encode_frame(AVCodecContext *avctx, - unsigned char *buf, int bufsize, const AVSubtitle *sub) +static int srt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet) { - return encode_frame(avctx, buf, bufsize, sub, &srt_callbacks); + return encode_frame(avctx, avpkt, frame, got_packet, &srt_callbacks); } -static int text_encode_frame(AVCodecContext *avctx, - unsigned char *buf, int bufsize, const AVSubtitle *sub) +static int text_encode_frame(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet) { - return encode_frame(avctx, buf, bufsize, sub, &text_callbacks); + return encode_frame(avctx, avpkt, frame, got_packet, &text_callbacks); } static int srt_encode_close(AVCodecContext *avctx) @@ -298,7 +336,7 @@ const AVCodec ff_srt_encoder = { .id = AV_CODEC_ID_SUBRIP, .priv_data_size = sizeof(SRTContext), .init = srt_encode_init, - .encode_sub = srt_encode_frame, + .encode2 = srt_encode_frame, .close = srt_encode_close, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; @@ -312,7 +350,7 @@ const AVCodec ff_subrip_encoder = { .id = AV_CODEC_ID_SUBRIP, .priv_data_size = sizeof(SRTContext), .init = srt_encode_init, - .encode_sub = srt_encode_frame, + .encode2 = srt_encode_frame, .close = srt_encode_close, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; @@ -326,7 +364,7 @@ const AVCodec ff_text_encoder = { .id = AV_CODEC_ID_TEXT, .priv_data_size = sizeof(SRTContext), .init = srt_encode_init, - .encode_sub = text_encode_frame, + .encode2 = text_encode_frame, .close = srt_encode_close, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; diff --git a/libavcodec/tests/avcodec.c b/libavcodec/tests/avcodec.c index 5d0ff9432c..bd979b2184 100644 --- a/libavcodec/tests/avcodec.c +++ b/libavcodec/tests/avcodec.c @@ -107,8 +107,6 @@ int main(void){ continue; } if (is_encoder) { - if (codec->type == AVMEDIA_TYPE_SUBTITLE ^ !!codec->encode_sub) - ERR("Encoder %s is both subtitle encoder and not subtitle encoder."); if (!!codec->encode_sub + !!codec->encode2 + !!codec->receive_packet != 1) ERR("Encoder %s does not implement exactly one encode API.\n"); if (codec->update_thread_context || codec->update_thread_context_for_user || codec->bsfs) diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c index 083f2dd67a..e6d8a01a73 100644 --- a/libavcodec/ttmlenc.c +++ b/libavcodec/ttmlenc.c @@ -33,11 +33,17 @@ #include "libavutil/bprint.h" #include "libavutil/internal.h" #include "libavutil/ass_split_internal.h" +#include "libavutil/ass_internal.h" #include "ttmlenc.h" +#include "encode.h" + + typedef struct { AVCodecContext *avctx; ASSSplitContext *ass_ctx; + int is_default_ass_context; + int extradata_written; AVBPrint buffer; } TTMLContext; @@ -76,28 +82,75 @@ static const ASSCodesCallbacks ttml_callbacks = { .new_line = ttml_new_line_cb, }; -static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, - int bufsize, const AVSubtitle *sub) +static int ttml_write_header_content(AVCodecContext* avctx); + +static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame) +{ + TTMLContext* s = avctx->priv_data; + int ret; + + if (s->ass_ctx && !s->is_default_ass_context) + // We already have a (non-default context) + return; + + if (!frame->num_subtitle_areas) + // Don't need ass context for processing empty subtitle frames + return; + + // The frame has content, so we need to set up a context + if (frame->subtitle_header && frame->subtitle_header->size > 0) { + const char* subtitle_header = (char*)frame->subtitle_header->data; + avpriv_ass_split_free(s->ass_ctx); + s->ass_ctx = avpriv_ass_split(subtitle_header); + s->is_default_ass_context = 0; + } + else if (!s->ass_ctx) { + char* subtitle_header = avpriv_ass_get_subtitle_header_default(0); + if (!subtitle_header) + return; + + s->ass_ctx = avpriv_ass_split(subtitle_header); + s->is_default_ass_context = 1; + av_free(subtitle_header); + } + + if (s->ass_ctx && !s->extradata_written) { + s->extradata_written = 1; + if ((ret = ttml_write_header_content(avctx)) < 0) { + av_log(avctx, AV_LOG_ERROR, "Error writing header content.\n"); + } + } +} + +static int ttml_encode_frame(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet) { TTMLContext *s = avctx->priv_data; ASSDialog *dialog; - int i; + int i, ret; + + ensure_ass_context(avctx, frame); av_bprint_clear(&s->buffer); - for (i=0; inum_rects; i++) { - const char *ass = sub->rects[i]->ass; - int ret; + for (i=0; i< frame->num_subtitle_areas; i++) { + const char *ass = frame->subtitle_areas[i]->ass; - if (sub->rects[i]->type != SUBTITLE_ASS) { - av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); + if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) { + av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n"); return AVERROR(EINVAL); } + if (!ass) + continue; + dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); if (!dialog) return AVERROR(ENOMEM); + if (i > 0) + ttml_new_line_cb(s, 0); + if (dialog->style) { av_bprintf(&s->buffer, "buffer, dialog->style, NULL, @@ -130,17 +183,19 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, if (!av_bprint_is_complete(&s->buffer)) return AVERROR(ENOMEM); - if (!s->buffer.len) - return 0; - - // force null-termination, so in case our destination buffer is - // too small, the return value is larger than bufsize minus null. - if (av_strlcpy(buf, s->buffer.str, bufsize) > bufsize - 1) { - av_log(avctx, AV_LOG_ERROR, "Buffer too small for TTML event.\n"); - return AVERROR_BUFFER_TOO_SMALL; + + ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); + return ret; } - return s->buffer.len; + av_strlcpy(avpkt->data, s->buffer.str, avpkt->size); + + avpkt->size = s->buffer.len; + *got_packet = 1; + + return 0; } static av_cold int ttml_encode_close(AVCodecContext *avctx) @@ -370,13 +425,13 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx) s->avctx = avctx; av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); + s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header); - if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) { - return AVERROR_INVALIDDATA; - } + if (s->ass_ctx) { + if (ret = ttml_write_header_content(avctx) < 0) + return ret; - if ((ret = ttml_write_header_content(avctx)) < 0) { - return ret; + s->extradata_written = 1; } return 0; @@ -389,7 +444,7 @@ const AVCodec ff_ttml_encoder = { .id = AV_CODEC_ID_TTML, .priv_data_size = sizeof(TTMLContext), .init = ttml_encode_init, - .encode_sub = ttml_encode_frame, + .encode2 = ttml_encode_frame, .close = ttml_encode_close, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c index 761099b69a..c0436f5739 100644 --- a/libavcodec/webvttenc.c +++ b/libavcodec/webvttenc.c @@ -22,6 +22,7 @@ #include #include "avcodec.h" +#include "encode.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/ass_split_internal.h" @@ -32,6 +33,7 @@ typedef struct { AVCodecContext *avctx; ASSSplitContext *ass_ctx; + int is_default_ass_context; AVBPrint buffer; unsigned timestamp_end; int count; @@ -155,43 +157,81 @@ static const ASSCodesCallbacks webvtt_callbacks = { .end = webvtt_end_cb, }; -static int webvtt_encode_frame(AVCodecContext *avctx, - unsigned char *buf, int bufsize, const AVSubtitle *sub) +static void ensure_ass_context(WebVTTContext* s, const AVFrame* frame) +{ + if (s->ass_ctx && !s->is_default_ass_context) + // We already have a (non-default context) + return; + + if (!frame->num_subtitle_areas) + // Don't need ass context for processing empty subtitle frames + return; + + // The frame has content, so we need to set up a context + if (frame->subtitle_header && frame->subtitle_header->size > 0) { + avpriv_ass_split_free(s->ass_ctx); + const char* subtitle_header = (char*)frame->subtitle_header->data; + s->ass_ctx = avpriv_ass_split(subtitle_header); + s->is_default_ass_context = 0; + } + else if (!s->ass_ctx) { + char* subtitle_header = avpriv_ass_get_subtitle_header_default(0); + if (!subtitle_header) + return; + + s->ass_ctx = avpriv_ass_split(subtitle_header); + s->is_default_ass_context = 1; + av_free(subtitle_header); + } +} + +static int webvtt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet) { WebVTTContext *s = avctx->priv_data; ASSDialog *dialog; - int i; + int ret, i; + + ensure_ass_context(s, frame); av_bprint_clear(&s->buffer); - for (i=0; inum_rects; i++) { - const char *ass = sub->rects[i]->ass; + for (i=0; i< frame->num_subtitle_areas; i++) { + const char *ass = frame->subtitle_areas[i]->ass; - if (sub->rects[i]->type != SUBTITLE_ASS) { - av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); + if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) { + av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n"); return AVERROR(EINVAL); } - dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); - if (!dialog) - return AVERROR(ENOMEM); - webvtt_style_apply(s, dialog->style); - avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text); - avpriv_ass_free_dialog(&dialog); + if (ass) { + + if (i > 0) + webvtt_new_line_cb(s, 0); + + dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); + if (!dialog) + return AVERROR(ENOMEM); + webvtt_style_apply(s, dialog->style); + avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text); + avpriv_ass_free_dialog(&dialog); + } } if (!av_bprint_is_complete(&s->buffer)) return AVERROR(ENOMEM); - if (!s->buffer.len) - return 0; - if (s->buffer.len > bufsize) { - av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); - return AVERROR_BUFFER_TOO_SMALL; + ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); + return ret; } - memcpy(buf, s->buffer.str, s->buffer.len); - return s->buffer.len; + memcpy(avpkt->data, s->buffer.str, s->buffer.len); + avpkt->size = s->buffer.len; + *got_packet = s->buffer.len > 0; + + return 0; } static int webvtt_encode_close(AVCodecContext *avctx) @@ -206,9 +246,9 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx) { WebVTTContext *s = avctx->priv_data; s->avctx = avctx; - s->ass_ctx = avpriv_ass_split(avctx->subtitle_header); + s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header); av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); - return s->ass_ctx ? 0 : AVERROR_INVALIDDATA; + return 0; } const AVCodec ff_webvtt_encoder = { @@ -218,7 +258,7 @@ const AVCodec ff_webvtt_encoder = { .id = AV_CODEC_ID_WEBVTT, .priv_data_size = sizeof(WebVTTContext), .init = webvtt_encode_init, - .encode_sub = webvtt_encode_frame, + .encode2 = webvtt_encode_frame, .close = webvtt_encode_close, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; diff --git a/libavcodec/xsubenc.c b/libavcodec/xsubenc.c index 03d0dc2d86..ef804f21f4 100644 --- a/libavcodec/xsubenc.c +++ b/libavcodec/xsubenc.c @@ -22,6 +22,7 @@ #include "avcodec.h" #include "bytestream.h" +#include "encode.h" #include "internal.h" #include "put_bits.h" @@ -111,39 +112,56 @@ static int make_tc(uint64_t ms, int *tc) return ms > 99; } -static int xsub_encode(AVCodecContext *avctx, unsigned char *buf, - int bufsize, const AVSubtitle *h) +static int xsub_encode(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet) { - uint64_t startTime = h->pts / 1000; // FIXME: need better solution... - uint64_t endTime = startTime + h->end_display_time - h->start_display_time; + const int64_t duration_ms = (int64_t)((double)frame->subtitle_timing.duration * av_q2d(AV_TIME_BASE_Q) * 1000); + const uint64_t startTime = (int64_t)((double)frame->subtitle_timing.start_pts * av_q2d(AV_TIME_BASE_Q) * 1000); + const uint64_t endTime = startTime + duration_ms; int start_tc[4], end_tc[4]; - uint8_t *hdr = buf + 27; // Point behind the timestamp + uint8_t *hdr; uint8_t *rlelenptr; uint16_t width, height; - int i; + int i, ret; PutBitContext pb; + uint8_t* buf; + int64_t req_size; - if (bufsize < 27 + 7*2 + 4*3) { - av_log(avctx, AV_LOG_ERROR, "Buffer too small for XSUB header.\n"); - return AVERROR_BUFFER_TOO_SMALL; + if (!frame->num_subtitle_areas) { + // Don't encode empty sub events + return 0; } + // Estimate size (timestamp 27, header 7*2 + 4*3, padding 10) + req_size = 27 + 7*2 + 4*3 + 10; + req_size += frame->subtitle_areas[0]->linesize[0] * frame->subtitle_areas[0]->h; + req_size += 256; // Palette + + ret = ff_get_encode_buffer(avctx, avpkt, req_size, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); + return ret; + } + + buf = avpkt->data; + hdr = avpkt->data + 27; // Point behind the timestamp + // TODO: support multiple rects - if (h->num_rects != 1) - av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", h->num_rects); + if (frame->num_subtitle_areas != 1) + av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", frame->num_subtitle_areas); // TODO: render text-based subtitles into bitmaps - if (!h->rects[0]->data[0] || !h->rects[0]->data[1]) { + if (!frame->subtitle_areas[0]->buf[0]->data || !frame->subtitle_areas[0]->pal) { av_log(avctx, AV_LOG_WARNING, "No subtitle bitmap available.\n"); return AVERROR(EINVAL); } // TODO: color reduction, similar to dvdsub encoder - if (h->rects[0]->nb_colors > 4) - av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", h->rects[0]->nb_colors); + if (frame->subtitle_areas[0]->nb_colors > 4) + av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", frame->subtitle_areas[0]->nb_colors); // TODO: Palette swapping if color zero is not transparent - if (((uint32_t *)h->rects[0]->data[1])[0] & 0xff000000) + if (((uint32_t *)frame->subtitle_areas[0]->pal)[0] & 0xff000000) av_log(avctx, AV_LOG_WARNING, "Color index 0 is not transparent. Transparency will be messed up.\n"); if (make_tc(startTime, start_tc) || make_tc(endTime, end_tc)) { @@ -151,7 +169,7 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf, return AVERROR(EINVAL); } - snprintf(buf, 28, + snprintf((char *)avpkt->data, 28, "[%02d:%02d:%02d.%03d-%02d:%02d:%02d.%03d]", start_tc[3], start_tc[2], start_tc[1], start_tc[0], end_tc[3], end_tc[2], end_tc[1], end_tc[0]); @@ -160,45 +178,47 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf, // 2 pixels required on either side of subtitle. // Possibly due to limitations of hardware renderers. // TODO: check if the bitmap is already padded - width = FFALIGN(h->rects[0]->w, 2) + PADDING * 2; - height = FFALIGN(h->rects[0]->h, 2); + width = FFALIGN(frame->subtitle_areas[0]->w, 2) + PADDING * 2; + height = FFALIGN(frame->subtitle_areas[0]->h, 2); bytestream_put_le16(&hdr, width); bytestream_put_le16(&hdr, height); - bytestream_put_le16(&hdr, h->rects[0]->x); - bytestream_put_le16(&hdr, h->rects[0]->y); - bytestream_put_le16(&hdr, h->rects[0]->x + width -1); - bytestream_put_le16(&hdr, h->rects[0]->y + height -1); + bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x); + bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y); + bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x + width -1); + bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y + height -1); rlelenptr = hdr; // Will store length of first field here later. hdr+=2; // Palette for (i=0; i<4; i++) - bytestream_put_be24(&hdr, ((uint32_t *)h->rects[0]->data[1])[i]); + bytestream_put_be24(&hdr, ((uint32_t *)frame->subtitle_areas[0]->pal)[i]); // Bitmap // RLE buffer. Reserve 2 bytes for possible padding after the last row. - init_put_bits(&pb, hdr, bufsize - (hdr - buf) - 2); - if (xsub_encode_rle(&pb, h->rects[0]->data[0], - h->rects[0]->linesize[0] * 2, - h->rects[0]->w, (h->rects[0]->h + 1) >> 1)) + init_put_bits(&pb, hdr, avpkt->size - (hdr - buf) - 2); + if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data, + frame->subtitle_areas[0]->linesize[0] * 2, + frame->subtitle_areas[0]->w, (frame->subtitle_areas[0]->h + 1) >> 1)) return AVERROR_BUFFER_TOO_SMALL; bytestream_put_le16(&rlelenptr, put_bytes_count(&pb, 0)); // Length of first field - if (xsub_encode_rle(&pb, h->rects[0]->data[0] + h->rects[0]->linesize[0], - h->rects[0]->linesize[0] * 2, - h->rects[0]->w, h->rects[0]->h >> 1)) + if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data + frame->subtitle_areas[0]->linesize[0], + frame->subtitle_areas[0]->linesize[0] * 2, + frame->subtitle_areas[0]->w, frame->subtitle_areas[0]->h >> 1)) return AVERROR_BUFFER_TOO_SMALL; // Enforce total height to be a multiple of 2 - if (h->rects[0]->h & 1) { - put_xsub_rle(&pb, h->rects[0]->w, PADDING_COLOR); + if (frame->subtitle_areas[0]->h & 1) { + put_xsub_rle(&pb, frame->subtitle_areas[0]->w, PADDING_COLOR); } flush_put_bits(&pb); - return hdr - buf + put_bytes_output(&pb); + avpkt->size = hdr - buf + put_bytes_output(&pb); + *got_packet = 1; + return 0; } static av_cold int xsub_encoder_init(AVCodecContext *avctx) @@ -217,6 +237,6 @@ const AVCodec ff_xsub_encoder = { .type = AVMEDIA_TYPE_SUBTITLE, .id = AV_CODEC_ID_XSUB, .init = xsub_encoder_init, - .encode_sub = xsub_encode, + .encode2 = xsub_encode, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; From patchwork Fri Jan 14 01:13:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33573 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp985987iog; Thu, 13 Jan 2022 17:15:42 -0800 (PST) X-Google-Smtp-Source: ABdhPJw86/INjbOHEieVU3Tik+H0h+zL2QK9v+Fyyebptavv5/OmLNiRdHv7NS+URPZVCYnPsE2C X-Received: by 2002:a05:6402:f1b:: with SMTP id i27mr6509333eda.225.1642122941997; Thu, 13 Jan 2022 17:15:41 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642122941; cv=none; d=google.com; s=arc-20160816; b=xnPr8stISVFusG5znMpjSwfvaM22jkNmZhAl7UYZYvl3G+VT8QEXWkSCBxahJObfTz BGo8TbQ8MmUrxZeb1ytphjswJ4qGpYjzZXNYL6oSMJ2CEVgGSyfEzI8XolZ0zxDQpDY8 9xKrwBAtF3bEJwXojoNqBmFbSghpReUEXusLzEm9ao2EQFPw36wEg9Mjdwxvv/Cp9yfn Bz+kjt3L3e4kocKiTF7MgC5/9rv5c40ndYzWE+9kaaDBrMpFbwzE82DhS4mjdW+sxFgu YKicu+uxNc4hPyrXGV3qQtsFsLAhV6IcjW8R4QZgOxLjFAny1Ql3p/FtmMkb1tON7dkf vGOQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=1RAxF2fj15VFJ9FPM3zCBf7wMu+GEwUZz0PHlrEq2cM=; b=mgO7+/oBr67TLDcKnQyR4Y+PTVwGcNO5PQ4eC1MEHVHJ7r+UE/l94lYyfDkRglI0Pw f8B0tEEeQtYXTCYWel/XJaoFIvrAWqJ9r5o0rJOnJGyE6GE46jlST55iUOgukD72YrMJ KCVZOQlo133v3FkCvFMF/IOavfxKTx+FWfjpC50dx+srzp02Q4glcs773u3Pj4kdcqAL 8+p02G2Q33Hmo9NJH8OmH2QuA/iIgjR4ds7qAkILnCkc5d9SWiX7h0zfeJkNhmkVIqCL +k77qhDJDvySRAs82UZW8dAn583iea7Ir6NtlNuV/+wp5Ol6SRLiY+e0UJJoDVLvbZ+J gInA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=N01cPKU4; 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 w2si2383115ejn.640.2022.01.13.17.15.41; Thu, 13 Jan 2022 17:15:41 -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 header.s=20210112 header.b=N01cPKU4; 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 D038F68B5C0; Fri, 14 Jan 2022 03:13:57 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pg1-f181.google.com (mail-pg1-f181.google.com [209.85.215.181]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 973A568B5A5 for ; Fri, 14 Jan 2022 03:13:44 +0200 (EET) Received: by mail-pg1-f181.google.com with SMTP id s1so1434659pga.5 for ; Thu, 13 Jan 2022 17:13:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=+Qq3NC5GW+Y2NNctI0yVrxm3g6AID74nGNCi+ZUPOkA=; b=N01cPKU45PD8q0KcWLsicVeKdsPrHf1kZZebqpDwyNFNtPzzM2cST7NmAKcRxlys0y cwcnGavudsqgiu9dR+ymVbll+saI3U/qvtzlTqO8+C0U2LGK8mXzMMvvabnX/GC71VIJ ki1WcXmEUZy4OlCJQYH3nsYCRw2Ho7CCuXrv94nZWlm9I+LVB5LF2gVdR++GZ6xrPIRi DmJl9ZV9KKxO6dGJyEecE9Banc7SL7dW5gVPlJ9lltnYtOYgaKlhW24HxfD5aXPWU8wA qRPF02Nh0z8QTHM0aIEO+X9NjoAfbDLn+jtiJ8jWv5g5mXwji6hROGsB4YDktYmUD+7h b5fw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=+Qq3NC5GW+Y2NNctI0yVrxm3g6AID74nGNCi+ZUPOkA=; b=JyunWREgCNSJbcs14/JxHcYhS453XIUTHMGSGbjZkZwQ76GpuHddb99fK8H6TfLiaO 5wkm/+NZn/b4RbwzPcbti/k971DkI2/aSuVejcy7eJr0XggmfnOafD57nLiwrutfz2Gx m+S9lP/DOw0G+niZS/BToTAUlWHffzX7Nq/ksPI1OTnSGt7TvACD6onP+U/dtHO4R4pm bxi1bmQIVTYOmQzTBlMohJ2gPihmjgP9YtOv8NQirY56hu+ndLZUSHWLxIWbmjKT0asy ZvVsUkMCWjTVfjgmZp98IM1rb0gEM0ciAOZNHHLNSxZCuaYItIBTwXdG6qZhhqExoerm 7jqA== X-Gm-Message-State: AOAM533L12WrlAHP97l9N3Qkf58TY3CQ2TCGX4lMjmRv2IoSe1Vl2XUl bF0wr08HdnURCXhwW/uwOsWAXX2gCgGBUw== X-Received: by 2002:a63:a00a:: with SMTP id r10mr6010779pge.553.1642122822989; Thu, 13 Jan 2022 17:13:42 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id 198sm3703109pfy.202.2022.01.13.17.13.42 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:42 -0800 (PST) Message-Id: <34be31a7ebd17c55ee9314f42482b89de1759ea4.1642122814.git.ffmpegagent@gmail.com> In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:16 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 07/24] avcodec/subtitles: Replace deprecated enum values 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: d7z41H5UAmAu From: softworkz Signed-off-by: softworkz --- libavcodec/ass.h | 2 +- libavcodec/assdec.c | 2 +- libavcodec/dvbsubdec.c | 2 +- libavcodec/dvdsubdec.c | 2 +- libavcodec/dvdsubenc.c | 2 +- libavcodec/pgssubdec.c | 2 +- libavcodec/xsubdec.c | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libavcodec/ass.h b/libavcodec/ass.h index 8bc13d7ab8..43c5ad651a 100644 --- a/libavcodec/ass.h +++ b/libavcodec/ass.h @@ -83,7 +83,7 @@ static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog, rects[sub->num_rects] = av_mallocz(sizeof(*rects[0])); if (!rects[sub->num_rects]) return AVERROR(ENOMEM); - rects[sub->num_rects]->type = SUBTITLE_ASS; + rects[sub->num_rects]->type = AV_SUBTITLE_FMT_ASS; ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog); if (!ass_str) return AVERROR(ENOMEM); diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c index 7802a44e71..fd321e7004 100644 --- a/libavcodec/assdec.c +++ b/libavcodec/assdec.c @@ -54,7 +54,7 @@ static int ass_decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr, if (!sub->rects[0]) return AVERROR(ENOMEM); sub->num_rects = 1; - sub->rects[0]->type = SUBTITLE_ASS; + sub->rects[0]->type = AV_SUBTITLE_FMT_ASS; sub->rects[0]->ass = av_strdup(avpkt->data); if (!sub->rects[0]->ass) return AVERROR(ENOMEM); diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c index bc741a1de6..0d64c6e71c 100644 --- a/libavcodec/dvbsubdec.c +++ b/libavcodec/dvbsubdec.c @@ -795,7 +795,7 @@ static int save_subtitle_set(AVCodecContext *avctx, AVSubtitle *sub, int *got_ou rect->w = region->width; rect->h = region->height; rect->nb_colors = (1 << region->depth); - rect->type = SUBTITLE_BITMAP; + rect->type = AV_SUBTITLE_FMT_BITMAP; rect->linesize[0] = region->width; clut = get_clut(ctx, region->clut); diff --git a/libavcodec/dvdsubdec.c b/libavcodec/dvdsubdec.c index 52259f0730..b39b3d1838 100644 --- a/libavcodec/dvdsubdec.c +++ b/libavcodec/dvdsubdec.c @@ -406,7 +406,7 @@ static int decode_dvd_subtitles(DVDSubContext *ctx, AVSubtitle *sub_header, sub_header->rects[0]->y = y1; sub_header->rects[0]->w = w; sub_header->rects[0]->h = h; - sub_header->rects[0]->type = SUBTITLE_BITMAP; + sub_header->rects[0]->type = AV_SUBTITLE_FMT_BITMAP; sub_header->rects[0]->linesize[0] = w; sub_header->rects[0]->flags = is_menu ? AV_SUBTITLE_FLAG_FORCED : 0; } diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c index 84f2a544f0..ff21e75ef3 100644 --- a/libavcodec/dvdsubenc.c +++ b/libavcodec/dvdsubenc.c @@ -274,7 +274,7 @@ static int encode_dvd_subtitles(AVCodecContext* avctx, AVPacket* avpkt, return AVERROR(EINVAL); for (i = 0; i < rects; i++) - if (frame->subtitle_areas[i]->type != SUBTITLE_BITMAP) { + if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_BITMAP) { av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n"); return AVERROR(EINVAL); } diff --git a/libavcodec/pgssubdec.c b/libavcodec/pgssubdec.c index bdd20c914b..22b6616f9b 100644 --- a/libavcodec/pgssubdec.c +++ b/libavcodec/pgssubdec.c @@ -535,7 +535,7 @@ static int display_end_segment(AVCodecContext *avctx, void *data, if (!rect) return AVERROR(ENOMEM); sub->rects[sub->num_rects++] = rect; - rect->type = SUBTITLE_BITMAP; + rect->type = AV_SUBTITLE_FMT_BITMAP; /* Process bitmap */ object = find_object(ctx->presentation.objects[i].id, &ctx->objects); diff --git a/libavcodec/xsubdec.c b/libavcodec/xsubdec.c index 85cd7d1c20..a4be18a1d8 100644 --- a/libavcodec/xsubdec.c +++ b/libavcodec/xsubdec.c @@ -107,7 +107,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr, sub->num_rects = 1; rect->x = x; rect->y = y; rect->w = w; rect->h = h; - rect->type = SUBTITLE_BITMAP; + rect->type = AV_SUBTITLE_FMT_BITMAP; rect->linesize[0] = w; rect->data[0] = av_malloc(w * h); rect->nb_colors = 4; From patchwork Fri Jan 14 01:13:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33574 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp986117iog; Thu, 13 Jan 2022 17:15:51 -0800 (PST) X-Google-Smtp-Source: ABdhPJwi6KmstBC2rq2pRkiylObVosxX3FCAD3VRhVbd2/wx+A2PK7Q+7YsUtCGtoFcFz0B2PoHO X-Received: by 2002:a17:906:d555:: with SMTP id cr21mr5715436ejc.299.1642122951592; Thu, 13 Jan 2022 17:15:51 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642122951; cv=none; d=google.com; s=arc-20160816; b=gy8d+WR4OCLWUouajyR2ChhI8lz53+fhh4TQwGfUsaTQkCZZnyBlJBFJ4KhRZ713QV RFrC3xPtrn7SfcT7C3pKqsCrV2FTfIXhzuwxJG6qYhR6VC/8vmYm9pjGWMCGc96by/p6 /gsIjWURbXQ4kOJg2Hb5U49clzN+Ru+M3KujJm2UXVREjwknDDn+qPSVSJju5NUfFKkE TUD7TfbzWBxualr9NYd2ApiROk5yUzIQKe5nx1IPiyBaMprHI7FALjeUw2f1NGPVWrrF Hjb3ciu+oNKMwEMnP4g6e7Bj2NaxNs5fRF2HoD+nQ7GLqF35Hhp/uHthUdAnydbLEwL2 thzg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=PuDIsSEQfRTr0LiEkrvBq+UiNiA9XVGOjPG/JZtV0xk=; b=Q7XlWvFSfI0fTZNrUjEHAA3Xt96WS/efUFBcDFideyo051cQMdJd4oxtKZG8fdH0px ECdusoZpagcXqexwN3ggVWNHtnYU+1FAKh3vrT+ASV0fD/Sd8isQHPm+Lyf21iDHHnrG c5Pzb5M2SlG8g8Nh5oXMao0fGmR/EpglnV0RA9Kf9Oa+H0GfLJA55O8aPtgfO59tqCoJ 1DdRPIeIUZaEEvmoW7eByh6cFbHBTfyw5kZ/zk1DfSOaCDJYth0jEED89Inq8b5VkqJY j+6yTUAtZBaFdNupgtlbChpjs3QoPd0vU9O0Rnhos0VXtTG2Ggp1ejrc9qcdFU2Qu5nR 2ksg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=WkOrrTR0; 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 dt2si850400ejc.870.2022.01.13.17.15.51; Thu, 13 Jan 2022 17:15:51 -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 header.s=20210112 header.b=WkOrrTR0; 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 D55CE68B655; Fri, 14 Jan 2022 03:13:58 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pl1-f181.google.com (mail-pl1-f181.google.com [209.85.214.181]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 97D1468B5BB for ; Fri, 14 Jan 2022 03:13:45 +0200 (EET) Received: by mail-pl1-f181.google.com with SMTP id h1so11894183pls.11 for ; Thu, 13 Jan 2022 17:13:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=Q7AT0tUuuLt8OrCL8HtZP7NgFA2rMArjQNFYX8ukW4c=; b=WkOrrTR0HCmc9NnYoGuRO/7ztk2amOtlVW3mEuax6qCBy4RvCfIR7CCv1mi3VysOOX 4PeIGPM/7sIKraljHda+jqcqEFGCSLFOsMf7bOdWY6sZWLc3MwxckQXm0ro8cP45Yf2z FSCK2DN1OjBfNSftw+/Xv0FdrycJyx1O3/AoCvipi/Uphkml53n9V9KNIQgppxDsHB12 ue5JOMpg0QkfWYzQRZUWC8obH9w1z6QbnhQx/7h8kw9K4kSUQGLoQEYVZzR7gMeStqJF Mr5qmoTd0MT/g9Nm2qixxYUcz9qGkoIlmtQGQaYZi704g6zyBdVMwKyoz/xt1cUStPmV /fWA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=Q7AT0tUuuLt8OrCL8HtZP7NgFA2rMArjQNFYX8ukW4c=; b=I53vB4idRmf1FwnAjBrRyibvPHwXZsaYstniaQk7opvlhrt0iQComeY8JPBjMfBXx9 sLuFsEW0mDcz4XcqHzEFZrCsKpx6zB6MmN2l22kna/SPUuTaZzMSqp1UCP1tIzKlbnEn 69NFj2ZMIy0pnLkS/JKW06BqPZ2DLWcP/baaH4QrGV7oKLajWIiThLj5igkqMBfwErIc ZohyJrQx0WVn9URc2BWzxT/w/VJj18CGXvOT2Fm2mc57snRUK9Ej1SJt5qL4IYiQaiIu +OBRsY8AtooCJz8HDgCJo/w8T2pcCsAdi8tCg+ICvnAdV19cD/gL8HAXBQwuaJvnOJ7a eIyw== X-Gm-Message-State: AOAM531Dy53nFors54sZM4a3GMu53YgK42du0zWUn8u7Eel0jpA9gh4P 0EpGJgqoWGPFz3c6dY90B8ngKfVc4ljHEg== X-Received: by 2002:a17:90b:4012:: with SMTP id ie18mr8011388pjb.43.1642122824004; Thu, 13 Jan 2022 17:13:44 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id x1sm3197914pgh.44.2022.01.13.17.13.43 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:43 -0800 (PST) Message-Id: In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:17 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 08/24] fftools/play, probe: Adjust for subtitle changes 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: KIJ5C1Qp/AdG From: softworkz Signed-off-by: softworkz --- fftools/ffplay.c | 102 +++++++++++++++++++++------------------------- fftools/ffprobe.c | 47 +++++++++++++-------- 2 files changed, 77 insertions(+), 72 deletions(-) diff --git a/fftools/ffplay.c b/fftools/ffplay.c index e7b20be76b..94286eb678 100644 --- a/fftools/ffplay.c +++ b/fftools/ffplay.c @@ -152,7 +152,6 @@ typedef struct Clock { /* Common struct for handling all types of decoded data and allocated render buffers. */ typedef struct Frame { AVFrame *frame; - AVSubtitle sub; int serial; double pts; /* presentation timestamp for the frame */ double duration; /* estimated duration of the frame */ @@ -586,7 +585,7 @@ static int decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue, S return 0; } -static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) { +static int decoder_decode_frame(Decoder *d, AVFrame *frame) { int ret = AVERROR(EAGAIN); for (;;) { @@ -620,6 +619,9 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) { } } break; + case AVMEDIA_TYPE_SUBTITLE: + ret = avcodec_receive_frame(d->avctx, frame); + break; } if (ret == AVERROR_EOF) { d->finished = d->pkt_serial; @@ -652,25 +654,11 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) { av_packet_unref(d->pkt); } while (1); - if (d->avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { - int got_frame = 0; - ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, d->pkt); - if (ret < 0) { - ret = AVERROR(EAGAIN); - } else { - if (got_frame && !d->pkt->data) { - d->packet_pending = 1; - } - ret = got_frame ? 0 : (d->pkt->data ? AVERROR(EAGAIN) : AVERROR_EOF); - } - av_packet_unref(d->pkt); + if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) { + av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n"); + d->packet_pending = 1; } else { - if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) { - av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n"); - d->packet_pending = 1; - } else { - av_packet_unref(d->pkt); - } + av_packet_unref(d->pkt); } } } @@ -683,7 +671,6 @@ static void decoder_destroy(Decoder *d) { static void frame_queue_unref_item(Frame *vp) { av_frame_unref(vp->frame); - avsubtitle_free(&vp->sub); } static int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last) @@ -981,7 +968,7 @@ static void video_image_display(VideoState *is) if (frame_queue_nb_remaining(&is->subpq) > 0) { sp = frame_queue_peek(&is->subpq); - if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) { + if (vp->pts >= sp->pts) { if (!sp->uploaded) { uint8_t* pixels[4]; int pitch[4]; @@ -993,25 +980,27 @@ static void video_image_display(VideoState *is) if (realloc_texture(&is->sub_texture, SDL_PIXELFORMAT_ARGB8888, sp->width, sp->height, SDL_BLENDMODE_BLEND, 1) < 0) return; - for (i = 0; i < sp->sub.num_rects; i++) { - AVSubtitleRect *sub_rect = sp->sub.rects[i]; + for (i = 0; i < sp->frame->num_subtitle_areas; i++) { + AVSubtitleArea *area = sp->frame->subtitle_areas[i]; + SDL_Rect sdl_rect = { .x = area->x, .y = area->y, .w = area->w, .h = area->h }; - sub_rect->x = av_clip(sub_rect->x, 0, sp->width ); - sub_rect->y = av_clip(sub_rect->y, 0, sp->height); - sub_rect->w = av_clip(sub_rect->w, 0, sp->width - sub_rect->x); - sub_rect->h = av_clip(sub_rect->h, 0, sp->height - sub_rect->y); + area->x = av_clip(area->x, 0, sp->width ); + area->y = av_clip(area->y, 0, sp->height); + area->w = av_clip(area->w, 0, sp->width - area->x); + area->h = av_clip(area->h, 0, sp->height - area->y); is->sub_convert_ctx = sws_getCachedContext(is->sub_convert_ctx, - sub_rect->w, sub_rect->h, AV_PIX_FMT_PAL8, - sub_rect->w, sub_rect->h, AV_PIX_FMT_BGRA, + area->w, area->h, AV_PIX_FMT_PAL8, + area->w, area->h, AV_PIX_FMT_BGRA, 0, NULL, NULL, NULL); if (!is->sub_convert_ctx) { av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n"); return; } - if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)pixels, pitch)) { - sws_scale(is->sub_convert_ctx, (const uint8_t * const *)sub_rect->data, sub_rect->linesize, - 0, sub_rect->h, pixels, pitch); + if (!SDL_LockTexture(is->sub_texture, &sdl_rect, (void **)pixels, pitch)) { + const uint8_t* data[2] = { area->buf[0]->data, (uint8_t *)&area->pal }; + sws_scale(is->sub_convert_ctx, data, area->linesize, + 0, area->h, pixels, pitch); SDL_UnlockTexture(is->sub_texture); } } @@ -1038,16 +1027,18 @@ static void video_image_display(VideoState *is) #if USE_ONEPASS_SUBTITLE_RENDER SDL_RenderCopy(renderer, is->sub_texture, NULL, &rect); #else - int i; + unsigned i; double xratio = (double)rect.w / (double)sp->width; double yratio = (double)rect.h / (double)sp->height; - for (i = 0; i < sp->sub.num_rects; i++) { - SDL_Rect *sub_rect = (SDL_Rect*)sp->sub.rects[i]; - SDL_Rect target = {.x = rect.x + sub_rect->x * xratio, - .y = rect.y + sub_rect->y * yratio, - .w = sub_rect->w * xratio, - .h = sub_rect->h * yratio}; - SDL_RenderCopy(renderer, is->sub_texture, sub_rect, &target); + for (i = 0; i < sp->frame->num_subtitle_areas; i++) { + AVSubtitleArea *area = sp->frame->subtitle_areas[i]; + SDL_Rect sub_rect = { .x = area->x, .y = area->y, + .w = area->w, .h = area->h}; + SDL_Rect target = {.x = rect.x + sub_rect.x * xratio, + .y = rect.y + sub_rect.y * yratio, + .w = sub_rect.w * xratio, + .h = sub_rect.h * yratio}; + SDL_RenderCopy(renderer, is->sub_texture, &sub_rect, &target); } #endif } @@ -1651,19 +1642,20 @@ retry: sp2 = NULL; if (sp->serial != is->subtitleq.serial - || (is->vidclk.pts > (sp->pts + ((float) sp->sub.end_display_time / 1000))) - || (sp2 && is->vidclk.pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000)))) + || (is->vidclk.pts > (sp->pts + ((float) sp->frame->subtitle_timing.duration / AV_TIME_BASE))) + || (sp2 && is->vidclk.pts > (sp2->pts))) { if (sp->uploaded) { int i; - for (i = 0; i < sp->sub.num_rects; i++) { - AVSubtitleRect *sub_rect = sp->sub.rects[i]; + for (i = 0; i < sp->frame->num_subtitle_areas; i++) { + AVSubtitleArea *area = sp->frame->subtitle_areas[i]; + SDL_Rect sdl_rect = { .x = area->x, .y = area->y, .w = area->w, .h = area->h }; uint8_t *pixels; int pitch, j; - if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)&pixels, &pitch)) { - for (j = 0; j < sub_rect->h; j++, pixels += pitch) - memset(pixels, 0, sub_rect->w << 2); + if (!SDL_LockTexture(is->sub_texture, &sdl_rect, (void **)&pixels, &pitch)) { + for (j = 0; j < area->h; j++, pixels += pitch) + memset(pixels, 0, area->w << 2); SDL_UnlockTexture(is->sub_texture); } } @@ -1774,7 +1766,7 @@ static int get_video_frame(VideoState *is, AVFrame *frame) { int got_picture; - if ((got_picture = decoder_decode_frame(&is->viddec, frame, NULL)) < 0) + if ((got_picture = decoder_decode_frame(&is->viddec, frame)) < 0) return -1; if (got_picture) { @@ -2048,7 +2040,7 @@ static int audio_thread(void *arg) return AVERROR(ENOMEM); do { - if ((got_frame = decoder_decode_frame(&is->auddec, frame, NULL)) < 0) + if ((got_frame = decoder_decode_frame(&is->auddec, frame)) < 0) goto the_end; if (got_frame) { @@ -2246,14 +2238,14 @@ static int subtitle_thread(void *arg) if (!(sp = frame_queue_peek_writable(&is->subpq))) return 0; - if ((got_subtitle = decoder_decode_frame(&is->subdec, NULL, &sp->sub)) < 0) + if ((got_subtitle = decoder_decode_frame(&is->subdec, sp->frame)) < 0) break; pts = 0; - if (got_subtitle && sp->sub.format == 0) { - if (sp->sub.pts != AV_NOPTS_VALUE) - pts = sp->sub.pts / (double)AV_TIME_BASE; + if (got_subtitle && sp->frame->format == AV_SUBTITLE_FMT_BITMAP) { + if (sp->frame->subtitle_timing.start_pts != AV_NOPTS_VALUE) + pts = (double)sp->frame->subtitle_timing.start_pts / (double)AV_TIME_BASE; sp->pts = pts; sp->serial = is->subdec.pkt_serial; sp->width = is->subdec.avctx->width; @@ -2263,7 +2255,7 @@ static int subtitle_thread(void *arg) /* now we can update the picture count */ frame_queue_push(&is->subpq); } else if (got_subtitle) { - avsubtitle_free(&sp->sub); + av_frame_free(&sp->frame); } } return 0; diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c index 20582ca7ac..a969faec1d 100644 --- a/fftools/ffprobe.c +++ b/fftools/ffprobe.c @@ -2375,22 +2375,42 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p fflush(stdout); } -static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream, +static void show_subtitle(WriterContext *w, AVFrame *sub, AVStream *stream, AVFormatContext *fmt_ctx) { AVBPrint pbuf; + const char *s; av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED); writer_print_section_header(w, SECTION_ID_SUBTITLE); print_str ("media_type", "subtitle"); - print_ts ("pts", sub->pts); - print_time("pts_time", sub->pts, &AV_TIME_BASE_Q); - print_int ("format", sub->format); - print_int ("start_display_time", sub->start_display_time); - print_int ("end_display_time", sub->end_display_time); - print_int ("num_rects", sub->num_rects); + print_ts ("pts", sub->subtitle_timing.start_pts); + print_time("pts_time", sub->subtitle_timing.start_pts, &AV_TIME_BASE_Q); + print_time("duration", sub->subtitle_timing.duration, &AV_TIME_BASE_Q); + + // Remain compatible with previous outputs + switch (sub->format) { + case AV_SUBTITLE_FMT_BITMAP: + print_int ("format", 0); + break; + case AV_SUBTITLE_FMT_TEXT: + print_int ("format", 1); + break; + case AV_SUBTITLE_FMT_ASS: + print_int ("format", 1); + break; + default: + print_int ("format", -1); + break; + } + + s = av_get_subtitle_fmt_name(sub->format); + if (s) print_str ("format_str", s); + else print_str_opt("format_str", "unknown"); + + print_int ("num_subtitle_rects", sub->num_subtitle_areas); writer_print_section_footer(w); @@ -2557,7 +2577,6 @@ static av_always_inline int process_frame(WriterContext *w, AVFormatContext *fmt_ctx = ifile->fmt_ctx; AVCodecContext *dec_ctx = ifile->streams[pkt->stream_index].dec_ctx; AVCodecParameters *par = ifile->streams[pkt->stream_index].st->codecpar; - AVSubtitle sub; int ret = 0, got_frame = 0; clear_log(1); @@ -2565,6 +2584,7 @@ static av_always_inline int process_frame(WriterContext *w, switch (par->codec_type) { case AVMEDIA_TYPE_VIDEO: case AVMEDIA_TYPE_AUDIO: + case AVMEDIA_TYPE_SUBTITLE: if (*packet_new) { ret = avcodec_send_packet(dec_ctx, pkt); if (ret == AVERROR(EAGAIN)) { @@ -2583,12 +2603,6 @@ static av_always_inline int process_frame(WriterContext *w, } } break; - - case AVMEDIA_TYPE_SUBTITLE: - if (*packet_new) - ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_frame, pkt); - *packet_new = 0; - break; default: *packet_new = 0; } @@ -2603,12 +2617,11 @@ static av_always_inline int process_frame(WriterContext *w, nb_streams_frames[pkt->stream_index]++; if (do_show_frames) if (is_sub) - show_subtitle(w, &sub, ifile->streams[pkt->stream_index].st, fmt_ctx); + show_subtitle(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx); else show_frame(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx); - if (is_sub) - avsubtitle_free(&sub); } + return got_frame || *packet_new; } From patchwork Fri Jan 14 01:13:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33567 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp985248iog; Thu, 13 Jan 2022 17:14:42 -0800 (PST) X-Google-Smtp-Source: ABdhPJyjq2alPFimgR2edsl7T39x1cQu6UuZcTBVKbtFTCXsVCtagxd3lkfTZkP5X6y8MG/W6+cp X-Received: by 2002:a17:907:1692:: with SMTP id hc18mr5418926ejc.25.1642122882722; Thu, 13 Jan 2022 17:14:42 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642122882; cv=none; d=google.com; s=arc-20160816; b=RBM36rab9C2mFVTYDDnYZvPmm6HcZdMzb9qTCfyDWOly1B51xFAQuJKKapd3r6cBof 2crXrewR5xkEUAP+bjVZZ903FA5N3vdw8JYI0+vPAS1qJ85kkjsxiNSEew/v2M2g1V52 cAwo/V9ecXfMrevxUzw4eI3rs8jumVQdaShfzQCSuXBvcNVvASirQV7hyKE1ZC28ldRy W5tMYDdq7tqyuiz8n5QpY9M13gyO/p5aTgbdz7M1GRH6zfYJywjfmQt/h6Y+50abjWWo oQicYY7fMAEA+fdhN/k7Hv83a2N+PtL997qOyYPYmiIhwOTf03ZwYOHxdwExkj12ywgj fOhQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=0jJFOjM+SNwtYHP4xV5SxFdRL4hEh27q+T9SwZxRjI0=; b=cNqw1CMruI/r2hF47p/S7t+frZpQZAsGYsWf+8iNISnc3d9PBMW6k1YS7h+Xavoc59 oZqKYXhFpkd4bqp7YsGivhe7BNVGkDwP9wSEEvE/3ur3V+rck1ybDMRvHGp71YgkeDNl 8vG3tgtzFgp2zGI2q2N5ZKEKJo/V1AMudW84cYU4XcY8uMO9SV+GXUQHqXEJNJ8DUYsK yCU0MtRNKKpHrZD5BrOvO8V0wFjPwQodg70Irr+Cak+LiZKMpkW/QVryyOpzSLNxuYpy tr82qyrRNI0X1p8uCpg3mMsuowEIihz3TghfMLFHnWrq98Z4riH1Jx8jwEdi1dvNtC69 QOfQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=pD448B5O; 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 he8si2210251ejc.251.2022.01.13.17.14.42; Thu, 13 Jan 2022 17:14:42 -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 header.s=20210112 header.b=pD448B5O; 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 55AF068B5C8; Fri, 14 Jan 2022 03:13:51 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pl1-f177.google.com (mail-pl1-f177.google.com [209.85.214.177]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id CB29368B570 for ; Fri, 14 Jan 2022 03:13:46 +0200 (EET) Received: by mail-pl1-f177.google.com with SMTP id t18so11913403plg.9 for ; Thu, 13 Jan 2022 17:13:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=g6eBESuZzneX5zzYySSkWZTd5rQbqeKeBwqCcRu3PTA=; b=pD448B5ObJx0tZzNndqxcI0oI+qHM2qAfKbcGeE/KYZOFNqUUis9OvsJVA80TJW7/z /LH+vq/VfXYkrFNJ+RZstYFUkA3kFO0SeahO3sG+kXb628QMNiYwB5MrEdy/47IEBzIT 0IzM0+6+kh/VjMiki65nAgN9r8gFuSbgIjQb8mC5UAu+Psoz7jRA8UMwCYmujgJhLAgS kkLtftPWiVIfotz8mK4aJWXi/u2xFDvonb9RNCP6Aenuhbwulaj6RG5TkLx5pl7DlxdH rRQywm2MnvKtpIVeL+hWXK2uKm0zPlMvGvqrAeMnvKWJPjzre4ro1QGPBfvamw+8fVU1 Cbrg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=g6eBESuZzneX5zzYySSkWZTd5rQbqeKeBwqCcRu3PTA=; b=7VbGYG3JsddOFhMxMjUXWIr2AXvuelAWFcrSpSyuc0rIIxi+y4heJ6rDKspq6GIeBk NsgYviTIsPp/8HJRJFy6KmDUj80ntw5O8Qlf/+4IU1GmqZDE3UWmRZn1sVxNABhwAGeq 3MBZxQ8lpS7CMDy/G46PhevC+RZ6WnEXLYJBpLPLkPXrUMRAJ8xeTUGIjAdTecb+cWBV aj8VMsQgLD7XlbUR7wnPtBAZj8NNDWr2hLsW5s1UzW/HRYKlXRJUeYl3+o1z3UGbUw9i vnpoSsIVdL9ac9GQU5FfQTHb6zduXkeOynaPy0gkOKbFZzA3el1tdT2z2uy2KKyqUbsv dvRg== X-Gm-Message-State: AOAM532zuvWKMxg9bkqcGd/+g9+N+ISLLRAcua/HI8yf44h2EIFg6Fzk GHbZt9VHrtE683IoaElOwC1qgo9T1UavuA== X-Received: by 2002:a17:90b:1186:: with SMTP id gk6mr17542802pjb.127.1642122824936; Thu, 13 Jan 2022 17:13:44 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id e8sm3463565pjs.46.2022.01.13.17.13.44 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:44 -0800 (PST) Message-Id: In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:18 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 09/24] avfilter/subtitles: Add subtitles.c for subtitle frame allocation 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 8iHZc4wQDYvU From: softworkz Analog to avfilter/video.c and avfilter/audio.c Signed-off-by: softworkz --- libavfilter/Makefile | 1 + libavfilter/avfilter.c | 4 +++ libavfilter/internal.h | 1 + libavfilter/subtitles.c | 63 +++++++++++++++++++++++++++++++++++++++++ libavfilter/subtitles.h | 44 ++++++++++++++++++++++++++++ 5 files changed, 113 insertions(+) create mode 100644 libavfilter/subtitles.c create mode 100644 libavfilter/subtitles.h diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 1adbea75bd..283dd436cd 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -19,6 +19,7 @@ OBJS = allfilters.o \ framequeue.o \ graphdump.o \ graphparser.o \ + subtitles.o \ video.o \ OBJS-$(HAVE_THREADS) += pthread.o diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index 7362bcdab5..df5b8f483c 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -43,6 +43,7 @@ #include "formats.h" #include "framepool.h" #include "internal.h" +#include "subtitles.h" #include "libavutil/ffversion.h" const char av_filter_ffversion[] = "FFmpeg version " FFMPEG_VERSION; @@ -1475,6 +1476,9 @@ int ff_inlink_make_frame_writable(AVFilterLink *link, AVFrame **rframe) case AVMEDIA_TYPE_AUDIO: out = ff_get_audio_buffer(link, frame->nb_samples); break; + case AVMEDIA_TYPE_SUBTITLE: + out = ff_get_subtitles_buffer(link, link->format); + break; default: return AVERROR(EINVAL); } diff --git a/libavfilter/internal.h b/libavfilter/internal.h index 1099b82b4b..fc09ef574c 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -90,6 +90,7 @@ struct AVFilterPad { union { AVFrame *(*video)(AVFilterLink *link, int w, int h); AVFrame *(*audio)(AVFilterLink *link, int nb_samples); + AVFrame *(*subtitle)(AVFilterLink *link, int format); } get_buffer; /** diff --git a/libavfilter/subtitles.c b/libavfilter/subtitles.c new file mode 100644 index 0000000000..951bfd612c --- /dev/null +++ b/libavfilter/subtitles.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 softworkz + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/common.h" + +#include "subtitles.h" +#include "avfilter.h" +#include "internal.h" + + +AVFrame *ff_null_get_subtitles_buffer(AVFilterLink *link, int format) +{ + return ff_get_subtitles_buffer(link->dst->outputs[0], format); +} + +AVFrame *ff_default_get_subtitles_buffer(AVFilterLink *link, int format) +{ + AVFrame *frame; + + frame = av_frame_alloc(); + if (!frame) + return NULL; + + frame->format = format; + frame->type = AVMEDIA_TYPE_SUBTITLE; + + if (av_frame_get_buffer2(frame, 0) < 0) { + av_frame_free(&frame); + return NULL; + } + + return frame; +} + +AVFrame *ff_get_subtitles_buffer(AVFilterLink *link, int format) +{ + AVFrame *ret = NULL; + + if (link->dstpad->get_buffer.subtitle) + ret = link->dstpad->get_buffer.subtitle(link, format); + + if (!ret) + ret = ff_default_get_subtitles_buffer(link, format); + + return ret; +} diff --git a/libavfilter/subtitles.h b/libavfilter/subtitles.h new file mode 100644 index 0000000000..4a9115126e --- /dev/null +++ b/libavfilter/subtitles.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 softworkz + * + * 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 + */ + +#ifndef AVFILTER_SUBTITLES_H +#define AVFILTER_SUBTITLES_H + +#include "avfilter.h" +#include "internal.h" + +/** default handler for get_subtitles_buffer() for subtitle inputs */ +AVFrame *ff_default_get_subtitles_buffer(AVFilterLink *link, int format); + +/** get_subtitles_buffer() handler for filters which simply pass subtitles along */ +AVFrame *ff_null_get_subtitles_buffer(AVFilterLink *link, int format); + +/** + * Request a subtitles frame with a specific set of permissions. + * + * @param link the output link to the filter from which the buffer will + * be requested + * @param format The subtitles format. + * @return A reference to the frame. This must be unreferenced with + * av_frame_free when you are finished with it. +*/ +AVFrame *ff_get_subtitles_buffer(AVFilterLink *link, int format); + +#endif /* AVFILTER_SUBTITLES_H */ From patchwork Fri Jan 14 01:13:19 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33568 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp985361iog; Thu, 13 Jan 2022 17:14:52 -0800 (PST) X-Google-Smtp-Source: ABdhPJwvbUiew9qGxSvdNUiKCXEjrOCyEEt0hiykMqn4zV0nXOFn8NnLK9dgAbeHTgIcHmhR/L4Q X-Received: by 2002:a17:907:728c:: with SMTP id dt12mr5245916ejc.313.1642122891943; Thu, 13 Jan 2022 17:14:51 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642122891; cv=none; d=google.com; s=arc-20160816; b=JbPiv2LxEBpF/UCEH7ARjr82gt6AJr9Fd2/MOJ8naBpqX4hhSSRXb+4e0VWigTTBc+ t3ueL1KNqXWBAS96VigcLvRw+7u2I9uNzwhse2b52Dl1STRxiPIBnEpti/7K/j5N/sKB 1Cz6zkAptj3hdAWPKAn9DnIKiLJke0kW5fl8j4g3nE/2APiqmbCqvr6POcmTia3F1UYG 6iJwRdsBkIO00laqU4GGqITRH8K05MhgmYnYDDjNFmvPDxQOwRCksek/EneznakPCw1O zgHMk0Tb9dOpuZCYsoT37h8wRGeR2y0DsKM7p4wMElNzyetiE19fo9H7bJKBbyul1mUo SyjQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=D9XK8js9iHuuBlLaPiA207PU1mV68aglOYiO792yl24=; b=FJ3YHD2StJGkvUekcFaKiC0ik1DzzX6nziJ6OEOe5JUn0yQErZfCKt+ULdPukCgEkR 5mHKkyYENWR65Ya5XooGmPh9h4/hc4s6dNuVb/bSH79lxG2e/UGgP68HkV+shhkTEbU8 LhflzQgMYhSRiB5EKLa0TUGTZk6sQQmn2ILqTFwrlyCiwtWOmERl1DQWsWjVmeA0vLBi j9i69T+90nxWwOaBbOkBqfbEHd2K1JXhb5l+VD0JjKidXBIY4B/RSs6jMqzuLKkEV6nn jcpDLX60c+3CnBHHAu4K8Sgge18zPHPrc0ZYEKvn1zxPVjxfv6qonji/Y7yVTMaK3L2g CA8g== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=pa+ycnbl; 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 og22si1987634ejc.179.2022.01.13.17.14.51; Thu, 13 Jan 2022 17:14:51 -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 header.s=20210112 header.b=pa+ycnbl; 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 9114268B5D7; Fri, 14 Jan 2022 03:13:52 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pj1-f46.google.com (mail-pj1-f46.google.com [209.85.216.46]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id F3A7A68B595 for ; Fri, 14 Jan 2022 03:13:47 +0200 (EET) Received: by mail-pj1-f46.google.com with SMTP id l16so2604217pjl.4 for ; Thu, 13 Jan 2022 17:13:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=dLPH5zBj8sHlvUt27XXmrtiFrKhqq8u2PLmhuETt2do=; b=pa+ycnblMA9Uam0eh6+HRX6tZSccCwxRzQr5EaSK2bkEtLWD1esg/0gkTbo/WiNFFX 1O9JhMEtvAx1JauByCNNclIsnbgX9NNdFjbNjQzfkqO5OJ+ZBgDhgzpbAQLX3xpCf0wa 70Iu5rrdO3vh6Pxa7Zf8Kh4czK/685aja7cjykfQTBmeLcYhLBcAAJw+VTk0Rz6b+pKN QxHO7REZ5qRWMvRx5GNR3XKXa8dhmqz/lT4hKYB/38HTvi1d9ahR5PQ8qINeYwUDrwa3 SmI5aBVO6YenqfXgcsPduLs+LGDv2hHtLnHoR3gwLROC4LQas9NesfPqUp5xR6jTUPe7 wS/Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=dLPH5zBj8sHlvUt27XXmrtiFrKhqq8u2PLmhuETt2do=; b=UmAj90Z7Aw3nxs3gBWE0Na0Qs+CI+n3Qje24JR/EmUl/zEdkVXBD2u35Wrb9G6u7xe uVTThtbkwQHgTNxrh/tnUAy8dr6PyRxA3V4ovox78zeJ7fHcEC3O8qZxVp/IlO0qtDWd 2Vnd05nAegGi9f3FRydGkw1oHYJuSgUVEjhE1CMFRNnNfg2rRK8ntdWiwXIdLhQ7SJ1+ SQ7q4fuBGDLRQG7Ud7m0zcfHvBsxndVRdl1yJ/1kaHzwv5HDVDnDkWOioaqZVWD4pOBh t4gdNrUxAL5YWPxiqFCGhQMbpDDVoZn9SrmGD/mxJopo7mSbZdoOn9LK40mKrsMxlk5z V68A== X-Gm-Message-State: AOAM532lHuKPqNiA5Xn9b9ewlgk4rbgllwF+OK4yljw/PjzzWStEeQj7 f8xWRv8Dt7U0m/saRjjpngzaZ7BdvXCwuw== X-Received: by 2002:a17:902:8549:b0:149:5d2f:a0a0 with SMTP id d9-20020a170902854900b001495d2fa0a0mr7418017plo.22.1642122826043; Thu, 13 Jan 2022 17:13:46 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id r192sm3055664pgr.93.2022.01.13.17.13.45 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:45 -0800 (PST) Message-Id: In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:19 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 10/24] avfilter/avfilter: Handle subtitle frames 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: /p7JOExNUuwD From: softworkz Signed-off-by: softworkz --- libavfilter/avfilter.c | 8 +++++--- libavfilter/avfilter.h | 11 +++++++++++ libavfilter/avfiltergraph.c | 5 +++++ libavfilter/formats.c | 22 ++++++++++++++++++++++ libavfilter/formats.h | 3 +++ libavfilter/internal.h | 18 +++++++++++++++--- 6 files changed, 61 insertions(+), 6 deletions(-) diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index df5b8f483c..75d5e86539 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -56,7 +56,8 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end) ref->linesize[0], ref->linesize[1], ref->linesize[2], ref->linesize[3], ref->pts, ref->pkt_pos); - if (ref->width) { + switch(ref->type) { + case AVMEDIA_TYPE_VIDEO: ff_tlog(ctx, " a:%d/%d s:%dx%d i:%c iskey:%d type:%c", ref->sample_aspect_ratio.num, ref->sample_aspect_ratio.den, ref->width, ref->height, @@ -64,12 +65,13 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end) ref->top_field_first ? 'T' : 'B', /* Top / Bottom */ ref->key_frame, av_get_picture_type_char(ref->pict_type)); - } - if (ref->nb_samples) { + break; + case AVMEDIA_TYPE_AUDIO: ff_tlog(ctx, " cl:%"PRId64"d n:%d r:%d", ref->channel_layout, ref->nb_samples, ref->sample_rate); + break; } ff_tlog(ctx, "]%s", end ? "\n" : ""); diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index b105dc3159..9f917deb41 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -45,6 +45,7 @@ #include "libavutil/log.h" #include "libavutil/samplefmt.h" #include "libavutil/pixfmt.h" +#include "libavutil/subfmt.h" #include "libavutil/rational.h" #include "libavfilter/version.h" @@ -343,6 +344,12 @@ typedef struct AVFilter { * and outputs use the same sample rate and channel count/layout. */ const enum AVSampleFormat *samples_list; + /** + * Analogous to pixels, but delimited by AV_SUBTITLE_FMT_NONE + * and restricted to filters that only have AVMEDIA_TYPE_SUBTITLE + * inputs and outputs. + */ + const enum AVSubtitleType *subs_list; /** * Equivalent to { pix_fmt, AV_PIX_FMT_NONE } as pixels_list. */ @@ -351,6 +358,10 @@ typedef struct AVFilter { * Equivalent to { sample_fmt, AV_SAMPLE_FMT_NONE } as samples_list. */ enum AVSampleFormat sample_fmt; + /** + * Equivalent to { sub_fmt, AV_SUBTITLE_FMT_NONE } as subs_list. + */ + enum AVSubtitleType sub_fmt; } formats; int priv_size; ///< size of private data to allocate for the filter diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index b8b432e98b..f4987654af 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -311,6 +311,8 @@ static int filter_link_check_formats(void *log, AVFilterLink *link, AVFilterForm return ret; break; + case AVMEDIA_TYPE_SUBTITLE: + return 0; default: av_assert0(!"reached"); } @@ -441,6 +443,9 @@ static int query_formats(AVFilterGraph *graph, void *log_ctx) if (!link) continue; + if (link->type == AVMEDIA_TYPE_SUBTITLE) + continue; + neg = ff_filter_get_negotiation(link); av_assert0(neg); for (neg_step = 1; neg_step < neg->nb_mergers; neg_step++) { diff --git a/libavfilter/formats.c b/libavfilter/formats.c index ba62f73248..5c972bb183 100644 --- a/libavfilter/formats.c +++ b/libavfilter/formats.c @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/subfmt.h" #include "libavutil/avassert.h" #include "libavutil/channel_layout.h" #include "libavutil/common.h" @@ -431,6 +432,12 @@ int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout) return 0; } +int ff_add_subtitle_type(AVFilterFormats **avff, int64_t fmt) +{ + ADD_FORMAT(avff, fmt, ff_formats_unref, int, formats, nb_formats); + return 0; +} + AVFilterFormats *ff_make_formats_list_singleton(int fmt) { int fmts[2] = { fmt, -1 }; @@ -450,6 +457,13 @@ AVFilterFormats *ff_all_formats(enum AVMediaType type) return NULL; fmt++; } + } else if (type == AVMEDIA_TYPE_SUBTITLE) { + if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_BITMAP) < 0) + return NULL; + if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_ASS) < 0) + return NULL; + if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_TEXT) < 0) + return NULL; } return ret; @@ -724,6 +738,10 @@ int ff_default_query_formats(AVFilterContext *ctx) type = AVMEDIA_TYPE_AUDIO; formats = ff_make_format_list(f->formats.samples_list); break; + case FF_FILTER_FORMATS_SUBFMTS_LIST: + type = AVMEDIA_TYPE_SUBTITLE; + formats = ff_make_format_list(f->formats.subs_list); + break; case FF_FILTER_FORMATS_SINGLE_PIXFMT: type = AVMEDIA_TYPE_VIDEO; formats = ff_make_formats_list_singleton(f->formats.pix_fmt); @@ -732,6 +750,10 @@ int ff_default_query_formats(AVFilterContext *ctx) type = AVMEDIA_TYPE_AUDIO; formats = ff_make_formats_list_singleton(f->formats.sample_fmt); break; + case FF_FILTER_FORMATS_SINGLE_SUBFMT: + type = AVMEDIA_TYPE_SUBTITLE; + formats = ff_make_formats_list_singleton(f->formats.sub_fmt); + break; default: av_assert2(!"Unreachable"); /* Intended fallthrough */ diff --git a/libavfilter/formats.h b/libavfilter/formats.h index a884d15213..94754ebd88 100644 --- a/libavfilter/formats.h +++ b/libavfilter/formats.h @@ -180,6 +180,9 @@ int ff_set_common_formats_from_list(AVFilterContext *ctx, const int *fmts); av_warn_unused_result int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout); +av_warn_unused_result +int ff_add_subtitle_type(AVFilterFormats **avff, int64_t fmt); + /** * Add *ref as a new reference to f. */ diff --git a/libavfilter/internal.h b/libavfilter/internal.h index fc09ef574c..192b0ae196 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -149,9 +149,11 @@ static av_always_inline int ff_filter_execute(AVFilterContext *ctx, avfilter_act enum FilterFormatsState { /** - * The default value meaning that this filter supports all formats - * and (for audio) sample rates and channel layouts/counts as long - * as these properties agree for all inputs and outputs. + * The default value meaning that this filter supports + * - For video: all formats + * - For audio: all sample rates and channel layouts/counts + * - For subtitles: all subtitle formats + * as long as these properties agree for all inputs and outputs. * This state is only allowed in case all inputs and outputs actually * have the same type. * The union is unused in this state. @@ -162,8 +164,10 @@ enum FilterFormatsState { FF_FILTER_FORMATS_QUERY_FUNC, ///< formats.query active. FF_FILTER_FORMATS_PIXFMT_LIST, ///< formats.pixels_list active. FF_FILTER_FORMATS_SAMPLEFMTS_LIST, ///< formats.samples_list active. + FF_FILTER_FORMATS_SUBFMTS_LIST, ///< formats.subs_list active. FF_FILTER_FORMATS_SINGLE_PIXFMT, ///< formats.pix_fmt active FF_FILTER_FORMATS_SINGLE_SAMPLEFMT, ///< formats.sample_fmt active. + FF_FILTER_FORMATS_SINGLE_SUBFMT, ///< formats.sub_fmt active. }; #define FILTER_QUERY_FUNC(func) \ @@ -175,16 +179,24 @@ enum FilterFormatsState { #define FILTER_SAMPLEFMTS_ARRAY(array) \ .formats.samples_list = array, \ .formats_state = FF_FILTER_FORMATS_SAMPLEFMTS_LIST +#define FILTER_SUBFMTS_ARRAY(array) \ + .formats.subs_list = array, \ + .formats_state = FF_FILTER_FORMATS_SUBFMTS_LIST #define FILTER_PIXFMTS(...) \ FILTER_PIXFMTS_ARRAY(((const enum AVPixelFormat []) { __VA_ARGS__, AV_PIX_FMT_NONE })) #define FILTER_SAMPLEFMTS(...) \ FILTER_SAMPLEFMTS_ARRAY(((const enum AVSampleFormat[]) { __VA_ARGS__, AV_SAMPLE_FMT_NONE })) +#define FILTER_SUBFMTS(...) \ + FILTER_SUBFMTS_ARRAY(((const enum AVSubtitleType[]) { __VA_ARGS__, AV_SUBTITLE_FMT_NONE })) #define FILTER_SINGLE_PIXFMT(pix_fmt_) \ .formats.pix_fmt = pix_fmt_, \ .formats_state = FF_FILTER_FORMATS_SINGLE_PIXFMT #define FILTER_SINGLE_SAMPLEFMT(sample_fmt_) \ .formats.sample_fmt = sample_fmt_, \ .formats_state = FF_FILTER_FORMATS_SINGLE_SAMPLEFMT +#define FILTER_SINGLE_SUBFMT(sub_fmt_) \ + .formats.sub_fmt = sub_fmt_, \ + .formats_state = FF_FILTER_FORMATS_SINGLE_SUBFMT #define FILTER_INOUTPADS(inout, array) \ .inout = array, \ From patchwork Fri Jan 14 01:13:20 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33570 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp985628iog; Thu, 13 Jan 2022 17:15:12 -0800 (PST) X-Google-Smtp-Source: ABdhPJzvfaQ5lkW3/RImrQzDObAM3j79/OBIH8jBM6n49Ulj47glU+safN6mmC6icZzbH8j0DGFQ X-Received: by 2002:a17:907:1c1f:: with SMTP id nc31mr5709866ejc.624.1642122912735; Thu, 13 Jan 2022 17:15:12 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642122912; cv=none; d=google.com; s=arc-20160816; b=CEFV/nFY7unFaw+ALby7DGzajEZo7UQ5Q18mBsgtHNNBSOqjvB9fOqfev2gPWNK47M cWvXbaxoThPl2LoxJltDC2b+k7r4iLMOLja0SMOem1EdpCRBn83E/jM5zadApcpHGmxS E397C2wYlmqpOYa6G6Q7IFgB1Amh+s6u5Q/+q/sX19R2xBXrgQF5kjagu09E/xV3n4nY o90YUnJfbrFVM0In7qoV3nWU4Aey+erw8I54R6iaHwovFgO6mUnmbGvLAqiKpkZ9zAXv VBUdAIHUOinJqd3OT2MFZaxAtBR2GTvbcoJmUQ688iUUqaS+Z/X4dOhNY2YgUmVKsQcE I3Ww== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=DMORk3iCvLf5j+sjlnx5ZV2v9mtRZ96F5CuJJ7deRQU=; b=aHcroDNsPxo7tojVRafMaK3p3A3MfaYHPYqKe2M9SXGH7oyjz9dsHfnpeTI3op+Aob dHlrjSwtWaWWsbp8DnPgNndliFJq0PIgWi3eJsZx98KzbSan4qc0ui50j4SB9JwS6gnq FtLQ2qYQHDxIV3/zvtFUTfZT2n66aNyHgfd5Ra4DVP9oNcQ/AJBhTNsXGgQjpUy+0mIa R5nxsWwGtGJSMOlxtJYh58LejP2AF1SX2jtcGEO3ZuwZXPNYM4kX395YxVtdF0pR7Zip KzacPf+/WCGYVtJWEJ7XX9CZCEvn3FryRHHIMS+9Zgii/TXAPoBL6GGse3Y8nOGkciG3 bC4Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=iEKsSZfS; 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 t21si2171139ejj.855.2022.01.13.17.15.11; Thu, 13 Jan 2022 17:15:12 -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 header.s=20210112 header.b=iEKsSZfS; 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 D269A68B5D0; Fri, 14 Jan 2022 03:13:54 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pg1-f174.google.com (mail-pg1-f174.google.com [209.85.215.174]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id C848D68B59D for ; Fri, 14 Jan 2022 03:13:48 +0200 (EET) Received: by mail-pg1-f174.google.com with SMTP id v25so1433467pge.2 for ; Thu, 13 Jan 2022 17:13:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=QPt1g4d++6eqMGF272qoYSTMz7/wEE7bKAIBuuhcHL8=; b=iEKsSZfSrYmu4sBcD7jFUhRezS8eViAd83HYDxrkW7LneWjWTcEeudlL+VVwhIbzpf Dy4sdjAeKpGQgaUtiONHRKtvqWzeMGNI0GimHfJ6pRmnFkj9ShrLsJgpZwMroZy7kxSO 9Zt5mO12YIVKLJLck3ZGmne3t6WcPeHHKwHJV0PaOIdQmjCle8PJiwuJ5iiTLQfhmOEV mqQ5fZKNm778flr6dbs0Efue9XD0AgnhDfPjagAOjS1koG8j4aW8y+x+YNGlRJPLJI+F kVfrKHYVLiwQQ+hYxWyXH7JFPGAAXRlctafszTBZqOxCvKcaizTqupp+UwwgkPi50lBs VP2g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=QPt1g4d++6eqMGF272qoYSTMz7/wEE7bKAIBuuhcHL8=; b=2l+P9MCeahegBeRRiJ7BFIQ+tR2Sa3cOyXbH8X9i8l4iGw9k8kloeok0jOdgZGp5Dq R5cUkoFmwtRZ1XxjztOpypD98cT+wg0AEnKSm5zTcOStSkBdgdrXhWDhK8pQQGRgnfcu ORy0MzuOdAm1CldEP623D9eYooOWuHYJXTm6+5gsrSy9gsF7OXJETNAjS0p5o0a8xh1v avtWxTFdH4KptmvRG90QnGv6bMgO1rb4sC/QpIjAucqAepe/yLsRpRIVIMwK6jV8zX/m y6JPgB0/OKItcJK45HgXtLLzqqPncXfETAjw6+XXKBLQmDBSTbIh1433FhmhRg/n8pK8 LmyQ== X-Gm-Message-State: AOAM530Ybx3yEaShoQjbvlPZiZOBLjvhckaXu6d+Kn3zIUu9/mxDSuKS abkr1TNhQzUgL713lvooOJuG7x0cFJ2lWw== X-Received: by 2002:aa7:8658:0:b0:4c1:3a5c:3df6 with SMTP id a24-20020aa78658000000b004c13a5c3df6mr6662353pfo.58.1642122827023; Thu, 13 Jan 2022 17:13:47 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id z16sm3205008pgi.89.2022.01.13.17.13.46 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:46 -0800 (PST) Message-Id: <76a236404899811b17b09d8905b5cb586c6409ff.1642122814.git.ffmpegagent@gmail.com> In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:20 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 11/24] avfilter/avfilter: Fix hardcoded input index 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: PLsrTWLVGgpS From: softworkz This fix targets (rare) cases where multiple input pads have a .filter_frame function. ff_request_frame_to_filter needs to call ff_request_frame with the correct input pad instead of the hardcoded first one. Signed-off-by: softworkz --- libavfilter/avfilter.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index 75d5e86539..aa9aa71f53 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -463,7 +463,7 @@ static int64_t guess_status_pts(AVFilterContext *ctx, int status, AVRational lin return AV_NOPTS_VALUE; } -static int ff_request_frame_to_filter(AVFilterLink *link) +static int ff_request_frame_to_filter(AVFilterLink *link, int input_index) { int ret = -1; @@ -472,8 +472,8 @@ static int ff_request_frame_to_filter(AVFilterLink *link) link->frame_blocked_in = 1; if (link->srcpad->request_frame) ret = link->srcpad->request_frame(link); - else if (link->src->inputs[0]) - ret = ff_request_frame(link->src->inputs[0]); + else if (link->src->inputs[input_index]) + ret = ff_request_frame(link->src->inputs[input_index]); if (ret < 0) { if (ret != AVERROR(EAGAIN) && ret != link->status_in) ff_avfilter_link_set_in_status(link, ret, guess_status_pts(link->src, ret, link->time_base)); @@ -1172,6 +1172,14 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in) { unsigned out = 0, progress = 0; int ret; + int input_index = 0; + + for (int i = 0; i < in->dst->nb_inputs; i++) { + if (&in->dst->input_pads[i] == in->dstpad) { + input_index = i; + break; + } + } av_assert0(!in->status_out); if (!filter->nb_outputs) { @@ -1181,7 +1189,7 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in) while (!in->status_out) { if (!filter->outputs[out]->status_in) { progress++; - ret = ff_request_frame_to_filter(filter->outputs[out]); + ret = ff_request_frame_to_filter(filter->outputs[out], input_index); if (ret < 0) return ret; } @@ -1218,7 +1226,7 @@ static int ff_filter_activate_default(AVFilterContext *filter) for (i = 0; i < filter->nb_outputs; i++) { if (filter->outputs[i]->frame_wanted_out && !filter->outputs[i]->frame_blocked_in) { - return ff_request_frame_to_filter(filter->outputs[i]); + return ff_request_frame_to_filter(filter->outputs[i], 0); } } return FFERROR_NOT_READY; From patchwork Fri Jan 14 01:13:21 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33571 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp985739iog; Thu, 13 Jan 2022 17:15:21 -0800 (PST) X-Google-Smtp-Source: ABdhPJytR7CRknPKYva/8zeAiVQNHhmxxhp5mVGebbOwSP4U6FC38rKAQjyIQLFi5dss063cYBW7 X-Received: by 2002:a17:906:64f:: with SMTP id t15mr5584944ejb.218.1642122921300; Thu, 13 Jan 2022 17:15:21 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642122921; cv=none; d=google.com; s=arc-20160816; b=0bz77S9rsT3rBXlrGMoOxAOBYYfFuNeXpR8scE+lThDshdnwp+/3990qQcMl7gsooZ 37GeIkz8+7tknpy4M0SM1kWlaVymAVHZQMKZgBGu1F7bydOR7qstx8YnrANAlav/h9Vy H+AvZ2mkeUbVsp1YtwCsLhJTurHk27lLg36Sap+YSqx0W04vuWZlYTnT+Q8WVWqvB0Jc 5wo1ucwJu/ZQ6B/A9g28LI5urnWvq0/3HeFldsTpP3FJM5nFCNBAORlqQjf41KnJhMCJ /Bc9MIbWBeLpqTlceKl1IJuXRqOAwY+3NfF6e7YE5CIxmQhEhJCSd5bnVTCyLYVUYbp1 CbeA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=3QzRD6dJwMKOSBvI2vjcJDt/1U+H42LYDxQLb81ori8=; b=ak/rPSbXbQd0hm/tEnvzeXoSqEmhhYI3cbV0eSSlwX3b3UKAAhqGc4GEqXqZg/ydtc 7BHgU4fbz+XTqXqkklsJcOet8fETcaSVEr4Bu1+P/kqw/xhazqUf2nNHeJHjO7zROehT hiYEMYz/DzgMY83T0Nc8+BRQkTjRkUlWJ5HsXJ4r7cV8eKZtJoLjbsrw+xbHyT3LagLx 8AxCoMhse5LA9ppMXkgF1QA42YUi3L6luK/uB5QRqmm9aWWtGb2mqX6GJJrkMFX1qXNt PwDInOvSrco+fbgWZs0lT5mvYp6JBvYAfy+nzdt5cgshvjZS5qTBEYCD/0/3O2+KBsG8 1EJw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=C+oLPODD; 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 d2si2615125edm.64.2022.01.13.17.15.20; Thu, 13 Jan 2022 17:15:21 -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 header.s=20210112 header.b=C+oLPODD; 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 D61BD68B623; Fri, 14 Jan 2022 03:13:55 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f179.google.com (mail-pf1-f179.google.com [209.85.210.179]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 89B3168B5A4 for ; Fri, 14 Jan 2022 03:13:49 +0200 (EET) Received: by mail-pf1-f179.google.com with SMTP id i17so1397983pfk.11 for ; Thu, 13 Jan 2022 17:13:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=aA9BiLPufx8fjq8tQd/9mfsN2RMJ4EkElAISy3yUcMM=; b=C+oLPODDivCrEbBsCm7wSL6r/vlsJBmKsGz91NRlFn017jE8fHun9iKMyJyb2D+4uC O9r28fOfnTXSWR9FrUqTtIwR5vfLModXqSn/XeqQ+aDwiE5R8Uu0x1ZvaNdshq5fIF0L SpcXEqd+/HDORy8ctKtxsO47H+Pf0gbjVs4H9fCM1dx74y25Uca3UEJaysjI/mRzlwfT cj7lSeIm44bKMSaCNN+fq/LFaRN31NF2mvSxY+JiXe+vBYAocd2v4PJjiNtDs1wAcrFf riUe4curhw0NjmUxmp4XybnYxjVilftYopmHeBpVBc9LyGF5Fd6nSP6+qlKUgiyL7THE q9Fw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=aA9BiLPufx8fjq8tQd/9mfsN2RMJ4EkElAISy3yUcMM=; b=JCt46FEvnA8XNBu/9EQagHRCgQ4avwFU6uDpWvfUEeOTAnqI8T3zG6sLG6o3EGrlLf tY93oNLFdPxJWccmpxiVMZmUCsUFG8Gq2sYV7jQ0TJx8stJtDH6G8uOVzmYxVHm00aFz WAyTWqbVGwge5lENl2WawW3vVaqsVuj8dtGZg7lfqq3l7igYgm6X7ewNB5P4zRaDY9RM DfIY3HY+YnP39y3tdBfqWS8YhnA9rpG1U14Frh0dPNMWFiWdOF0ZZ2G3kQuwMBNU5U3f ZdHvY91rajkhjcaSmahNwkMhGiXQAnuy2fWk+M887OStMQ7G2AQX6uNmCKETXEUndYuU HeJA== X-Gm-Message-State: AOAM530T33fMJEb9R7oUPjUwEZIDpi1vNGMDjgtDkQdnmOopBxH/56hY a4s+8JdkOaM63uXCbOot1lMq8dC6LUTsUA== X-Received: by 2002:a65:5a41:: with SMTP id z1mr6003205pgs.585.1642122828024; Thu, 13 Jan 2022 17:13:48 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id h11sm3618329pjs.10.2022.01.13.17.13.47 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:47 -0800 (PST) Message-Id: In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:21 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 12/24] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: VG/hM661+/et From: softworkz Signed-off-by: softworkz --- configure | 2 +- libavfilter/allfilters.c | 2 ++ libavfilter/buffersink.c | 54 ++++++++++++++++++++++++++++++ libavfilter/buffersink.h | 7 ++++ libavfilter/buffersrc.c | 72 ++++++++++++++++++++++++++++++++++++++++ libavfilter/buffersrc.h | 1 + 6 files changed, 137 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 1413122d87..7c2931bd50 100755 --- a/configure +++ b/configure @@ -7854,7 +7854,7 @@ print_enabled_components(){ fi done if [ "$name" = "filter_list" ]; then - for c in asrc_abuffer vsrc_buffer asink_abuffer vsink_buffer; do + for c in asrc_abuffer vsrc_buffer ssrc_sbuffer asink_abuffer vsink_buffer ssink_sbuffer; do printf " &ff_%s,\n" $c >> $TMPH done fi diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 4325a3e557..8a3bd03924 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -556,8 +556,10 @@ extern const AVFilter ff_avsrc_movie; * being the same while having different 'types'). */ extern const AVFilter ff_asrc_abuffer; extern const AVFilter ff_vsrc_buffer; +extern const AVFilter ff_ssrc_sbuffer; extern const AVFilter ff_asink_abuffer; extern const AVFilter ff_vsink_buffer; +extern const AVFilter ff_ssink_sbuffer; extern const AVFilter ff_af_afifo; extern const AVFilter ff_vf_fifo; diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c index c0215669e7..0b268c2fa4 100644 --- a/libavfilter/buffersink.c +++ b/libavfilter/buffersink.c @@ -29,6 +29,8 @@ #include "libavutil/internal.h" #include "libavutil/opt.h" +#include "libavcodec/avcodec.h" + #define FF_INTERNAL_FIELDS 1 #include "framequeue.h" @@ -57,6 +59,10 @@ typedef struct BufferSinkContext { int *sample_rates; ///< list of accepted sample rates int sample_rates_size; + /* only used for subtitles */ + enum AVSubtitleType *subtitle_types; ///< list of accepted subtitle types, must be terminated with -1 + int subtitle_types_size; + AVFrame *peeked_frame; } BufferSinkContext; @@ -305,6 +311,28 @@ static int asink_query_formats(AVFilterContext *ctx) return 0; } +static int ssink_query_formats(AVFilterContext *ctx) +{ + BufferSinkContext *buf = ctx->priv; + AVFilterFormats *formats = NULL; + unsigned i; + int ret; + + CHECK_LIST_SIZE(subtitle_types) + if (buf->subtitle_types_size) { + for (i = 0; i < NB_ITEMS(buf->subtitle_types); i++) + if ((ret = ff_add_subtitle_type(&formats, buf->subtitle_types[i])) < 0) + return ret; + if ((ret = ff_set_common_formats(ctx, formats)) < 0) + return ret; + } else { + if ((ret = ff_default_query_formats(ctx)) < 0) + return ret; + } + + return 0; +} + #define OFFSET(x) offsetof(BufferSinkContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption buffersink_options[] = { @@ -322,9 +350,16 @@ static const AVOption abuffersink_options[] = { { NULL }, }; #undef FLAGS +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM +static const AVOption sbuffersink_options[] = { + { "subtitle_types", "set the supported subtitle formats", OFFSET(subtitle_types), AV_OPT_TYPE_BINARY, .flags = FLAGS }, + { NULL }, +}; +#undef FLAGS AVFILTER_DEFINE_CLASS(buffersink); AVFILTER_DEFINE_CLASS(abuffersink); +AVFILTER_DEFINE_CLASS(sbuffersink); static const AVFilterPad avfilter_vsink_buffer_inputs[] = { { @@ -363,3 +398,22 @@ const AVFilter ff_asink_abuffer = { .outputs = NULL, FILTER_QUERY_FUNC(asink_query_formats), }; + +static const AVFilterPad avfilter_ssink_sbuffer_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + }, +}; + +const AVFilter ff_ssink_sbuffer = { + .name = "sbuffersink", + .description = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them available to the end of the filter graph."), + .priv_class = &sbuffersink_class, + .priv_size = sizeof(BufferSinkContext), + .init = common_init, + .activate = activate, + FILTER_INPUTS(avfilter_ssink_sbuffer_inputs), + .outputs = NULL, + FILTER_QUERY_FUNC(ssink_query_formats), +}; diff --git a/libavfilter/buffersink.h b/libavfilter/buffersink.h index 69ed0f29a8..11905abdc5 100644 --- a/libavfilter/buffersink.h +++ b/libavfilter/buffersink.h @@ -129,6 +129,13 @@ typedef struct AVABufferSinkParams { */ attribute_deprecated AVABufferSinkParams *av_abuffersink_params_alloc(void); + +/** + * Deprecated and unused struct to use for initializing an sbuffersink context. + */ +typedef struct AVSBufferSinkParams { + const int *subtitle_type; +} AVSBufferSinkParams; #endif /** diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c index b0611872f1..d2362999a2 100644 --- a/libavfilter/buffersrc.c +++ b/libavfilter/buffersrc.c @@ -39,6 +39,7 @@ #include "formats.h" #include "internal.h" #include "video.h" +#include "libavcodec/avcodec.h" typedef struct BufferSourceContext { const AVClass *class; @@ -63,6 +64,9 @@ typedef struct BufferSourceContext { uint64_t channel_layout; char *channel_layout_str; + /* subtitle only */ + enum AVSubtitleType subtitle_type; + int eof; } BufferSourceContext; @@ -130,6 +134,13 @@ int av_buffersrc_parameters_set(AVFilterContext *ctx, AVBufferSrcParameters *par if (param->channel_layout) s->channel_layout = param->channel_layout; break; + case AVMEDIA_TYPE_SUBTITLE: + s->subtitle_type = param->format; + if (param->width > 0) + s->w = param->width; + if (param->height > 0) + s->h = param->height; + break; default: return AVERROR_BUG; } @@ -197,6 +208,8 @@ int attribute_align_arg av_buffersrc_add_frame_flags(AVFilterContext *ctx, AVFra CHECK_AUDIO_PARAM_CHANGE(ctx, s, frame->sample_rate, frame->channel_layout, frame->channels, frame->format, frame->pts); break; + case AVMEDIA_TYPE_SUBTITLE: + break; default: return AVERROR(EINVAL); } @@ -269,6 +282,7 @@ unsigned av_buffersrc_get_nb_failed_requests(AVFilterContext *buffer_src) #define OFFSET(x) offsetof(BufferSourceContext, x) #define A AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM #define V AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM +#define S AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM static const AVOption buffer_options[] = { { "width", NULL, OFFSET(w), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, V }, @@ -298,6 +312,16 @@ static const AVOption abuffer_options[] = { AVFILTER_DEFINE_CLASS(abuffer); +static const AVOption sbuffer_options[] = { + { "time_base", NULL, OFFSET(time_base), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, INT_MAX, S }, + { "subtitle_type", NULL, OFFSET(subtitle_type), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, S }, + { "width", NULL, OFFSET(w), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, V }, + { "height", NULL, OFFSET(h), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, V }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(sbuffer); + static av_cold int init_audio(AVFilterContext *ctx) { BufferSourceContext *s = ctx->priv; @@ -347,6 +371,21 @@ static av_cold int init_audio(AVFilterContext *ctx) return ret; } +static av_cold int init_subtitle(AVFilterContext *ctx) +{ + BufferSourceContext *c = ctx->priv; + + if (c->subtitle_type == AV_SUBTITLE_FMT_BITMAP) + av_log(ctx, AV_LOG_VERBOSE, "graphical subtitles - w:%d h:%d tb:%d/%d\n", + c->w, c->h, c->time_base.num, c->time_base.den); + else + av_log(ctx, AV_LOG_VERBOSE, "text subtitles - w:%d h:%d tb:%d/%d\n", + c->w, c->h, c->time_base.num, c->time_base.den); + + return 0; +} + + static av_cold void uninit(AVFilterContext *ctx) { BufferSourceContext *s = ctx->priv; @@ -381,6 +420,11 @@ static int query_formats(AVFilterContext *ctx) if ((ret = ff_set_common_channel_layouts(ctx, channel_layouts)) < 0) return ret; break; + case AVMEDIA_TYPE_SUBTITLE: + if ((ret = ff_add_format (&formats, c->subtitle_type)) < 0 || + (ret = ff_set_common_formats (ctx , formats )) < 0) + return ret; + break; default: return AVERROR(EINVAL); } @@ -408,6 +452,11 @@ static int config_props(AVFilterLink *link) if (!c->channel_layout) c->channel_layout = link->channel_layout; break; + case AVMEDIA_TYPE_SUBTITLE: + link->format = c->subtitle_type; + link->w = c->w; + link->h = c->h; + break; default: return AVERROR(EINVAL); } @@ -472,3 +521,26 @@ const AVFilter ff_asrc_abuffer = { FILTER_QUERY_FUNC(query_formats), .priv_class = &abuffer_class, }; + +static const AVFilterPad ssrc_sbuffer_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .request_frame = request_frame, + .config_props = config_props, + }, +}; + +const AVFilter ff_ssrc_sbuffer = { + .name = "sbuffer", + .description = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them accessible to the filterchain."), + .priv_size = sizeof(BufferSourceContext), + + .init = init_subtitle, + .uninit = uninit, + + .inputs = NULL, + FILTER_OUTPUTS(ssrc_sbuffer_outputs), + FILTER_QUERY_FUNC(query_formats), + .priv_class = &sbuffer_class, +}; diff --git a/libavfilter/buffersrc.h b/libavfilter/buffersrc.h index 08fbd18a47..929a2fa249 100644 --- a/libavfilter/buffersrc.h +++ b/libavfilter/buffersrc.h @@ -74,6 +74,7 @@ typedef struct AVBufferSrcParameters { /** * video: the pixel format, value corresponds to enum AVPixelFormat * audio: the sample format, value corresponds to enum AVSampleFormat + * subtitles: the subtitle format, value corresponds to enum AVSubtitleType */ int format; /** From patchwork Fri Jan 14 01:13:22 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33575 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp986253iog; Thu, 13 Jan 2022 17:16:02 -0800 (PST) X-Google-Smtp-Source: ABdhPJzO5VqnKRRNPxrSkBPjcw983vrlQQm/0JPJ9LcPQ5L3Wqf+wp0K4Vi+gZQCCLoek9P59vBp X-Received: by 2002:a17:907:da1:: with SMTP id go33mr5391342ejc.491.1642122961868; Thu, 13 Jan 2022 17:16:01 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642122961; cv=none; d=google.com; s=arc-20160816; b=vSZ00x4Ma8iwn5YN+eqX6HhMNYYe0v7K71RX6dS+hunW+3fs946MFogDiZJp4p3/n5 VAPluipG/TEFKyR2X08SvoXgqTHgXB9/DuNeaoZSK3JinjNlerQMrdZcSnCqBZsGiYxG Zq7lVO7CZpnG7vkkAOX8Eb99t9fckpuFyxHQVxeFKHOdMLiiDMMELqHhEXkeSa0n83lX nbYcn6bkiMQ0iLKYVP12sjrV21XjrY+Mxdb1Qtik1mWvFCaN6T3A7NfE3CrdxuqDdMX/ YkNCWwYHbAccYe3XA932qCZBev5/a0KKZo2vIUjiXsZUCm7fU/SU4Q+N99ijJtTH2kLr QdUg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=SkrPxF/Vd8/I3blVmgampPFZjDxhj7+0kZRmjTHDZbA=; b=zMbbLVkGSNfD2ujXeTfsDAQyE47J8b0vB5dgJyULeotg7vcuAx68y0TY/RN1TjBL5f Z95iBoDNcvyYffkgzUNBQrLXObnHMH3tQ38Xy0aEbVoVIe47lVKPMEmjizeYFjflbONL DxVX2+UEahmkzZxnIb+KXTSxYTeNA5Jb4hCadQNab2oBkmZ7Q/4vCpSmg/TV5Kxn4Scg ojsJbIdXTtewRJDFk9nChBehlO30BfY2ih+4FLfL8io+Gd4CqlRjhneqXRdRNWd9zWIb k2ntwTkuTX6phPC198l2Vv5myfpz4naPdMfNpuXIcRA2C/cOpMgFbZ1Kzxyf8JIzY/rP nqVQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=WkLzHCAc; 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 g18si2297478edu.512.2022.01.13.17.16.01; Thu, 13 Jan 2022 17:16:01 -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 header.s=20210112 header.b=WkLzHCAc; 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 CD85168B663; Fri, 14 Jan 2022 03:13:59 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pj1-f52.google.com (mail-pj1-f52.google.com [209.85.216.52]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5EEC668B5D8 for ; Fri, 14 Jan 2022 03:13:51 +0200 (EET) Received: by mail-pj1-f52.google.com with SMTP id r16-20020a17090a0ad000b001b276aa3aabso20617950pje.0 for ; Thu, 13 Jan 2022 17:13:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=lx9KhJ1i6IPXiTSHabJhMttvaAOJldRF7o2cVo3jzV0=; b=WkLzHCAcvUXeh9M0upeGUiZp/sr7EGRcOebfvYaE7RoMsQ6G29ADBUaxmJAyJR3bXr ZG7uY4Vo7baRgoa9D2TIfhqYkOZ9lmje50lCXKK9H+/crQNrDj1sa3cYU1kPU/KJiu4F xch94DH5IKtksfe8bXNweNvLKRTkse94CYKE8yT6zJBrAPRXERbSF1ynTwud5cua/c4t Z0Rpew2qnGPM5NamBmMUCHns6qVwrjHR5YzhLffU71Cq49zxsrcDGQWE2OOiIRrdKzni no72/ZjoF/veu0mLGAUMYughCll+yamBvgrRwhtFHKFHGqMXxhcxIbKwQCgF5J2pzIe4 5xPg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=lx9KhJ1i6IPXiTSHabJhMttvaAOJldRF7o2cVo3jzV0=; b=wmQxZ8CaIGi8kRG7EHtvblDT5y4GfVpUdNd3Ca8ZJ8CJxYCBCAQBC7jkt9S5wnr1mW tbxisk6zJotKEtIkhvaTIOK8/8w7BIcVf6/r/7+6t+a9fqgtVpn8XdWtW9oTX004ZcAB 32P8+Q32hpdKY/cma28y8IRb25GjwNFF5XzN/6aKPJ5w3qwrG27rQLgGaIdM1lPocW3h l4pP9i3dN2BOwAS4czmyaulRLPU2LlvV9z8VwoUfs1gmiQHMGMs9tqOojPnlQGkh7zRQ nSXgziN+bb3QgoW4incrUauVWFyBWOxN4HF9rI3afmQJootTLO1A4xMDfMS8n9JNUoMl 5yAQ== X-Gm-Message-State: AOAM531ZLG9yUrwoqh+5GFbawyfvnDJ7d8NX2N6GPDhAXkJwQYeNMZsv qz2DjlOcJ5l0IbLDmOslhFgbkZ+qYgL6eg== X-Received: by 2002:a17:90a:a588:: with SMTP id b8mr17668409pjq.25.1642122829324; Thu, 13 Jan 2022 17:13:49 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id h7sm4062396pfc.152.2022.01.13.17.13.48 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:48 -0800 (PST) Message-Id: In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:22 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 13/24] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: aaUpoAAXAgUo From: softworkz - overlaygraphicsubs (VS -> V) Overlay graphic subtitles onto a video stream - graphicsub2video {S -> V) Converts graphic subtitles to video frames (with alpha) Gets auto-inserted for retaining compatibility with sub2video command lines Signed-off-by: softworkz --- doc/filters.texi | 118 +++++ libavfilter/Makefile | 2 + libavfilter/allfilters.c | 2 + libavfilter/vf_overlaygraphicsubs.c | 765 ++++++++++++++++++++++++++++ 4 files changed, 887 insertions(+) create mode 100644 libavfilter/vf_overlaygraphicsubs.c diff --git a/doc/filters.texi b/doc/filters.texi index 05d4b1a56e..4fc4a57dbb 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25713,6 +25713,124 @@ tools. @c man end VIDEO SINKS +@chapter Subtitle Filters +@c man begin SUBTITLE FILTERS + +When you configure your FFmpeg build, you can disable any of the +existing filters using @code{--disable-filters}. + +Below is a description of the currently available subtitle filters. + +@section graphicsub2video + +Renders graphic subtitles as video frames. + +This filter replaces the previous "sub2video" hack which did the conversion implicitly and up-front as subtitle filtering wasn't possible at that time. +To retain compatibility with earlier sub2video command lines, this filter is being auto-inserted in those cases. + +For overlaying graphicsal subtitles it is recommended to use the 'overlay_graphicsubs' filter which is more efficient and takes less processing resources. + +This filter is still useful in cases where the overlay is done with hardware acceleration (e.g. overlay_qsv, overlay_vaapi, overlay_cuda) for preparing the overlay frames. + +Inputs: +@itemize +@item 0: Subtitles [BITMAP] +@end itemize + +Outputs: +@itemize +@item 0: Video [RGB32] +@end itemize + + +It accepts the following parameters: + +@table @option +@item size, s +Set the size of the output video frame. + +@end table + +@subsection Examples + +@itemize +@item +Overlay PGS subtitles +(not recommended - better use overlay_graphicsubs) +@example +ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:1]graphicsub2video[subs];[0:0][subs]overlay" output.mp4 +@end example + +@item +Overlay PGS subtitles implicitly +The graphicsub2video is inserted automatically for compatibility with legacy command lines. +@example +ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlay" output.mp4 +@end example +@end itemize + +@section overlaygraphicsubs + +Overlay graphic subtitles onto a video stream. + +This filter can blend graphical subtitles on a video stream directly, i.e. without creating full-size alpha images first. +The blending operation is limited to the area of the subtitle rectangles, which also means that no processing is done at times where no subtitles are to be displayed. + +Inputs: +@itemize +@item 0: Video [YUV420P, YUV422P, YUV444P, ARGB, RGBA, ABGR, BGRA, RGB24, BGR24] +@item 1: Subtitles [BITMAP] +@end itemize + +Outputs: +@itemize +@item 0: Video (same as input) +@end itemize + +It accepts the following parameters: + +@table @option +@item x +@item y +Set the expression for the x and y coordinates of the overlaid video +on the main video. Default value is "0" for both expressions. In case +the expression is invalid, it is set to a huge value (meaning that the +overlay will not be displayed within the output visible area). + +@item eof_action +See @ref{framesync}. + +@item eval +Set when the expressions for @option{x}, and @option{y} are evaluated. + +It accepts the following values: +@table @samp +@item init +only evaluate expressions once during the filter initialization or +when a command is processed + +@item frame +evaluate expressions for each incoming frame +@end table + +Default value is @samp{frame}. + +@item shortest +See @ref{framesync}. + +@end table + +@subsection Examples + +@itemize +@item +Overlay PGS subtitles +@example +ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlaygraphicsubs" output.mp4 +@end example +@end itemize +@c man end SUBTITLE FILTERS + @chapter Multimedia Filters @c man begin MULTIMEDIA FILTERS diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 283dd436cd..a372effc12 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -298,6 +298,7 @@ OBJS-$(CONFIG_GBLUR_FILTER) += vf_gblur.o OBJS-$(CONFIG_GBLUR_VULKAN_FILTER) += vf_gblur_vulkan.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_GEQ_FILTER) += vf_geq.o OBJS-$(CONFIG_GRADFUN_FILTER) += vf_gradfun.o +OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER) += vf_overlaygraphicsubs.o framesync.o OBJS-$(CONFIG_GRAPHMONITOR_FILTER) += f_graphmonitor.o OBJS-$(CONFIG_GRAYWORLD_FILTER) += vf_grayworld.o OBJS-$(CONFIG_GREYEDGE_FILTER) += vf_colorconstancy.o @@ -378,6 +379,7 @@ OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER) += vf_overlay_opencl.o opencl.o \ opencl/overlay.o framesync.o OBJS-$(CONFIG_OVERLAY_QSV_FILTER) += vf_overlay_qsv.o framesync.o OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER) += vf_overlay_vulkan.o vulkan.o vulkan_filter.o +OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER) += vf_overlaygraphicsubs.o framesync.o OBJS-$(CONFIG_OWDENOISE_FILTER) += vf_owdenoise.o OBJS-$(CONFIG_PAD_FILTER) += vf_pad.o OBJS-$(CONFIG_PAD_OPENCL_FILTER) += vf_pad_opencl.o opencl.o opencl/pad.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 8a3bd03924..eb4e30a270 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -358,6 +358,7 @@ extern const AVFilter ff_vf_oscilloscope; extern const AVFilter ff_vf_overlay; extern const AVFilter ff_vf_overlay_opencl; extern const AVFilter ff_vf_overlay_qsv; +extern const AVFilter ff_vf_overlaygraphicsubs; extern const AVFilter ff_vf_overlay_vulkan; extern const AVFilter ff_vf_overlay_cuda; extern const AVFilter ff_vf_owdenoise; @@ -545,6 +546,7 @@ extern const AVFilter ff_avf_showvolume; extern const AVFilter ff_avf_showwaves; extern const AVFilter ff_avf_showwavespic; extern const AVFilter ff_vaf_spectrumsynth; +extern const AVFilter ff_svf_graphicsub2video; /* multimedia sources */ extern const AVFilter ff_avsrc_amovie; diff --git a/libavfilter/vf_overlaygraphicsubs.c b/libavfilter/vf_overlaygraphicsubs.c new file mode 100644 index 0000000000..7b26d8ef37 --- /dev/null +++ b/libavfilter/vf_overlaygraphicsubs.c @@ -0,0 +1,765 @@ +/* + * Copyright (c) 2021 softworkz (derived from vf_overlay) + * Copyright (c) 2010 Stefano Sabatini + * Copyright (c) 2010 Baptiste Coudurier + * Copyright (c) 2007 Bobby Bingham + * + * 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 + * overlay graphical subtitles on top of a video frame + */ + +#include "libavutil/eval.h" +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" +#include "internal.h" +#include "drawutils.h" +#include "framesync.h" + +enum var_name { + VAR_MAIN_W, VAR_MW, + VAR_MAIN_H, VAR_MH, + VAR_OVERLAY_W, VAR_OW, + VAR_OVERLAY_H, VAR_OH, + VAR_HSUB, + VAR_VSUB, + VAR_X, + VAR_Y, + VAR_N, + VAR_POS, + VAR_T, + VAR_VARS_NB +}; + +typedef struct OverlaySubsContext { + const AVClass *class; + int x, y; ///< position of overlaid picture + int w, h; + AVFrame *outpicref; + + int main_is_packed_rgb; + uint8_t main_rgba_map[4]; + int main_has_alpha; + uint8_t overlay_rgba_map[4]; + int eval_mode; ///< EvalMode + int use_caching; + AVFrame *cache_frame; + + FFFrameSync fs; + + int main_pix_step[4]; ///< steps per pixel for each plane of the main output + int hsub, vsub; ///< chroma subsampling values + const AVPixFmtDescriptor *main_desc; ///< format descriptor for main input + + double var_values[VAR_VARS_NB]; + char *x_expr, *y_expr; + + AVExpr *x_pexpr, *y_pexpr; + + int pic_counter; +} OverlaySubsContext; + +static const char *const var_names[] = { + "main_w", "W", ///< width of the main video + "main_h", "H", ///< height of the main video + "overlay_w", "w", ///< width of the overlay video + "overlay_h", "h", ///< height of the overlay video + "hsub", + "vsub", + "x", + "y", + "n", ///< number of frame + "pos", ///< position in the file + "t", ///< timestamp expressed in seconds + NULL +}; + +#define MAIN 0 +#define OVERLAY 1 + +#define R 0 +#define G 1 +#define B 2 +#define A 3 + +#define Y 0 +#define U 1 +#define V 2 + +enum EvalMode { + EVAL_MODE_INIT, + EVAL_MODE_FRAME, + EVAL_MODE_NB +}; + +static av_cold void overlay_graphicsubs_uninit(AVFilterContext *ctx) +{ + OverlaySubsContext *s = ctx->priv; + + av_frame_free(&s->cache_frame); + ff_framesync_uninit(&s->fs); + av_expr_free(s->x_pexpr); s->x_pexpr = NULL; + av_expr_free(s->y_pexpr); s->y_pexpr = NULL; +} + +static inline int normalize_xy(double d, int chroma_sub) +{ + if (isnan(d)) + return INT_MAX; + return (int)d & ~((1 << chroma_sub) - 1); +} + +static void eval_expr(AVFilterContext *ctx) +{ + OverlaySubsContext *s = ctx->priv; + + s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL); + s->var_values[VAR_Y] = av_expr_eval(s->y_pexpr, s->var_values, NULL); + /* It is necessary if x is expressed from y */ + s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL); + s->x = normalize_xy(s->var_values[VAR_X], s->hsub); + s->y = normalize_xy(s->var_values[VAR_Y], s->vsub); +} + +static int set_expr(AVExpr **pexpr, const char *expr, const char *option, void *log_ctx) +{ + int ret; + AVExpr *old = NULL; + + if (*pexpr) + old = *pexpr; + ret = av_expr_parse(pexpr, expr, var_names, + NULL, NULL, NULL, NULL, 0, log_ctx); + if (ret < 0) { + av_log(log_ctx, AV_LOG_ERROR, + "Error when evaluating the expression '%s' for %s\n", + expr, option); + *pexpr = old; + return ret; + } + + av_expr_free(old); + return 0; +} + +static int overlay_graphicsubs_query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats; + AVFilterLink *inlink0 = ctx->inputs[0]; + AVFilterLink *inlink1 = ctx->inputs[1]; + AVFilterLink *outlink = ctx->outputs[0]; + int ret; + static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE }; + static const enum AVPixelFormat supported_pix_fmts[] = { + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_ARGB, AV_PIX_FMT_RGBA, + AV_PIX_FMT_ABGR, AV_PIX_FMT_BGRA, + AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, + AV_PIX_FMT_NONE + }; + + /* set input0 video formats */ + formats = ff_make_format_list(supported_pix_fmts); + if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0) + return ret; + + /* set output0 video formats */ + if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0) + return ret; + + /* set input1 subtitle formats */ + formats = ff_make_format_list(subtitle_fmts); + if ((ret = ff_formats_ref(formats, &inlink1->outcfg.formats)) < 0) + return ret; + + return 0; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + OverlaySubsContext *s = ctx->priv; + int ret; + + if ((ret = ff_framesync_init_dualinput(&s->fs, ctx)) < 0) + return ret; + + outlink->w = ctx->inputs[MAIN]->w; + outlink->h = ctx->inputs[MAIN]->h; + outlink->time_base = ctx->inputs[MAIN]->time_base; + outlink->frame_rate = ctx->inputs[MAIN]->frame_rate; + + return ff_framesync_configure(&s->fs); +} + +// divide by 255 and round to nearest +// apply a fast variant: (X+127)/255 = ((X+127)*257+257)>>16 = ((X+128)*257)>>16 +#define FAST_DIV255(x) ((((x) + 128) * 257) >> 16) + +// calculate the non-pre-multiplied alpha, applying the general equation: +// alpha = alpha_overlay / ( (alpha_main + alpha_overlay) - (alpha_main * alpha_overlay) ) +// (((x) << 16) - ((x) << 9) + (x)) is a faster version of: 255 * 255 * x +// ((((x) + (y)) << 8) - ((x) + (y)) - (y) * (x)) is a faster version of: 255 * (x + y) +#define UNPREMULTIPLY_ALPHA(x, y) ((((x) << 16) - ((x) << 9) + (x)) / ((((x) + (y)) << 8) - ((x) + (y)) - (y) * (x))) + +/** + * Blend image in src to destination buffer dst at position (x, y). + */ +static av_always_inline void blend_packed_rgb(const AVFilterContext *ctx, + const AVFrame *dst, const AVSubtitleArea *src, + int x, int y, + int is_straight) +{ + OverlaySubsContext *s = ctx->priv; + int i, imax, j, jmax; + const int src_w = src->w; + const int src_h = src->h; + const int dst_w = dst->width; + const int dst_h = dst->height; + uint8_t alpha; ///< the amount of overlay to blend on to main + const int dr = s->main_rgba_map[R]; + const int dg = s->main_rgba_map[G]; + const int db = s->main_rgba_map[B]; + const int da = s->main_rgba_map[A]; + const int dstep = s->main_pix_step[0]; + const int sr = s->overlay_rgba_map[R]; + const int sg = s->overlay_rgba_map[G]; + const int sb = s->overlay_rgba_map[B]; + const int sa = s->overlay_rgba_map[A]; + int slice_start, slice_end; + uint8_t *S, *sp, *d, *dp; + + i = FFMAX(-y, 0); + imax = FFMIN3(-y + dst_h, FFMIN(src_h, dst_h), y + src_h); + + slice_start = i; + slice_end = i + imax; + + sp = src->buf[0]->data + slice_start * src->linesize[0]; + dp = dst->data[0] + (slice_start + y) * dst->linesize[0]; + + for (i = slice_start; i < slice_end; i++) { + j = FFMAX(-x, 0); + S = sp + j; + d = dp + ((x + j) * dstep); + + for (jmax = FFMIN(-x + dst_w, src_w); j < jmax; j++) { + uint32_t val = src->pal[*S]; + const uint8_t *sval = (uint8_t *)&val; + alpha = sval[sa]; + + // if the main channel has an alpha channel, alpha has to be calculated + // to create an un-premultiplied (straight) alpha value + if (s->main_has_alpha && alpha != 0 && alpha != 255) { + const uint8_t alpha_d = d[da]; + alpha = UNPREMULTIPLY_ALPHA(alpha, alpha_d); + } + + switch (alpha) { + case 0: + break; + case 255: + d[dr] = sval[sr]; + d[dg] = sval[sg]; + d[db] = sval[sb]; + break; + default: + // main_value = main_value * (1 - alpha) + overlay_value * alpha + // since alpha is in the range 0-255, the result must divided by 255 + d[dr] = is_straight ? FAST_DIV255(d[dr] * (255 - alpha) + sval[sr] * alpha) : + FFMIN(FAST_DIV255(d[dr] * (255 - alpha)) + sval[sr], 255); + d[dg] = is_straight ? FAST_DIV255(d[dg] * (255 - alpha) + sval[sg] * alpha) : + FFMIN(FAST_DIV255(d[dg] * (255 - alpha)) + sval[sg], 255); + d[db] = is_straight ? FAST_DIV255(d[db] * (255 - alpha) + sval[sb] * alpha) : + FFMIN(FAST_DIV255(d[db] * (255 - alpha)) + sval[sb], 255); + } + + if (s->main_has_alpha) { + switch (alpha) { + case 0: + break; + case 255: + d[da] = sval[sa]; + break; + default: + // apply alpha compositing: main_alpha += (1-main_alpha) * overlay_alpha + d[da] += FAST_DIV255((255 - d[da]) * S[sa]); + } + } + d += dstep; + S += 1; + } + dp += dst->linesize[0]; + sp += src->linesize[0]; + } +} + +static av_always_inline void blend_plane_8_8bits(const AVFilterContext *ctx, const AVFrame *dst, const AVSubtitleArea *area, + const uint32_t *yuv_pal, int src_w, int src_h, int dst_w, int dst_h, int plane, int hsub, int vsub, + int x, int y, int dst_plane, int dst_offset, int dst_step) +{ + const int src_wp = AV_CEIL_RSHIFT(src_w, hsub); + const int src_hp = AV_CEIL_RSHIFT(src_h, vsub); + const int dst_wp = AV_CEIL_RSHIFT(dst_w, hsub); + const int dst_hp = AV_CEIL_RSHIFT(dst_h, vsub); + const int yp = y >> vsub; + const int xp = x >> hsub; + uint8_t *s, *sp, *d, *dp, *dap; + int imax, i, j, jmax; + int slice_start, slice_end; + + i = FFMAX(-yp, 0); \ + imax = FFMIN3(-yp + dst_hp, FFMIN(src_hp, dst_hp), yp + src_hp); \ + + slice_start = i; + slice_end = i + imax; + + sp = area->buf[0]->data + (slice_start << vsub) * area->linesize[0]; + dp = dst->data[dst_plane] + (yp + slice_start) * dst->linesize[dst_plane] + dst_offset; + + dap = dst->data[3] + ((yp + slice_start) << vsub) * dst->linesize[3]; + + for (i = slice_start; i < slice_end; i++) { + j = FFMAX(-xp, 0); + d = dp + (xp + j) * dst_step; + s = sp + (j << hsub); + jmax = FFMIN(-xp + dst_wp, src_wp); + + for (; j < jmax; j++) { + uint32_t val = yuv_pal[*s]; + const uint8_t *sval = (uint8_t *)&val; + const int alpha = sval[3]; + const int max = 255, mid = 128; + const int d_int = *d; + const int sval_int = sval[plane]; + + switch (alpha) { + case 0: + break; + case 255: + *d = sval[plane]; + break; + default: + if (plane > 0) + *d = av_clip(FAST_DIV255((d_int - mid) * (max - alpha) + (sval_int - mid) * alpha) , -mid, mid) + mid; + else + *d = FAST_DIV255(d_int * (max - alpha) + sval_int * alpha); + break; + } + + d += dst_step; + s += 1 << hsub; + } + dp += dst->linesize[dst_plane]; + sp += (1 << vsub) * area->linesize[0]; + dap += (1 << vsub) * dst->linesize[3]; + } +} + +#define RGB2Y(r, g, b) (uint8_t)(((66 * (r) + 129 * (g) + 25 * (b) + 128) >> 8) + 16) +#define RGB2U(r, g, b) (uint8_t)(((-38 * (r) - 74 * (g) + 112 * (b) + 128) >> 8) + 128) +#define RGB2V(r, g, b) (uint8_t)(((112 * (r) - 94 * (g) - 18 * (b) + 128) >> 8) + 128) +/* Converts R8 G8 B8 color to YUV. */ +static av_always_inline void rgb_2_yuv(uint8_t r, uint8_t g, uint8_t b, uint8_t* y, uint8_t* u, uint8_t* v) +{ + *y = RGB2Y((int)r, (int)g, (int)b); + *u = RGB2U((int)r, (int)g, (int)b); + *v = RGB2V((int)r, (int)g, (int)b); +} + + +static av_always_inline void blend_yuv_8_8bits(AVFilterContext *ctx, AVFrame *dst, const AVSubtitleArea *area, int hsub, int vsub, int x, int y) +{ + OverlaySubsContext *s = ctx->priv; + const int src_w = area->w; + const int src_h = area->h; + const int dst_w = dst->width; + const int dst_h = dst->height; + const int sr = s->overlay_rgba_map[R]; + const int sg = s->overlay_rgba_map[G]; + const int sb = s->overlay_rgba_map[B]; + const int sa = s->overlay_rgba_map[A]; + uint32_t yuvpal[256]; + + for (int i = 0; i < 256; ++i) { + const uint8_t *rgba = (const uint8_t *)&area->pal[i]; + uint8_t *yuva = (uint8_t *)&yuvpal[i]; + rgb_2_yuv(rgba[sr], rgba[sg], rgba[sb], &yuva[Y], &yuva[U], &yuva[V]); + yuva[3] = rgba[sa]; + } + + blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, Y, 0, 0, x, y, s->main_desc->comp[Y].plane, s->main_desc->comp[Y].offset, s->main_desc->comp[Y].step); + blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, U, hsub, vsub, x, y, s->main_desc->comp[U].plane, s->main_desc->comp[U].offset, s->main_desc->comp[U].step); + blend_plane_8_8bits(ctx, dst, area, yuvpal, src_w, src_h, dst_w, dst_h, V, hsub, vsub, x, y, s->main_desc->comp[V].plane, s->main_desc->comp[V].offset, s->main_desc->comp[V].step); +} + +static int config_input_main(AVFilterLink *inlink) +{ + int ret; + AVFilterContext *ctx = inlink->dst; + OverlaySubsContext *s = inlink->dst->priv; + const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format); + + av_image_fill_max_pixsteps(s->main_pix_step, NULL, pix_desc); + ff_fill_rgba_map(s->overlay_rgba_map, AV_PIX_FMT_RGB32); // it's actually AV_PIX_FMT_PAL8); + + s->hsub = pix_desc->log2_chroma_w; + s->vsub = pix_desc->log2_chroma_h; + + s->main_desc = pix_desc; + + s->main_is_packed_rgb = ff_fill_rgba_map(s->main_rgba_map, inlink->format) >= 0; + s->main_has_alpha = !!(pix_desc->flags & AV_PIX_FMT_FLAG_ALPHA); + + /* Finish the configuration by evaluating the expressions + now when both inputs are configured. */ + s->var_values[VAR_MAIN_W ] = s->var_values[VAR_MW] = ctx->inputs[MAIN ]->w; + s->var_values[VAR_MAIN_H ] = s->var_values[VAR_MH] = ctx->inputs[MAIN ]->h; + s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = ctx->inputs[OVERLAY]->w; + s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = ctx->inputs[OVERLAY]->h; + s->var_values[VAR_HSUB] = 1<log2_chroma_w; + s->var_values[VAR_VSUB] = 1<log2_chroma_h; + s->var_values[VAR_X] = NAN; + s->var_values[VAR_Y] = NAN; + s->var_values[VAR_N] = 0; + s->var_values[VAR_T] = NAN; + s->var_values[VAR_POS] = NAN; + + if ((ret = set_expr(&s->x_pexpr, s->x_expr, "x", ctx)) < 0 || + (ret = set_expr(&s->y_pexpr, s->y_expr, "y", ctx)) < 0) + return ret; + + if (s->eval_mode == EVAL_MODE_INIT) { + eval_expr(ctx); + av_log(ctx, AV_LOG_VERBOSE, "x:%f xi:%d y:%f yi:%d\n", + s->var_values[VAR_X], s->x, + s->var_values[VAR_Y], s->y); + } + + av_log(ctx, AV_LOG_VERBOSE, + "main w:%d h:%d fmt:%s overlay w:%d h:%d fmt:%s\n", + ctx->inputs[MAIN]->w, ctx->inputs[MAIN]->h, + av_get_pix_fmt_name(ctx->inputs[MAIN]->format), + ctx->inputs[OVERLAY]->w, ctx->inputs[OVERLAY]->h, + av_get_pix_fmt_name(ctx->inputs[OVERLAY]->format)); + return 0; +} + +static int do_blend(FFFrameSync *fs) +{ + AVFilterContext *ctx = fs->parent; + AVFrame *mainpic, *second; + OverlaySubsContext *s = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + unsigned i; + int ret; + + ret = ff_framesync_dualinput_get_writable(fs, &mainpic, &second); + if (ret < 0) + return ret; + if (!second) + return ff_filter_frame(ctx->outputs[0], mainpic); + + if (s->eval_mode == EVAL_MODE_FRAME) { + int64_t pos = mainpic->pkt_pos; + + s->var_values[VAR_N] = (double)inlink->frame_count_out; + s->var_values[VAR_T] = mainpic->pts == AV_NOPTS_VALUE ? + NAN :(double)mainpic->pts * av_q2d(inlink->time_base); + s->var_values[VAR_POS] = pos == -1 ? NAN : (double)pos; + + s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = second->width; + s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = second->height; + s->var_values[VAR_MAIN_W ] = s->var_values[VAR_MW] = mainpic->width; + s->var_values[VAR_MAIN_H ] = s->var_values[VAR_MH] = mainpic->height; + + eval_expr(ctx); + av_log(ctx, AV_LOG_DEBUG, "n:%f t:%f pos:%f x:%f xi:%d y:%f yi:%d\n", + s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_POS], + s->var_values[VAR_X], s->x, + s->var_values[VAR_Y], s->y); + } + + for (i = 0; i < second->num_subtitle_areas; i++) { + const AVSubtitleArea *sub_area = second->subtitle_areas[i]; + + if (sub_area->type != AV_SUBTITLE_FMT_BITMAP) { + av_log(NULL, AV_LOG_WARNING, "overlay_graphicsubs: non-bitmap subtitle\n"); + return AVERROR_INVALIDDATA; + } + + switch (inlink->format) { + case AV_PIX_FMT_YUV420P: + blend_yuv_8_8bits(ctx, mainpic, sub_area, 1, 1, sub_area->x + s->x, sub_area->y + s->y); + break; + case AV_PIX_FMT_YUV422P: + blend_yuv_8_8bits(ctx, mainpic, sub_area, 1, 0, sub_area->x + s->x, sub_area->y + s->y); + break; + case AV_PIX_FMT_YUV444P: + blend_yuv_8_8bits(ctx, mainpic, sub_area, 0, 0, sub_area->x + s->x, sub_area->y + s->y); + break; + case AV_PIX_FMT_RGB24: + case AV_PIX_FMT_BGR24: + case AV_PIX_FMT_ARGB: + case AV_PIX_FMT_RGBA: + case AV_PIX_FMT_BGRA: + case AV_PIX_FMT_ABGR: + blend_packed_rgb(ctx, mainpic, sub_area, sub_area->x + s->x, sub_area->y + s->y, 1); + break; + default: + av_log(NULL, AV_LOG_ERROR, "Unsupported input pix fmt: %d\n", inlink->format); + return AVERROR(EINVAL); + } + } + + return ff_filter_frame(ctx->outputs[0], mainpic); +} + +static av_cold int overlay_graphicsubs_init(AVFilterContext *ctx) +{ + OverlaySubsContext *s = ctx->priv; + + s->fs.on_event = do_blend; + return 0; +} + +static int overlay_graphicsubs_activate(AVFilterContext *ctx) +{ + OverlaySubsContext *s = ctx->priv; + return ff_framesync_activate(&s->fs); +} + +static int graphicsub2video_query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats; + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE }; + static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGB32, AV_PIX_FMT_NONE }; + int ret; + + /* set input subtitle formats */ + formats = ff_make_format_list(subtitle_fmts); + if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0) + return ret; + + /* set output video formats */ + formats = ff_make_format_list(pix_fmts); + if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0) + return ret; + + return 0; +} + +static int graphicsub2video_config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + OverlaySubsContext *s = ctx->priv; + + if (s->w <= 0 || s->h <= 0) { + s->w = inlink->w; + s->h = inlink->h; + } + return 0; +} + +static int graphicsub2video_config_output(AVFilterLink *outlink) +{ + const AVFilterContext *ctx = outlink->src; + OverlaySubsContext *s = ctx->priv; + const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(outlink->format); + + outlink->w = s->w; + outlink->h = s->h; + + if (outlink->w == 0 && outlink->h == 0) { + outlink->w = 1; + outlink->h = 1; + } + outlink->sample_aspect_ratio = (AVRational){1,1}; + outlink->time_base = ctx->inputs[0]->time_base; + outlink->frame_rate = ctx->inputs[0]->frame_rate; + + av_image_fill_max_pixsteps(s->main_pix_step, NULL, pix_desc); + ff_fill_rgba_map(s->overlay_rgba_map, AV_PIX_FMT_RGB32); + + s->hsub = pix_desc->log2_chroma_w; + s->vsub = pix_desc->log2_chroma_h; + + s->main_desc = pix_desc; + + s->main_is_packed_rgb = ff_fill_rgba_map(s->main_rgba_map, outlink->format) >= 0; + s->main_has_alpha = !!(pix_desc->flags & AV_PIX_FMT_FLAG_ALPHA); + + return 0; +} + +static int graphicsub2video_filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFilterLink *outlink = inlink->dst->outputs[0]; + const AVFilterContext *ctx = outlink->src; + OverlaySubsContext *s = ctx->priv; + AVFrame *out; + const unsigned num_rects = frame->num_subtitle_areas; + unsigned int i; + int ret; + + if (s->use_caching && s->cache_frame && frame->repeat_sub + && s->cache_frame->subtitle_timing.start_pts == frame->subtitle_timing.start_pts) { + out = av_frame_clone(s->cache_frame); + if (!out) + return AVERROR(ENOMEM); + + ret = av_frame_copy_props(out, frame); + if (ret < 0) + return ret; + + av_log(inlink->dst, AV_LOG_DEBUG, "graphicsub2video CACHED - size %dx%d pts: %"PRId64" areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas); + av_frame_free(&frame); + return ff_filter_frame(outlink, out); + } + + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&frame); + return AVERROR(ENOMEM); + } + + memset(out->data[0], 0, (size_t)out->linesize[0] * out->height); + + out->pts = out->pkt_dts = out->best_effort_timestamp = frame->pts; + out->coded_picture_number = out->display_picture_number = s->pic_counter++; + + for (i = 0; i < num_rects; i++) { + const AVSubtitleArea *sub_rect = frame->subtitle_areas[i]; + + if (sub_rect->type != AV_SUBTITLE_FMT_BITMAP) { + av_log(NULL, AV_LOG_WARNING, "graphicsub2video: non-bitmap subtitle\n"); + av_frame_free(&frame); + return AVERROR_INVALIDDATA; + } + + blend_packed_rgb(inlink->dst, out, sub_rect, sub_rect->x, sub_rect->y, 1); + } + + av_log(inlink->dst, AV_LOG_DEBUG, "graphicsub2video output - size %dx%d pts: %"PRId64" areas: %d\n", out->width, out->height, out->pts, frame->num_subtitle_areas); + + if (s->use_caching) { + av_frame_free(&s->cache_frame); + s->cache_frame = av_frame_clone(out); + } + + av_frame_free(&frame); + return ff_filter_frame(outlink, out); +} + +#define OFFSET(x) offsetof(OverlaySubsContext, x) +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM) + +static const AVOption overlaygraphicsubs_options[] = { + { "x", "set the x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, .flags = FLAGS }, + { "y", "set the y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, .flags = FLAGS }, + { "eof_action", "Action to take when encountering EOF from secondary input ", + OFFSET(fs.opt_eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_REPEAT }, + EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, "eof_action" }, + { "repeat", "Repeat the previous frame.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, "eof_action" }, + { "endall", "End both streams.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, "eof_action" }, + { "pass", "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, .flags = FLAGS, "eof_action" }, + { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_FRAME}, 0, EVAL_MODE_NB-1, FLAGS, "eval" }, + { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" }, + { "frame", "eval expressions per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" }, + { "shortest", "force termination when the shortest input terminates", OFFSET(fs.opt_shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = FLAGS }, + { "repeatlast", "repeat overlay of the last overlay frame", OFFSET(fs.opt_repeatlast), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, .flags = FLAGS }, + { NULL } +}; + +static const AVOption graphicsub2video_options[] = { + { "size", "set output frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS }, + { "s", "set output frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS }, + { "use_caching", "Cache output frames", OFFSET(use_caching), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, .flags = FLAGS }, + { NULL } +}; + +FRAMESYNC_DEFINE_CLASS(overlaygraphicsubs, OverlaySubsContext, fs); + +static const AVFilterPad overlaygraphicsubs_inputs[] = { + { + .name = "main", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input_main, + .flags = AVFILTERPAD_FLAG_NEEDS_WRITABLE, + }, + { + .name = "overlay", + .type = AVMEDIA_TYPE_SUBTITLE, + }, +}; + +static const AVFilterPad overlaygraphicsubs_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, +}; + +const AVFilter ff_vf_overlaygraphicsubs = { + .name = "overlaygraphicsubs", + .description = NULL_IF_CONFIG_SMALL("Overlay graphical subtitles on top of the input."), + .preinit = overlaygraphicsubs_framesync_preinit, + .init = overlay_graphicsubs_init, + .uninit = overlay_graphicsubs_uninit, + .priv_size = sizeof(OverlaySubsContext), + .priv_class = &overlaygraphicsubs_class, + .activate = overlay_graphicsubs_activate, + FILTER_INPUTS(overlaygraphicsubs_inputs), + FILTER_OUTPUTS(overlaygraphicsubs_outputs), + FILTER_QUERY_FUNC(overlay_graphicsubs_query_formats), +}; + +AVFILTER_DEFINE_CLASS(graphicsub2video); + +static const AVFilterPad graphicsub2video_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .filter_frame = graphicsub2video_filter_frame, + .config_props = graphicsub2video_config_input, + }, +}; + +static const AVFilterPad graphicsub2video_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = graphicsub2video_config_output, + }, +}; + +const AVFilter ff_svf_graphicsub2video = { + .name = "graphicsub2video", + .description = NULL_IF_CONFIG_SMALL("Convert graphical subtitles to video"), + .priv_size = sizeof(OverlaySubsContext), + .priv_class = &graphicsub2video_class, + FILTER_INPUTS(graphicsub2video_inputs), + FILTER_OUTPUTS(graphicsub2video_outputs), + FILTER_QUERY_FUNC(graphicsub2video_query_formats), +}; From patchwork Fri Jan 14 01:13:23 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33576 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp986387iog; Thu, 13 Jan 2022 17:16:12 -0800 (PST) X-Google-Smtp-Source: ABdhPJx7hmQQlRHHEugTMvYbSvJQslJ6huC6zSBDcl/1x97ZOE2SIIktDHx0P7fhBuUPJjRoyfYX X-Received: by 2002:a05:6402:4490:: with SMTP id er16mr6542060edb.203.1642122972454; Thu, 13 Jan 2022 17:16:12 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642122972; cv=none; d=google.com; s=arc-20160816; b=VS4iCd6OIvoikDJA4Y/ZBUutB8Z+0QQLphY6fd/QvhFnyESXi+WWFmLaAX3ZKJQTil igdMNSjwMSmFEd9+ciXNklQQuXS6KTifzCUdaofQuTp4tj3aRTWoQuQ1aIpoZlidDCtg 1gO9cao0lGXYvLZ6AnQ8pUceUCLgRPi/Xm/MmgcgxcANoS1KQkRHwWMA1sp75XJsY9pg t0z+UX/Rp/pGecuz7ekZnmQt2nPM1ZtbuVS9U9pca++qp09h7wzE3DZ/CQ1m+BLpauax hMgIsJpbVnCpeRzAlx0NCrz06cd8fFiFPloaFC+wW9/GOgZdlKdI+1NOecwWNQ8vHgB0 ov3g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=45SlCdU94gpgEDXZNZB4KTU1DsX6IifTNex4PVQmGEk=; b=x7GhV3ltbQPxjhca+74bQqpMjXiB+QIO4NFYLcaWyiQpFtF0pSbTkgZaDfD5jwyZc8 2/bv4VqHFqKwZ2SYkfr0YFQCRZ/BYmBDP4gs9+YHhuhs5ApE99iD73q/zRlODMl+5k98 vOgkqE06lkpBvSlHeX0le2aXZk/NWRYp63yd+9552Lu203bQQBuEj5uoCjLCVN8QQgWf +VKdK1SxM6AilApzZ1Tcr9wrF5jr0Y2P6uUZ3tDn7nXDjMcWFXwKBa2v+Lll/nxRkm88 1+34zRdxn1annjpstNpAFejrFdddfPV0RjWoETZIwW3fwlpncLmdHQ9iIej5+Mo82U11 kN+g== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=Qx5hl2oR; 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 i8si2842577edc.626.2022.01.13.17.16.11; Thu, 13 Jan 2022 17:16:12 -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 header.s=20210112 header.b=Qx5hl2oR; 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 CA15168B678; Fri, 14 Jan 2022 03:14:00 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pl1-f177.google.com (mail-pl1-f177.google.com [209.85.214.177]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 245ED68B5E3 for ; Fri, 14 Jan 2022 03:13:52 +0200 (EET) Received: by mail-pl1-f177.google.com with SMTP id e19so11908505plc.10 for ; Thu, 13 Jan 2022 17:13:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=t7mDpaoV64eEYd5Kkp7eXQLdndEqphTrQPjgPir4Bfc=; b=Qx5hl2oRCd6hLd99zy4rs4ZzbKFgzGxXvwF11AQQVM/5Fz00Vlb/bRlqhwE5mKApx6 P0CgvjEYLvznpi5hWJAeAGLtwGVg7cN1kBNAkmNHSdWRS/nlVmDdVz0BTbxWyAqqvPf+ OPG91U6rNKK1gQ5iHZQcgHHLBfUwLM2N8AmdefOz8hGhHnzQ/O5juDNMjtovtFlD1bJy NTepfsYNU+GS/99qGHkm0CryqNaaBOpE68g+0qeijrlQNHKkmL/8UXbhrMivjnm0sYVk W2usSdypj0d8t9iGprn9q2hJPD0+AbH21ykh2peJkzSJqf8xO3CvgB6vKj1+CQXbl8+a qY8A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=t7mDpaoV64eEYd5Kkp7eXQLdndEqphTrQPjgPir4Bfc=; b=zFxiIvkVIwh8jNKfJ3GcDpBD4TYVD2qZ4PNvLbBPO4xARa5u9d7ftshuHbDsp9kLic 5u7JT7dDeTnRAdUXpdLpdoHjYSAAJyArs8ht2F8hjdxGMb7rXfWV/5zryVy2vZGapkfB oBIUMgpUwCbau2eoRCJFtOfEtAMnaYAO7UigH+ZGk9PILYEH96Xcemn3gjVWKG/iAfrB O0xiNHag84Mzligc+rVuthDoSfCmjLH7zA5LgRVA4jcC4Zj5GAHXSWoGxH7ZQ31kHo9w 5QvqpPidYRba20J6VL/76ljapU6cK7IbQc2xP88pFWgQmWmGyn3E1YgVl7wvJo3S+7aG X6dA== X-Gm-Message-State: AOAM530lwFDgZSO+8f3J5Aovk9fZjsL1i21lEZPyXbqILXfGmgpQnagE kmSPii3ducKlr8VLx019H0ZBFk7rSz0MiA== X-Received: by 2002:a17:90a:8407:: with SMTP id j7mr17746538pjn.113.1642122830395; Thu, 13 Jan 2022 17:13:50 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id c124sm3665017pfb.139.2022.01.13.17.13.49 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:49 -0800 (PST) Message-Id: <453bef0c34fe22b4a109dad706506490dae38a5c.1642122814.git.ffmpegagent@gmail.com> In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:23 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 14/24] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: U0iRjGjtUlNE From: softworkz - overlaytextsubs {VS -> V) Overlay text subtitles onto a video stream. - textsubs2video {S -> V) Converts text subtitles to video frames Signed-off-by: softworkz --- configure | 2 + doc/filters.texi | 113 ++++++ libavfilter/Makefile | 2 + libavfilter/allfilters.c | 4 +- libavfilter/vf_overlaytextsubs.c | 671 +++++++++++++++++++++++++++++++ 5 files changed, 791 insertions(+), 1 deletion(-) create mode 100644 libavfilter/vf_overlaytextsubs.c diff --git a/configure b/configure index 7c2931bd50..340a198fa6 100755 --- a/configure +++ b/configure @@ -3691,6 +3691,7 @@ overlay_opencl_filter_deps="opencl" overlay_qsv_filter_deps="libmfx" overlay_qsv_filter_select="qsvvpp" overlay_vulkan_filter_deps="vulkan spirv_compiler" +overlaytextsubs_filter_deps="avcodec libass" owdenoise_filter_deps="gpl" pad_opencl_filter_deps="opencl" pan_filter_deps="swresample" @@ -3735,6 +3736,7 @@ superequalizer_filter_deps="avcodec" superequalizer_filter_select="rdft" surround_filter_deps="avcodec" surround_filter_select="rdft" +textsub2video_filter_deps="avcodec libass" tinterlace_filter_deps="gpl" tinterlace_merge_test_deps="tinterlace_filter" tinterlace_pad_test_deps="tinterlace_filter" diff --git a/doc/filters.texi b/doc/filters.texi index 4fc4a57dbb..51771f88e4 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25829,6 +25829,119 @@ Overlay PGS subtitles ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlaygraphicsubs" output.mp4 @end example @end itemize + +@section overlaytextsubs + +Overlay text subtitles onto a video stream. + +This filter supersedes the classic @ref{subtitles} filter opposed to which it does no longer require to open and access the source stream separately, which is often causing problems or doesn't even work for non-local or slow sources. + +Inputs: +@itemize +@item 0: Video [YUV420P, YUV422P, YUV444P, ARGB, RGBA, ABGR, BGRA, RGB24, BGR24] +@item 1: Subtitles [TEXT] +@end itemize + +Outputs: +@itemize +@item 0: Video (same as input) +@end itemize + +It accepts the following parameters: + +@table @option + +@item alpha +Process alpha channel, by default alpha channel is untouched. + +@item fonts_dir +Set a directory path containing fonts that can be used by the filter. +These fonts will be used in addition to whatever the font provider uses. + +@item default_font_path +Path to a font file to be used as the default font. + +@item font_size +Set the default font size. + +@item fontconfig_file +Path to ASS fontconfig configuration file. + +@item force_style +Override default style or script info parameters of the subtitles. It accepts a +string containing ASS style format @code{KEY=VALUE} couples separated by ",". + +@item margin +Set the rendering margin in pixels. + +@item render_latest_only +For rendering, alway use the latest event only, which is covering the given point in time +@end table + +@subsection Examples + +@itemize +@item +Overlay ASS subtitles with animations: +@example +ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:v]overlaytextsubs" -map 0 -y out.mkv +@end example +@end itemize + +@section textsub2video + +Converts text subtitles to video frames. + +For overlaying text subtitles onto video frames it is recommended to use the overlay_textsubs filter. +The textsub2video is useful for for creating transparent text-frames when overlay is done via hw acceleration + +Inputs: +@itemize +@item 0: Subtitles [TEXT] +@end itemize + +Outputs: +@itemize +@item 0: Video [RGB32] +@end itemize + +It accepts the following parameters: + +@table @option + +@item rate, r +Set the framerate for updating overlay frames. +Normally, overlay frames will only be updated each time when the subtitles to display are changing. +In cases where subtitles include advanced features (like animation), this parameter determines the frequency by which the overlay frames should be updated. + +@item size, s +Set the output frame size. +Allows to override the size of output video frames. + +@item fonts_dir +Set a directory path containing fonts that can be used by the filter. +These fonts will be used in addition to whatever the font provider uses. + +@item default_font_path +Path to a font file to be used as the default font. + +@item font_size +Set the default font size. + +@item fontconfig_file +Path to ASS fontconfig configuration file. + +@item force_style +Override default style or script info parameters of the subtitles. It accepts a +string containing ASS style format @code{KEY=VALUE} couples separated by ",". + +@item margin +Set the rendering margin in pixels. + +@item render_latest_only +For rendering, alway use the latest event only, which is covering the given point in time. +@end table + @c man end SUBTITLE FILTERS @chapter Multimedia Filters diff --git a/libavfilter/Makefile b/libavfilter/Makefile index a372effc12..48c9182c55 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -380,6 +380,7 @@ OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER) += vf_overlay_opencl.o opencl.o \ OBJS-$(CONFIG_OVERLAY_QSV_FILTER) += vf_overlay_qsv.o framesync.o OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER) += vf_overlay_vulkan.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER) += vf_overlaygraphicsubs.o framesync.o +OBJS-$(CONFIG_OVERLAYTEXTSUBS_FILTER) += vf_overlaytextsubs.o OBJS-$(CONFIG_OWDENOISE_FILTER) += vf_owdenoise.o OBJS-$(CONFIG_PAD_FILTER) += vf_pad.o OBJS-$(CONFIG_PAD_OPENCL_FILTER) += vf_pad_opencl.o opencl.o opencl/pad.o @@ -469,6 +470,7 @@ OBJS-$(CONFIG_SWAPRECT_FILTER) += vf_swaprect.o OBJS-$(CONFIG_SWAPUV_FILTER) += vf_swapuv.o OBJS-$(CONFIG_TBLEND_FILTER) += vf_blend.o framesync.o OBJS-$(CONFIG_TELECINE_FILTER) += vf_telecine.o +OBJS-$(CONFIG_TEXTSUB2VIDEO_FILTER) += vf_overlaytextsubs.o OBJS-$(CONFIG_THISTOGRAM_FILTER) += vf_histogram.o OBJS-$(CONFIG_THRESHOLD_FILTER) += vf_threshold.o framesync.o OBJS-$(CONFIG_THUMBNAIL_FILTER) += vf_thumbnail.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index eb4e30a270..9ba89db128 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -358,9 +358,10 @@ extern const AVFilter ff_vf_oscilloscope; extern const AVFilter ff_vf_overlay; extern const AVFilter ff_vf_overlay_opencl; extern const AVFilter ff_vf_overlay_qsv; -extern const AVFilter ff_vf_overlaygraphicsubs; extern const AVFilter ff_vf_overlay_vulkan; extern const AVFilter ff_vf_overlay_cuda; +extern const AVFilter ff_vf_overlaygraphicsubs; +extern const AVFilter ff_vf_overlaytextsubs; extern const AVFilter ff_vf_owdenoise; extern const AVFilter ff_vf_pad; extern const AVFilter ff_vf_pad_opencl; @@ -547,6 +548,7 @@ extern const AVFilter ff_avf_showwaves; extern const AVFilter ff_avf_showwavespic; extern const AVFilter ff_vaf_spectrumsynth; extern const AVFilter ff_svf_graphicsub2video; +extern const AVFilter ff_svf_textsub2video; /* multimedia sources */ extern const AVFilter ff_avsrc_amovie; diff --git a/libavfilter/vf_overlaytextsubs.c b/libavfilter/vf_overlaytextsubs.c new file mode 100644 index 0000000000..efe2684dac --- /dev/null +++ b/libavfilter/vf_overlaytextsubs.c @@ -0,0 +1,671 @@ +/* + * Copyright (c) 2021 softworkz + * + * 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 + * overlay text subtitles on top of a video frame + */ + +#include +#include "libavutil/ass_internal.h" +#include "libavutil/thread.h" + +#include "drawutils.h" +#include "filters.h" + +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" + +typedef struct TextSubsContext { + const AVClass *class; + AVMutex mutex; + int is_mutex_initialized; + + ASS_Library *library; + ASS_Renderer *renderer; + ASS_Track *track; + + char *default_font_path; + char *fonts_dir; + char *fc_file; + double font_size; + char *force_style; + char *language; + int margin; + int render_latest_only; + + int alpha; + FFDrawContext draw; + + int got_header; + int out_w, out_h; + AVRational frame_rate; + AVFrame *last_frame; + int need_frame; + int eof; +} TextSubsContext; + +/* libass supports a log level ranging from 0 to 7 */ +static const int ass_libavfilter_log_level_map[] = { + AV_LOG_QUIET, /* 0 */ + AV_LOG_PANIC, /* 1 */ + AV_LOG_FATAL, /* 2 */ + AV_LOG_ERROR, /* 3 */ + AV_LOG_WARNING, /* 4 */ + AV_LOG_INFO, /* 5 */ + AV_LOG_VERBOSE, /* 6 */ + AV_LOG_DEBUG, /* 7 */ +}; + +static void ass_log(int ass_level, const char *fmt, va_list args, void *ctx) +{ + const int ass_level_clip = av_clip(ass_level, 0, FF_ARRAY_ELEMS(ass_libavfilter_log_level_map) - 1); + const int level = ass_libavfilter_log_level_map[ass_level_clip]; + + av_vlog(ctx, level, fmt, args); + av_log(ctx, level, "\n"); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + TextSubsContext *s = ctx->priv; + + if (s->track) + ass_free_track(s->track); + if (s->renderer) + ass_renderer_done(s->renderer); + if (s->library) + ass_library_done(s->library); + + s->track = NULL; + s->renderer = NULL; + s->library = NULL; + + if (s->is_mutex_initialized) { + ff_mutex_destroy(&s->mutex); + s->is_mutex_initialized = 0; + } + + av_frame_free(&s->last_frame); +} + +static int overlay_textsubs_query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats; + AVFilterLink *inlink0 = ctx->inputs[0]; + AVFilterLink *inlink1 = ctx->inputs[1]; + AVFilterLink *outlink = ctx->outputs[0]; + static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE }; + int ret; + + /* set input0 and output0 video formats */ + formats = ff_draw_supported_pixel_formats(0); + if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0) + return ret; + if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0) + return ret; + + /* set input1 subtitle formats */ + formats = ff_make_format_list(subtitle_fmts); + if ((ret = ff_formats_ref(formats, &inlink1->outcfg.formats)) < 0) + return ret; + + return 0; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + + outlink->w = ctx->inputs[0]->w; + outlink->h = ctx->inputs[0]->h; + outlink->time_base = ctx->inputs[0]->time_base; + outlink->frame_rate = ctx->inputs[0]->frame_rate; + + return 0; +} + +static int config_input_main(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + TextSubsContext *s = inlink->dst->priv; + int ret; + + ret = ff_draw_init(&s->draw, inlink->format, s->alpha ? FF_DRAW_PROCESS_ALPHA : 0); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Could not initialize ff_draw.\n"); + return ret; + } + + ass_set_frame_size (s->renderer, inlink->w, inlink->h); + ass_set_pixel_aspect(s->renderer, av_q2d(inlink->sample_aspect_ratio)); + + av_log(ctx, AV_LOG_VERBOSE, "Subtitle screen: %dx%d\n\n\n\n", inlink->w, inlink->h); + + return 0; +} + +/* libass stores an RGBA color in the format RRGGBBTT, where TT is the transparency level */ +#define AR(c) ( (c)>>24) +#define AG(c) (((c)>>16)&0xFF) +#define AB(c) (((c)>>8) &0xFF) +#define AA(c) ((0xFF-(c)) &0xFF) + +static void overlay_ass_image(TextSubsContext *s, AVFrame *picref, + const ASS_Image *image) +{ + for (; image; image = image->next) { + uint8_t rgba_color[] = {AR(image->color), AG(image->color), AB(image->color), AA(image->color)}; + FFDrawColor color; + ff_draw_color(&s->draw, &color, rgba_color); + ff_blend_mask(&s->draw, &color, + picref->data, picref->linesize, + picref->width, picref->height, + image->bitmap, image->stride, image->w, image->h, + 3, 0, image->dst_x, image->dst_y); + } +} + +static void process_header(AVFilterContext *link, AVFrame *frame) +{ + TextSubsContext *s = link->priv; + ASS_Track *track = s->track; + ASS_Style *style; + int sid = 0; + + if (!track) + return; + + if (frame && frame->subtitle_header) { + char *subtitle_header = (char *)frame->subtitle_header->data; + ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header)); + } + else { + char* subtitle_header = avpriv_ass_get_subtitle_header_default(0); + if (!subtitle_header) + return; + + ass_process_codec_private(s->track, subtitle_header, strlen(subtitle_header)); + av_free(subtitle_header); + } + + if (s->language) + s->track->Language = av_strdup(s->language); + + if (!s->track->event_format) { + s->track->event_format = av_strdup("ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text"); + } + + if (s->track->n_styles == 0) { + sid = ass_alloc_style(track); + style = &s->track->styles[sid]; + style->Name = av_strdup("Default"); + style->PrimaryColour = 0xffffff00; + style->SecondaryColour = 0x00ffff00; + style->OutlineColour = 0x00000000; + style->BackColour = 0x00000080; + style->Bold = 200; + style->ScaleX = 1.0; + style->ScaleY = 1.0; + style->Spacing = 0; + style->BorderStyle = 1; + style->Outline = 2; + style->Shadow = 3; + style->Alignment = 2; + } + else + style = &s->track->styles[sid]; + + style->FontSize = s->font_size; + style->MarginL = style->MarginR = style->MarginV = s->margin; + + track->default_style = sid; + + s->got_header = 1; +} + +static int filter_video_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFilterContext *ctx = inlink->dst; + TextSubsContext *s = ctx->priv; + int detect_change = 0; + ASS_Image *image; + + ff_request_frame(ctx->inputs[1]); + + int64_t time_ms = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000); + + ff_mutex_lock(&s->mutex); + image = ass_render_frame(s->renderer, s->track, time_ms, &detect_change); + ff_mutex_unlock(&s->mutex); + + if (detect_change) + av_log(ctx, AV_LOG_DEBUG, "Change happened at time ms:%"PRId64"\n", time_ms); + + overlay_ass_image(s, frame, image); + + return ff_filter_frame(ctx->outputs[0], frame); +} + +static int filter_subtitle_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFilterContext *ctx = inlink->dst; + TextSubsContext *s = ctx->priv; + const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000)); + const int64_t duration = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000)); + + // Postpone header processing until we receive a frame with content + if (!s->got_header && frame->num_subtitle_areas > 0) + process_header(ctx, frame); + + if (frame->repeat_sub) + goto exit; + + ff_mutex_lock(&s->mutex); + + if (s->render_latest_only && s->track->n_events > 0) { + const int64_t previous_start_time = s->track->events[s->track->n_events - 1].Start; + const int64_t diff = start_time - previous_start_time; + for (int i = s->track->n_events - 1; i >= 0; i--) { + if (previous_start_time != s->track->events[i].Start) + break; + + if (s->track->events[i].Duration > diff) + s->track->events[i].Duration = diff; + + } + } + + for (unsigned i = 0; i < frame->num_subtitle_areas; i++) { + char *ass_line = frame->subtitle_areas[i]->ass; + if (!ass_line) + continue; + + ass_process_chunk(s->track, ass_line, strlen(ass_line), start_time, duration); + } + + ff_mutex_unlock(&s->mutex); + +exit: + av_frame_free(&frame); + return 0; +} + +static av_cold int init(AVFilterContext *ctx) +{ + int ret; + TextSubsContext *s = ctx->priv; + + s->library = ass_library_init(); + + if (!s->library) { + av_log(ctx, AV_LOG_ERROR, "Could not initialize libass.\n"); + return AVERROR(EINVAL); + } + + ass_set_message_cb(s->library, ass_log, ctx); + + /* Initialize fonts */ + if (s->fonts_dir) + ass_set_fonts_dir(s->library, s->fonts_dir); + + ass_set_extract_fonts(s->library, 1); + + s->renderer = ass_renderer_init(s->library); + if (!s->renderer) { + av_log(ctx, AV_LOG_ERROR, "Could not initialize libass renderer.\n"); + return AVERROR(EINVAL); + } + + s->track = ass_new_track(s->library); + if (!s->track) { + av_log(ctx, AV_LOG_ERROR, "ass_new_track() failed!\n"); + return AVERROR(EINVAL); + } + + ass_set_check_readorder(s->track, 0); + + ass_set_fonts(s->renderer, s->default_font_path, NULL, 1, s->fc_file, 1); + + if (s->force_style) { + char **list = NULL; + char *temp = NULL; + char *ptr = av_strtok(s->force_style, ",", &temp); + int i = 0; + while (ptr) { + av_dynarray_add(&list, &i, ptr); + if (!list) { + return AVERROR(ENOMEM); + } + ptr = av_strtok(NULL, ",", &temp); + } + av_dynarray_add(&list, &i, NULL); + if (!list) { + return AVERROR(ENOMEM); + } + ass_set_style_overrides(s->library, list); + av_free(list); + } + + ret = ff_mutex_init(&s->mutex, NULL); + if (ret) { + av_log(ctx, AV_LOG_ERROR, "mutex initialiuzation failed! Error code: %d\n", ret); + return AVERROR(EINVAL); + } + + s->is_mutex_initialized = 1; + + return ret; +} + +static int textsub2video_query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats; + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE }; + int ret; + + /* set input0 subtitle format */ + formats = ff_make_format_list(subtitle_fmts); + if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0) + return ret; + + /* set output0 video format */ + formats = ff_draw_supported_pixel_formats(AV_PIX_FMT_FLAG_ALPHA); + if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0) + return ret; + + return 0; +} + +static int textsub2video_config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + TextSubsContext *s = ctx->priv; + + if (s->out_w <= 0 || s->out_h <= 0) { + s->out_w = inlink->w; + s->out_h = inlink->h; + } + + return 0; +} + +static int textsub2video_config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + TextSubsContext *s = ctx->priv; + int ret; + + ret = ff_draw_init(&s->draw, outlink->format, FF_DRAW_PROCESS_ALPHA); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Could not initialize ff_draw.\n"); + return ret; + } + + if (s->out_w <= 0 || s->out_h <= 0) { + av_log(ctx, AV_LOG_ERROR, "No output image size set.\n"); + return AVERROR(EINVAL); + } + + ass_set_frame_size (s->renderer, s->out_w, s->out_h); + + outlink->w = s->out_w; + outlink->h = s->out_h; + outlink->sample_aspect_ratio = (AVRational){1,1}; + outlink->frame_rate = s->frame_rate; + + return 0; +} + +static int textsub2video_request_frame(AVFilterLink *outlink) +{ + TextSubsContext *s = outlink->src->priv; + AVFilterLink *inlink = outlink->src->inputs[0]; + int64_t last_pts = outlink->current_pts; + int64_t next_pts, time_ms; + int i, detect_change = 0, status; + AVFrame *out; + ASS_Image *image; + + status = ff_outlink_get_status(inlink); + if (status == AVERROR_EOF) + return AVERROR_EOF; + + if (s->eof) + return AVERROR_EOF; + + if (inlink->current_pts == AV_NOPTS_VALUE) { // || outlink->current_pts > inlink->current_pts) { + int ret = ff_request_frame(inlink); + if (ret == AVERROR_EOF) { + s->eof = 1; + } + + if (ret != 0) + av_log(outlink->src, AV_LOG_DEBUG, "ff_request_frame returned: %d\n", ret); + + s->need_frame = 1; + return 0; + } + + if (last_pts == AV_NOPTS_VALUE) + next_pts = last_pts = inlink->current_pts * av_q2d(inlink->time_base) / av_q2d(outlink->time_base); + else + next_pts = last_pts + (int64_t)(1.0 / av_q2d(outlink->frame_rate) / av_q2d(outlink->time_base)); + + time_ms = (int64_t)((double)next_pts * av_q2d(outlink->time_base) * 1000); + + ff_mutex_lock(&s->mutex); + image = ass_render_frame(s->renderer, s->track, time_ms, &detect_change); + ff_mutex_unlock(&s->mutex); + + if (detect_change) + av_log(outlink->src, AV_LOG_VERBOSE, "Change happened at time ms:%"PRId64" pts:%"PRId64"\n", time_ms, next_pts); + else if (s->last_frame) { + out = av_frame_clone(s->last_frame); + if (!out) + return AVERROR(ENOMEM); + + out->pts = out->pkt_dts = out->best_effort_timestamp = next_pts; + return ff_filter_frame(outlink, out); + } + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) + return AVERROR(ENOMEM); + + for (i = 0; i < AV_NUM_DATA_POINTERS; i++) { + if (out->buf[i] && i != 1) + memset(out->buf[i]->data, 0, out->buf[i]->size); + } + + out->pts = out->pkt_dts = out->best_effort_timestamp = next_pts; + + if (image) + overlay_ass_image(s, out, image); + + av_frame_free(&s->last_frame); + + s->last_frame = av_frame_clone(out); + + return ff_filter_frame(outlink, out); +} + +static int textsub2video_filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFilterContext *ctx = inlink->dst; + TextSubsContext *s = ctx->priv; + const int64_t start_time = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, av_make_q(1, 1000)); + const int64_t duration = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, av_make_q(1, 1000)); + + av_log(ctx, AV_LOG_VERBOSE, "textsub2video_filter_frame num_subtitle_rects: %d, start_time_ms: %"PRId64"\n", frame->num_subtitle_areas, start_time); + + if (!s->got_header && frame->num_subtitle_areas > 0) + process_header(ctx, frame); + + if (frame->repeat_sub) + goto exit; + + ff_mutex_lock(&s->mutex); + + if (s->render_latest_only && s->track->n_events > 0) { + const int64_t previous_start_time = s->track->events[s->track->n_events - 1].Start; + const int64_t diff = start_time - previous_start_time; + for (int i = s->track->n_events - 1; i >= 0; i--) { + if (previous_start_time != s->track->events[i].Start) + break; + + if (s->track->events[i].Duration > diff) + s->track->events[i].Duration = diff; + + } + } + + for (unsigned i = 0; i < frame->num_subtitle_areas; i++) { + char *ass_line = frame->subtitle_areas[i]->ass; + if (!ass_line) + continue; + + ass_process_chunk(s->track, ass_line, strlen(ass_line), start_time, duration); + } + + + ff_mutex_unlock(&s->mutex); + +exit: + av_frame_free(&frame); + + if (s->need_frame) { + s->need_frame = 0; + return textsub2video_request_frame(ctx->outputs[0]); + } + + return 0; +} + +#define OFFSET(x) offsetof(TextSubsContext, x) +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM) + +static const AVOption overlaytextsubs_options[] = { + {"alpha", "enable processing of alpha channel", OFFSET(alpha), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, .flags = FLAGS}, + {"font_size", "default font size", OFFSET(font_size), AV_OPT_TYPE_DOUBLE, {.dbl = 18.0}, 0.0, 100.0, .flags = FLAGS}, + {"force_style", "force subtitle style", OFFSET(force_style), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, .flags = FLAGS}, + {"margin", "default margin", OFFSET(margin), AV_OPT_TYPE_INT, {.i64 = 20 }, 0, INT_MAX, .flags = FLAGS}, + {"default_font_path", "path to default font", OFFSET(default_font_path), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"fonts_dir", "directory to scan for fonts", OFFSET(fonts_dir), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"fontsdir", "directory to scan for fonts", OFFSET(fonts_dir), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"fontconfig_file", "fontconfig file to load", OFFSET(fc_file), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"language", "default language", OFFSET(language), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"render_latest_only", "newest sub event for each time", OFFSET(render_latest_only), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, .flags = FLAGS}, + { .name = NULL } +}; + +static const AVOption textsub2video_options[] = { + {"rate", "set frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="8"}, 0, INT_MAX, .flags = FLAGS}, + {"r", "set frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="8"}, 0, INT_MAX, .flags = FLAGS}, + {"size", "set video size", OFFSET(out_w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS}, + {"s", "set video size", OFFSET(out_w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS}, + {"font_size", "default font size", OFFSET(font_size), AV_OPT_TYPE_DOUBLE, {.dbl = 18.0}, 0.0, 100.0, .flags = FLAGS}, + {"force_style", "force subtitle style", OFFSET(force_style), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, .flags = FLAGS}, + {"margin", "default margin", OFFSET(margin), AV_OPT_TYPE_INT, {.i64 = 20 }, 0, INT_MAX, .flags = FLAGS}, + {"default_font_path", "path to default font", OFFSET(default_font_path), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"fonts_dir", "directory to scan for fonts", OFFSET(fonts_dir), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"fontsdir", "directory to scan for fonts", OFFSET(fonts_dir), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"fontconfig_file", "fontconfig file to load", OFFSET(fc_file), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"language", "default language", OFFSET(language), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"render_latest_only", "newest sub event for each time", OFFSET(render_latest_only), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, .flags = FLAGS}, + { .name = NULL } +}; + +#if CONFIG_OVERLAYTEXTSUBS_FILTER + +AVFILTER_DEFINE_CLASS(overlaytextsubs); + +static const AVFilterPad overlaytextsubs_inputs[] = { + { + .name = "main", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input_main, + .flags = AVFILTERPAD_FLAG_NEEDS_WRITABLE, + .filter_frame = filter_video_frame, + }, + { + .name = "overlay", + .type = AVMEDIA_TYPE_SUBTITLE, + .filter_frame = filter_subtitle_frame, + }, +}; + +static const AVFilterPad overlaytextsubs_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, +}; + +const AVFilter ff_vf_overlaytextsubs = { + .name = "overlaytextsubs", + .description = NULL_IF_CONFIG_SMALL("Overlay textual subtitles on top of the input."), + .init = init, + .uninit = uninit, + .priv_size = sizeof(TextSubsContext), + .priv_class = &overlaytextsubs_class, + FILTER_INPUTS(overlaytextsubs_inputs), + FILTER_OUTPUTS(overlaytextsubs_outputs), + FILTER_QUERY_FUNC(overlay_textsubs_query_formats), +}; +#endif + +#if CONFIG_TEXTSUB2VIDEO_FILTER + +AVFILTER_DEFINE_CLASS(textsub2video); + +static const AVFilterPad textsub2video_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .config_props = textsub2video_config_input, + .filter_frame = textsub2video_filter_frame, + }, +}; + +static const AVFilterPad textsub2video_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = textsub2video_config_output, + .request_frame = textsub2video_request_frame, + }, +}; + +const AVFilter ff_svf_textsub2video = { + .name = "textsub2video", + .description = NULL_IF_CONFIG_SMALL("Convert textual subtitles to video frames"), + .init = init, + .uninit = uninit, + .priv_size = sizeof(TextSubsContext), + .priv_class = &textsub2video_class, + FILTER_INPUTS(textsub2video_inputs), + FILTER_OUTPUTS(textsub2video_outputs), + FILTER_QUERY_FUNC(textsub2video_query_formats), +}; +#endif From patchwork Fri Jan 14 01:13:24 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33577 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp986524iog; Thu, 13 Jan 2022 17:16:23 -0800 (PST) X-Google-Smtp-Source: ABdhPJxnp9GlRNqsXZMtOm1EiUGl2Y8S/Dx7TspjUjFDHjblTV1a+n1dfLV2OjOUqYspMrdKTwUy X-Received: by 2002:aa7:dbc2:: with SMTP id v2mr6742623edt.59.1642122983338; Thu, 13 Jan 2022 17:16:23 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642122983; cv=none; d=google.com; s=arc-20160816; b=Veicd2d58dZO6k021cvQ8d5Vo0aMMmINSiXS4TUnPzDo7MQCJIrpDhpfdj74sg8WXq IStnkJgS1J7ctHww/kIO7l5Co2q9PKNoqoJl+lBfs20r5Luj0GZs4t03FfnhbRBdgOnY bm99jO0U7Q7r+/EzYwMrV6uRf1NLIhnfaArUmgldpGjp3vfhn9aWHOy9wxMjnom1BEv9 fd9xVN30DNhoI2fDbbikCJGYDdnnnYkOq6k2/JO75Z1AnhEyQq01sMlTT5WX+OqiD14n 9RY9tfQICzASOkK+9tWKjK9hRn4w8TuHO9wJpDFAOKBVjS82EorSnTx3HpeHd1gOqArS cIhg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=kH588UDS9KOktEMXfuraKfZNYJOWgwEKUvJu81ROt9Y=; b=DQVUXWcBAp0DBABrnZClHQ67mSGaHCS1M+DsDzWnmBu+KOwtL2Zs0GUoM+/1nUffnB GoogMNn6baR7fdpjiLTfOOv9MLwOA8AF4iSLThHTbLbJwqTGP4FRznJZJbYLOkg9Mnnn TddEm+KdYtIas8CiiGpGk7h6G3SwbPSEZ1+vqvWDYji9HrR4NJZohhA6T3RbzobVq0UO KhJGwQT4QphM8U8jsbylOh/0OcTLU86FTzCH4lQjL8DX2e9cHKHevL7n6BakS6sa9hTM /7gJQhoE/NPsoLpXi7xH0GA47qXF7VVGLJFUYao4QliY+eZU+97omchemLrOqIW+N+OX ar2w== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=pM1TqLEi; 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 gs41si16266ejc.559.2022.01.13.17.16.22; Thu, 13 Jan 2022 17:16:23 -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 header.s=20210112 header.b=pM1TqLEi; 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 DFF8B68B688; Fri, 14 Jan 2022 03:14:01 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pl1-f170.google.com (mail-pl1-f170.google.com [209.85.214.170]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 80C9A68B5EE for ; Fri, 14 Jan 2022 03:13:53 +0200 (EET) Received: by mail-pl1-f170.google.com with SMTP id c3so11953458pls.5 for ; Thu, 13 Jan 2022 17:13:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=mtkvL/RfZPPo2MiaVchQOoH+5/WW3XgH9Kgf4A/Xypw=; b=pM1TqLEiHMylaD+w1yzGWaYHhdDnxaQNe/3B9VcpQAMgyCe5qZP6IOpp5igsoLFoNH P/0Pwxp0+o7DZVhycXedGx9MWgkMCRlEIfefPmp2g/cmADxq4KhTY5yGq97eVLLJ5k+D 1Zh0fVsiO9RyCLGQA1pQUeKMgC8bBX2uKgBxfgYYX+P2Yy5xyEviDjx1n17/W1Pdxc5j 6mONrgutqWLEbvbsulGL1eC1P4+hBnegMAjuTCpobY5X0dQrV0klsBkOP1ae06aGkppQ hXdB2ai2Kfiz5FDPpw1nsPadXU1nD68Y9SdyR3w6py01PnTCOAmNFKJI8bFzv5CULZgV ZrCw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=mtkvL/RfZPPo2MiaVchQOoH+5/WW3XgH9Kgf4A/Xypw=; b=Y4ALer5XQewlxx5zvS3m8EbRe9NHSsD9l3n7WXt2qPUMdZyh+piSQfLhZAWBDFbHcZ rY8CGEw/vMegV9xcpebPAxczOCOpknAx99/Pp1052JIQWtSCviWxt2GT1NnZdiNLMPuo dUjnZ2Iw63SH/rcH807oiKFv3r1hRcmdSRyULicd5mZGrchLfXXAiXrYs2lZzPUTpFHw QPflRd9xwUUqZjKkjUo1Kn7evUV8JB1ZGqFiEEc/MMj6lGY95SJXuP3Pfypir9JtHrfh hBjUynxwycNY7bD1Z92Qyq4oVEsheVGhzRwy51K4A+7xEplxqOihA956rZXc79YQIoMB yzJQ== X-Gm-Message-State: AOAM532JkdmdNIIf5mSa7kp+DS9g0jxA2195C2334b7cw7DQ38vrIu7/ roWzAFA3xEXsZrvqQXlsBEXvX7VuJBdK/Q== X-Received: by 2002:a17:902:eccb:b0:14a:3457:1299 with SMTP id a11-20020a170902eccb00b0014a34571299mr7108549plh.69.1642122831374; Thu, 13 Jan 2022 17:13:51 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id p10sm3970565pfo.95.2022.01.13.17.13.50 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:50 -0800 (PST) Message-Id: In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:24 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 15/24] avfilter/textmod: Add textmod, censor and show_speaker filters 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 0a1/O00+zghf From: softworkz - textmod {S -> S) Modify subtitle text in a number of ways - censor {S -> S) Censor subtitles using a word list - show_speaker {S -> S) Prepend speaker names from ASS subtitles to the visible text lines Signed-off-by: softworkz --- doc/filters.texi | 206 ++++++++++++ libavfilter/Makefile | 5 + libavfilter/allfilters.c | 3 + libavfilter/sf_textmod.c | 710 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 924 insertions(+) create mode 100644 libavfilter/sf_textmod.c diff --git a/doc/filters.texi b/doc/filters.texi index 51771f88e4..494ee6f062 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25721,6 +25721,145 @@ existing filters using @code{--disable-filters}. Below is a description of the currently available subtitle filters. + +@section censor + +Censor selected words in text subtitles. + +Inputs: +@itemize +@item 0: Subtitles[TEXT] +@end itemize + +Outputs: +@itemize +@item 0: Subtitles[TEXT] +@end itemize + +It accepts the following parameters: + +@table @option +@item mode +The censoring mode to apply. + +Supported censoring modes are: + +@table @var +@item 0, keep_first_last +Replace all characters with the 'censor_char' except the first and the last character of a word. +For words with less than 4 characters, the last character will be replaced as well. +For words with less than 3 characters, the first character will be replaced as well. +@item 1, keep_first +Replace all characters with the 'censor_char' except the first character of a word. +For words with less than 3 characters, the first character will be replaced as well. +@item 2, all +Replace all characters with the 'censor_char'. +@end table + +@item words +A list of words to censor, separated by 'separator'. + +@item words_file +Specify a file from which to load the contents for the 'words' parameter. + +@item censor_char +Single character used as replacement for censoring. + +@item separator +Delimiter character for words. Used with replace_words and remove_words- Must be a single character. +The default is '.'. + +@end table + +@subsection Examples + +@itemize +@item +Censor a few given words with a pound character. +@example +ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:1]censor=words='diss,louder,hope,beam,word':censor_char='#'" -map 0 -y output.mkv +@end example +@end itemize + + +@section textmod + +Modify subtitle text in a number of ways. + +Inputs: +@itemize +@item 0: Subtitles[TEXT] +@end itemize + +Outputs: +@itemize +@item 0: Subtitles[TEXT] +@end itemize + +It accepts the following parameters: + +@table @option +@item mode +The kind of text modification to apply + +Supported operation modes are: + +@table @var +@item 0, leet +Convert subtitle text to 'leet speak'. It's primarily useful for testing as the modification will be visible with almost all text lines. +@item 1, to_upper +Change all text to upper case. Might improve readability. +@item 2, to_lower +Change all text to lower case. +@item 3, replace_chars +Replace one or more characters. Requires the find and replace parameters to be specified. +Both need to be equal in length. +The first char in find is replaced by the first char in replace, same for all subsequent chars. +@item 4, remove_chars +Remove certain characters. Requires the find parameter to be specified. +All chars in the find parameter string will be removed from all subtitle text. +@item 5, replace_words +Replace one or more words. Requires the find and replace parameters to be specified. Multiple words must be separated by the delimiter char specified vie the separator parameter (default: ','). +The number of words in the find and replace parameters needs to be equal. +The first word in find is replaced by the first word in replace, same for all subsequent words +@item 6, remove_words +Remove certain words. Requires the find parameter to be specified. Multiple words must be separated by the delimiter char specified vie the separator parameter (default: ','). +All words in the find parameter string will be removed from all subtitle text. +@end table + +@item find +Required for replace_chars, remove_chars, replace_words and remove_words. + +@item find_file +Specify a file from which to load the contents for the 'find' parameter. + +@item replace +Required for replace_chars and replace_words. + +@item replace_file +Specify a file from which to load the contents for the 'replace' parameter. + +@item separator +Delimiter character for words. Used with replace_words and remove_words- Must be a single character. +The default is '.'. + +@end table + +@subsection Examples + +@itemize +@item +Change all characters to upper case while keeping all styles and animations: +@example +ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_complex "[0:s]textmod=mode=to_upper" -map 0 -y out.mkv +@end example +@item +Remove a set of symbol characters for am improved and smoother visual apperance: +@example +ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_complex "[0:s]textmod=mode=remove_chars:find='$&#@*§'" -map 0 -y out.mkv +@end example +@end itemize + @section graphicsub2video Renders graphic subtitles as video frames. @@ -25888,6 +26027,73 @@ ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex. @end example @end itemize +@section showspeaker + +Prepend speaker names to subtitle lines (when available). + +Subtitles in ASS/SSA format are often including the names of the persons +or character for each subtitle line. The showspeaker filter adds those names +to the actual subtitle text to make it visible on playback. + +Inputs: +@itemize +@item 0: Subtitles[TEXT] +@end itemize + +Outputs: +@itemize +@item 0: Subtitles[TEXT] +@end itemize + +It accepts the following parameters: + +@table @option +@item format +The format for prepending speaker names. Default is 'square_brackets'. + +Supported operation modes are: + +@table @var +@item 0, square_brackets +Enclose the speaker name in square brackets, followed by space ('[speaker] text'). +@item 1, round_brackets +Enclose the speaker name in round brackets, followed by space ('(speaker) text'). +@item 2, colon +Separate the speaker name with a colon and space ('speaker: text'). +@item 3, plain +Separate the speaker name with a space only ('speaker text'). +@end table + +@item line_break +Set thís parameter to insert a line break between speaker name and text instead of the space character. + +@item style +Allows to set a specific style for the speaker name text. + +This can be either a named style that exists in the ass subtitle header script (e.g. 'Default') or an ass style override code. +Example: @{\\c&HDD0000&\\be1\\i1\\bord10@} +This sets the color to blue, enables edge blurring, italic font and a border of size 10. + +The behavior is as follows: + +- When the style parameter is not provided, the filter will find the first position in the event string that is actual text. + The speaker name will be inserted at this position. This allows to have the speaker name shown in the same style like the + regular text, in case the string would start with a sequence of style codes. +- When the style parameter is provided, everything will be prepended to the original text: + Style Code or Style name >> Speaker Name >> Style Reset Code >> Original Text + +@end table + +@subsection Examples + +@itemize +@item +Prepend speaker names with blue text, smooth edges and blend/overlay that onto the video. +@example +ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\\be1@}',[0:v]overlay_textsubs" +@end example +@end itemize + @section textsub2video Converts text subtitles to video frames. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 48c9182c55..1af4f4b9bc 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -561,6 +561,11 @@ OBJS-$(CONFIG_YUVTESTSRC_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_NULLSINK_FILTER) += vsink_nullsink.o +# subtitle filters +OBJS-$(CONFIG_CENSOR_FILTER) += sf_textmod.o +OBJS-$(CONFIG_SHOW_SPEAKER_FILTER) += sf_textmod.o +OBJS-$(CONFIG_TEXTMOD_FILTER) += sf_textmod.o + # multimedia filters OBJS-$(CONFIG_ABITSCOPE_FILTER) += avf_abitscope.o OBJS-$(CONFIG_ADRAWGRAPH_FILTER) += f_drawgraph.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 9ba89db128..ac7d71547b 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -547,6 +547,9 @@ extern const AVFilter ff_avf_showvolume; extern const AVFilter ff_avf_showwaves; extern const AVFilter ff_avf_showwavespic; extern const AVFilter ff_vaf_spectrumsynth; +extern const AVFilter ff_sf_censor; +extern const AVFilter ff_sf_showspeaker; +extern const AVFilter ff_sf_textmod; extern const AVFilter ff_svf_graphicsub2video; extern const AVFilter ff_svf_textsub2video; diff --git a/libavfilter/sf_textmod.c b/libavfilter/sf_textmod.c new file mode 100644 index 0000000000..c571edc1d2 --- /dev/null +++ b/libavfilter/sf_textmod.c @@ -0,0 +1,710 @@ +/* + * Copyright (c) 2021 softworkz + * + * 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 + * text subtitle filter which allows to modify subtitle text in several ways + */ + +#include + +#include "libavutil/opt.h" +#include "internal.h" +#include "libavutil/ass_split_internal.h" +#include "libavutil/bprint.h" +#include "libavutil/file.h" + +static const char* leet_src = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +static const char* leet_dst = "abcd3f6#1jklmn0pq257uvwxyzAB(D3F6#1JKLMN0PQ257UVWXYZ"; + +enum TextModFilterType { + TM_TEXTMOD, + TM_CENSOR, + TM_SHOW_SPEAKER, +}; + +enum TextModOperation { + OP_LEET, + OP_TO_UPPER, + OP_TO_LOWER, + OP_REPLACE_CHARS, + OP_REMOVE_CHARS, + OP_REPLACE_WORDS, + OP_REMOVE_WORDS, + NB_OPS, +}; + +enum CensorMode { + CM_KEEP_FIRST_LAST, + CM_KEEP_FIRST, + CM_ALL, +}; + +enum ShowSpeakerMode { + SM_SQUARE_BRACKETS, + SM_ROUND_BRACKETS, + SM_COLON, + SM_PLAIN, +}; + +typedef struct TextModContext { + const AVClass *class; + enum AVSubtitleType format; + enum TextModFilterType filter_type; + enum TextModOperation operation; + enum CensorMode censor_mode; + enum ShowSpeakerMode speaker_mode; + char *find; + char *find_file; + char *style; + char *replace; + char *replace_file; + char *separator; + char *censor_char; + char **find_list; + int line_break; + int nb_find_list; + char **replace_list; + int nb_replace_list; +} TextModContext; + +static char **split_string(char *source, int *nb_elems, const char *delim) +{ + char **list = NULL; + char *temp = NULL; + char *ptr = av_strtok(source, delim, &temp); + + while (ptr) { + if (strlen(ptr)) { + av_dynarray_add(&list, nb_elems, ptr); + if (!list) + return NULL; + } + + ptr = av_strtok(NULL, delim, &temp); + } + + if (!list) + return NULL; + + for (int i = 0; i < *nb_elems; i++) { + list[i] = av_strdup(list[i]); + if (!list[i]) { + for (int n = 0; n < i; n++) + av_free(list[n]); + av_free(list); + return NULL; + } + } + + return list; +} + +static int load_text_from_file(AVFilterContext *ctx, const char *file_name, char **text, char separator) +{ + int err; + uint8_t *textbuf; + char *tmp; + size_t textbuf_size; + int offset = 0; + + if ((err = av_file_map(file_name, &textbuf, &textbuf_size, 0, ctx)) < 0) { + av_log(ctx, AV_LOG_ERROR, "The text file '%s' could not be read or is empty\n", file_name); + return err; + } + + if (textbuf_size > 1 && + (textbuf[0] == 0xFF && textbuf[1] == 0xFE + || textbuf[0] == 0xFE && textbuf[1] == 0xFF)) { + av_log(ctx, AV_LOG_ERROR, "UTF text files are not supported. File: %s\n", file_name); + return AVERROR(EINVAL); + } + + if (textbuf_size > 2 && textbuf[0] == 0xEF && textbuf[1] == 0xBB && textbuf[2] == 0xBF) + offset = 3; // UTF-8 + + if (textbuf_size > SIZE_MAX - 1 || !((tmp = av_strndup((char *)textbuf + offset, textbuf_size - offset)))) { + av_file_unmap(textbuf, textbuf_size); + return AVERROR(ENOMEM); + } + + av_file_unmap(textbuf, textbuf_size); + + for (size_t i = 0; i < strlen(tmp); i++) { + switch (tmp[i]) { + case '\n': + case '\r': + case '\f': + case '\v': + tmp[i] = separator; + } + } + + *text = tmp; + + return 0; +} + +static int load_files(AVFilterContext *ctx) +{ + TextModContext *s = ctx->priv; + int ret; + + if (!s->separator || strlen(s->separator) != 1) { + av_log(ctx, AV_LOG_ERROR, "A single character needs to be specified for the separator parameter.\n"); + return AVERROR(EINVAL); + } + + if (s->find_file && strlen(s->find_file)) { + ret = load_text_from_file(ctx, s->find_file, &s->find, s->separator[0]); + if (ret < 0 ) + return ret; + } + + if (s->replace_file && strlen(s->replace_file)) { + ret = load_text_from_file(ctx, s->replace_file, &s->replace, s->separator[0]); + if (ret < 0 ) + return ret; + } + + return 0; +} + +static int init_censor(AVFilterContext *ctx) +{ + TextModContext *s = ctx->priv; + int ret; + + s->filter_type = TM_CENSOR; + s->operation = OP_REPLACE_WORDS; + + ret = load_files(ctx); + if (ret < 0 ) + return ret; + + if (!s->find || !strlen(s->find)) { + av_log(ctx, AV_LOG_ERROR, "Either the 'words' or the 'words_file' parameter needs to be specified\n"); + return AVERROR(EINVAL); + } + + if (!s->censor_char || strlen(s->censor_char) != 1) { + av_log(ctx, AV_LOG_ERROR, "A single character needs to be specified for the censor_char parameter\n"); + return AVERROR(EINVAL); + } + + s->find_list = split_string(s->find, &s->nb_find_list, s->separator); + if (!s->find_list) + return AVERROR(ENOMEM); + + s->replace_list = av_calloc(s->nb_find_list, sizeof(char *)); + if (!s->replace_list) + return AVERROR(ENOMEM); + + for (int i = 0; i < s->nb_find_list; i++) { + size_t len, start = 0, end; + char *item = av_strdup(s->find_list[i]); + if (!item) + return AVERROR(ENOMEM); + + len = end = strlen(item); + + switch (s->censor_mode) { + case CM_KEEP_FIRST_LAST: + + if (len > 2) + start = 1; + if (len > 3) + end--; + + break; + case CM_KEEP_FIRST: + + if (len > 2) + start = 1; + + break; + } + + for (size_t n = start; n < end; n++) + item[n] = s->censor_char[0]; + + s->replace_list[i] = item; + } + + return 0; +} + +static int init_showspeaker(AVFilterContext *ctx) +{ + TextModContext *s = ctx->priv; + s->filter_type = TM_SHOW_SPEAKER; + + return 0; +} + +static int init(AVFilterContext *ctx) +{ + TextModContext *s = ctx->priv; + int ret; + + ret = load_files(ctx); + if (ret < 0 ) + return ret; + + switch (s->operation) { + case OP_REPLACE_CHARS: + case OP_REMOVE_CHARS: + case OP_REPLACE_WORDS: + case OP_REMOVE_WORDS: + if (!s->find || !strlen(s->find)) { + av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'find' parameter to be specified\n"); + return AVERROR(EINVAL); + } + break; + } + + switch (s->operation) { + case OP_REPLACE_CHARS: + case OP_REPLACE_WORDS: + if (!s->replace || !strlen(s->replace)) { + av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'replace' parameter to be specified\n"); + return AVERROR(EINVAL); + } + break; + } + + if (s->operation == OP_REPLACE_CHARS && strlen(s->find) != strlen(s->replace)) { + av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'find' and 'replace' parameters to have the same length\n"); + return AVERROR(EINVAL); + } + + if (s->operation == OP_REPLACE_WORDS || s->operation == OP_REMOVE_WORDS) { + if (!s->separator || strlen(s->separator) != 1) { + av_log(ctx, AV_LOG_ERROR, "Selected mode requires a single separator char to be specified\n"); + return AVERROR(EINVAL); + } + + s->find_list = split_string(s->find, &s->nb_find_list, s->separator); + if (!s->find_list) + return AVERROR(ENOMEM); + + if (s->operation == OP_REPLACE_WORDS) { + + s->replace_list = split_string(s->replace, &s->nb_replace_list, s->separator); + if (!s->replace_list) + return AVERROR(ENOMEM); + + if (s->nb_find_list != s->nb_replace_list) { + av_log(ctx, AV_LOG_ERROR, "The number of words in 'find' and 'replace' needs to be equal\n"); + return AVERROR(EINVAL); + } + } + } + + return 0; +} + +static void uninit(AVFilterContext *ctx) +{ + TextModContext *s = ctx->priv; + + for (int i = 0; i < s->nb_find_list; i++) + av_freep(&s->find_list[i]); + + s->nb_find_list = 0; + av_freep(&s->find_list); + + for (int i = 0; i < s->nb_replace_list; i++) + av_freep(&s->replace_list[i]); + + s->nb_replace_list = 0; + av_freep(&s->replace_list); +} + +static char *process_text(TextModContext *s, char *text) +{ + const char *char_src = s->find; + const char *char_dst = s->replace; + char *result = NULL; + int escape_level = 0, k = 0; + + switch (s->operation) { + case OP_LEET: + case OP_REPLACE_CHARS: + + if (s->operation == OP_LEET) { + char_src = leet_src; + char_dst = leet_dst; + } + + result = av_strdup(text); + if (!result) + return NULL; + + for (size_t n = 0; n < strlen(result); n++) { + if (result[n] == '{') + escape_level++; + + if (!escape_level) { + size_t len = strlen(char_src); + for (size_t t = 0; t < len; t++) { + if (result[n] == char_src[t]) { + result[n] = char_dst[t]; + break; + } + } + } + + if (result[n] == '}') + escape_level--; + } + + break; + case OP_TO_UPPER: + case OP_TO_LOWER: + + result = av_strdup(text); + if (!result) + return NULL; + + for (size_t n = 0; n < strlen(result); n++) { + if (result[n] == '{') + escape_level++; + if (!escape_level) + result[n] = s->operation == OP_TO_LOWER ? av_tolower(result[n]) : av_toupper(result[n]); + if (result[n] == '}') + escape_level--; + } + + break; + case OP_REMOVE_CHARS: + + result = av_strdup(text); + if (!result) + return NULL; + + for (size_t n = 0; n < strlen(result); n++) { + int skip_char = 0; + + if (result[n] == '{') + escape_level++; + + if (!escape_level) { + size_t len = strlen(char_src); + for (size_t t = 0; t < len; t++) { + if (result[n] == char_src[t]) { + skip_char = 1; + break; + } + } + } + + if (!skip_char) + result[k++] = result[n]; + + if (result[n] == '}') + escape_level--; + } + + result[k] = 0; + + break; + case OP_REPLACE_WORDS: + case OP_REMOVE_WORDS: + + result = av_strdup(text); + if (!result) + return NULL; + + for (int n = 0; n < s->nb_find_list; n++) { + char *tmp = result; + const char *replace = (s->operation == OP_REPLACE_WORDS) ? s->replace_list[n] : ""; + + result = av_strireplace(result, s->find_list[n], replace); + if (!result) + return NULL; + + av_free(tmp); + } + + break; + } + + return result; +} + +static char *process_dialog_show_speaker(TextModContext *s, char *ass_line) +{ + ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line); + int escape_level = 0; + unsigned pos = 0, len; + char *result, *text; + AVBPrint pbuf; + + if (!dialog) + return NULL; + + text = process_text(s, dialog->text); + if (!text) + return NULL; + + if (!dialog->name || !strlen(dialog->name) || !dialog->text || !strlen(dialog->text)) + return av_strdup(ass_line); + + // Find insertion point in case the line starts with style codes + len = (unsigned)strlen(dialog->text); + for (unsigned i = 0; i < len; i++) { + + if (dialog->text[i] == '{') + escape_level++; + + if (dialog->text[i] == '}') + escape_level--; + + if (escape_level == 0) { + pos = i; + break; + } + } + + if (s->style && strlen(s->style)) + // When a style is specified reset the insertion point + // (always add speaker plus style at the start in that case) + pos = 0; + + if (pos >= len - 1) + return av_strdup(ass_line); + + av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED); + + if (pos > 0) { + av_bprint_append_data(&pbuf, dialog->text, pos); + } + + if (s->style && strlen(s->style)) { + if (s->style[0] == '{') + // Assume complete and valid style code, e.g. {\c&HFF0000&} + av_bprintf(&pbuf, "%s", s->style); + else + // Otherwise it must be a style name + av_bprintf(&pbuf, "{\\r%s}", s->style); + } + + switch (s->speaker_mode) { + case SM_SQUARE_BRACKETS: + av_bprintf(&pbuf, "[%s]", dialog->name); + break; + case SM_ROUND_BRACKETS: + av_bprintf(&pbuf, "(%s)", dialog->name); + break; + case SM_COLON: + av_bprintf(&pbuf, "%s:", dialog->name); + break; + case SM_PLAIN: + av_bprintf(&pbuf, "%s", dialog->name); + break; + } + + if (s->style && strlen(s->style)) { + // Reset line style + if (dialog->style && strlen(dialog->style) && !av_strcasecmp(dialog->style, "default")) + av_bprintf(&pbuf, "{\\r%s}", dialog->style); + else + av_bprintf(&pbuf, "{\\r}"); + } + + if (s->line_break) + av_bprintf(&pbuf, "\\N"); + else + av_bprintf(&pbuf, " "); + + av_bprint_append_data(&pbuf, dialog->text + pos, len - pos); + + av_bprint_finalize(&pbuf, &text); + + result = avpriv_ass_get_dialog(dialog->readorder, dialog->layer, dialog->style, dialog->name, text); + + av_free(text); + avpriv_ass_free_dialog(&dialog); + return result; +} + +static char *process_dialog(TextModContext *s, char *ass_line) +{ + ASSDialog *dialog; + char *result, *text; + + if (s->filter_type == TM_SHOW_SPEAKER) + return process_dialog_show_speaker(s, ass_line); + + dialog = avpriv_ass_split_dialog(NULL, ass_line); + if (!dialog) + return NULL; + + text = process_text(s, dialog->text); + if (!text) + return NULL; + + result = avpriv_ass_get_dialog(dialog->readorder, dialog->layer, dialog->style, dialog->name, text); + + av_free(text); + avpriv_ass_free_dialog(&dialog); + return result; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterLink *inlink = outlink->src->inputs[0]; + + outlink->w = inlink->w; + outlink->h = inlink->h; + outlink->time_base = inlink->time_base; + outlink->frame_rate = inlink->frame_rate; + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + TextModContext *s = inlink->dst->priv; + AVFilterLink *outlink = inlink->dst->outputs[0]; + int ret; + + outlink->format = inlink->format; + + ret = av_frame_make_writable(frame); + if (ret < 0) + return ret; + + for (unsigned i = 0; i < frame->num_subtitle_areas; i++) { + + AVSubtitleArea *area = frame->subtitle_areas[i]; + + if (area->ass) { + char *tmp = area->ass; + area->ass = process_dialog(s, area->ass); + av_free(tmp); + if (!area->ass) + return AVERROR(ENOMEM); + } + } + + return ff_filter_frame(outlink, frame); +} + +#define OFFSET(x) offsetof(TextModContext, x) +#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM) + +static const AVOption textmod_options[] = { + { "mode", "set operation mode", OFFSET(operation), AV_OPT_TYPE_INT, {.i64=OP_LEET}, OP_LEET, NB_OPS-1, FLAGS, "mode" }, + { "leet", "convert text to 'leet speak'", 0, AV_OPT_TYPE_CONST, {.i64=OP_LEET}, 0, 0, FLAGS, "mode" }, + { "to_upper", "change to upper case", 0, AV_OPT_TYPE_CONST, {.i64=OP_TO_UPPER}, 0, 0, FLAGS, "mode" }, + { "to_lower", "change to lower case", 0, AV_OPT_TYPE_CONST, {.i64=OP_TO_LOWER}, 0, 0, FLAGS, "mode" }, + { "replace_chars", "replace characters", 0, AV_OPT_TYPE_CONST, {.i64=OP_REPLACE_CHARS}, 0, 0, FLAGS, "mode" }, + { "remove_chars", "remove characters", 0, AV_OPT_TYPE_CONST, {.i64=OP_REMOVE_CHARS}, 0, 0, FLAGS, "mode" }, + { "replace_words", "replace words", 0, AV_OPT_TYPE_CONST, {.i64=OP_REPLACE_WORDS}, 0, 0, FLAGS, "mode" }, + { "remove_words", "remove words", 0, AV_OPT_TYPE_CONST, {.i64=OP_REMOVE_WORDS}, 0, 0, FLAGS, "mode" }, + { "find", "chars/words to find or remove", OFFSET(find), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS, NULL }, + { "find_file", "load find param from file", OFFSET(find_file), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS, NULL }, + { "replace", "chars/words to replace", OFFSET(replace), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS, NULL }, + { "replace_file", "load replace param from file", OFFSET(replace_file), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS, NULL }, + { "separator", "word separator", OFFSET(separator), AV_OPT_TYPE_STRING, {.str = ","}, 0, 0, FLAGS, NULL }, + { NULL }, +}; + + +static const AVOption censor_options[] = { + { "mode", "set censoring mode", OFFSET(censor_mode), AV_OPT_TYPE_INT, {.i64=CM_KEEP_FIRST_LAST}, 0, 2, FLAGS, "mode" }, + { "keep_first_last", "censor inner chars", 0, AV_OPT_TYPE_CONST, {.i64=CM_KEEP_FIRST_LAST}, 0, 0, FLAGS, "mode" }, + { "keep_first", "censor all but first char", 0, AV_OPT_TYPE_CONST, {.i64=CM_KEEP_FIRST}, 0, 0, FLAGS, "mode" }, + { "all", "censor all chars", 0, AV_OPT_TYPE_CONST, {.i64=CM_ALL}, 0, 0, FLAGS, "mode" }, + { "words", "list of words to censor", OFFSET(find), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS, NULL }, + { "words_file", "path to word list file", OFFSET(find_file), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS, NULL }, + { "separator", "word separator", OFFSET(separator), AV_OPT_TYPE_STRING, {.str = ","}, 0, 0, FLAGS, NULL }, + { "censor_char", "replacement character", OFFSET(censor_char), AV_OPT_TYPE_STRING, {.str = "*"}, 0, 0, FLAGS, NULL }, + { NULL }, +}; + +static const AVOption showspeaker_options[] = { + { "format", "speaker name formatting", OFFSET(speaker_mode), AV_OPT_TYPE_INT, {.i64=SM_SQUARE_BRACKETS}, 0, 2, FLAGS, "format" }, + { "square_brackets", "[speaker] text", 0, AV_OPT_TYPE_CONST, {.i64=SM_SQUARE_BRACKETS}, 0, 0, FLAGS, "format" }, + { "round_brackets", "(speaker) text", 0, AV_OPT_TYPE_CONST, {.i64=SM_ROUND_BRACKETS}, 0, 0, FLAGS, "format" }, + { "colon", "speaker: text", 0, AV_OPT_TYPE_CONST, {.i64=SM_COLON}, 0, 0, FLAGS, "format" }, + { "plain", "speaker text", 0, AV_OPT_TYPE_CONST, {.i64=SM_PLAIN}, 0, 0, FLAGS, "format" }, + { "line_break", "insert line break", OFFSET(line_break), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, NULL }, + { "style", "ass type name or style code", OFFSET(style), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS, NULL }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(textmod); +AVFILTER_DEFINE_CLASS(censor); +AVFILTER_DEFINE_CLASS(showspeaker); + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .filter_frame = filter_frame, + }, +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .config_props = config_output, + }, +}; + +const AVFilter ff_sf_textmod = { + .name = "textmod", + .description = NULL_IF_CONFIG_SMALL("Modify subtitle text in several ways"), + .init = init, + .uninit = uninit, + .priv_size = sizeof(TextModContext), + .priv_class = &textmod_class, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS), +}; + +const AVFilter ff_sf_censor = { + .name = "censor", + .description = NULL_IF_CONFIG_SMALL("Censor words in subtitle text"), + .init = init_censor, + .uninit = uninit, + .priv_size = sizeof(TextModContext), + .priv_class = &censor_class, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS), +}; + +const AVFilter ff_sf_showspeaker = { + .name = "showspeaker", + .description = NULL_IF_CONFIG_SMALL("Prepend speaker names to text subtitles (when available)"), + .init = init_showspeaker, + .uninit = uninit, + .priv_size = sizeof(TextModContext), + .priv_class = &showspeaker_class, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS), +}; From patchwork Fri Jan 14 01:13:25 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33578 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp986649iog; Thu, 13 Jan 2022 17:16:34 -0800 (PST) X-Google-Smtp-Source: ABdhPJzxpt5YH1UPUIiQ8hVrZBa6w3Qctu45pJALcwErZgF7mcv75ZaoZg+oCxyfpcDIAUuspu41 X-Received: by 2002:a17:906:43c9:: with SMTP id j9mr5548484ejn.136.1642122993972; Thu, 13 Jan 2022 17:16:33 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642122993; cv=none; d=google.com; s=arc-20160816; b=CTCeTtvsZTGThtHjcpamrPOMruRqs3cA3ROr6/ZBYZ3fR1PcZt78z6g1Shfna2Dl+4 Tap6sBybDE6z6KFnWFg0YgN0HZ6VbInos8htIpp6LvRIHsOkzzIK75atLGuaXWXmScR3 St1OQBs5XlM5HebLWVOc3kllRZf0ZMDQ5/CoG/JFCxs3ABl0StgL4jh9JruGpvj2ecHk sCoV2zP7Gjp9aq7PPT3KT2yh1oNMNQvD9DNrnlMSGqtR5+n/3GKhjT/e8v0pbIZY0wxy Wy4eujMquDX/Iur012jpeVjFvkGoBtT1gbelT+zHBRE8zaT9uxQn9QFwdTgeK/T5njcL iavQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=14+ycTYsA0G1qsQqV8U7ncqD5Tgn776zhUGnfZHBJ8E=; b=xY0OHM8wstQpBOmqNogLDiNDen5oAE4wDVcjwHPkUW62EfCGUc9YT6/ysMEu+P2jsf IEBb4+sVJQ/mR2BZgw/J5WHIqWLl4Zj8HI8M8iR9Z6D7Ot5q1VP/skgLhKRj7/meJpiA xUzmbYhYdrZLZo7XTMXZfx1b51y0677taEjjGYzdmjFunYHadb+RHC9074WhhpvgVrQf vFz1vAAzhh/9M9sIV8Fc7N7vRzJJJpJSMEvEpycbchPaClCTzbQ/hVdI0yL09AaRHAoV AWJY+9LUg6lGT+qYKgCO5Xe1zezGwUnesnIaYkSokazb+T/wHtqgR4c07UW5dZIwYLmv AIhQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=fPMWKNIM; 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 ca15si1951390edb.173.2022.01.13.17.16.33; Thu, 13 Jan 2022 17:16:33 -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 header.s=20210112 header.b=fPMWKNIM; 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 EFD0968B62B; Fri, 14 Jan 2022 03:14:02 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f177.google.com (mail-pf1-f177.google.com [209.85.210.177]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 615DE68B5AA for ; Fri, 14 Jan 2022 03:13:54 +0200 (EET) Received: by mail-pf1-f177.google.com with SMTP id a5so1397080pfo.5 for ; Thu, 13 Jan 2022 17:13:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=wKnZr/gK4BeIukBWXqq6H44TZ3N8ipLRX+/0LtcrXC4=; b=fPMWKNIMzzpk8fY/RmeLsSlQNui5p0ad53/Nm0a2nQth7z0YIcnIRlHiUG7DnoIYoG Ows8/M7dsmyLzB/FajXFPqrO8UR4P/CDE3oflv/sXH3l250BXlFu5vpvOWH6lOy8QJAY HZyo1tw4pvquxMomv4C/+QuJ6DZZHmhysnoeuiOrb/pBYJkBQJ+N+IZEOwn61t3yIntV VmNp0y+vSWxyzfSMf8EduxoEBt6RVv1FJd+9i2DynTL76YXCXr3SKJLp+566uFIbobLp 2z0WwuudhNGw47CNyJwEWRchWsAxFgMOmmUo9Us/BMj0Se1l+XQ+9kgdHupIK+t0bIIS cGiA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=wKnZr/gK4BeIukBWXqq6H44TZ3N8ipLRX+/0LtcrXC4=; b=m7w/knT19FSbSB+7ALJvPfbrFmK6EhCiwkQtFibCPWIif9gQ6TgtooP/P7+He9FjcZ XAU/eZoOtDrNr3BEnuuT4rylAh5gW+mrgSlBzmtVV9M/fBl/+Kct0YSMI/ogm23NiNWj jfvXLKvI/1mdD46DJ2IcCqqQY5WsI+87Lv5RKEHXvCHHNTZHjzVP6+Itclxuph5Vah32 qXxnGywe7a/tIWO0jzckKOu6DejApY6A65s2cGR5sSqFzcB4ccrTy57j8I5fo4b0zPps S2tRfsJUWdxpOVvj/ySYCNvjE7iKnNFu+qoiYtO5WjdGFR+1nVDRy53ZrwMFdyB+Ajyv 7xIg== X-Gm-Message-State: AOAM530ClQPfMO7Dz4RDYBG94lFRMVjEZ++FX+dCWros2IpKasPvUKQF yvAc9dQETc0sReyf4DQ5OoMeiD3NbGsGVw== X-Received: by 2002:a63:fd53:: with SMTP id m19mr5920252pgj.563.1642122832520; Thu, 13 Jan 2022 17:13:52 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id h21sm3633437pfo.38.2022.01.13.17.13.51 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:51 -0800 (PST) Message-Id: <54af3f8d07b4bfc3cb65b4cdd3e867212f065f14.1642122814.git.ffmpegagent@gmail.com> In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:25 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 16/24] avfilter/stripstyles: Add stripstyles filter 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 1hVYAsOQod3e From: softworkz - stripstyles {S -> S) Remove all inline styles from subtitle events Signed-off-by: softworkz --- doc/filters.texi | 37 +++++++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/sf_stripstyles.c | 209 +++++++++++++++++++++++++++++++++++ 4 files changed, 248 insertions(+) create mode 100644 libavfilter/sf_stripstyles.c diff --git a/doc/filters.texi b/doc/filters.texi index 494ee6f062..c0f0fe13e7 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25781,6 +25781,43 @@ ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex. @end example @end itemize +@section stripstyles + +Remove all inline styles from subtitle events. + +Inputs: +@itemize +@item 0: Subtitles[TEXT] +@end itemize + +Outputs: +@itemize +@item 0: Subtitles[TEXT] +@end itemize + +It accepts the following parameters: + +@table @option +@item remove_animated +Also remove text which is subject to animation (default: true) +Usually, animated text elements are used used in addition to static subtitle lines for creating effects, so in most cases it is safe to remove the animation content. +If subtitle text is missing, try setting this to false. + +@item select_layer +Process only ASS subtitle events from a specific layer. This allows to filter out certain effects where an ASS author duplicates the text onto multiple layers. + +@end table + +@subsection Examples + +@itemize +@item +Remove styles and animations from ASS subtitles and output events from ass layer 0 only. Then convert asn save as SRT stream: +@example +ffmpeg -i "https://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:1]stripstyles=select_layer=0" -map 0 -c:s srt output.mkv +@end example +@end itemize + @section textmod diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 1af4f4b9bc..d330020f67 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -565,6 +565,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER) += vsink_nullsink.o OBJS-$(CONFIG_CENSOR_FILTER) += sf_textmod.o OBJS-$(CONFIG_SHOW_SPEAKER_FILTER) += sf_textmod.o OBJS-$(CONFIG_TEXTMOD_FILTER) += sf_textmod.o +OBJS-$(CONFIG_STRIPSTYLES_FILTER) += sf_stripstyles.o # multimedia filters OBJS-$(CONFIG_ABITSCOPE_FILTER) += avf_abitscope.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index ac7d71547b..b0c12595af 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -549,6 +549,7 @@ extern const AVFilter ff_avf_showwavespic; extern const AVFilter ff_vaf_spectrumsynth; extern const AVFilter ff_sf_censor; extern const AVFilter ff_sf_showspeaker; +extern const AVFilter ff_sf_stripstyles; extern const AVFilter ff_sf_textmod; extern const AVFilter ff_svf_graphicsub2video; extern const AVFilter ff_svf_textsub2video; diff --git a/libavfilter/sf_stripstyles.c b/libavfilter/sf_stripstyles.c new file mode 100644 index 0000000000..bbd731cc03 --- /dev/null +++ b/libavfilter/sf_stripstyles.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2021 softworkz + * + * 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 + * text subtitle filter which removes inline-styles from subtitles + */ + +#include "libavutil/opt.h" +#include "internal.h" +#include "libavutil/ass_split_internal.h" +#include "libavutil/bprint.h" + +typedef struct StripStylesContext { + const AVClass *class; + enum AVSubtitleType format; + int remove_animated; + int select_layer; +} StripStylesContext; + +typedef struct DialogContext { + StripStylesContext* ss_ctx; + AVBPrint buffer; + int drawing_scale; + int is_animated; +} DialogContext; + +static void dialog_text_cb(void *priv, const char *text, int len) +{ + DialogContext *s = priv; + + av_log(s->ss_ctx, AV_LOG_DEBUG, "dialog_text_cb: %s\n", text); + + if (!s->drawing_scale && (!s->is_animated || !s->ss_ctx->remove_animated)) + av_bprint_append_data(&s->buffer, text, len); +} + +static void dialog_new_line_cb(void *priv, int forced) +{ + DialogContext *s = priv; + if (!s->drawing_scale && !s->is_animated) + av_bprint_append_data(&s->buffer, forced ? "\\N" : "\\n", 2); +} + +static void dialog_drawing_mode_cb(void *priv, int scale) +{ + DialogContext *s = priv; + s->drawing_scale = scale; +} + +static void dialog_animate_cb(void *priv, int t1, int t2, int accel, char *style) +{ + DialogContext *s = priv; + s->is_animated = 1; +} + +static void dialog_move_cb(void *priv, int x1, int y1, int x2, int y2, int t1, int t2) +{ + DialogContext *s = priv; + if (t1 >= 0 || t2 >= 0) + s->is_animated = 1; +} + +static const ASSCodesCallbacks dialog_callbacks = { + .text = dialog_text_cb, + .new_line = dialog_new_line_cb, + .drawing_mode = dialog_drawing_mode_cb, + .animate = dialog_animate_cb, + .move = dialog_move_cb, +}; + +static char *ass_get_line(int readorder, int layer, const char *style, + const char *speaker, const char *effect, const char *text) +{ + return av_asprintf("%d,%d,%s,%s,0,0,0,%s,%s", + readorder, layer, style ? style : "Default", + speaker ? speaker : "", effect, text); +} + +static char *process_dialog(StripStylesContext *s, const char *ass_line) +{ + DialogContext dlg_ctx = { .ss_ctx = s }; + ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line); + char *result = NULL; + + if (!dialog) + return NULL; + + if (s->select_layer >= 0 && dialog->layer != s->select_layer) + return NULL; + + dlg_ctx.ss_ctx = s; + + av_bprint_init(&dlg_ctx.buffer, 0, AV_BPRINT_SIZE_UNLIMITED); + + avpriv_ass_split_override_codes(&dialog_callbacks, &dlg_ctx, dialog->text); + + if (av_bprint_is_complete(&dlg_ctx.buffer) + && dlg_ctx.buffer.len > 0) + result = ass_get_line(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->effect, dlg_ctx.buffer.str); + + av_bprint_finalize(&dlg_ctx.buffer, NULL); + avpriv_ass_free_dialog(&dialog); + + return result; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterLink *inlink = outlink->src->inputs[0]; + + outlink->w = inlink->w; + outlink->h = inlink->h; + outlink->time_base = inlink->time_base; + outlink->frame_rate = inlink->frame_rate; + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + StripStylesContext *s = inlink->dst->priv; + AVFilterLink *outlink = inlink->dst->outputs[0]; + int ret; + + outlink->format = inlink->format; + + ret = av_frame_make_writable(frame); + if (ret <0 ) { + av_frame_free(&frame); + return AVERROR(ENOMEM); + } + + for (unsigned i = 0; i < frame->num_subtitle_areas; i++) { + + AVSubtitleArea *area = frame->subtitle_areas[i]; + + if (area->ass) { + char *tmp = area->ass; + area->ass = process_dialog(s, area->ass); + + if (area->ass) { + av_log(inlink->dst, AV_LOG_INFO, "original: %d %s\n", i, tmp); + av_log(inlink->dst, AV_LOG_INFO, "stripped: %d %s\n", i, area->ass); + } + else + area->ass = NULL; + + av_free(tmp); + } + } + + return ff_filter_frame(outlink, frame); +} + +#define OFFSET(x) offsetof(StripStylesContext, x) +#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM) + +static const AVOption stripstyles_options[] = { + { "remove_animated", "remove animated text (default: yes)", OFFSET(remove_animated), AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, FLAGS, 0 }, + { "select_layer", "process a specific ass layer only", OFFSET(remove_animated), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, FLAGS, 0 }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(stripstyles); + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .filter_frame = filter_frame, + }, +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .config_props = config_output, + }, +}; + +const AVFilter ff_sf_stripstyles = { + .name = "stripstyles", + .description = NULL_IF_CONFIG_SMALL("Strip subtitle inline styles"), + .priv_size = sizeof(StripStylesContext), + .priv_class = &stripstyles_class, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS), +}; + From patchwork Fri Jan 14 01:13:26 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33579 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp986766iog; Thu, 13 Jan 2022 17:16:44 -0800 (PST) X-Google-Smtp-Source: ABdhPJxdtIuEvG6Y2faUJK0lVfbNr7pidsJmIQbsqmz2e7rLUc4ZTbOC4qMfdRJMKzUQPEK6cKOD X-Received: by 2002:a17:906:6a90:: with SMTP id p16mr5430837ejr.115.1642123003845; Thu, 13 Jan 2022 17:16:43 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642123003; cv=none; d=google.com; s=arc-20160816; b=0QktfiwqlmSe+yjI5PYJZh8d2T8pADyX7M84uztcEyOj5idDESy5NV748gPEmoWolg W5xLMTw1m1UiT1NwV17Bi335e1zc44qpoHS+jW+jSvQAN7LwD3WA9f40Id7hNvRrIwdr MFgCyPfi2sUoNwgbF6pCHv6LYnRBfQj6YbSJSBr5ivRFiFkRWR/QQcfH2II1vRsgT1OJ YoMXpLeOEtYA3Y0Ktch7OWwtsqAN3ATAY/Sh0wPpIuC1vwf449AcxhQ0G99tk7+potZS 7/Vb5yVfh9jfnnTyKI1EycityYzopLe2PV1PWW5hWRVNAAF+jq03KKM6RcIGSqchGSpL jo7g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=FPSDTFl2ziIWR9p3DZSF5DimSsoR+Xsws0jwB/ukdgI=; b=Qo4X5nhhpyxa4XofIaBT7hy839Vw7AE/AvtGQ0PZG0U4KCRP5XXw5/Nagpkjn+hzqi 8Evnp648W9iUwxe/CooE9KEonIyUs0lBUeRXLvoOkfzwJ935/cn08QgzkL6esYUydGJF XqVSPqfeDKBJafmK8qjf3nLKqrcTt/0cwEm4NXk11x5kuClBOHx1A4vRMDi/kCSpV8ao YMr54KBgEFx+QMGdkM9kFJzLBcd9Y+rBKUCVuNpKtTIgawJDlhvWpPOKiHjREM36hXEt HZDEQgn6CNrWWd4QxkG508gj1HiAXNTLuA/WPAUMJJYwI0tD4vdWNqq/4FtOu2An34MS xTKg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=RdylxyRD; 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 cb24si178233edb.244.2022.01.13.17.16.43; Thu, 13 Jan 2022 17:16:43 -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 header.s=20210112 header.b=RdylxyRD; 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 04A7368B69A; Fri, 14 Jan 2022 03:14:04 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f169.google.com (mail-pf1-f169.google.com [209.85.210.169]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 741EA68B610 for ; Fri, 14 Jan 2022 03:13:55 +0200 (EET) Received: by mail-pf1-f169.google.com with SMTP id m21so1403352pfd.3 for ; Thu, 13 Jan 2022 17:13:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=M/UOFNQ0acvmGq+22IuwFaJwDOXZbk1UVLih3uTvHE0=; b=RdylxyRDs7CBWVBTNNN6m13dLry5OBSAGGT89ArSEJ0IPiJpMlwNaeT3DAOS6Gb18n 3Y43Y0lvUIDAWkG5GyegleiJxT8b7jMMI1t20scLj6+6i3vHxEzbOblx+h4nyQTJyHds FtMfjPz7VD+xvDffV33AI0NHi2Ja2H2P53igkDZzB16PxCst6ZFbcUvAgKHDYXW0qfAW rtVbZSmS6tb0Qs14nZj+C4E0QrzANsTb/v4Ayj8oUiWVwIWWUeZlgvRh//AipZQCVXQ3 7E9M9zIuLwTPIhFeOKTcckNWuXqn4g0m6xaECinZenxAL2WddwqYC1ptv7lOAjPuguhH kYEQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=M/UOFNQ0acvmGq+22IuwFaJwDOXZbk1UVLih3uTvHE0=; b=CbPocRvURVYjAo6+ClzaKM7ZNDgcxe2tVkViWHUPF4RxEtqhwQwprPlLCWeXnQ69Ib e/p0K+tQfGzPrtQ+AuxZayyRE5WlsECigwDiLSnXuwDkDblbpXpeVQM0ZAYR66YuUzJL wmPhxK/HCnHc/ZPs7OsXJSub0h7BQd3N47gFZqsWeSlXA79qKH7dMdGvg9HBcZpGOmh2 peovK+l6zNY007Ob1I/+2r0KkMXjuHYPERpGeUXXyzjvVnqAkbeMkDdV/yN0DaZ0l0IT 7qQqxh3shX653ZF0ReQiWmW5iy/9pRdZ3EyxRbthONVCN21VwgL8eYDNd4mOGkEkridr hOMQ== X-Gm-Message-State: AOAM5316EixV+rlWqiXa2V1orp4PB7v/1TeMsFL2LV2SSlScX97tk4hO /nRS2Bo1Fxl4TfNbcE/Q5tPG0d9zygimhA== X-Received: by 2002:a63:79c3:: with SMTP id u186mr6176404pgc.488.1642122833567; Thu, 13 Jan 2022 17:13:53 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id f4sm4034869pfj.25.2022.01.13.17.13.52 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:53 -0800 (PST) Message-Id: In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:26 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 17/24] avfilter/splitcc: Add splitcc filter for closed caption handling 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: gt0aCFmPtmpD From: softworkz - splitcc {V -> VS) Extract closed-caption (A53) data from video frames as subtitle Frames ffmpeg -y -loglevel verbose -i "https://streams.videolan.org/streams /ts/CC/NewsStream-608-ac3.ts" -filter_complex "[0:v]splitcc[vid1], textmod=mode=remove_chars:find='@',[vid1]overlay_textsubs" output.mkv Signed-off-by: softworkz --- configure | 1 + doc/filters.texi | 63 +++++++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/sf_splitcc.c | 385 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 451 insertions(+) create mode 100644 libavfilter/sf_splitcc.c diff --git a/configure b/configure index 340a198fa6..c1d2bc41c2 100755 --- a/configure +++ b/configure @@ -3729,6 +3729,7 @@ spp_filter_select="fft idctdsp fdctdsp me_cmp pixblockdsp" sr_filter_deps="avformat swscale" sr_filter_select="dnn" stereo3d_filter_deps="gpl" +splitcc_filter_deps="avcodec" subtitles_filter_deps="avformat avcodec libass" super2xsai_filter_deps="gpl" pixfmts_super2xsai_test_deps="super2xsai_filter" diff --git a/doc/filters.texi b/doc/filters.texi index c0f0fe13e7..265a267e9d 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -26131,6 +26131,69 @@ ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\ @end example @end itemize + +@section splitcc + +Split-out closed-caption/A53 subtitles from video frame side data. + +This filter provides an input and an output for video frames, which are just passed through without modification. +The second out provides subtitle frames which are extracted from video frame side data. + +Inputs: +@itemize +@item 0: Video [ALL] +@end itemize + +Outputs: +@itemize +@item 0: Video (same as input) +@item 1: Subtitles [TEXT] +@end itemize + +It accepts the following parameters: + +@table @option + +@item use_cc_styles +Emit closed caption style header. +This will make closed captions appear in white font with a black rectangle background. + +@item real_time +Emit subtitle events as they are decoded for real-time display. + +@item real_time_latency_msec +Minimum elapsed time between emitting real-time subtitle events. +Only applies to real_time mode. + +@item data_field +Select data field. Possible values: + +@table @samp +@item auto +Pick first one that appears. +@item first +@item second +@end table + +@end table + +@subsection Examples + +@itemize +@item +Extract closed captions as text subtitle stream and overlay it onto the video in cc style (black bar background): +@example +ffmpeg -i "https://streams.videolan.org/streams/ts/CC/NewsStream-608-ac3.ts" -filter_complex "[0:v:0]splitcc=use_cc_styles=1[vid1][sub1];[vid1][sub1]overlaytextsubs" output.mkv +@end example + +@item +A nicer variant, using realtime output from cc_dec and rendering it with the render_latest_only parameter from overlaytextsubs to avoid ghosting by timely overlap. +@example +ffmpeg -i "https://streams.videolan.org/streams/ts/CC/NewsStream-608-ac3.ts" -filter_complex "[0:v:0]splitcc=real_time=1:real_time_latency_msec=200[vid1][sub1];[vid1][sub1]overlaytextsubs=render_latest_only=1" output.mkv +@end example +@end itemize + + @section textsub2video Converts text subtitles to video frames. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index d330020f67..c6a4a4f5ae 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -565,6 +565,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER) += vsink_nullsink.o OBJS-$(CONFIG_CENSOR_FILTER) += sf_textmod.o OBJS-$(CONFIG_SHOW_SPEAKER_FILTER) += sf_textmod.o OBJS-$(CONFIG_TEXTMOD_FILTER) += sf_textmod.o +OBJS-$(CONFIG_SPLITCC_FILTER) += sf_splitcc.o OBJS-$(CONFIG_STRIPSTYLES_FILTER) += sf_stripstyles.o # multimedia filters diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index b0c12595af..50498e8ec4 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -549,6 +549,7 @@ extern const AVFilter ff_avf_showwavespic; extern const AVFilter ff_vaf_spectrumsynth; extern const AVFilter ff_sf_censor; extern const AVFilter ff_sf_showspeaker; +extern const AVFilter ff_sf_splitcc; extern const AVFilter ff_sf_stripstyles; extern const AVFilter ff_sf_textmod; extern const AVFilter ff_svf_graphicsub2video; diff --git a/libavfilter/sf_splitcc.c b/libavfilter/sf_splitcc.c new file mode 100644 index 0000000000..084f5f6969 --- /dev/null +++ b/libavfilter/sf_splitcc.c @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2021 softworkz + * + * 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 + * subtitle filter for splitting out closed-caption/A53 subtitles from video frame side data + */ + +#include "filters.h" +#include "libavutil/opt.h" +#include "subtitles.h" +#include "libavcodec/avcodec.h" + +static const AVRational ms_tb = {1, 1000}; + +typedef struct SplitCaptionsContext { + const AVClass *class; + enum AVSubtitleType format; + AVCodecContext *cc_dec; + int eof; + AVFrame *next_sub_frame; + AVFrame *empty_sub_frame; + int new_frame; + int64_t next_repetition_pts; + AVBufferRef *subtitle_header; + int use_cc_styles; + int real_time; + int real_time_latency_msec; + int data_field; + int scatter_realtime_output; +} SplitCaptionsContext; + +static int64_t ms_to_avtb(int64_t ms) +{ + return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q); +} + +static int init(AVFilterContext *ctx) +{ + SplitCaptionsContext *s = ctx->priv; + AVDictionary *options = NULL; + + int ret; + const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_EIA_608); + if (!codec) { + av_log(ctx, AV_LOG_ERROR, "failed to find EIA-608/708 decoder\n"); + return AVERROR_DECODER_NOT_FOUND; + } + + if (!((s->cc_dec = avcodec_alloc_context3(codec)))) { + av_log(ctx, AV_LOG_ERROR, "failed to allocate EIA-608/708 decoder\n"); + return AVERROR(ENOMEM); + } + + av_dict_set_int(&options, "real_time", s->real_time, 0); + av_dict_set_int(&options, "real_time_latency_msec", s->real_time_latency_msec, 0); + av_dict_set_int(&options, "data_field", s->data_field, 0); + + if ((ret = avcodec_open2(s->cc_dec, codec, &options)) < 0) { + av_log(ctx, AV_LOG_ERROR, "failed to open EIA-608/708 decoder: %i\n", ret); + return ret; + } + + if (s->use_cc_styles && s->cc_dec->subtitle_header && s->cc_dec->subtitle_header[0] != 0) { + char* subtitle_header = av_strdup((char *)s->cc_dec->subtitle_header); + if (!subtitle_header) + return AVERROR(ENOMEM); + s->subtitle_header = av_buffer_create((uint8_t *)subtitle_header, strlen(subtitle_header) + 1, NULL, NULL, 0); + if (!s->subtitle_header) { + av_free(subtitle_header); + return AVERROR(ENOMEM); + } + } + + return 0; +} + +static void uninit(AVFilterContext *ctx) +{ + SplitCaptionsContext *s = ctx->priv; + av_frame_free(&s->next_sub_frame); + av_frame_free(&s->empty_sub_frame); + av_buffer_unref(&s->subtitle_header); +} + +static int config_input(AVFilterLink *link) +{ + const SplitCaptionsContext *context = link->dst->priv; + + if (context->cc_dec) + context->cc_dec->pkt_timebase = link->time_base; + + return 0; +} + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats; + AVFilterLink *inlink0 = ctx->inputs[0]; + AVFilterLink *outlink0 = ctx->outputs[0]; + AVFilterLink *outlink1 = ctx->outputs[1]; + static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NB }; + int ret; + + /* set input0 video formats */ + formats = ff_all_formats(AVMEDIA_TYPE_VIDEO); + if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0) + return ret; + + /* set output0 video formats */ + if ((ret = ff_formats_ref(formats, &outlink0->incfg.formats)) < 0) + return ret; + + /* set output1 subtitle formats */ + formats = ff_make_format_list(subtitle_fmts); + if ((ret = ff_formats_ref(formats, &outlink1->incfg.formats)) < 0) + return ret; + + return 0; +} + +static int config_video_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + const AVFilterLink *inlink = outlink->src->inputs[0]; + + outlink->w = ctx->inputs[0]->w; + outlink->h = ctx->inputs[0]->h; + outlink->frame_rate = ctx->inputs[0]->frame_rate; + outlink->time_base = ctx->inputs[0]->time_base; + outlink->sample_aspect_ratio = ctx->inputs[0]->sample_aspect_ratio; + + if (inlink->hw_frames_ctx) + outlink->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx); + + return 0; +} + +static int config_sub_output(AVFilterLink *outlink) +{ + SplitCaptionsContext *s = outlink->src->priv; + const AVFilterLink *inlink = outlink->src->inputs[0]; + + outlink->time_base = inlink->time_base; + outlink->format = AV_SUBTITLE_FMT_ASS; + outlink->frame_rate = (AVRational){1000, s->real_time_latency_msec}; + + return 0; +} + +static int request_sub_frame(AVFilterLink *outlink) +{ + SplitCaptionsContext *s = outlink->src->priv; + int status; + int64_t pts; + + if (!s->empty_sub_frame) { + s->empty_sub_frame = ff_get_subtitles_buffer(outlink, outlink->format); + if (!s->empty_sub_frame) + return AVERROR(ENOMEM); + } + + if (!s->eof && ff_inlink_acknowledge_status(outlink->src->inputs[0], &status, &pts)) { + if (status == AVERROR_EOF) + s->eof = 1; + } + + if (s->eof) + return AVERROR_EOF; + + if (s->next_sub_frame) { + + AVFrame *out = NULL; + s->next_sub_frame->pts++; + + if (s->new_frame) + out = av_frame_clone(s->next_sub_frame); + else if (s->empty_sub_frame) { + s->empty_sub_frame->pts = s->next_sub_frame->pts; + out = av_frame_clone(s->empty_sub_frame); + av_frame_copy_props(out, s->next_sub_frame); + out->repeat_sub = 1; + } + + if (!out) + return AVERROR(ENOMEM); + + out->subtitle_timing.start_pts = av_rescale_q(s->next_sub_frame->pts, outlink->time_base, AV_TIME_BASE_Q); + s->new_frame = 0; + + return ff_filter_frame(outlink, out); + } + + return 0; +} + +static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt) +{ + int ret; + + *got_frame = 0; + + if (pkt) { + ret = avcodec_send_packet(avctx, pkt); + // In particular, we don't expect AVERROR(EAGAIN), because we read all + // decoded frames with avcodec_receive_frame() until done. + if (ret < 0 && ret != AVERROR_EOF) + return ret; + } + + ret = avcodec_receive_frame(avctx, frame); + if (ret < 0 && ret != AVERROR(EAGAIN)) + return ret; + if (ret >= 0) + *got_frame = 1; + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFrameSideData *sd; + SplitCaptionsContext *s = inlink->dst->priv; + AVFilterLink *outlink0 = inlink->dst->outputs[0]; + AVFilterLink *outlink1 = inlink->dst->outputs[1]; + AVPacket *pkt = NULL; + AVFrame *sub_out = NULL; + + int ret; + + outlink0->format = inlink->format; + + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_A53_CC); + + if (sd) { + int got_output = 0; + + pkt = av_packet_alloc(); + pkt->buf = av_buffer_ref(sd->buf); + if (!pkt->buf) { + ret = AVERROR(ENOMEM); + goto fail; + } + + pkt->data = sd->data; + pkt->size = (int)sd->size; + pkt->pts = frame->pts; + + sub_out = ff_get_subtitles_buffer(outlink1, AV_SUBTITLE_FMT_ASS); + if (!sub_out) { + ret = AVERROR(ENOMEM); + goto fail; + } + + if ((ret = av_buffer_replace(&sub_out->subtitle_header, s->subtitle_header)) < 0) + goto fail; + + ret = decode(s->cc_dec, sub_out, &got_output, pkt); + + if (ret < 0) + goto fail; + + if (got_output) { + sub_out->pts = frame->pts; + av_frame_free(&s->next_sub_frame); + s->next_sub_frame = sub_out; + sub_out = NULL; + s->new_frame = 1; + s->next_sub_frame->pts = frame->pts; + + if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0) + goto fail; + + if (s->real_time && s->scatter_realtime_output) { + if (s->next_repetition_pts) + s->next_sub_frame->pts = s->next_repetition_pts; + + s->next_sub_frame->subtitle_timing.duration = ms_to_avtb(s->real_time_latency_msec); + s->next_repetition_pts = s->next_sub_frame->pts + av_rescale_q(s->real_time_latency_msec, ms_tb, inlink->time_base); + } + + ret = request_sub_frame(outlink1); + if (ret < 0) + goto fail; + } + } + + if (s->real_time && s->scatter_realtime_output && !s->new_frame && s->next_repetition_pts > 0 && frame->pts > s->next_repetition_pts) { + s->new_frame = 1; + s->next_sub_frame->pts = s->next_repetition_pts; + s->next_repetition_pts = s->next_sub_frame->pts + av_rescale_q(s->real_time_latency_msec, ms_tb, inlink->time_base); + } + + if (!s->next_sub_frame) { + s->next_sub_frame = ff_get_subtitles_buffer(outlink1, outlink1->format); + if (!s->next_sub_frame) { + ret = AVERROR(ENOMEM); + goto fail; + } + + s->next_sub_frame->subtitle_timing.duration = ms_to_avtb(s->real_time_latency_msec); + s->next_sub_frame->pts = frame->pts; + s->new_frame = 1; + + if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0) + goto fail; + } + + ret = ff_filter_frame(outlink0, frame); + +fail: + av_packet_free(&pkt); + av_frame_free(&sub_out); + return ret; +} + +#define OFFSET(x) offsetof(SplitCaptionsContext, x) +#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM) + +static const AVOption split_cc_options[] = { + { "use_cc_styles", "Emit closed caption style header", OFFSET(use_cc_styles), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, NULL }, + { "real_time", "emit subtitle events as they are decoded for real-time display", OFFSET(real_time), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, + { "real_time_latency_msec", "minimum elapsed time between emitting real-time subtitle events", OFFSET(real_time_latency_msec), AV_OPT_TYPE_INT, { .i64 = 200 }, 0, 500, FLAGS }, + { "scatter_realtime_output", "scatter output events to a duration of real_time_latency_msec", OFFSET(scatter_realtime_output), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, + { "data_field", "select data field", OFFSET(data_field), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, FLAGS, "data_field" }, + { "auto", "pick first one that appears", 0, AV_OPT_TYPE_CONST, { .i64 =-1 }, 0, 0, FLAGS, "data_field" }, + { "first", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "data_field" }, + { "second", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "data_field" }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(split_cc); + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + .config_props = config_input, + }, +}; + +static const AVFilterPad outputs[] = { + { + .name = "video_passthrough", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_video_output, + }, + { + .name = "subtitles", + .type = AVMEDIA_TYPE_SUBTITLE, + .request_frame = request_sub_frame, + .config_props = config_sub_output, + }, +}; + +const AVFilter ff_sf_splitcc = { + .name = "splitcc", + .description = NULL_IF_CONFIG_SMALL("Extract closed-caption (A53) data from video as subtitle stream."), + .init = init, + .uninit = uninit, + .priv_size = sizeof(SplitCaptionsContext), + .priv_class = &split_cc_class, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_QUERY_FUNC(query_formats), +}; From patchwork Fri Jan 14 01:13:27 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33580 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp986902iog; Thu, 13 Jan 2022 17:16:54 -0800 (PST) X-Google-Smtp-Source: ABdhPJyEs/Qb1OBv2QfJ8MpJmJbgCHQH/p/HfKRJ1IE5IyUbrL5o81zbBdk9Q7NO/Q/8mZkxuoHk X-Received: by 2002:a05:6402:4413:: with SMTP id y19mr6841003eda.26.1642123014460; Thu, 13 Jan 2022 17:16:54 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642123014; cv=none; d=google.com; s=arc-20160816; b=LS3/nIxD8tPlovtq7QFpDqgPT7MK3Dy7er7XWduIEMtbXQzNMEOdxeLATa/HgG9/BR tfKjcK7Z05lSU6ELKOs/2FIlvMzQKKd9GkENLD+NEd/bu3Pw423EiFWI3KgaYiETD85k rDYzZeUrYfDCXaAQFKrGqI/+w9GCk487Gby3YZKSS/2gpm5VYVXJRH2pmSiUHfKk8nmP ZhEKm2du2GVReOxe/Lgob28kvko4eHoUzfSkBYHKTCjHwXUZZ8Kdvx4GdkcTNCbYcGHY b642g54AEszUfa0L+8dN7SeImxmlsIDdbE3lDFl9N1u8y3CjmKE8qqlZQBtVJ/A7Le7K SykQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=KQ96g414xe8OsrRYUcvaKHVuDXz2Hp73W2YSpC0kAQc=; b=xqfPE/hGFzuZizTDRBWGVT+34j68hR9FT8OXHQxA8E0Oj/cIIdQ5nGJpgZoP2chG/S RvSl9N3kC3xT26AzmPHxPqGJD/WEaK6WBRI9+WaB+fMNqgnfuInN5GPk7Muddp/9/yN4 i2VqnTMPs7bjYo40BbsfUfWVccuebmWEANZOPdDquGS60ujlbRhx9BHag/Ib/FgF+5hl EDsPmZyPnQoA2TXpVs1NRcC0oHVEUkaMoqHs0unbTs/4PcwAy7LZRdfBSAiOmO3s/v31 IB8dCdssj6oNR0d8Ce21EXrBtj248UoOi3/fI45f8kKch7k8KCCBHInwYIKMKzFWp8oM LyBg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=SR7x4tsd; 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 y5si2660426edc.129.2022.01.13.17.16.54; Thu, 13 Jan 2022 17:16:54 -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 header.s=20210112 header.b=SR7x4tsd; 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 E336B68B6A9; Fri, 14 Jan 2022 03:14:04 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pg1-f172.google.com (mail-pg1-f172.google.com [209.85.215.172]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id CB74368B636 for ; Fri, 14 Jan 2022 03:13:56 +0200 (EET) Received: by mail-pg1-f172.google.com with SMTP id j27so1431444pgj.3 for ; Thu, 13 Jan 2022 17:13:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=nIGCObIeQVcQZxUP0C8lE/xDPMjDfzG6v0dIwOvB+j0=; b=SR7x4tsd8LUnEuGem9GjmkAxni7T66v7hE46DAt2eXnK+qX1MqBF1+UNrgtg0gKAFo FYJ5n08Fyof9ogADiYliQvpkc6wks8MTgwXe7gxtRDgr2eBW1Kkh5uz0rdHcY4MWhEeY dUL7KZ9ZFL60VXs5StH1LGpF45FLA7vvxOvbKo6jxZFFkIAqzZzCk1ah1Ev59rSFQvI3 jNN6irwk41qvEiz8q0VX4xtwjA3ZP98Nq/iqHj20pqjYQEYGI7XNu9k57jqHXgjBtip7 x+LytQOszze6x+/i18Vfl7zUMVHE988gmMBdmmX2ocPwRHDn6SXsJJHrMDRybSYYuf11 v8tQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=nIGCObIeQVcQZxUP0C8lE/xDPMjDfzG6v0dIwOvB+j0=; b=OFK7TBi4/cas9RyjFGrQ1mVo3fwfLqXGmynPkyEC/a4Y3vqqV/+EPksuy57XkEYybW uJ6qAuNuI/4wVgfRaVUI5P/psN6EMN9InO8Oo00S7NbhpdS78KCb9wn3mNwzSQMgs4zF HoTUg7kcNx3U88EAU5MO0CwUZjmHCww5J02fHmI/PKcjgmtweodppeDn25B7u09fl5YN ftXsMCKbXYUAedg+hW/l7vHMGp7ilXnaiWCKAWORnpaHdJ70TAdgWV8J1BcmOI+HcPag t5ggmRdU2cIXMZPXU4R/4xXArmlncyum8Kqr5IJm9A3f0BCjyEGZPIvlKhx4VnkM62xr PIPw== X-Gm-Message-State: AOAM531zS0Z8/h1Hk3iZRQzZn19wDf5bsP9sbk87P//AFC8RwmNZkDqc 2BHrVj+Dym0WUYpRSdoKwaUZk7Aq6/e4UA== X-Received: by 2002:a62:3003:0:b0:4c1:6983:f1c3 with SMTP id w3-20020a623003000000b004c16983f1c3mr6640621pfw.20.1642122834638; Thu, 13 Jan 2022 17:13:54 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id gm2sm9431509pjb.21.2022.01.13.17.13.53 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:54 -0800 (PST) Message-Id: <2cf4d421674f7bb3fea7a058a598e07d15fd36f1.1642122814.git.ffmpegagent@gmail.com> In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:27 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 18/24] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: tooW31rmvGXy From: softworkz Signed-off-by: softworkz --- configure | 1 + doc/filters.texi | 55 ++ libavfilter/Makefile | 2 + libavfilter/allfilters.c | 1 + libavfilter/sf_graphicsub2text.c | 1132 ++++++++++++++++++++++++++++++ 5 files changed, 1191 insertions(+) create mode 100644 libavfilter/sf_graphicsub2text.c diff --git a/configure b/configure index c1d2bc41c2..ee7afffb05 100755 --- a/configure +++ b/configure @@ -3665,6 +3665,7 @@ frei0r_filter_deps="frei0r" frei0r_src_filter_deps="frei0r" fspp_filter_deps="gpl" gblur_vulkan_filter_deps="vulkan spirv_compiler" +graphicsub2text_filter_deps="libtesseract" hflip_vulkan_filter_deps="vulkan spirv_compiler" histeq_filter_deps="gpl" hqdn3d_filter_deps="gpl" diff --git a/doc/filters.texi b/doc/filters.texi index 265a267e9d..7bc02b9415 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25897,6 +25897,61 @@ ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_comple @end example @end itemize +@section graphicsub2text + +Converts graphic subtitles to text subtitles by performing OCR. + +For this filter to be available, ffmpeg needs to be compiled with libtesseract (see https://github.com/tesseract-ocr/tesseract). +Language models need to be downloaded from https://github.com/tesseract-ocr/tessdata and put into as subfolder named 'tessdata' or into a folder specified via the environment variable 'TESSDATA_PREFIX'. +The path can also be specified via filter option (see below). + +Note: These models are including the data for both OCR modes. + +Inputs: +- 0: Subtitles [bitmap] + +Outputs: +- 0: Subtitles [text] + +It accepts the following parameters: + +@table @option +@item ocr_mode +The character recognition mode to use. + +Supported OCR modes are: + +@table @var +@item 0, tesseract +This is the classic libtesseract operation mode. It is fast but less accurate than LSTM. +@item 1, lstm +Newer OCR implementation based on ML models. Provides usually better results, requires more processing resources. +@item 2, both +Use a combination of both modes. +@end table + +@item tessdata_path +The path to a folder containing the language models to be used. + +@item language +The recognition language. It needs to match the first three characters of a language model file in the tessdata path. + +@end table + + +@subsection Examples + +@itemize +@item +Convert DVB graphic subtitles to ASS (text) subtitles + +Note: For this to work, you need to have the data file 'eng.traineddata' in a 'tessdata' subfolder (see above). +@example +ffmpeg ffmpeg -loglevel verbose -i "https://streams.videolan.org/streams/ts/video_subs_ttxt%2Bdvbsub.ts" -filter_complex "[0:13]graphicsub2text=ocr_mode=both" -c:s ass -y output.mkv +@end example +@end itemize + + @section graphicsub2video Renders graphic subtitles as video frames. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index c6a4a4f5ae..ead3e38507 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -299,6 +299,8 @@ OBJS-$(CONFIG_GBLUR_VULKAN_FILTER) += vf_gblur_vulkan.o vulkan.o vulka OBJS-$(CONFIG_GEQ_FILTER) += vf_geq.o OBJS-$(CONFIG_GRADFUN_FILTER) += vf_gradfun.o OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER) += vf_overlaygraphicsubs.o framesync.o +OBJS-$(CONFIG_GRAPHICSUB2TEXT_FILTER) += sf_graphicsub2text.o +OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER) += vf_overlaygraphicsubs.o framesync.o OBJS-$(CONFIG_GRAPHMONITOR_FILTER) += f_graphmonitor.o OBJS-$(CONFIG_GRAYWORLD_FILTER) += vf_grayworld.o OBJS-$(CONFIG_GREYEDGE_FILTER) += vf_colorconstancy.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 50498e8ec4..34576016ce 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -548,6 +548,7 @@ extern const AVFilter ff_avf_showwaves; extern const AVFilter ff_avf_showwavespic; extern const AVFilter ff_vaf_spectrumsynth; extern const AVFilter ff_sf_censor; +extern const AVFilter ff_sf_graphicsub2text; extern const AVFilter ff_sf_showspeaker; extern const AVFilter ff_sf_splitcc; extern const AVFilter ff_sf_stripstyles; diff --git a/libavfilter/sf_graphicsub2text.c b/libavfilter/sf_graphicsub2text.c new file mode 100644 index 0000000000..fcabb19892 --- /dev/null +++ b/libavfilter/sf_graphicsub2text.c @@ -0,0 +1,1132 @@ +/* + * Copyright (c) 2021 softworkz + * + * 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 + * subtitle filter to convert graphical subs to text subs via OCR + */ + +#include +#include + +#include "drawutils.h" +#include "libavutil/opt.h" +#include "subtitles.h" + +#include "libavcodec/elbg.h" + +enum { + RFLAGS_NONE = 0, + RFLAGS_HALIGN = 1 << 0, + RFLAGS_VALIGN = 1 << 1, + RFLAGS_FBOLD = 1 << 2, + RFLAGS_FITALIC = 1 << 3, + RFLAGS_FUNDERLINE = 1 << 4, + RFLAGS_FONT = 1 << 5, + RFLAGS_FONTSIZE = 1 << 6, + RFLAGS_COLOR = 1 << 7, + RFLAGS_OUTLINECOLOR = 1 << 8, + RFLAGS_ALL = RFLAGS_HALIGN | RFLAGS_VALIGN | RFLAGS_FBOLD | RFLAGS_FITALIC | RFLAGS_FUNDERLINE | + RFLAGS_FONT | RFLAGS_FONTSIZE | RFLAGS_COLOR | RFLAGS_OUTLINECOLOR, +}; + +typedef struct SubOcrContext { + const AVClass *class; + int w, h; + + TessBaseAPI *tapi; + TessOcrEngineMode ocr_mode; + char *tessdata_path; + char *language; + int preprocess_images; + int dump_bitmaps; + int delay_when_no_duration; + int recognize; + double font_size_factor; + + int readorder_counter; + + AVFrame *pending_frame; + AVBufferRef *subtitle_header; + AVBPrint buffer; + + // Color Quantization Fields + struct ELBGContext *ctx; + AVLFG lfg; + int *codeword; + int *codeword_closest_codebook_idxs; + int *codebook; + int r_idx, g_idx, b_idx, a_idx; + int64_t last_subtitle_pts; +} SubOcrContext; + +typedef struct OcrImageProps { + int background_color_index; + int fill_color_index; + +} OcrImageProps; + +static int64_t ms_to_avtb(int64_t ms) +{ + return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q); +} + +static int create_ass_header(AVFilterContext* ctx) +{ + SubOcrContext* s = ctx->priv; + + if (!(s->w && s->h)) { + av_log(ctx, AV_LOG_WARNING, "create_ass_header: no width and height specified!\n"); + s->w = ASS_DEFAULT_PLAYRESX; + s->h = ASS_DEFAULT_PLAYRESY; + } + + char* subtitle_header_text = avpriv_ass_get_subtitle_header_full(s->w, s->h, ASS_DEFAULT_FONT, ASS_DEFAULT_FONT_SIZE, + ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR, ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BOLD, + ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE, ASS_DEFAULT_BORDERSTYLE, ASS_DEFAULT_ALIGNMENT, 0); + + if (!subtitle_header_text) + return AVERROR(ENOMEM); + + s->subtitle_header = av_buffer_create((uint8_t*)subtitle_header_text, strlen(subtitle_header_text) + 1, NULL, NULL, 0); + + if (!s->subtitle_header) + return AVERROR(ENOMEM); + + return 0; +} + +static int init(AVFilterContext *ctx) +{ + SubOcrContext *s = ctx->priv; + const char* tver = TessVersion(); + uint8_t rgba_map[4]; + int ret; + + s->tapi = TessBaseAPICreate(); + + if (!s->tapi || !tver || !strlen(tver)) { + av_log(ctx, AV_LOG_ERROR, "Failed to access libtesseract\n"); + return AVERROR(ENOSYS); + } + + av_log(ctx, AV_LOG_VERBOSE, "Initializing libtesseract, version: %s\n", tver); + + ret = TessBaseAPIInit4(s->tapi, s->tessdata_path, s->language, s->ocr_mode, NULL, 0, NULL, NULL, 0, 1); + if (ret < 0 ) { + av_log(ctx, AV_LOG_ERROR, "Failed to initialize libtesseract. Error: %d\n", ret); + return AVERROR(ENOSYS); + } + + ret = TessBaseAPISetVariable(s->tapi, "tessedit_char_blacklist", "|"); + if (ret < 0 ) { + av_log(ctx, AV_LOG_ERROR, "Failed to set 'tessedit_char_blacklist'. Error: %d\n", ret); + return AVERROR(EINVAL); + } + + av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); + + ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32); + + s->r_idx = rgba_map[0]; // R + s->g_idx = rgba_map[1]; // G + s->b_idx = rgba_map[2]; // B + s->a_idx = rgba_map[3]; // A + + av_lfg_init(&s->lfg, 123456789); + + return 0; +} + +static void uninit(AVFilterContext *ctx) +{ + SubOcrContext *s = ctx->priv; + + av_buffer_unref(&s->subtitle_header); + av_bprint_finalize(&s->buffer, NULL); + + if (s->tapi) { + TessBaseAPIEnd(s->tapi); + TessBaseAPIDelete(s->tapi); + } + + avpriv_elbg_free(&s->ctx); +} + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats, *formats2; + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + static const enum AVSubtitleType in_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_NONE }; + static const enum AVSubtitleType out_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE }; + int ret; + + /* set input format */ + formats = ff_make_format_list(in_fmts); + if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0) + return ret; + + /* set output format */ + formats2 = ff_make_format_list(out_fmts); + if ((ret = ff_formats_ref(formats2, &outlink->incfg.formats)) < 0) + return ret; + + return 0; +} + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + SubOcrContext *s = ctx->priv; + + if (s->w <= 0 || s->h <= 0) { + s->w = inlink->w; + s->h = inlink->h; + } + + return create_ass_header(ctx); +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterLink *inlink = outlink->src->inputs[0]; + const AVFilterContext *ctx = outlink->src; + SubOcrContext *s = ctx->priv; + + outlink->format = AV_SUBTITLE_FMT_ASS; + outlink->w = s->w; + outlink->h = s->h; + outlink->time_base = inlink->time_base; + outlink->frame_rate = inlink->frame_rate; + + return 0; +} + +static void free_subtitle_area(AVSubtitleArea *area) +{ + for (unsigned n = 0; n < FF_ARRAY_ELEMS(area->buf); n++) + av_buffer_unref(&area->buf[n]); + + av_freep(&area->text); + av_freep(&area->ass); + av_free(area); + +} + +static AVSubtitleArea *copy_subtitle_area(const AVSubtitleArea *src) +{ + AVSubtitleArea *dst = av_mallocz(sizeof(AVSubtitleArea)); + + if (!dst) + return NULL; + + dst->x = src->x; + dst->y = src->y; + dst->w = src->w; + dst->h = src->h; + dst->nb_colors = src->nb_colors; + dst->type = src->type; + dst->flags = src->flags; + + for (unsigned i = 0; i < AV_NUM_BUFFER_POINTERS; i++) { + if (src->h > 0 && src->w > 0 && src->buf[i]) { + dst->buf[0] = av_buffer_ref(src->buf[i]); + if (!dst->buf[i]) + return NULL; + + const int ret = av_buffer_make_writable(&dst->buf[i]); + if (ret < 0) + return NULL; + + dst->linesize[i] = src->linesize[i]; + } + } + + memcpy(&dst->pal[0], &src->pal[0], sizeof(src->pal[0]) * 256); + + + return dst; +} + +static int quantize_image_colors(SubOcrContext *const s, AVSubtitleArea *subtitle_area) +{ + const int num_quantized_colors = 3; + int k, ret; + const int codeword_length = subtitle_area->w * subtitle_area->h; + uint8_t *src_data = subtitle_area->buf[0]->data; + + if (subtitle_area->nb_colors <= num_quantized_colors) { + av_log(s, AV_LOG_DEBUG, "No need to quantize colors. Color count: %d\n", subtitle_area->nb_colors); + return 0; + } + + // Convert palette to grayscale + for (int i = 0; i < subtitle_area->nb_colors; i++) { + uint8_t *color = (uint8_t *)&subtitle_area->pal[i]; + const uint8_t average = (uint8_t)(((int)color[s->r_idx] + color[s->g_idx] + color[s->b_idx]) / 3); + color[s->b_idx] = average; + color[s->g_idx] = average; + color[s->r_idx] = average; + } + + /* Re-Initialize */ + s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword)); + if (!s->codeword) + return AVERROR(ENOMEM); + + s->codeword_closest_codebook_idxs = av_realloc_f(s->codeword_closest_codebook_idxs, + codeword_length, sizeof(*s->codeword_closest_codebook_idxs)); + if (!s->codeword_closest_codebook_idxs) + return AVERROR(ENOMEM); + + s->codebook = av_realloc_f(s->codebook, num_quantized_colors, 4 * sizeof(*s->codebook)); + if (!s->codebook) + return AVERROR(ENOMEM); + + /* build the codeword */ + k = 0; + for (int i = 0; i < subtitle_area->h; i++) { + uint8_t *p = src_data; + for (int j = 0; j < subtitle_area->w; j++) { + const uint8_t *color = (uint8_t *)&subtitle_area->pal[*p]; + s->codeword[k++] = color[s->b_idx]; + s->codeword[k++] = color[s->g_idx]; + s->codeword[k++] = color[s->r_idx]; + s->codeword[k++] = color[s->a_idx]; + p++; + } + src_data += subtitle_area->linesize[0]; + } + + /* compute the codebook */ + ret = avpriv_elbg_do(&s->ctx, s->codeword, 4, codeword_length, s->codebook, + num_quantized_colors, 1, s->codeword_closest_codebook_idxs, &s->lfg, 0); + if (ret < 0) + return ret; + + /* Write Palette */ + for (int i = 0; i < num_quantized_colors; i++) { + subtitle_area->pal[i] = s->codebook[i*4+3] << 24 | + (s->codebook[i*4+2] << 16) | + (s->codebook[i*4+1] << 8) | + (s->codebook[i*4 ] << 0); + } + + + av_log(s, AV_LOG_DEBUG, "Quantized colors from %d to %d\n", subtitle_area->nb_colors, num_quantized_colors); + + subtitle_area->nb_colors = num_quantized_colors; + src_data = subtitle_area->buf[0]->data; + + /* Write Image */ + k = 0; + for (int i = 0; i < subtitle_area->h; i++) { + uint8_t *p = src_data; + for (int j = 0; j < subtitle_area->w; j++, p++) { + p[0] = (uint8_t)s->codeword_closest_codebook_idxs[k++]; + } + + src_data += subtitle_area->linesize[0]; + } + + return ret; +} + +#define MEASURE_LINE_COUNT 6 + +static uint8_t get_background_color_index(SubOcrContext *const s, const AVSubtitleArea *subtitle_area) +{ + const int linesize = subtitle_area->linesize[0]; + int index_counts[256] = {0}; + const unsigned int line_offsets[MEASURE_LINE_COUNT] = { + 0, + linesize, + 2 * linesize, + (subtitle_area->h - 3) * linesize, + (subtitle_area->h - 2) * linesize, + (subtitle_area->h - 1) * linesize + }; + + const uint8_t *src_data = subtitle_area->buf[0]->data; + const uint8_t tl = src_data[0]; + const uint8_t tr = src_data[subtitle_area->w - 1]; + const uint8_t bl = src_data[(subtitle_area->h - 1) * linesize + 0]; + const uint8_t br = src_data[(subtitle_area->h - 1) * linesize + subtitle_area->w - 1]; + uint8_t max_index = 0; + int max_count; + + // When all corner pixels are equal, assume that as background color + if (tl == tr == bl == br || subtitle_area->h < 6) + return tl; + + for (unsigned int i = 0; i < MEASURE_LINE_COUNT; i++) { + uint8_t *p = subtitle_area->buf[0]->data + line_offsets[i]; + for (int k = 0; k < subtitle_area->w; k++) + index_counts[p[k]]++; + } + + max_count = index_counts[0]; + + for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) { + if (index_counts[i] > max_count) { + max_count = index_counts[i]; + max_index = i; + } + } + + return max_index; +} + +static uint8_t get_text_color_index(SubOcrContext *const s, const AVSubtitleArea *subtitle_area, const uint8_t bg_color_index, uint8_t *outline_color_index) +{ + const int linesize = subtitle_area->linesize[0]; + int index_counts[256] = {0}; + uint8_t last_index = bg_color_index; + int max_count, min_req_count; + uint8_t max_index = 0; + + for (int i = 3; i < subtitle_area->h - 3; i += 5) { + const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i); + for (int k = 0; k < subtitle_area->w; k++) { + const uint8_t cur_index = p[k]; + + // When color hasn't changed, continue + if (cur_index == last_index) + continue; + + if (cur_index != bg_color_index) + index_counts[cur_index]++; + + last_index = cur_index; + } + } + + max_count = index_counts[0]; + + for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) { + if (index_counts[i] > max_count) { + max_count = index_counts[i]; + max_index = i; + } + } + + min_req_count = max_count / 3; + + for (uint8_t i = 1; i < subtitle_area->nb_colors; i++) { + if (index_counts[i] < min_req_count) + index_counts[i] = 0; + } + + *outline_color_index = max_index; + + index_counts[max_index] = 0; + max_count = 0; + + for (uint8_t i = 0; i < subtitle_area->nb_colors; i++) { + if (index_counts[i] > max_count) { + max_count = index_counts[i]; + max_index = i; + } + } + + if (*outline_color_index == max_index) + *outline_color_index = 255; + + return max_index; +} + +static void make_image_binary(SubOcrContext *const s, AVSubtitleArea *subtitle_area, const uint8_t text_color_index) +{ + for (int i = 0; i < subtitle_area->nb_colors; i++) { + + if (i != text_color_index) + subtitle_area->pal[i] = 0xffffffff; + else + subtitle_area->pal[i] = 0xff000000; + } +} + +static int get_crop_region(SubOcrContext *const s, const AVSubtitleArea *subtitle_area, uint8_t text_color_index, int *x, int *y, int *w, int *h) +{ + const int linesize = subtitle_area->linesize[0]; + int max_y = 0, max_x = 0; + int min_y = subtitle_area->h - 1, min_x = subtitle_area->w - 1; + + for (int i = 0; i < subtitle_area->h; i += 3) { + const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i); + for (int k = 0; k < subtitle_area->w; k += 2) { + if (p[k] == text_color_index) { + min_y = FFMIN(min_y, i); + min_x = FFMIN(min_x, k); + max_y = FFMAX(max_y, i); + max_x = FFMAX(max_x, k); + } + } + } + + if (max_y <= min_y || max_x <= min_x) { + av_log(s, AV_LOG_WARNING, "Unable to detect crop region\n"); + *x = 0; + *y = 0; + *w = subtitle_area->w; + *h = subtitle_area->h; + } else { + *x = FFMAX(min_x - 10, 0); + *y = FFMAX(min_y - 10, 0); + *w = FFMIN(max_x + 10 - *x, (subtitle_area->w - *x)); + *h = FFMIN(max_y + 10 - *y, (subtitle_area->h - *y)); + } + + return 0; +} + +static int crop_area_bitmap(SubOcrContext *const s, AVSubtitleArea *subtitle_area, int x, int y, int w, int h) +{ + const int linesize = subtitle_area->linesize[0]; + AVBufferRef *dst = av_buffer_allocz(h * w); + uint8_t *d; + + if (!dst) + return AVERROR(ENOMEM); + + d = dst->data; + + for (int i = y; i < y + h; i++) { + const uint8_t *p = subtitle_area->buf[0]->data + ((ptrdiff_t)linesize * i); + for (int k = x; k < x + w; k++) { + *d = p[k]; + d++; + } + } + + subtitle_area->w = w; + subtitle_area->h = h; + subtitle_area->x += x; + subtitle_area->y += y; + subtitle_area->linesize[0] = w; + av_buffer_replace(&subtitle_area->buf[0], dst); + + av_buffer_unref(&dst); + return 0; +} + +#define R 0 +#define G 1 +#define B 2 +#define A 3 + +static int print_code(AVBPrint *buf, int in_code, const char *fmt, ...) +{ + va_list vl; + + if (!in_code) + av_bprint_chars(buf, '{', 1); + + va_start(vl, fmt); + av_vbprintf(buf, fmt, vl); + va_end(vl); + + return 1; +} + +static int end_code(AVBPrint *buf, int in_code) +{ + if (in_code) + av_bprint_chars(buf, '}', 1); + return 0; +} + +static uint8_t* create_grayscale_image(AVFilterContext *ctx, AVSubtitleArea *area, int invert) +{ + uint8_t gray_pal[256]; + const size_t img_size = area->buf[0]->size; + const uint8_t* img = area->buf[0]->data; + uint8_t* gs_img = av_malloc(img_size); + + if (!gs_img) + return NULL; + + for (unsigned i = 0; i < 256; i++) { + const uint8_t *col = (uint8_t*)&area->pal[i]; + const int val = (int)col[3] * FFMAX3(col[0], col[1], col[2]); + gray_pal[i] = (uint8_t)(val >> 8); + } + + if (invert) + for (unsigned i = 0; i < img_size; i++) + gs_img[i] = 255 - gray_pal[img[i]]; + else + for (unsigned i = 0; i < img_size; i++) + gs_img[i] = gray_pal[img[i]]; + + return gs_img; +} + +static uint8_t* create_bitmap_image(AVFilterContext *ctx, AVSubtitleArea *area, const uint8_t text_color_index) +{ + const size_t img_size = area->buf[0]->size; + const uint8_t* img = area->buf[0]->data; + uint8_t* gs_img = av_malloc(img_size); + + if (!gs_img) + return NULL; + + for (unsigned i = 0; i < img_size; i++) { + if (img[i] == text_color_index) + gs_img[i] = 0; + else + gs_img[i] = 255; + } + + return gs_img; +} + +static void png_save(AVFilterContext *ctx, const char *filename, AVSubtitleArea *area) +{ + int x, y; + int v; + FILE *f; + char fname[40]; + const uint8_t *data = area->buf[0]->data; + + snprintf(fname, sizeof(fname), "%s.ppm", filename); + + f = fopen(fname, "wb"); + if (!f) { + perror(fname); + return; + } + fprintf(f, "P6\n" + "%d %d\n" + "%d\n", + area->w, area->h, 255); + for(y = 0; y < area->h; y++) { + for(x = 0; x < area->w; x++) { + const uint8_t index = data[y * area->linesize[0] + x]; + v = (int)area->pal[index]; + putc(v >> 16 & 0xff, f); + putc(v >> 8 & 0xff, f); + putc(v >> 0 & 0xff, f); + } + } + + fclose(f); +} + +static int get_max_index(int score[256]) +{ + int max_val = 0, max_index = 0; + + for (int i = 0; i < 256; i++) { + if (score[i] > max_val) { + max_val = score[i]; + max_index = i; + } + } + + return max_index; +} + +static int get_word_colors(AVFilterContext *ctx, TessResultIterator* ri, const AVSubtitleArea* area, const AVSubtitleArea* original_area, + uint8_t bg_color_index, uint8_t text_color_index, uint8_t outline_color_index, + uint32_t* bg_color, uint32_t* text_color, uint32_t* outline_color) +{ + int left = 0, top = 0, right = 0, bottom = 0, ret; + int bg_score[256] = {0}, text_score[256] = {0}, outline_score[256] = {0}; + int max_index; + + ret = TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_WORD, &left, &top, &right, &bottom); + if (ret < 0) { + av_log(ctx, AV_LOG_WARNING, "get_word_colors: IteratorBoundingBox failed: %d\n", ret); + return ret; + } + + if (left >= area->w || right >= area->w || top >= area->h || bottom >= area->h) { + av_log(ctx, AV_LOG_WARNING, "get_word_colors: word bounding box (l: %d, t: %d r: %d, b: %d) out of image bounds (%dx%d)\n", left,top, right, bottom, area->w, area->h); + return AVERROR(EINVAL); + } + + for (int y = top; y < bottom; y += 3) { + uint8_t *p = area->buf[0]->data + (y * area->linesize[0]) + left; + uint8_t *porig = original_area->buf[0]->data + (y * original_area->linesize[0]) + left; + + for (int x = left; x < right; x++, p++, porig++) { + if (*p == bg_color_index) + bg_score[*porig]++; + if (*p == text_color_index) + text_score[*porig]++; + if (*p == outline_color_index) + outline_score[*porig]++; + } + } + + max_index = get_max_index(bg_score); + if (bg_score[max_index] > 0) + *bg_color = original_area->pal[max_index]; + + max_index = get_max_index(text_score); + if (text_score[max_index] > 0) + *text_color = original_area->pal[max_index]; + + max_index = get_max_index(outline_score); + if (outline_score[max_index] > 0) + *outline_color = original_area->pal[max_index]; + + return 0; +} + +static int convert_area(AVFilterContext *ctx, AVSubtitleArea *area, const AVFrame *frame, const unsigned area_index, int *margin_v) +{ + SubOcrContext *s = ctx->priv; + char *ocr_text = NULL; + int ret = 0; + uint8_t *gs_img; + uint8_t bg_color_index; + uint8_t text_color_index = 255; + uint8_t outline_color_index = 255; + char filename[32]; + AVSubtitleArea *original_area = copy_subtitle_area(area); + + if (!original_area) + return AVERROR(ENOMEM); + + if (area->w < 6 || area->h < 6) { + area->ass = NULL; + goto exit; + } + + if (s->dump_bitmaps) { + snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_original", frame->subtitle_timing.start_pts, area_index); + png_save(ctx, filename, area); + } + + if (s->preprocess_images) { + ret = quantize_image_colors(s, area); + if (ret < 0) + goto exit; + if (s->dump_bitmaps && original_area->nb_colors != area->nb_colors) { + snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_quantized", frame->subtitle_timing.start_pts, area_index); + png_save(ctx, filename, area); + } + } + + bg_color_index = get_background_color_index(s, area); + + if (s->preprocess_images) { + int x, y, w, h; + + for (int i = 0; i < area->nb_colors; ++i) { + av_log(s, AV_LOG_DEBUG, "Color #%d: %0.8X\n", i, area->pal[i]); + } + + text_color_index = get_text_color_index(s, area, bg_color_index, &outline_color_index); + + get_crop_region(s, area, text_color_index, &x, &y, &w, &h); + + if ((ret = crop_area_bitmap(s, area, x, y, w, h) < 0)) + goto exit; + + if ((ret = crop_area_bitmap(s, original_area, x, y, w, h) < 0)) + goto exit; + + make_image_binary(s, area, text_color_index); + + if (s->dump_bitmaps) { + snprintf(filename, sizeof(filename), "graphicsub2text_%"PRId64"_%d_preprocessed", frame->subtitle_timing.start_pts, area_index); + png_save(ctx, filename, area); + } + + gs_img = create_bitmap_image(ctx, area, text_color_index); + } else + gs_img = create_grayscale_image(ctx, area, 1); + + if (!gs_img) { + ret = AVERROR(ENOMEM); + goto exit; + } + + area->type = AV_SUBTITLE_FMT_ASS; + TessBaseAPISetImage(s->tapi, gs_img, area->w, area->h, 1, area->linesize[0]); + + TessBaseAPISetSourceResolution(s->tapi, 72); + + ret = TessBaseAPIRecognize(s->tapi, NULL); + if (ret == 0) + ocr_text = TessBaseAPIGetUTF8Text(s->tapi); + + if (!ocr_text || !strlen(ocr_text)) { + av_log(ctx, AV_LOG_WARNING, "OCR didn't return a text. ret=%d\n", ret); + area->ass = NULL; + + goto exit; + } + + const size_t len = strlen(ocr_text); + if (len > 0 && ocr_text[len - 1] == '\n') + ocr_text[len - 1] = 0; + + av_log(ctx, AV_LOG_VERBOSE, "OCR Result: %s\n", ocr_text); + + area->ass = av_strdup(ocr_text); + TessDeleteText(ocr_text); + + // End of simple recognition + + if (s->recognize != RFLAGS_NONE) { + TessResultIterator* ri = 0; + const TessPageIteratorLevel level = RIL_WORD; + int cur_is_bold = 0, cur_is_italic = 0, cur_is_underlined = 0, cur_pointsize = 0; + uint32_t cur_text_color = 0, cur_bg_color = 0, cur_outline_color = 0; + + char *cur_font_name = NULL; + int valign = 0; // 0: bottom, 4: top, 8 middle + int halign = 2; // 1: left, 2: center, 3: right + int in_code = 0; + double font_factor = (0.000666 * (s->h - 480) + 1) * s->font_size_factor; + + av_freep(&area->ass); + av_bprint_clear(&s->buffer); + + ri = TessBaseAPIGetIterator(s->tapi); + + // Horizontal Alignment + if (s->w && s->recognize & RFLAGS_HALIGN) { + int left_margin = area->x; + int right_margin = s->w - area->x - area->w; + double relative_diff = ((double)left_margin - right_margin) / s->w; + + if (FFABS(relative_diff) < 0.1) + halign = 2; // center + else if (relative_diff > 0) + halign = 3; // right + else + halign = 1; // left + } + + // Vertical Alignment + if (s->h && frame->height && s->recognize & RFLAGS_VALIGN) { + int left = 0, top = 0, right = 0, bottom = 0; + + TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_TEXTLINE, &left, &top, &right, &bottom); + av_log(s, AV_LOG_DEBUG, "RIL_TEXTLINE - TOP: %d BOTTOM: %d HEIGHT: %d\n", top, bottom, bottom - top); + + TessPageIteratorBoundingBox((TessPageIterator*)ri, RIL_BLOCK, &left, &top, &right, &bottom); + + const int vertical_pos = area->y + area->h / 2; + if (vertical_pos < s->h / 3) { + *margin_v = area->y + top; + valign = 4; + } + else if (vertical_pos < s->h / 3 * 2) { + *margin_v = 0; + valign = 8; + } else { + *margin_v = frame->height - area->y - area->h; + valign = 0; + } + } + + if (*margin_v < 0) + *margin_v = 0; + + // Set alignment when not default (2) + if ((valign | halign) != 2) + in_code = print_code(&s->buffer, in_code, "\\a%d", valign | halign); + + do { + int is_bold, is_italic, is_underlined, is_monospace, is_serif, is_smallcaps, pointsize, font_id; + char* word; + const char *font_name = TessResultIteratorWordFontAttributes(ri, &is_bold, &is_italic, &is_underlined, &is_monospace, &is_serif, &is_smallcaps, &pointsize, &font_id); + uint32_t text_color = 0, bg_color = 0, outline_color = 0; + + if (cur_is_underlined && !is_underlined && s->recognize & RFLAGS_FUNDERLINE) + in_code = print_code(&s->buffer, in_code, "\\u0"); + + if (cur_is_bold && !is_bold && s->recognize & RFLAGS_FBOLD) + in_code = print_code(&s->buffer, in_code, "\\b0"); + + if (cur_is_italic && !is_italic && s->recognize & RFLAGS_FITALIC) + in_code = print_code(&s->buffer, in_code, "\\i0"); + + + if (TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_TEXTLINE) && !TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_BLOCK)) { + in_code = end_code(&s->buffer, in_code); + av_bprintf(&s->buffer, "\\N"); + } + + if (get_word_colors(ctx, ri, area, original_area, bg_color_index, text_color_index, outline_color_index, &bg_color, &text_color, &outline_color) == 0) { + + if (text_color > 0 && cur_text_color != text_color && s->recognize & RFLAGS_COLOR) { + const uint8_t* tval = (uint8_t*)&text_color; + const int color = (int)tval[R] << 16 | (int)tval[G] << 8 | tval[B]; + + in_code = print_code(&s->buffer, in_code, "\\1c&H%0.6X&", color); + if (tval[A] != 255) + in_code = print_code(&s->buffer, in_code, "\\1a&H%0.2X&", 255 - tval[A]); + } + + if (outline_color > 0 && cur_outline_color != outline_color && s->recognize & RFLAGS_OUTLINECOLOR) { + const uint8_t* tval = (uint8_t*)&outline_color; + const int color = (int)tval[R] << 16 | (int)tval[G] << 8 | tval[B]; + + in_code = print_code(&s->buffer, in_code, "\\3c&H%0.6X&\\bord2", color); + in_code = print_code(&s->buffer, in_code, "\\3a&H%0.2X&", FFMIN(255 - tval[A], 30)); + } + + cur_text_color = text_color; + cur_outline_color = outline_color; + } + + if (font_name && strlen(font_name) && s->recognize & RFLAGS_FONT) { + if (!cur_font_name || !strlen(cur_font_name) || strcmp(cur_font_name, font_name) != 0) { + char *sanitized_font_name = av_strireplace(font_name, "_", " "); + if (!sanitized_font_name) { + ret = AVERROR(ENOMEM); + goto exit; + } + + in_code = print_code(&s->buffer, in_code, "\\fn%s", sanitized_font_name); + av_freep(&sanitized_font_name); + + if (cur_font_name) + av_freep(&cur_font_name); + cur_font_name = av_strdup(font_name); + if (!cur_font_name) { + ret = AVERROR(ENOMEM); + goto exit; + } + } + } + + if (pointsize != cur_pointsize && s->recognize & RFLAGS_FONTSIZE) { + av_log(s, AV_LOG_DEBUG, "pointsize - pointsize: %d\n", pointsize); + in_code = print_code(&s->buffer, in_code, "\\fs%d", (int)(pointsize * font_factor)); + cur_pointsize = pointsize; + } + + if (is_italic && !cur_is_italic && s->recognize & RFLAGS_FITALIC) + in_code = print_code(&s->buffer, in_code, "\\i1"); + + if (is_bold && !cur_is_bold && s->recognize & RFLAGS_FBOLD) + in_code = print_code(&s->buffer, in_code, "\\b1"); + + if (is_underlined && !cur_is_underlined && s->recognize & RFLAGS_FUNDERLINE) + in_code = print_code(&s->buffer, in_code, "\\u1"); + + in_code = end_code(&s->buffer, in_code); + + cur_is_underlined = is_underlined; + cur_is_bold = is_bold; + cur_is_italic = is_italic; + + if (!TessPageIteratorIsAtBeginningOf((TessPageIterator*)ri, RIL_TEXTLINE)) + av_bprint_chars(&s->buffer, ' ', 1); + + word = TessResultIteratorGetUTF8Text(ri, level); + av_bprint_append_data(&s->buffer, word, strlen(word)); + TessDeleteText(word); + + } while (TessResultIteratorNext(ri, level)); + + if (!av_bprint_is_complete(&s->buffer)) + ret = AVERROR(ENOMEM); + else { + av_log(ctx, AV_LOG_VERBOSE, "ASS Result: %s\n", s->buffer.str); + area->ass = av_strdup(s->buffer.str); + } + + TessResultIteratorDelete(ri); + av_freep(&cur_font_name); + } + +exit: + free_subtitle_area(original_area); + av_freep(&gs_img); + av_buffer_unref(&area->buf[0]); + area->type = AV_SUBTITLE_FMT_ASS; + + return ret; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFilterContext *ctx = inlink->dst; + SubOcrContext *s = ctx->priv; + AVFilterLink *outlink = inlink->dst->outputs[0]; + int ret, frame_sent = 0; + + if (s->pending_frame && !frame->repeat_sub) { + const int64_t pts_diff = frame->subtitle_timing.start_pts - s->pending_frame->subtitle_timing.start_pts; + + if (pts_diff == 0) { + // This is just a repetition of the previous frame, ignore it + av_frame_free(&frame); + return 0; + } + + s->pending_frame->subtitle_timing.duration = pts_diff; + + if ((ret = av_buffer_replace(&s->pending_frame->subtitle_header, s->subtitle_header)) < 0) + return ret; + + ret = ff_filter_frame(outlink, s->pending_frame); + s->pending_frame = NULL; + if (ret < 0) + return ret; + + frame_sent = 1; + s->last_subtitle_pts = frame->subtitle_timing.start_pts; + + if (frame->num_subtitle_areas == 0) { + // No need to forward this empty frame + av_frame_free(&frame); + return 0; + } + } + + if (frame->repeat_sub && frame->subtitle_timing.start_pts == s->last_subtitle_pts) { + // Ignore repeated frame + av_frame_free(&frame); + return 0; + } + + s->last_subtitle_pts = frame->subtitle_timing.start_pts; + + ret = av_frame_make_writable(frame); + + if (ret < 0) { + av_frame_free(&frame); + return ret; + } + + frame->format = AV_SUBTITLE_FMT_ASS; + + av_log(ctx, AV_LOG_VERBOSE, "filter_frame sub_pts: %"PRIu64", duration: %"PRIu64", num_areas: %d\n", + frame->subtitle_timing.start_pts, frame->subtitle_timing.duration, frame->num_subtitle_areas); + + if (frame->num_subtitle_areas > 1 && + frame->subtitle_areas[0]->y > frame->subtitle_areas[frame->num_subtitle_areas - 1]->y) { + + for (unsigned i = 0; i < frame->num_subtitle_areas / 2; i++) + FFSWAP(AVSubtitleArea*, frame->subtitle_areas[i], frame->subtitle_areas[frame->num_subtitle_areas - i - 1]); + } + + for (int i = 0; i < frame->num_subtitle_areas; i++) { + AVSubtitleArea *area = frame->subtitle_areas[i]; + int margin_v = 0; + + ret = convert_area(ctx, area, frame, i, &margin_v); + if (ret < 0) + return ret; + + if (area->ass && area->ass[0] != '\0') { + + const int layer = s->recognize ? i : 0; + char *tmp = area->ass; + area->ass = avpriv_ass_get_dialog_ex(s->readorder_counter++, layer, "Default", NULL, 0, 0, margin_v, tmp); + av_free(tmp); + } + } + + // When decoders can't determine the end time, they are setting it either to UINT32_NAX + // or 30s (dvbsub). + if (s->delay_when_no_duration && frame->num_subtitle_areas > 0 && frame->subtitle_timing.duration >= ms_to_avtb(29000)) { + // Can't send it without end time, wait for the next frame to determine the end_display time + s->pending_frame = frame; + + if (frame_sent) + return 0; + + // To keep all going, send an empty frame instead + frame = ff_get_subtitles_buffer(outlink, AV_SUBTITLE_FMT_ASS); + if (!frame) + return AVERROR(ENOMEM); + + av_frame_copy_props(frame, s->pending_frame); + frame->subtitle_timing.start_pts = 0; + frame->subtitle_timing.duration = 1; + frame->repeat_sub = 1; + } + + if ((ret = av_buffer_replace(&frame->subtitle_header, s->subtitle_header)) < 0) + return ret; + + return ff_filter_frame(outlink, frame); +} + +#define OFFSET(x) offsetof(SubOcrContext, x) +#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM) + +static const AVOption graphicsub2text_options[] = { + { "delay_when_no_duration", "delay output when duration is unknown", OFFSET(delay_when_no_duration), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS, NULL }, + { "dump_bitmaps", "save processed bitmaps as .ppm", OFFSET(dump_bitmaps), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS, NULL }, + { "font_size_factor", "font size adjustment factor", OFFSET(font_size_factor), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 0.2, 5, FLAGS, NULL }, + { "language", "ocr language", OFFSET(language), AV_OPT_TYPE_STRING, { .str = "eng" }, 0, 0, FLAGS, NULL }, + { "ocr_mode", "set ocr mode", OFFSET(ocr_mode), AV_OPT_TYPE_INT, { .i64=OEM_TESSERACT_ONLY }, OEM_TESSERACT_ONLY, 2, FLAGS, "ocr_mode" }, + { "tesseract", "classic tesseract ocr", 0, AV_OPT_TYPE_CONST, { .i64=OEM_TESSERACT_ONLY }, 0, 0, FLAGS, "ocr_mode" }, + { "lstm", "lstm (ML based)", 0, AV_OPT_TYPE_CONST, { .i64=OEM_LSTM_ONLY}, 0, 0, FLAGS, "ocr_mode" }, + { "both", "use both models combined", 0, AV_OPT_TYPE_CONST, { .i64=OEM_TESSERACT_LSTM_COMBINED }, 0, 0, FLAGS, "ocr_mode" }, + { "preprocess_images", "reduce colors, remove outlines", OFFSET(preprocess_images), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS, NULL }, + { "recognize", "detect fonts, styles and colors", OFFSET(recognize), AV_OPT_TYPE_FLAGS, { .i64 = RFLAGS_ALL}, 0, INT_MAX, FLAGS, "reco_flags" }, + { "none", "no format detection", 0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_NONE }, 0, 0, FLAGS, "reco_flags" }, + { "halign", "horizontal alignment", 0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_HALIGN }, 0, 0, FLAGS, "reco_flags" }, + { "valign", "vertical alignment", 0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_VALIGN }, 0, 0, FLAGS, "reco_flags" }, + { "bold", "font bold", 0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FBOLD }, 0, 0, FLAGS, "reco_flags" }, + { "italic", "font italic", 0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FITALIC }, 0, 0, FLAGS, "reco_flags" }, + { "underline", "font underline", 0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FUNDERLINE }, 0, 0, FLAGS, "reco_flags" }, + { "font", "font name", 0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FONT }, 0, 0, FLAGS, "reco_flags" }, + { "fontsize", "font size", 0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_FONTSIZE }, 0, 0, FLAGS, "reco_flags" }, + { "color", "font color", 0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_COLOR }, 0, 0, FLAGS, "reco_flags" }, + { "outlinecolor", "outline color", 0, AV_OPT_TYPE_CONST, { .i64 = RFLAGS_OUTLINECOLOR }, 0, 0, FLAGS, "reco_flags" }, + { "tessdata_path", "path to tesseract data", OFFSET(tessdata_path), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS, NULL }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(graphicsub2text); + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .filter_frame = filter_frame, + .config_props = config_input, + }, +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .config_props = config_output, + }, +}; + +const AVFilter ff_sf_graphicsub2text = { + .name = "graphicsub2text", + .description = NULL_IF_CONFIG_SMALL("Convert graphical subtitles to text subtitles via OCR"), + .init = init, + .uninit = uninit, + .priv_size = sizeof(SubOcrContext), + .priv_class = &graphicsub2text_class, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_QUERY_FUNC(query_formats), +}; From patchwork Fri Jan 14 01:13:28 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33581 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp987033iog; Thu, 13 Jan 2022 17:17:06 -0800 (PST) X-Google-Smtp-Source: ABdhPJxwz8dKxKGSdiZXIWr2q+ZkL9SMUgn8Ma78vl4tQrifakAcxzjzWRbcgqz7BqXMDoP4gthb X-Received: by 2002:aa7:c6c1:: with SMTP id b1mr6688243eds.172.1642123026327; Thu, 13 Jan 2022 17:17:06 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642123026; cv=none; d=google.com; s=arc-20160816; b=ar52cd1ACLj4ee41N1MOLEEEdLFfeJH8TVmZo0Q/rg4mJcV78FOrujzMWldo3Z/QwN GW8oX6UmXwnATSj5CmwphGIGiMNAhS6hayZDi8q2U0Wr534t7cuhIQLiNGIcE69eNLcW WxOLErIl1Lw1e9D2+W66Rnu3prxe5SzmGkvj5anmi2ckOzKqKmqbqUBAskljbBThx+Wj tIzm+0MSuUJsBUjfDzn8OILJ1yDLD5PEIpm4cVt+xdLRTtFVsoL5xmIQmby7BDLWaELx BWg05C6Bt6ER44crjUmeAZwdTggeu5+hoqjuvg1qvE/akmjP9XKru80WTRuk8ljrj3zR vJlA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=fKcNsobU/L4mWg4u20G7o5yTXiqUXCxrQoQnP4hwjwE=; b=PinD25dargRfoWLRQAoUIFGu5vco4NbPZ0ntZiukQmmkeRCShOU8gx1ppDJeoP6TFB MgTj47Jaw126QSnvOU5aJ2+dmHt/EI2u5IH/TxfsXJQSIUuv5Lj6PKm+3BMvb5jxcegD So3M2SKNbLNKu4iV5+Mm6HsKbBMe4MMgXSUqA41vAaoRmrN6KOcm5zBIScjSpAEb+3Sn w5tIrISUm7yvKWULeTyn9NtecrxLN9p1TVVz1oSXMqpcV1EHwbl4mty25ob0I/8arHTm 3lOB8+OaYJjZoQA0QMd6yDj+W2txD64fvWWZ2rd8A1v2rXJoZ4GdW+W2jtjk/Pw9eXPo FcAQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b="YW/1Xm+d"; 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 i9si2137121ejz.292.2022.01.13.17.17.05; Thu, 13 Jan 2022 17:17:06 -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 header.s=20210112 header.b="YW/1Xm+d"; 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 E0B7A68B67A; Fri, 14 Jan 2022 03:14:05 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f181.google.com (mail-pf1-f181.google.com [209.85.210.181]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id BCFE168B648 for ; Fri, 14 Jan 2022 03:13:57 +0200 (EET) Received: by mail-pf1-f181.google.com with SMTP id s15so1403205pfw.1 for ; Thu, 13 Jan 2022 17:13:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=kMRmZeEAqEwG9mWn83X4mljGEKOfsamyT230jCRczJ4=; b=YW/1Xm+dUd8yucRS7c4XVRHhE6mJAot2kq3cPASnb6yLQLNONFcoPsx9elsrAzf2CK 4/2paLzfwjIuNawuKTgtVJE0INOv9MKnmYO/klqhuq7ypfE6cI0bPOCJeuCXHLLWM7Fa SHt41mZp93ZzC+aReAL6wfXR9OKBXJfCq2k1aaPmMN3AhquQjvTpLzEJnEXFZed3LtOz gGGAeU+VAZHCNvSVykiK7CSQQ+H9IIqY5mQ5UmU06va93rgS0pgDTvkjIKFTPr3aaP8k ObIC2xqIziTJclJDJWlhD14SISCcBdHUo3x9Y+63TsAmz6h2Lo/0Fz/CvVLravKVi7SY lvkw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=kMRmZeEAqEwG9mWn83X4mljGEKOfsamyT230jCRczJ4=; b=n3VbZTjt96/wDcY2PIs6hF9cpEiqExdlwPiISaG7Nv1TEXfnfKmNYFykC+nP3sTv4t Z1NPOpIOjMHz8hpXw54GR+bppoFRBcNHYBSqPsr3TmFoWFejO+0Z0tTcOtym7VxzwmOS KSd5hzFhV/Mjf/6xbxP5GSMz/yf6RjeWg0diHuoNoUh6ZOr3ym+MSi+NUWQQwjmrTT7x UbUtCwzMH1McVR38F83v99+FiXMmR9Zf+WHuXcvhUDkTxQaqPiQLyI0cQ3tUtPZzJuWB d/MCebafe07XE6wvaXl8H/GEPYUjDCzQRSU74+et2GaUn71o/Aq+A+1NE/b8c7cuofyE 3vIQ== X-Gm-Message-State: AOAM533DRAK8MzpmtVrKoxnJBvL6JQ2oYlyDOo+w21/WEoIM8r01TxeW a2bUqeBguDhMknwlqTbwpow4i3Rvlyyotg== X-Received: by 2002:a05:6a00:2356:b0:4c1:c351:a7d0 with SMTP id j22-20020a056a00235600b004c1c351a7d0mr6927742pfj.36.1642122835632; Thu, 13 Jan 2022 17:13:55 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id p186sm3776239pfp.128.2022.01.13.17.13.54 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:55 -0800 (PST) Message-Id: In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:28 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 19/24] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: mOznZcVvm9BU From: softworkz Signed-off-by: softworkz --- configure | 1 + doc/filters.texi | 164 +++++++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/sf_subscale.c | 884 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 1051 insertions(+) create mode 100644 libavfilter/sf_subscale.c diff --git a/configure b/configure index ee7afffb05..a8b7ce8a26 100755 --- a/configure +++ b/configure @@ -3731,6 +3731,7 @@ sr_filter_deps="avformat swscale" sr_filter_select="dnn" stereo3d_filter_deps="gpl" splitcc_filter_deps="avcodec" +subscale_filter_deps="swscale avcodec" subtitles_filter_deps="avformat avcodec libass" super2xsai_filter_deps="gpl" pixfmts_super2xsai_test_deps="super2xsai_filter" diff --git a/doc/filters.texi b/doc/filters.texi index 7bc02b9415..ee6e184882 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -26303,6 +26303,170 @@ Set the rendering margin in pixels. For rendering, alway use the latest event only, which is covering the given point in time. @end table +@section subscale + +Provides high-quality scaling and rearranging functionality for graphical subtitles. + +The subscale filter provides multiple approaches for manipulating +the size and position of graphical subtitle rectangles wich can +be combined or used separately. +Scaling is performed by converting the palettized subtitle bitmaps +to RGBA and re-quantization to palette colors afterwards via elbg algorithm. + +The two major operations are 'scale' and 're-arrange' with the +latter being separated as 'arrange_h' and 'arrange_v'. + + +Inputs: +- 0: Subtitles [bitmap] + +Outputs: +- 0: Subtitles [bitmap] + +It accepts the following parameters: + +@table @option + +@item w, width +Set the width of the output. +Width and height in case of graphical subtitles are just indicating +a virtual size for which the output (consisting of 0-n bitmap rectangles) +is intended to be displayed on. + +@item h, height +Set the height of the output. + +@item margin_h +Sets a horizontal margin to be preserverved when using any +of the arrange modes. + +@item margin_v +Sets a vertical margin to be preserverved when using any +of the arrange modes. + +@item force_original_aspect_ratio +Enable decreasing or increasing output video width or height if necessary to +keep the original aspect ratio. Possible values: + +@table @samp +@item disable +Scale the video as specified and disable this feature. + +@item decrease +The output video dimensions will automatically be decreased if needed. + +@item increase +The output video dimensions will automatically be increased if needed. + +@end table + + +@item scale_mode +Specifies how subtitle bitmaps should be scaled. +The scale factor is determined by the the factor between input +and output size. + +@table @samp +@item none +Do not apply any common scaling. + +@item uniform +Uniformly scale all subtitle bitmaps including their positions. + +@item uniform_no_reposition +Uniformly scale all subtitle bitmaps without changing positions. + +@end table + + +@item arrange_h +Specifies how subtitle bitmaps should be arranged horizontally. + +@item arrange_v +Specifies how subtitle bitmaps should be arranged vertically. + + +@table @samp +@item none +Do not rearrange subtitle bitmaps. + +@item margin_no_scale +Move subtitle bitmaps to be positioned inside the specified +margin (margin_h or margin_v) when possible and without scaling. + +@item margin_and_scale +Move subtitle bitmaps to be positioned inside the specified +margin (margin_h or margin_v) and scale in case it doesn't fit. + +@item snapalign_no_scale +Categorize subtitle bitmap positions as one of left/center/right +or top/bottom/middle based on original positioning and apply +these alignments for the target positioning. +No scaling will be applied. + +@item snapalign_and_scale +Categorize subtitle bitmap positions as one of left/center/right +or top/bottom/middle based on original positioning and apply +these alignments for the target positioning. +Bitmaps that do not fit inside the margins borders are +scaled to fit. +@end table + +@item eval +Set evaluation mode for the expressions (@option{width}, @option{height}). + +It accepts the following values: +@table @samp +@item init +Evaluate expressions only once during the filter initialization. + +@item frame +Evaluate expressions for each incoming frame. This is way slower than the +@samp{init} mode since it requires all the scalers to be re-computed, but it +allows advanced dynamic expressions. +@end table + +Default value is @samp{init}. + + +@item num_colors +Set the number of palette colors for output images. +Choose the maximum (256) when further processing is done (e.g. +overlaying on a video). +When subtitles will be encoded as bitmap subtitles (e.g. dvbsub), +a smaller number of palette colors (e.g. 4-16) might need to be used, depending +on the target format and codec. + +@item bitmap_width_align +@item bitmap_height_align +Make sure that subtitle bitmap sizes are a multiple of this +value. Default is 2. + +@end table + +@subsection Examples + +@itemize +@item +Uniformly scale down video and bitmap subtitles and encode +subtitles as dvbsub. +@example +ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=480x270[vid1];[0:10]subscale=w=480:h=270:num_colors=16[sub1]" -map [vid1] -map [sub1] -c:s dvbsub -c:v libx265 output.ts +@end example +@item +Squeeze video vertically and arrange subtitle bitmaps +inside the video area without scaling, then overlay +subtitles onto the video. +@example +ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=1920x400,setsar=1[vid1];[0:10]subscale=w=1920:h=400:scale_mode=none:margin_h=50:arrange_h=margin_no_scale:arrange_v=margin_no_scale:margin_v=50:num_colors=256[sub1];[vid1][sub1]overlay_graphicsubs" -c:v libx265 output.ts +@end example +@item +Scale both, a video and its embedded VOB subs, then encode them as separate streams into an MKV. +@example +ffmpeg -y -y -loglevel verbose -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=720x576[vid1];[0:7]subscale=w=720:h=576:num_colors=16[sub1]" -map [vid1] -map [sub1] -c:s dvdsub out.mkv +@end example +@end itemize + @c man end SUBTITLE FILTERS @chapter Multimedia Filters diff --git a/libavfilter/Makefile b/libavfilter/Makefile index ead3e38507..0e3e48613e 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -569,6 +569,7 @@ OBJS-$(CONFIG_SHOW_SPEAKER_FILTER) += sf_textmod.o OBJS-$(CONFIG_TEXTMOD_FILTER) += sf_textmod.o OBJS-$(CONFIG_SPLITCC_FILTER) += sf_splitcc.o OBJS-$(CONFIG_STRIPSTYLES_FILTER) += sf_stripstyles.o +OBJS-$(CONFIG_SUBSCALE_FILTER) += sf_subscale.o # multimedia filters OBJS-$(CONFIG_ABITSCOPE_FILTER) += avf_abitscope.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 34576016ce..8e4f2feca3 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -552,6 +552,7 @@ extern const AVFilter ff_sf_graphicsub2text; extern const AVFilter ff_sf_showspeaker; extern const AVFilter ff_sf_splitcc; extern const AVFilter ff_sf_stripstyles; +extern const AVFilter ff_sf_subscale; extern const AVFilter ff_sf_textmod; extern const AVFilter ff_svf_graphicsub2video; extern const AVFilter ff_svf_textsub2video; diff --git a/libavfilter/sf_subscale.c b/libavfilter/sf_subscale.c new file mode 100644 index 0000000000..04f0f16c27 --- /dev/null +++ b/libavfilter/sf_subscale.c @@ -0,0 +1,884 @@ +/* + * Copyright (c) 2021 softworkz + * + * 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 + * scale graphical subtitles filter + */ + +#include + +#include "drawutils.h" +#include "internal.h" +#include "scale_eval.h" +#include "libavutil/eval.h" +#include "libavutil/opt.h" +#include "libswscale/swscale.h" + +#include "libavcodec/elbg.h" + +static const char *const var_names[] = { + "in_w", "iw", + "in_h", "ih", + "out_w", "ow", + "out_h", "oh", + "a", + "sar", + "dar", + "margin_h", + "margin_v", + NULL +}; + +enum var_name { + VAR_IN_W, VAR_IW, + VAR_IN_H, VAR_IH, + VAR_OUT_W, VAR_OW, + VAR_OUT_H, VAR_OH, + VAR_A, + VAR_SAR, + VAR_DAR, + VARS_B_H, + VARS_B_V, + VARS_NB +}; + +enum EvalMode { + EVAL_MODE_INIT, + EVAL_MODE_FRAME, + EVAL_MODE_NB +}; + +enum SubScaleMode { + SSM_NONE, + SSM_UNIFORM, + SSM_UNIFORM_NO_REPOSITION, +}; + +enum SubArrangeMode { + SAM_NONE, + SAM_ENSUREMARGIN_NO_SCALE, + SAM_ENSUREMARGIN_AND_SCALE, + SAM_SNAPALIGNMENT_NO_SCALE, + SAM_SNAPALIGNMENT_AND_SCALE, +}; + +typedef struct SubScaleContext { + const AVClass *class; + struct SwsContext *sws; + AVDictionary *opts; + + int w, h; + + char *w_expr; ///< width expression string + char *h_expr; ///< height expression string + AVExpr *w_pexpr; + AVExpr *h_pexpr; + double var_values[VARS_NB]; + + int force_original_aspect_ratio; + int eval_mode; ///< expression evaluation mode + + int use_caching; + + // Scale Options + enum SubScaleMode scale_mode; + + // Arrange Options + enum SubArrangeMode arrange_mode_h; + enum SubArrangeMode arrange_mode_v; + int margin_h; + int margin_v; + char *margin_h_expr; + char *margin_v_expr; + AVExpr *margin_h_pexpr; + AVExpr *margin_v_pexpr; + + // Bitmap Options + int num_output_colors; + int bitmap_width_align; + int bitmap_height_align; + + // Color Quantization Fields + struct ELBGContext *ctx; + AVLFG lfg; + int *codeword; + int *codeword_closest_codebook_idxs; + int *codebook; + int r_idx, g_idx, b_idx, a_idx; + AVFrame *cache_frame; +} SubScaleContext; + + +static int config_output(AVFilterLink *outlink); + +static int check_exprs(AVFilterContext *ctx) +{ + const SubScaleContext *s = ctx->priv; + unsigned vars_w[VARS_NB] = { 0 }, vars_h[VARS_NB] = { 0 }; + + if (!s->w_pexpr && !s->h_pexpr) + return AVERROR(EINVAL); + + if (s->w_pexpr) + av_expr_count_vars(s->w_pexpr, vars_w, VARS_NB); + if (s->h_pexpr) + av_expr_count_vars(s->h_pexpr, vars_h, VARS_NB); + + if (vars_w[VAR_OUT_W] || vars_w[VAR_OW]) { + av_log(ctx, AV_LOG_ERROR, "Width expression cannot be self-referencing: '%s'.\n", s->w_expr); + return AVERROR(EINVAL); + } + + if (vars_h[VAR_OUT_H] || vars_h[VAR_OH]) { + av_log(ctx, AV_LOG_ERROR, "Height expression cannot be self-referencing: '%s'.\n", s->h_expr); + return AVERROR(EINVAL); + } + + if ((vars_w[VAR_OUT_H] || vars_w[VAR_OH]) && + (vars_h[VAR_OUT_W] || vars_h[VAR_OW])) { + av_log(ctx, AV_LOG_WARNING, "Circular references detected for width '%s' and height '%s' - possibly invalid.\n", s->w_expr, s->h_expr); + } + + if (s->margin_h_pexpr) + av_expr_count_vars(s->margin_h_pexpr, vars_w, VARS_NB); + if (s->margin_v_pexpr) + av_expr_count_vars(s->margin_v_pexpr, vars_h, VARS_NB); + + return 0; +} + +static int scale_parse_expr(AVFilterContext *ctx, char *str_expr, AVExpr **pexpr_ptr, const char *var, const char *args) +{ + SubScaleContext *s = ctx->priv; + int ret, is_inited = 0; + char *old_str_expr = NULL; + AVExpr *old_pexpr = NULL; + + if (str_expr) { + old_str_expr = av_strdup(str_expr); + if (!old_str_expr) + return AVERROR(ENOMEM); + av_opt_set(s, var, args, 0); + } + + if (*pexpr_ptr) { + old_pexpr = *pexpr_ptr; + *pexpr_ptr = NULL; + is_inited = 1; + } + + ret = av_expr_parse(pexpr_ptr, args, var_names, + NULL, NULL, NULL, NULL, 0, ctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Cannot parse expression for %s: '%s'\n", var, args); + goto revert; + } + + ret = check_exprs(ctx); + if (ret < 0) + goto revert; + + if (is_inited && (ret = config_output(ctx->outputs[0])) < 0) + goto revert; + + av_expr_free(old_pexpr); + av_freep(&old_str_expr); + + return 0; + +revert: + av_expr_free(*pexpr_ptr); + *pexpr_ptr = NULL; + if (old_str_expr) { + av_opt_set(s, var, old_str_expr, 0); + av_free(old_str_expr); + } + if (old_pexpr) + *pexpr_ptr = old_pexpr; + + return ret; +} + +static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts) +{ + SubScaleContext *s = ctx->priv; + uint8_t rgba_map[4]; + int ret; + + if (!s->w_expr) + av_opt_set(s, "w", "iw", 0); + if (!s->h_expr) + av_opt_set(s, "h", "ih", 0); + + ret = scale_parse_expr(ctx, NULL, &s->w_pexpr, "width", s->w_expr); + if (ret < 0) + return ret; + + ret = scale_parse_expr(ctx, NULL, &s->h_pexpr, "height", s->h_expr); + if (ret < 0) + return ret; + + av_log(ctx, AV_LOG_VERBOSE, "w:%s h:%s\n", + s->w_expr, s->h_expr); + + if (!s->margin_h_expr) + av_opt_set(s, "margin_h", "0", 0); + if (!s->margin_v_expr) + av_opt_set(s, "margin_v", "0", 0); + + ret = scale_parse_expr(ctx, NULL, &s->margin_h_pexpr, "margin_h", s->margin_h_expr); + if (ret < 0) + return ret; + + ret = scale_parse_expr(ctx, NULL, &s->margin_v_pexpr, "margin_v", s->margin_v_expr); + if (ret < 0) + return ret; + + s->opts = *opts; + *opts = NULL; + + ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32); + + s->r_idx = rgba_map[0]; // R + s->g_idx = rgba_map[1]; // G + s->b_idx = rgba_map[2]; // B + s->a_idx = rgba_map[3]; // A + + av_lfg_init(&s->lfg, 123456789); + + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + SubScaleContext *s = ctx->priv; + + av_frame_free(&s->cache_frame); + + av_expr_free(s->w_pexpr); + av_expr_free(s->h_pexpr); + s->w_pexpr = s->h_pexpr = NULL; + + av_expr_free(s->margin_h_pexpr); + av_expr_free(s->margin_v_pexpr); + s->margin_h_pexpr = s->margin_v_pexpr = NULL; + + sws_freeContext(s->sws); + s->sws = NULL; + av_dict_free(&s->opts); + + avpriv_elbg_free(&s->ctx); + + av_freep(&s->codebook); + av_freep(&s->codeword); + av_freep(&s->codeword_closest_codebook_idxs); +} + +static int config_input(AVFilterLink *inlink) +{ + ////const AVFilterContext *ctx = inlink->dst; + ////SubScaleContext *s = ctx->priv; + + ////if (s->w <= 0 || s->h <= 0) { + //// s->w = inlink->w; + //// s->h = inlink->h; + ////} + return 0; +} + +static int scale_eval_dimensions(AVFilterContext *ctx) +{ + SubScaleContext *s = ctx->priv; + const AVFilterLink *inlink = ctx->inputs[0]; + char *expr; + int eval_w, eval_h, margin_h, margin_v; + int ret; + double res; + + s->var_values[VAR_IN_W] = s->var_values[VAR_IW] = inlink->w; + s->var_values[VAR_IN_H] = s->var_values[VAR_IH] = inlink->h; + s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = NAN; + s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = NAN; + s->var_values[VARS_B_H] = s->var_values[VARS_B_V] = 0; + s->var_values[VAR_A] = (double) inlink->w / inlink->h; + s->var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? + (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1; + s->var_values[VAR_DAR] = s->var_values[VAR_A] * s->var_values[VAR_SAR]; + + res = av_expr_eval(s->w_pexpr, s->var_values, NULL); + s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res; + + res = av_expr_eval(s->h_pexpr, s->var_values, NULL); + if (isnan(res)) { + expr = s->h_expr; + ret = AVERROR(EINVAL); + goto fail; + } + eval_h = s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = (int) res == 0 ? inlink->h : (int) res; + + res = av_expr_eval(s->w_pexpr, s->var_values, NULL); + if (isnan(res)) { + expr = s->w_expr; + ret = AVERROR(EINVAL); + goto fail; + } + eval_w = s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res; + + s->w = eval_w; + s->h = eval_h; + + res = av_expr_eval(s->margin_h_pexpr, s->var_values, NULL); + s->var_values[VARS_B_H] = (int)res; + + res = av_expr_eval(s->margin_v_pexpr, s->var_values, NULL); + if (isnan(res)) { + expr = s->margin_v_expr; + ret = AVERROR(EINVAL); + goto fail; + } + margin_v = s->var_values[VARS_B_V] = (int)res; + + res = av_expr_eval(s->margin_h_pexpr, s->var_values, NULL); + if (isnan(res)) { + expr = s->margin_h_expr; + ret = AVERROR(EINVAL); + goto fail; + } + margin_h = s->var_values[VARS_B_H] = (int)res; + + s->margin_h = margin_h; + s->margin_v = margin_v; + + return 0; + +fail: + av_log(ctx, AV_LOG_ERROR, + "Error when evaluating the expression '%s'.\n", expr); + return ret; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AVFilterLink *inlink = outlink->src->inputs[0]; + SubScaleContext *s = ctx->priv; + int ret; + + outlink->format = AV_SUBTITLE_FMT_BITMAP; + outlink->time_base = ctx->inputs[0]->time_base; + outlink->frame_rate = ctx->inputs[0]->frame_rate; + + if ((ret = scale_eval_dimensions(ctx)) < 0) + goto fail; + + ff_scale_adjust_dimensions(inlink, &s->w, &s->h, + s->force_original_aspect_ratio, 2); + + if (s->w > INT_MAX || + s->h > INT_MAX || + (s->h * inlink->w) > INT_MAX || + (s->w * inlink->h) > INT_MAX) + av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n"); + + outlink->w = s->w; + outlink->h = s->h; + + if (s->sws) + sws_freeContext(s->sws); + + s->sws = sws_alloc_context(); + if (!s->sws) + return AVERROR(ENOMEM); + + av_opt_set_pixel_fmt(s->sws, "src_format", AV_PIX_FMT_PAL8, 0); + av_opt_set_int(s->sws, "dst_format", AV_PIX_FMT_RGB32, 0); + av_opt_set_int(s->sws, "threads", ff_filter_get_nb_threads(ctx), 0); + + if (s->opts) { + const AVDictionaryEntry *e = NULL; + while ((e = av_dict_get(s->opts, "", e, AV_DICT_IGNORE_SUFFIX))) { + if ((ret = av_opt_set(s->sws, e->key, e->value, 0)) < 0) + return ret; + } + } + + if ((ret = sws_init_context(s->sws, NULL, NULL)) < 0) + return ret; + + if (inlink->sample_aspect_ratio.num){ + outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h * inlink->w, outlink->w * inlink->h}, inlink->sample_aspect_ratio); + } else + outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; + + av_log(ctx, AV_LOG_VERBOSE, "Output size set to %dx%d.\n", outlink->w, outlink->h); + + return 0; +fail: + return ret; +} + +static int palettize_image(SubScaleContext *const s, const int w, const int h, const int src_linesize, uint8_t *src_data, + int dst_linesize, uint8_t *dst_data, uint32_t *dst_pal) +{ + int k, ret; + const int codeword_length = w * h; + + /* Re-Initialize */ + s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword)); + if (!s->codeword) + return AVERROR(ENOMEM); + + s->codeword_closest_codebook_idxs = + av_realloc_f(s->codeword_closest_codebook_idxs, codeword_length, + sizeof(*s->codeword_closest_codebook_idxs)); + if (!s->codeword_closest_codebook_idxs) + return AVERROR(ENOMEM); + + s->codebook = av_realloc_f(s->codebook, s->num_output_colors, 4 * sizeof(*s->codebook)); + if (!s->codebook) + return AVERROR(ENOMEM); + + /* build the codeword */ + k = 0; + for (int i = 0; i < h; i++) { + uint8_t *p = src_data; + for (int j = 0; j < w; j++) { + s->codeword[k++] = p[s->b_idx]; + s->codeword[k++] = p[s->g_idx]; + s->codeword[k++] = p[s->r_idx]; + s->codeword[k++] = p[s->a_idx]; + p += 4; + } + src_data += src_linesize; + } + + /* compute the codebook */ + ret = avpriv_elbg_do(&s->ctx, s->codeword, 4, + codeword_length, s->codebook, + s->num_output_colors, 1, + s->codeword_closest_codebook_idxs, &s->lfg, 0); + + if (ret < 0) + return ret; + + /* Write Palette */ + for (int i = 0; i < s->num_output_colors; i++) { + dst_pal[i] = s->codebook[i*4+3] << 24 | + (s->codebook[i*4+2] << 16) | + (s->codebook[i*4+1] << 8) | + s->codebook[i*4 ]; + } + + /* Write Image */ + k = 0; + for (int i = 0; i < h; i++) { + uint8_t *p = dst_data; + for (int j = 0; j < w; j++, p++) { + p[0] = s->codeword_closest_codebook_idxs[k++]; + } + + dst_data += dst_linesize; + } + + return ret; +} + +static int rescale_size(int64_t a, AVRational factor) +{ + const int64_t res = av_rescale_rnd(a, factor.num, factor.den, AV_ROUND_NEAR_INF); + if (res > INT32_MAX || res < 0) + return 0; + + return (int)res; +} + + +static int scale_area(AVFilterLink *link, AVSubtitleArea *area, const int target_width, const int target_height) +{ + const AVFilterContext *ctx = link->dst; + SubScaleContext *s = ctx->priv; + int ret; + + AVBufferRef *dst_buffer; + const uint8_t* data[2] = { area->buf[0]->data, (uint8_t *)&area->pal }; + const int dstW = FFALIGN(target_width, s->bitmap_width_align); + const int dstH = FFALIGN(target_height, s->bitmap_height_align); + const int tmp_linesize[2] = { FFALIGN(dstW * 4, 32), 0 }; + const int dst_linesize[2] = { dstW, 0 }; + uint8_t* tmp[2] = { 0, 0 }; + + AVBufferRef *tmp_buffer = av_buffer_allocz(tmp_linesize[0] * dstH); + if (!tmp_buffer) + return AVERROR(ENOMEM); + + if (!s->sws) + return 0; + + tmp[0] = tmp_buffer->data; + + s->sws = sws_getCachedContext(s->sws, area->w, area->h, AV_PIX_FMT_PAL8, + dstW, dstH, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL); + if (!s->sws) { + av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context. dstW=%d dstH=%d\n", dstW, dstH); + return AVERROR(EINVAL); + } + + // Rescale to ARGB + ret = sws_scale(s->sws, data, area->linesize, 0, area->h, tmp, tmp_linesize); + if (ret < 0) { + av_buffer_unref(&tmp_buffer); + return ret; + } + + // Alloc output buffer + dst_buffer = av_buffer_allocz(dst_linesize[0] * dstH); + if (!dst_buffer) { + av_buffer_unref(&tmp_buffer); + return AVERROR(ENOMEM); + } + + // Quantize to palettized image + ret = palettize_image(s, dstW, dstH, tmp_linesize[0], tmp[0], dst_linesize[0], dst_buffer->data, area->pal); + av_buffer_unref(&tmp_buffer); + + if (ret < 0) { + av_buffer_unref(&dst_buffer); + return ret; + } + + av_buffer_unref(&area->buf[0]); + ret = av_buffer_replace(&area->buf[0], dst_buffer); + if (ret < 0) { + av_buffer_unref(&dst_buffer); + return ret; + } + + area->w = dstW; + area->h = dstH; + area->linesize[0] = dst_linesize[0]; + area->nb_colors = s->num_output_colors; + + return ret; +} + +static int process_area(AVFilterLink *inlink, AVSubtitleArea *area, AVRational x_factor, AVRational y_factor) +{ + AVFilterContext *ctx = inlink->dst; + const SubScaleContext *s = ctx->priv; + int target_w, target_h, target_x, target_y; + const int border_l = s->margin_h; + const int border_r = s->w - s->margin_h; + const int border_t = s->margin_v; + const int border_b = s->h - s->margin_v; + + av_log(ctx, AV_LOG_DEBUG, "process_area - start: x/y: (%d:%d) size: %dx%d scale_mode: %d x-factor: %d:%d y-factor: %d:%d\n", + area->x, area->y, area->w, area->h, s->scale_mode, x_factor.num, x_factor.den, y_factor.num, y_factor.den); + + switch (s->scale_mode) { + case SSM_NONE: + target_w = area->w; + target_h = area->h; + target_x = area->x; + target_y = area->y; + break; + case SSM_UNIFORM: + target_w = rescale_size(area->w, x_factor); + target_h = rescale_size(area->h, y_factor); + target_x = rescale_size(area->x, x_factor); + target_y = rescale_size(area->y, y_factor); + break; + case SSM_UNIFORM_NO_REPOSITION: + target_w = rescale_size(area->w, x_factor); + target_h = rescale_size(area->h, y_factor); + target_x = area->x; + target_y = area->y; + break; + default: + av_log(ctx, AV_LOG_ERROR, "Invalid scale_mode: %d\n", s->scale_mode); + return AVERROR(EINVAL); + } + + av_log(ctx, AV_LOG_DEBUG, "process_area - scaled: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h); + + + switch (s->arrange_mode_h) { + case SAM_ENSUREMARGIN_AND_SCALE: + case SAM_SNAPALIGNMENT_AND_SCALE: + { + // IF it doesn't fit - scale it + int max_width = s->w - 2 * s->margin_h; + + if (max_width < 2) + max_width = 2; + + if (target_w > max_width) { + target_h = (int)av_rescale(target_h, max_width, target_w); + target_w = max_width; + target_x = s->margin_h; + } + } + break; + } + + switch (s->arrange_mode_h) { + case SAM_NONE: + break; + case SAM_ENSUREMARGIN_NO_SCALE: + case SAM_ENSUREMARGIN_AND_SCALE: + // Left border + if (target_x < border_l) + target_x = border_l; + + // Right border + if (target_x + target_w > border_r) + target_x = border_r - target_w; + + break; + case SAM_SNAPALIGNMENT_NO_SCALE: + case SAM_SNAPALIGNMENT_AND_SCALE: + { + // Use original values to detect alignment + const int left_margin = area->x; + const int right_margin = inlink->w - area->x - area->w; + const AVRational diff_factor_r = { left_margin - right_margin, area->w }; + const float diff_factor = (float)av_q2d(diff_factor_r); + + if (diff_factor > 0.2f) { + // Right aligned + target_x = border_r - target_w; + } else if (diff_factor < -0.2f) { + // Left aligned + target_x = border_l; + } else { + // Centered + target_x = (inlink->w - area->w) / 2; + } + } + + break; + default: + av_log(ctx, AV_LOG_ERROR, "Invalid arrange_mode_h: %d\n", s->arrange_mode_h); + return AVERROR(EINVAL); + } + + av_log(ctx, AV_LOG_DEBUG, "process_area - arr_h: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h); + + + switch (s->arrange_mode_v) { + case SAM_ENSUREMARGIN_AND_SCALE: + case SAM_SNAPALIGNMENT_AND_SCALE: + { + // IF it doesn't fit - scale it + int max_height = s->h - 2 * s->margin_v; + + if (max_height < 2) + max_height = 2; + + if (target_h > max_height) { + target_w = (int)av_rescale(target_w, max_height, target_h); + target_h = max_height; + target_y = s->margin_v; + } + } + break; + } + + switch (s->arrange_mode_v) { + case SAM_NONE: + break; + case SAM_ENSUREMARGIN_NO_SCALE: + case SAM_ENSUREMARGIN_AND_SCALE: + // Top border + if (target_y < border_t) + target_y = border_t; + + // Bottom border + if (target_y + target_h > border_b) + target_y = border_b - target_h; + + break; + case SAM_SNAPALIGNMENT_NO_SCALE: + case SAM_SNAPALIGNMENT_AND_SCALE: + { + // Use original values to detect alignment + const int top_margin = area->y; + const int bottom_margin = inlink->h - area->y - area->h; + const AVRational diff_factor_r = { top_margin - bottom_margin, area->h }; + const float diff_factor = (float)av_q2d(diff_factor_r); + + if (diff_factor > 0.2f) { + // Bottom aligned + target_y = border_b - target_h; + } else if (diff_factor < -0.2f) { + // Top aligned + target_y = border_t; + } else { + // Centered + target_y = (inlink->h - area->h) / 2; + } + } + + break; + default: + av_log(ctx, AV_LOG_ERROR, "Invalid arrange_mode_v: %d\n", s->arrange_mode_v); + return AVERROR(EINVAL); + } + + av_log(ctx, AV_LOG_VERBOSE, "process_area - arr_v: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h); + + area->x = target_x; + area->y = target_y; + + if (area->w != target_w || area->h != target_h) + return scale_area(inlink, area, target_w, target_h); + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + const AVFilterContext *ctx = inlink->dst; + SubScaleContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + int ret; + + // just forward empty frames + if (frame->num_subtitle_areas == 0) { + av_frame_free(&s->cache_frame); + return ff_filter_frame(outlink, frame); + } + + if (s->use_caching && s->cache_frame && frame->repeat_sub + && s->cache_frame->subtitle_timing.start_pts == frame->subtitle_timing.start_pts) { + AVFrame *out = av_frame_clone(s->cache_frame); + if (!out) + return AVERROR(ENOMEM); + + ret = av_frame_copy_props(out, frame); + if (ret < 0) + return ret; + + av_log(inlink->dst, AV_LOG_DEBUG, "subscale CACHED - size %dx%d pts: %"PRId64" areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas); + av_frame_free(&frame); + return ff_filter_frame(outlink, out); + } + + ret = av_frame_make_writable(frame); + if (ret >= 0) { + const AVRational x_factor = { .num = outlink->w, .den = inlink->w} ; + const AVRational y_factor = { .num = outlink->h, .den = inlink->h} ; + + for (unsigned i = 0; i < frame->num_subtitle_areas; ++i) { + AVSubtitleArea *area = frame->subtitle_areas[i]; + + ret = process_area(inlink, area, x_factor, y_factor); + if (ret < 0) + return ret; + } + + av_log(inlink->dst, AV_LOG_DEBUG, "subscale output - size %dx%d pts: %"PRId64" areas: %d\n", frame->width, frame->height, frame->subtitle_timing.start_pts, frame->num_subtitle_areas); + + if (s->use_caching) { + av_frame_free(&s->cache_frame); + s->cache_frame = av_frame_clone(frame); + } + + return ff_filter_frame(outlink, frame); + } + + return ret; +} + +#define OFFSET(x) offsetof(SubScaleContext, x) +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM) + +static const AVOption subscale_options[] = { + { "margin_h", "horizontal border", OFFSET(margin_h_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, + { "margin_v", "vertical border", OFFSET(margin_v_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, + { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, + { "width", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, + { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, + { "height","Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, + { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, FLAGS, "force_oar" }, + { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "force_oar" }, + { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "force_oar" }, + { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, "force_oar" }, + { "scale_mode", "specify how to scale subtitles", OFFSET(scale_mode), AV_OPT_TYPE_INT, {.i64 = SSM_UNIFORM}, 0, SSM_UNIFORM_NO_REPOSITION, FLAGS, "scale_mode" }, + { "none", "no common scaling", 0, AV_OPT_TYPE_CONST, {.i64=SSM_NONE}, .flags = FLAGS, .unit = "scale_mode" }, + { "uniform", "uniformly scale and reposition", 0, AV_OPT_TYPE_CONST, {.i64=SSM_UNIFORM}, .flags = FLAGS, .unit = "scale_mode" }, + { "uniform_no_reposition", "uniformly scale but keep positions", 0, AV_OPT_TYPE_CONST, {.i64=SSM_UNIFORM_NO_REPOSITION}, .flags = FLAGS, .unit = "scale_mode" }, + { "use_caching", "Cache output frames", OFFSET(use_caching), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, .flags = FLAGS }, + + { "arrange_h", "specify how to arrange subtitles horizontally", OFFSET(arrange_mode_h), AV_OPT_TYPE_INT, {.i64 = SAM_NONE}, 0, SAM_SNAPALIGNMENT_AND_SCALE, FLAGS, "arrange" }, + { "arrange_v", "specify how to arrange subtitles vertically", OFFSET(arrange_mode_v), AV_OPT_TYPE_INT, {.i64 = SAM_NONE}, 0, SAM_SNAPALIGNMENT_AND_SCALE, FLAGS, "arrange" }, + { "none", "no repositioning", 0, AV_OPT_TYPE_CONST, {.i64=SAM_NONE}, .flags = FLAGS, .unit = "arrange" }, + { "margin_no_scale", "move subs inside border when possible", 0, AV_OPT_TYPE_CONST, {.i64=SAM_ENSUREMARGIN_NO_SCALE,}, .flags = FLAGS, .unit = "arrange" }, + { "margin_and_scale", "move subs inside border and scale as needed", 0, AV_OPT_TYPE_CONST, {.i64=SAM_ENSUREMARGIN_AND_SCALE,}, .flags = FLAGS, .unit = "arrange" }, + { "snapalign_no_scale", "snap subs to near/far/center when possible", 0, AV_OPT_TYPE_CONST, {.i64=SAM_SNAPALIGNMENT_NO_SCALE,}, .flags = FLAGS, .unit = "arrange" }, + { "snapalign_and_scale", "snap subs to near/far/center and scale as needed", 0, AV_OPT_TYPE_CONST, {.i64=SAM_SNAPALIGNMENT_AND_SCALE,}, .flags = FLAGS, .unit = "arrange" }, + + { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" }, + { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" }, + { "frame", "eval expressions during initialization and per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" }, + { "num_colors", "number of palette colors in output", OFFSET(num_output_colors), AV_OPT_TYPE_INT, {.i64 = 256 }, 2, 256, .flags = FLAGS }, + { "bitmap_width_align", "Bitmap width alignment", OFFSET(bitmap_width_align), AV_OPT_TYPE_INT, {.i64 = 2 }, 1, 256, .flags = FLAGS }, + { "bitmap_height_align", "Bitmap height alignment", OFFSET(bitmap_height_align), AV_OPT_TYPE_INT, {.i64 = 2 }, 1, 256, .flags = FLAGS }, + { .name = NULL } +}; + +static const AVClass subscale_class = { + .class_name = "subscale", + .item_name = av_default_item_name, + .option = subscale_options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_FILTER, +}; + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .filter_frame = filter_frame, + .config_props = config_input, + }, +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .config_props = config_output, + }, +}; + +const AVFilter ff_sf_subscale = { + .name = "subscale", + .description = NULL_IF_CONFIG_SMALL("Scale graphical subtitles."), + .init_dict = init_dict, + .uninit = uninit, + .priv_size = sizeof(SubScaleContext), + .priv_class = &subscale_class, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_BITMAP), +}; + From patchwork Fri Jan 14 01:13:29 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33582 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp987186iog; Thu, 13 Jan 2022 17:17:17 -0800 (PST) X-Google-Smtp-Source: ABdhPJwM1cvXy7gfmFdfRuyfbz0LaSMtomq0pQ2NVMVFujFB4EH/Vfaq/r3C4AHx8MuRd6/m1IC/ X-Received: by 2002:aa7:d617:: with SMTP id c23mr3991162edr.138.1642123036870; Thu, 13 Jan 2022 17:17:16 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642123036; cv=none; d=google.com; s=arc-20160816; b=jF68kO2AbRk17Uzx62K6jWKsHW2oqeJVTNpuVxMJs9wSG8ejPZ5lxQHQk1N6zBAKpY jgN4uDbeegZZBD2lE+/KlmRYVoS1N6HSd0E4UDyG1P0XRaj6N5SMs+DIIXubt9i+fVru lk2LfBITBq9cB0tFP926WffsHr/we/9dmA7qEgkoS0pZ9tSa8DG4bXfH3wsjVqwT9h+D ZYsAMvmB9BphmUu43emTpAi6Q8cv8kl52BIgNOXsahAyOlhlxtjuZEsvULZaOP57iuxY 75uCYwi/yk3KmtRoLDrf9xIEeEf/cC+4nTUwkys1h/HtKxFhz1va+Nks7qx2Ap1hzLkz l75w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=4MFAKhanQGzIgfJRtGC/MchLDWgO9OlIagunB09X7LA=; b=cr7lLZWDqcmawc228tjHjnHEmdRqKXdUphlhPtLsgqV6FivVXxsNG0ZTtiomraY4hF 2Qru44kLBgcLlliT6S2i7HQGMPt3198sBmTzML40oQ1FfF6yxgPIHO5+GnxeJFKi5ux4 LZoOPkSMzTqicM9ln0BMDeTkufi2dgzlPvghIt8QT0xX2iZQ2vDJYjHCd9fow9cfncLH 0UH7LG49cG2ihMDnBuFcI3I+5aNOV3khDjRQWWLQ7o9xK4qYxBFmuRxFagy/lVgSn6Wp 9CA92PQSWoLjLRs6Czmoi0FkBbRIn/7W6F29yfKhR2xFKRKtkepS7+Gz3MlQQ+iyEiRT 9o9A== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=fxrSw5FF; 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 dt12si2538958ejc.3.2022.01.13.17.17.16; Thu, 13 Jan 2022 17:17:16 -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 header.s=20210112 header.b=fxrSw5FF; 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 B6DD768B6B7; Fri, 14 Jan 2022 03:14:06 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pl1-f172.google.com (mail-pl1-f172.google.com [209.85.214.172]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 7FCF768B63F for ; Fri, 14 Jan 2022 03:13:58 +0200 (EET) Received: by mail-pl1-f172.google.com with SMTP id c6so3484897plh.6 for ; Thu, 13 Jan 2022 17:13:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=MF95656/MFa2GFYw06UBU9Y72DDy/jUFdoK6guABViM=; b=fxrSw5FFCmCIrfaKcgosezGQMhK23qLa94vhMdnpHHzcDR4UX2coKSx4FRqIhV1qxJ V/831fR1Z1MYCfW7iirhcW7N19HmDVQsIyBeySk3ZBBuqk8SF0dzSFo0XQWgi/gs3yPq fCuvElxbTOIjw+dXln3r3C/3Bh5mCaHXpG92wL8cSDwz6jDqzDRFbM/DhFRTGSF+as1R HTJYz1qGgq8Y9XkdD6emKwx4U13Wvtjda0wJS8ynub7Kx1F7iZq8tzatbI+EYTq1YBVY WIFyaiJHjK1O3uVGRBLVGHIEUwi3IG52nmGhOHtW7G9eer8GOya3hLvqfJ4r+/dFemwd JFvQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=MF95656/MFa2GFYw06UBU9Y72DDy/jUFdoK6guABViM=; b=d9Dlielf1yDRKy4F16tPzmyWIxY6IW3ZPWYzKlAFJG64MXOEEg7Qc9e0gcFIALlopi PRqd+jIyS6reCMtpfE0gISqaWL2CabecPVPImzN0QO+cAQoaThDjdnnPJYAGLX7SeXfY ChwroxVrLSfExxtpFWM/KSbdDXtfXAkfGB2FL4DlaRF6FWB/YNlixzjl4tsYGmcXR4qx mAUNiZfbjZV1Wq/37QEwh4COzal6Lem2pA8ZXM86jf7gT4DrOBIPPUB0sQtKG70h8xVt Tgtl76m2S0Gr1pKviZTXqRRtaxNyU70Adg8d9VhDcUe0dt4pu1k/9xTuj+iDmTzJuIX/ 0b1g== X-Gm-Message-State: AOAM530f6e++Y8d0Pcq8fnLHd4ORGwqAwyFmZ/efpvJ+2qhMTwfu5Qz3 obZxUHAtOr/7EPGNQ67htFx/yaU9kwBNow== X-Received: by 2002:a17:90a:7788:: with SMTP id v8mr17280289pjk.216.1642122836614; Thu, 13 Jan 2022 17:13:56 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id f8sm9317075pjs.47.2022.01.13.17.13.55 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:56 -0800 (PST) Message-Id: In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:29 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 20/24] avfilter/subfeed: add subtitle feed filter 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: l3EA7bltif3u From: softworkz Signed-off-by: softworkz --- libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/sf_subfeed.c | 366 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 368 insertions(+) create mode 100644 libavfilter/sf_subfeed.c diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 0e3e48613e..5711c7770b 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -570,6 +570,7 @@ OBJS-$(CONFIG_TEXTMOD_FILTER) += sf_textmod.o OBJS-$(CONFIG_SPLITCC_FILTER) += sf_splitcc.o OBJS-$(CONFIG_STRIPSTYLES_FILTER) += sf_stripstyles.o OBJS-$(CONFIG_SUBSCALE_FILTER) += sf_subscale.o +OBJS-$(CONFIG_SUBFEED_FILTER) += sf_subfeed.o # multimedia filters OBJS-$(CONFIG_ABITSCOPE_FILTER) += avf_abitscope.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 8e4f2feca3..10b14b7b8e 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -553,6 +553,7 @@ extern const AVFilter ff_sf_showspeaker; extern const AVFilter ff_sf_splitcc; extern const AVFilter ff_sf_stripstyles; extern const AVFilter ff_sf_subscale; +extern const AVFilter ff_sf_subfeed; extern const AVFilter ff_sf_textmod; extern const AVFilter ff_svf_graphicsub2video; extern const AVFilter ff_svf_textsub2video; diff --git a/libavfilter/sf_subfeed.c b/libavfilter/sf_subfeed.c new file mode 100644 index 0000000000..8a7ac5528b --- /dev/null +++ b/libavfilter/sf_subfeed.c @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2021 softworkz + * + * 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 + * subtitle filter for feeding subtitle frames into a filtergraph in a contiguous way + * + * + * also supports + * - duration fixup + * delaying a subtitle event with unknown duration and infer duration from the + * start time of the subsequent subtitle + * - scattering + * splitting a subtitle event with unknown duration into multiple ones with + * a short and fixed duration + * + */ + +#include "filters.h" +#include "libavutil/opt.h" +#include "subtitles.h" +#include "libavutil/avassert.h" + +enum SubFeedMode { + FM_REPEAT, + FM_SCATTER, + FM_FORWARD, +}; + +typedef struct SubFeedContext { + const AVClass *class; + enum AVSubtitleType format; + enum SubFeedMode mode; + + AVRational frame_rate; + int fix_durations; + int fix_overlap; + + int current_frame_isnew; + int eof; + int got_first_input; + int need_frame; + int64_t next_pts_offset; + int64_t recent_subtitle_pts; + + int64_t counter; + + /** + * Queue of frames waiting to be filtered. + */ + FFFrameQueue fifo; + +} SubFeedContext; + +static int64_t ms_to_avtb(int64_t ms) +{ + return av_rescale_q(ms, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q); +} + +static int64_t avtb_to_ms(int64_t avtb) +{ + return av_rescale_q(avtb, AV_TIME_BASE_Q, (AVRational){ 1, 1000 }); +} + +static int init(AVFilterContext *ctx) +{ + SubFeedContext *s = ctx->priv; + + ff_framequeue_init(&s->fifo, NULL); + + return 0; +} + +static void uninit(AVFilterContext *ctx) +{ + SubFeedContext *s = ctx->priv; + ff_framequeue_free(&s->fifo); +} + +static int config_input(AVFilterLink *link) +{ + ////const subfeedContext *context = link->dst->priv; + + return 0; +} + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats; + AVFilterLink *inlink0 = ctx->inputs[0]; + AVFilterLink *outlink0 = ctx->outputs[0]; + static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_BITMAP, AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NB }; + int ret; + + formats = ff_make_format_list(subtitle_fmts); + + if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0) + return ret; + + if ((ret = ff_formats_ref(formats, &outlink0->incfg.formats)) < 0) + return ret; + + return 0; +} + +static int config_output(AVFilterLink *outlink) +{ + SubFeedContext *s = outlink->src->priv; + const AVFilterLink *inlink = outlink->src->inputs[0]; + + outlink->time_base = AV_TIME_BASE_Q; + outlink->format = inlink->format; + outlink->w = inlink->w; + outlink->h = inlink->h; + + if (s->mode == FM_FORWARD) + outlink->frame_rate = (AVRational) { 1, 0 }; + else + outlink->frame_rate = s->frame_rate; + + return 0; +} + +static int request_frame(AVFilterLink *outlink) +{ + SubFeedContext *s = outlink->src->priv; + AVFilterLink *inlink = outlink->src->inputs[0]; + int64_t last_pts = outlink->current_pts; + int64_t next_pts; + int64_t interval = ms_to_avtb((int64_t)(av_q2d(av_inv_q(outlink->frame_rate)) * 1000)); + AVFrame *out; + int status; + + if (s->mode == FM_FORWARD) + return ff_request_frame(inlink); + + s->counter++; + if (interval == 0) + interval = ms_to_avtb(200); + + status = ff_outlink_get_status(inlink); + if (status == AVERROR_EOF) + s->eof = 1; + + if (s->eof) + return AVERROR_EOF; + + if (!s->got_first_input && inlink->current_pts != AV_NOPTS_VALUE) { + + s->got_first_input = 1; + next_pts = av_rescale_q(inlink->current_pts, inlink->time_base, AV_TIME_BASE_Q); + if (next_pts < last_pts) + next_pts = last_pts + interval; + + } else if (last_pts == AV_NOPTS_VALUE) + next_pts = av_rescale_q(inlink->current_pts, inlink->time_base, AV_TIME_BASE_Q); + else + next_pts = last_pts + interval; + + if (next_pts == AV_NOPTS_VALUE) + next_pts = 0; + + if (s->next_pts_offset) { + next_pts -= s->next_pts_offset; + s->next_pts_offset = 0; + } + +retry: + if (ff_framequeue_queued_frames(&s->fifo) && !s->current_frame_isnew) { + + const AVFrame *current_frame = ff_framequeue_peek(&s->fifo, 0); + const int64_t sub_end_time = current_frame->subtitle_timing.start_pts + current_frame->subtitle_timing.duration; + + if (next_pts > sub_end_time) { + AVFrame *remove_frame = ff_framequeue_take(&s->fifo); + av_frame_free(&remove_frame); + + if (ff_framequeue_queued_frames(&s->fifo)) { + s->current_frame_isnew = 1; + goto retry; + } + } + } + + if (ff_framequeue_queued_frames(&s->fifo)) { + AVFrame *current_frame = ff_framequeue_peek(&s->fifo, 0); + + if (current_frame && current_frame->subtitle_timing.start_pts <= next_pts + interval) { + if (!s->current_frame_isnew) + current_frame->repeat_sub++; + + out = av_frame_clone(current_frame); + + if (!out) + return AVERROR(ENOMEM); + + if (!s->current_frame_isnew) { + out->pts = next_pts; + } else { + out->pts = out->subtitle_timing.start_pts; + + if (out->pts < next_pts) + out->pts = next_pts; + + s->next_pts_offset = (out->pts - next_pts) % interval; + } + + if (s->mode == FM_SCATTER) { + const int64_t sub_end_time = current_frame->subtitle_timing.start_pts + current_frame->subtitle_timing.duration; + + if (s->current_frame_isnew == 1 && current_frame->subtitle_timing.start_pts < out->pts) { + const int64_t diff = out->pts - current_frame->subtitle_timing.start_pts; + current_frame->subtitle_timing.duration -= diff; + } + + out->repeat_sub = 0; + out->subtitle_timing.start_pts = out->pts; + out->subtitle_timing.duration = interval; + av_assert1(out->pts >= next_pts); + av_assert1(out->pts < next_pts + interval); + av_assert1(out->pts < sub_end_time); + + if (out->pts > next_pts) + out->subtitle_timing.duration -= out->pts - next_pts; + + if (sub_end_time < next_pts + interval) { + const int64_t diff = next_pts + interval - sub_end_time; + av_assert1(diff <= out->subtitle_timing.duration); + out->subtitle_timing.duration -= diff; + } + } + + s->current_frame_isnew = 0; + s->recent_subtitle_pts = out->subtitle_timing.start_pts; + + return ff_filter_frame(outlink, out); + } + } + + if (ff_framequeue_queued_frames(&s->fifo) == 0) { + status = ff_request_frame(inlink); + if (status == AVERROR_EOF) { + s->eof = 1; + return status; + } + + if (s->counter > 1 && s->counter % 2) + return 0; + } + + out = ff_get_subtitles_buffer(outlink, outlink->format); + out->pts = next_pts; + out->repeat_sub = 1; + out->subtitle_timing.start_pts = s->recent_subtitle_pts; + + return ff_filter_frame(outlink, out); +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFilterContext *ctx = inlink->dst; + SubFeedContext *s = inlink->dst->priv; + AVFilterLink *outlink = inlink->dst->outputs[0]; + const int64_t index = (int64_t)ff_framequeue_queued_frames(&s->fifo) - 1; + int ret = 0; + + frame->pts = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q); + + if (index < 0) { + s->current_frame_isnew = 1; + } else if (s->fix_durations || s->fix_overlap) { + AVFrame *previous_frame = ff_framequeue_peek(&s->fifo, index); + const int64_t pts_diff = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts; + + if (s->fix_durations && pts_diff > 0 && previous_frame->subtitle_timing.duration > ms_to_avtb(29000)) + previous_frame->subtitle_timing.duration = frame->subtitle_timing.start_pts - previous_frame->subtitle_timing.start_pts; + + if (s->fix_overlap && pts_diff > 0 && previous_frame->subtitle_timing.duration > pts_diff) + previous_frame->subtitle_timing.duration = pts_diff; + } + + ff_framequeue_add(&s->fifo, frame); + + if (index > 3) + av_log(ctx, AV_LOG_WARNING, "frame queue count: %d\n", (int)index); + + if (s->mode == FM_FORWARD && ff_framequeue_queued_frames(&s->fifo)) { + + AVFrame *first_frame = ff_framequeue_peek(&s->fifo, 0); + + if (s->fix_overlap && ff_framequeue_queued_frames(&s->fifo) < 2) + return 0; + + if (s->fix_durations && first_frame->subtitle_timing.duration > ms_to_avtb(29000)) + return 0; + + first_frame = ff_framequeue_take(&s->fifo); + return ff_filter_frame(outlink, first_frame); + } + + return ret; +} + +#define OFFSET(x) offsetof(SubFeedContext, x) +#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM) + +static const AVOption subfeed_options[] = { + { "fix_durations", "delay output and determine duration from next frame", OFFSET(fix_durations), AV_OPT_TYPE_BOOL, { .i64=1 }, 0, 1, FLAGS, NULL }, + { "fix_overlap", "delay output and adjust durations to prevent overlap", OFFSET(fix_overlap), AV_OPT_TYPE_BOOL, { .i64=0 }, 0, 1, FLAGS, NULL }, + { "mode", "set feed mode", OFFSET(mode), AV_OPT_TYPE_INT, { .i64=FM_REPEAT }, FM_REPEAT, FM_FORWARD, FLAGS, "mode" }, + { "repeat", "repeat recent while valid, send empty otherwise", 0, AV_OPT_TYPE_CONST, { .i64=FM_REPEAT }, 0, 0, FLAGS, "mode" }, + { "scatter", "subdivide subtitles into 1/framerate segments", 0, AV_OPT_TYPE_CONST, { .i64=FM_SCATTER }, 0, 0, FLAGS, "mode" }, + { "forward", "forward only (clears output framerate)", 0, AV_OPT_TYPE_CONST, { .i64=FM_FORWARD }, 0, 0, FLAGS, "mode" }, + { "rate", "output frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, { .str = "5"}, 0, INT_MAX, FLAGS, NULL },\ + { "r", "output frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, { .str = "5"}, 0, INT_MAX, FLAGS, NULL },\ + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(subfeed); + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .filter_frame = filter_frame, + .config_props = config_input, + }, +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .request_frame = request_frame, + .config_props = config_output, + }, +}; + +const AVFilter ff_sf_subfeed = { + .name = "subfeed", + .description = NULL_IF_CONFIG_SMALL("Scatter subtitle events by duration"), + .init = init, + .uninit = uninit, + .priv_size = sizeof(SubFeedContext), + .priv_class = &subfeed_class, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_QUERY_FUNC(query_formats), +}; From patchwork Fri Jan 14 01:13:30 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33584 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp987400iog; Thu, 13 Jan 2022 17:17:40 -0800 (PST) X-Google-Smtp-Source: ABdhPJzS/BRlqWj77v60S/ESq22FOlCPrG0dS9HkAY5zUNSjQW7yzxfkoE7TYw9jWFco4F6CjTx2 X-Received: by 2002:a17:906:ad82:: with SMTP id la2mr3457330ejb.266.1642123060325; Thu, 13 Jan 2022 17:17:40 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642123060; cv=none; d=google.com; s=arc-20160816; b=gg5BN9vMq6sEsTgfuo2Tjw8/Z9HZfmXPNgVTAf8OzNZkVFcnWgnxWlDSnAim9Dw+sj 0gu95jfkC0ioK+Teve/n2s8kdvrzdbutCfiTp/b4bwrQcVmoxoK/I4/43UpHmCQyeO6V 2PFcRerDhGhbuT1Xf7eAmTYMLcz2ExZRd7phqlNob29ebXuMSkWUGicjyjmYa7MP61Rb DFPzCIzjlzVbWDZzTrxZcIw5sWMaOOCI8H7kssAoaoYVcZqtZDoVf0a9QB/WfOYtq1z6 2vRliaS7bQxb/HVEYhSXCEyPboL9V7QR0hKadiXOgTunkR2Phdevm0aqVypdzy3y0X39 QMZg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=1glZ5vOJSW0cSFToNvY/AbBVMyJQsrW0MYaCawXJL9c=; b=N9atxqZ8BIEupcQSy+5FPbBtshcu+NP8m2oEjBNIZCrMl219i6RGEYbkH3qjVBfRWK 5UWKL0PTtHm8mG3cs6WO9aQTNL6QumY1iBbWk9jU6i68JuMcmVEJR8eu/Pv4yvp966hh vG0MCniplW0QWJjgcLuQ86L/aK4UwRu9N3GlADWHBiun//RhoWtPVN/HB7d/qA8ScunC LBm4FxCCxJeuYLyxbdSeg/Hn+cdoSDndp1RvQvLW5Qpl0MOZogY9rAeXemcY+pVqph8I 854yYZpB8C2uiwaX8LOZMGbBIFmWcyWh1ycbAwRBkQpxJiOu1SoYbcoSddXTCbE0RT7A p8dQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=lI6vpwTe; 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 p6si2150905ejl.536.2022.01.13.17.17.39; Thu, 13 Jan 2022 17:17:40 -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 header.s=20210112 header.b=lI6vpwTe; 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 A5B8068B6C9; Fri, 14 Jan 2022 03:14:08 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pl1-f178.google.com (mail-pl1-f178.google.com [209.85.214.178]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 70ED268B665 for ; Fri, 14 Jan 2022 03:14:00 +0200 (EET) Received: by mail-pl1-f178.google.com with SMTP id h1so11894573pls.11 for ; Thu, 13 Jan 2022 17:14:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=eJQKjDPKeZxps0FjXw6U896263T+AN+GrsqyJPvb1ac=; b=lI6vpwTenASw0HnkjnuaEMANZP29ZyDf9OtoQ3VjL+1lRC8w3j0IVpXBSKwh1Pew1X KBugc7r/Wpx3rqnMpboVQOlg5xKrjWyVPVhCRfAcW3i+3nJr0lboDvfVqHStiSxgS11E WSNz97JuXjNQ983pIhhwd2Y9PZHy0TbYqYy24BFH37Mw0KnYvvGCnapR431iVw1W3kkQ OqcASMZ8gApIA7rdwblkRTblOeolZwaAn5UcWyuDWf7+CPOmNf93Yzd9J3NHdXKAU1vM aUXn2ozZzoZPDg0GquBsth5Y0ERHXRIR4816tp2Y5X9JtCjUWKkBMd3Jz3yg+s5wk7db wMZw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=eJQKjDPKeZxps0FjXw6U896263T+AN+GrsqyJPvb1ac=; b=EYKYd9batNFTGqxjjU6RvyHpORWGXf7xSi9ChqoH4g269fFNwMtOUWb1twzBpZOgTE qf3FCRsaAA7Bh3S08sYltuMNu5iFiI2DMkg9NSFW7bv7jKZnloNi6ZOvX0XrfiE37rlE GaHJoTLloSr4TcFTFUYnc5JcK43fEgM0VkvFpH1WfsRuAeYRJr/52AruWVN87P4Okc9p wYwzucfD++1s8aDc8kRBXfAaP9mE7hlD/u5IxyTfyKjANksekZRC3OPtonvkuHGJLWr9 kNuRXb2abWB0hDKWvSkrT/ChBxnXBM+H6nfVqeCleIRflLcqiTAc+QNU766FEBBWx9Ut +Khg== X-Gm-Message-State: AOAM530PRhOZKAa7myv94KMHQzZpN8X1mg81tT0s++d5sWFX7HUbaK5u F7VeyscVkLRQ5opD8BCn/wWUwfzbaNn4Sg== X-Received: by 2002:a17:902:6b03:b0:149:7dd8:e56d with SMTP id o3-20020a1709026b0300b001497dd8e56dmr7118837plk.29.1642122837745; Thu, 13 Jan 2022 17:13:57 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id s40sm4024241pfg.144.2022.01.13.17.13.56 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:57 -0800 (PST) Message-Id: <00643582cfc13d22e7967fef50e9b0078ee99657.1642122814.git.ffmpegagent@gmail.com> In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:30 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 21/24] fftools/ffmpeg: Introduce subtitle filtering new frame-based subtitle encoding 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 4wSuidj6bY87 From: softworkz This commit actually enables subtitle filtering in ffmpeg by sending and receiving subtitle frames to and from a filtergraph. The heartbeat functionality from the previous sub2video implementation is removed and now provided by the 'subfeed' filter. The other part of sub2video functionality is retained by auto-insertion of the new graphicsub2video filter. Justification for changed test refs: - sub2video The new results are identical excepting the last frame which is due to the implementation changes - sub2video_basic The previous results had some incorrect output because multiple frames had the same dts The non-empty content frames are visually identical, the different CRC is due to the different blending algorithm that is being used. - sub2video_time_limited The third frame in the previous ref was a repetition, which doesn't happen anymore with the new subtitle filtering. - sub-dvb Running ffprobe -show_frames on the source file shows that there are 7 subtitle frames with 0 rects in the source at the start and 2 at the end. This translates to the 14 and 4 additional entries in the new test results. - filter-overlay-dvdsub-2397 Overlay results have slightly different CRCs due to different blending implementation Signed-off-by: softworkz --- fftools/ffmpeg.c | 493 ++++---- fftools/ffmpeg.h | 13 +- fftools/ffmpeg_filter.c | 235 ++-- fftools/ffmpeg_hw.c | 2 +- fftools/ffmpeg_opt.c | 3 +- tests/ref/fate/filter-overlay-dvdsub-2397 | 182 +-- tests/ref/fate/sub-dvb | 162 +-- tests/ref/fate/sub2video | 1091 +++++++++++++++++- tests/ref/fate/sub2video_basic | 1239 +++++++++++++++++++-- tests/ref/fate/sub2video_time_limited | 78 +- 10 files changed, 2829 insertions(+), 669 deletions(-) diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index 5d134b025f..d088a3cd2b 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -143,8 +143,6 @@ static int want_sdp = 1; static BenchmarkTimeStamps current_time; AVIOContext *progress_avio = NULL; -static uint8_t *subtitle_out; - InputStream **input_streams = NULL; int nb_input_streams = 0; InputFile **input_files = NULL; @@ -169,163 +167,6 @@ static int restore_tty; static void free_input_threads(void); #endif -/* sub2video hack: - Convert subtitles to video with alpha to insert them in filter graphs. - This is a temporary solution until libavfilter gets real subtitles support. - */ - -static int sub2video_get_blank_frame(InputStream *ist) -{ - int ret; - AVFrame *frame = ist->sub2video.frame; - - av_frame_unref(frame); - ist->sub2video.frame->width = ist->dec_ctx->width ? ist->dec_ctx->width : ist->sub2video.w; - ist->sub2video.frame->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h; - ist->sub2video.frame->format = AV_PIX_FMT_RGB32; - if ((ret = av_frame_get_buffer(frame, 0)) < 0) - return ret; - memset(frame->data[0], 0, frame->height * frame->linesize[0]); - return 0; -} - -static void sub2video_copy_rect(uint8_t *dst, int dst_linesize, int w, int h, - AVSubtitleRect *r) -{ - uint32_t *pal, *dst2; - uint8_t *src, *src2; - int x, y; - - if (r->type != SUBTITLE_BITMAP) { - av_log(NULL, AV_LOG_WARNING, "sub2video: non-bitmap subtitle\n"); - return; - } - if (r->x < 0 || r->x + r->w > w || r->y < 0 || r->y + r->h > h) { - av_log(NULL, AV_LOG_WARNING, "sub2video: rectangle (%d %d %d %d) overflowing %d %d\n", - r->x, r->y, r->w, r->h, w, h - ); - return; - } - - dst += r->y * dst_linesize + r->x * 4; - src = r->data[0]; - pal = (uint32_t *)r->data[1]; - for (y = 0; y < r->h; y++) { - dst2 = (uint32_t *)dst; - src2 = src; - for (x = 0; x < r->w; x++) - *(dst2++) = pal[*(src2++)]; - dst += dst_linesize; - src += r->linesize[0]; - } -} - -static void sub2video_push_ref(InputStream *ist, int64_t pts) -{ - AVFrame *frame = ist->sub2video.frame; - int i; - int ret; - - av_assert1(frame->data[0]); - ist->sub2video.last_pts = frame->pts = pts; - for (i = 0; i < ist->nb_filters; i++) { - ret = av_buffersrc_add_frame_flags(ist->filters[i]->filter, frame, - AV_BUFFERSRC_FLAG_KEEP_REF | - AV_BUFFERSRC_FLAG_PUSH); - if (ret != AVERROR_EOF && ret < 0) - av_log(NULL, AV_LOG_WARNING, "Error while add the frame to buffer source(%s).\n", - av_err2str(ret)); - } -} - -void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub) -{ - AVFrame *frame = ist->sub2video.frame; - int8_t *dst; - int dst_linesize; - int num_rects, i; - int64_t pts, end_pts; - - if (!frame) - return; - if (sub) { - pts = av_rescale_q(sub->pts + sub->start_display_time * 1000LL, - AV_TIME_BASE_Q, ist->st->time_base); - end_pts = av_rescale_q(sub->pts + sub->end_display_time * 1000LL, - AV_TIME_BASE_Q, ist->st->time_base); - num_rects = sub->num_rects; - } else { - /* If we are initializing the system, utilize current heartbeat - PTS as the start time, and show until the following subpicture - is received. Otherwise, utilize the previous subpicture's end time - as the fall-back value. */ - pts = ist->sub2video.initialize ? - heartbeat_pts : ist->sub2video.end_pts; - end_pts = INT64_MAX; - num_rects = 0; - } - if (sub2video_get_blank_frame(ist) < 0) { - av_log(ist->dec_ctx, AV_LOG_ERROR, - "Impossible to get a blank canvas.\n"); - return; - } - dst = frame->data [0]; - dst_linesize = frame->linesize[0]; - for (i = 0; i < num_rects; i++) - sub2video_copy_rect(dst, dst_linesize, frame->width, frame->height, sub->rects[i]); - sub2video_push_ref(ist, pts); - ist->sub2video.end_pts = end_pts; - ist->sub2video.initialize = 0; -} - -static void sub2video_heartbeat(InputStream *ist, int64_t pts) -{ - InputFile *infile = input_files[ist->file_index]; - int i, j, nb_reqs; - int64_t pts2; - - /* When a frame is read from a file, examine all sub2video streams in - the same file and send the sub2video frame again. Otherwise, decoded - video frames could be accumulating in the filter graph while a filter - (possibly overlay) is desperately waiting for a subtitle frame. */ - for (i = 0; i < infile->nb_streams; i++) { - InputStream *ist2 = input_streams[infile->ist_index + i]; - if (!ist2->sub2video.frame) - continue; - /* subtitles seem to be usually muxed ahead of other streams; - if not, subtracting a larger time here is necessary */ - pts2 = av_rescale_q(pts, ist->st->time_base, ist2->st->time_base) - 1; - /* do not send the heartbeat frame if the subtitle is already ahead */ - if (pts2 <= ist2->sub2video.last_pts) - continue; - if (pts2 >= ist2->sub2video.end_pts || ist2->sub2video.initialize) - /* if we have hit the end of the current displayed subpicture, - or if we need to initialize the system, update the - overlayed subpicture and its start/end times */ - sub2video_update(ist2, pts2 + 1, NULL); - for (j = 0, nb_reqs = 0; j < ist2->nb_filters; j++) - nb_reqs += av_buffersrc_get_nb_failed_requests(ist2->filters[j]->filter); - if (nb_reqs) - sub2video_push_ref(ist2, pts2); - } -} - -static void sub2video_flush(InputStream *ist) -{ - int i; - int ret; - - if (ist->sub2video.end_pts < INT64_MAX) - sub2video_update(ist, INT64_MAX, NULL); - for (i = 0; i < ist->nb_filters; i++) { - ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL); - if (ret != AVERROR_EOF && ret < 0) - av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n"); - } -} - -/* end of sub2video hack */ - static void term_exit_sigsafe(void) { #if HAVE_TERMIOS_H @@ -526,7 +367,6 @@ static void ffmpeg_cleanup(int ret) avfilter_graph_free(&fg->graph); for (j = 0; j < fg->nb_inputs; j++) { InputFilter *ifilter = fg->inputs[j]; - struct InputStream *ist = ifilter->ist; while (av_fifo_size(ifilter->frame_queue)) { AVFrame *frame; @@ -536,15 +376,6 @@ static void ffmpeg_cleanup(int ret) } av_fifo_freep(&ifilter->frame_queue); av_freep(&ifilter->displaymatrix); - if (ist->sub2video.sub_queue) { - while (av_fifo_size(ist->sub2video.sub_queue)) { - AVSubtitle sub; - av_fifo_generic_read(ist->sub2video.sub_queue, - &sub, sizeof(sub), NULL); - avsubtitle_free(&sub); - } - av_fifo_freep(&ist->sub2video.sub_queue); - } av_buffer_unref(&ifilter->hw_frames_ctx); av_freep(&ifilter->name); av_freep(&fg->inputs[j]); @@ -564,7 +395,7 @@ static void ffmpeg_cleanup(int ret) } av_freep(&filtergraphs); - av_freep(&subtitle_out); + ////av_freep(&subtitle_out); /* close files */ for (i = 0; i < nb_output_files; i++) { @@ -632,12 +463,12 @@ static void ffmpeg_cleanup(int ret) av_frame_free(&ist->decoded_frame); av_packet_free(&ist->pkt); av_dict_free(&ist->decoder_opts); - avsubtitle_free(&ist->prev_sub.subtitle); - av_frame_free(&ist->sub2video.frame); + av_frame_free(&ist->prev_sub.subtitle); av_freep(&ist->filters); av_freep(&ist->hwaccel_device); av_freep(&ist->dts_buffer); + av_buffer_unref(&ist->subtitle_header); avcodec_free_context(&ist->dec_ctx); av_freep(&input_streams[i]); @@ -1055,33 +886,76 @@ error: exit_program(1); } -static void do_subtitle_out(OutputFile *of, - OutputStream *ost, - AVSubtitle *sub) +static void encode_subtitle_frame(OutputFile *of, OutputStream *ost, AVFrame *frame, AVPacket *pkt, int64_t pts_offset) { - int subtitle_out_max_size = 1024 * 1024; - int subtitle_out_size, nb, i; + AVCodecContext *enc = ost->enc_ctx; + int ret; + + ost->frames_encoded++; + + ret = avcodec_send_frame(enc, frame); + if (ret < 0) + goto error; + + while (1) { + ret = avcodec_receive_packet(enc, pkt); + update_benchmark("encode_subtitles %d.%d", ost->file_index, ost->index); + if (ret == AVERROR(EAGAIN)) + break; + if (ret < 0) + goto error; + + if (debug_ts) { + av_log(NULL, AV_LOG_INFO, "encoder -> type:subtitles " + "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s\n", + av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &enc->time_base), + av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &enc->time_base)); + } + + pkt->pts = av_rescale_q(frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, ost->mux_timebase); + pkt->duration = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, ost->mux_timebase); + pkt->pts += pts_offset; + + pkt->dts = pkt->pts; + output_packet(of, pkt, ost, 0); + } + ost->sync_opts++; + ost->frame_number++; + + return; +error: + av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed - Error code: %d\n", ret); + exit_program(1); +} + +static void do_subtitle_out(OutputFile *of, OutputStream *ost, AVFrame *frame) +{ + int nb, i; AVCodecContext *enc; AVPacket *pkt = ost->pkt; int64_t pts; - if (sub->pts == AV_NOPTS_VALUE) { + if (!frame) + return; + + av_log(NULL, AV_LOG_DEBUG, "do_subtitle_out: sub->pts: %"PRId64" frame->pts: %"PRId64"\n", frame->subtitle_timing.start_pts, frame->pts); + + if (frame->subtitle_timing.start_pts == AV_NOPTS_VALUE) { av_log(NULL, AV_LOG_ERROR, "Subtitle packets must have a pts\n"); if (exit_on_error) exit_program(1); return; } - enc = ost->enc_ctx; - - if (!subtitle_out) { - subtitle_out = av_malloc(subtitle_out_max_size); - if (!subtitle_out) { - av_log(NULL, AV_LOG_FATAL, "Failed to allocate subtitle_out\n"); - exit_program(1); - } + if (frame->repeat_sub) { + av_log(NULL, AV_LOG_WARNING, "Ignoring repeated subtitle frame\n"); + return; } + init_output_stream_wrapper(ost, frame, 1); + + enc = ost->enc_ctx; + /* Note: DVB subtitle need one packet to draw them and one other packet to clear them */ /* XXX: signal it in the codec context ? */ @@ -1091,50 +965,38 @@ static void do_subtitle_out(OutputFile *of, nb = 1; /* shift timestamp to honor -ss and make check_recording_time() work with -t */ - pts = sub->pts; + pts = frame->subtitle_timing.start_pts; if (output_files[ost->file_index]->start_time != AV_NOPTS_VALUE) pts -= output_files[ost->file_index]->start_time; - for (i = 0; i < nb; i++) { - unsigned save_num_rects = sub->num_rects; - ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base); - if (!check_recording_time(ost)) - return; + ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base); + if (!check_recording_time(ost)) + return; - sub->pts = pts; - // start_display_time is required to be 0 - sub->pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q); - sub->end_display_time -= sub->start_display_time; - sub->start_display_time = 0; - if (i == 1) - sub->num_rects = 0; + frame->subtitle_timing.start_pts = pts; + + for (i = 0; i < nb; i++) { + const unsigned save_num_rects = frame->num_subtitle_areas; + int64_t pts_offset = 0; ost->frames_encoded++; - subtitle_out_size = avcodec_encode_subtitle(enc, subtitle_out, - subtitle_out_max_size, sub); if (i == 1) - sub->num_rects = save_num_rects; - if (subtitle_out_size < 0) { - av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed\n"); - exit_program(1); - } + frame->num_subtitle_areas = 0; - av_packet_unref(pkt); - pkt->data = subtitle_out; - pkt->size = subtitle_out_size; - pkt->pts = av_rescale_q(sub->pts, AV_TIME_BASE_Q, ost->mux_timebase); - pkt->duration = av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase); if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) { /* XXX: the pts correction is handled here. Maybe handling it in the codec would be better */ if (i == 0) - pkt->pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase); + pts_offset = 0; else - pkt->pts += av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase); + pts_offset = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, ost->mux_timebase); } - pkt->dts = pkt->pts; - output_packet(of, pkt, ost, 0); + + encode_subtitle_frame(of, ost, frame, pkt, pts_offset); + + if (i == 1) + frame->num_subtitle_areas = save_num_rects; } } @@ -1544,8 +1406,26 @@ static int reap_filters(int flush) } do_audio_out(of, ost, filtered_frame); break; + case AVMEDIA_TYPE_SUBTITLE: + + if (filtered_frame->format == AV_SUBTITLE_FMT_ASS && !enc->subtitle_header + && filtered_frame->subtitle_header) { + const char *subtitle_header = (char *)filtered_frame->subtitle_header->data; + enc->subtitle_header = (uint8_t *)av_strdup(subtitle_header); + if (!enc->subtitle_header) + return AVERROR(ENOMEM); + enc->subtitle_header_size = strlen(subtitle_header); + } + + if ((ost->enc_ctx->width == 0 || ost->enc_ctx->height == 0) + && filter->inputs[0]->w > 0 && filter->inputs[0]->h > 0 ) { + ost->enc_ctx->width = filter->inputs[0]->w; + ost->enc_ctx->height = filter->inputs[0]->h; + } + + do_subtitle_out(of, ost, filtered_frame); + break; default: - // TODO support subtitle filters av_assert0(0); } @@ -2138,7 +2018,8 @@ static int ifilter_has_all_input_formats(FilterGraph *fg) int i; for (i = 0; i < fg->nb_inputs; i++) { if (fg->inputs[i]->format < 0 && (fg->inputs[i]->type == AVMEDIA_TYPE_AUDIO || - fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO)) + fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO || + fg->inputs[i]->type == AVMEDIA_TYPE_SUBTITLE)) return 0; } return 1; @@ -2164,8 +2045,12 @@ static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame, int keep_ref ifilter->channel_layout != frame->channel_layout; break; case AVMEDIA_TYPE_VIDEO: + case AVMEDIA_TYPE_SUBTITLE: need_reinit |= ifilter->width != frame->width || ifilter->height != frame->height; + + if (need_reinit) + need_reinit = 1; break; } @@ -2243,7 +2128,7 @@ static int ifilter_send_eof(InputFilter *ifilter, int64_t pts) // the filtergraph was never configured if (ifilter->format < 0) ifilter_parameters_from_codecpar(ifilter, ifilter->ist->st->codecpar); - if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO)) { + if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO || ifilter->type == AVMEDIA_TYPE_SUBTITLE)) { av_log(NULL, AV_LOG_ERROR, "Cannot determine format of input stream %d:%d after EOF\n", ifilter->ist->file_index, ifilter->ist->st->index); return AVERROR_INVALIDDATA; } @@ -2281,7 +2166,7 @@ static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacke static int send_frame_to_filters(InputStream *ist, AVFrame *decoded_frame) { - int i, ret; + int i, ret = 0; av_assert1(ist->nb_filters > 0); /* ensure ret is initialized */ for (i = 0; i < ist->nb_filters; i++) { @@ -2482,81 +2367,114 @@ fail: return err < 0 ? err : ret; } -static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output, +static InputStream *get_input_stream(OutputStream *ost) +{ + if (ost->source_index >= 0) + return input_streams[ost->source_index]; + return NULL; +} + +static int decode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output, int *decode_failed) { - AVSubtitle subtitle; - int free_sub = 1; - int i, ret = avcodec_decode_subtitle2(ist->dec_ctx, - &subtitle, got_output, pkt); + AVFrame *decoded_frame; + AVCodecContext *avctx = ist->dec_ctx; + int i = 0, ret = 0, err = 0; - check_decode_result(NULL, got_output, ret); + if (!ist->decoded_frame && !(ist->decoded_frame = av_frame_alloc())) + return AVERROR(ENOMEM); + decoded_frame = ist->decoded_frame; + decoded_frame->type = AVMEDIA_TYPE_SUBTITLE; + decoded_frame->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor); + + if (!ist->subtitle_header && avctx->subtitle_header && avctx->subtitle_header_size > 0) { + ist->subtitle_header = av_buffer_allocz(avctx->subtitle_header_size + 1); + if (!ist->subtitle_header) + return AVERROR(ENOMEM); + memcpy(ist->subtitle_header->data, avctx->subtitle_header, avctx->subtitle_header_size); + } + + ret = decode(avctx, decoded_frame, got_output, pkt); + + if (ret != AVERROR_EOF) + check_decode_result(NULL, got_output, ret); if (ret < 0 || !*got_output) { *decode_failed = 1; - if (!pkt->size) - sub2video_flush(ist); + if (!pkt->size) { + // Flush + for (i = 0; i < ist->nb_filters; i++) { + ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL); + if (ret != AVERROR_EOF && ret < 0) + av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n"); + } + } return ret; } if (ist->fix_sub_duration) { - int end = 1; - if (ist->prev_sub.got_output) { - end = av_rescale(subtitle.pts - ist->prev_sub.subtitle.pts, - 1000, AV_TIME_BASE); - if (end < ist->prev_sub.subtitle.end_display_time) { - av_log(ist->dec_ctx, AV_LOG_DEBUG, - "Subtitle duration reduced from %"PRId32" to %d%s\n", - ist->prev_sub.subtitle.end_display_time, end, - end <= 0 ? ", dropping it" : ""); - ist->prev_sub.subtitle.end_display_time = end; + int64_t end = 1; + if (ist->prev_sub.got_output && ist->prev_sub.subtitle) { + + const int64_t duration = ist->prev_sub.subtitle->subtitle_timing.duration; + end = decoded_frame->subtitle_timing.start_pts - ist->prev_sub.subtitle->subtitle_timing.start_pts; + + if (end < duration) { + av_log(avctx, AV_LOG_DEBUG, "Subtitle duration reduced from %"PRId64" to %"PRId64"%s\n", + duration, end, end <= 0 ? ", dropping it" : ""); + ist->prev_sub.subtitle->subtitle_timing.duration = end; } } - FFSWAP(int, *got_output, ist->prev_sub.got_output); - FFSWAP(int, ret, ist->prev_sub.ret); - FFSWAP(AVSubtitle, subtitle, ist->prev_sub.subtitle); + FFSWAP(int, *got_output, ist->prev_sub.got_output); + FFSWAP(int, ret, ist->prev_sub.ret); + FFSWAP(AVFrame*, decoded_frame, ist->prev_sub.subtitle); if (end <= 0) - goto out; + return (int)end; } - if (!*got_output) + if (!*got_output || !decoded_frame) return ret; - if (ist->sub2video.frame) { - sub2video_update(ist, INT64_MIN, &subtitle); - } else if (ist->nb_filters) { - if (!ist->sub2video.sub_queue) - ist->sub2video.sub_queue = av_fifo_alloc(8 * sizeof(AVSubtitle)); - if (!ist->sub2video.sub_queue) - exit_program(1); - if (!av_fifo_space(ist->sub2video.sub_queue)) { - ret = av_fifo_realloc2(ist->sub2video.sub_queue, 2 * av_fifo_size(ist->sub2video.sub_queue)); - if (ret < 0) - exit_program(1); + decoded_frame->type = AVMEDIA_TYPE_SUBTITLE; + decoded_frame->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor); + + if ((ret = av_buffer_replace(&decoded_frame->subtitle_header, ist->subtitle_header)) < 0) + return ret; + + decoded_frame->pts = av_rescale_q(decoded_frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, ist->st->time_base); + + if (ist->nb_filters > 0) { + AVFrame *filter_frame = av_frame_clone(decoded_frame); + if (!filter_frame) { + err = AVERROR(ENOMEM); + goto end; } - av_fifo_generic_write(ist->sub2video.sub_queue, &subtitle, sizeof(subtitle), NULL); - free_sub = 0; - } - if (!subtitle.num_rects) - goto out; + err = send_frame_to_filters(ist, filter_frame); + av_frame_free(&filter_frame); + } - ist->frames_decoded++; + if (err >= 0) { + for (i = 0; i < nb_output_streams; i++) { + OutputStream *ost = output_streams[i]; + InputStream *ist_src = get_input_stream(ost); - for (i = 0; i < nb_output_streams; i++) { - OutputStream *ost = output_streams[i]; + if (!ist_src || !check_output_constraints(ist, ost) + || ist_src != ist + || !ost->encoding_needed + || ost->enc->type != AVMEDIA_TYPE_SUBTITLE) + continue; - if (!check_output_constraints(ist, ost) || !ost->encoding_needed - || ost->enc->type != AVMEDIA_TYPE_SUBTITLE) - continue; + if (ost->filter && ost->filter->filter->nb_inputs > 0) + continue; - do_subtitle_out(output_files[ost->file_index], ost, &subtitle); + do_subtitle_out(output_files[ost->file_index], ost, decoded_frame); + } } -out: - if (free_sub) - avsubtitle_free(&subtitle); - return ret; +end: + av_frame_unref(decoded_frame); + return err < 0 ? err : ret; } static int send_filter_eof(InputStream *ist) @@ -2660,7 +2578,7 @@ static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eo case AVMEDIA_TYPE_SUBTITLE: if (repeating) break; - ret = transcode_subtitles(ist, avpkt, &got_output, &decode_failed); + ret = decode_subtitles(ist, avpkt, &got_output, &decode_failed); if (!pkt && ret >= 0) ret = AVERROR_EOF; av_packet_unref(avpkt); @@ -2928,13 +2846,6 @@ FF_ENABLE_DEPRECATION_WARNINGS return 0; } -static InputStream *get_input_stream(OutputStream *ost) -{ - if (ost->source_index >= 0) - return input_streams[ost->source_index]; - return NULL; -} - static int compare_int64(const void *a, const void *b) { return FFDIFFSIGN(*(const int64_t *)a, *(const int64_t *)b); @@ -3411,7 +3322,7 @@ static int init_output_stream_encode(OutputStream *ost, AVFrame *frame) break; case AVMEDIA_TYPE_SUBTITLE: enc_ctx->time_base = AV_TIME_BASE_Q; - if (!enc_ctx->width) { + if (!enc_ctx->width && ost->source_index >= 0) { enc_ctx->width = input_streams[ost->source_index]->st->codecpar->width; enc_ctx->height = input_streams[ost->source_index]->st->codecpar->height; } @@ -3464,19 +3375,14 @@ static int init_output_stream(OutputStream *ost, AVFrame *frame, } if (ist && ist->dec->type == AVMEDIA_TYPE_SUBTITLE && ost->enc->type == AVMEDIA_TYPE_SUBTITLE) { - int input_props = 0, output_props = 0; - AVCodecDescriptor const *input_descriptor = - avcodec_descriptor_get(dec->codec_id); - AVCodecDescriptor const *output_descriptor = - avcodec_descriptor_get(ost->enc_ctx->codec_id); - if (input_descriptor) - input_props = input_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB); - if (output_descriptor) - output_props = output_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB); - if (input_props && output_props && input_props != output_props) { - snprintf(error, error_len, - "Subtitle encoding currently only possible from text to text " - "or bitmap to bitmap"); + AVCodecDescriptor const *input_descriptor = avcodec_descriptor_get(dec->codec_id); + AVCodecDescriptor const *output_descriptor = avcodec_descriptor_get(ost->enc_ctx->codec_id); + const enum AVSubtitleType in_subtitle_format = output_descriptor ? avcodec_descriptor_get_subtitle_format(input_descriptor) : AV_SUBTITLE_FMT_UNKNOWN; + const enum AVSubtitleType out_subtitle_format = output_descriptor ? avcodec_descriptor_get_subtitle_format(output_descriptor) : AV_SUBTITLE_FMT_UNKNOWN; + + if (ist->nb_filters == 0 && in_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN && out_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN + && in_subtitle_format != out_subtitle_format) { + snprintf(error, error_len, "Subtitle encoding is only possible from text to text or bitmap to bitmap"); return AVERROR_INVALIDDATA; } } @@ -3640,7 +3546,8 @@ static int transcode_init(void) for (i = 0; i < nb_output_streams; i++) { if (!output_streams[i]->stream_copy && (output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO || - output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO)) + output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO || + output_streams[i]->enc_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE)) continue; ret = init_output_stream_wrapper(output_streams[i], NULL, 0); @@ -4477,8 +4384,6 @@ static int process_input(int file_index) av_ts2timestr(input_files[ist->file_index]->ts_offset, &AV_TIME_BASE_Q)); } - sub2video_heartbeat(ist, pkt->pts); - process_input_packet(ist, pkt, 0); discard_packet: diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 9b200b806a..5ebd01c14e 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -349,17 +349,10 @@ typedef struct InputStream { struct { /* previous decoded subtitle and related variables */ int got_output; int ret; - AVSubtitle subtitle; + AVFrame *subtitle; } prev_sub; - struct sub2video { - int64_t last_pts; - int64_t end_pts; - AVFifoBuffer *sub_queue; ///< queue of AVSubtitle* before filter init - AVFrame *frame; - int w, h; - unsigned int initialize; ///< marks if sub2video_update should force an initialization - } sub2video; + AVBufferRef *subtitle_header; /* decoded data from this stream goes into all those filters * currently video and audio only */ @@ -659,8 +652,6 @@ int filtergraph_is_simple(FilterGraph *fg); int init_simple_filtergraph(InputStream *ist, OutputStream *ost); int init_complex_filtergraph(FilterGraph *fg); -void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub); - int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame); int ffmpeg_parse_options(int argc, char **argv); diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index 1f6cba2c04..17ce79cfaa 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -22,6 +22,8 @@ #include "ffmpeg.h" +#include "libavutil/ass_split_internal.h" + #include "libavfilter/avfilter.h" #include "libavfilter/buffersink.h" #include "libavfilter/buffersrc.h" @@ -30,11 +32,9 @@ #include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/channel_layout.h" -#include "libavutil/display.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/pixfmt.h" -#include "libavutil/imgutils.h" #include "libavutil/samplefmt.h" // FIXME: YUV420P etc. are actually supported with full color range, @@ -215,9 +215,8 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) InputFilter *ifilter; int i; - // TODO: support other filter types - if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) { - av_log(NULL, AV_LOG_FATAL, "Only video and audio filters supported " + if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO && type != AVMEDIA_TYPE_SUBTITLE) { + av_log(NULL, AV_LOG_FATAL, "Only video, audio and subtitle filters supported " "currently.\n"); exit_program(1); } @@ -238,8 +237,9 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) for (i = 0; i < s->nb_streams; i++) { enum AVMediaType stream_type = s->streams[i]->codecpar->codec_type; if (stream_type != type && - !(stream_type == AVMEDIA_TYPE_SUBTITLE && - type == AVMEDIA_TYPE_VIDEO /* sub2video hack */)) + // in the followng case we auto-insert the graphicsub2video conversion filter + // for retaining compatibility with the previous sub2video hack + !(stream_type == AVMEDIA_TYPE_SUBTITLE && type == AVMEDIA_TYPE_VIDEO)) continue; if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : p) == 1) { st = s->streams[i]; @@ -286,6 +286,17 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) ifilter->type = ist->st->codecpar->codec_type; ifilter->name = describe_filter_link(fg, in, 1); + if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE && ist->dec_ctx) { + const AVCodecDescriptor *codec_descriptor = ist->dec_ctx->codec_descriptor; + if (!codec_descriptor) + codec_descriptor = avcodec_descriptor_get(ist->dec_ctx->codec_id); + + // For subtitles, we need to set the format here. Would we leave the format + // at -1, it would delay processing (ifilter_has_all_input_formats()) until + // the first subtile frame arrives, which could never happen in the worst case + fg->inputs[fg->nb_inputs - 1]->format = avcodec_descriptor_get_subtitle_format(codec_descriptor); + } + ifilter->frame_queue = av_fifo_alloc(8 * sizeof(AVFrame*)); if (!ifilter->frame_queue) exit_program(1); @@ -405,6 +416,39 @@ static int insert_filter(AVFilterContext **last_filter, int *pad_idx, return 0; } +static int configure_output_subtitle_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) +{ + OutputStream *ost = ofilter->ost; + AVFilterContext *last_filter = out->filter_ctx; + int pad_idx = out->pad_idx; + int ret; + char name[255]; + + snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index); + ret = avfilter_graph_create_filter(&ofilter->filter, + avfilter_get_by_name("sbuffersink"), + name, NULL, NULL, fg->graph); + + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "Unable to create filter sbuffersink\n"); + return ret; + } + + ////snprintf(name, sizeof(name), "trim_out_%d_%d", + //// ost->file_index, ost->index); + ////ret = insert_trim(of->start_time, of->recording_time, + //// &last_filter, &pad_idx, name); + ////if (ret < 0) + //// return ret; + + ////ost->st->codecpar->codec_tag = MKTAG('a', 's', 's', 's'); + + if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0) + return ret; + + return 0; +} + static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) { OutputStream *ost = ofilter->ost; @@ -585,7 +629,8 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, int i; for (i=0; ictx->nb_streams; i++) - if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) + if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || + of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) break; if (ictx->nb_streams) { @@ -619,6 +664,7 @@ static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) { case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, ofilter, out); case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, ofilter, out); + case AVMEDIA_TYPE_SUBTITLE: return configure_output_subtitle_filter(fg, ofilter, out); default: av_assert0(0); return 0; } } @@ -638,51 +684,126 @@ void check_filter_outputs(void) } } -static int sub2video_prepare(InputStream *ist, InputFilter *ifilter) +static int configure_input_subtitle_filter(FilterGraph *fg, InputFilter *ifilter, + AVFilterInOut *in) { - AVFormatContext *avf = input_files[ist->file_index]->ctx; - int i, w, h; + AVFilterContext *last_filter; + const AVFilter *buffer_filt = avfilter_get_by_name("sbuffer"); + InputStream *ist = ifilter->ist; + AVBPrint args; + char name[255]; + int ret, pad_idx = 0; + int w, h; + AVBufferSrcParameters *par = av_buffersrc_parameters_alloc(); + enum AVMediaType media_type; + + if (!par) + return AVERROR(ENOMEM); + + par->format = AV_PIX_FMT_NONE; + + if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { + av_log(NULL, AV_LOG_ERROR, "Cannot connect subtitle filter to audio input\n"); + ret = AVERROR(EINVAL); + goto fail; + } + + if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { + av_log(NULL, AV_LOG_ERROR, "Cannot connect subtitle filter to video input\n"); + ret = AVERROR(EINVAL); + goto fail; + } - /* Compute the size of the canvas for the subtitles stream. - If the subtitles codecpar has set a size, use it. Otherwise use the - maximum dimensions of the video streams in the same file. */ w = ifilter->width; h = ifilter->height; + if (!(w && h)) { - for (i = 0; i < avf->nb_streams; i++) { - if (avf->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - w = FFMAX(w, avf->streams[i]->codecpar->width); - h = FFMAX(h, avf->streams[i]->codecpar->height); - } - } - if (!(w && h)) { - w = FFMAX(w, 720); - h = FFMAX(h, 576); - } - av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h); + w = ist->dec_ctx->width; + h = ist->dec_ctx->height; } - ist->sub2video.w = ifilter->width = w; - ist->sub2video.h = ifilter->height = h; - ifilter->width = ist->dec_ctx->width ? ist->dec_ctx->width : ist->sub2video.w; - ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h; + if (!(w && h) && ist->dec_ctx->subtitle_header) { + ASSSplitContext *ass_ctx = avpriv_ass_split((char *)ist->dec_ctx->subtitle_header); + ASS *ass = (ASS *)ass_ctx; + w = ass->script_info.play_res_x; + h = ass->script_info.play_res_y; + avpriv_ass_split_free(ass_ctx); + } - /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the - palettes for all rectangles are identical or compatible */ - ifilter->format = AV_PIX_FMT_RGB32; + ifilter->width = w; + ifilter->height = h; + ist->dec_ctx->width = w; + ist->dec_ctx->height = h; - ist->sub2video.frame = av_frame_alloc(); - if (!ist->sub2video.frame) - return AVERROR(ENOMEM); - ist->sub2video.last_pts = INT64_MIN; - ist->sub2video.end_pts = INT64_MIN; + snprintf(name, sizeof(name), "graph %d subtitle input from stream %d:%d", fg->index, + ist->file_index, ist->st->index); + + + av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC); + av_bprintf(&args, + "subtitle_type=%d:width=%d:height=%d:time_base=%d/%d:", + ifilter->format, ifilter->width, ifilter->height, + ist->st->time_base.num, ist->st->time_base.den); + if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name, + args.str, NULL, fg->graph)) < 0) + goto fail; + + par->hw_frames_ctx = ifilter->hw_frames_ctx; + par->format = ifilter->format; + par->width = ifilter->width; + par->height = ifilter->height; + + ret = av_buffersrc_parameters_set(ifilter->filter, par); + if (ret < 0) + goto fail; + av_freep(&par); + last_filter = ifilter->filter; + + // This is for sub2video compatibility + media_type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx); + if (media_type == AVMEDIA_TYPE_VIDEO) { + int subscale_w = w, subscale_h = h; + + av_log(NULL, AV_LOG_INFO, "Auto-inserting subfeed filter\n"); + ret = insert_filter(&last_filter, &pad_idx, "subfeed", NULL); + if (ret < 0) + return ret; + + if (!(subscale_w && subscale_h)) { + // If the subtitle frame size is unknown, try to find a video input + // and use its size for adding a subscale filter + for (int i = 0; i < fg->nb_inputs; i++) { + InputFilter *input = fg->inputs[i]; + if (input->type == AVMEDIA_TYPE_VIDEO && input->width && input->height) { + subscale_w = input->width; + subscale_h = input->height; + break; + } + } + } + + if (subscale_w && subscale_h) { + char subscale_params[64]; + snprintf(subscale_params, sizeof(subscale_params), "w=%d:h=%d", subscale_w, subscale_h); + ret = insert_filter(&last_filter, &pad_idx, "subscale", subscale_params); + if (ret < 0) + return ret; + } - /* sub2video structure has been (re-)initialized. - Mark it as such so that the system will be - initialized with the first received heartbeat. */ - ist->sub2video.initialize = 1; + av_log(NULL, AV_LOG_INFO, "Auto-inserting graphicsub2video filter\n"); + ret = insert_filter(&last_filter, &pad_idx, "graphicsub2video", NULL); + if (ret < 0) + return ret; + } + + if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0) + return ret; return 0; +fail: + av_freep(&par); + + return ret; } static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, @@ -701,8 +822,15 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, char name[255]; int ret, pad_idx = 0; int64_t tsoffset = 0; - AVBufferSrcParameters *par = av_buffersrc_parameters_alloc(); + AVBufferSrcParameters *par; + if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { + // Automatically insert conversion filter to retain compatibility + // with sub2video command lines + return configure_input_subtitle_filter(fg, ifilter, in); + } + + par = av_buffersrc_parameters_alloc(); if (!par) return AVERROR(ENOMEM); memset(par, 0, sizeof(*par)); @@ -717,12 +845,6 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, if (!fr.num) fr = av_guess_frame_rate(input_files[ist->file_index]->ctx, ist->st, NULL); - if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { - ret = sub2video_prepare(ist, ifilter); - if (ret < 0) - goto fail; - } - sar = ifilter->sample_aspect_ratio; if(!sar.den) sar = (AVRational){0,1}; @@ -734,7 +856,7 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, tb.num, tb.den, sar.num, sar.den); if (fr.num && fr.den) av_bprintf(&args, ":frame_rate=%d/%d", fr.num, fr.den); - snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index, + snprintf(name, sizeof(name), "graph %d video input from stream %d:%d", fg->index, ist->file_index, ist->st->index); @@ -932,6 +1054,7 @@ static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter, switch (avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx)) { case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, ifilter, in); case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, ifilter, in); + case AVMEDIA_TYPE_SUBTITLE: return configure_input_subtitle_filter(fg, ifilter, in); default: av_assert0(0); return 0; } } @@ -1125,19 +1248,6 @@ int configure_filtergraph(FilterGraph *fg) } } - /* process queued up subtitle packets */ - for (i = 0; i < fg->nb_inputs; i++) { - InputStream *ist = fg->inputs[i]->ist; - if (ist->sub2video.sub_queue && ist->sub2video.frame) { - while (av_fifo_size(ist->sub2video.sub_queue)) { - AVSubtitle tmp; - av_fifo_generic_read(ist->sub2video.sub_queue, &tmp, sizeof(tmp), NULL); - sub2video_update(ist, INT64_MIN, &tmp); - avsubtitle_free(&tmp); - } - } - } - return 0; fail: @@ -1160,6 +1270,7 @@ int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame) ifilter->sample_rate = frame->sample_rate; ifilter->channels = frame->channels; ifilter->channel_layout = frame->channel_layout; + ifilter->type = frame->type; av_freep(&ifilter->displaymatrix); sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX); diff --git a/fftools/ffmpeg_hw.c b/fftools/ffmpeg_hw.c index 14e702bd92..be69d54aaf 100644 --- a/fftools/ffmpeg_hw.c +++ b/fftools/ffmpeg_hw.c @@ -449,7 +449,7 @@ int hw_device_setup_for_encode(OutputStream *ost) AVBufferRef *frames_ref = NULL; int i; - if (ost->filter) { + if (ost->filter && ost->filter->filter) { frames_ref = av_buffersink_get_hw_frames_ctx(ost->filter->filter); if (frames_ref && ((AVHWFramesContext*)frames_ref->data)->format == diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index 9c820ab73f..476ef628e4 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -2211,8 +2211,9 @@ static void init_output_filter(OutputFilter *ofilter, OptionsContext *o, switch (ofilter->type) { case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(o, oc, -1); break; case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream(o, oc, -1); break; + case AVMEDIA_TYPE_SUBTITLE: ost = new_subtitle_stream(o, oc, -1); break; default: - av_log(NULL, AV_LOG_FATAL, "Only video and audio filters are supported " + av_log(NULL, AV_LOG_FATAL, "Only video, audio and subtitle filters are supported " "currently.\n"); exit_program(1); } diff --git a/tests/ref/fate/filter-overlay-dvdsub-2397 b/tests/ref/fate/filter-overlay-dvdsub-2397 index 483e5fa4e0..42042b967c 100644 --- a/tests/ref/fate/filter-overlay-dvdsub-2397 +++ b/tests/ref/fate/filter-overlay-dvdsub-2397 @@ -490,368 +490,368 @@ 1, 3877, 3877, 10, 2013, 0x95a39f9c 1, 3887, 3887, 10, 2013, 0x4f7ea123 1, 3897, 3897, 10, 2013, 0x9efb9ba1 -0, 117, 117, 1, 518400, 0xbf8523da +0, 117, 117, 1, 518400, 0xc44e1d4c 1, 3907, 3907, 10, 2013, 0xf395b2cd 1, 3917, 3917, 10, 2013, 0x261a881e 1, 3927, 3927, 10, 2013, 0x7f2d9f72 1, 3937, 3937, 10, 2013, 0x0105b38d -0, 118, 118, 1, 518400, 0x41890ed6 +0, 118, 118, 1, 518400, 0xad1a084c 1, 3952, 3952, 10, 2013, 0x0e5db67e 1, 3962, 3962, 10, 2013, 0xfc9baf97 -0, 119, 119, 1, 518400, 0x588534fc +0, 119, 119, 1, 518400, 0x52d52e73 1, 3972, 3972, 10, 2013, 0x8e02a1b1 1, 3982, 3982, 10, 2013, 0x6eecaac8 1, 3992, 3992, 10, 2013, 0xf5558f0c 1, 4002, 4002, 10, 2013, 0x512ba99b -0, 120, 120, 1, 518400, 0x2145ebc1 +0, 120, 120, 1, 518400, 0xc732e546 1, 4012, 4012, 10, 2013, 0x932b9932 1, 4022, 4022, 10, 2013, 0xc01ea987 -0, 121, 121, 1, 518400, 0x28bca595 +0, 121, 121, 1, 518400, 0xc36f9f14 1, 4038, 4038, 10, 2013, 0x10879cf7 1, 4048, 4048, 10, 2013, 0x90679338 1, 4058, 4058, 10, 2013, 0x077d8a9e 1, 4068, 4068, 10, 2013, 0x969fa57c -0, 122, 122, 1, 518400, 0x77dc951e +0, 122, 122, 1, 518400, 0x78428e8b 1, 4078, 4078, 10, 2013, 0xe049ab07 1, 4088, 4088, 10, 2013, 0xf535b3b3 1, 4098, 4098, 10, 2013, 0xfe76bd37 -0, 123, 123, 1, 518400, 0xe8924c17 +0, 123, 123, 1, 518400, 0xf0f8458d 1, 4108, 4108, 10, 2013, 0xde79ad8c 1, 4123, 4123, 10, 2013, 0xe89b9c47 1, 4133, 4133, 10, 2013, 0xc570b0f0 -0, 124, 124, 1, 518400, 0xadb4cccc +0, 124, 124, 1, 518400, 0x7083c653 1, 4143, 4143, 10, 2013, 0xee709cd9 1, 4153, 4153, 10, 2013, 0xcfe5afab 1, 4163, 4163, 10, 2013, 0x98ff8ce4 -0, 125, 125, 1, 518400, 0x1d7b56ac +0, 125, 125, 1, 518400, 0xa105502c 1, 4173, 4173, 10, 2013, 0x9d19b44c 1, 4183, 4183, 10, 2013, 0x4349917a 1, 4193, 4193, 10, 2013, 0xbf54a59a -0, 126, 126, 1, 518400, 0xad5739a4 +0, 126, 126, 1, 518400, 0xd411331a 1, 4208, 4208, 10, 2013, 0xc4a399e0 1, 4218, 4218, 10, 2013, 0x1bf58ff0 1, 4228, 4228, 10, 2013, 0x3518ac56 -0, 127, 127, 1, 518400, 0x2733d35a +0, 127, 127, 1, 518400, 0x83b0ccdb 1, 4238, 4238, 10, 2013, 0xcd38c1de 1, 4248, 4248, 10, 2013, 0xbe7d9c4d 1, 4258, 4258, 10, 2013, 0xe113a306 1, 4268, 4268, 10, 2013, 0x083197ea -0, 128, 128, 1, 518400, 0x78e76da2 +0, 128, 128, 1, 518400, 0xa9be671a 1, 4278, 4278, 10, 2013, 0x1929b1eb 1, 4294, 4294, 10, 2013, 0x5d6ea5af 1, 4304, 4304, 10, 2013, 0x05519d53 -0, 129, 129, 1, 518400, 0x6c076013 +0, 129, 129, 1, 518400, 0xaeb75983 1, 4314, 4314, 10, 2013, 0x5773b380 1, 4324, 4324, 10, 2013, 0xaa70a8f5 1, 4334, 4334, 10, 2013, 0x990db0ec -0, 130, 130, 1, 518400, 0x7854f2b1 +0, 130, 130, 1, 518400, 0x81f8ec13 1, 4344, 4344, 10, 2013, 0x91d3a623 1, 4354, 4354, 10, 2013, 0xc91f9824 1, 4364, 4364, 10, 2013, 0x1d058abf -0, 131, 131, 1, 518400, 0xd2ae1ecd +0, 131, 131, 1, 518400, 0x8aaa1839 1, 4379, 4379, 10, 2013, 0x8de1b8d5 1, 4389, 4389, 10, 2013, 0x7872b06b 1, 4399, 4399, 10, 2013, 0xa084c203 -0, 132, 132, 1, 518400, 0xf5eab38d +0, 132, 132, 1, 518400, 0xc98bacf5 1, 4409, 4409, 10, 2013, 0xff90ae8d 1, 4419, 4419, 10, 2013, 0x61dead8e 1, 4429, 4429, 10, 2013, 0xee76b284 -0, 133, 133, 1, 518400, 0x994d3e9c +0, 133, 133, 1, 518400, 0x31083804 1, 4439, 4439, 10, 2013, 0xe888af7f 1, 4449, 4449, 10, 2013, 0x5d57b115 1, 4464, 4464, 10, 2013, 0xcdbfb1d0 -0, 134, 134, 1, 518400, 0x95ab705a +0, 134, 134, 1, 518400, 0x540a69dc 1, 4474, 4474, 10, 2013, 0x2e28a952 1, 4484, 4484, 10, 2013, 0x4795a994 1, 4494, 4494, 10, 2013, 0x7e7ea304 1, 4504, 4504, 10, 2013, 0x9502c1e1 -0, 135, 135, 1, 518400, 0x3c83c5ce +0, 135, 135, 1, 518400, 0x80d3bf46 1, 4514, 4514, 10, 2013, 0xf7c78ab2 1, 4524, 4524, 10, 2013, 0x24049816 1, 4534, 4534, 10, 2013, 0x52089dcf -0, 136, 136, 1, 518400, 0xfa22c508 +0, 136, 136, 1, 518400, 0x2967be7f 1, 4550, 4550, 10, 2013, 0x2150a0b1 1, 4560, 4560, 10, 2013, 0x3c2e9b93 1, 4570, 4570, 10, 2013, 0x491f932b -0, 137, 137, 1, 518400, 0xddda1712 +0, 137, 137, 1, 518400, 0x5a3b1092 1, 4580, 4580, 10, 2013, 0x31359cf8 1, 4590, 4590, 10, 2013, 0x1b00ac3f 1, 4600, 4600, 10, 2013, 0x8d7ab3cb -0, 138, 138, 1, 518400, 0x985a3b93 +0, 138, 138, 1, 518400, 0x8741350b 1, 4610, 4610, 10, 2013, 0xb2c2a4de 1, 4620, 4620, 10, 2013, 0x80a4abf2 1, 4635, 4635, 10, 2013, 0x0701a4ee -0, 139, 139, 1, 518400, 0xea63c5e7 +0, 139, 139, 1, 518400, 0xd5a9bf60 1, 4645, 4645, 10, 2013, 0xdc1ba5bc 1, 4655, 4655, 10, 2013, 0x6083a8a4 1, 4665, 4665, 10, 2013, 0x6226ad45 -0, 140, 140, 1, 518400, 0xef64983d +0, 140, 140, 1, 518400, 0xc05f91ba 1, 4675, 4675, 10, 2013, 0x2732a205 1, 4685, 4685, 10, 2013, 0x0f62a0d3 1, 4695, 4695, 10, 2013, 0xc1799249 -0, 141, 141, 1, 518400, 0x747bb193 +0, 141, 141, 1, 518400, 0x3fdaab0b 1, 4705, 4705, 10, 2013, 0xbccfa9c8 1, 4720, 4720, 10, 2013, 0xded096e7 1, 4730, 4730, 10, 2013, 0x7f0daf43 -0, 142, 142, 1, 518400, 0xb8748862 +0, 142, 142, 1, 518400, 0xab7281d9 1, 4740, 4740, 10, 2013, 0xc47ea682 1, 4750, 4750, 10, 2013, 0x5a72b07a 1, 4760, 4760, 10, 2013, 0x386faa8c 1, 4770, 4770, 10, 2013, 0xf9919a91 -0, 143, 143, 1, 518400, 0xaab55a5f +0, 143, 143, 1, 518400, 0xc80053d6 1, 4780, 4780, 10, 2013, 0x4908897e 1, 4790, 4790, 10, 2013, 0x4882b594 -0, 144, 144, 1, 518400, 0x7b468add +0, 144, 144, 1, 518400, 0x6526845c 1, 4806, 4806, 10, 2013, 0x113e98d1 1, 4816, 4816, 10, 2013, 0x5098b30d 1, 4826, 4826, 10, 2013, 0x0ef7b857 1, 4836, 4836, 10, 2013, 0x216ea176 -0, 145, 145, 1, 518400, 0xf2078707 +0, 145, 145, 1, 518400, 0x1b788089 1, 4846, 4846, 10, 2013, 0xf906944a 1, 4856, 4856, 10, 2013, 0xee9b92fb 1, 4866, 4866, 10, 2013, 0xd6029209 -0, 146, 146, 1, 518400, 0x6a2d931e +0, 146, 146, 1, 518400, 0xfa8e8ca9 1, 4876, 4876, 10, 2013, 0x2256a12e 1, 4891, 4891, 10, 2013, 0x89de8e4a 1, 4901, 4901, 10, 2013, 0x0bf0a584 -0, 147, 147, 1, 518400, 0xbbe3c417 +0, 147, 147, 1, 518400, 0xb278bda1 1, 4911, 4911, 10, 2013, 0x6a5ebd58 1, 4921, 4921, 10, 2013, 0x3edd9aa4 1, 4931, 4931, 10, 2013, 0xbd66ac26 -0, 148, 148, 1, 518400, 0x6294e449 +0, 148, 148, 1, 518400, 0xb0c3ddca 1, 4941, 4941, 10, 2013, 0x313896ea 1, 4951, 4951, 10, 2013, 0x6b83a6a0 1, 4961, 4961, 10, 2013, 0x9aafb109 -0, 149, 149, 1, 518400, 0xa05721e7 +0, 149, 149, 1, 518400, 0x10351b53 1, 4976, 4976, 10, 2013, 0x5192a85a 1, 4986, 4986, 10, 2013, 0x1f919f79 1, 4996, 4996, 10, 2013, 0xc0799c40 -0, 150, 150, 1, 518400, 0x37749183 +0, 150, 150, 1, 518400, 0xc1408aee 1, 5006, 5006, 10, 2013, 0x2988bcd8 1, 5016, 5016, 10, 2013, 0x1482913a 1, 5026, 5026, 10, 2013, 0x74da9a94 1, 5036, 5036, 10, 2013, 0x763eb709 -0, 151, 151, 1, 518400, 0xf9d9dca0 +0, 151, 151, 1, 518400, 0xf016d615 1, 5046, 5046, 10, 2013, 0x1285b405 1, 5062, 5062, 10, 2013, 0xb6ab9dfc -0, 152, 152, 1, 518400, 0x5f8ccf08 +0, 152, 152, 1, 518400, 0xa768c892 1, 5072, 5072, 10, 2013, 0xe4c8bf19 1, 5082, 5082, 10, 2013, 0xabbbade8 1, 5092, 5092, 10, 2013, 0xf8b69d89 1, 5102, 5102, 10, 2013, 0xce04a866 -0, 153, 153, 1, 518400, 0x7303f77b +0, 153, 153, 1, 518400, 0x11c3f11e 1, 5112, 5112, 10, 2013, 0x07528abf 1, 5122, 5122, 10, 2013, 0x74fb98bf 1, 5132, 5132, 10, 2013, 0x579fb1c9 -0, 154, 154, 1, 518400, 0x22b0513f +0, 154, 154, 1, 518400, 0xcd9a4ac4 1, 5147, 5147, 10, 2013, 0x7ddea2ed 1, 5157, 5157, 10, 2013, 0x296caa2c 1, 5167, 5167, 10, 2013, 0x346d9c4f -0, 155, 155, 1, 518400, 0x330485d2 +0, 155, 155, 1, 518400, 0x4ade7f5e 1, 5177, 5177, 10, 2013, 0x3e1fba15 1, 5187, 5187, 10, 2013, 0x48a2908f 1, 5197, 5197, 10, 2013, 0xc1938d09 -0, 156, 156, 1, 518400, 0x7f83daea +0, 156, 156, 1, 518400, 0x655dd46b 1, 5207, 5207, 10, 2013, 0x0e96a060 1, 5217, 5217, 10, 2013, 0x7b6a9e06 1, 5232, 5232, 10, 2013, 0x5b779d28 -0, 157, 157, 1, 518400, 0xee19f2df +0, 157, 157, 1, 518400, 0x5ab5ec61 1, 5242, 5242, 10, 2013, 0xf600aca1 1, 5252, 5252, 10, 2013, 0x3a6c9e68 1, 5262, 5262, 10, 2013, 0x0c8dc1b0 -0, 158, 158, 1, 518400, 0xb71b1c77 +0, 158, 158, 1, 518400, 0x45dc15e6 1, 5272, 5272, 10, 2013, 0x26beb245 1, 5282, 5282, 10, 2013, 0x2bc09557 1, 5292, 5292, 10, 2013, 0x27fc8845 1, 5302, 5302, 10, 2013, 0x1025aa47 -0, 159, 159, 1, 518400, 0xbffc1856 +0, 159, 159, 1, 518400, 0x201911d3 1, 5318, 5318, 10, 2013, 0xc2e69baa 1, 5328, 5328, 10, 2013, 0xdb249b92 1, 5338, 5338, 10, 2013, 0x6ccda29e -0, 160, 160, 1, 518400, 0xabc125aa +0, 160, 160, 1, 518400, 0x0fbc1f46 1, 5348, 5348, 10, 2013, 0xeaf6a1cf 1, 5358, 5358, 10, 2013, 0x509ba397 1, 5368, 5368, 10, 2013, 0xfaf8a2df -0, 161, 161, 1, 518400, 0x5ee467f8 +0, 161, 161, 1, 518400, 0x7e316179 1, 5378, 5378, 10, 2013, 0x41388f28 1, 5388, 5388, 10, 2013, 0xfe5eab39 1, 5403, 5403, 10, 2013, 0xd5ffa066 -0, 162, 162, 1, 518400, 0x6c2cf168 +0, 162, 162, 1, 518400, 0x73bbeaed 1, 5413, 5413, 10, 2013, 0x6813a30a 1, 5423, 5423, 10, 2013, 0x9be89718 1, 5433, 5433, 10, 2013, 0xaec3a27b -0, 163, 163, 1, 518400, 0x63996b26 +0, 163, 163, 1, 518400, 0x3a7c648a 1, 5446, 5446, 10, 2013, 0x579a983e 1, 5456, 5456, 10, 2013, 0x98cea21f 1, 5466, 5466, 10, 2013, 0xca77a58a -0, 164, 164, 1, 518400, 0xb34d789a +0, 164, 164, 1, 518400, 0x9f707209 1, 5476, 5476, 10, 2013, 0xcbc3b1ee 1, 5486, 5486, 10, 2013, 0xf3bb8f07 1, 5496, 5496, 10, 2013, 0x6aeebd92 -0, 165, 165, 1, 518400, 0xf49c030f +0, 165, 165, 1, 518400, 0x9f25fc5c 1, 5506, 5506, 10, 2013, 0xe955a449 1, 5516, 5516, 10, 2013, 0x9436aa5b 1, 5531, 5531, 10, 2013, 0x4f0a8f9f -0, 166, 166, 1, 518400, 0x092dc41a +0, 166, 166, 1, 518400, 0x2ed8bd75 1, 5541, 5541, 10, 2013, 0x3551b22d 1, 5551, 5551, 10, 2013, 0x0959a3d4 1, 5561, 5561, 10, 2013, 0x2ed5a11b 1, 5571, 5571, 10, 2013, 0x8f52a5c3 -0, 167, 167, 1, 518400, 0x4134c577 +0, 167, 167, 1, 518400, 0xb493becb 1, 5581, 5581, 10, 2013, 0x6552978d 1, 5591, 5591, 10, 2013, 0x7dcca0c1 1, 5601, 5601, 10, 2013, 0xbcd4a3c9 -0, 168, 168, 1, 518400, 0x261de1ed +0, 168, 168, 1, 518400, 0x7df6db57 1, 5616, 5616, 10, 2013, 0xfe41a8d8 1, 5626, 5626, 10, 2013, 0xc85aae14 1, 5636, 5636, 10, 2013, 0x1185b346 -0, 169, 169, 1, 518400, 0xcbc8566a +0, 169, 169, 1, 518400, 0x1cb94fca 1, 5646, 5646, 10, 2013, 0xf7429a0d 1, 5656, 5656, 10, 2013, 0x48c2a160 1, 5666, 5666, 10, 2013, 0x9d85a85d -0, 170, 170, 1, 518400, 0x407a5c76 +0, 170, 170, 1, 518400, 0x70db55d8 1, 5676, 5676, 10, 2013, 0xbbe89fe9 1, 5686, 5686, 10, 2013, 0xea429fe2 1, 5702, 5702, 10, 2013, 0x221ca1d4 -0, 171, 171, 1, 518400, 0x1ed73bb2 +0, 171, 171, 1, 518400, 0xc1d9351b 1, 5712, 5712, 10, 2013, 0x394b925b 1, 5722, 5722, 10, 2013, 0x556dc26f 1, 5732, 5732, 10, 2013, 0xce21a5e1 -0, 172, 172, 1, 518400, 0x8467ddb5 +0, 172, 172, 1, 518400, 0xa4b0d717 1, 5742, 5742, 10, 2013, 0xbc87c0a8 1, 5752, 5752, 10, 2013, 0xbac4ac07 1, 5762, 5762, 10, 2013, 0xdeefa4aa 1, 5772, 5772, 10, 2013, 0x1f15b362 -0, 173, 173, 1, 518400, 0x0523dc73 +0, 173, 173, 1, 518400, 0x3730d5e9 1, 5787, 5787, 10, 2013, 0x6406b7b2 1, 5797, 5797, 10, 2013, 0x8030a03d -0, 174, 174, 1, 518400, 0x81f5e895 +0, 174, 174, 1, 518400, 0x9673e1ec 1, 5807, 5807, 10, 2013, 0x0373a5b1 1, 5817, 5817, 10, 2013, 0x34ef93da 1, 5827, 5827, 10, 2013, 0x94c198fe 1, 5837, 5837, 10, 2013, 0xfefcabad -0, 175, 175, 1, 518400, 0xfc74608d +0, 175, 175, 1, 518400, 0x877959d5 1, 5847, 5847, 10, 2013, 0x8755b3ec 1, 5857, 5857, 10, 2013, 0xe436a6fd 1, 5872, 5872, 10, 2013, 0x9cf5a11e -0, 176, 176, 1, 518400, 0xc4e0dae0 +0, 176, 176, 1, 518400, 0x04f3d421 1, 5882, 5882, 10, 2013, 0x03b8a98c 1, 5892, 5892, 10, 2013, 0x6216a138 1, 5902, 5902, 10, 2013, 0xd87b9f12 -0, 177, 177, 1, 518400, 0x98367f5b +0, 177, 177, 1, 518400, 0x4f3078bc 1, 5912, 5912, 10, 2013, 0x4ce99653 1, 5922, 5922, 10, 2013, 0x6c2ea9e2 1, 5932, 5932, 10, 2013, 0x918cae4c -0, 178, 178, 1, 518400, 0x0f1a869d +0, 178, 178, 1, 518400, 0x8a127ff8 1, 5942, 5942, 10, 2013, 0xd19fa5f2 1, 5958, 5958, 10, 2013, 0x0bdda7c6 1, 5968, 5968, 10, 2013, 0x0f9ab0ca -0, 179, 179, 1, 518400, 0x45b6ccf2 +0, 179, 179, 1, 518400, 0x5864c64f 1, 5978, 5978, 10, 2013, 0x410a92b1 1, 5988, 5988, 10, 2013, 0xcfbe9d1c 1, 5998, 5998, 10, 2013, 0x59ed9d15 -0, 180, 180, 1, 518400, 0x5f9ccb77 +0, 180, 180, 1, 518400, 0xdaccc4c0 1, 6008, 6008, 10, 2013, 0x4e129e27 1, 6018, 6018, 10, 2013, 0x7bb9ac0a 1, 6028, 6028, 10, 2013, 0x826ca82b -0, 181, 181, 1, 518400, 0x5f15ea31 +0, 181, 181, 1, 518400, 0xd999e376 1, 6043, 6043, 10, 2013, 0x9ad5a74b 1, 6053, 6053, 10, 2013, 0x6c5f969a 1, 6063, 6063, 10, 2013, 0x8479a0e5 -0, 182, 182, 1, 518400, 0x86369f27 +0, 182, 182, 1, 518400, 0x8af39876 1, 6073, 6073, 10, 2013, 0x165298ef 1, 6083, 6083, 10, 2013, 0xdcadb4a1 1, 6093, 6093, 10, 2013, 0xa90e987c 1, 6103, 6103, 10, 2013, 0x1ac5b510 -0, 183, 183, 1, 518400, 0x2e27f9fa +0, 183, 183, 1, 518400, 0x5e72f33d 1, 6113, 6113, 10, 2013, 0x66728d85 1, 6128, 6128, 10, 2013, 0xe4859fc5 1, 6138, 6138, 10, 2013, 0x9901786e -0, 184, 184, 1, 518400, 0xc029a44d +0, 184, 184, 1, 518400, 0x14af9d92 1, 6148, 6148, 10, 2013, 0x6aebb406 1, 6158, 6158, 10, 2013, 0x7d13a2cc 1, 6168, 6168, 10, 2013, 0x99b7a8cc -0, 185, 185, 1, 518400, 0xebee33b0 +0, 185, 185, 1, 518400, 0x50b82d10 1, 6178, 6178, 10, 2013, 0x80b8a624 1, 6188, 6188, 10, 2013, 0xbb6aa271 1, 6198, 6198, 10, 2013, 0x17af9e4a -0, 186, 186, 1, 518400, 0x19e5494f +0, 186, 186, 1, 518400, 0xc068429c 1, 6214, 6214, 10, 2013, 0xfaf0a8f1 1, 6224, 6224, 10, 2013, 0xd6849b93 1, 6234, 6234, 10, 2013, 0xe9829669 -0, 187, 187, 1, 518400, 0xf697bd7c +0, 187, 187, 1, 518400, 0x8934b6d1 1, 6244, 6244, 10, 2013, 0x7ec98944 1, 6254, 6254, 10, 2013, 0x2b2099a4 1, 6264, 6264, 10, 2013, 0x1033a82f -0, 188, 188, 1, 518400, 0x82569002 +0, 188, 188, 1, 518400, 0x11d08947 1, 6274, 6274, 10, 2013, 0x5ec88990 1, 6284, 6284, 10, 2013, 0xd2a19b3d 1, 6299, 6299, 10, 2013, 0xa377b268 -0, 189, 189, 1, 518400, 0xfcb6d707 +0, 189, 189, 1, 518400, 0x8a27d041 1, 6309, 6309, 10, 2013, 0xfa859901 1, 6319, 6319, 10, 2013, 0x1713955a 1, 6329, 6329, 10, 2013, 0x70aab0da 1, 6339, 6339, 10, 2013, 0xcdaea422 -0, 190, 190, 1, 518400, 0x82a9662b +0, 190, 190, 1, 518400, 0xab265f7d 1, 6349, 6349, 10, 2013, 0x65c3bf80 1, 6359, 6359, 10, 2013, 0x1d75a55f 1, 6369, 6369, 10, 2013, 0xa5bea4de -0, 191, 191, 1, 518400, 0x212e16ee +0, 191, 191, 1, 518400, 0xff491040 1, 6384, 6384, 10, 2013, 0x184db71c 1, 6394, 6394, 10, 2013, 0x99858ec8 1, 6404, 6404, 10, 2013, 0xb8f2aee5 -0, 192, 192, 1, 518400, 0x2ca34dca +0, 192, 192, 1, 518400, 0x822b4704 1, 6414, 6414, 10, 2013, 0x4435b2ef 1, 6424, 6424, 10, 2013, 0x8acfa6c7 1, 6434, 6434, 10, 2013, 0x42b4c01f -0, 193, 193, 1, 518400, 0xe9ebe0a5 +0, 193, 193, 1, 518400, 0x4523d9f4 1, 6444, 6444, 10, 2013, 0x6e308c13 1, 6454, 6454, 10, 2013, 0x8227a0f6 1, 6470, 6470, 10, 2013, 0x6f12a7a2 -0, 194, 194, 1, 518400, 0x4e6b6917 +0, 194, 194, 1, 518400, 0xfc3c626e 1, 6480, 6480, 10, 2013, 0x785392be 1, 6490, 6490, 10, 2013, 0x81849c2b 1, 6500, 6500, 10, 2013, 0x5cf2af65 -0, 195, 195, 1, 518400, 0x7dcf20ab +0, 195, 195, 1, 518400, 0x237319e5 1, 6510, 6510, 10, 2013, 0x0c6ca6b4 1, 6520, 6520, 10, 2013, 0x412fab9f 1, 6530, 6530, 10, 2013, 0x08e792b4 -0, 196, 196, 1, 518400, 0xf30fac97 +0, 196, 196, 1, 518400, 0x892ca5d8 1, 6540, 6540, 10, 2013, 0x407aace3 1, 6555, 6555, 10, 2013, 0xd26bac16 1, 6565, 6565, 10, 2013, 0xac8bb295 -0, 197, 197, 1, 518400, 0xcb9fc692 +0, 197, 197, 1, 518400, 0xc4c0bfc7 1, 6575, 6575, 10, 2013, 0xddd1949c 1, 6585, 6585, 10, 2013, 0x6b26b868 1, 6595, 6595, 10, 2013, 0x5eaba587 1, 6605, 6605, 10, 2013, 0xef0793b9 -0, 198, 198, 1, 518400, 0x5d05601e +0, 198, 198, 1, 518400, 0x57c85956 1, 6615, 6615, 10, 2013, 0xdef19bd6 1, 6625, 6625, 10, 2013, 0xca98a635 -0, 199, 199, 1, 518400, 0x456c1417 +0, 199, 199, 1, 518400, 0xd6300d46 1, 6640, 6640, 10, 2013, 0x06269a5a 1, 6650, 6650, 10, 2013, 0x32cb9952 1, 6660, 6660, 10, 2013, 0xf01fa95a 1, 6670, 6670, 10, 2013, 0xefab9e55 -0, 200, 200, 1, 518400, 0x9a0fd1ad +0, 200, 200, 1, 518400, 0xd3dacaec 1, 6680, 6680, 10, 2013, 0x55a3b63a 1, 6690, 6690, 10, 2013, 0xcd36a553 1, 6700, 6700, 10, 2013, 0x2ec19877 -0, 201, 201, 1, 518400, 0x55db9716 +0, 201, 201, 1, 518400, 0x65429052 1, 6710, 6710, 10, 2013, 0xc18b924c 1, 6726, 6726, 10, 2013, 0xf132b04c 1, 6736, 6736, 10, 2013, 0x7975a44d -0, 202, 202, 1, 518400, 0x1f0d40d6 +0, 202, 202, 1, 518400, 0xec803a15 1, 6746, 6746, 10, 2013, 0x2aaf94cb 1, 6756, 6756, 10, 2013, 0x58cfa60f 1, 6766, 6766, 10, 2013, 0x9757a658 -0, 203, 203, 1, 518400, 0x73695c82 +0, 203, 203, 1, 518400, 0x7a9a55c9 1, 6776, 6776, 10, 2013, 0x67ebc0d5 1, 6786, 6786, 10, 2013, 0x3c50a70e 1, 6796, 6796, 10, 2013, 0x9c5799c6 -0, 204, 204, 1, 518400, 0xb0f10812 +0, 204, 204, 1, 518400, 0xcac30160 1, 6811, 6811, 10, 2013, 0x018d85b2 1, 6821, 6821, 10, 2013, 0x5367a956 -0, 205, 205, 1, 518400, 0xdec18505 -0, 208, 208, 1, 518400, 0xb147b947 -0, 240, 240, 1, 518400, 0x9d2e3977 +0, 205, 205, 1, 518400, 0x7e187e4f +0, 208, 208, 1, 518400, 0x0be0b2a2 +0, 213, 213, 1, 518400, 0x0be0b2a2 diff --git a/tests/ref/fate/sub-dvb b/tests/ref/fate/sub-dvb index cbd1801d64..8f33c75d70 100644 --- a/tests/ref/fate/sub-dvb +++ b/tests/ref/fate/sub-dvb @@ -1,75 +1,93 @@ #tb 0: 1/1000000 #media_type 0: subtitle #codec_id 0: dvb_subtitle -0, 15600000, 15600000, 159000, 1168, 0xd0f89d82 -0, 15759000, 15759000, 159000, 14, 0x064900eb -0, 15760000, 15760000, 239000, 1544, 0xe60f1751 -0, 15999000, 15999000, 239000, 14, 0x0729010b -0, 16000000, 16000000, 339000, 1658, 0xbe343093 -0, 16339000, 16339000, 339000, 14, 0x0809012b -0, 16340000, 16340000, 599000, 2343, 0xc68f07ef -0, 16939000, 16939000, 599000, 14, 0x08e9014b -0, 16940000, 16940000, 459000, 2568, 0x0ee657b1 -0, 17399000, 17399000, 459000, 14, 0x09c9016b -0, 17400000, 17400000, 359000, 3422, 0xba5b63ce -0, 17759000, 17759000, 359000, 14, 0x0aa9018b -0, 17760000, 17760000, 219000, 5078, 0x95b19902 -0, 17979000, 17979000, 219000, 14, 0x0b8901ab -0, 17980000, 17980000, 959000, 5808, 0xc9717b89 -0, 18939000, 18939000, 959000, 14, 0x0c6901cb -0, 18940000, 18940000, 219000, 6015, 0x0becbfac -0, 19159000, 19159000, 219000, 14, 0x064900eb -0, 19160000, 19160000, 259000, 6519, 0xfcd24d26 -0, 19419000, 19419000, 259000, 14, 0x0729010b -0, 19420000, 19420000, 99000, 7061, 0xf0320408 -0, 19519000, 19519000, 99000, 14, 0x0809012b -0, 19520000, 19520000, 219000, 4773, 0x66c93074 -0, 19739000, 19739000, 219000, 14, 0x08e9014b -0, 19740000, 19740000, 219000, 5546, 0x06052c81 -0, 19959000, 19959000, 219000, 14, 0x09c9016b -0, 19960000, 19960000, 239000, 5754, 0x904f7325 -0, 20199000, 20199000, 239000, 14, 0x0aa9018b -0, 20200000, 20200000, 139000, 6099, 0xe30cde07 -0, 20339000, 20339000, 139000, 14, 0x0b8901ab -0, 20340000, 20340000, 799000, 6839, 0x770fcb6c -0, 21139000, 21139000, 799000, 14, 0x0c6901cb -0, 21140000, 21140000, 239000, 4744, 0xa91e1b41 -0, 21379000, 21379000, 239000, 14, 0x064900eb -0, 21380000, 21380000, 339000, 5824, 0xcf6d782b -0, 21719000, 21719000, 339000, 14, 0x0729010b -0, 21720000, 21720000, 1439000, 6212, 0xabf8f7cf -0, 23159000, 23159000, 1439000, 14, 0x0809012b -0, 23160000, 23160000, 1319000, 7082, 0xd7ca10f2 -0, 24479000, 24479000, 1319000, 14, 0x08e9014b -0, 24480000, 24480000, 219000, 5345, 0x12b2cae0 -0, 24699000, 24699000, 219000, 14, 0x09c9016b -0, 24700000, 24700000, 219000, 5765, 0xc7d46192 -0, 24919000, 24919000, 219000, 14, 0x0aa9018b -0, 24920000, 24920000, 599000, 6557, 0xcb995d30 -0, 25519000, 25519000, 599000, 14, 0x0b8901ab -0, 25520000, 25520000, 219000, 7091, 0xe6ea0559 -0, 25739000, 25739000, 219000, 14, 0x0c6901cb -0, 25740000, 25740000, 239000, 7305, 0xb66c404e -0, 25979000, 25979000, 239000, 14, 0x064900eb -0, 25980000, 25980000, 359000, 7590, 0x0cc2a481 -0, 26339000, 26339000, 359000, 14, 0x0729010b -0, 26340000, 26340000, 219000, 4629, 0xe18cfea8 -0, 26559000, 26559000, 219000, 14, 0x0809012b -0, 26560000, 26560000, 719000, 4785, 0x82043fc0 -0, 27279000, 27279000, 719000, 14, 0x08e9014b -0, 27280000, 27280000, 459000, 6061, 0xbde7d245 -0, 27739000, 27739000, 459000, 14, 0x09c9016b -0, 27740000, 27740000, 239000, 6301, 0x92d01a51 -0, 27979000, 27979000, 239000, 14, 0x0aa9018b -0, 27980000, 27980000, 99000, 6736, 0xbd25a134 -0, 28079000, 28079000, 99000, 14, 0x0b8901ab -0, 28080000, 28080000, 219000, 7214, 0x7ef93c13 -0, 28299000, 28299000, 219000, 14, 0x0c6901cb -0, 28300000, 28300000, 239000, 7366, 0x5bed7fcd -0, 28539000, 28539000, 239000, 14, 0x064900eb -0, 28540000, 28540000, 599000, 4564, 0x7f4c014b -0, 29139000, 29139000, 599000, 14, 0x0729010b -0, 29140000, 29140000, 219000, 4637, 0x682626b7 -0, 29359000, 29359000, 219000, 14, 0x0809012b -0, 29360000, 29360000, 1679000, 5358, 0x29e30c48 -0, 31039000, 31039000, 1679000, 14, 0x08e9014b +0, 0, 0, 279000, 14, 0x05d900db +0, 279000, 279000, 279000, 14, 0x064900eb +0, 280000, 280000, 4999000, 14, 0x06b900fb +0, 5279000, 5279000, 4999000, 14, 0x0729010b +0, 5280000, 5280000, 5019000, 14, 0x0799011b +0, 10299000, 10299000, 5019000, 14, 0x0809012b +0, 10300000, 10300000, 3599000, 14, 0x0879013b +0, 13899000, 13899000, 3599000, 14, 0x08e9014b +0, 13900000, 13900000, 219000, 14, 0x0959015b +0, 14119000, 14119000, 219000, 14, 0x09c9016b +0, 14120000, 14120000, 1439000, 14, 0x0a39017b +0, 15559000, 15559000, 1439000, 14, 0x0aa9018b +0, 15560000, 15560000, 39000, 14, 0x0b19019b +0, 15599000, 15599000, 39000, 14, 0x0b8901ab +0, 15600000, 15600000, 159000, 1168, 0xd69da022 +0, 15759000, 15759000, 159000, 14, 0x0c6901cb +0, 15760000, 15760000, 239000, 1544, 0xc5f116f1 +0, 15999000, 15999000, 239000, 14, 0x064900eb +0, 16000000, 16000000, 339000, 1658, 0x73563033 +0, 16339000, 16339000, 339000, 14, 0x0729010b +0, 16340000, 16340000, 599000, 2343, 0x7ac2078f +0, 16939000, 16939000, 599000, 14, 0x0809012b +0, 16940000, 16940000, 459000, 2568, 0x6eaa5751 +0, 17399000, 17399000, 459000, 14, 0x08e9014b +0, 17400000, 17400000, 359000, 3422, 0xd9d0636e +0, 17759000, 17759000, 359000, 14, 0x09c9016b +0, 17760000, 17760000, 219000, 5078, 0x722c9862 +0, 17979000, 17979000, 219000, 14, 0x0aa9018b +0, 17980000, 17980000, 959000, 5808, 0x38dd7ae9 +0, 18939000, 18939000, 959000, 14, 0x0b8901ab +0, 18940000, 18940000, 219000, 6015, 0xd4d2c40c +0, 19159000, 19159000, 219000, 14, 0x0c6901cb +0, 19160000, 19160000, 259000, 6519, 0x08af4c86 +0, 19419000, 19419000, 259000, 14, 0x064900eb +0, 19420000, 19420000, 99000, 7061, 0xecf10368 +0, 19519000, 19519000, 99000, 14, 0x0729010b +0, 19520000, 19520000, 219000, 4773, 0xbee42fd4 +0, 19739000, 19739000, 219000, 14, 0x0809012b +0, 19740000, 19740000, 219000, 5546, 0xdb822be1 +0, 19959000, 19959000, 219000, 14, 0x08e9014b +0, 19960000, 19960000, 239000, 5754, 0xfdcc7285 +0, 20199000, 20199000, 239000, 14, 0x09c9016b +0, 20200000, 20200000, 139000, 6099, 0xa409dd67 +0, 20339000, 20339000, 139000, 14, 0x0aa9018b +0, 20340000, 20340000, 799000, 6839, 0xc5eecacc +0, 21139000, 21139000, 799000, 14, 0x0b8901ab +0, 21140000, 21140000, 239000, 4744, 0x4e451fa1 +0, 21379000, 21379000, 239000, 14, 0x0c6901cb +0, 21380000, 21380000, 339000, 5824, 0x5299778b +0, 21719000, 21719000, 339000, 14, 0x064900eb +0, 21720000, 21720000, 1439000, 6212, 0x6d15f72f +0, 23159000, 23159000, 1439000, 14, 0x0729010b +0, 23160000, 23160000, 1319000, 7082, 0xe5c91052 +0, 24479000, 24479000, 1319000, 14, 0x0809012b +0, 24480000, 24480000, 219000, 5345, 0x2e5eca40 +0, 24699000, 24699000, 219000, 14, 0x08e9014b +0, 24700000, 24700000, 219000, 5765, 0x118060f2 +0, 24919000, 24919000, 219000, 14, 0x09c9016b +0, 24920000, 24920000, 599000, 6557, 0x89275c90 +0, 25519000, 25519000, 599000, 14, 0x0aa9018b +0, 25520000, 25520000, 219000, 7091, 0x996904b9 +0, 25739000, 25739000, 219000, 14, 0x0b8901ab +0, 25740000, 25740000, 239000, 7305, 0xc23e44ae +0, 25979000, 25979000, 239000, 14, 0x0c6901cb +0, 25980000, 25980000, 359000, 7590, 0xc5a3a3e1 +0, 26339000, 26339000, 359000, 14, 0x064900eb +0, 26340000, 26340000, 219000, 4629, 0x7ad6fe08 +0, 26559000, 26559000, 219000, 14, 0x0729010b +0, 26560000, 26560000, 719000, 4785, 0xcd3f3f20 +0, 27279000, 27279000, 719000, 14, 0x0809012b +0, 27280000, 27280000, 459000, 6061, 0x8b04d1a5 +0, 27739000, 27739000, 459000, 14, 0x08e9014b +0, 27740000, 27740000, 239000, 6301, 0xe7de19b1 +0, 27979000, 27979000, 239000, 14, 0x09c9016b +0, 27980000, 27980000, 99000, 6736, 0x38b3a094 +0, 28079000, 28079000, 99000, 14, 0x0aa9018b +0, 28080000, 28080000, 219000, 7214, 0x0b783b73 +0, 28299000, 28299000, 219000, 14, 0x0b8901ab +0, 28300000, 28300000, 239000, 7366, 0x98bf842d +0, 28539000, 28539000, 239000, 14, 0x0c6901cb +0, 28540000, 28540000, 599000, 4564, 0x3d9600ab +0, 29139000, 29139000, 599000, 14, 0x064900eb +0, 29140000, 29140000, 219000, 4637, 0x01f02617 +0, 29359000, 29359000, 219000, 14, 0x0729010b +0, 29360000, 29360000, 1679000, 5358, 0x5b0f0ba8 +0, 31039000, 31039000, 1679000, 14, 0x0809012b +0, 31040000, 31040000, 359000, 14, 0x0879013b +0, 31399000, 31399000, 359000, 14, 0x08e9014b +0, 31400000, 31400000, 479000, 14, 0x0959015b +0, 31879000, 31879000, 479000, 14, 0x09c9016b diff --git a/tests/ref/fate/sub2video b/tests/ref/fate/sub2video index 80abe9c905..6f9f92b8e0 100644 --- a/tests/ref/fate/sub2video +++ b/tests/ref/fate/sub2video @@ -58,129 +58,1136 @@ 0, 47, 47, 1, 518400, 0xde69683f 0, 48, 48, 1, 518400, 0x7df08fba 0, 49, 49, 1, 518400, 0xbab197ea +0, 50, 50, 1, 518400, 0xbab197ea +0, 51, 51, 1, 518400, 0xbab197ea +0, 52, 52, 1, 518400, 0xbab197ea +0, 53, 53, 1, 518400, 0xbab197ea +0, 54, 54, 1, 518400, 0xbab197ea +0, 55, 55, 1, 518400, 0xbab197ea +0, 56, 56, 1, 518400, 0xbab197ea +0, 57, 57, 1, 518400, 0xbab197ea +0, 58, 58, 1, 518400, 0xbab197ea +0, 59, 59, 1, 518400, 0xbab197ea +0, 60, 60, 1, 518400, 0xbab197ea +0, 61, 61, 1, 518400, 0xbab197ea +0, 62, 62, 1, 518400, 0xbab197ea +0, 63, 63, 1, 518400, 0xbab197ea +0, 64, 64, 1, 518400, 0xbab197ea +0, 65, 65, 1, 518400, 0xbab197ea +0, 66, 66, 1, 518400, 0xbab197ea +0, 67, 67, 1, 518400, 0xbab197ea +0, 68, 68, 1, 518400, 0xbab197ea +0, 69, 69, 1, 518400, 0xbab197ea +0, 70, 70, 1, 518400, 0xbab197ea +0, 71, 71, 1, 518400, 0xbab197ea +0, 72, 72, 1, 518400, 0xbab197ea +0, 73, 73, 1, 518400, 0xbab197ea +0, 74, 74, 1, 518400, 0xbab197ea +0, 75, 75, 1, 518400, 0xbab197ea +0, 76, 76, 1, 518400, 0xbab197ea 1, 15355000, 15355000, 4733000, 2094, 0x3c171425 0, 77, 77, 1, 518400, 0x902285d9 -0, 100, 100, 1, 518400, 0xbab197ea +0, 78, 78, 1, 518400, 0x902285d9 +0, 79, 79, 1, 518400, 0x902285d9 +0, 80, 80, 1, 518400, 0x902285d9 +0, 81, 81, 1, 518400, 0x902285d9 +0, 82, 82, 1, 518400, 0x902285d9 +0, 83, 83, 1, 518400, 0x902285d9 +0, 84, 84, 1, 518400, 0x902285d9 +0, 85, 85, 1, 518400, 0x902285d9 +0, 86, 86, 1, 518400, 0x902285d9 +0, 87, 87, 1, 518400, 0x902285d9 +0, 88, 88, 1, 518400, 0x902285d9 +0, 89, 89, 1, 518400, 0x902285d9 +0, 90, 90, 1, 518400, 0x902285d9 +0, 91, 91, 1, 518400, 0x902285d9 +0, 92, 92, 1, 518400, 0x902285d9 +0, 93, 93, 1, 518400, 0x902285d9 +0, 94, 94, 1, 518400, 0x902285d9 +0, 95, 95, 1, 518400, 0x902285d9 +0, 96, 96, 1, 518400, 0x902285d9 +0, 97, 97, 1, 518400, 0x902285d9 +0, 98, 98, 1, 518400, 0x902285d9 +0, 99, 99, 1, 518400, 0x902285d9 +0, 100, 100, 1, 518400, 0x902285d9 +0, 101, 101, 1, 518400, 0xbab197ea +0, 102, 102, 1, 518400, 0xbab197ea +0, 103, 103, 1, 518400, 0xbab197ea +0, 104, 104, 1, 518400, 0xbab197ea +0, 105, 105, 1, 518400, 0xbab197ea +0, 106, 106, 1, 518400, 0xbab197ea +0, 107, 107, 1, 518400, 0xbab197ea +0, 108, 108, 1, 518400, 0xbab197ea +0, 109, 109, 1, 518400, 0xbab197ea +0, 110, 110, 1, 518400, 0xbab197ea +0, 111, 111, 1, 518400, 0xbab197ea +0, 112, 112, 1, 518400, 0xbab197ea +0, 113, 113, 1, 518400, 0xbab197ea +0, 114, 114, 1, 518400, 0xbab197ea +0, 115, 115, 1, 518400, 0xbab197ea +0, 116, 116, 1, 518400, 0xbab197ea +0, 117, 117, 1, 518400, 0xbab197ea +0, 118, 118, 1, 518400, 0xbab197ea +0, 119, 119, 1, 518400, 0xbab197ea +0, 120, 120, 1, 518400, 0xbab197ea +0, 121, 121, 1, 518400, 0xbab197ea +0, 122, 122, 1, 518400, 0xbab197ea +0, 123, 123, 1, 518400, 0xbab197ea +0, 124, 124, 1, 518400, 0xbab197ea +0, 125, 125, 1, 518400, 0xbab197ea +0, 126, 126, 1, 518400, 0xbab197ea +0, 127, 127, 1, 518400, 0xbab197ea +0, 128, 128, 1, 518400, 0xbab197ea +0, 129, 129, 1, 518400, 0xbab197ea +0, 130, 130, 1, 518400, 0xbab197ea +0, 131, 131, 1, 518400, 0xbab197ea +0, 132, 132, 1, 518400, 0xbab197ea +0, 133, 133, 1, 518400, 0xbab197ea +0, 134, 134, 1, 518400, 0xbab197ea +0, 135, 135, 1, 518400, 0xbab197ea +0, 136, 136, 1, 518400, 0xbab197ea +0, 137, 137, 1, 518400, 0xbab197ea +0, 138, 138, 1, 518400, 0xbab197ea +0, 139, 139, 1, 518400, 0xbab197ea +0, 140, 140, 1, 518400, 0xbab197ea +0, 141, 141, 1, 518400, 0xbab197ea +0, 142, 142, 1, 518400, 0xbab197ea +0, 143, 143, 1, 518400, 0xbab197ea +0, 144, 144, 1, 518400, 0xbab197ea +0, 145, 145, 1, 518400, 0xbab197ea +0, 146, 146, 1, 518400, 0xbab197ea +0, 147, 147, 1, 518400, 0xbab197ea +0, 148, 148, 1, 518400, 0xbab197ea +0, 149, 149, 1, 518400, 0xbab197ea +0, 150, 150, 1, 518400, 0xbab197ea +0, 151, 151, 1, 518400, 0xbab197ea +0, 152, 152, 1, 518400, 0xbab197ea +0, 153, 153, 1, 518400, 0xbab197ea +0, 154, 154, 1, 518400, 0xbab197ea +0, 155, 155, 1, 518400, 0xbab197ea +0, 156, 156, 1, 518400, 0xbab197ea +0, 157, 157, 1, 518400, 0xbab197ea +0, 158, 158, 1, 518400, 0xbab197ea +0, 159, 159, 1, 518400, 0xbab197ea +0, 160, 160, 1, 518400, 0xbab197ea +0, 161, 161, 1, 518400, 0xbab197ea +0, 162, 162, 1, 518400, 0xbab197ea +0, 163, 163, 1, 518400, 0xbab197ea +0, 164, 164, 1, 518400, 0xbab197ea +0, 165, 165, 1, 518400, 0xbab197ea +0, 166, 166, 1, 518400, 0xbab197ea +0, 167, 167, 1, 518400, 0xbab197ea +0, 168, 168, 1, 518400, 0xbab197ea +0, 169, 169, 1, 518400, 0xbab197ea +0, 170, 170, 1, 518400, 0xbab197ea +0, 171, 171, 1, 518400, 0xbab197ea +0, 172, 172, 1, 518400, 0xbab197ea +0, 173, 173, 1, 518400, 0xbab197ea +0, 174, 174, 1, 518400, 0xbab197ea +0, 175, 175, 1, 518400, 0xbab197ea +0, 176, 176, 1, 518400, 0xbab197ea +0, 177, 177, 1, 518400, 0xbab197ea +0, 178, 178, 1, 518400, 0xbab197ea +0, 179, 179, 1, 518400, 0xbab197ea +0, 180, 180, 1, 518400, 0xbab197ea +0, 181, 181, 1, 518400, 0xbab197ea +0, 182, 182, 1, 518400, 0xbab197ea +0, 183, 183, 1, 518400, 0xbab197ea +0, 184, 184, 1, 518400, 0xbab197ea +0, 185, 185, 1, 518400, 0xbab197ea +0, 186, 186, 1, 518400, 0xbab197ea +0, 187, 187, 1, 518400, 0xbab197ea +0, 188, 188, 1, 518400, 0xbab197ea +0, 189, 189, 1, 518400, 0xbab197ea +0, 190, 190, 1, 518400, 0xbab197ea +0, 191, 191, 1, 518400, 0xbab197ea +0, 192, 192, 1, 518400, 0xbab197ea +0, 193, 193, 1, 518400, 0xbab197ea +0, 194, 194, 1, 518400, 0xbab197ea +0, 195, 195, 1, 518400, 0xbab197ea +0, 196, 196, 1, 518400, 0xbab197ea +0, 197, 197, 1, 518400, 0xbab197ea +0, 198, 198, 1, 518400, 0xbab197ea +0, 199, 199, 1, 518400, 0xbab197ea +0, 200, 200, 1, 518400, 0xbab197ea +0, 201, 201, 1, 518400, 0xbab197ea +0, 202, 202, 1, 518400, 0xbab197ea +0, 203, 203, 1, 518400, 0xbab197ea +0, 204, 204, 1, 518400, 0xbab197ea +0, 205, 205, 1, 518400, 0xbab197ea +0, 206, 206, 1, 518400, 0xbab197ea +0, 207, 207, 1, 518400, 0xbab197ea +0, 208, 208, 1, 518400, 0xbab197ea +0, 209, 209, 1, 518400, 0xbab197ea +0, 210, 210, 1, 518400, 0xbab197ea +0, 211, 211, 1, 518400, 0xbab197ea +0, 212, 212, 1, 518400, 0xbab197ea +0, 213, 213, 1, 518400, 0xbab197ea +0, 214, 214, 1, 518400, 0xbab197ea +0, 215, 215, 1, 518400, 0xbab197ea +0, 216, 216, 1, 518400, 0xbab197ea +0, 217, 217, 1, 518400, 0xbab197ea +0, 218, 218, 1, 518400, 0xbab197ea +0, 219, 219, 1, 518400, 0xbab197ea +0, 220, 220, 1, 518400, 0xbab197ea +0, 221, 221, 1, 518400, 0xbab197ea +0, 222, 222, 1, 518400, 0xbab197ea +0, 223, 223, 1, 518400, 0xbab197ea +0, 224, 224, 1, 518400, 0xbab197ea +0, 225, 225, 1, 518400, 0xbab197ea +0, 226, 226, 1, 518400, 0xbab197ea +0, 227, 227, 1, 518400, 0xbab197ea +0, 228, 228, 1, 518400, 0xbab197ea +0, 229, 229, 1, 518400, 0xbab197ea +0, 230, 230, 1, 518400, 0xbab197ea +0, 231, 231, 1, 518400, 0xbab197ea +0, 232, 232, 1, 518400, 0xbab197ea +0, 233, 233, 1, 518400, 0xbab197ea +0, 234, 234, 1, 518400, 0xbab197ea +0, 235, 235, 1, 518400, 0xbab197ea +0, 236, 236, 1, 518400, 0xbab197ea +0, 237, 237, 1, 518400, 0xbab197ea +0, 238, 238, 1, 518400, 0xbab197ea +0, 239, 239, 1, 518400, 0xbab197ea +0, 240, 240, 1, 518400, 0xbab197ea +0, 241, 241, 1, 518400, 0xbab197ea +0, 242, 242, 1, 518400, 0xbab197ea +0, 243, 243, 1, 518400, 0xbab197ea 1, 48797000, 48797000, 2560000, 2480, 0x7c0edf21 0, 244, 244, 1, 518400, 0x7a11c812 -0, 257, 257, 1, 518400, 0xbab197ea +0, 245, 245, 1, 518400, 0x7a11c812 +0, 246, 246, 1, 518400, 0x7a11c812 +0, 247, 247, 1, 518400, 0x7a11c812 +0, 248, 248, 1, 518400, 0x7a11c812 +0, 249, 249, 1, 518400, 0x7a11c812 +0, 250, 250, 1, 518400, 0x7a11c812 +0, 251, 251, 1, 518400, 0x7a11c812 +0, 252, 252, 1, 518400, 0x7a11c812 +0, 253, 253, 1, 518400, 0x7a11c812 +0, 254, 254, 1, 518400, 0x7a11c812 +0, 255, 255, 1, 518400, 0x7a11c812 +0, 256, 256, 1, 518400, 0x7a11c812 +0, 257, 257, 1, 518400, 0x7a11c812 1, 51433000, 51433000, 2366000, 3059, 0xc95b8a05 0, 258, 258, 1, 518400, 0x34cdddee -0, 269, 269, 1, 518400, 0xbab197ea +0, 259, 259, 1, 518400, 0x34cdddee +0, 260, 260, 1, 518400, 0x34cdddee +0, 261, 261, 1, 518400, 0x34cdddee +0, 262, 262, 1, 518400, 0x34cdddee +0, 263, 263, 1, 518400, 0x34cdddee +0, 264, 264, 1, 518400, 0x34cdddee +0, 265, 265, 1, 518400, 0x34cdddee +0, 266, 266, 1, 518400, 0x34cdddee +0, 267, 267, 1, 518400, 0x34cdddee +0, 268, 268, 1, 518400, 0x34cdddee +0, 269, 269, 1, 518400, 0x34cdddee 1, 53910000, 53910000, 2696000, 2095, 0x61bb15ed 0, 270, 270, 1, 518400, 0x4db4ce51 -0, 283, 283, 1, 518400, 0xbab197ea +0, 271, 271, 1, 518400, 0x4db4ce51 +0, 272, 272, 1, 518400, 0x4db4ce51 +0, 273, 273, 1, 518400, 0x4db4ce51 +0, 274, 274, 1, 518400, 0x4db4ce51 +0, 275, 275, 1, 518400, 0x4db4ce51 +0, 276, 276, 1, 518400, 0x4db4ce51 +0, 277, 277, 1, 518400, 0x4db4ce51 +0, 278, 278, 1, 518400, 0x4db4ce51 +0, 279, 279, 1, 518400, 0x4db4ce51 +0, 280, 280, 1, 518400, 0x4db4ce51 +0, 281, 281, 1, 518400, 0x4db4ce51 +0, 282, 282, 1, 518400, 0x4db4ce51 +0, 283, 283, 1, 518400, 0x4db4ce51 1, 56663000, 56663000, 1262000, 1013, 0xc9ae89b7 0, 284, 284, 1, 518400, 0xe6bc0ea9 -0, 290, 290, 1, 518400, 0xbab197ea +0, 285, 285, 1, 518400, 0xe6bc0ea9 +0, 286, 286, 1, 518400, 0xe6bc0ea9 +0, 287, 287, 1, 518400, 0xe6bc0ea9 +0, 288, 288, 1, 518400, 0xe6bc0ea9 +0, 289, 289, 1, 518400, 0xe6bc0ea9 +0, 290, 290, 1, 518400, 0xe6bc0ea9 1, 58014000, 58014000, 1661000, 969, 0xe01878f0 0, 291, 291, 1, 518400, 0xa8643af7 -0, 298, 298, 1, 518400, 0xbab197ea +0, 292, 292, 1, 518400, 0xa8643af7 +0, 293, 293, 1, 518400, 0xa8643af7 +0, 294, 294, 1, 518400, 0xa8643af7 +0, 295, 295, 1, 518400, 0xa8643af7 +0, 296, 296, 1, 518400, 0xa8643af7 +0, 297, 297, 1, 518400, 0xa8643af7 +0, 298, 298, 1, 518400, 0xa8643af7 +0, 299, 299, 1, 518400, 0xbab197ea +0, 300, 300, 1, 518400, 0xbab197ea +0, 301, 301, 1, 518400, 0xbab197ea +0, 302, 302, 1, 518400, 0xbab197ea +0, 303, 303, 1, 518400, 0xbab197ea +0, 304, 304, 1, 518400, 0xbab197ea +0, 305, 305, 1, 518400, 0xbab197ea +0, 306, 306, 1, 518400, 0xbab197ea +0, 307, 307, 1, 518400, 0xbab197ea +0, 308, 308, 1, 518400, 0xbab197ea +0, 309, 309, 1, 518400, 0xbab197ea +0, 310, 310, 1, 518400, 0xbab197ea +0, 311, 311, 1, 518400, 0xbab197ea +0, 312, 312, 1, 518400, 0xbab197ea +0, 313, 313, 1, 518400, 0xbab197ea +0, 314, 314, 1, 518400, 0xbab197ea +0, 315, 315, 1, 518400, 0xbab197ea +0, 316, 316, 1, 518400, 0xbab197ea +0, 317, 317, 1, 518400, 0xbab197ea +0, 318, 318, 1, 518400, 0xbab197ea +0, 319, 319, 1, 518400, 0xbab197ea +0, 320, 320, 1, 518400, 0xbab197ea +0, 321, 321, 1, 518400, 0xbab197ea +0, 322, 322, 1, 518400, 0xbab197ea +0, 323, 323, 1, 518400, 0xbab197ea +0, 324, 324, 1, 518400, 0xbab197ea +0, 325, 325, 1, 518400, 0xbab197ea +0, 326, 326, 1, 518400, 0xbab197ea +0, 327, 327, 1, 518400, 0xbab197ea +0, 328, 328, 1, 518400, 0xbab197ea +0, 329, 329, 1, 518400, 0xbab197ea +0, 330, 330, 1, 518400, 0xbab197ea +0, 331, 331, 1, 518400, 0xbab197ea +0, 332, 332, 1, 518400, 0xbab197ea +0, 333, 333, 1, 518400, 0xbab197ea +0, 334, 334, 1, 518400, 0xbab197ea +0, 335, 335, 1, 518400, 0xbab197ea +0, 336, 336, 1, 518400, 0xbab197ea +0, 337, 337, 1, 518400, 0xbab197ea +0, 338, 338, 1, 518400, 0xbab197ea 1, 67724000, 67724000, 1365000, 844, 0xe7db4fc1 0, 339, 339, 1, 518400, 0xb1885c67 -0, 345, 345, 1, 518400, 0xbab197ea +0, 340, 340, 1, 518400, 0xb1885c67 +0, 341, 341, 1, 518400, 0xb1885c67 +0, 342, 342, 1, 518400, 0xb1885c67 +0, 343, 343, 1, 518400, 0xb1885c67 +0, 344, 344, 1, 518400, 0xb1885c67 +0, 345, 345, 1, 518400, 0xb1885c67 1, 69175000, 69175000, 1558000, 802, 0xf48531ba 0, 346, 346, 1, 518400, 0x378e3fd0 -0, 354, 354, 1, 518400, 0xbab197ea +0, 347, 347, 1, 518400, 0x378e3fd0 +0, 348, 348, 1, 518400, 0x378e3fd0 +0, 349, 349, 1, 518400, 0x378e3fd0 +0, 350, 350, 1, 518400, 0x378e3fd0 +0, 351, 351, 1, 518400, 0x378e3fd0 +0, 352, 352, 1, 518400, 0x378e3fd0 +0, 353, 353, 1, 518400, 0x378e3fd0 +0, 354, 354, 1, 518400, 0x378e3fd0 1, 70819000, 70819000, 1865000, 1709, 0xb4d5a1bd 0, 355, 355, 1, 518400, 0xa3782469 -0, 363, 363, 1, 518400, 0xbab197ea +0, 356, 356, 1, 518400, 0xa3782469 +0, 357, 357, 1, 518400, 0xa3782469 +0, 358, 358, 1, 518400, 0xa3782469 +0, 359, 359, 1, 518400, 0xa3782469 +0, 360, 360, 1, 518400, 0xa3782469 +0, 361, 361, 1, 518400, 0xa3782469 +0, 362, 362, 1, 518400, 0xa3782469 +0, 363, 363, 1, 518400, 0xa3782469 1, 72762000, 72762000, 1968000, 2438, 0x99d7bc82 0, 364, 364, 1, 518400, 0xba23a0d5 -0, 374, 374, 1, 518400, 0xbab197ea +0, 365, 365, 1, 518400, 0xba23a0d5 +0, 366, 366, 1, 518400, 0xba23a0d5 +0, 367, 367, 1, 518400, 0xba23a0d5 +0, 368, 368, 1, 518400, 0xba23a0d5 +0, 369, 369, 1, 518400, 0xba23a0d5 +0, 370, 370, 1, 518400, 0xba23a0d5 +0, 371, 371, 1, 518400, 0xba23a0d5 +0, 372, 372, 1, 518400, 0xba23a0d5 +0, 373, 373, 1, 518400, 0xba23a0d5 +0, 374, 374, 1, 518400, 0xba23a0d5 1, 74806000, 74806000, 1831000, 2116, 0x96514097 0, 375, 375, 1, 518400, 0x129de2f8 -0, 383, 383, 1, 518400, 0xbab197ea +0, 376, 376, 1, 518400, 0x129de2f8 +0, 377, 377, 1, 518400, 0x129de2f8 +0, 378, 378, 1, 518400, 0x129de2f8 +0, 379, 379, 1, 518400, 0x129de2f8 +0, 380, 380, 1, 518400, 0x129de2f8 +0, 381, 381, 1, 518400, 0x129de2f8 +0, 382, 382, 1, 518400, 0x129de2f8 +0, 383, 383, 1, 518400, 0x129de2f8 1, 76716000, 76716000, 1262000, 1822, 0xefccc72e 0, 384, 384, 1, 518400, 0x19772f0f -0, 390, 390, 1, 518400, 0xbab197ea +0, 385, 385, 1, 518400, 0x19772f0f +0, 386, 386, 1, 518400, 0x19772f0f +0, 387, 387, 1, 518400, 0x19772f0f +0, 388, 388, 1, 518400, 0x19772f0f +0, 389, 389, 1, 518400, 0x19772f0f +0, 390, 390, 1, 518400, 0x19772f0f 1, 78051000, 78051000, 1524000, 987, 0x7b927a27 0, 391, 391, 1, 518400, 0x56f54e73 -0, 398, 398, 1, 518400, 0xbab197ea +0, 392, 392, 1, 518400, 0x56f54e73 +0, 393, 393, 1, 518400, 0x56f54e73 +0, 394, 394, 1, 518400, 0x56f54e73 +0, 395, 395, 1, 518400, 0x56f54e73 +0, 396, 396, 1, 518400, 0x56f54e73 +0, 397, 397, 1, 518400, 0x56f54e73 +0, 398, 398, 1, 518400, 0x56f54e73 1, 79644000, 79644000, 2662000, 2956, 0x190778f7 0, 399, 399, 1, 518400, 0x300b5247 +0, 400, 400, 1, 518400, 0x300b5247 +0, 401, 401, 1, 518400, 0x300b5247 +0, 402, 402, 1, 518400, 0x300b5247 +0, 403, 403, 1, 518400, 0x300b5247 +0, 404, 404, 1, 518400, 0x300b5247 +0, 405, 405, 1, 518400, 0x300b5247 +0, 406, 406, 1, 518400, 0x300b5247 +0, 407, 407, 1, 518400, 0x300b5247 +0, 408, 408, 1, 518400, 0x300b5247 +0, 409, 409, 1, 518400, 0x300b5247 +0, 410, 410, 1, 518400, 0x300b5247 +0, 411, 411, 1, 518400, 0x300b5247 1, 82380000, 82380000, 2764000, 3094, 0xc021b7d3 -0, 412, 412, 1, 518400, 0xbab197ea +0, 412, 412, 1, 518400, 0x300b5247 0, 413, 413, 1, 518400, 0x6fd028fa -0, 426, 426, 1, 518400, 0xbab197ea +0, 414, 414, 1, 518400, 0x6fd028fa +0, 415, 415, 1, 518400, 0x6fd028fa +0, 416, 416, 1, 518400, 0x6fd028fa +0, 417, 417, 1, 518400, 0x6fd028fa +0, 418, 418, 1, 518400, 0x6fd028fa +0, 419, 419, 1, 518400, 0x6fd028fa +0, 420, 420, 1, 518400, 0x6fd028fa +0, 421, 421, 1, 518400, 0x6fd028fa +0, 422, 422, 1, 518400, 0x6fd028fa +0, 423, 423, 1, 518400, 0x6fd028fa +0, 424, 424, 1, 518400, 0x6fd028fa +0, 425, 425, 1, 518400, 0x6fd028fa +0, 426, 426, 1, 518400, 0x6fd028fa 1, 85225000, 85225000, 2366000, 2585, 0x74d0048f 0, 427, 427, 1, 518400, 0x01f80e9d -0, 438, 438, 1, 518400, 0xbab197ea +0, 428, 428, 1, 518400, 0x01f80e9d +0, 429, 429, 1, 518400, 0x01f80e9d +0, 430, 430, 1, 518400, 0x01f80e9d +0, 431, 431, 1, 518400, 0x01f80e9d +0, 432, 432, 1, 518400, 0x01f80e9d +0, 433, 433, 1, 518400, 0x01f80e9d +0, 434, 434, 1, 518400, 0x01f80e9d +0, 435, 435, 1, 518400, 0x01f80e9d +0, 436, 436, 1, 518400, 0x01f80e9d +0, 437, 437, 1, 518400, 0x01f80e9d +0, 438, 438, 1, 518400, 0x01f80e9d 1, 87652000, 87652000, 1831000, 634, 0x8832fda1 0, 439, 439, 1, 518400, 0xb48d90c0 -0, 447, 447, 1, 518400, 0xbab197ea +0, 440, 440, 1, 518400, 0xb48d90c0 +0, 441, 441, 1, 518400, 0xb48d90c0 +0, 442, 442, 1, 518400, 0xb48d90c0 +0, 443, 443, 1, 518400, 0xb48d90c0 +0, 444, 444, 1, 518400, 0xb48d90c0 +0, 445, 445, 1, 518400, 0xb48d90c0 +0, 446, 446, 1, 518400, 0xb48d90c0 +0, 447, 447, 1, 518400, 0xb48d90c0 +0, 448, 448, 1, 518400, 0xbab197ea +0, 449, 449, 1, 518400, 0xbab197ea +0, 450, 450, 1, 518400, 0xbab197ea +0, 451, 451, 1, 518400, 0xbab197ea +0, 452, 452, 1, 518400, 0xbab197ea +0, 453, 453, 1, 518400, 0xbab197ea +0, 454, 454, 1, 518400, 0xbab197ea +0, 455, 455, 1, 518400, 0xbab197ea +0, 456, 456, 1, 518400, 0xbab197ea +0, 457, 457, 1, 518400, 0xbab197ea 1, 91531000, 91531000, 2332000, 2080, 0x97a1146f 0, 458, 458, 1, 518400, 0xcb5a0173 -0, 469, 469, 1, 518400, 0xbab197ea +0, 459, 459, 1, 518400, 0xcb5a0173 +0, 460, 460, 1, 518400, 0xcb5a0173 +0, 461, 461, 1, 518400, 0xcb5a0173 +0, 462, 462, 1, 518400, 0xcb5a0173 +0, 463, 463, 1, 518400, 0xcb5a0173 +0, 464, 464, 1, 518400, 0xcb5a0173 +0, 465, 465, 1, 518400, 0xcb5a0173 +0, 466, 466, 1, 518400, 0xcb5a0173 +0, 467, 467, 1, 518400, 0xcb5a0173 +0, 468, 468, 1, 518400, 0xcb5a0173 +0, 469, 469, 1, 518400, 0xcb5a0173 +0, 470, 470, 1, 518400, 0xbab197ea +0, 471, 471, 1, 518400, 0xbab197ea +0, 472, 472, 1, 518400, 0xbab197ea +0, 473, 473, 1, 518400, 0xbab197ea +0, 474, 474, 1, 518400, 0xbab197ea +0, 475, 475, 1, 518400, 0xbab197ea +0, 476, 476, 1, 518400, 0xbab197ea +0, 477, 477, 1, 518400, 0xbab197ea 1, 95510000, 95510000, 3299000, 2964, 0x8b8f6684 0, 478, 478, 1, 518400, 0xb8a323e4 -0, 494, 494, 1, 518400, 0xbab197ea +0, 479, 479, 1, 518400, 0xb8a323e4 +0, 480, 480, 1, 518400, 0xb8a323e4 +0, 481, 481, 1, 518400, 0xb8a323e4 +0, 482, 482, 1, 518400, 0xb8a323e4 +0, 483, 483, 1, 518400, 0xb8a323e4 +0, 484, 484, 1, 518400, 0xb8a323e4 +0, 485, 485, 1, 518400, 0xb8a323e4 +0, 486, 486, 1, 518400, 0xb8a323e4 +0, 487, 487, 1, 518400, 0xb8a323e4 +0, 488, 488, 1, 518400, 0xb8a323e4 +0, 489, 489, 1, 518400, 0xb8a323e4 +0, 490, 490, 1, 518400, 0xb8a323e4 +0, 491, 491, 1, 518400, 0xb8a323e4 +0, 492, 492, 1, 518400, 0xb8a323e4 +0, 493, 493, 1, 518400, 0xb8a323e4 +0, 494, 494, 1, 518400, 0xb8a323e4 1, 98872000, 98872000, 2161000, 1875, 0x9002ef71 0, 495, 495, 1, 518400, 0xc43518ba -0, 505, 505, 1, 518400, 0xbab197ea +0, 496, 496, 1, 518400, 0xc43518ba +0, 497, 497, 1, 518400, 0xc43518ba +0, 498, 498, 1, 518400, 0xc43518ba +0, 499, 499, 1, 518400, 0xc43518ba +0, 500, 500, 1, 518400, 0xc43518ba +0, 501, 501, 1, 518400, 0xc43518ba +0, 502, 502, 1, 518400, 0xc43518ba +0, 503, 503, 1, 518400, 0xc43518ba +0, 504, 504, 1, 518400, 0xc43518ba +0, 505, 505, 1, 518400, 0xc43518ba 1, 101124000, 101124000, 4096000, 3872, 0x20c6ed9c 0, 506, 506, 1, 518400, 0x04e38692 -0, 526, 526, 1, 518400, 0xbab197ea +0, 507, 507, 1, 518400, 0x04e38692 +0, 508, 508, 1, 518400, 0x04e38692 +0, 509, 509, 1, 518400, 0x04e38692 +0, 510, 510, 1, 518400, 0x04e38692 +0, 511, 511, 1, 518400, 0x04e38692 +0, 512, 512, 1, 518400, 0x04e38692 +0, 513, 513, 1, 518400, 0x04e38692 +0, 514, 514, 1, 518400, 0x04e38692 +0, 515, 515, 1, 518400, 0x04e38692 +0, 516, 516, 1, 518400, 0x04e38692 +0, 517, 517, 1, 518400, 0x04e38692 +0, 518, 518, 1, 518400, 0x04e38692 +0, 519, 519, 1, 518400, 0x04e38692 +0, 520, 520, 1, 518400, 0x04e38692 +0, 521, 521, 1, 518400, 0x04e38692 +0, 522, 522, 1, 518400, 0x04e38692 +0, 523, 523, 1, 518400, 0x04e38692 +0, 524, 524, 1, 518400, 0x04e38692 +0, 525, 525, 1, 518400, 0x04e38692 +0, 526, 526, 1, 518400, 0x04e38692 1, 105303000, 105303000, 2730000, 3094, 0xf203a663 0, 527, 527, 1, 518400, 0x856b0ee5 -0, 540, 540, 1, 518400, 0xbab197ea +0, 528, 528, 1, 518400, 0x856b0ee5 +0, 529, 529, 1, 518400, 0x856b0ee5 +0, 530, 530, 1, 518400, 0x856b0ee5 +0, 531, 531, 1, 518400, 0x856b0ee5 +0, 532, 532, 1, 518400, 0x856b0ee5 +0, 533, 533, 1, 518400, 0x856b0ee5 +0, 534, 534, 1, 518400, 0x856b0ee5 +0, 535, 535, 1, 518400, 0x856b0ee5 +0, 536, 536, 1, 518400, 0x856b0ee5 +0, 537, 537, 1, 518400, 0x856b0ee5 +0, 538, 538, 1, 518400, 0x856b0ee5 +0, 539, 539, 1, 518400, 0x856b0ee5 +0, 540, 540, 1, 518400, 0x856b0ee5 1, 108106000, 108106000, 2059000, 2404, 0x41a7b429 0, 541, 541, 1, 518400, 0x3e5beee2 -0, 551, 551, 1, 518400, 0xbab197ea +0, 542, 542, 1, 518400, 0x3e5beee2 +0, 543, 543, 1, 518400, 0x3e5beee2 +0, 544, 544, 1, 518400, 0x3e5beee2 +0, 545, 545, 1, 518400, 0x3e5beee2 +0, 546, 546, 1, 518400, 0x3e5beee2 +0, 547, 547, 1, 518400, 0x3e5beee2 +0, 548, 548, 1, 518400, 0x3e5beee2 +0, 549, 549, 1, 518400, 0x3e5beee2 +0, 550, 550, 1, 518400, 0x3e5beee2 +0, 551, 551, 1, 518400, 0x3e5beee2 +0, 552, 552, 1, 518400, 0xbab197ea +0, 553, 553, 1, 518400, 0xbab197ea +0, 554, 554, 1, 518400, 0xbab197ea +0, 555, 555, 1, 518400, 0xbab197ea +0, 556, 556, 1, 518400, 0xbab197ea +0, 557, 557, 1, 518400, 0xbab197ea +0, 558, 558, 1, 518400, 0xbab197ea +0, 559, 559, 1, 518400, 0xbab197ea +0, 560, 560, 1, 518400, 0xbab197ea +0, 561, 561, 1, 518400, 0xbab197ea +0, 562, 562, 1, 518400, 0xbab197ea +0, 563, 563, 1, 518400, 0xbab197ea +0, 564, 564, 1, 518400, 0xbab197ea +0, 565, 565, 1, 518400, 0xbab197ea +0, 566, 566, 1, 518400, 0xbab197ea +0, 567, 567, 1, 518400, 0xbab197ea +0, 568, 568, 1, 518400, 0xbab197ea +0, 569, 569, 1, 518400, 0xbab197ea +0, 570, 570, 1, 518400, 0xbab197ea +0, 571, 571, 1, 518400, 0xbab197ea +0, 572, 572, 1, 518400, 0xbab197ea +0, 573, 573, 1, 518400, 0xbab197ea +0, 574, 574, 1, 518400, 0xbab197ea +0, 575, 575, 1, 518400, 0xbab197ea +0, 576, 576, 1, 518400, 0xbab197ea +0, 577, 577, 1, 518400, 0xbab197ea +0, 578, 578, 1, 518400, 0xbab197ea +0, 579, 579, 1, 518400, 0xbab197ea +0, 580, 580, 1, 518400, 0xbab197ea +0, 581, 581, 1, 518400, 0xbab197ea +0, 582, 582, 1, 518400, 0xbab197ea +0, 583, 583, 1, 518400, 0xbab197ea +0, 584, 584, 1, 518400, 0xbab197ea +0, 585, 585, 1, 518400, 0xbab197ea +0, 586, 586, 1, 518400, 0xbab197ea +0, 587, 587, 1, 518400, 0xbab197ea +0, 588, 588, 1, 518400, 0xbab197ea +0, 589, 589, 1, 518400, 0xbab197ea +0, 590, 590, 1, 518400, 0xbab197ea +0, 591, 591, 1, 518400, 0xbab197ea +0, 592, 592, 1, 518400, 0xbab197ea +0, 593, 593, 1, 518400, 0xbab197ea +0, 594, 594, 1, 518400, 0xbab197ea +0, 595, 595, 1, 518400, 0xbab197ea +0, 596, 596, 1, 518400, 0xbab197ea +0, 597, 597, 1, 518400, 0xbab197ea +0, 598, 598, 1, 518400, 0xbab197ea +0, 599, 599, 1, 518400, 0xbab197ea +0, 600, 600, 1, 518400, 0xbab197ea +0, 601, 601, 1, 518400, 0xbab197ea +0, 602, 602, 1, 518400, 0xbab197ea +0, 603, 603, 1, 518400, 0xbab197ea +0, 604, 604, 1, 518400, 0xbab197ea +0, 605, 605, 1, 518400, 0xbab197ea +0, 606, 606, 1, 518400, 0xbab197ea +0, 607, 607, 1, 518400, 0xbab197ea +0, 608, 608, 1, 518400, 0xbab197ea +0, 609, 609, 1, 518400, 0xbab197ea +0, 610, 610, 1, 518400, 0xbab197ea +0, 611, 611, 1, 518400, 0xbab197ea +0, 612, 612, 1, 518400, 0xbab197ea +0, 613, 613, 1, 518400, 0xbab197ea +0, 614, 614, 1, 518400, 0xbab197ea +0, 615, 615, 1, 518400, 0xbab197ea +0, 616, 616, 1, 518400, 0xbab197ea +0, 617, 617, 1, 518400, 0xbab197ea +0, 618, 618, 1, 518400, 0xbab197ea +0, 619, 619, 1, 518400, 0xbab197ea +0, 620, 620, 1, 518400, 0xbab197ea +0, 621, 621, 1, 518400, 0xbab197ea +0, 622, 622, 1, 518400, 0xbab197ea +0, 623, 623, 1, 518400, 0xbab197ea +0, 624, 624, 1, 518400, 0xbab197ea +0, 625, 625, 1, 518400, 0xbab197ea +0, 626, 626, 1, 518400, 0xbab197ea +0, 627, 627, 1, 518400, 0xbab197ea +0, 628, 628, 1, 518400, 0xbab197ea +0, 629, 629, 1, 518400, 0xbab197ea +0, 630, 630, 1, 518400, 0xbab197ea +0, 631, 631, 1, 518400, 0xbab197ea +0, 632, 632, 1, 518400, 0xbab197ea +0, 633, 633, 1, 518400, 0xbab197ea +0, 634, 634, 1, 518400, 0xbab197ea +0, 635, 635, 1, 518400, 0xbab197ea +0, 636, 636, 1, 518400, 0xbab197ea +0, 637, 637, 1, 518400, 0xbab197ea +0, 638, 638, 1, 518400, 0xbab197ea +0, 639, 639, 1, 518400, 0xbab197ea +0, 640, 640, 1, 518400, 0xbab197ea +0, 641, 641, 1, 518400, 0xbab197ea +0, 642, 642, 1, 518400, 0xbab197ea +0, 643, 643, 1, 518400, 0xbab197ea +0, 644, 644, 1, 518400, 0xbab197ea +0, 645, 645, 1, 518400, 0xbab197ea +0, 646, 646, 1, 518400, 0xbab197ea +0, 647, 647, 1, 518400, 0xbab197ea +0, 648, 648, 1, 518400, 0xbab197ea +0, 649, 649, 1, 518400, 0xbab197ea +0, 650, 650, 1, 518400, 0xbab197ea +0, 651, 651, 1, 518400, 0xbab197ea +0, 652, 652, 1, 518400, 0xbab197ea +0, 653, 653, 1, 518400, 0xbab197ea +0, 654, 654, 1, 518400, 0xbab197ea +0, 655, 655, 1, 518400, 0xbab197ea +0, 656, 656, 1, 518400, 0xbab197ea +0, 657, 657, 1, 518400, 0xbab197ea +0, 658, 658, 1, 518400, 0xbab197ea +0, 659, 659, 1, 518400, 0xbab197ea +0, 660, 660, 1, 518400, 0xbab197ea +0, 661, 661, 1, 518400, 0xbab197ea +0, 662, 662, 1, 518400, 0xbab197ea +0, 663, 663, 1, 518400, 0xbab197ea +0, 664, 664, 1, 518400, 0xbab197ea +0, 665, 665, 1, 518400, 0xbab197ea +0, 666, 666, 1, 518400, 0xbab197ea +0, 667, 667, 1, 518400, 0xbab197ea +0, 668, 668, 1, 518400, 0xbab197ea +0, 669, 669, 1, 518400, 0xbab197ea +0, 670, 670, 1, 518400, 0xbab197ea +0, 671, 671, 1, 518400, 0xbab197ea +0, 672, 672, 1, 518400, 0xbab197ea +0, 673, 673, 1, 518400, 0xbab197ea +0, 674, 674, 1, 518400, 0xbab197ea +0, 675, 675, 1, 518400, 0xbab197ea +0, 676, 676, 1, 518400, 0xbab197ea +0, 677, 677, 1, 518400, 0xbab197ea +0, 678, 678, 1, 518400, 0xbab197ea +0, 679, 679, 1, 518400, 0xbab197ea +0, 680, 680, 1, 518400, 0xbab197ea +0, 681, 681, 1, 518400, 0xbab197ea +0, 682, 682, 1, 518400, 0xbab197ea +0, 683, 683, 1, 518400, 0xbab197ea +0, 684, 684, 1, 518400, 0xbab197ea +0, 685, 685, 1, 518400, 0xbab197ea +0, 686, 686, 1, 518400, 0xbab197ea +0, 687, 687, 1, 518400, 0xbab197ea +0, 688, 688, 1, 518400, 0xbab197ea +0, 689, 689, 1, 518400, 0xbab197ea +0, 690, 690, 1, 518400, 0xbab197ea +0, 691, 691, 1, 518400, 0xbab197ea +0, 692, 692, 1, 518400, 0xbab197ea +0, 693, 693, 1, 518400, 0xbab197ea +0, 694, 694, 1, 518400, 0xbab197ea +0, 695, 695, 1, 518400, 0xbab197ea +0, 696, 696, 1, 518400, 0xbab197ea +0, 697, 697, 1, 518400, 0xbab197ea +0, 698, 698, 1, 518400, 0xbab197ea +0, 699, 699, 1, 518400, 0xbab197ea +0, 700, 700, 1, 518400, 0xbab197ea +0, 701, 701, 1, 518400, 0xbab197ea +0, 702, 702, 1, 518400, 0xbab197ea +0, 703, 703, 1, 518400, 0xbab197ea +0, 704, 704, 1, 518400, 0xbab197ea +0, 705, 705, 1, 518400, 0xbab197ea +0, 706, 706, 1, 518400, 0xbab197ea +0, 707, 707, 1, 518400, 0xbab197ea 1, 141556000, 141556000, 1661000, 1088, 0xde20aa20 0, 708, 708, 1, 518400, 0xb8bc1365 -0, 716, 716, 1, 518400, 0xbab197ea +0, 709, 709, 1, 518400, 0xb8bc1365 +0, 710, 710, 1, 518400, 0xb8bc1365 +0, 711, 711, 1, 518400, 0xb8bc1365 +0, 712, 712, 1, 518400, 0xb8bc1365 +0, 713, 713, 1, 518400, 0xb8bc1365 +0, 714, 714, 1, 518400, 0xb8bc1365 +0, 715, 715, 1, 518400, 0xb8bc1365 +0, 716, 716, 1, 518400, 0xb8bc1365 +0, 717, 717, 1, 518400, 0xbab197ea +0, 718, 718, 1, 518400, 0xbab197ea +0, 719, 719, 1, 518400, 0xbab197ea +0, 720, 720, 1, 518400, 0xbab197ea +0, 721, 721, 1, 518400, 0xbab197ea +0, 722, 722, 1, 518400, 0xbab197ea +0, 723, 723, 1, 518400, 0xbab197ea +0, 724, 724, 1, 518400, 0xbab197ea +0, 725, 725, 1, 518400, 0xbab197ea +0, 726, 726, 1, 518400, 0xbab197ea +0, 727, 727, 1, 518400, 0xbab197ea +0, 728, 728, 1, 518400, 0xbab197ea +0, 729, 729, 1, 518400, 0xbab197ea +0, 730, 730, 1, 518400, 0xbab197ea +0, 731, 731, 1, 518400, 0xbab197ea +0, 732, 732, 1, 518400, 0xbab197ea +0, 733, 733, 1, 518400, 0xbab197ea +0, 734, 734, 1, 518400, 0xbab197ea +0, 735, 735, 1, 518400, 0xbab197ea +0, 736, 736, 1, 518400, 0xbab197ea +0, 737, 737, 1, 518400, 0xbab197ea +0, 738, 738, 1, 518400, 0xbab197ea +0, 739, 739, 1, 518400, 0xbab197ea +0, 740, 740, 1, 518400, 0xbab197ea +0, 741, 741, 1, 518400, 0xbab197ea +0, 742, 742, 1, 518400, 0xbab197ea +0, 743, 743, 1, 518400, 0xbab197ea +0, 744, 744, 1, 518400, 0xbab197ea +0, 745, 745, 1, 518400, 0xbab197ea +0, 746, 746, 1, 518400, 0xbab197ea +0, 747, 747, 1, 518400, 0xbab197ea +0, 748, 748, 1, 518400, 0xbab197ea +0, 749, 749, 1, 518400, 0xbab197ea +0, 750, 750, 1, 518400, 0xbab197ea +0, 751, 751, 1, 518400, 0xbab197ea +0, 752, 752, 1, 518400, 0xbab197ea +0, 753, 753, 1, 518400, 0xbab197ea +0, 754, 754, 1, 518400, 0xbab197ea +0, 755, 755, 1, 518400, 0xbab197ea +0, 756, 756, 1, 518400, 0xbab197ea +0, 757, 757, 1, 518400, 0xbab197ea +0, 758, 758, 1, 518400, 0xbab197ea +0, 759, 759, 1, 518400, 0xbab197ea +0, 760, 760, 1, 518400, 0xbab197ea +0, 761, 761, 1, 518400, 0xbab197ea +0, 762, 762, 1, 518400, 0xbab197ea +0, 763, 763, 1, 518400, 0xbab197ea +0, 764, 764, 1, 518400, 0xbab197ea +0, 765, 765, 1, 518400, 0xbab197ea +0, 766, 766, 1, 518400, 0xbab197ea +0, 767, 767, 1, 518400, 0xbab197ea +0, 768, 768, 1, 518400, 0xbab197ea +0, 769, 769, 1, 518400, 0xbab197ea +0, 770, 770, 1, 518400, 0xbab197ea +0, 771, 771, 1, 518400, 0xbab197ea +0, 772, 772, 1, 518400, 0xbab197ea +0, 773, 773, 1, 518400, 0xbab197ea +0, 774, 774, 1, 518400, 0xbab197ea +0, 775, 775, 1, 518400, 0xbab197ea +0, 776, 776, 1, 518400, 0xbab197ea +0, 777, 777, 1, 518400, 0xbab197ea +0, 778, 778, 1, 518400, 0xbab197ea +0, 779, 779, 1, 518400, 0xbab197ea +0, 780, 780, 1, 518400, 0xbab197ea +0, 781, 781, 1, 518400, 0xbab197ea +0, 782, 782, 1, 518400, 0xbab197ea +0, 783, 783, 1, 518400, 0xbab197ea +0, 784, 784, 1, 518400, 0xbab197ea +0, 785, 785, 1, 518400, 0xbab197ea +0, 786, 786, 1, 518400, 0xbab197ea +0, 787, 787, 1, 518400, 0xbab197ea +0, 788, 788, 1, 518400, 0xbab197ea +0, 789, 789, 1, 518400, 0xbab197ea +0, 790, 790, 1, 518400, 0xbab197ea +0, 791, 791, 1, 518400, 0xbab197ea +0, 792, 792, 1, 518400, 0xbab197ea +0, 793, 793, 1, 518400, 0xbab197ea +0, 794, 794, 1, 518400, 0xbab197ea +0, 795, 795, 1, 518400, 0xbab197ea +0, 796, 796, 1, 518400, 0xbab197ea +0, 797, 797, 1, 518400, 0xbab197ea +0, 798, 798, 1, 518400, 0xbab197ea +0, 799, 799, 1, 518400, 0xbab197ea +0, 800, 800, 1, 518400, 0xbab197ea +0, 801, 801, 1, 518400, 0xbab197ea +0, 802, 802, 1, 518400, 0xbab197ea +0, 803, 803, 1, 518400, 0xbab197ea +0, 804, 804, 1, 518400, 0xbab197ea +0, 805, 805, 1, 518400, 0xbab197ea +0, 806, 806, 1, 518400, 0xbab197ea +0, 807, 807, 1, 518400, 0xbab197ea +0, 808, 808, 1, 518400, 0xbab197ea +0, 809, 809, 1, 518400, 0xbab197ea +0, 810, 810, 1, 518400, 0xbab197ea +0, 811, 811, 1, 518400, 0xbab197ea +0, 812, 812, 1, 518400, 0xbab197ea +0, 813, 813, 1, 518400, 0xbab197ea +0, 814, 814, 1, 518400, 0xbab197ea +0, 815, 815, 1, 518400, 0xbab197ea +0, 816, 816, 1, 518400, 0xbab197ea 0, 817, 817, 1, 518400, 0x83efa32d 1, 163445000, 163445000, 1331000, 339, 0x8bd186ef -0, 824, 824, 1, 518400, 0xbab197ea +0, 818, 818, 1, 518400, 0x83efa32d +0, 819, 819, 1, 518400, 0x83efa32d +0, 820, 820, 1, 518400, 0x83efa32d +0, 821, 821, 1, 518400, 0x83efa32d +0, 822, 822, 1, 518400, 0x83efa32d +0, 823, 823, 1, 518400, 0x83efa32d +0, 824, 824, 1, 518400, 0x83efa32d +0, 825, 825, 1, 518400, 0xbab197ea +0, 826, 826, 1, 518400, 0xbab197ea +0, 827, 827, 1, 518400, 0xbab197ea +0, 828, 828, 1, 518400, 0xbab197ea +0, 829, 829, 1, 518400, 0xbab197ea +0, 830, 830, 1, 518400, 0xbab197ea +0, 831, 831, 1, 518400, 0xbab197ea +0, 832, 832, 1, 518400, 0xbab197ea +0, 833, 833, 1, 518400, 0xbab197ea +0, 834, 834, 1, 518400, 0xbab197ea +0, 835, 835, 1, 518400, 0xbab197ea +0, 836, 836, 1, 518400, 0xbab197ea +0, 837, 837, 1, 518400, 0xbab197ea +0, 838, 838, 1, 518400, 0xbab197ea +0, 839, 839, 1, 518400, 0xbab197ea 0, 840, 840, 1, 518400, 0x03ea0e90 1, 168049000, 168049000, 1900000, 1312, 0x0bf20e8d -0, 850, 850, 1, 518400, 0xbab197ea +0, 841, 841, 1, 518400, 0x03ea0e90 +0, 842, 842, 1, 518400, 0x03ea0e90 +0, 843, 843, 1, 518400, 0x03ea0e90 +0, 844, 844, 1, 518400, 0x03ea0e90 +0, 845, 845, 1, 518400, 0x03ea0e90 +0, 846, 846, 1, 518400, 0x03ea0e90 +0, 847, 847, 1, 518400, 0x03ea0e90 +0, 848, 848, 1, 518400, 0x03ea0e90 +0, 849, 849, 1, 518400, 0x03ea0e90 +0, 850, 850, 1, 518400, 0x03ea0e90 1, 170035000, 170035000, 1524000, 1279, 0xb6c2dafe 0, 851, 851, 1, 518400, 0x8780239e -0, 858, 858, 1, 518400, 0xbab197ea +0, 852, 852, 1, 518400, 0x8780239e +0, 853, 853, 1, 518400, 0x8780239e +0, 854, 854, 1, 518400, 0x8780239e +0, 855, 855, 1, 518400, 0x8780239e +0, 856, 856, 1, 518400, 0x8780239e +0, 857, 857, 1, 518400, 0x8780239e +0, 858, 858, 1, 518400, 0x8780239e +0, 859, 859, 1, 518400, 0xbab197ea +0, 860, 860, 1, 518400, 0xbab197ea 0, 861, 861, 1, 518400, 0x6eb72347 1, 172203000, 172203000, 1695000, 1826, 0x9a1ac769 -0, 869, 869, 1, 518400, 0xbab197ea +0, 862, 862, 1, 518400, 0x6eb72347 +0, 863, 863, 1, 518400, 0x6eb72347 +0, 864, 864, 1, 518400, 0x6eb72347 +0, 865, 865, 1, 518400, 0x6eb72347 +0, 866, 866, 1, 518400, 0x6eb72347 +0, 867, 867, 1, 518400, 0x6eb72347 +0, 868, 868, 1, 518400, 0x6eb72347 +0, 869, 869, 1, 518400, 0x6eb72347 1, 173947000, 173947000, 1934000, 1474, 0xa9b03cdc 0, 870, 870, 1, 518400, 0x9c4a3a3d -0, 879, 879, 1, 518400, 0xbab197ea +0, 871, 871, 1, 518400, 0x9c4a3a3d +0, 872, 872, 1, 518400, 0x9c4a3a3d +0, 873, 873, 1, 518400, 0x9c4a3a3d +0, 874, 874, 1, 518400, 0x9c4a3a3d +0, 875, 875, 1, 518400, 0x9c4a3a3d +0, 876, 876, 1, 518400, 0x9c4a3a3d +0, 877, 877, 1, 518400, 0x9c4a3a3d +0, 878, 878, 1, 518400, 0x9c4a3a3d +0, 879, 879, 1, 518400, 0x9c4a3a3d 1, 175957000, 175957000, 1763000, 1019, 0x20409355 0, 880, 880, 1, 518400, 0xc9ebfa89 -0, 889, 889, 1, 518400, 0xbab197ea +0, 881, 881, 1, 518400, 0xc9ebfa89 +0, 882, 882, 1, 518400, 0xc9ebfa89 +0, 883, 883, 1, 518400, 0xc9ebfa89 +0, 884, 884, 1, 518400, 0xc9ebfa89 +0, 885, 885, 1, 518400, 0xc9ebfa89 +0, 886, 886, 1, 518400, 0xc9ebfa89 +0, 887, 887, 1, 518400, 0xc9ebfa89 +0, 888, 888, 1, 518400, 0xc9ebfa89 +0, 889, 889, 1, 518400, 0xc9ebfa89 +0, 890, 890, 1, 518400, 0xbab197ea +0, 891, 891, 1, 518400, 0xbab197ea +0, 892, 892, 1, 518400, 0xbab197ea +0, 893, 893, 1, 518400, 0xbab197ea +0, 894, 894, 1, 518400, 0xbab197ea +0, 895, 895, 1, 518400, 0xbab197ea +0, 896, 896, 1, 518400, 0xbab197ea +0, 897, 897, 1, 518400, 0xbab197ea +0, 898, 898, 1, 518400, 0xbab197ea +0, 899, 899, 1, 518400, 0xbab197ea +0, 900, 900, 1, 518400, 0xbab197ea +0, 901, 901, 1, 518400, 0xbab197ea +0, 902, 902, 1, 518400, 0xbab197ea +0, 903, 903, 1, 518400, 0xbab197ea +0, 904, 904, 1, 518400, 0xbab197ea +0, 905, 905, 1, 518400, 0xbab197ea +0, 906, 906, 1, 518400, 0xbab197ea +0, 907, 907, 1, 518400, 0xbab197ea +0, 908, 908, 1, 518400, 0xbab197ea +0, 909, 909, 1, 518400, 0xbab197ea +0, 910, 910, 1, 518400, 0xbab197ea +0, 911, 911, 1, 518400, 0xbab197ea +0, 912, 912, 1, 518400, 0xbab197ea +0, 913, 913, 1, 518400, 0xbab197ea +0, 914, 914, 1, 518400, 0xbab197ea +0, 915, 915, 1, 518400, 0xbab197ea +0, 916, 916, 1, 518400, 0xbab197ea +0, 917, 917, 1, 518400, 0xbab197ea +0, 918, 918, 1, 518400, 0xbab197ea +0, 919, 919, 1, 518400, 0xbab197ea +0, 920, 920, 1, 518400, 0xbab197ea +0, 921, 921, 1, 518400, 0xbab197ea +0, 922, 922, 1, 518400, 0xbab197ea +0, 923, 923, 1, 518400, 0xbab197ea +0, 924, 924, 1, 518400, 0xbab197ea +0, 925, 925, 1, 518400, 0xbab197ea +0, 926, 926, 1, 518400, 0xbab197ea +0, 927, 927, 1, 518400, 0xbab197ea +0, 928, 928, 1, 518400, 0xbab197ea +0, 929, 929, 1, 518400, 0xbab197ea +0, 930, 930, 1, 518400, 0xbab197ea +0, 931, 931, 1, 518400, 0xbab197ea +0, 932, 932, 1, 518400, 0xbab197ea +0, 933, 933, 1, 518400, 0xbab197ea +0, 934, 934, 1, 518400, 0xbab197ea +0, 935, 935, 1, 518400, 0xbab197ea +0, 936, 936, 1, 518400, 0xbab197ea +0, 937, 937, 1, 518400, 0xbab197ea +0, 938, 938, 1, 518400, 0xbab197ea +0, 939, 939, 1, 518400, 0xbab197ea +0, 940, 940, 1, 518400, 0xbab197ea +0, 941, 941, 1, 518400, 0xbab197ea +0, 942, 942, 1, 518400, 0xbab197ea +0, 943, 943, 1, 518400, 0xbab197ea +0, 944, 944, 1, 518400, 0xbab197ea +0, 945, 945, 1, 518400, 0xbab197ea 0, 946, 946, 1, 518400, 0xbaf801ef 1, 189295000, 189295000, 1968000, 1596, 0x408c726e -0, 956, 956, 1, 518400, 0xbab197ea +0, 947, 947, 1, 518400, 0xbaf801ef +0, 948, 948, 1, 518400, 0xbaf801ef +0, 949, 949, 1, 518400, 0xbaf801ef +0, 950, 950, 1, 518400, 0xbaf801ef +0, 951, 951, 1, 518400, 0xbaf801ef +0, 952, 952, 1, 518400, 0xbaf801ef +0, 953, 953, 1, 518400, 0xbaf801ef +0, 954, 954, 1, 518400, 0xbaf801ef +0, 955, 955, 1, 518400, 0xbaf801ef +0, 956, 956, 1, 518400, 0xbaf801ef 1, 191356000, 191356000, 1228000, 1517, 0xae8c5c2b 0, 957, 957, 1, 518400, 0x59f4e72f -0, 963, 963, 1, 518400, 0xbab197ea +0, 958, 958, 1, 518400, 0x59f4e72f +0, 959, 959, 1, 518400, 0x59f4e72f +0, 960, 960, 1, 518400, 0x59f4e72f +0, 961, 961, 1, 518400, 0x59f4e72f +0, 962, 962, 1, 518400, 0x59f4e72f +0, 963, 963, 1, 518400, 0x59f4e72f 1, 192640000, 192640000, 1763000, 2506, 0xa458d6d4 0, 964, 964, 1, 518400, 0x9d5b9d69 -0, 972, 972, 1, 518400, 0xbab197ea +0, 965, 965, 1, 518400, 0x9d5b9d69 +0, 966, 966, 1, 518400, 0x9d5b9d69 +0, 967, 967, 1, 518400, 0x9d5b9d69 +0, 968, 968, 1, 518400, 0x9d5b9d69 +0, 969, 969, 1, 518400, 0x9d5b9d69 +0, 970, 970, 1, 518400, 0x9d5b9d69 +0, 971, 971, 1, 518400, 0x9d5b9d69 +0, 972, 972, 1, 518400, 0x9d5b9d69 +0, 973, 973, 1, 518400, 0xbab197ea +0, 974, 974, 1, 518400, 0xbab197ea +0, 975, 975, 1, 518400, 0xbab197ea 1, 195193000, 195193000, 1092000, 1074, 0x397ba9a8 0, 976, 976, 1, 518400, 0x923d1ce7 -0, 981, 981, 1, 518400, 0xbab197ea +0, 977, 977, 1, 518400, 0x923d1ce7 +0, 978, 978, 1, 518400, 0x923d1ce7 +0, 979, 979, 1, 518400, 0x923d1ce7 +0, 980, 980, 1, 518400, 0x923d1ce7 +0, 981, 981, 1, 518400, 0x923d1ce7 1, 196361000, 196361000, 1524000, 1715, 0x695ca41e 0, 982, 982, 1, 518400, 0x6e652cd2 -0, 989, 989, 1, 518400, 0xbab197ea +0, 983, 983, 1, 518400, 0x6e652cd2 +0, 984, 984, 1, 518400, 0x6e652cd2 +0, 985, 985, 1, 518400, 0x6e652cd2 +0, 986, 986, 1, 518400, 0x6e652cd2 +0, 987, 987, 1, 518400, 0x6e652cd2 +0, 988, 988, 1, 518400, 0x6e652cd2 +0, 989, 989, 1, 518400, 0x6e652cd2 1, 197946000, 197946000, 1160000, 789, 0xc63a189e 0, 990, 990, 1, 518400, 0x25113966 -0, 996, 996, 1, 518400, 0xbab197ea +0, 991, 991, 1, 518400, 0x25113966 +0, 992, 992, 1, 518400, 0x25113966 +0, 993, 993, 1, 518400, 0x25113966 +0, 994, 994, 1, 518400, 0x25113966 +0, 995, 995, 1, 518400, 0x25113966 +0, 996, 996, 1, 518400, 0x25113966 1, 199230000, 199230000, 1627000, 1846, 0xeea8c599 0, 997, 997, 1, 518400, 0x2dc83609 -0, 1004, 1004, 1, 518400, 0xbab197ea +0, 998, 998, 1, 518400, 0x2dc83609 +0, 999, 999, 1, 518400, 0x2dc83609 +0, 1000, 1000, 1, 518400, 0x2dc83609 +0, 1001, 1001, 1, 518400, 0x2dc83609 +0, 1002, 1002, 1, 518400, 0x2dc83609 +0, 1003, 1003, 1, 518400, 0x2dc83609 +0, 1004, 1004, 1, 518400, 0x2dc83609 1, 200924000, 200924000, 1763000, 922, 0xd4a87222 0, 1005, 1005, 1, 518400, 0x90483bc6 -0, 1013, 1013, 1, 518400, 0xbab197ea +0, 1006, 1006, 1, 518400, 0x90483bc6 +0, 1007, 1007, 1, 518400, 0x90483bc6 +0, 1008, 1008, 1, 518400, 0x90483bc6 +0, 1009, 1009, 1, 518400, 0x90483bc6 +0, 1010, 1010, 1, 518400, 0x90483bc6 +0, 1011, 1011, 1, 518400, 0x90483bc6 +0, 1012, 1012, 1, 518400, 0x90483bc6 +0, 1013, 1013, 1, 518400, 0x90483bc6 +0, 1014, 1014, 1, 518400, 0xbab197ea +0, 1015, 1015, 1, 518400, 0xbab197ea +0, 1016, 1016, 1, 518400, 0xbab197ea +0, 1017, 1017, 1, 518400, 0xbab197ea +0, 1018, 1018, 1, 518400, 0xbab197ea +0, 1019, 1019, 1, 518400, 0xbab197ea +0, 1020, 1020, 1, 518400, 0xbab197ea +0, 1021, 1021, 1, 518400, 0xbab197ea +0, 1022, 1022, 1, 518400, 0xbab197ea +0, 1023, 1023, 1, 518400, 0xbab197ea +0, 1024, 1024, 1, 518400, 0xbab197ea +0, 1025, 1025, 1, 518400, 0xbab197ea +0, 1026, 1026, 1, 518400, 0xbab197ea +0, 1027, 1027, 1, 518400, 0xbab197ea +0, 1028, 1028, 1, 518400, 0xbab197ea +0, 1029, 1029, 1, 518400, 0xbab197ea +0, 1030, 1030, 1, 518400, 0xbab197ea +0, 1031, 1031, 1, 518400, 0xbab197ea +0, 1032, 1032, 1, 518400, 0xbab197ea +0, 1033, 1033, 1, 518400, 0xbab197ea +0, 1034, 1034, 1, 518400, 0xbab197ea +0, 1035, 1035, 1, 518400, 0xbab197ea +0, 1036, 1036, 1, 518400, 0xbab197ea +0, 1037, 1037, 1, 518400, 0xbab197ea +0, 1038, 1038, 1, 518400, 0xbab197ea +0, 1039, 1039, 1, 518400, 0xbab197ea +0, 1040, 1040, 1, 518400, 0xbab197ea +0, 1041, 1041, 1, 518400, 0xbab197ea +0, 1042, 1042, 1, 518400, 0xbab197ea +0, 1043, 1043, 1, 518400, 0xbab197ea +0, 1044, 1044, 1, 518400, 0xbab197ea +0, 1045, 1045, 1, 518400, 0xbab197ea +0, 1046, 1046, 1, 518400, 0xbab197ea +0, 1047, 1047, 1, 518400, 0xbab197ea +0, 1048, 1048, 1, 518400, 0xbab197ea +0, 1049, 1049, 1, 518400, 0xbab197ea +0, 1050, 1050, 1, 518400, 0xbab197ea +0, 1051, 1051, 1, 518400, 0xbab197ea +0, 1052, 1052, 1, 518400, 0xbab197ea 0, 1053, 1053, 1, 518400, 0x3de86ab7 1, 210600000, 210600000, 1831000, 665, 0x55580135 -0, 1062, 1062, 1, 518400, 0xbab197ea +0, 1054, 1054, 1, 518400, 0x3de86ab7 +0, 1055, 1055, 1, 518400, 0x3de86ab7 +0, 1056, 1056, 1, 518400, 0x3de86ab7 +0, 1057, 1057, 1, 518400, 0x3de86ab7 +0, 1058, 1058, 1, 518400, 0x3de86ab7 +0, 1059, 1059, 1, 518400, 0x3de86ab7 +0, 1060, 1060, 1, 518400, 0x3de86ab7 +0, 1061, 1061, 1, 518400, 0x3de86ab7 +0, 1062, 1062, 1, 518400, 0x3de86ab7 +0, 1063, 1063, 1, 518400, 0xbab197ea +0, 1064, 1064, 1, 518400, 0xbab197ea +0, 1065, 1065, 1, 518400, 0xbab197ea +0, 1066, 1066, 1, 518400, 0xbab197ea +0, 1067, 1067, 1, 518400, 0xbab197ea +0, 1068, 1068, 1, 518400, 0xbab197ea +0, 1069, 1069, 1, 518400, 0xbab197ea +0, 1070, 1070, 1, 518400, 0xbab197ea +0, 1071, 1071, 1, 518400, 0xbab197ea +0, 1072, 1072, 1, 518400, 0xbab197ea +0, 1073, 1073, 1, 518400, 0xbab197ea 1, 214771000, 214771000, 1558000, 1216, 0x50d1f6c5 0, 1074, 1074, 1, 518400, 0x8c320e68 -0, 1082, 1082, 1, 518400, 0xbab197ea +0, 1075, 1075, 1, 518400, 0x8c320e68 +0, 1076, 1076, 1, 518400, 0x8c320e68 +0, 1077, 1077, 1, 518400, 0x8c320e68 +0, 1078, 1078, 1, 518400, 0x8c320e68 +0, 1079, 1079, 1, 518400, 0x8c320e68 +0, 1080, 1080, 1, 518400, 0x8c320e68 +0, 1081, 1081, 1, 518400, 0x8c320e68 +0, 1082, 1082, 1, 518400, 0x8c320e68 +0, 1083, 1083, 1, 518400, 0xbab197ea +0, 1084, 1084, 1, 518400, 0xbab197ea +0, 1085, 1085, 1, 518400, 0xbab197ea +0, 1086, 1086, 1, 518400, 0xbab197ea +0, 1087, 1087, 1, 518400, 0xbab197ea +0, 1088, 1088, 1, 518400, 0xbab197ea +0, 1089, 1089, 1, 518400, 0xbab197ea +0, 1090, 1090, 1, 518400, 0xbab197ea +0, 1091, 1091, 1, 518400, 0xbab197ea +0, 1092, 1092, 1, 518400, 0xbab197ea +0, 1093, 1093, 1, 518400, 0xbab197ea +0, 1094, 1094, 1, 518400, 0xbab197ea +0, 1095, 1095, 1, 518400, 0xbab197ea +0, 1096, 1096, 1, 518400, 0xbab197ea +0, 1097, 1097, 1, 518400, 0xbab197ea +0, 1098, 1098, 1, 518400, 0xbab197ea +0, 1099, 1099, 1, 518400, 0xbab197ea +0, 1100, 1100, 1, 518400, 0xbab197ea +0, 1101, 1101, 1, 518400, 0xbab197ea +0, 1102, 1102, 1, 518400, 0xbab197ea +0, 1103, 1103, 1, 518400, 0xbab197ea +0, 1104, 1104, 1, 518400, 0xbab197ea +0, 1105, 1105, 1, 518400, 0xbab197ea +0, 1106, 1106, 1, 518400, 0xbab197ea +0, 1107, 1107, 1, 518400, 0xbab197ea +0, 1108, 1108, 1, 518400, 0xbab197ea +0, 1109, 1109, 1, 518400, 0xbab197ea +0, 1110, 1110, 1, 518400, 0xbab197ea +0, 1111, 1111, 1, 518400, 0xbab197ea +0, 1112, 1112, 1, 518400, 0xbab197ea +0, 1113, 1113, 1, 518400, 0xbab197ea +0, 1114, 1114, 1, 518400, 0xbab197ea +0, 1115, 1115, 1, 518400, 0xbab197ea +0, 1116, 1116, 1, 518400, 0xbab197ea +0, 1117, 1117, 1, 518400, 0xbab197ea +0, 1118, 1118, 1, 518400, 0xbab197ea +0, 1119, 1119, 1, 518400, 0xbab197ea +0, 1120, 1120, 1, 518400, 0xbab197ea +0, 1121, 1121, 1, 518400, 0xbab197ea +0, 1122, 1122, 1, 518400, 0xbab197ea +0, 1123, 1123, 1, 518400, 0xbab197ea +0, 1124, 1124, 1, 518400, 0xbab197ea +0, 1125, 1125, 1, 518400, 0xbab197ea +0, 1126, 1126, 1, 518400, 0xbab197ea +0, 1127, 1127, 1, 518400, 0xbab197ea 0, 1128, 1128, 1, 518400, 0x81e977b2 1, 225640000, 225640000, 2127000, 2133, 0x670c11a5 -0, 1139, 1139, 1, 518400, 0xbab197ea +0, 1129, 1129, 1, 518400, 0x81e977b2 +0, 1130, 1130, 1, 518400, 0x81e977b2 +0, 1131, 1131, 1, 518400, 0x81e977b2 +0, 1132, 1132, 1, 518400, 0x81e977b2 +0, 1133, 1133, 1, 518400, 0x81e977b2 +0, 1134, 1134, 1, 518400, 0x81e977b2 +0, 1135, 1135, 1, 518400, 0x81e977b2 +0, 1136, 1136, 1, 518400, 0x81e977b2 +0, 1137, 1137, 1, 518400, 0x81e977b2 +0, 1138, 1138, 1, 518400, 0x81e977b2 +0, 1139, 1139, 1, 518400, 0x81e977b2 1, 227834000, 227834000, 1262000, 1264, 0xc1d9fc57 0, 1140, 1140, 1, 518400, 0xb046dd30 -0, 1145, 1145, 1, 518400, 0xbab197ea diff --git a/tests/ref/fate/sub2video_basic b/tests/ref/fate/sub2video_basic index 5f72e292c9..3c9fc71b5c 100644 --- a/tests/ref/fate/sub2video_basic +++ b/tests/ref/fate/sub2video_basic @@ -1,95 +1,1150 @@ -#tb 0: 1/25 +#tb 0: 1/5 #media_type 0: video #codec_id 0: rawvideo #dimensions 0: 720x480 -#sar 0: 0/1 -0, 3312, 3312, 1, 1382400, 0x00000000 -0, 3312, 3312, 1, 1382400, 0x8c93c2ba -0, 3436, 3436, 1, 1382400, 0x00000000 -0, 3684, 3684, 1, 1382400, 0xb02e32ca -0, 3802, 3802, 1, 1382400, 0x00000000 -0, 4520, 4520, 1, 1382400, 0x83b71116 -0, 4584, 4584, 1, 1382400, 0x00000000 -0, 4586, 4586, 1, 1382400, 0x85547fd1 -0, 4645, 4645, 1, 1382400, 0x00000000 -0, 4648, 4648, 1, 1382400, 0x00000000 -0, 4648, 4648, 1, 1382400, 0xb6a8f181 -0, 4715, 4715, 1, 1382400, 0x00000000 -0, 4717, 4717, 1, 1382400, 0xb64d1a2c -0, 4748, 4748, 1, 1382400, 0x00000000 -0, 4750, 4750, 1, 1382400, 0x7b37ecf3 -0, 4792, 4792, 1, 1382400, 0x00000000 -0, 4993, 4993, 1, 1382400, 0xdc025bd1 -0, 5027, 5027, 1, 1382400, 0x00000000 -0, 5029, 5029, 1, 1382400, 0x688b294d -0, 5068, 5068, 1, 1382400, 0x00000000 -0, 5070, 5070, 1, 1382400, 0xa2b33d1b -0, 5117, 5117, 1, 1382400, 0x00000000 -0, 5119, 5119, 1, 1382400, 0xb3e525e3 -0, 5168, 5168, 1, 1382400, 0x00000000 -0, 5170, 5170, 1, 1382400, 0xaa8fbdd7 -0, 5216, 5216, 1, 1382400, 0x00000000 -0, 5218, 5218, 1, 1382400, 0x7b7f26dd -0, 5249, 5249, 1, 1382400, 0x00000000 -0, 5251, 5251, 1, 1382400, 0x15e2f836 -0, 5289, 5289, 1, 1382400, 0x00000000 -0, 5291, 5291, 1, 1382400, 0x0fee9b0c -0, 5358, 5358, 1, 1382400, 0x00000000 -0, 5360, 5360, 1, 1382400, 0x89d62791 -0, 5429, 5429, 1, 1382400, 0x00000000 -0, 5431, 5431, 1, 1382400, 0xa6a9fd74 -0, 5490, 5490, 1, 1382400, 0x00000000 -0, 5491, 5491, 1, 1382400, 0x7896178d -0, 5537, 5537, 1, 1382400, 0x00000000 -0, 5588, 5588, 1, 1382400, 0x01751a52 -0, 5647, 5647, 1, 1382400, 0x00000000 -0, 5688, 5688, 1, 1382400, 0xa3959c6f -0, 5770, 5770, 1, 1382400, 0x00000000 -0, 5772, 5772, 1, 1382400, 0x3d3ea47b -0, 5826, 5826, 1, 1382400, 0x00000000 -0, 5828, 5828, 1, 1382400, 0x593f8b24 -0, 5931, 5931, 1, 1382400, 0x00000000 -0, 5933, 5933, 1, 1382400, 0x171f05ba -0, 6001, 6001, 1, 1382400, 0x00000000 -0, 6003, 6003, 1, 1382400, 0xb014cdf1 -0, 6054, 6054, 1, 1382400, 0x00000000 -0, 6839, 6839, 1, 1382400, 0xd918e667 -0, 6880, 6880, 1, 1382400, 0x00000000 -0, 7386, 7386, 1, 1382400, 0xc9406331 -0, 7419, 7419, 1, 1382400, 0x00000000 -0, 7501, 7501, 1, 1382400, 0xaf08b10d -0, 7549, 7549, 1, 1382400, 0x00000000 -0, 7551, 7551, 1, 1382400, 0x00000000 -0, 7551, 7551, 1, 1382400, 0x853a9d93 -0, 7589, 7589, 1, 1382400, 0x00000000 -0, 7605, 7605, 1, 1382400, 0x7491a87d -0, 7647, 7647, 1, 1382400, 0x00000000 -0, 7649, 7649, 1, 1382400, 0xf7383c58 -0, 7697, 7697, 1, 1382400, 0x00000000 -0, 7699, 7699, 1, 1382400, 0xe66be411 -0, 7743, 7743, 1, 1382400, 0x00000000 -0, 8032, 8032, 1, 1382400, 0xd6850362 -0, 8082, 8082, 1, 1382400, 0x00000000 -0, 8084, 8084, 1, 1382400, 0x3e1ed109 -0, 8115, 8115, 1, 1382400, 0x00000000 -0, 8116, 8116, 1, 1382400, 0x39c1b7bd -0, 8160, 8160, 1, 1382400, 0x00000000 -0, 8180, 8180, 1, 1382400, 0x35b85f2e -0, 8207, 8207, 1, 1382400, 0x00000000 -0, 8209, 8209, 1, 1382400, 0x00000000 -0, 8209, 8209, 1, 1382400, 0x83f103e5 -0, 8247, 8247, 1, 1382400, 0x00000000 -0, 8249, 8249, 1, 1382400, 0xbc1ca9b3 -0, 8278, 8278, 1, 1382400, 0x00000000 -0, 8281, 8281, 1, 1382400, 0x94d4a51e -0, 8321, 8321, 1, 1382400, 0x00000000 -0, 8323, 8323, 1, 1382400, 0xf88cdfde -0, 8367, 8367, 1, 1382400, 0x00000000 -0, 8565, 8565, 1, 1382400, 0xdd51423b -0, 8611, 8611, 1, 1382400, 0x00000000 -0, 8669, 8669, 1, 1382400, 0x08259fa4 -0, 8708, 8708, 1, 1382400, 0x00000000 -0, 8941, 8941, 1, 1382400, 0x1663fa34 -0, 8994, 8994, 1, 1382400, 0x00000000 -0, 8996, 8996, 1, 1382400, 0xda2ceb55 -0, 9027, 9027, 1, 1382400, 0x00000000 +#sar 0: 1/1 +0, 0, 0, 1, 1382400, 0x00000000 +0, 662, 662, 1, 1382400, 0xc637b893 +0, 663, 663, 1, 1382400, 0xc637b893 +0, 664, 664, 1, 1382400, 0xc637b893 +0, 665, 665, 1, 1382400, 0xc637b893 +0, 666, 666, 1, 1382400, 0xc637b893 +0, 667, 667, 1, 1382400, 0xc637b893 +0, 668, 668, 1, 1382400, 0xc637b893 +0, 669, 669, 1, 1382400, 0xc637b893 +0, 670, 670, 1, 1382400, 0xc637b893 +0, 671, 671, 1, 1382400, 0xc637b893 +0, 672, 672, 1, 1382400, 0xc637b893 +0, 673, 673, 1, 1382400, 0xc637b893 +0, 674, 674, 1, 1382400, 0xc637b893 +0, 675, 675, 1, 1382400, 0xc637b893 +0, 676, 676, 1, 1382400, 0xc637b893 +0, 677, 677, 1, 1382400, 0xc637b893 +0, 678, 678, 1, 1382400, 0xc637b893 +0, 679, 679, 1, 1382400, 0xc637b893 +0, 680, 680, 1, 1382400, 0xc637b893 +0, 681, 681, 1, 1382400, 0xc637b893 +0, 682, 682, 1, 1382400, 0xc637b893 +0, 683, 683, 1, 1382400, 0xc637b893 +0, 684, 684, 1, 1382400, 0xc637b893 +0, 685, 685, 1, 1382400, 0xc637b893 +0, 686, 686, 1, 1382400, 0xc637b893 +0, 687, 687, 1, 1382400, 0x00000000 +0, 688, 688, 1, 1382400, 0x00000000 +0, 689, 689, 1, 1382400, 0x00000000 +0, 690, 690, 1, 1382400, 0x00000000 +0, 691, 691, 1, 1382400, 0x00000000 +0, 692, 692, 1, 1382400, 0x00000000 +0, 693, 693, 1, 1382400, 0x00000000 +0, 694, 694, 1, 1382400, 0x00000000 +0, 695, 695, 1, 1382400, 0x00000000 +0, 696, 696, 1, 1382400, 0x00000000 +0, 697, 697, 1, 1382400, 0x00000000 +0, 698, 698, 1, 1382400, 0x00000000 +0, 699, 699, 1, 1382400, 0x00000000 +0, 700, 700, 1, 1382400, 0x00000000 +0, 701, 701, 1, 1382400, 0x00000000 +0, 702, 702, 1, 1382400, 0x00000000 +0, 703, 703, 1, 1382400, 0x00000000 +0, 704, 704, 1, 1382400, 0x00000000 +0, 705, 705, 1, 1382400, 0x00000000 +0, 706, 706, 1, 1382400, 0x00000000 +0, 707, 707, 1, 1382400, 0x00000000 +0, 708, 708, 1, 1382400, 0x00000000 +0, 709, 709, 1, 1382400, 0x00000000 +0, 710, 710, 1, 1382400, 0x00000000 +0, 711, 711, 1, 1382400, 0x00000000 +0, 712, 712, 1, 1382400, 0x00000000 +0, 713, 713, 1, 1382400, 0x00000000 +0, 714, 714, 1, 1382400, 0x00000000 +0, 715, 715, 1, 1382400, 0x00000000 +0, 716, 716, 1, 1382400, 0x00000000 +0, 717, 717, 1, 1382400, 0x00000000 +0, 718, 718, 1, 1382400, 0x00000000 +0, 719, 719, 1, 1382400, 0x00000000 +0, 720, 720, 1, 1382400, 0x00000000 +0, 721, 721, 1, 1382400, 0x00000000 +0, 722, 722, 1, 1382400, 0x00000000 +0, 723, 723, 1, 1382400, 0x00000000 +0, 724, 724, 1, 1382400, 0x00000000 +0, 725, 725, 1, 1382400, 0x00000000 +0, 726, 726, 1, 1382400, 0x00000000 +0, 727, 727, 1, 1382400, 0x00000000 +0, 728, 728, 1, 1382400, 0x00000000 +0, 729, 729, 1, 1382400, 0x00000000 +0, 730, 730, 1, 1382400, 0x00000000 +0, 731, 731, 1, 1382400, 0x00000000 +0, 732, 732, 1, 1382400, 0x00000000 +0, 733, 733, 1, 1382400, 0x00000000 +0, 734, 734, 1, 1382400, 0x00000000 +0, 735, 735, 1, 1382400, 0x00000000 +0, 737, 737, 1, 1382400, 0x4c2960ca +0, 737, 737, 1, 1382400, 0x4c2960ca +0, 738, 738, 1, 1382400, 0x4c2960ca +0, 739, 739, 1, 1382400, 0x4c2960ca +0, 740, 740, 1, 1382400, 0x4c2960ca +0, 741, 741, 1, 1382400, 0x4c2960ca +0, 742, 742, 1, 1382400, 0x4c2960ca +0, 743, 743, 1, 1382400, 0x4c2960ca +0, 744, 744, 1, 1382400, 0x4c2960ca +0, 745, 745, 1, 1382400, 0x4c2960ca +0, 746, 746, 1, 1382400, 0x4c2960ca +0, 747, 747, 1, 1382400, 0x4c2960ca +0, 748, 748, 1, 1382400, 0x4c2960ca +0, 749, 749, 1, 1382400, 0x4c2960ca +0, 750, 750, 1, 1382400, 0x4c2960ca +0, 751, 751, 1, 1382400, 0x4c2960ca +0, 752, 752, 1, 1382400, 0x4c2960ca +0, 753, 753, 1, 1382400, 0x4c2960ca +0, 754, 754, 1, 1382400, 0x4c2960ca +0, 755, 755, 1, 1382400, 0x4c2960ca +0, 756, 756, 1, 1382400, 0x4c2960ca +0, 757, 757, 1, 1382400, 0x4c2960ca +0, 758, 758, 1, 1382400, 0x4c2960ca +0, 759, 759, 1, 1382400, 0x4c2960ca +0, 760, 760, 1, 1382400, 0x00000000 +0, 761, 761, 1, 1382400, 0x00000000 +0, 762, 762, 1, 1382400, 0x00000000 +0, 763, 763, 1, 1382400, 0x00000000 +0, 764, 764, 1, 1382400, 0x00000000 +0, 765, 765, 1, 1382400, 0x00000000 +0, 766, 766, 1, 1382400, 0x00000000 +0, 767, 767, 1, 1382400, 0x00000000 +0, 768, 768, 1, 1382400, 0x00000000 +0, 769, 769, 1, 1382400, 0x00000000 +0, 770, 770, 1, 1382400, 0x00000000 +0, 771, 771, 1, 1382400, 0x00000000 +0, 772, 772, 1, 1382400, 0x00000000 +0, 773, 773, 1, 1382400, 0x00000000 +0, 774, 774, 1, 1382400, 0x00000000 +0, 775, 775, 1, 1382400, 0x00000000 +0, 776, 776, 1, 1382400, 0x00000000 +0, 777, 777, 1, 1382400, 0x00000000 +0, 778, 778, 1, 1382400, 0x00000000 +0, 779, 779, 1, 1382400, 0x00000000 +0, 780, 780, 1, 1382400, 0x00000000 +0, 781, 781, 1, 1382400, 0x00000000 +0, 782, 782, 1, 1382400, 0x00000000 +0, 783, 783, 1, 1382400, 0x00000000 +0, 784, 784, 1, 1382400, 0x00000000 +0, 785, 785, 1, 1382400, 0x00000000 +0, 786, 786, 1, 1382400, 0x00000000 +0, 787, 787, 1, 1382400, 0x00000000 +0, 788, 788, 1, 1382400, 0x00000000 +0, 789, 789, 1, 1382400, 0x00000000 +0, 790, 790, 1, 1382400, 0x00000000 +0, 791, 791, 1, 1382400, 0x00000000 +0, 792, 792, 1, 1382400, 0x00000000 +0, 793, 793, 1, 1382400, 0x00000000 +0, 794, 794, 1, 1382400, 0x00000000 +0, 795, 795, 1, 1382400, 0x00000000 +0, 796, 796, 1, 1382400, 0x00000000 +0, 797, 797, 1, 1382400, 0x00000000 +0, 798, 798, 1, 1382400, 0x00000000 +0, 799, 799, 1, 1382400, 0x00000000 +0, 800, 800, 1, 1382400, 0x00000000 +0, 801, 801, 1, 1382400, 0x00000000 +0, 802, 802, 1, 1382400, 0x00000000 +0, 803, 803, 1, 1382400, 0x00000000 +0, 804, 804, 1, 1382400, 0x00000000 +0, 805, 805, 1, 1382400, 0x00000000 +0, 806, 806, 1, 1382400, 0x00000000 +0, 807, 807, 1, 1382400, 0x00000000 +0, 808, 808, 1, 1382400, 0x00000000 +0, 809, 809, 1, 1382400, 0x00000000 +0, 810, 810, 1, 1382400, 0x00000000 +0, 811, 811, 1, 1382400, 0x00000000 +0, 812, 812, 1, 1382400, 0x00000000 +0, 813, 813, 1, 1382400, 0x00000000 +0, 814, 814, 1, 1382400, 0x00000000 +0, 815, 815, 1, 1382400, 0x00000000 +0, 816, 816, 1, 1382400, 0x00000000 +0, 817, 817, 1, 1382400, 0x00000000 +0, 818, 818, 1, 1382400, 0x00000000 +0, 819, 819, 1, 1382400, 0x00000000 +0, 820, 820, 1, 1382400, 0x00000000 +0, 821, 821, 1, 1382400, 0x00000000 +0, 822, 822, 1, 1382400, 0x00000000 +0, 823, 823, 1, 1382400, 0x00000000 +0, 824, 824, 1, 1382400, 0x00000000 +0, 825, 825, 1, 1382400, 0x00000000 +0, 826, 826, 1, 1382400, 0x00000000 +0, 827, 827, 1, 1382400, 0x00000000 +0, 828, 828, 1, 1382400, 0x00000000 +0, 829, 829, 1, 1382400, 0x00000000 +0, 830, 830, 1, 1382400, 0x00000000 +0, 831, 831, 1, 1382400, 0x00000000 +0, 832, 832, 1, 1382400, 0x00000000 +0, 833, 833, 1, 1382400, 0x00000000 +0, 834, 834, 1, 1382400, 0x00000000 +0, 835, 835, 1, 1382400, 0x00000000 +0, 836, 836, 1, 1382400, 0x00000000 +0, 837, 837, 1, 1382400, 0x00000000 +0, 838, 838, 1, 1382400, 0x00000000 +0, 839, 839, 1, 1382400, 0x00000000 +0, 840, 840, 1, 1382400, 0x00000000 +0, 841, 841, 1, 1382400, 0x00000000 +0, 842, 842, 1, 1382400, 0x00000000 +0, 843, 843, 1, 1382400, 0x00000000 +0, 844, 844, 1, 1382400, 0x00000000 +0, 845, 845, 1, 1382400, 0x00000000 +0, 846, 846, 1, 1382400, 0x00000000 +0, 847, 847, 1, 1382400, 0x00000000 +0, 848, 848, 1, 1382400, 0x00000000 +0, 849, 849, 1, 1382400, 0x00000000 +0, 850, 850, 1, 1382400, 0x00000000 +0, 851, 851, 1, 1382400, 0x00000000 +0, 852, 852, 1, 1382400, 0x00000000 +0, 853, 853, 1, 1382400, 0x00000000 +0, 854, 854, 1, 1382400, 0x00000000 +0, 855, 855, 1, 1382400, 0x00000000 +0, 856, 856, 1, 1382400, 0x00000000 +0, 857, 857, 1, 1382400, 0x00000000 +0, 858, 858, 1, 1382400, 0x00000000 +0, 859, 859, 1, 1382400, 0x00000000 +0, 860, 860, 1, 1382400, 0x00000000 +0, 861, 861, 1, 1382400, 0x00000000 +0, 862, 862, 1, 1382400, 0x00000000 +0, 863, 863, 1, 1382400, 0x00000000 +0, 864, 864, 1, 1382400, 0x00000000 +0, 865, 865, 1, 1382400, 0x00000000 +0, 866, 866, 1, 1382400, 0x00000000 +0, 867, 867, 1, 1382400, 0x00000000 +0, 868, 868, 1, 1382400, 0x00000000 +0, 869, 869, 1, 1382400, 0x00000000 +0, 870, 870, 1, 1382400, 0x00000000 +0, 871, 871, 1, 1382400, 0x00000000 +0, 872, 872, 1, 1382400, 0x00000000 +0, 873, 873, 1, 1382400, 0x00000000 +0, 874, 874, 1, 1382400, 0x00000000 +0, 875, 875, 1, 1382400, 0x00000000 +0, 876, 876, 1, 1382400, 0x00000000 +0, 877, 877, 1, 1382400, 0x00000000 +0, 878, 878, 1, 1382400, 0x00000000 +0, 879, 879, 1, 1382400, 0x00000000 +0, 880, 880, 1, 1382400, 0x00000000 +0, 881, 881, 1, 1382400, 0x00000000 +0, 882, 882, 1, 1382400, 0x00000000 +0, 883, 883, 1, 1382400, 0x00000000 +0, 884, 884, 1, 1382400, 0x00000000 +0, 885, 885, 1, 1382400, 0x00000000 +0, 886, 886, 1, 1382400, 0x00000000 +0, 887, 887, 1, 1382400, 0x00000000 +0, 888, 888, 1, 1382400, 0x00000000 +0, 889, 889, 1, 1382400, 0x00000000 +0, 890, 890, 1, 1382400, 0x00000000 +0, 891, 891, 1, 1382400, 0x00000000 +0, 892, 892, 1, 1382400, 0x00000000 +0, 893, 893, 1, 1382400, 0x00000000 +0, 894, 894, 1, 1382400, 0x00000000 +0, 895, 895, 1, 1382400, 0x00000000 +0, 896, 896, 1, 1382400, 0x00000000 +0, 897, 897, 1, 1382400, 0x00000000 +0, 898, 898, 1, 1382400, 0x00000000 +0, 899, 899, 1, 1382400, 0x00000000 +0, 900, 900, 1, 1382400, 0x00000000 +0, 901, 901, 1, 1382400, 0x00000000 +0, 902, 902, 1, 1382400, 0x00000000 +0, 904, 904, 1, 1382400, 0x5fa18966 +0, 904, 904, 1, 1382400, 0x5fa18966 +0, 905, 905, 1, 1382400, 0x5fa18966 +0, 906, 906, 1, 1382400, 0x5fa18966 +0, 907, 907, 1, 1382400, 0x5fa18966 +0, 908, 908, 1, 1382400, 0x5fa18966 +0, 909, 909, 1, 1382400, 0x5fa18966 +0, 910, 910, 1, 1382400, 0x5fa18966 +0, 911, 911, 1, 1382400, 0x5fa18966 +0, 912, 912, 1, 1382400, 0x5fa18966 +0, 913, 913, 1, 1382400, 0x5fa18966 +0, 914, 914, 1, 1382400, 0x5fa18966 +0, 915, 915, 1, 1382400, 0x5fa18966 +0, 916, 916, 1, 1382400, 0x5fa18966 +0, 917, 917, 1, 1382400, 0x55f4b7b1 +0, 918, 918, 1, 1382400, 0x55f4b7b1 +0, 919, 919, 1, 1382400, 0x55f4b7b1 +0, 920, 920, 1, 1382400, 0x55f4b7b1 +0, 921, 921, 1, 1382400, 0x55f4b7b1 +0, 922, 922, 1, 1382400, 0x55f4b7b1 +0, 923, 923, 1, 1382400, 0x55f4b7b1 +0, 924, 924, 1, 1382400, 0x55f4b7b1 +0, 925, 925, 1, 1382400, 0x55f4b7b1 +0, 926, 926, 1, 1382400, 0x55f4b7b1 +0, 927, 927, 1, 1382400, 0x55f4b7b1 +0, 928, 928, 1, 1382400, 0x55f4b7b1 +0, 929, 929, 1, 1382400, 0x00000000 +0, 930, 930, 1, 1382400, 0xdfa4cf32 +0, 931, 931, 1, 1382400, 0xdfa4cf32 +0, 932, 932, 1, 1382400, 0xdfa4cf32 +0, 933, 933, 1, 1382400, 0xdfa4cf32 +0, 934, 934, 1, 1382400, 0xdfa4cf32 +0, 935, 935, 1, 1382400, 0xdfa4cf32 +0, 936, 936, 1, 1382400, 0xdfa4cf32 +0, 937, 937, 1, 1382400, 0xdfa4cf32 +0, 938, 938, 1, 1382400, 0xdfa4cf32 +0, 939, 939, 1, 1382400, 0xdfa4cf32 +0, 940, 940, 1, 1382400, 0xdfa4cf32 +0, 941, 941, 1, 1382400, 0xdfa4cf32 +0, 942, 942, 1, 1382400, 0xdfa4cf32 +0, 943, 943, 1, 1382400, 0x35023df8 +0, 944, 944, 1, 1382400, 0x35023df8 +0, 945, 945, 1, 1382400, 0x35023df8 +0, 946, 946, 1, 1382400, 0x35023df8 +0, 947, 947, 1, 1382400, 0x35023df8 +0, 948, 948, 1, 1382400, 0x35023df8 +0, 949, 949, 1, 1382400, 0x35023df8 +0, 950, 950, 1, 1382400, 0xed933219 +0, 951, 951, 1, 1382400, 0xed933219 +0, 952, 952, 1, 1382400, 0xed933219 +0, 953, 953, 1, 1382400, 0xed933219 +0, 954, 954, 1, 1382400, 0xed933219 +0, 955, 955, 1, 1382400, 0xed933219 +0, 956, 956, 1, 1382400, 0xed933219 +0, 957, 957, 1, 1382400, 0xed933219 +0, 958, 958, 1, 1382400, 0x00000000 +0, 959, 959, 1, 1382400, 0x00000000 +0, 960, 960, 1, 1382400, 0x00000000 +0, 961, 961, 1, 1382400, 0x00000000 +0, 962, 962, 1, 1382400, 0x00000000 +0, 963, 963, 1, 1382400, 0x00000000 +0, 964, 964, 1, 1382400, 0x00000000 +0, 965, 965, 1, 1382400, 0x00000000 +0, 966, 966, 1, 1382400, 0x00000000 +0, 967, 967, 1, 1382400, 0x00000000 +0, 968, 968, 1, 1382400, 0x00000000 +0, 969, 969, 1, 1382400, 0x00000000 +0, 970, 970, 1, 1382400, 0x00000000 +0, 971, 971, 1, 1382400, 0x00000000 +0, 972, 972, 1, 1382400, 0x00000000 +0, 973, 973, 1, 1382400, 0x00000000 +0, 974, 974, 1, 1382400, 0x00000000 +0, 975, 975, 1, 1382400, 0x00000000 +0, 976, 976, 1, 1382400, 0x00000000 +0, 977, 977, 1, 1382400, 0x00000000 +0, 978, 978, 1, 1382400, 0x00000000 +0, 979, 979, 1, 1382400, 0x00000000 +0, 980, 980, 1, 1382400, 0x00000000 +0, 981, 981, 1, 1382400, 0x00000000 +0, 982, 982, 1, 1382400, 0x00000000 +0, 983, 983, 1, 1382400, 0x00000000 +0, 984, 984, 1, 1382400, 0x00000000 +0, 985, 985, 1, 1382400, 0x00000000 +0, 986, 986, 1, 1382400, 0x00000000 +0, 987, 987, 1, 1382400, 0x00000000 +0, 988, 988, 1, 1382400, 0x00000000 +0, 989, 989, 1, 1382400, 0x00000000 +0, 990, 990, 1, 1382400, 0x00000000 +0, 991, 991, 1, 1382400, 0x00000000 +0, 992, 992, 1, 1382400, 0x00000000 +0, 993, 993, 1, 1382400, 0x00000000 +0, 994, 994, 1, 1382400, 0x00000000 +0, 995, 995, 1, 1382400, 0x00000000 +0, 996, 996, 1, 1382400, 0x00000000 +0, 997, 997, 1, 1382400, 0x00000000 +0, 999, 999, 1, 1382400, 0x1b26389a +0, 999, 999, 1, 1382400, 0x1b26389a +0, 1000, 1000, 1, 1382400, 0x1b26389a +0, 1001, 1001, 1, 1382400, 0x1b26389a +0, 1002, 1002, 1, 1382400, 0x1b26389a +0, 1003, 1003, 1, 1382400, 0x1b26389a +0, 1004, 1004, 1, 1382400, 0x1b26389a +0, 1006, 1006, 1, 1382400, 0xf0c7028b +0, 1006, 1006, 1, 1382400, 0xf0c7028b +0, 1007, 1007, 1, 1382400, 0xf0c7028b +0, 1008, 1008, 1, 1382400, 0xf0c7028b +0, 1009, 1009, 1, 1382400, 0xf0c7028b +0, 1010, 1010, 1, 1382400, 0xf0c7028b +0, 1011, 1011, 1, 1382400, 0xf0c7028b +0, 1012, 1012, 1, 1382400, 0xf0c7028b +0, 1013, 1013, 1, 1382400, 0xf0c7028b +0, 1014, 1014, 1, 1382400, 0x395f521d +0, 1015, 1015, 1, 1382400, 0x395f521d +0, 1016, 1016, 1, 1382400, 0x395f521d +0, 1017, 1017, 1, 1382400, 0x395f521d +0, 1018, 1018, 1, 1382400, 0x395f521d +0, 1019, 1019, 1, 1382400, 0x395f521d +0, 1020, 1020, 1, 1382400, 0x395f521d +0, 1021, 1021, 1, 1382400, 0x395f521d +0, 1022, 1022, 1, 1382400, 0x395f521d +0, 1024, 1024, 1, 1382400, 0x1ea87415 +0, 1024, 1024, 1, 1382400, 0x1ea87415 +0, 1025, 1025, 1, 1382400, 0x1ea87415 +0, 1026, 1026, 1, 1382400, 0x1ea87415 +0, 1027, 1027, 1, 1382400, 0x1ea87415 +0, 1028, 1028, 1, 1382400, 0x1ea87415 +0, 1029, 1029, 1, 1382400, 0x1ea87415 +0, 1030, 1030, 1, 1382400, 0x1ea87415 +0, 1031, 1031, 1, 1382400, 0x1ea87415 +0, 1032, 1032, 1, 1382400, 0x1ea87415 +0, 1033, 1033, 1, 1382400, 0x1ea87415 +0, 1034, 1034, 1, 1382400, 0xc6effdc1 +0, 1035, 1035, 1, 1382400, 0xc6effdc1 +0, 1036, 1036, 1, 1382400, 0xc6effdc1 +0, 1037, 1037, 1, 1382400, 0xc6effdc1 +0, 1038, 1038, 1, 1382400, 0xc6effdc1 +0, 1039, 1039, 1, 1382400, 0xc6effdc1 +0, 1040, 1040, 1, 1382400, 0xc6effdc1 +0, 1041, 1041, 1, 1382400, 0xc6effdc1 +0, 1042, 1042, 1, 1382400, 0xc6effdc1 +0, 1044, 1044, 1, 1382400, 0xba6846f8 +0, 1044, 1044, 1, 1382400, 0xba6846f8 +0, 1045, 1045, 1, 1382400, 0xba6846f8 +0, 1046, 1046, 1, 1382400, 0xba6846f8 +0, 1047, 1047, 1, 1382400, 0xba6846f8 +0, 1048, 1048, 1, 1382400, 0xba6846f8 +0, 1049, 1049, 1, 1382400, 0xba6846f8 +0, 1050, 1050, 1, 1382400, 0x033c5d5b +0, 1051, 1051, 1, 1382400, 0x033c5d5b +0, 1052, 1052, 1, 1382400, 0x033c5d5b +0, 1053, 1053, 1, 1382400, 0x033c5d5b +0, 1054, 1054, 1, 1382400, 0x033c5d5b +0, 1055, 1055, 1, 1382400, 0x033c5d5b +0, 1056, 1056, 1, 1382400, 0x033c5d5b +0, 1057, 1057, 1, 1382400, 0x033c5d5b +0, 1058, 1058, 1, 1382400, 0x00000000 +0, 1059, 1059, 1, 1382400, 0xef5abf66 +0, 1060, 1060, 1, 1382400, 0xef5abf66 +0, 1061, 1061, 1, 1382400, 0xef5abf66 +0, 1062, 1062, 1, 1382400, 0xef5abf66 +0, 1063, 1063, 1, 1382400, 0xef5abf66 +0, 1064, 1064, 1, 1382400, 0xef5abf66 +0, 1065, 1065, 1, 1382400, 0xef5abf66 +0, 1066, 1066, 1, 1382400, 0xef5abf66 +0, 1067, 1067, 1, 1382400, 0xef5abf66 +0, 1068, 1068, 1, 1382400, 0xef5abf66 +0, 1069, 1069, 1, 1382400, 0xef5abf66 +0, 1070, 1070, 1, 1382400, 0xef5abf66 +0, 1071, 1071, 1, 1382400, 0xef5abf66 +0, 1072, 1072, 1, 1382400, 0x00000000 +0, 1073, 1073, 1, 1382400, 0xec747954 +0, 1074, 1074, 1, 1382400, 0xec747954 +0, 1075, 1075, 1, 1382400, 0xec747954 +0, 1076, 1076, 1, 1382400, 0xec747954 +0, 1077, 1077, 1, 1382400, 0xec747954 +0, 1078, 1078, 1, 1382400, 0xec747954 +0, 1079, 1079, 1, 1382400, 0xec747954 +0, 1080, 1080, 1, 1382400, 0xec747954 +0, 1081, 1081, 1, 1382400, 0xec747954 +0, 1082, 1082, 1, 1382400, 0xec747954 +0, 1083, 1083, 1, 1382400, 0xec747954 +0, 1084, 1084, 1, 1382400, 0xec747954 +0, 1085, 1085, 1, 1382400, 0xec747954 +0, 1086, 1086, 1, 1382400, 0x00000000 +0, 1087, 1087, 1, 1382400, 0xfa34bcaf +0, 1088, 1088, 1, 1382400, 0xfa34bcaf +0, 1089, 1089, 1, 1382400, 0xfa34bcaf +0, 1090, 1090, 1, 1382400, 0xfa34bcaf +0, 1091, 1091, 1, 1382400, 0xfa34bcaf +0, 1092, 1092, 1, 1382400, 0xfa34bcaf +0, 1093, 1093, 1, 1382400, 0xfa34bcaf +0, 1094, 1094, 1, 1382400, 0xfa34bcaf +0, 1095, 1095, 1, 1382400, 0xfa34bcaf +0, 1096, 1096, 1, 1382400, 0xfa34bcaf +0, 1097, 1097, 1, 1382400, 0xfa34bcaf +0, 1098, 1098, 1, 1382400, 0x00000000 +0, 1099, 1099, 1, 1382400, 0x8b7a709b +0, 1100, 1100, 1, 1382400, 0x8b7a709b +0, 1101, 1101, 1, 1382400, 0x8b7a709b +0, 1102, 1102, 1, 1382400, 0x8b7a709b +0, 1103, 1103, 1, 1382400, 0x8b7a709b +0, 1104, 1104, 1, 1382400, 0x8b7a709b +0, 1105, 1105, 1, 1382400, 0x8b7a709b +0, 1106, 1106, 1, 1382400, 0x8b7a709b +0, 1107, 1107, 1, 1382400, 0x00000000 +0, 1108, 1108, 1, 1382400, 0x00000000 +0, 1109, 1109, 1, 1382400, 0x00000000 +0, 1110, 1110, 1, 1382400, 0x00000000 +0, 1111, 1111, 1, 1382400, 0x00000000 +0, 1112, 1112, 1, 1382400, 0x00000000 +0, 1113, 1113, 1, 1382400, 0x00000000 +0, 1114, 1114, 1, 1382400, 0x00000000 +0, 1115, 1115, 1, 1382400, 0x00000000 +0, 1116, 1116, 1, 1382400, 0x00000000 +0, 1118, 1118, 1, 1382400, 0xc333382f +0, 1118, 1118, 1, 1382400, 0xc333382f +0, 1119, 1119, 1, 1382400, 0xc333382f +0, 1120, 1120, 1, 1382400, 0xc333382f +0, 1121, 1121, 1, 1382400, 0xc333382f +0, 1122, 1122, 1, 1382400, 0xc333382f +0, 1123, 1123, 1, 1382400, 0xc333382f +0, 1124, 1124, 1, 1382400, 0xc333382f +0, 1125, 1125, 1, 1382400, 0xc333382f +0, 1126, 1126, 1, 1382400, 0xc333382f +0, 1127, 1127, 1, 1382400, 0xc333382f +0, 1128, 1128, 1, 1382400, 0xc333382f +0, 1129, 1129, 1, 1382400, 0x00000000 +0, 1130, 1130, 1, 1382400, 0x00000000 +0, 1131, 1131, 1, 1382400, 0x00000000 +0, 1132, 1132, 1, 1382400, 0x00000000 +0, 1133, 1133, 1, 1382400, 0x00000000 +0, 1134, 1134, 1, 1382400, 0x00000000 +0, 1135, 1135, 1, 1382400, 0x00000000 +0, 1136, 1136, 1, 1382400, 0x00000000 +0, 1138, 1138, 1, 1382400, 0xabe5dfcf +0, 1138, 1138, 1, 1382400, 0xabe5dfcf +0, 1139, 1139, 1, 1382400, 0xabe5dfcf +0, 1140, 1140, 1, 1382400, 0xabe5dfcf +0, 1141, 1141, 1, 1382400, 0xabe5dfcf +0, 1142, 1142, 1, 1382400, 0xabe5dfcf +0, 1143, 1143, 1, 1382400, 0xabe5dfcf +0, 1144, 1144, 1, 1382400, 0xabe5dfcf +0, 1145, 1145, 1, 1382400, 0xabe5dfcf +0, 1146, 1146, 1, 1382400, 0xabe5dfcf +0, 1147, 1147, 1, 1382400, 0xabe5dfcf +0, 1148, 1148, 1, 1382400, 0xabe5dfcf +0, 1149, 1149, 1, 1382400, 0xabe5dfcf +0, 1150, 1150, 1, 1382400, 0xabe5dfcf +0, 1151, 1151, 1, 1382400, 0xabe5dfcf +0, 1152, 1152, 1, 1382400, 0xabe5dfcf +0, 1153, 1153, 1, 1382400, 0xabe5dfcf +0, 1154, 1154, 1, 1382400, 0x56948101 +0, 1155, 1155, 1, 1382400, 0x56948101 +0, 1156, 1156, 1, 1382400, 0x56948101 +0, 1157, 1157, 1, 1382400, 0x56948101 +0, 1158, 1158, 1, 1382400, 0x56948101 +0, 1159, 1159, 1, 1382400, 0x56948101 +0, 1160, 1160, 1, 1382400, 0x56948101 +0, 1161, 1161, 1, 1382400, 0x56948101 +0, 1162, 1162, 1, 1382400, 0x56948101 +0, 1163, 1163, 1, 1382400, 0x56948101 +0, 1164, 1164, 1, 1382400, 0x56948101 +0, 1166, 1166, 1, 1382400, 0xb747834a +0, 1166, 1166, 1, 1382400, 0xb747834a +0, 1167, 1167, 1, 1382400, 0xb747834a +0, 1168, 1168, 1, 1382400, 0xb747834a +0, 1169, 1169, 1, 1382400, 0xb747834a +0, 1170, 1170, 1, 1382400, 0xb747834a +0, 1171, 1171, 1, 1382400, 0xb747834a +0, 1172, 1172, 1, 1382400, 0xb747834a +0, 1173, 1173, 1, 1382400, 0xb747834a +0, 1174, 1174, 1, 1382400, 0xb747834a +0, 1175, 1175, 1, 1382400, 0xb747834a +0, 1176, 1176, 1, 1382400, 0xb747834a +0, 1177, 1177, 1, 1382400, 0xb747834a +0, 1178, 1178, 1, 1382400, 0xb747834a +0, 1179, 1179, 1, 1382400, 0xb747834a +0, 1180, 1180, 1, 1382400, 0xb747834a +0, 1181, 1181, 1, 1382400, 0xb747834a +0, 1182, 1182, 1, 1382400, 0xb747834a +0, 1183, 1183, 1, 1382400, 0xb747834a +0, 1184, 1184, 1, 1382400, 0xb747834a +0, 1185, 1185, 1, 1382400, 0xb747834a +0, 1187, 1187, 1, 1382400, 0x3448baad +0, 1187, 1187, 1, 1382400, 0x3448baad +0, 1188, 1188, 1, 1382400, 0x3448baad +0, 1189, 1189, 1, 1382400, 0x3448baad +0, 1190, 1190, 1, 1382400, 0x3448baad +0, 1191, 1191, 1, 1382400, 0x3448baad +0, 1192, 1192, 1, 1382400, 0x3448baad +0, 1193, 1193, 1, 1382400, 0x3448baad +0, 1194, 1194, 1, 1382400, 0x3448baad +0, 1195, 1195, 1, 1382400, 0x3448baad +0, 1196, 1196, 1, 1382400, 0x3448baad +0, 1197, 1197, 1, 1382400, 0x3448baad +0, 1198, 1198, 1, 1382400, 0x3448baad +0, 1199, 1199, 1, 1382400, 0x3448baad +0, 1200, 1200, 1, 1382400, 0x00000000 +0, 1201, 1201, 1, 1382400, 0xaabe4f37 +0, 1202, 1202, 1, 1382400, 0xaabe4f37 +0, 1203, 1203, 1, 1382400, 0xaabe4f37 +0, 1204, 1204, 1, 1382400, 0xaabe4f37 +0, 1205, 1205, 1, 1382400, 0xaabe4f37 +0, 1206, 1206, 1, 1382400, 0xaabe4f37 +0, 1207, 1207, 1, 1382400, 0xaabe4f37 +0, 1208, 1208, 1, 1382400, 0xaabe4f37 +0, 1209, 1209, 1, 1382400, 0xaabe4f37 +0, 1210, 1210, 1, 1382400, 0xaabe4f37 +0, 1211, 1211, 1, 1382400, 0x00000000 +0, 1212, 1212, 1, 1382400, 0x00000000 +0, 1213, 1213, 1, 1382400, 0x00000000 +0, 1214, 1214, 1, 1382400, 0x00000000 +0, 1215, 1215, 1, 1382400, 0x00000000 +0, 1216, 1216, 1, 1382400, 0x00000000 +0, 1217, 1217, 1, 1382400, 0x00000000 +0, 1218, 1218, 1, 1382400, 0x00000000 +0, 1219, 1219, 1, 1382400, 0x00000000 +0, 1220, 1220, 1, 1382400, 0x00000000 +0, 1221, 1221, 1, 1382400, 0x00000000 +0, 1222, 1222, 1, 1382400, 0x00000000 +0, 1223, 1223, 1, 1382400, 0x00000000 +0, 1224, 1224, 1, 1382400, 0x00000000 +0, 1225, 1225, 1, 1382400, 0x00000000 +0, 1226, 1226, 1, 1382400, 0x00000000 +0, 1227, 1227, 1, 1382400, 0x00000000 +0, 1228, 1228, 1, 1382400, 0x00000000 +0, 1229, 1229, 1, 1382400, 0x00000000 +0, 1230, 1230, 1, 1382400, 0x00000000 +0, 1231, 1231, 1, 1382400, 0x00000000 +0, 1232, 1232, 1, 1382400, 0x00000000 +0, 1233, 1233, 1, 1382400, 0x00000000 +0, 1234, 1234, 1, 1382400, 0x00000000 +0, 1235, 1235, 1, 1382400, 0x00000000 +0, 1236, 1236, 1, 1382400, 0x00000000 +0, 1237, 1237, 1, 1382400, 0x00000000 +0, 1238, 1238, 1, 1382400, 0x00000000 +0, 1239, 1239, 1, 1382400, 0x00000000 +0, 1240, 1240, 1, 1382400, 0x00000000 +0, 1241, 1241, 1, 1382400, 0x00000000 +0, 1242, 1242, 1, 1382400, 0x00000000 +0, 1243, 1243, 1, 1382400, 0x00000000 +0, 1244, 1244, 1, 1382400, 0x00000000 +0, 1245, 1245, 1, 1382400, 0x00000000 +0, 1246, 1246, 1, 1382400, 0x00000000 +0, 1247, 1247, 1, 1382400, 0x00000000 +0, 1248, 1248, 1, 1382400, 0x00000000 +0, 1249, 1249, 1, 1382400, 0x00000000 +0, 1250, 1250, 1, 1382400, 0x00000000 +0, 1251, 1251, 1, 1382400, 0x00000000 +0, 1252, 1252, 1, 1382400, 0x00000000 +0, 1253, 1253, 1, 1382400, 0x00000000 +0, 1254, 1254, 1, 1382400, 0x00000000 +0, 1255, 1255, 1, 1382400, 0x00000000 +0, 1256, 1256, 1, 1382400, 0x00000000 +0, 1257, 1257, 1, 1382400, 0x00000000 +0, 1258, 1258, 1, 1382400, 0x00000000 +0, 1259, 1259, 1, 1382400, 0x00000000 +0, 1260, 1260, 1, 1382400, 0x00000000 +0, 1261, 1261, 1, 1382400, 0x00000000 +0, 1262, 1262, 1, 1382400, 0x00000000 +0, 1263, 1263, 1, 1382400, 0x00000000 +0, 1264, 1264, 1, 1382400, 0x00000000 +0, 1265, 1265, 1, 1382400, 0x00000000 +0, 1266, 1266, 1, 1382400, 0x00000000 +0, 1267, 1267, 1, 1382400, 0x00000000 +0, 1268, 1268, 1, 1382400, 0x00000000 +0, 1269, 1269, 1, 1382400, 0x00000000 +0, 1270, 1270, 1, 1382400, 0x00000000 +0, 1271, 1271, 1, 1382400, 0x00000000 +0, 1272, 1272, 1, 1382400, 0x00000000 +0, 1273, 1273, 1, 1382400, 0x00000000 +0, 1274, 1274, 1, 1382400, 0x00000000 +0, 1275, 1275, 1, 1382400, 0x00000000 +0, 1276, 1276, 1, 1382400, 0x00000000 +0, 1277, 1277, 1, 1382400, 0x00000000 +0, 1278, 1278, 1, 1382400, 0x00000000 +0, 1279, 1279, 1, 1382400, 0x00000000 +0, 1280, 1280, 1, 1382400, 0x00000000 +0, 1281, 1281, 1, 1382400, 0x00000000 +0, 1282, 1282, 1, 1382400, 0x00000000 +0, 1283, 1283, 1, 1382400, 0x00000000 +0, 1284, 1284, 1, 1382400, 0x00000000 +0, 1285, 1285, 1, 1382400, 0x00000000 +0, 1286, 1286, 1, 1382400, 0x00000000 +0, 1287, 1287, 1, 1382400, 0x00000000 +0, 1288, 1288, 1, 1382400, 0x00000000 +0, 1289, 1289, 1, 1382400, 0x00000000 +0, 1290, 1290, 1, 1382400, 0x00000000 +0, 1291, 1291, 1, 1382400, 0x00000000 +0, 1292, 1292, 1, 1382400, 0x00000000 +0, 1293, 1293, 1, 1382400, 0x00000000 +0, 1294, 1294, 1, 1382400, 0x00000000 +0, 1295, 1295, 1, 1382400, 0x00000000 +0, 1296, 1296, 1, 1382400, 0x00000000 +0, 1297, 1297, 1, 1382400, 0x00000000 +0, 1298, 1298, 1, 1382400, 0x00000000 +0, 1299, 1299, 1, 1382400, 0x00000000 +0, 1300, 1300, 1, 1382400, 0x00000000 +0, 1301, 1301, 1, 1382400, 0x00000000 +0, 1302, 1302, 1, 1382400, 0x00000000 +0, 1303, 1303, 1, 1382400, 0x00000000 +0, 1304, 1304, 1, 1382400, 0x00000000 +0, 1305, 1305, 1, 1382400, 0x00000000 +0, 1306, 1306, 1, 1382400, 0x00000000 +0, 1307, 1307, 1, 1382400, 0x00000000 +0, 1308, 1308, 1, 1382400, 0x00000000 +0, 1309, 1309, 1, 1382400, 0x00000000 +0, 1310, 1310, 1, 1382400, 0x00000000 +0, 1311, 1311, 1, 1382400, 0x00000000 +0, 1312, 1312, 1, 1382400, 0x00000000 +0, 1313, 1313, 1, 1382400, 0x00000000 +0, 1314, 1314, 1, 1382400, 0x00000000 +0, 1315, 1315, 1, 1382400, 0x00000000 +0, 1316, 1316, 1, 1382400, 0x00000000 +0, 1317, 1317, 1, 1382400, 0x00000000 +0, 1318, 1318, 1, 1382400, 0x00000000 +0, 1319, 1319, 1, 1382400, 0x00000000 +0, 1320, 1320, 1, 1382400, 0x00000000 +0, 1321, 1321, 1, 1382400, 0x00000000 +0, 1322, 1322, 1, 1382400, 0x00000000 +0, 1323, 1323, 1, 1382400, 0x00000000 +0, 1324, 1324, 1, 1382400, 0x00000000 +0, 1325, 1325, 1, 1382400, 0x00000000 +0, 1326, 1326, 1, 1382400, 0x00000000 +0, 1327, 1327, 1, 1382400, 0x00000000 +0, 1328, 1328, 1, 1382400, 0x00000000 +0, 1329, 1329, 1, 1382400, 0x00000000 +0, 1330, 1330, 1, 1382400, 0x00000000 +0, 1331, 1331, 1, 1382400, 0x00000000 +0, 1332, 1332, 1, 1382400, 0x00000000 +0, 1333, 1333, 1, 1382400, 0x00000000 +0, 1334, 1334, 1, 1382400, 0x00000000 +0, 1335, 1335, 1, 1382400, 0x00000000 +0, 1336, 1336, 1, 1382400, 0x00000000 +0, 1337, 1337, 1, 1382400, 0x00000000 +0, 1338, 1338, 1, 1382400, 0x00000000 +0, 1339, 1339, 1, 1382400, 0x00000000 +0, 1340, 1340, 1, 1382400, 0x00000000 +0, 1341, 1341, 1, 1382400, 0x00000000 +0, 1342, 1342, 1, 1382400, 0x00000000 +0, 1343, 1343, 1, 1382400, 0x00000000 +0, 1344, 1344, 1, 1382400, 0x00000000 +0, 1345, 1345, 1, 1382400, 0x00000000 +0, 1346, 1346, 1, 1382400, 0x00000000 +0, 1347, 1347, 1, 1382400, 0x00000000 +0, 1348, 1348, 1, 1382400, 0x00000000 +0, 1349, 1349, 1, 1382400, 0x00000000 +0, 1350, 1350, 1, 1382400, 0x00000000 +0, 1351, 1351, 1, 1382400, 0x00000000 +0, 1352, 1352, 1, 1382400, 0x00000000 +0, 1353, 1353, 1, 1382400, 0x00000000 +0, 1354, 1354, 1, 1382400, 0x00000000 +0, 1355, 1355, 1, 1382400, 0x00000000 +0, 1356, 1356, 1, 1382400, 0x00000000 +0, 1357, 1357, 1, 1382400, 0x00000000 +0, 1358, 1358, 1, 1382400, 0x00000000 +0, 1359, 1359, 1, 1382400, 0x00000000 +0, 1360, 1360, 1, 1382400, 0x00000000 +0, 1361, 1361, 1, 1382400, 0x00000000 +0, 1362, 1362, 1, 1382400, 0x00000000 +0, 1363, 1363, 1, 1382400, 0x00000000 +0, 1364, 1364, 1, 1382400, 0x00000000 +0, 1365, 1365, 1, 1382400, 0x00000000 +0, 1366, 1366, 1, 1382400, 0x00000000 +0, 1368, 1368, 1, 1382400, 0x8a48cd6f +0, 1368, 1368, 1, 1382400, 0x8a48cd6f +0, 1369, 1369, 1, 1382400, 0x8a48cd6f +0, 1370, 1370, 1, 1382400, 0x8a48cd6f +0, 1371, 1371, 1, 1382400, 0x8a48cd6f +0, 1372, 1372, 1, 1382400, 0x8a48cd6f +0, 1373, 1373, 1, 1382400, 0x8a48cd6f +0, 1374, 1374, 1, 1382400, 0x8a48cd6f +0, 1375, 1375, 1, 1382400, 0x8a48cd6f +0, 1376, 1376, 1, 1382400, 0x00000000 +0, 1377, 1377, 1, 1382400, 0x00000000 +0, 1378, 1378, 1, 1382400, 0x00000000 +0, 1379, 1379, 1, 1382400, 0x00000000 +0, 1380, 1380, 1, 1382400, 0x00000000 +0, 1381, 1381, 1, 1382400, 0x00000000 +0, 1382, 1382, 1, 1382400, 0x00000000 +0, 1383, 1383, 1, 1382400, 0x00000000 +0, 1384, 1384, 1, 1382400, 0x00000000 +0, 1385, 1385, 1, 1382400, 0x00000000 +0, 1386, 1386, 1, 1382400, 0x00000000 +0, 1387, 1387, 1, 1382400, 0x00000000 +0, 1388, 1388, 1, 1382400, 0x00000000 +0, 1389, 1389, 1, 1382400, 0x00000000 +0, 1390, 1390, 1, 1382400, 0x00000000 +0, 1391, 1391, 1, 1382400, 0x00000000 +0, 1392, 1392, 1, 1382400, 0x00000000 +0, 1393, 1393, 1, 1382400, 0x00000000 +0, 1394, 1394, 1, 1382400, 0x00000000 +0, 1395, 1395, 1, 1382400, 0x00000000 +0, 1396, 1396, 1, 1382400, 0x00000000 +0, 1397, 1397, 1, 1382400, 0x00000000 +0, 1398, 1398, 1, 1382400, 0x00000000 +0, 1399, 1399, 1, 1382400, 0x00000000 +0, 1400, 1400, 1, 1382400, 0x00000000 +0, 1401, 1401, 1, 1382400, 0x00000000 +0, 1402, 1402, 1, 1382400, 0x00000000 +0, 1403, 1403, 1, 1382400, 0x00000000 +0, 1404, 1404, 1, 1382400, 0x00000000 +0, 1405, 1405, 1, 1382400, 0x00000000 +0, 1406, 1406, 1, 1382400, 0x00000000 +0, 1407, 1407, 1, 1382400, 0x00000000 +0, 1408, 1408, 1, 1382400, 0x00000000 +0, 1409, 1409, 1, 1382400, 0x00000000 +0, 1410, 1410, 1, 1382400, 0x00000000 +0, 1411, 1411, 1, 1382400, 0x00000000 +0, 1412, 1412, 1, 1382400, 0x00000000 +0, 1413, 1413, 1, 1382400, 0x00000000 +0, 1414, 1414, 1, 1382400, 0x00000000 +0, 1415, 1415, 1, 1382400, 0x00000000 +0, 1416, 1416, 1, 1382400, 0x00000000 +0, 1417, 1417, 1, 1382400, 0x00000000 +0, 1418, 1418, 1, 1382400, 0x00000000 +0, 1419, 1419, 1, 1382400, 0x00000000 +0, 1420, 1420, 1, 1382400, 0x00000000 +0, 1421, 1421, 1, 1382400, 0x00000000 +0, 1422, 1422, 1, 1382400, 0x00000000 +0, 1423, 1423, 1, 1382400, 0x00000000 +0, 1424, 1424, 1, 1382400, 0x00000000 +0, 1425, 1425, 1, 1382400, 0x00000000 +0, 1426, 1426, 1, 1382400, 0x00000000 +0, 1427, 1427, 1, 1382400, 0x00000000 +0, 1428, 1428, 1, 1382400, 0x00000000 +0, 1429, 1429, 1, 1382400, 0x00000000 +0, 1430, 1430, 1, 1382400, 0x00000000 +0, 1431, 1431, 1, 1382400, 0x00000000 +0, 1432, 1432, 1, 1382400, 0x00000000 +0, 1433, 1433, 1, 1382400, 0x00000000 +0, 1434, 1434, 1, 1382400, 0x00000000 +0, 1435, 1435, 1, 1382400, 0x00000000 +0, 1436, 1436, 1, 1382400, 0x00000000 +0, 1437, 1437, 1, 1382400, 0x00000000 +0, 1438, 1438, 1, 1382400, 0x00000000 +0, 1439, 1439, 1, 1382400, 0x00000000 +0, 1440, 1440, 1, 1382400, 0x00000000 +0, 1441, 1441, 1, 1382400, 0x00000000 +0, 1442, 1442, 1, 1382400, 0x00000000 +0, 1443, 1443, 1, 1382400, 0x00000000 +0, 1444, 1444, 1, 1382400, 0x00000000 +0, 1445, 1445, 1, 1382400, 0x00000000 +0, 1446, 1446, 1, 1382400, 0x00000000 +0, 1447, 1447, 1, 1382400, 0x00000000 +0, 1448, 1448, 1, 1382400, 0x00000000 +0, 1449, 1449, 1, 1382400, 0x00000000 +0, 1450, 1450, 1, 1382400, 0x00000000 +0, 1451, 1451, 1, 1382400, 0x00000000 +0, 1452, 1452, 1, 1382400, 0x00000000 +0, 1453, 1453, 1, 1382400, 0x00000000 +0, 1454, 1454, 1, 1382400, 0x00000000 +0, 1455, 1455, 1, 1382400, 0x00000000 +0, 1456, 1456, 1, 1382400, 0x00000000 +0, 1457, 1457, 1, 1382400, 0x00000000 +0, 1458, 1458, 1, 1382400, 0x00000000 +0, 1459, 1459, 1, 1382400, 0x00000000 +0, 1460, 1460, 1, 1382400, 0x00000000 +0, 1461, 1461, 1, 1382400, 0x00000000 +0, 1462, 1462, 1, 1382400, 0x00000000 +0, 1463, 1463, 1, 1382400, 0x00000000 +0, 1464, 1464, 1, 1382400, 0x00000000 +0, 1465, 1465, 1, 1382400, 0x00000000 +0, 1466, 1466, 1, 1382400, 0x00000000 +0, 1467, 1467, 1, 1382400, 0x00000000 +0, 1468, 1468, 1, 1382400, 0x00000000 +0, 1469, 1469, 1, 1382400, 0x00000000 +0, 1470, 1470, 1, 1382400, 0x00000000 +0, 1471, 1471, 1, 1382400, 0x00000000 +0, 1472, 1472, 1, 1382400, 0x00000000 +0, 1473, 1473, 1, 1382400, 0x00000000 +0, 1474, 1474, 1, 1382400, 0x00000000 +0, 1475, 1475, 1, 1382400, 0x00000000 +0, 1477, 1477, 1, 1382400, 0x49518c43 +0, 1477, 1477, 1, 1382400, 0x49518c43 +0, 1478, 1478, 1, 1382400, 0x49518c43 +0, 1479, 1479, 1, 1382400, 0x49518c43 +0, 1480, 1480, 1, 1382400, 0x49518c43 +0, 1481, 1481, 1, 1382400, 0x49518c43 +0, 1482, 1482, 1, 1382400, 0x49518c43 +0, 1483, 1483, 1, 1382400, 0x49518c43 +0, 1484, 1484, 1, 1382400, 0x00000000 +0, 1485, 1485, 1, 1382400, 0x00000000 +0, 1486, 1486, 1, 1382400, 0x00000000 +0, 1487, 1487, 1, 1382400, 0x00000000 +0, 1488, 1488, 1, 1382400, 0x00000000 +0, 1489, 1489, 1, 1382400, 0x00000000 +0, 1490, 1490, 1, 1382400, 0x00000000 +0, 1491, 1491, 1, 1382400, 0x00000000 +0, 1492, 1492, 1, 1382400, 0x00000000 +0, 1493, 1493, 1, 1382400, 0x00000000 +0, 1494, 1494, 1, 1382400, 0x00000000 +0, 1495, 1495, 1, 1382400, 0x00000000 +0, 1496, 1496, 1, 1382400, 0x00000000 +0, 1497, 1497, 1, 1382400, 0x00000000 +0, 1498, 1498, 1, 1382400, 0x00000000 +0, 1500, 1500, 1, 1382400, 0x4a72fa21 +0, 1500, 1500, 1, 1382400, 0x4a72fa21 +0, 1501, 1501, 1, 1382400, 0x4a72fa21 +0, 1502, 1502, 1, 1382400, 0x4a72fa21 +0, 1503, 1503, 1, 1382400, 0x4a72fa21 +0, 1504, 1504, 1, 1382400, 0x4a72fa21 +0, 1505, 1505, 1, 1382400, 0x4a72fa21 +0, 1506, 1506, 1, 1382400, 0x4a72fa21 +0, 1507, 1507, 1, 1382400, 0x4a72fa21 +0, 1508, 1508, 1, 1382400, 0x4a72fa21 +0, 1509, 1509, 1, 1382400, 0x4a72fa21 +0, 1510, 1510, 1, 1382400, 0x00000000 +0, 1511, 1511, 1, 1382400, 0xa82f7de8 +0, 1512, 1512, 1, 1382400, 0xa82f7de8 +0, 1513, 1513, 1, 1382400, 0xa82f7de8 +0, 1514, 1514, 1, 1382400, 0xa82f7de8 +0, 1515, 1515, 1, 1382400, 0xa82f7de8 +0, 1516, 1516, 1, 1382400, 0xa82f7de8 +0, 1517, 1517, 1, 1382400, 0xa82f7de8 +0, 1518, 1518, 1, 1382400, 0x00000000 +0, 1519, 1519, 1, 1382400, 0x00000000 +0, 1521, 1521, 1, 1382400, 0xeba0b5f3 +0, 1521, 1521, 1, 1382400, 0xeba0b5f3 +0, 1522, 1522, 1, 1382400, 0xeba0b5f3 +0, 1523, 1523, 1, 1382400, 0xeba0b5f3 +0, 1524, 1524, 1, 1382400, 0xeba0b5f3 +0, 1525, 1525, 1, 1382400, 0xeba0b5f3 +0, 1526, 1526, 1, 1382400, 0xeba0b5f3 +0, 1527, 1527, 1, 1382400, 0xeba0b5f3 +0, 1528, 1528, 1, 1382400, 0xeba0b5f3 +0, 1530, 1530, 1, 1382400, 0xd6a91770 +0, 1530, 1530, 1, 1382400, 0xd6a91770 +0, 1531, 1531, 1, 1382400, 0xd6a91770 +0, 1532, 1532, 1, 1382400, 0xd6a91770 +0, 1533, 1533, 1, 1382400, 0xd6a91770 +0, 1534, 1534, 1, 1382400, 0xd6a91770 +0, 1535, 1535, 1, 1382400, 0xd6a91770 +0, 1536, 1536, 1, 1382400, 0xd6a91770 +0, 1537, 1537, 1, 1382400, 0xd6a91770 +0, 1538, 1538, 1, 1382400, 0xd6a91770 +0, 1539, 1539, 1, 1382400, 0x00000000 +0, 1540, 1540, 1, 1382400, 0x222f827c +0, 1541, 1541, 1, 1382400, 0x222f827c +0, 1542, 1542, 1, 1382400, 0x222f827c +0, 1543, 1543, 1, 1382400, 0x222f827c +0, 1544, 1544, 1, 1382400, 0x222f827c +0, 1545, 1545, 1, 1382400, 0x222f827c +0, 1546, 1546, 1, 1382400, 0x222f827c +0, 1547, 1547, 1, 1382400, 0x222f827c +0, 1548, 1548, 1, 1382400, 0x222f827c +0, 1549, 1549, 1, 1382400, 0x00000000 +0, 1550, 1550, 1, 1382400, 0x00000000 +0, 1551, 1551, 1, 1382400, 0x00000000 +0, 1552, 1552, 1, 1382400, 0x00000000 +0, 1553, 1553, 1, 1382400, 0x00000000 +0, 1554, 1554, 1, 1382400, 0x00000000 +0, 1555, 1555, 1, 1382400, 0x00000000 +0, 1556, 1556, 1, 1382400, 0x00000000 +0, 1557, 1557, 1, 1382400, 0x00000000 +0, 1558, 1558, 1, 1382400, 0x00000000 +0, 1559, 1559, 1, 1382400, 0x00000000 +0, 1560, 1560, 1, 1382400, 0x00000000 +0, 1561, 1561, 1, 1382400, 0x00000000 +0, 1562, 1562, 1, 1382400, 0x00000000 +0, 1563, 1563, 1, 1382400, 0x00000000 +0, 1564, 1564, 1, 1382400, 0x00000000 +0, 1565, 1565, 1, 1382400, 0x00000000 +0, 1566, 1566, 1, 1382400, 0x00000000 +0, 1567, 1567, 1, 1382400, 0x00000000 +0, 1568, 1568, 1, 1382400, 0x00000000 +0, 1569, 1569, 1, 1382400, 0x00000000 +0, 1570, 1570, 1, 1382400, 0x00000000 +0, 1571, 1571, 1, 1382400, 0x00000000 +0, 1572, 1572, 1, 1382400, 0x00000000 +0, 1573, 1573, 1, 1382400, 0x00000000 +0, 1574, 1574, 1, 1382400, 0x00000000 +0, 1575, 1575, 1, 1382400, 0x00000000 +0, 1576, 1576, 1, 1382400, 0x00000000 +0, 1577, 1577, 1, 1382400, 0x00000000 +0, 1578, 1578, 1, 1382400, 0x00000000 +0, 1579, 1579, 1, 1382400, 0x00000000 +0, 1580, 1580, 1, 1382400, 0x00000000 +0, 1581, 1581, 1, 1382400, 0x00000000 +0, 1582, 1582, 1, 1382400, 0x00000000 +0, 1583, 1583, 1, 1382400, 0x00000000 +0, 1584, 1584, 1, 1382400, 0x00000000 +0, 1585, 1585, 1, 1382400, 0x00000000 +0, 1586, 1586, 1, 1382400, 0x00000000 +0, 1587, 1587, 1, 1382400, 0x00000000 +0, 1588, 1588, 1, 1382400, 0x00000000 +0, 1589, 1589, 1, 1382400, 0x00000000 +0, 1590, 1590, 1, 1382400, 0x00000000 +0, 1591, 1591, 1, 1382400, 0x00000000 +0, 1592, 1592, 1, 1382400, 0x00000000 +0, 1593, 1593, 1, 1382400, 0x00000000 +0, 1594, 1594, 1, 1382400, 0x00000000 +0, 1595, 1595, 1, 1382400, 0x00000000 +0, 1596, 1596, 1, 1382400, 0x00000000 +0, 1597, 1597, 1, 1382400, 0x00000000 +0, 1598, 1598, 1, 1382400, 0x00000000 +0, 1599, 1599, 1, 1382400, 0x00000000 +0, 1600, 1600, 1, 1382400, 0x00000000 +0, 1601, 1601, 1, 1382400, 0x00000000 +0, 1602, 1602, 1, 1382400, 0x00000000 +0, 1603, 1603, 1, 1382400, 0x00000000 +0, 1604, 1604, 1, 1382400, 0x00000000 +0, 1606, 1606, 1, 1382400, 0x3270f4ff +0, 1606, 1606, 1, 1382400, 0x3270f4ff +0, 1607, 1607, 1, 1382400, 0x3270f4ff +0, 1608, 1608, 1, 1382400, 0x3270f4ff +0, 1609, 1609, 1, 1382400, 0x3270f4ff +0, 1610, 1610, 1, 1382400, 0x3270f4ff +0, 1611, 1611, 1, 1382400, 0x3270f4ff +0, 1612, 1612, 1, 1382400, 0x3270f4ff +0, 1613, 1613, 1, 1382400, 0x3270f4ff +0, 1614, 1614, 1, 1382400, 0x3270f4ff +0, 1615, 1615, 1, 1382400, 0x3270f4ff +0, 1617, 1617, 1, 1382400, 0x40813cb3 +0, 1617, 1617, 1, 1382400, 0x40813cb3 +0, 1618, 1618, 1, 1382400, 0x40813cb3 +0, 1619, 1619, 1, 1382400, 0x40813cb3 +0, 1620, 1620, 1, 1382400, 0x40813cb3 +0, 1621, 1621, 1, 1382400, 0x40813cb3 +0, 1622, 1622, 1, 1382400, 0x40813cb3 +0, 1623, 1623, 1, 1382400, 0x9d8fde41 +0, 1624, 1624, 1, 1382400, 0x9d8fde41 +0, 1625, 1625, 1, 1382400, 0x9d8fde41 +0, 1626, 1626, 1, 1382400, 0x9d8fde41 +0, 1627, 1627, 1, 1382400, 0x9d8fde41 +0, 1628, 1628, 1, 1382400, 0x9d8fde41 +0, 1629, 1629, 1, 1382400, 0x9d8fde41 +0, 1630, 1630, 1, 1382400, 0x9d8fde41 +0, 1631, 1631, 1, 1382400, 0x9d8fde41 +0, 1632, 1632, 1, 1382400, 0x00000000 +0, 1633, 1633, 1, 1382400, 0x00000000 +0, 1634, 1634, 1, 1382400, 0x00000000 +0, 1636, 1636, 1, 1382400, 0xc6d7a701 +0, 1636, 1636, 1, 1382400, 0xc6d7a701 +0, 1637, 1637, 1, 1382400, 0xc6d7a701 +0, 1638, 1638, 1, 1382400, 0xc6d7a701 +0, 1639, 1639, 1, 1382400, 0xc6d7a701 +0, 1640, 1640, 1, 1382400, 0xc6d7a701 +0, 1642, 1642, 1, 1382400, 0x9d45f2dc +0, 1642, 1642, 1, 1382400, 0x9d45f2dc +0, 1643, 1643, 1, 1382400, 0x9d45f2dc +0, 1644, 1644, 1, 1382400, 0x9d45f2dc +0, 1645, 1645, 1, 1382400, 0x9d45f2dc +0, 1646, 1646, 1, 1382400, 0x9d45f2dc +0, 1647, 1647, 1, 1382400, 0x9d45f2dc +0, 1648, 1648, 1, 1382400, 0x9d45f2dc +0, 1649, 1649, 1, 1382400, 0x00000000 +0, 1650, 1650, 1, 1382400, 0x8525ee40 +0, 1651, 1651, 1, 1382400, 0x8525ee40 +0, 1652, 1652, 1, 1382400, 0x8525ee40 +0, 1653, 1653, 1, 1382400, 0x8525ee40 +0, 1654, 1654, 1, 1382400, 0x8525ee40 +0, 1655, 1655, 1, 1382400, 0x8525ee40 +0, 1656, 1656, 1, 1382400, 0x5b26b98b +0, 1657, 1657, 1, 1382400, 0x5b26b98b +0, 1658, 1658, 1, 1382400, 0x5b26b98b +0, 1659, 1659, 1, 1382400, 0x5b26b98b +0, 1660, 1660, 1, 1382400, 0x5b26b98b +0, 1661, 1661, 1, 1382400, 0x5b26b98b +0, 1662, 1662, 1, 1382400, 0x5b26b98b +0, 1663, 1663, 1, 1382400, 0x5b26b98b +0, 1664, 1664, 1, 1382400, 0x00000000 +0, 1665, 1665, 1, 1382400, 0x51be311f +0, 1666, 1666, 1, 1382400, 0x51be311f +0, 1667, 1667, 1, 1382400, 0x51be311f +0, 1668, 1668, 1, 1382400, 0x51be311f +0, 1669, 1669, 1, 1382400, 0x51be311f +0, 1670, 1670, 1, 1382400, 0x51be311f +0, 1671, 1671, 1, 1382400, 0x51be311f +0, 1672, 1672, 1, 1382400, 0x51be311f +0, 1673, 1673, 1, 1382400, 0x00000000 +0, 1674, 1674, 1, 1382400, 0x00000000 +0, 1675, 1675, 1, 1382400, 0x00000000 +0, 1676, 1676, 1, 1382400, 0x00000000 +0, 1677, 1677, 1, 1382400, 0x00000000 +0, 1678, 1678, 1, 1382400, 0x00000000 +0, 1679, 1679, 1, 1382400, 0x00000000 +0, 1680, 1680, 1, 1382400, 0x00000000 +0, 1681, 1681, 1, 1382400, 0x00000000 +0, 1682, 1682, 1, 1382400, 0x00000000 +0, 1683, 1683, 1, 1382400, 0x00000000 +0, 1684, 1684, 1, 1382400, 0x00000000 +0, 1685, 1685, 1, 1382400, 0x00000000 +0, 1686, 1686, 1, 1382400, 0x00000000 +0, 1687, 1687, 1, 1382400, 0x00000000 +0, 1688, 1688, 1, 1382400, 0x00000000 +0, 1689, 1689, 1, 1382400, 0x00000000 +0, 1690, 1690, 1, 1382400, 0x00000000 +0, 1691, 1691, 1, 1382400, 0x00000000 +0, 1692, 1692, 1, 1382400, 0x00000000 +0, 1693, 1693, 1, 1382400, 0x00000000 +0, 1694, 1694, 1, 1382400, 0x00000000 +0, 1695, 1695, 1, 1382400, 0x00000000 +0, 1696, 1696, 1, 1382400, 0x00000000 +0, 1697, 1697, 1, 1382400, 0x00000000 +0, 1698, 1698, 1, 1382400, 0x00000000 +0, 1699, 1699, 1, 1382400, 0x00000000 +0, 1700, 1700, 1, 1382400, 0x00000000 +0, 1701, 1701, 1, 1382400, 0x00000000 +0, 1702, 1702, 1, 1382400, 0x00000000 +0, 1703, 1703, 1, 1382400, 0x00000000 +0, 1704, 1704, 1, 1382400, 0x00000000 +0, 1705, 1705, 1, 1382400, 0x00000000 +0, 1706, 1706, 1, 1382400, 0x00000000 +0, 1707, 1707, 1, 1382400, 0x00000000 +0, 1708, 1708, 1, 1382400, 0x00000000 +0, 1709, 1709, 1, 1382400, 0x00000000 +0, 1710, 1710, 1, 1382400, 0x00000000 +0, 1711, 1711, 1, 1382400, 0x00000000 +0, 1713, 1713, 1, 1382400, 0x00a4f2a3 +0, 1713, 1713, 1, 1382400, 0x00a4f2a3 +0, 1714, 1714, 1, 1382400, 0x00a4f2a3 +0, 1715, 1715, 1, 1382400, 0x00a4f2a3 +0, 1716, 1716, 1, 1382400, 0x00a4f2a3 +0, 1717, 1717, 1, 1382400, 0x00a4f2a3 +0, 1718, 1718, 1, 1382400, 0x00a4f2a3 +0, 1719, 1719, 1, 1382400, 0x00a4f2a3 +0, 1720, 1720, 1, 1382400, 0x00a4f2a3 +0, 1721, 1721, 1, 1382400, 0x00a4f2a3 +0, 1722, 1722, 1, 1382400, 0x00000000 +0, 1723, 1723, 1, 1382400, 0x00000000 +0, 1724, 1724, 1, 1382400, 0x00000000 +0, 1725, 1725, 1, 1382400, 0x00000000 +0, 1726, 1726, 1, 1382400, 0x00000000 +0, 1727, 1727, 1, 1382400, 0x00000000 +0, 1728, 1728, 1, 1382400, 0x00000000 +0, 1729, 1729, 1, 1382400, 0x00000000 +0, 1730, 1730, 1, 1382400, 0x00000000 +0, 1731, 1731, 1, 1382400, 0x00000000 +0, 1732, 1732, 1, 1382400, 0x00000000 +0, 1734, 1734, 1, 1382400, 0x40a445e8 +0, 1734, 1734, 1, 1382400, 0x40a445e8 +0, 1735, 1735, 1, 1382400, 0x40a445e8 +0, 1736, 1736, 1, 1382400, 0x40a445e8 +0, 1737, 1737, 1, 1382400, 0x40a445e8 +0, 1738, 1738, 1, 1382400, 0x40a445e8 +0, 1739, 1739, 1, 1382400, 0x40a445e8 +0, 1740, 1740, 1, 1382400, 0x40a445e8 +0, 1741, 1741, 1, 1382400, 0x40a445e8 +0, 1742, 1742, 1, 1382400, 0x00000000 +0, 1743, 1743, 1, 1382400, 0x00000000 +0, 1744, 1744, 1, 1382400, 0x00000000 +0, 1745, 1745, 1, 1382400, 0x00000000 +0, 1746, 1746, 1, 1382400, 0x00000000 +0, 1747, 1747, 1, 1382400, 0x00000000 +0, 1748, 1748, 1, 1382400, 0x00000000 +0, 1749, 1749, 1, 1382400, 0x00000000 +0, 1750, 1750, 1, 1382400, 0x00000000 +0, 1751, 1751, 1, 1382400, 0x00000000 +0, 1752, 1752, 1, 1382400, 0x00000000 +0, 1753, 1753, 1, 1382400, 0x00000000 +0, 1754, 1754, 1, 1382400, 0x00000000 +0, 1755, 1755, 1, 1382400, 0x00000000 +0, 1756, 1756, 1, 1382400, 0x00000000 +0, 1757, 1757, 1, 1382400, 0x00000000 +0, 1758, 1758, 1, 1382400, 0x00000000 +0, 1759, 1759, 1, 1382400, 0x00000000 +0, 1760, 1760, 1, 1382400, 0x00000000 +0, 1761, 1761, 1, 1382400, 0x00000000 +0, 1762, 1762, 1, 1382400, 0x00000000 +0, 1763, 1763, 1, 1382400, 0x00000000 +0, 1764, 1764, 1, 1382400, 0x00000000 +0, 1765, 1765, 1, 1382400, 0x00000000 +0, 1766, 1766, 1, 1382400, 0x00000000 +0, 1767, 1767, 1, 1382400, 0x00000000 +0, 1768, 1768, 1, 1382400, 0x00000000 +0, 1769, 1769, 1, 1382400, 0x00000000 +0, 1770, 1770, 1, 1382400, 0x00000000 +0, 1771, 1771, 1, 1382400, 0x00000000 +0, 1772, 1772, 1, 1382400, 0x00000000 +0, 1773, 1773, 1, 1382400, 0x00000000 +0, 1774, 1774, 1, 1382400, 0x00000000 +0, 1775, 1775, 1, 1382400, 0x00000000 +0, 1776, 1776, 1, 1382400, 0x00000000 +0, 1777, 1777, 1, 1382400, 0x00000000 +0, 1778, 1778, 1, 1382400, 0x00000000 +0, 1779, 1779, 1, 1382400, 0x00000000 +0, 1780, 1780, 1, 1382400, 0x00000000 +0, 1781, 1781, 1, 1382400, 0x00000000 +0, 1782, 1782, 1, 1382400, 0x00000000 +0, 1783, 1783, 1, 1382400, 0x00000000 +0, 1784, 1784, 1, 1382400, 0x00000000 +0, 1785, 1785, 1, 1382400, 0x00000000 +0, 1786, 1786, 1, 1382400, 0x00000000 +0, 1788, 1788, 1, 1382400, 0x43ef5128 +0, 1788, 1788, 1, 1382400, 0x43ef5128 +0, 1789, 1789, 1, 1382400, 0x43ef5128 +0, 1790, 1790, 1, 1382400, 0x43ef5128 +0, 1791, 1791, 1, 1382400, 0x43ef5128 +0, 1792, 1792, 1, 1382400, 0x43ef5128 +0, 1793, 1793, 1, 1382400, 0x43ef5128 +0, 1794, 1794, 1, 1382400, 0x43ef5128 +0, 1795, 1795, 1, 1382400, 0x43ef5128 +0, 1796, 1796, 1, 1382400, 0x43ef5128 +0, 1797, 1797, 1, 1382400, 0x43ef5128 +0, 1798, 1798, 1, 1382400, 0x43ef5128 +0, 1799, 1799, 1, 1382400, 0x3c3e3819 +0, 1800, 1800, 1, 1382400, 0x3c3e3819 +0, 1801, 1801, 1, 1382400, 0x3c3e3819 +0, 1802, 1802, 1, 1382400, 0x3c3e3819 +0, 1803, 1803, 1, 1382400, 0x3c3e3819 +0, 1804, 1804, 1, 1382400, 0x3c3e3819 +0, 1805, 1805, 1, 1382400, 0x00000000 diff --git a/tests/ref/fate/sub2video_time_limited b/tests/ref/fate/sub2video_time_limited index 9fb6fb06f9..3a7cca384a 100644 --- a/tests/ref/fate/sub2video_time_limited +++ b/tests/ref/fate/sub2video_time_limited @@ -1,8 +1,80 @@ -#tb 0: 1/25 +#tb 0: 1/5 #media_type 0: video #codec_id 0: rawvideo #dimensions 0: 1920x1080 -#sar 0: 0/1 -0, 2, 2, 1, 8294400, 0x00000000 +#sar 0: 1/1 +0, 0, 0, 1, 8294400, 0x00000000 +0, 1, 1, 1, 8294400, 0x00000000 0, 2, 2, 1, 8294400, 0xa87c518f +0, 3, 3, 1, 8294400, 0xa87c518f +0, 4, 4, 1, 8294400, 0xa87c518f +0, 5, 5, 1, 8294400, 0xa87c518f +0, 6, 6, 1, 8294400, 0xa87c518f +0, 7, 7, 1, 8294400, 0xa87c518f +0, 8, 8, 1, 8294400, 0xa87c518f +0, 9, 9, 1, 8294400, 0xa87c518f 0, 10, 10, 1, 8294400, 0xa87c518f +0, 11, 11, 1, 8294400, 0xa87c518f +0, 12, 12, 1, 8294400, 0xa87c518f +0, 13, 13, 1, 8294400, 0xa87c518f +0, 14, 14, 1, 8294400, 0xa87c518f +0, 15, 15, 1, 8294400, 0xa87c518f +0, 16, 16, 1, 8294400, 0xa87c518f +0, 17, 17, 1, 8294400, 0xa87c518f +0, 18, 18, 1, 8294400, 0xa87c518f +0, 19, 19, 1, 8294400, 0xa87c518f +0, 20, 20, 1, 8294400, 0xa87c518f +0, 21, 21, 1, 8294400, 0xa87c518f +0, 22, 22, 1, 8294400, 0xa87c518f +0, 23, 23, 1, 8294400, 0xa87c518f +0, 24, 24, 1, 8294400, 0xa87c518f +0, 25, 25, 1, 8294400, 0xa87c518f +0, 26, 26, 1, 8294400, 0xa87c518f +0, 27, 27, 1, 8294400, 0xa87c518f +0, 28, 28, 1, 8294400, 0xa87c518f +0, 29, 29, 1, 8294400, 0xa87c518f +0, 30, 30, 1, 8294400, 0xa87c518f +0, 31, 31, 1, 8294400, 0xa87c518f +0, 32, 32, 1, 8294400, 0xa87c518f +0, 33, 33, 1, 8294400, 0xa87c518f +0, 34, 34, 1, 8294400, 0xa87c518f +0, 35, 35, 1, 8294400, 0xa87c518f +0, 36, 36, 1, 8294400, 0xa87c518f +0, 37, 37, 1, 8294400, 0xa87c518f +0, 38, 38, 1, 8294400, 0xa87c518f +0, 39, 39, 1, 8294400, 0xa87c518f +0, 40, 40, 1, 8294400, 0xa87c518f +0, 41, 41, 1, 8294400, 0xa87c518f +0, 42, 42, 1, 8294400, 0xa87c518f +0, 43, 43, 1, 8294400, 0xa87c518f +0, 44, 44, 1, 8294400, 0xa87c518f +0, 45, 45, 1, 8294400, 0xa87c518f +0, 46, 46, 1, 8294400, 0xa87c518f +0, 47, 47, 1, 8294400, 0xa87c518f +0, 48, 48, 1, 8294400, 0xa87c518f +0, 49, 49, 1, 8294400, 0xa87c518f +0, 50, 50, 1, 8294400, 0xa87c518f +0, 51, 51, 1, 8294400, 0xa87c518f +0, 52, 52, 1, 8294400, 0xa87c518f +0, 53, 53, 1, 8294400, 0xa87c518f +0, 54, 54, 1, 8294400, 0xa87c518f +0, 55, 55, 1, 8294400, 0xa87c518f +0, 56, 56, 1, 8294400, 0xa87c518f +0, 57, 57, 1, 8294400, 0xa87c518f +0, 58, 58, 1, 8294400, 0xa87c518f +0, 59, 59, 1, 8294400, 0xa87c518f +0, 60, 60, 1, 8294400, 0xa87c518f +0, 61, 61, 1, 8294400, 0xa87c518f +0, 62, 62, 1, 8294400, 0xa87c518f +0, 63, 63, 1, 8294400, 0xa87c518f +0, 64, 64, 1, 8294400, 0xa87c518f +0, 65, 65, 1, 8294400, 0xa87c518f +0, 66, 66, 1, 8294400, 0xa87c518f +0, 67, 67, 1, 8294400, 0xa87c518f +0, 68, 68, 1, 8294400, 0xa87c518f +0, 69, 69, 1, 8294400, 0xa87c518f +0, 70, 70, 1, 8294400, 0xa87c518f +0, 71, 71, 1, 8294400, 0xa87c518f +0, 72, 72, 1, 8294400, 0xa87c518f +0, 73, 73, 1, 8294400, 0xa87c518f +0, 74, 74, 1, 8294400, 0xa87c518f From patchwork Fri Jan 14 01:13:31 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33583 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp987262iog; Thu, 13 Jan 2022 17:17:28 -0800 (PST) X-Google-Smtp-Source: ABdhPJxcqLqZ/8WZFWkP0TwO2w10D7DEMB5dn7+KBPdnu7Z5FEA5W3Smg6rQEs0k2b3RdnI/ns39 X-Received: by 2002:a17:906:5048:: with SMTP id e8mr5499109ejk.651.1642123047977; Thu, 13 Jan 2022 17:17:27 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642123047; cv=none; d=google.com; s=arc-20160816; b=IjVHVXZYJbFA3F62TmOPKVZgYf3ts8U26L/OiIlvhsw0JjA+sMxsBtPcLDI4XQDOdS PiyqeHSsJSngkwtp1pyRDx2+BJatfp5jdU1xE9M/D1zLbwzMfwXqvyNanwwP11dJa+Ep XqapNO8Cm2rWqSUz+R3F8xibPcc0+V1JGroIL5hvHkQCRNt3HbH6eMWJ1dqGUi3r0ScZ 2KFLLLiIQiIuYE2QKHBPtURL6lR7RpZmoSdnxhyD4oZAeX5AUHLyHFOk9nMFOEs3Wa7F ew2yLG20RKb6pl1eXZZew1G0B0WjnPAY4OSCnjx0u6i1/MmLDBRPDoiOZKRaW4PVPRYA FDTg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=bZ00FryTwFg3nH1eVfbXAU0nnaP9oE8OIc0cLVeVNvg=; b=Z5vGrIsBGg97TS0QDX2U3jHhQLLTO4n82eAJo+mqkB4H+c8Y6q1BHKai0+HO1EnVDE ckzV4eC+KtVAFiqc7TXCAHpDRX2HIPzERl4heupnaUbRr9IURievaqCBOt8fmhZiaPD9 I7enhIZW0jXca3WTBsP1nMHf/8+ocFG3OFK+ZY4sL4+ac3OPnQiU1daBqDUjDPIpm9cp saSlePVKEBHHbEnag9h5bL5+2in5PgBF5dYPo3/FkLeEsBPSnahjj3Y2vdsSlps1+SsB G6ki+ASY+pjFRLtHCQqIkxeJ6+wa0Y8mILYwf0oBzZRnyPSoL16Vt3BlcNEOUFEfNqef bHoQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=XnAFlV6u; 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 j20si2893753edw.264.2022.01.13.17.17.27; Thu, 13 Jan 2022 17:17:27 -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 header.s=20210112 header.b=XnAFlV6u; 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 8FF8F68B6BF; Fri, 14 Jan 2022 03:14:07 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.175]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id A7D2F68B678 for ; Fri, 14 Jan 2022 03:14:00 +0200 (EET) Received: by mail-pl1-f175.google.com with SMTP id u11so6764541plh.13 for ; Thu, 13 Jan 2022 17:14:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=6lL1Drvf74udQ6x2F0uYxEkOnLM6KMGfvH0WCeTBYYg=; b=XnAFlV6uYuddcSvFWdaasMwPOrvbxc6dUhnx5eHDcFH1jIzAWoMRjwQlGMJ8t3tINn Xk17qVq286WSGyAKBPPluettDV+zZy2d4ZwSj6AfEBRHYx8DuBy8egIklPsipggD3rFM 2yF8ZgS6S0KK+TP1Tkel1yrKZ68gs2fnrUd+RwYrxO9gOUzT2V0nR5yZXUazDJeeRE0I DQGJGn7IljLeEkWNDd6Ogz0XH5sJBatYom6Xb9TzZFsB8vZr4hm40ADUUc2mvs2RqgAp mcUaf3mLU2DVOWP/E23ZswChjcyeZXzKdUqpigArgNQRcnnM9IBNrb4Qnu4WGR5hpiEV 4N3Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=6lL1Drvf74udQ6x2F0uYxEkOnLM6KMGfvH0WCeTBYYg=; b=0ZMTYLoCdq4tN4Lu65Xkb0WnTedYHSnVPwSWdLYByLt5m4JFPorgpSJNFXReLmqS9w SW8TPi7YfsdmFbXriAHsBb9/OKQA6u8M1ihMkc9AQgE0L4XAe5nfL2ZJxpWB4fqDIaKl 3a7SO9E51j2CmWUW3wKdRrFpBx7KnEns4asDFlmUY4/tbypE3GKIk5dxFvNJxmIqL+yC nKvoN1OQmA8Sq5uUUSeaCuM9NqXtnv22lKe4UcvHHLYbmhJIV7UgjzUPV4rq26uXcPsH APhQM13PtS2ASli6K3/OAqr5lW8FDgDkbuuL70uqpqnQzEC+p9sLe4tXjmi2oEgNnuqp kW5Q== X-Gm-Message-State: AOAM530c+JjqlaQMncpwT2imXzPShDGRkfs3/ndji6ir+HnzfzG4Q27w 1P6HS6wriatUeYBt38idy1M7ZceJnC+pbQ== X-Received: by 2002:a17:902:b08b:b0:149:ee23:8907 with SMTP id p11-20020a170902b08b00b00149ee238907mr7550934plr.59.1642122838726; Thu, 13 Jan 2022 17:13:58 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id x1sm3198184pgh.44.2022.01.13.17.13.58 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:58 -0800 (PST) Message-Id: In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:31 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 22/24] avutil/ass_split: Add parsing of hard-space tags (\h) 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: jk1ThAZzsMJB From: softworkz The \h tag in ASS/SSA is indicating a non-breaking space. See https://github.com/Aegisub/aegisite/blob/master/source/docs/3.2/ ASS_Tags.html.md The ass_split implementation is used by almost all text subtitle encoders and it didn't handle this tag. Interestingly, several tests are testing for \h parsing and had incorrect reference data for those tests. The \h tag is specific to ASS and doesn't have any meaning outside of ASS. Still, the reference data for ttmlenc, textenc and webvttenc were full of \h tags even though this tag doesn't have a meaning there. Signed-off-by: softworkz --- libavutil/ass_split.c | 7 +++++++ tests/ref/fate/mov-mp4-ttml-dfxp | 8 ++++---- tests/ref/fate/mov-mp4-ttml-stpp | 8 ++++---- tests/ref/fate/sub-textenc | 10 +++++----- tests/ref/fate/sub-ttmlenc | 8 ++++---- tests/ref/fate/sub-webvttenc | 10 +++++----- 6 files changed, 29 insertions(+), 22 deletions(-) diff --git a/libavutil/ass_split.c b/libavutil/ass_split.c index c5963351fc..30512dfc74 100644 --- a/libavutil/ass_split.c +++ b/libavutil/ass_split.c @@ -484,6 +484,7 @@ int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *pr while (buf && *buf) { if (text && callbacks->text && (sscanf(buf, "\\%1[nN]", new_line) == 1 || + sscanf(buf, "\\%1[hH]", new_line) == 1 || !strncmp(buf, "{\\", 2))) { callbacks->text(priv, text, text_len); text = NULL; @@ -492,6 +493,12 @@ int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *pr if (callbacks->new_line) callbacks->new_line(priv, new_line[0] == 'N'); buf += 2; + } else if (sscanf(buf, "\\%1[hH]", new_line) == 1) { + if (callbacks->hard_space) + callbacks->hard_space(priv); + else if (callbacks->text) + callbacks->text(priv, " ", 1); + buf += 2; } else if (!strncmp(buf, "{\\", 2)) { buf++; while (*buf == '\\') { diff --git a/tests/ref/fate/mov-mp4-ttml-dfxp b/tests/ref/fate/mov-mp4-ttml-dfxp index e24b5d618b..e565ffa1f6 100644 --- a/tests/ref/fate/mov-mp4-ttml-dfxp +++ b/tests/ref/fate/mov-mp4-ttml-dfxp @@ -1,9 +1,9 @@ -2e7e01c821c111466e7a2844826b7f6d *tests/data/fate/mov-mp4-ttml-dfxp.mp4 -8519 tests/data/fate/mov-mp4-ttml-dfxp.mp4 +658884e1b789e75c454b25bdf71283c9 *tests/data/fate/mov-mp4-ttml-dfxp.mp4 +8486 tests/data/fate/mov-mp4-ttml-dfxp.mp4 #tb 0: 1/1000 #media_type 0: data #codec_id 0: none -0, 0, 0, 68500, 7866, 0x456c36b7 +0, 0, 0, 68500, 7833, 0x31b22193 { "packets": [ { @@ -15,7 +15,7 @@ "dts_time": "0.000000", "duration": 68500, "duration_time": "68.500000", - "size": "7866", + "size": "7833", "pos": "44", "flags": "K_" } diff --git a/tests/ref/fate/mov-mp4-ttml-stpp b/tests/ref/fate/mov-mp4-ttml-stpp index 77bd23b7bf..f25b5b2d28 100644 --- a/tests/ref/fate/mov-mp4-ttml-stpp +++ b/tests/ref/fate/mov-mp4-ttml-stpp @@ -1,9 +1,9 @@ -cbd2c7ff864a663b0d893deac5a0caec *tests/data/fate/mov-mp4-ttml-stpp.mp4 -8547 tests/data/fate/mov-mp4-ttml-stpp.mp4 +c9570de0ccebc858b0c662a7e449582c *tests/data/fate/mov-mp4-ttml-stpp.mp4 +8514 tests/data/fate/mov-mp4-ttml-stpp.mp4 #tb 0: 1/1000 #media_type 0: data #codec_id 0: none -0, 0, 0, 68500, 7866, 0x456c36b7 +0, 0, 0, 68500, 7833, 0x31b22193 { "packets": [ { @@ -15,7 +15,7 @@ cbd2c7ff864a663b0d893deac5a0caec *tests/data/fate/mov-mp4-ttml-stpp.mp4 "dts_time": "0.000000", "duration": 68500, "duration_time": "68.500000", - "size": "7866", + "size": "7833", "pos": "44", "flags": "K_" } diff --git a/tests/ref/fate/sub-textenc b/tests/ref/fate/sub-textenc index 3ea56b38f0..910ca3d6e3 100644 --- a/tests/ref/fate/sub-textenc +++ b/tests/ref/fate/sub-textenc @@ -160,18 +160,18 @@ but show this: {normal text} \ N is a forced line break \ h is a hard space Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed. -The\hline\hwill\hnever\hbreak\hautomatically\hright\hbefore\hor\hafter\ha\hhard\hspace.\h:-D +The line will never break automatically right before or after a hard space. :-D 31 00:00:54,501 --> 00:00:56,500 -\h\h\h\h\hA (05 hard spaces followed by a letter) + A (05 hard spaces followed by a letter) A (Normal spaces followed by a letter) A (No hard spaces followed by a letter) 32 00:00:56,501 --> 00:00:58,500 -\h\h\h\h\hA (05 hard spaces followed by a letter) + A (05 hard spaces followed by a letter) A (Normal spaces followed by a letter) A (No hard spaces followed by a letter) Show this: \TEST and this: \-) @@ -179,10 +179,10 @@ Show this: \TEST and this: \-) 33 00:00:58,501 --> 00:01:00,500 -A letter followed by 05 hard spaces: A\h\h\h\h\h +A letter followed by 05 hard spaces: A A letter followed by normal spaces: A A letter followed by no hard spaces: A -05 hard spaces between letters: A\h\h\h\h\hA +05 hard spaces between letters: A A 5 normal spaces between letters: A A ^--Forced line break diff --git a/tests/ref/fate/sub-ttmlenc b/tests/ref/fate/sub-ttmlenc index 4df8f8796f..aea09bb31e 100644 --- a/tests/ref/fate/sub-ttmlenc +++ b/tests/ref/fate/sub-ttmlenc @@ -109,16 +109,16 @@ end="00:00:54.500">Hide these tags:
also hide these tags:
but show this: {normal text}


\ N is a forced line break
\ h is a hard space
Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.
The\hline\hwill\hnever\hbreak\hautomatically\hright\hbefore\hor\hafter\ha\hhard\hspace.\h:-D

+ end="00:01:00.500">
\ N is a forced line break
\ h is a hard space
Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed.
The line will never break automatically right before or after a hard space. :-D


\h\h\h\h\hA (05 hard spaces followed by a letter)
A (Normal spaces followed by a letter)
A (No hard spaces followed by a letter)

+ end="00:00:56.500">
A (05 hard spaces followed by a letter)
A (Normal spaces followed by a letter)
A (No hard spaces followed by a letter)

\h\h\h\h\hA (05 hard spaces followed by a letter)
A (Normal spaces followed by a letter)
A (No hard spaces followed by a letter)
Show this: \TEST and this: \-)

+ end="00:00:58.500"> A (05 hard spaces followed by a letter)
A (Normal spaces followed by a letter)
A (No hard spaces followed by a letter)
Show this: \TEST and this: \-)


A letter followed by 05 hard spaces: A\h\h\h\h\h
A letter followed by normal spaces: A
A letter followed by no hard spaces: A
05 hard spaces between letters: A\h\h\h\h\hA
5 normal spaces between letters: A A

^--Forced line break

+ end="00:01:00.500">
A letter followed by 05 hard spaces: A
A letter followed by normal spaces: A
A letter followed by no hard spaces: A
05 hard spaces between letters: A A
5 normal spaces between letters: A A

^--Forced line break

Both line should be strikethrough,
yes.
Correctly closed tags
should be hidden.

diff --git a/tests/ref/fate/sub-webvttenc b/tests/ref/fate/sub-webvttenc index 45ae0b6131..f4172dcc84 100644 --- a/tests/ref/fate/sub-webvttenc +++ b/tests/ref/fate/sub-webvttenc @@ -132,26 +132,26 @@ but show this: {normal text} \ N is a forced line break \ h is a hard space Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed. -The\hline\hwill\hnever\hbreak\hautomatically\hright\hbefore\hor\hafter\ha\hhard\hspace.\h:-D +The line will never break automatically right before or after a hard space. :-D 00:54.501 --> 00:56.500 -\h\h\h\h\hA (05 hard spaces followed by a letter) + A (05 hard spaces followed by a letter) A (Normal spaces followed by a letter) A (No hard spaces followed by a letter) 00:56.501 --> 00:58.500 -\h\h\h\h\hA (05 hard spaces followed by a letter) + A (05 hard spaces followed by a letter) A (Normal spaces followed by a letter) A (No hard spaces followed by a letter) Show this: \TEST and this: \-) 00:58.501 --> 01:00.500 -A letter followed by 05 hard spaces: A\h\h\h\h\h +A letter followed by 05 hard spaces: A A letter followed by normal spaces: A A letter followed by no hard spaces: A -05 hard spaces between letters: A\h\h\h\h\hA +05 hard spaces between letters: A A 5 normal spaces between letters: A A ^--Forced line break From patchwork Fri Jan 14 01:13:32 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33585 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp987562iog; Thu, 13 Jan 2022 17:17:55 -0800 (PST) X-Google-Smtp-Source: ABdhPJzWoOp42KCYfo6Gz60/6yS+C+CCexlHu3I6W/WWp+qQ/u3zstTYKEj2+PwPubEvbVCeKd+o X-Received: by 2002:a05:6402:4386:: with SMTP id o6mr6802131edc.47.1642123075241; Thu, 13 Jan 2022 17:17:55 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642123075; cv=none; d=google.com; s=arc-20160816; b=BjUBfaOjEDdew7VYPCAXZmpbMHQrnCHm0zsrj65WAGTa0WtU73LrGibE7PCCEjzMfw 176lkgx/DKWP8HmOAcQehy6Nfz+tzWfOLC7qLf/d5x/d6D2gW8mSx9ER/CCFHrhbGRev ZiJBcDdMYREllPzHwelSj41FQJXDIVsr2fSd2/a0kz42hZdg6lZM5mWTzEcW3K6KyCCV 14HOVJ4CLU4b8Ej1SW4OUY3BPvcWgD/zWjfMlHv/1zAQhJlN6V2p3UZ6DBvwXlLkaplv GGZIA4X2NuxWMkaQ4reNZXCwH9V/1rphf6y5n5okc4NBhT5JfWE6tt1fiO8BIRb+pE6t Q93w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=KB04cco0No4xge1c4IQGsu0fJap6WNSZQ56YrIeWIag=; b=hD3o4rqe/HEx5SGLcz1xaQcpLjLXjC/9iXCV5Xvuzr4j3kIyHyHNsq6aKZouXmEXpN px/z7J8vPu0+DMBOhwlA2E5rBzOPl0b4AgNtidujGrzDexNGoWXabKrdFBxv2qFyjvcb Cw2lW1Z2FdsZRmnlNr6g2OxCfJADCLHMxjvaL+qOqrBc/WI8vJ4hCjTx1V4kMrxhg2wN w6/4jcv+PpzbEw9bpz9M3l08dM+IdO++wuTf8Pejpc4KD0ThWSW+cSnnqcveeUrZK+dt B6eSedvlfPSLV3E7CiPE7kcFdvjpFnjnVXMX+xCO01rwL1vQfQRIqhOKcSR27Xhn2kHh O3PQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=dTmG4Bpr; 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 s4si1425593edr.496.2022.01.13.17.17.54; Thu, 13 Jan 2022 17:17:55 -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 header.s=20210112 header.b=dTmG4Bpr; 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 B111E68B6D3; Fri, 14 Jan 2022 03:14:09 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f181.google.com (mail-pf1-f181.google.com [209.85.210.181]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5D74568B680 for ; Fri, 14 Jan 2022 03:14:01 +0200 (EET) Received: by mail-pf1-f181.google.com with SMTP id p37so1401064pfh.4 for ; Thu, 13 Jan 2022 17:14:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=kvfdnmsm1ycOUMPMsqonIN9VM28V0LuiM2jsDLNw1I8=; b=dTmG4BprhbrpR/E8MreKUoIbZwAlK5joMagURpsecDiZ4W1XOXrNwQsLWldoOhhxsi 42bqapmXcQpt8LLUCtFy4sVjD6QNhqRLaejRqdsgWdr5uLMNWBrKKp7nQ2sExVDk514f 2ejWSvUvLY4ah2m733FADMeHpLeq/j3sRum1D5PQLqteUckZY0PRJz7dAyWR8AlElAi/ JJlIyScIIU0GGZZINk2ItjnUxEVeLHRlRMqhmSF99so3rwCAEJDnJsGz3+v1xXRNoQea XFu9vB6GqyhwKgj42KIoeT4LJ0xX425tep5rB8odLCNP7h9jW1Fl9kxwPaGxcU5fDfe/ FRWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=kvfdnmsm1ycOUMPMsqonIN9VM28V0LuiM2jsDLNw1I8=; b=c6PI4ImhnyIEz0wxz3SaEjYxHEqCjhdLEajtrwvddzDAIxcxXSIo5qSq/X+JqXpQtk Ue/as6dA4aNSDoktdIJnK76hz+albmpAOlBOgl86YkDIM/pXLwVjyia1qRDWUruYHspg Wl7uodUvcudG3Piez8FHwODMjxQN8BIUFPUSxvFwv6YwKj7auvfCFfLOUWJNmanWfKwu JkB/QLeMAgzqaNfaci3heW7oQeqUCJmCxIj6RLG2vjl279HOZ1Hl7kGvJm/pUK+sehFJ 4jqWrpgFygAYBxaZN0f0z48B7AMmdr5DYA8wNYlFCDBwLYcjA5by/2MHaUOO5272EMt/ u+Hw== X-Gm-Message-State: AOAM533cawBisuC9SUNas3etDC222Fd7N7P0Ouf4Po4AGNttfs8axguv ux52kBDAbWBBPTLFJO095jbXgbsfrx5Naw== X-Received: by 2002:a63:9d46:: with SMTP id i67mr5968187pgd.441.1642122839664; Thu, 13 Jan 2022 17:13:59 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id t6sm3229639pgk.31.2022.01.13.17.13.59 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:13:59 -0800 (PST) Message-Id: <10c1404272f8c22fa611d1c4259a04c847cdbd18.1642122814.git.ffmpegagent@gmail.com> In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:32 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 23/24] avcodec/webvttenc: convert hard-space tags to   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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: q84evjZRd6Uc From: softworkz Signed-off-by: softworkz --- libavcodec/webvttenc.c | 6 ++++++ tests/ref/fate/sub-webvttenc | 10 +++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c index c0436f5739..48945dcb8e 100644 --- a/libavcodec/webvttenc.c +++ b/libavcodec/webvttenc.c @@ -123,6 +123,11 @@ static void webvtt_new_line_cb(void *priv, int forced) webvtt_print(priv, "\n"); } +static void webvtt_hard_space_cb(void *priv) +{ + webvtt_print(priv, " "); +} + static void webvtt_style_cb(void *priv, char style, int close) { if (style == 's') // strikethrough unsupported @@ -147,6 +152,7 @@ static void webvtt_end_cb(void *priv) static const ASSCodesCallbacks webvtt_callbacks = { .text = webvtt_text_cb, .new_line = webvtt_new_line_cb, + .hard_space = webvtt_hard_space_cb, .style = webvtt_style_cb, .color = NULL, .font_name = NULL, diff --git a/tests/ref/fate/sub-webvttenc b/tests/ref/fate/sub-webvttenc index f4172dcc84..ee9de2859e 100644 --- a/tests/ref/fate/sub-webvttenc +++ b/tests/ref/fate/sub-webvttenc @@ -132,26 +132,26 @@ but show this: {normal text} \ N is a forced line break \ h is a hard space Normal spaces at the start and at the end of the line are trimmed while hard spaces are not trimmed. -The line will never break automatically right before or after a hard space. :-D +The line will never break automatically right before or after a hard space. :-D 00:54.501 --> 00:56.500 - A (05 hard spaces followed by a letter) +     A (05 hard spaces followed by a letter) A (Normal spaces followed by a letter) A (No hard spaces followed by a letter) 00:56.501 --> 00:58.500 - A (05 hard spaces followed by a letter) +     A (05 hard spaces followed by a letter) A (Normal spaces followed by a letter) A (No hard spaces followed by a letter) Show this: \TEST and this: \-) 00:58.501 --> 01:00.500 -A letter followed by 05 hard spaces: A +A letter followed by 05 hard spaces: A      A letter followed by normal spaces: A A letter followed by no hard spaces: A -05 hard spaces between letters: A A +05 hard spaces between letters: A     A 5 normal spaces between letters: A A ^--Forced line break From patchwork Fri Jan 14 01:13:33 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 33586 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp987695iog; Thu, 13 Jan 2022 17:18:07 -0800 (PST) X-Google-Smtp-Source: ABdhPJzkTupxhqYih07NnBvBWVNlBIjVTimkl7ql15Kf05+EKZTrgVPN1WzNnc5kv3j/ASqX0frC X-Received: by 2002:a17:906:2ec8:: with SMTP id s8mr5646089eji.746.1642123087031; Thu, 13 Jan 2022 17:18:07 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1642123087; cv=none; d=google.com; s=arc-20160816; b=cM3cuXbnv8tfOMG2JKGL/kqMYtLyzoEOZXFYttx2WFFLLYny6ASlUAwM3Hy98Oiufk DpskiGkA3UOs3fkHNKtiAUNrEILaqJXuSNJ7zUFkSTSfUaeFnZ2fpqyGYJxy5J81fr1P 550zV83vwW6Y54MMfV2i8ixExSbG3AYtAliTpuD60x7HJQfTm3mTuK27CUDxNOsE+yDn laN3ZiFE6mq38W4STaKnlsGkJjxZpKzGeIUpdkt4MFjC0zcFsCpyCBm8h1m/Dvaa0WtF edCW41fUgBb0NgpF9AdRJtuH8m3+0VgjodDIKTn92O/Y7fvloHo/Z7p5bLZqfMy2FryG 8p+A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:mime-version:fcc:date:from:references :in-reply-to:message-id:dkim-signature:delivered-to; bh=7xL+k9hIxsaBAyLl9SEIGWs8/g2aSxHxTDr9AO4ok3s=; b=a4/nmQrqOaFXS7sS+BFHLI9jmrarKI2EN+di61fbIqMGICMaYARUsCmUDyXmJ3/jf5 ngamXA86KRYZpl3Z0CfT7kt33IEvve/iKJF/Zbi0cNMc/r6rx1PgFlIonyuyQrJ+Ph7R vQGjtyBMkWVlf7qTnEpJ/7Yct2tn4+H1H9HbESKzoYLBT5JluWMQtpX4eGMogVLzSIwj KzVeoh3DNYXhc1c62rrfQcMdTmhlZJB/r3InvUuHOOVwb3D4VceVCGZ0IFej/qU/stx1 6HbpvsM9BO9NxGEh4HdJVKVFqbrADMLpYSAX8qO/yL305JuqC78k13ZNFmAGw6COtBL/ 4dLw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=kLbeVUJU; 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 js16si2739791ejc.557.2022.01.13.17.18.06; Thu, 13 Jan 2022 17:18:07 -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 header.s=20210112 header.b=kLbeVUJU; 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 BE30E68B6D4; Fri, 14 Jan 2022 03:14:10 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pg1-f175.google.com (mail-pg1-f175.google.com [209.85.215.175]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id BE52368B62B for ; Fri, 14 Jan 2022 03:14:02 +0200 (EET) Received: by mail-pg1-f175.google.com with SMTP id c5so1433834pgk.12 for ; Thu, 13 Jan 2022 17:14:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=Tq2/OXRX52WCp6qWqOGMHzDL05/oRRltQSxA3kW3kxM=; b=kLbeVUJUdQQm56yGJ6urHyMu4zuJdkhcC75oEsbf0rJ2hshPAm7iArBWQHzHMq1Z89 fY3NDFfIztLg/VV0n9jXmoutsXUPJvAAFVvv3+20B757YrOVG5mhCAMzQGOihB4mksBe QHEWS2HfXlRZus8YPGqR3M/9+/3F0fq/9m1305tpDqxhveJX69wX52MyULrHQP4QZ1QP RVVKiAYT50gVTOgicGejgQUXCSVVMbesqwyhBT+i615H9CiwJE6VIRUAoXnfNZ1v5Bq0 uTAXGQSQBExYoVlUHb9DO2aPCzm7qGK8xo9t3hOnB8zDpJhPsCTiZn34LyXw3w/YIPpG KR8g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=Tq2/OXRX52WCp6qWqOGMHzDL05/oRRltQSxA3kW3kxM=; b=dxmEnyH/CejxsEqMg7IsQUQ4V5x5ySiQByJKa5pkw+FqD54yQH0LX0ajGRwpnxGKMj C+cmrtwl52DJcZEOwlI6egWrJeE6Z7+xGt+h0Ji2cJ3bN2+hi82eQ0L7SzEMREXu8Rsy cN2fBw+KHAMYx6TcOFgXTl3obZyU4QhRBmVvkkj6n6m4IG6V12hdYSEKYbUGTLMqyVcy 8yaR1sUxHXKksT6cCgrMvWNlI0DoHuNIqUwMgV98Ystl5b5U4qrZ4oH+jHJykz6B3b5H r+CzOGKSRbMMJSGJGuCFD4djuKZOYWlItalPa20523zaczcf5/no44gWub72LU3skKqe mKyw== X-Gm-Message-State: AOAM532K+h2rFvQ3bVkDzvC0w5sNmsqGCqzkiFxoeBGzrAKJn+bk/ROA MSnUK3XZlsmZsvpdiJAHwyOofuZC7jsP+A== X-Received: by 2002:aa7:9009:0:b0:4bf:f724:163e with SMTP id m9-20020aa79009000000b004bff724163emr6964493pfo.18.1642122840770; Thu, 13 Jan 2022 17:14:00 -0800 (PST) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id h2sm3937869pfh.93.2022.01.13.17.14.00 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jan 2022 17:14:00 -0800 (PST) Message-Id: In-Reply-To: References: From: ffmpegagent Date: Fri, 14 Jan 2022 01:13:33 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 24/24] doc/APIchanges: update for subtitle filtering changes 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 Cc: softworkz Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: MNoPxxbLJMPq From: softworkz Signed-off-by: softworkz --- doc/APIchanges | 24 ++++++++++++++++++++++++ libavcodec/version.h | 2 +- libavutil/version.h | 2 +- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/doc/APIchanges b/doc/APIchanges index 8df0364e4c..c8238fb008 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -14,6 +14,30 @@ libavutil: 2021-04-27 API changes, most recent first: +2021-12-05 - xxxxxxxxxx - lavc 59.15.100 - avcodec.h + Deprecate avcodec_encode_subtitle(), use regular encode api now + +2021-12-05 - xxxxxxxxxx - lavc 59.15.100 - codec_desc.h + Add avcodec_descriptor_get_subtitle_format() + +2021-12-05 - xxxxxxxxxx - lavc 59.15.100 - avcodec.h + Deprecate avsubtitle_free() + Deprecate avcodec_decode_subtitle2(), use regular decode api now + +2021-12-05 - xxxxxxxxxx - lavu 57.11.100 - frame.h + Add AVMediaType field to AVFrame + Add Fields for carrying subtitle data to AVFrame + (subtitle_areas, subtitle_header, subtitle_pts, start/end time, etc.) + Add av_frame_get_buffer2() and deprecate av_frame_get_buffer() + +2021-12-05 - xxxxxxxxxx - lavu 57.11.100 - subfmt.h + Add struct AVSubtitleArea (replaces AVSubtitle) + Add av_get_subtitle_fmt_name() and av_get_subtitle_fmt() + +2021-12-05 - xxxxxxxxxx - lavu 57.11.100 - subfmt.h + Add enum AVSubtitleType (moved from lavc), add new values, deprecate existing + +2021-11-xx - xxxxxxxxxx - lavfi 8.19.100 - avfilter.h 2022-01-04 - 78dc21b123e - lavu 57.16.100 - frame.h Add AV_FRAME_DATA_DOVI_METADATA. diff --git a/libavcodec/version.h b/libavcodec/version.h index a46fb05f1a..b5867ad041 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -28,7 +28,7 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 59 -#define LIBAVCODEC_VERSION_MINOR 20 +#define LIBAVCODEC_VERSION_MINOR 21 #define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ diff --git a/libavutil/version.h b/libavutil/version.h index 5bf48f6304..168e24f410 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -79,7 +79,7 @@ */ #define LIBAVUTIL_VERSION_MAJOR 57 -#define LIBAVUTIL_VERSION_MINOR 18 +#define LIBAVUTIL_VERSION_MINOR 19 #define LIBAVUTIL_VERSION_MICRO 100 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \