From patchwork Sun Jun 26 16:34:58 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 36456 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1539604pzh; Sun, 26 Jun 2022 09:35:46 -0700 (PDT) X-Google-Smtp-Source: AGRyM1tTovVhBjBY9YUqIPAVrFNA/qlrhQo/RQqB2LsB3YJ1QsS7xmNZaGFm1K3vjSqzyLpY7NxM X-Received: by 2002:a17:906:2bda:b0:726:3b59:3ea9 with SMTP id n26-20020a1709062bda00b007263b593ea9mr8598264ejg.43.1656261346244; Sun, 26 Jun 2022 09:35:46 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261346; cv=none; d=google.com; s=arc-20160816; b=F62IUL1jTl7J7u9cl07hbzKpK3AObki03zjo3suivO7JNPX9u+zYFIS9j6D3BichqR vXl+gQj/QEhgHe0hyCpVWt9IDH0tKF8AljOpASvDn0mkOAmsNOwwWsqYvASrrd4vaOBN pXwSVrYlQs6hMjg8zuTHFXLIpFd6HXmU3bXHHsA5YVUUrcB0agbcmIaD+E+sOoi4auJR WKLcGsQKNw4p6+Ar75Z4Sgm/Ce9pJBdDSuKT0zI6tUD6NEtKhZYu+UljZ50ZhRxeS4Bn fwCn5iEJa5x7Yr7O3BKuOHG/q3qr+BcKDXLwKZDphvz6DRvRQ1mvkPDvCvApMoUih6Zp UHSg== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=tS0ZR5+maUg+eNP91Lf48mxfBd53kSE8+ERSFdwgui0=; b=FiOYXAx1J9q0RQgy4VpZ3StarbUB72/qpVQT0Io0YHfMevInpdLZYYPhHVhrTeTq/m lCE1OPYL/Yl8qiWRVd53snWP5ZeOiiFLwPop+UHJzvSG8Un6LJq5ZrrdwId/uh85FzNt 32EPpxXydLxFwG7uvrCDg0ShnXJciy9qvX+S4jT8L84/rhWngWkE9mQVuMdrs7SuJsMO mXi9cKrQS3KRebEjBY9Nq3px8pH0bllmAghw+jzDkdDkxNSBBq5MYtTaNvrdTrKiee8e E9lxTF6RMcnug4AwtG/Ww95sSw26S2S/R7g15t1ngsO+LopXmFL9pEtUO6rbvHaLwyzP bwCA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=Q0rFBDgg; 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 e12-20020a17090658cc00b007262944c623si8690685ejs.869.2022.06.26.09.35.45; Sun, 26 Jun 2022 09:35:46 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=Q0rFBDgg; 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 BA52068B7E0; Sun, 26 Jun 2022 19:35:33 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pl1-f182.google.com (mail-pl1-f182.google.com [209.85.214.182]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id B38D368B74A for ; Sun, 26 Jun 2022 19:35:26 +0300 (EEST) Received: by mail-pl1-f182.google.com with SMTP id r1so6181615plo.10 for ; Sun, 26 Jun 2022 09:35:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=njJkpPnVsYzDGBt9pLbHtES/GiZF+/P3HYrS0CRAI0A=; b=Q0rFBDggu7u79g/5JeFBGSHFMEVPqCXlg2MHrijG9vLoM/p6UabZ8yUunGkAGymK1Q nSNGnprVzzmF2aCJgwf/niDotPYuDBfH4AkXlE6NyEwkS0jp7UfS8/E9Ea4LTrQV3iFN R4zzHo4jRk48iDdtHP18X3qTyXYiAg8ncm00mzlEhOP/19weFN13+sHMhCMaTE/5lPIG 4LGOOO/cw9uBs462RpsuZ+5ibVHEdfpMTJuu7xSerYRdGUISJncqfQp/7ylG/jKPfxqB +8A4g4+fiwo4xKpnxtnKmtv71Oxd3JeefAqvJKuxjNDibXjc0XwXeN1Qvtq7B9yf6bE3 9/2A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=njJkpPnVsYzDGBt9pLbHtES/GiZF+/P3HYrS0CRAI0A=; b=eDBtFqdsAjaxUHbtceC5whhyqqhfCryHwuCqrP1rYbZ0Y+pTCez/BW0nrHsChkpqx6 kNfbiDqoSvW/hSG+eUnFOZpmOu2SC6PkM9DX6xh7+jtXFgDLhLenqyr9dyNZyrwuoBlP XqFHAjoH1C/JvlBfNe5Kd8RnDs2ASUyGDOVblkMJhGSBPtUXEKUYU+cocjtrTOvC7pG0 JOdj2lyXX5/C77PUxmwzE6758C6yAubmvSCjSIquLxTJb6327caoKhQ+stJ1uYY4CwL3 lTD5Q60OX8Qx/dFK/QhywC77+nj2XxiLmM+pldQTF2a1XA0hOt1TT5FFbxHy/Pq9tsbk izkg== X-Gm-Message-State: AJIora8eD36e8CW2sjUIQk0dDnNeR0vrFZOrRggsQzLoaQzdN2BW9rD4 yF8Y1C6rkiAY10hGaGZycBjOWa3X3fQGgA== X-Received: by 2002:a17:90a:5104:b0:1ea:e86b:6aed with SMTP id t4-20020a17090a510400b001eae86b6aedmr15871466pjh.69.1656261325031; Sun, 26 Jun 2022 09:35:25 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id e9-20020a170902784900b001636d95fe59sm5410430pln.172.2022.06.26.09.35.24 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:24 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: In-Reply-To: References: Date: Sun, 26 Jun 2022 16:34:58 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 01/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: dhWVIAPOVeBH 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 4dae23d06e..56d551f92d 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" @@ -2255,24 +2256,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 29c170214c..edec708ff5 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -77,6 +77,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 2e9e02dda8..f9f84801a3 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -114,6 +114,7 @@ #define FF_API_XVMC (LIBAVUTIL_VERSION_MAJOR < 58) #define FF_API_OLD_CHANNEL_LAYOUT (LIBAVUTIL_VERSION_MAJOR < 58) #define FF_API_AV_FOPEN_UTF8 (LIBAVUTIL_VERSION_MAJOR < 58) +#define FF_API_OLD_SUBTITLES (LIBAVUTIL_VERSION_MAJOR < 58) /** * @} From patchwork Sun Jun 26 16:34:59 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 36457 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1539662pzh; Sun, 26 Jun 2022 09:35:56 -0700 (PDT) X-Google-Smtp-Source: AGRyM1vOkADcdhdCiD1apCPRI5IiskA0trSp+XT9eDeIFJGnwAKeGcLIMyrg/dePrCy8yfBS7cug X-Received: by 2002:a17:907:160a:b0:726:40fa:36d3 with SMTP id hb10-20020a170907160a00b0072640fa36d3mr8307091ejc.694.1656261356132; Sun, 26 Jun 2022 09:35:56 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261356; cv=none; d=google.com; s=arc-20160816; b=a/L7bbXwQp+zjtAamhyl8yOTMLoqab3Y0LPlIB3Dsq8Q6FKpxI4hnDuKELp2Now6RA VqDbrXAl8gDedWDDs1xg5hNmZcUEX0CgeSVejfoZrA4u2ocsxXZFgk+RkBj3+9kBjV4G Hz0GnJdTjjv3VPCe1lg0Ac+C11EZcZtKosVeBrFV9QS7XiBB2EJbNoJfWW+GU+KnGseu jyLhDqwmcB9D4Cv2BU06l5x8F/UD+WM0E1QfqDK6QF0fkdmm/CFUU+X8qeyBGD7gS2yQ /jpzDXDf3IKQcE3Bi1lc2PBhVyuPtXqDPkRDlXz3MgrVL0Lwsc0MGNXdpo+6gb2BgvYS WNLg== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=enIMMXwbK8sc43zpHkpfVMhKHY2PeTiIiu0EmkabC5Q=; b=ubNNJLYK3K0xAOfBwn60oxdauCs7BG5IjMcyYXAuK5zR4DuxuR0F97Qvc48HNpgNpL TgY1QFUUGU6adEavuZR7dfvCLOVjOWHiw6N3tqL0mNknv1Jbu2dO8jkvA0OoO4ctyRl6 Zkdn+fQSsO0njKmpMkRYqOoICfAPQPyOOsgxdlrdIol7rquBcmdr0X2qMOJq91Gm8yP1 2rwhBkwKV4MD5m62ZoH066Rcy1f2LV2fttCbt9DzLq+rgUy8Gpskv3oFd1WEQ6vgzX2/ C0Qr/Bs0+4cIqQXM7Cgd0VXiL6K6ib0Ykme/uXmB7wEbZUcrHqCiJ6ywcb1llxS5nVIj CYgw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=gGRXUw5Y; 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 dd10-20020a1709069b8a00b00722ec5b1693si10668394ejc.163.2022.06.26.09.35.55; Sun, 26 Jun 2022 09:35:56 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=gGRXUw5Y; 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 BE45B68B7FF; Sun, 26 Jun 2022 19:35:35 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pj1-f54.google.com (mail-pj1-f54.google.com [209.85.216.54]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id CD79B68B7C8 for ; Sun, 26 Jun 2022 19:35:27 +0300 (EEST) Received: by mail-pj1-f54.google.com with SMTP id l2so5942785pjf.1 for ; Sun, 26 Jun 2022 09:35:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=7Bwnirid3N1PLUVb86x3H29TxUqhlM9un5xUkda1bRY=; b=gGRXUw5YcBneeUTc/prvK6MtERAmaJE854aI4QUiwihyBQvICkJJo0gnde5zoAbYve oOg30mwodB/LbVcKC+QQv4aALSnaplKbqAhHCbSgQSLsTM1s0/eNruJr+MAZEf7NxqXQ QhMKgyuw/OWj3N0uytTF9upc9GJ1dHXJZh3C9JJaH5PKA1iBHYwECvip9be5f9Xgjutm R2Y+GotxQjxkSXa3flBapgwe6T0taMo/Yb8Wjb0amhpLA+Zml6y+fAiAlQedKb4CwDuP 3zJGmn9bMRZxQLHEp87jYE1u8lFCv7elIi8KRnkLxcL5+qYSW1Ott+GwD9TLnGmu5hMF 0wsA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=7Bwnirid3N1PLUVb86x3H29TxUqhlM9un5xUkda1bRY=; b=dMndrQVx17bR+qVQBg1TpGoMFXph1fFQWuBvp4rUQW1KzqVJJX9v4EBw4AIYV50xoN BUjzojHb6QydJoE/af/3d14VSS9zZeBrREFBGE9vWGRrI21WOmVQL1NfSA424wDxjofL CUapk2H05aXdRMlekfGtJy+BW9dbcA0w+mAYlOmJqgdpPvTnSe2RvloCBYafukU8BohS Nh6F4LI7kBgIqmVu09v1Yn2MN0+mKS2lPspLq0YRWfJqR5xV4Lfr8KVQ0vCy13nYebg2 ofl9NPTHvh1bsnqm7bZpq6VPdQMrostGYQ2UVqqMb088cIEPVi+1OcjgGLHhVysGbNmw B2oA== X-Gm-Message-State: AJIora8jYUt3tteZSdwQKsm1c1xa1EGsQZylAisXA1lYSOMDPEwUWIe5 JN8xmFr4+SeB2hCJXO79WdVvV+JyS0Hsjg== X-Received: by 2002:a17:902:d4c4:b0:16a:2a8c:c4aa with SMTP id o4-20020a170902d4c400b0016a2a8cc4aamr9865997plg.138.1656261325971; Sun, 26 Jun 2022 09:35:25 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id x6-20020a170902a38600b00163d8d9aefcsm5394669pla.80.2022.06.26.09.35.25 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:25 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: In-Reply-To: References: Date: Sun, 26 Jun 2022 16:34:59 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 02/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: M+Sf3uB6nVJ5 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 | 206 +++++++++++++++++++++++++++++++++++++++++---- libavutil/frame.h | 85 ++++++++++++++++++- libavutil/subfmt.c | 45 ++++++++++ libavutil/subfmt.h | 47 +++++++++++ 5 files changed, 363 insertions(+), 21 deletions(-) create mode 100644 libavutil/subfmt.c diff --git a/libavutil/Makefile b/libavutil/Makefile index edec708ff5..48f78c81e5 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -165,6 +165,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 4c16488c66..eadeabd926 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" #if FF_API_OLD_CHANNEL_LAYOUT @@ -52,6 +53,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)); @@ -72,7 +76,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) @@ -251,6 +260,23 @@ FF_ENABLE_DEPRECATION_WARNINGS } +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->format < 0) @@ -258,23 +284,41 @@ int av_frame_get_buffer(AVFrame *frame, int align) FF_DISABLE_DEPRECATION_WARNINGS if (frame->width > 0 && frame->height > 0) - return get_video_buffer(frame, align); + frame->type = AVMEDIA_TYPE_VIDEO; else if (frame->nb_samples > 0 && (av_channel_layout_check(&frame->ch_layout) #if FF_API_OLD_CHANNEL_LAYOUT || frame->channel_layout || frame->channels > 0 #endif )) - return get_audio_buffer(frame, align); + frame->type = AVMEDIA_TYPE_AUDIO; FF_ENABLE_DEPRECATION_WARNINGS - return AVERROR(EINVAL); + return av_frame_get_buffer2(frame, align); +} + +int av_frame_get_buffer2(AVFrame *frame, int align) +{ + if (frame->format < 0) + return AVERROR(EINVAL); + + switch (frame->type) { + case AVMEDIA_TYPE_VIDEO: + return get_video_buffer(frame, align); + case AVMEDIA_TYPE_AUDIO: + return get_audio_buffer(frame, align); + 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; @@ -306,6 +350,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); @@ -353,6 +403,7 @@ FF_ENABLE_DEPRECATION_WARNINGS av_assert1(dst->ch_layout.nb_channels == 0 && dst->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC); + dst->type = src->type; dst->format = src->format; dst->width = src->width; dst->height = src->height; @@ -385,7 +436,7 @@ FF_ENABLE_DEPRECATION_WARNINGS /* 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; @@ -407,6 +458,10 @@ FF_ENABLE_DEPRECATION_WARNINGS } } + /* 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)); @@ -476,7 +531,7 @@ AVFrame *av_frame_clone(const AVFrame *src) void av_frame_unref(AVFrame *frame) { - int i; + unsigned i, n; if (!frame) return; @@ -495,6 +550,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); @@ -522,18 +592,28 @@ FF_ENABLE_DEPRECATION_WARNINGS 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; } @@ -549,6 +629,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; @@ -568,7 +649,7 @@ FF_ENABLE_DEPRECATION_WARNINGS 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; @@ -603,7 +684,12 @@ AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int plane) uint8_t *data; int planes, i; - if (frame->nb_samples) { + switch(frame->type) { + case AVMEDIA_TYPE_VIDEO: + planes = 4; + break; + case AVMEDIA_TYPE_AUDIO: + { int channels = frame->ch_layout.nb_channels; #if FF_API_OLD_CHANNEL_LAYOUT @@ -617,8 +703,11 @@ FF_ENABLE_DEPRECATION_WARNINGS if (!channels) return NULL; planes = av_sample_fmt_is_planar(frame->format) ? channels : 1; - } else - planes = 4; + break; + } + default: + return NULL; + } if (plane < 0 || plane >= planes || !frame->extended_data[plane]) return NULL; @@ -761,22 +850,103 @@ FF_ENABLE_DEPRECATION_WARNINGS 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); -FF_DISABLE_DEPRECATION_WARNINGS - 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 && + case AVMEDIA_TYPE_AUDIO: + if (dst->nb_samples > 0 && (av_channel_layout_check(&dst->ch_layout) #if FF_API_OLD_CHANNEL_LAYOUT || dst->channels > 0 #endif )) - return frame_copy_audio(dst, src); -FF_ENABLE_DEPRECATION_WARNINGS + return frame_copy_audio(dst, src); + break; + case AVMEDIA_TYPE_SUBTITLE: + return frame_copy_subtitles(dst, src, 1); + } return AVERROR(EINVAL); } diff --git a/libavutil/frame.h b/libavutil/frame.h index 33fac2054c..dd36f5b27c 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" @@ -35,6 +34,7 @@ #include "rational.h" #include "samplefmt.h" #include "pixfmt.h" +#include "subfmt.h" #include "version.h" @@ -293,7 +293,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 @@ -407,7 +407,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; @@ -702,6 +702,53 @@ typedef struct AVFrame { * Channel layout of the audio data. */ AVChannelLayout ch_layout; + + /** + * 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; @@ -778,6 +825,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 @@ -797,9 +846,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 Sun Jun 26 16:35:00 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 36458 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1539727pzh; Sun, 26 Jun 2022 09:36:06 -0700 (PDT) X-Google-Smtp-Source: AGRyM1uyy15pZGYS+YUeYAdUJeKxM0h8bzzbPtdXG7MES9FYJzZ9Y807S2BVcE0EcW0lWFJ+C2um X-Received: by 2002:a05:6402:4386:b0:437:6450:b41f with SMTP id o6-20020a056402438600b004376450b41fmr11744338edc.97.1656261365762; Sun, 26 Jun 2022 09:36:05 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261365; cv=none; d=google.com; s=arc-20160816; b=vAerWKC7ItjVne8DCn4j52eayP9RcK3iXGRGpXG84EJnz7iW7AIMirtTZwvvRt24PZ ffyoGoLch6AgN5eobUE1wFWOhJIUEuqxGaOI3dF1mCLRG1f/IB7mcS6YJUuMsnhxdnPP oYFPxRys4pGSmr0ULzNwn2rIJvj5f3dyK3qkCElGlfH8DubYNlmMMFLvgY0ZX5yfeAFh pmVG/QNWY1Iy1DzSId0oGgMeJRLY+d9mL2OsiGOtLfrW6eMHVTQjO7T9fVeO+qdcAmRq JTDQe3iyTTUONGHZW9HmyTDTbwi9tW1zyU5uWASzJ/wOwpHFO1MEyreLXS3SBwZMyCIJ 3P7A== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=B0r2vPMQZt8RfF5nEHAnPaeaoa1wdV9HiTjt890TW24=; b=SR/VUAvP9tryVyD7OzsxXUCZggBSFkXhx4LiLgpKc+JEZ7Hk1QgTNuWV48xmF/g1nc Gr1VbrMkrpP3siNB/pNrd7s2OAXZzA3BoguqU2npG0ajKQExeRR3WyzcXha8HxHRhlK/ 0x83f4fI6AVP5fDz9mHLHwu/hOoWCFakx0yU03KUpi60u84s+zOWAzfVsXWLkGwnWc27 TlmSxo/zxYHVm6JrlzobYcyvFEvOX+JWbCKoHzTF+tuETqBPgvgOcVID0IyHkwl5peEv nyERVzkIw3Um1K3mkpyR0I6JrKQAW2OAoBJWSQz7GOZRwJe6yk+6c0XCVguCBYpjJLCH ew8g== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=HsEKVQZZ; 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 z25-20020a170906715900b00715771cd3c1si8632944ejj.622.2022.06.26.09.36.05; Sun, 26 Jun 2022 09:36:05 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=HsEKVQZZ; 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 C213E68B80F; Sun, 26 Jun 2022 19:35:36 +0300 (EEST) 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 AA68268B79C for ; Sun, 26 Jun 2022 19:35:28 +0300 (EEST) Received: by mail-pl1-f170.google.com with SMTP id k7so6195058plg.7 for ; Sun, 26 Jun 2022 09:35:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=/skisbii8ze4D/1K0NRg9Syg6zDsEmnSxaGfph/8Ux0=; b=HsEKVQZZzlOt5or0TPaNie5pcMMQzRtZqelzchXtBrdca6Az9VTR7zrdu2rLvpueq5 keke5vLA+LvKdz4XGv5A+1Di1zBrJcGNEuPJszol1+CgrAiAQGrZkUIplvgJQc3LJyl4 Ob+hEDvlghWiu25Y5JkJrwxlFZfFOibhOUHE4IDHB2IjM/S0fyglxMgozp3qYbCZjYnC enoflXGpdZCDRS7whISxSsu1DF7dRMXHevP9ULEpLkFQzSOWNIvowIBbPBuIrbZbUUsO Dasr7TF3sR5Y5FUkZWYDgUYCulcLZgssLaGAe3z/CoeBNHytoyurMXDDgF90ev9MWJAr phSg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=/skisbii8ze4D/1K0NRg9Syg6zDsEmnSxaGfph/8Ux0=; b=4xnyPN30ET+7VzcJGEe5SA6qfAlzFAQzTyxgtEvXlpD/JpvcpC4mHfMgsLCf6oDNx4 3547V0DvwUN93n89Ww+qP4B6BmOweDhSQ56ZJEUI/xz52fu0EMn5vVlHGnIT8S4s9YQs DN1/MFv64EF9ztXK5vmBQGRyAFugEu5rb9zGC2u1XcCT/x6tIAHd4ETb9EeUHMMw+7eD F68XsczcJn+fvT1T01tnY3FSjb3iH75wprW1cCLxQCaJfmauWQh5Ph6nOoe/gARCYvqN vJax3ES1PrqqxwSI6+VmuK7vbq5VR+Tfxdu6hCeCWAGF+qDsvWZZcwpvSp0eC/l7c/Oy R2Og== X-Gm-Message-State: AJIora/68LS+UPNGzCUx6hKRBIWu5lIlUeO9xbnfI+30sScVJguStwof MSFH2GkPqs8LXhcU2VmN8qXRHEXmdWe8ew== X-Received: by 2002:a17:903:1251:b0:168:c11a:13f6 with SMTP id u17-20020a170903125100b00168c11a13f6mr10178983plh.169.1656261326947; Sun, 26 Jun 2022 09:35:26 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id f15-20020a170902ff0f00b0016a84d232a6sm1229382plj.46.2022.06.26.09.35.26 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:26 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: <0a685a6b19a6e0dd8bd0d4f5dd8ceff4d8a1561f.1656261322.git.ffmpegagent@gmail.com> In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:00 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 03/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: E9bRSRsc7IFp From: softworkz - Modify avcodec_send_packet() to support subtitles via the regular frame based decoding API - Add decode_subtitle_shim() which takes subtitle frames, and serves as a compatibility shim to the legacy subtitle decoding API until all subtitle decoders are migrated to the frame-based API - Add additional methods for conversion between old and new API Signed-off-by: softworkz --- libavcodec/avcodec.c | 8 ++ libavcodec/avcodec.h | 10 ++- libavcodec/decode.c | 60 ++++++++++++-- libavcodec/internal.h | 16 ++++ libavcodec/utils.c | 184 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 269 insertions(+), 9 deletions(-) diff --git a/libavcodec/avcodec.c b/libavcodec/avcodec.c index 5f6e71a39e..0a1d961fc6 100644 --- a/libavcodec/avcodec.c +++ b/libavcodec/avcodec.c @@ -358,6 +358,14 @@ FF_DISABLE_DEPRECATION_WARNINGS FF_ENABLE_DEPRECATION_WARNINGS #endif + // Set the subtitle type from the codec descriptor in case the decoder hasn't done itself + if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE && avctx->subtitle_type == AV_SUBTITLE_FMT_UNKNOWN) { + if(avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB) + avctx->subtitle_type = AV_SUBTITLE_FMT_BITMAP; + if(avctx->codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB) + avctx->subtitle_type = AV_SUBTITLE_FMT_ASS; + } + #if FF_API_AVCTX_TIMEBASE if (avctx->framerate.num > 0 && avctx->framerate.den > 0) avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1})); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 56d551f92d..de87b0406b 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -1698,7 +1698,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()) @@ -2056,6 +2056,8 @@ typedef struct AVCodecContext { * The decoder can then override during decoding as needed. */ AVChannelLayout ch_layout; + + enum AVSubtitleType subtitle_type; } AVCodecContext; /** @@ -2432,7 +2434,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); /** @@ -2525,7 +2530,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/decode.c b/libavcodec/decode.c index 1893caa6a6..e8ca7b6da4 100644 --- a/libavcodec/decode.c +++ b/libavcodec/decode.c @@ -573,6 +573,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; @@ -587,6 +620,13 @@ 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) + // this does not exactly implement the avcodec_send_packet/avcodec_receive_frame API + // but we know that no subtitle decoder produces multiple AVSubtitles per packet through + // the legacy API, and this will be changed when migrating the subtitle decoders + // to the frame based decoding api + 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); @@ -648,7 +688,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; @@ -813,9 +855,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; @@ -861,10 +902,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 = (uint16_t)avctx->subtitle_type; for (unsigned i = 0; i < sub->num_rects; i++) { if (avctx->sub_charenc_mode != FF_SUB_CHARENC_MODE_IGNORE && @@ -885,6 +923,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 17e1de8127..69656729d8 100644 --- a/libavcodec/internal.h +++ b/libavcodec/internal.h @@ -290,4 +290,20 @@ 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); + #endif /* AVCODEC_INTERNAL_H */ diff --git a/libavcodec/utils.c b/libavcodec/utils.c index eb7e505a62..b67b6b6122 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -827,6 +827,190 @@ FF_ENABLE_DEPRECATION_WARNINGS 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 channels = par->ch_layout.nb_channels; From patchwork Sun Jun 26 16:35:01 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 36459 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1539790pzh; Sun, 26 Jun 2022 09:36:14 -0700 (PDT) X-Google-Smtp-Source: AGRyM1u/d/T8jEMDJJ4K3q87YKdkg1JfRydwplpiP4TPkCOa0Rr29BOqLDg9GCbjOWXK9ojso9dY X-Received: by 2002:a17:907:2d2b:b0:726:a628:d1ae with SMTP id gs43-20020a1709072d2b00b00726a628d1aemr1104210ejc.336.1656261374575; Sun, 26 Jun 2022 09:36:14 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261374; cv=none; d=google.com; s=arc-20160816; b=JcaV0RASUt4VeO6xNm2MiJg1+c/uchghh27o5Z/PDXS7B70GUh1zXsamDX0TCVtbzF 8YTcmh1s/qIFYNEEo8CrzwDMcvn47W0PRX6MxqeCb0LuSgm0BTP1s0vHDZBW7tN8mqTO hefpiZgsbtpNGhZ7FPwIk5rREOrnxHvLUDkBLVQSFjfZMinXpsA6wbpSEKggDKyQmoTx uIXzrhQAv4p6HBtHPREorrpYbq87vHw5HecDPS7O1aD6+8lco8LvjF8T9n4esHRkn5vD RBXyzCUFxOhx0e6WWKQMQaltW9HexQqOFyUIuGebn0SOo4t4jL0qwD+RqpJyL75R6Nbj AmfA== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=bgO2FILsiUaA9+DuYBhV5+Tp3oqJK/J1s227fb/Pp94=; b=CJOFIprtH4NsntW3neu6Qnd8oqBtp5lkCbL27uTWWQIIky5MI5arqb8xyP5S5RoX14 bRqf6WnauWg75UTtD7fzEu6vB48mG3lfzKyRb5KksSWiQGQpaf3ZVehn0VWiB2acfkZK E/0IDRj1SwKx82+JxBfV2QLQ4quu1utW74MPudHDJSFDfcyFej4O4Db9tD/Zzs8F4O7Q W4lVnhYsIL7yPfVoHPbiFeIcjHt2tuBI1DaVbcPAPZQDcZDewtTddvDpCjlZ17M/qvi3 nzKs/Dt5cdn9qVc4dljk2HFlMjjx3vS1VMEraWBONLE3LRu4Xhxq0At4zpOpcX8wJc5/ OClg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=qMNKXsXd; 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 y27-20020a50ce1b000000b0043576a75240si9847681edi.274.2022.06.26.09.36.14; Sun, 26 Jun 2022 09:36:14 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=qMNKXsXd; 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 96DBA68B81A; Sun, 26 Jun 2022 19:35:37 +0300 (EEST) 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 907A068B78B for ; Sun, 26 Jun 2022 19:35:29 +0300 (EEST) Received: by mail-pj1-f44.google.com with SMTP id a11-20020a17090acb8b00b001eca0041455so8507953pju.1 for ; Sun, 26 Jun 2022 09:35:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=Xms+Nl1RCwtH/NgtY1GhF/TxEMmjHnLFrVwEyH/IDWM=; b=qMNKXsXdIWKOLbIZz3hEiN6mVkYAp+3L5mo65f4cDTfAt895vjxg+QBBR0eO06bDB7 Trz/yRAngLzeMOul1drB9uaMc5muKw93RShllo4+CkTZzbIQvHBMMIrY1akDEVnAQTsF YgiRTgRip2ix6wN6V31Y1kep/j0+VeXFsAIZRi9n/rQpucyI0dGNrAttNHgK3Oa+vpUd Pl6g28bVu7JLs7PO8kERUnZwSvRb1DgQppdIfFSHFOZ964NNB7qgmAi46l4ZBDAr/QIf 92xX6I/+ceTFQmR8LiOKTn6jOTVg+19AA8mRJTwYDH0MfIIEid+lvGiVTpIXAb5rry1f 7eJQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=Xms+Nl1RCwtH/NgtY1GhF/TxEMmjHnLFrVwEyH/IDWM=; b=beeL+KVeMROfFefY84WyJQ/NXSpSXQowpgj0ZOXmEL2PnaO5eyeoh59AfkdOliLK7t EhHHXG0zcKs7w9hsezxS9/umsGVauJvDg1vl7iiPaVTaPIFBXU1lZ8sQ02TYdRNCDj3u 144HUyArwwvMEd9ev9Hr0t3dQCLggX6PUvDh4LtBBSOJcR31DOdekIJHb/ehV/p9Hr9J lUA4ruOs2XnVJt1HiQxJF0riO/CCTLFeUs6RTxyDkMzDXgQeCr80xPAEpIuWlYZktpeP ez0jYW+4kPCTlBASRf3esn4ovmq6R9/Zs5xC/o3p9Ea/+8VWXwmY/KIU47P7hlBNFkd6 OcGg== X-Gm-Message-State: AJIora+uYRVNdUIstMgh2qVolDp2p3V0Mg20HGtJoLfKHjcMs+ylslzQ VMMfOu+K7DnzDaq2so/KnYNJnLdKOgMZ6g== X-Received: by 2002:a17:90a:e2c8:b0:1ec:9d6b:196 with SMTP id fr8-20020a17090ae2c800b001ec9d6b0196mr10740643pjb.239.1656261328021; Sun, 26 Jun 2022 09:35:28 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id v2-20020a056a00148200b00525343b5047sm5381823pfu.76.2022.06.26.09.35.27 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:27 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: <0b69b1ce19e83149623c2464b5291b809691b002.1656261322.git.ffmpegagent@gmail.com> In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:01 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 04/25] avcodec/libzvbi: set subtitle type 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: BJNzHhazdfvz From: softworkz Signed-off-by: softworkz --- libavcodec/libzvbi-teletextdec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c index 92466cc11e..2aab10a548 100644 --- a/libavcodec/libzvbi-teletextdec.c +++ b/libavcodec/libzvbi-teletextdec.c @@ -751,10 +751,13 @@ static int teletext_init_decoder(AVCodecContext *avctx) switch (ctx->format_id) { case 0: + avctx->subtitle_type = AV_SUBTITLE_FMT_BITMAP; return 0; case 1: + avctx->subtitle_type = AV_SUBTITLE_FMT_ASS; return ff_ass_subtitle_header_default(avctx); case 2: + avctx->subtitle_type = AV_SUBTITLE_FMT_ASS; return my_ass_subtitle_header(avctx); } return AVERROR_BUG; From patchwork Sun Jun 26 16:35:02 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 36462 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1539941pzh; Sun, 26 Jun 2022 09:36:42 -0700 (PDT) X-Google-Smtp-Source: AGRyM1sTHOPi+LUpqSOfULbOZwQ4x2O8+ggJ2/x0fXm6OBvB/2dcq+N7tOtiP4bjawtCHPRzA0hY X-Received: by 2002:a17:906:5d14:b0:722:f288:f19d with SMTP id g20-20020a1709065d1400b00722f288f19dmr8688514ejt.323.1656261402367; Sun, 26 Jun 2022 09:36:42 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261402; cv=none; d=google.com; s=arc-20160816; b=kbsFKFGMja8SWcQ1Rt959PiCSaIQWQRakH/PntW5/QPAV/+RQa79MtMd2aeh6T+qYM PZXvKNuAs7i2S+WzqqK7TEBoR72tvG49erIOicsU7DgZNqjqF94atoJgEzI89JYWok7R 5khCu19K2YxhMxCmwGv8InytL9ecvl2Q8IY2Q/uSj53Lll2VaMUbWXrd3mevjcNejo0Y r6H8yYfhPl4C4vgTkHjJ5KSexo8mjwal21gXCjH5fq4sWTTCFG22WHfTbO7m61X4S0KM wvFMCG2sF7EhRFSKfyTXg1vpd+tBtsOxJt3pWxNL26h/K1qRxC0fdvB2BUNAlcEPDxr2 x3Ew== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=77pS+/jtHJYjMI6CvOVvrbGk3iHeJem4CBImfLKA2gQ=; b=steh7DXFV45ySbUfyPD9hcOvKelVIUa/d9wHe325P65V1G1chZfPPsMjrURkYjiJIy HoPGK7V65Z5PhGv5ackw1lKIxOvk/aA7oswTZiCPpVbKu424vYhi3ExKAB9lQn1s4k7d HAW1aWDXpBXdA0Duk+voh4eMyZVtDKkfXdsxe4D6lvH8vDKz3t+jllQdKVPrGQAdIkIF uPOchH+OXVLDl3Uee4zwVe6vscKkyvt5LEZaFUS6I8JtTbEvr9311px7MTxTx7QUT2ee g8DhRtqQyDmEY6PCCtDVn8oIUZ7SpOFQP3IAxwc0L0xydr4Yb4GffjJatz7WrmzRWRMY S7Mg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=fBN9Q5fW; 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 u20-20020aa7d0d4000000b004355e601557si9927895edo.170.2022.06.26.09.36.42; Sun, 26 Jun 2022 09:36:42 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=fBN9Q5fW; 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 73FDC68B84E; Sun, 26 Jun 2022 19:35:40 +0300 (EEST) 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 BDEC868B7FC for ; Sun, 26 Jun 2022 19:35:30 +0300 (EEST) Received: by mail-pg1-f172.google.com with SMTP id 23so6948428pgc.8 for ; Sun, 26 Jun 2022 09:35:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=OwAbOvynqRhMS61zCf3wNHPYflVi3DT6Sol+N98Ic8k=; b=fBN9Q5fWEwg617vhRotoNUYdbEl3XCfoYCfwjnVRMfFRKVwvqq8IkhhoBDMD5AT1jo mblUvmm4Lfp5n9MNsR9yH021E4rP6U3eJoj1SWoZ9C/RrI4IV6JYFeGEe2ZXl9O4TXUU 5yhtqPpLL4kXnLBiV2aPjOuia9R4QbZWnxNFPFn+Ela382GaRU+tZuyu/XZhqGHFkuv9 h5icBW/N2ecIOlyyHr4LpWvY62j1UEFKztgStKmJ/ixcrKptFnZW/cQVZgEWuRPq7NJk WH7/5/mSuAQAyXa3aI+luJkkP9rdXbdNGUFb6GPrBzhB7Zn8yUhErYn6COtSFsbDiWqg gB0w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=OwAbOvynqRhMS61zCf3wNHPYflVi3DT6Sol+N98Ic8k=; b=wQwmgIZSDErojIVqns0JAJ3hmndbLHJLDxYEfE72zoQvH6ivA7YYkHfmxVfNGLE2DO jUhZFvpvPosX+X7E/yZ3jIYtVO7Ab2oJIa/JFI2wktBzqORDqKkNLuQOUTzgw+i1760I ueIvVi8QRVynWIG6EIVhC/G7Fi0QdHYKdls9+Qb3I5ePQmJGp7wu98G4falL4lg2AqCL /zDVrmZxWeB3Fk8/tigkRyU8YVZTfKZIdAsFDHp8ea6O7eHV8I8Hj6gs2LnAB5T6W3xo R51qVt21V4OhVbCzeGho6XVylPSVbJuJhE5y9SCYcQBK0ilh7GJVhNhzHISvw5ym1ytQ THqQ== X-Gm-Message-State: AJIora+gwl8s7F0+Vc5mt16Jyi8db+M9qZs0dWQSg64TSUE1n9Ia9lKh XztyBPEIEpyCZilUqZCUfaJoM4Y4rlWodA== X-Received: by 2002:a63:8549:0:b0:40d:2864:efff with SMTP id u70-20020a638549000000b0040d2864efffmr8847363pgd.146.1656261328921; Sun, 26 Jun 2022 09:35:28 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id k22-20020a170902761600b00167942e0ee9sm5420914pll.61.2022.06.26.09.35.28 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:28 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: <0c2091e57cd464969fcfbc36fc3004a112910620.1656261322.git.ffmpegagent@gmail.com> In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:02 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 05/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: fvZVAkzakS3N From: softworkz Signed-off-by: softworkz --- libavfilter/vf_subtitles.c | 67 ++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/libavfilter/vf_subtitles.c b/libavfilter/vf_subtitles.c index 82e140e986..0ae156ad07 100644 --- a/libavfilter/vf_subtitles.c +++ b/libavfilter/vf_subtitles.c @@ -36,14 +36,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; @@ -304,8 +302,42 @@ 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 enum AVSubtitleType 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; +} + static av_cold int init_subtitles(AVFilterContext *ctx) { int j, ret, sid; @@ -318,6 +350,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); @@ -398,13 +431,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 = 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); @@ -460,27 +497,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 Sun Jun 26 16:35:03 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 36464 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1540119pzh; Sun, 26 Jun 2022 09:37:12 -0700 (PDT) X-Google-Smtp-Source: AGRyM1vSvGYBeJHObPXccwSk85wT8YSnSF7kiK9kJ0FYhW6VTaDsi0iUKeKS2Fp7SOXvzByD5YMo X-Received: by 2002:a05:6402:528f:b0:42a:c778:469e with SMTP id en15-20020a056402528f00b0042ac778469emr11625168edb.404.1656261432003; Sun, 26 Jun 2022 09:37:12 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261431; cv=none; d=google.com; s=arc-20160816; b=BwI3bVRywQiIZBTZbv0B9fKarI4w8gPRofDxFn89xQQm0IHoMHxtD1FgkO5XHHWq1O 7ElBF35ikil1c5MT6Iu4vDKoV3my+DLyaihScUDfyxAb/qInz1auTnmh9SqrvZS9S6rJ FH+RDe4rzMB7PTrq+srVgKo4MGDqkwMXwVu5ygeM+W0hedGCln2MKKc60W86Flt72Ta0 r66N0RgimB7SoTLnPylgH4PpkJVIxNx1OR465jOqy8tECp8fR1PNjoZEn+tHBflsX2UC 2OvgFW9U1vKO4LxUDpdazRzd4Jd7sewTQ+uVHG8o08TE/aJHVrD4X7CRMDLhyU+tB3es ew+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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=gN6x2sgzVZUi2+hiNul6kgkfPNdNSRCqCYXtlDrsUWc=; b=A60x+kDcghdcEUzs9N2odK+YEnPVvrWEOwIzEZLTzSoAm3lnwjAGOkagaDRFhwfK6r /oi1sbQ2AjjnmVvvCTzwmMqKDbBKWzA8lpFWNhG+fYXDDcHfLYV7tH4sxEzKnCd4qONu 1ERijTRNzt1/OGAf8xejaxhsU6DBf56PUA6v5HtjV+QtKc+Mwbl1fCs05SJ1eNUaAGAs 8VsfyT8yoH0gzjf9oHewyeIojJZBeqWXcbhdiUDhaDg0+JllgqaQEHXKXCqIMfEw/78N UCPRtAtLnr6YJvDlm8tz1Dr+Muqx0mAGLlMKgAc6EzZDcFPyx7M+wXon4TNxXenlVu/C V9XA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=AWPjPIEC; 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 k12-20020a17090666cc00b00718cd011cd9si6377326ejp.59.2022.06.26.09.37.11; Sun, 26 Jun 2022 09:37:11 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=AWPjPIEC; 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 162CF68B81E; Sun, 26 Jun 2022 19:35:43 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f173.google.com (mail-pf1-f173.google.com [209.85.210.173]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5A97068B808 for ; Sun, 26 Jun 2022 19:35:32 +0300 (EEST) Received: by mail-pf1-f173.google.com with SMTP id w6so6923969pfw.5 for ; Sun, 26 Jun 2022 09:35:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=Ym8pWO/MYVCoPzqWD2He+lVgVHVqYQVpep7VdsfAGLY=; b=AWPjPIECJ3BgtkOs9u+SBqiY8bAz37jR+9AIvfhfOIYx6x5FO1r8fHNttEZUh7DCFQ 3AoxKjTbyxjgnhgow/2FVGccTgPTV9lOTpuxI1js9JP4n3M5K7T9tqqkNeKQgG4eTb89 S1OR4a4R0v+Prs8hsQdLP4c+rXVAYZ/GvTG4FM1T3ip4/1/Vp6ea9/ZMyeYJVEELVWre vBB1AVICBtmbmjZgLrBTLw1fj741ES5sW/xbnsjbY7pdAtaWJfE4/EYUVyO+UWkoxebp pX7ovpjOpWbqjaiMPYTD7l3f7T90SgorqHqmyYqGUjOFAGGO+og6LfMmokyRh3q4+X56 wHAA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=Ym8pWO/MYVCoPzqWD2He+lVgVHVqYQVpep7VdsfAGLY=; b=z/hCiZnhDgtd4vOUEzPuta3x6s5VwCbjjJXK0CwpI/TyQz2heI7A1YbUk8UHaR+Uzm nXFMBaivgv3p7rDqwax7YvgLFTiT7ivrs5Ffp57/y8+JJS9kvO967pJCxmGo3qrLv3mi gK+iKQ/9yh2TTPcFBnuLid3vYCwDrEWIeB+/cjzjRQcdzxAF6zlBWvpCYh0+kh1jcvbi iB6j3N6p1VZ3wKAcKpch+JL2V+es5IiK7wx8Melwu3H6vCYQ3mKSceadRjQFPehe6VRt DfYentha9wHuP1/4JTf+zdsmPX8kG95Ma7ZDVr0Nh22zEN25Rj7XSnpAEjv0Y6YH66xP GLdQ== X-Gm-Message-State: AJIora91rrmnKBzfwB3FWCwK+u4GmWsWfFX7BRsi0rK/BGBd05D3JADD FPjH9Vrt41oLw3IyvhqIHEMosCDgCyLCwQ== X-Received: by 2002:a65:6b92:0:b0:39c:c97b:1b57 with SMTP id d18-20020a656b92000000b0039cc97b1b57mr8744243pgw.517.1656261330108; Sun, 26 Jun 2022 09:35:30 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id f1-20020a635541000000b003fcfdc9946dsm5237551pgm.51.2022.06.26.09.35.29 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:29 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: <4903cdd1cd10f71740f1623a0dc3e8edfe67ca21.1656261322.git.ffmpegagent@gmail.com> In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:03 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 06/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 9QdGYp3pGAPD From: softworkz Also add - hard_space callback (for upcoming fix) - extensible callback (for future extension) - new API which allows tag filtering Signed-off-by: softworkz --- libavcodec/Makefile | 56 +++--- libavcodec/ass.h | 151 +++++---------- libavcodec/ass_split.h | 191 ------------------- libavcodec/assdec.c | 2 +- libavcodec/assenc.c | 2 +- libavcodec/ccaption_dec.c | 20 +- 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 | 115 ++++-------- libavutil/ass_internal.h | 135 ++++++++++++++ {libavcodec => libavutil}/ass_split.c | 179 +++++++++++++++--- libavutil/ass_split_internal.h | 254 ++++++++++++++++++++++++++ 27 files changed, 726 insertions(+), 492 deletions(-) delete mode 100644 libavcodec/ass_split.h rename {libavcodec => libavutil}/ass.c (59%) create mode 100644 libavutil/ass_internal.h rename {libavcodec => libavutil}/ass_split.c (71%) create mode 100644 libavutil/ass_split_internal.h diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 3b8f7b5e01..4bfc90b6e9 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -221,10 +221,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 @@ -265,7 +265,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 @@ -442,7 +442,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 \ @@ -464,7 +464,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 @@ -479,8 +479,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 @@ -521,7 +521,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 @@ -574,7 +574,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 @@ -609,7 +609,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 @@ -624,7 +624,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 @@ -659,13 +659,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 @@ -675,8 +675,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 @@ -696,7 +696,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 @@ -753,15 +753,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 @@ -1051,7 +1051,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 @@ -1104,7 +1104,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 4dffe923d9..8bc13d7ab8 100644 --- a/libavcodec/ass.h +++ b/libavcodec/ass.h @@ -23,124 +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)); -/** - * Add an ASS dialog to a subtitle. - */ -int ff_ass_add_rect(AVSubtitle *sub, const char *dialog, - int readorder, int layer, const char *style, - const char *speaker); + 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_rect2(AVSubtitle *sub, const char *dialog, - int readorder, int layer, const char *style, - const char *speaker, unsigned *nb_rect_allocated); +static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog, + int readorder, int layer, const char *style, + 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/ass_split.h b/libavcodec/ass_split.h deleted file mode 100644 index a45fb9b8a1..0000000000 --- a/libavcodec/ass_split.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * SSA/ASS spliting 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 AVCODEC_ASS_SPLIT_H -#define AVCODEC_ASS_SPLIT_H - -/** - * fields extracted from the [Script Info] section - */ -typedef struct { - char *script_type; /**< SSA script format version (eg. v4.00) */ - char *collisions; /**< how subtitles are moved to prevent collisions */ - int play_res_x; /**< video width that ASS coords are referring to */ - int play_res_y; /**< video height that ASS coords are referring to */ - float timer; /**< time multiplier to apply to SSA clock (in %) */ -} ASSScriptInfo; - -/** - * fields extracted from the [V4(+) Styles] section - */ -typedef struct { - char *name; /**< name of the tyle (case sensitive) */ - char *font_name; /**< font face (case sensitive) */ - int font_size; /**< font height */ - int primary_color; /**< color that a subtitle will normally appear in */ - int secondary_color; - int outline_color; /**< color for outline in ASS, called tertiary in SSA */ - int back_color; /**< color of the subtitle outline or shadow */ - int bold; /**< whether text is bold (1) or not (0) */ - int italic; /**< whether text is italic (1) or not (0) */ - int underline; /**< whether text is underlined (1) or not (0) */ - int strikeout; - float scalex; - float scaley; - float spacing; - float angle; - int border_style; - float outline; - float shadow; - int alignment; /**< position of the text (left, center, top...), - defined after the layout of the numpad - (1-3 sub, 4-6 mid, 7-9 top) */ - int margin_l; - int margin_r; - int margin_v; - int alpha_level; - int encoding; -} ASSStyle; - -/** - * fields extracted from the [Events] section - */ -typedef struct { - int readorder; - int layer; /**< higher numbered layers are drawn over lower numbered */ - int start; /**< start time of the dialog in centiseconds */ - int end; /**< end time of the dialog in centiseconds */ - char *style; /**< name of the ASSStyle to use with this dialog */ - char *name; - int margin_l; - int margin_r; - int margin_v; - 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()) */ -} ASSDialog; - -/** - * structure containing the whole split ASS data - */ -typedef struct { - ASSScriptInfo script_info; /**< general information about the SSA script*/ - ASSStyle *styles; /**< array of split out styles */ - int styles_count; /**< number of ASSStyle in the styles array */ - ASSDialog *dialogs; /**< array of split out dialogs */ - int dialogs_count; /**< number of ASSDialog in the dialogs array*/ -} ASS; - -/** - * This struct can be casted to ASS to access to the split data. - */ -typedef struct ASSSplitContext ASSSplitContext; - -/** - * Split a full ASS file or a ASS header from a string buffer and store - * the split structure in a newly allocated context. - * - * @param buf String containing the ASS formatted data. - * @return Newly allocated struct containing split data. - */ -ASSSplitContext *ff_ass_split(const char *buf); - -/** - * Free a dialogue obtained from ff_ass_split_dialog(). - */ -void ff_ass_free_dialog(ASSDialog **dialogp); - -/** - * Split one ASS Dialogue line from a string buffer. - * - * @param ctx Context previously initialized by ff_ass_split(). - * @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); - -/** - * Free all the memory allocated for an ASSSplitContext. - * - * @param ctx Context previously initialized by ff_ass_split(). - */ -void ff_ass_split_free(ASSSplitContext *ctx); - - -/** - * Set of callback functions corresponding to each override codes that can - * be encountered in a "Dialogue" Text field. - */ -typedef struct { - /** - * @defgroup ass_styles ASS styles - * @{ - */ - void (*text)(void *priv, const char *text, int len); - 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); - void (*alpha)(void *priv, int alpha, int alpha_id); - void (*font_name)(void *priv, const char *name); - void (*font_size)(void *priv, int size); - void (*alignment)(void *priv, int alignment); - void (*cancel_overrides)(void *priv, const char *style); - /** @} */ - - /** - * @defgroup ass_functions ASS functions - * @{ - */ - void (*move)(void *priv, int x1, int y1, int x2, int y2, int t1, int t2); - void (*origin)(void *priv, int x, int y); - /** @} */ - - /** - * @defgroup ass_end end of Dialogue Event - * @{ - */ - void (*end)(void *priv); - /** @} */ -} ASSCodesCallbacks; - -/** - * Split override codes out of a ASS "Dialogue" Text field. - * - * @param callbacks Set of callback functions called for each override code - * encountered. - * @param priv Opaque pointer passed to the callback functions. - * @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, - const char *buf); - -/** - * Find an ASSStyle structure by its name. - * - * @param ctx Context previously initialized by ff_ass_split(). - * @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); - -#endif /* AVCODEC_ASS_SPLIT_H */ diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c index f43b500aa7..1a1363471d 100644 --- a/libavcodec/assdec.c +++ b/libavcodec/assdec.c @@ -22,9 +22,9 @@ #include #include "avcodec.h" -#include "ass.h" #include "codec_internal.h" #include "config_components.h" +#include "libavutil/ass_internal.h" #include "libavutil/internal.h" #include "libavutil/mem.h" diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c index 2ac40d5afe..391d690133 100644 --- a/libavcodec/assenc.c +++ b/libavcodec/assenc.c @@ -24,8 +24,8 @@ #include #include "avcodec.h" -#include "ass.h" #include "codec_internal.h" +#include "libavutil/ass_internal.h" #include "libavutil/avstring.h" #include "libavutil/internal.h" #include "libavutil/mem.h" diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c index 34f0513b1a..5f706d985f 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; } @@ -850,7 +847,6 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub, int len = avpkt->size; int ret = 0; int i; - unsigned nb_rect_allocated = 0; for (i = 0; i < len; i += 3) { uint8_t hi, cc_type = bptr[i] & 1; @@ -887,7 +883,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub, AV_TIME_BASE_Q, ms_tb); else sub->end_display_time = -1; - ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated); + 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; @@ -897,7 +893,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub, if (!bptr && !ctx->real_time && ctx->buffer[!ctx->buffer_index].str[0]) { bidx = !ctx->buffer_index; - ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated); + 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]; @@ -915,7 +911,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub, capture_screen(ctx); ctx->buffer_changed = 0; - ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated); + 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 e3bf9f4226..40abdebcc6 100644 --- a/libavcodec/jacosubdec.c +++ b/libavcodec/jacosubdec.c @@ -182,7 +182,7 @@ static int jacosub_decode_frame(AVCodecContext *avctx, AVSubtitle *sub, 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 9658e1d5ac..360e20834b 100644 --- a/libavcodec/libaribb24.c +++ b/libavcodec/libaribb24.c @@ -274,7 +274,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 2aab10a548..54a78342f2 100644 --- a/libavcodec/libzvbi-teletextdec.c +++ b/libavcodec/libzvbi-teletextdec.c @@ -153,12 +153,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; } @@ -225,7 +225,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; @@ -395,7 +395,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); @@ -403,7 +403,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; @@ -463,7 +463,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; } @@ -696,7 +696,7 @@ static int teletext_decode_frame(AVCodecContext *avctx, AVSubtitle *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 f36ad51468..de17400edd 100644 --- a/libavcodec/microdvddec.c +++ b/libavcodec/microdvddec.c @@ -309,7 +309,7 @@ static int microdvd_decode_frame(AVCodecContext *avctx, AVSubtitle *sub, } } 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; @@ -362,8 +362,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 70162b4888..8f2459d45b 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" @@ -553,7 +552,7 @@ static int mov_text_decode_frame(AVCodecContext *avctx, AVSubtitle *sub, } 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 728338f2cc..6f0b7a8a5c 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 "codec_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 56f008b65c..175bd319e1 100644 --- a/libavcodec/mpl2dec.c +++ b/libavcodec/mpl2dec.c @@ -73,7 +73,7 @@ static int mpl2_decode_frame(AVCodecContext *avctx, AVSubtitle *sub, 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 c3e138a7ba..49d42a1d4d 100644 --- a/libavcodec/realtextdec.c +++ b/libavcodec/realtextdec.c @@ -66,7 +66,7 @@ static int realtext_decode_frame(AVCodecContext *avctx, AVSubtitle *sub, 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 cf5dec955b..f35d312c2b 100644 --- a/libavcodec/samidec.c +++ b/libavcodec/samidec.c @@ -143,7 +143,7 @@ static int sami_decode_frame(AVCodecContext *avctx, AVSubtitle *sub, 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 b2df34474e..ccf5981263 100644 --- a/libavcodec/srtdec.c +++ b/libavcodec/srtdec.c @@ -79,7 +79,7 @@ static int srt_decode_frame(AVCodecContext *avctx, AVSubtitle *sub, 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 51456c8b9d..2baa6e70ad 100644 --- a/libavcodec/srtenc.c +++ b/libavcodec/srtenc.c @@ -25,9 +25,9 @@ #include "avcodec.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" -#include "ass_split.h" -#include "ass.h" #include "codec_internal.h" +#include "libavutil/ass_split_internal.h" +#include "libavutil/ass_internal.h" #define SRT_STACK_SIZE 64 @@ -96,7 +96,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) || @@ -137,7 +137,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; } @@ -247,14 +247,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)) @@ -286,7 +286,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 2bda5fa5c1..8651453e3f 100644 --- a/libavcodec/subviewerdec.c +++ b/libavcodec/subviewerdec.c @@ -57,7 +57,7 @@ static int subviewer_decode_frame(AVCodecContext *avctx, AVSubtitle *sub, 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 d509452391..06a25e7128 100644 --- a/libavcodec/textdec.c +++ b/libavcodec/textdec.c @@ -55,8 +55,8 @@ static int text_decode_frame(AVCodecContext *avctx, AVSubtitle *sub, 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 be1d8fb2e8..d4f11a87d2 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 fcf1062d86..549e60fe62 100644 --- a/libavcodec/webvttdec.c +++ b/libavcodec/webvttdec.c @@ -90,7 +90,7 @@ static int webvtt_decode_frame(AVCodecContext *avctx, AVSubtitle *sub, 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 e433bb4579..24d60c5dc1 100644 --- a/libavcodec/webvttenc.c +++ b/libavcodec/webvttenc.c @@ -24,9 +24,9 @@ #include "avcodec.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" -#include "ass_split.h" -#include "ass.h" #include "codec_internal.h" +#include "libavutil/ass_split_internal.h" +#include "libavutil/ass_internal.h" #define WEBVTT_STACK_SIZE 64 typedef struct { @@ -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 48f78c81e5..9da830c0b5 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -103,6 +103,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 59% rename from libavcodec/ass.c rename to libavutil/ass.c index a1e560d7ff..6739132acf 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,61 +113,17 @@ char *ff_ass_get_dialog(int readorder, int layer, const char *style, speaker ? speaker : "", text); } -int ff_ass_add_rect2(AVSubtitle *sub, const char *dialog, - int readorder, int layer, const char *style, - const char *speaker, unsigned *nb_rect_allocated) -{ - AVSubtitleRect **rects = sub->rects, *rect; - char *ass_str; - uint64_t new_nb = 0; - - if (sub->num_rects >= UINT_MAX) - return AVERROR(ENOMEM); - - if (nb_rect_allocated && *nb_rect_allocated <= sub->num_rects) { - if (sub->num_rects < UINT_MAX / 17 * 16) { - new_nb = sub->num_rects + sub->num_rects/16 + 1; - } else - new_nb = UINT_MAX; - } else if (!nb_rect_allocated) - new_nb = sub->num_rects + 1; - - if (new_nb) { - rects = av_realloc_array(rects, new_nb, sizeof(*sub->rects)); - if (!rects) - return AVERROR(ENOMEM); - if (nb_rect_allocated) - *nb_rect_allocated = new_nb; - 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; -} - -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 *effect, const char *text) { - return ff_ass_add_rect2(sub, dialog, readorder, layer, style, speaker, NULL); -} - -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,%s", + readorder, layer, style ? style : "Default", + speaker ? speaker : "", margin_l, margin_r, + margin_v, effect ? effect : "", 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..fc4e6232fb --- /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 *effect, 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 71% rename from libavcodec/ass_split.c rename to libavutil/ass_split.c index 73ef6196c5..3e5e24e2fa 100644 --- a/libavcodec/ass_split.c +++ b/libavutil/ass_split.c @@ -28,7 +28,7 @@ #include "libavutil/error.h" #include "libavutil/macros.h" #include "libavutil/mem.h" -#include "ass_split.h" +#include "ass_split_internal.h" typedef enum { ASS_STR, @@ -379,7 +379,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) @@ -388,7 +388,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; @@ -418,7 +418,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) @@ -430,7 +430,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[] = { @@ -457,7 +457,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); @@ -467,7 +467,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; @@ -479,87 +479,209 @@ void ff_ass_split_free(ASSSplitContext *ctx) } } +static int ass_remove_empty_braces(AVBPrint* buffer) +{ + char* tmp; + int ret = 0, n = 0; -int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, - const char *buf) + if (buffer == NULL || buffer->len == 0 || !av_bprint_is_complete(buffer)) + return 0; + + ret = av_bprint_finalize(buffer, &tmp); + if (ret) + return ret; + + for (unsigned i = 0; i < buffer->len; i++) { + if (tmp[i] == '{' && tmp[i+1] == '}') + i++; + else + tmp[n++] = tmp[i]; + } + + tmp[n++] = '\0'; + + av_bprint_init(buffer, n, n); + av_bprint_append_data(buffer, tmp, n - 1); + av_free(tmp); + + return ret; +} + +static void ass_write_filtered_line(AVBPrint* buffer, const char *buf, int len, enum ASSSplitComponents keep_flags, enum ASSSplitComponents split_component) +{ + if (buffer == NULL || buf == NULL || len == 0) + return; + + if (split_component != ASS_SPLIT_ANY && !(keep_flags & split_component)) + return; + + + av_bprint_append_data(buffer, buf, len - 1); +} + +int avpriv_ass_filter_override_codes(const ASSCodesCallbacks *callbacks, void *priv, const char *buf, AVBPrint* outbuffer, enum ASSSplitComponents keep_flags) { const char *text = NULL; char new_line[2]; - int text_len = 0; + int text_len = 0, ret = 0; while (buf && *buf) { - if (text && callbacks->text && - (sscanf(buf, "\\%1[nN]", new_line) == 1 || - !strncmp(buf, "{\\", 2))) { - callbacks->text(priv, text, text_len); + + if (text && (sscanf(buf, "\\%1[nN]", new_line) == 1 || !strncmp(buf, "{\\", 2))) { + ass_write_filtered_line(outbuffer, text, text_len + 1, keep_flags, ASS_SPLIT_TEXT | ASS_SPLIT_TEXT2); + + if (callbacks->text) + callbacks->text(priv, text, text_len); text = NULL; } + if (sscanf(buf, "\\%1[nN]", new_line) == 1) { if (callbacks->new_line) callbacks->new_line(priv, new_line[0] == 'N'); + ass_write_filtered_line(outbuffer, buf, 3, keep_flags, ASS_SPLIT_ANY); buf += 2; } else if (!strncmp(buf, "{\\", 2)) { + ass_write_filtered_line(outbuffer, buf, 2, keep_flags, ASS_SPLIT_ANY); buf++; while (*buf == '\\') { - char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0}; + char style[4], c[2], axis[3], sep[3], 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; + float f1 = 1; + int x1, y1, x2, y2, x3, t1 = -1, t2 = -1, t3 = -1, t4 = -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; + switch (c[0]) { + case 'b': + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_BOLD); + break; + case 'u': + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_UNDERLINE); + break; + case 'i': + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_ITALIC); + break; + case 'a': + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_STRIKEOUT); + break; + } if (callbacks->style) callbacks->style(priv, style[0], close); } else if (sscanf(buf, "\\c%1[\\}]%n", sep, &len) > 0 || sscanf(buf, "\\c&H%X&%1[\\}]%n", &color, sep, &len) > 1 || sscanf(buf, "\\%1[1234]c%1[\\}]%n", c_num, sep, &len) > 1 || sscanf(buf, "\\%1[1234]c&H%X&%1[\\}]%n", c_num, &color, sep, &len) > 2) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_COLOR); if (callbacks->color) callbacks->color(priv, color, c_num[0] - '0'); } else if (sscanf(buf, "\\alpha%1[\\}]%n", sep, &len) > 0 || sscanf(buf, "\\alpha&H%2X&%1[\\}]%n", &alpha, sep, &len) > 1 || sscanf(buf, "\\%1[1234]a%1[\\}]%n", c_num, sep, &len) > 1 || sscanf(buf, "\\%1[1234]a&H%2X&%1[\\}]%n", c_num, &alpha, sep, &len) > 2) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_ALPHA); if (callbacks->alpha) callbacks->alpha(priv, alpha, c_num[0] - '0'); } else if (sscanf(buf, "\\fn%1[\\}]%n", sep, &len) > 0 || sscanf(buf, "\\fn%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_NAME); if (callbacks->font_name) callbacks->font_name(priv, tmp[0] ? tmp : NULL); } else if (sscanf(buf, "\\fs%1[\\}]%n", sep, &len) > 0 || sscanf(buf, "\\fs%u%1[\\}]%n", &size, sep, &len) > 1) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_SIZE); if (callbacks->font_size) callbacks->font_size(priv, size); + } else if (sscanf(buf, "\\fscx%1[\\}]%n", sep, &len) > 0 || + sscanf(buf, "\\fscx%f%1[\\}]%n", &f1, sep, &len) > 1) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_SCALE); + } else if (sscanf(buf, "\\fscy%1[\\}]%n", sep, &len) > 0 || + sscanf(buf, "\\fscy%f%1[\\}]%n", &f1, sep, &len) > 1) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_SCALE); + } else if (sscanf(buf, "\\fsp%1[\\}]%n", sep, &len) > 0 || + sscanf(buf, "\\fsp%u%1[\\}]%n", &size, sep, &len) > 1) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_SPACING); + } else if (sscanf(buf, "\\fe%1[\\}]%n", sep, &len) > 0 || + sscanf(buf, "\\fe%u%1[\\}]%n", &size, sep, &len) > 1) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FONT_CHARSET); + } else if (sscanf(buf, "\\bord%1[\\}]%n", sep, &len) > 0 || + sscanf(buf, "\\bord%u%1[\\}]%n", &size, sep, &len) > 1) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_BORDER); + } else if (sscanf(buf, "\\shad%1[\\}]%n", sep, &len) > 0 || + sscanf(buf, "\\shad%u%1[\\}]%n", &size, sep, &len) > 1) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_SHADOW); + } else if (sscanf(buf, "\\fr%1[\\}]%n", sep, &len) > 0 || + sscanf(buf, "\\fr%u%1[\\}]%n", &x1, sep, &len) > 1 || + sscanf(buf, "\\fr%1[xyz]%1[\\}]%n", axis, sep, &len) > 1 || + sscanf(buf, "\\fr%1[xyz]%u%1[\\}]%n", axis, &size, sep, &len) > 2) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_ROTATE); + } else if (sscanf(buf, "\\blur%1[\\}]%n", sep, &len) > 0 || + sscanf(buf, "\\blur%u%1[\\}]%n", &size, sep, &len) > 1) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_BLUR); + } else if (sscanf(buf, "\\be%1[\\}]%n", sep, &len) > 0 || + sscanf(buf, "\\be%u%1[\\}]%n", &size, sep, &len) > 1) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_BLUR); + } else if (sscanf(buf, "\\q%1[\\}]%n", sep, &len) > 0 || + sscanf(buf, "\\q%u%1[\\}]%n", &size, sep, &len) > 1) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_WRAP); } else if (sscanf(buf, "\\a%1[\\}]%n", sep, &len) > 0 || sscanf(buf, "\\a%2u%1[\\}]%n", &an, sep, &len) > 1 || sscanf(buf, "\\an%1[\\}]%n", sep, &len) > 0 || sscanf(buf, "\\an%1u%1[\\}]%n", &an, sep, &len) > 1) { if (an != -1 && buf[2] != 'n') an = (an&3) + (an&4 ? 6 : an&8 ? 3 : 0); + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_TEXT_ALIGNMENT); if (callbacks->alignment) callbacks->alignment(priv, an); } else if (sscanf(buf, "\\r%1[\\}]%n", sep, &len) > 0 || sscanf(buf, "\\r%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_CANCELLING); if (callbacks->cancel_overrides) callbacks->cancel_overrides(priv, tmp); } else if (sscanf(buf, "\\move(%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, sep, &len) > 4 || sscanf(buf, "\\move(%d,%d,%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, &t1, &t2, sep, &len) > 6) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_MOVE); if (callbacks->move) callbacks->move(priv, x1, y1, x2, y2, t1, t2); } else if (sscanf(buf, "\\pos(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_POS); if (callbacks->move) callbacks->move(priv, x1, y1, x1, y1, -1, -1); } else if (sscanf(buf, "\\org(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_ORIGIN); if (callbacks->origin) callbacks->origin(priv, x1, y1); + } else if (sscanf(buf, "\\t(%1[\\}]%n", sep, &len) > 0 || + 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) { + + len = strcspn(buf, ")") + 2; + + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_ANIMATE); + if (callbacks->animate) + callbacks->animate(priv, t1, t2, accel, tmp); + } else if (sscanf(buf, "\\fade(%d,%d,%d,%d,%d,%d,%d)%1[\\}]%n", &x1, &x2, &x3, &t1, &t2, &t3, &t4, sep, &len) > 7) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FADE); + } else if (sscanf(buf, "\\fad(%d,%d)%1[\\}]%n", &t1, &t2, sep, &len) > 2) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_FADE); + } else if (sscanf(buf, "\\clip(%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, sep, &len) > 4) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_CLIP); + } else if (sscanf(buf, "\\p%1[\\}]%n", sep, &len) > 0 || + sscanf(buf, "\\p%u%1[\\}]%n", &scale, sep, &len) > 1) { + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_DRAW); + if (callbacks->drawing_mode) + callbacks->drawing_mode(priv, scale); } else { - len = strcspn(buf+1, "\\}") + 2; /* skip unknown code */ - } + len = strcspn(buf+1, "\\}") + 2; /* unknown code */ + ass_write_filtered_line(outbuffer, buf, len, keep_flags, ASS_SPLIT_UNKNOWN); + } buf += len - 1; } if (*buf++ != '}') return AVERROR_INVALIDDATA; - } else { + + ass_write_filtered_line(outbuffer, "}", 2, keep_flags, ASS_SPLIT_ANY); + } else { if (!text) { text = buf; text_len = 1; @@ -568,14 +690,27 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, buf++; } } + if (text) + ass_write_filtered_line(outbuffer, text, text_len + 1, keep_flags, ASS_SPLIT_TEXT | ASS_SPLIT_TEXT2); if (text && callbacks->text) callbacks->text(priv, text, text_len); if (callbacks->end) callbacks->end(priv); - return 0; + + if (outbuffer) + ret = ass_remove_empty_braces(outbuffer); + + return ret; +} + + +int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, + const char *buf) +{ + return avpriv_ass_filter_override_codes(callbacks, priv, buf, NULL, 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/libavutil/ass_split_internal.h b/libavutil/ass_split_internal.h new file mode 100644 index 0000000000..5d82aa8886 --- /dev/null +++ b/libavutil/ass_split_internal.h @@ -0,0 +1,254 @@ +/* + * SSA/ASS spliting 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_SPLIT_INTERNAL_H +#define AVUTIL_ASS_SPLIT_INTERNAL_H + +#include "bprint.h" + +enum ASSSplitComponents +{ + ASS_SPLIT_ANY = 0, + ASS_SPLIT_TEXT = (1 << 0), + ASS_SPLIT_TEXT2 = (1 << 1), // Same semantics as ASS_SPLIT_TEXT. To work around help output default display. + ASS_SPLIT_COLOR = (1 << 2), + ASS_SPLIT_ALPHA = (1 << 3), + ASS_SPLIT_FONT_NAME = (1 << 4), + ASS_SPLIT_FONT_SIZE = (1 << 5), + ASS_SPLIT_FONT_SCALE = (1 << 6), + ASS_SPLIT_FONT_SPACING = (1 << 7), + ASS_SPLIT_FONT_CHARSET = (1 << 8), + ASS_SPLIT_FONT_BOLD = (1 << 9), + ASS_SPLIT_FONT_ITALIC = (1 << 10), + ASS_SPLIT_FONT_UNDERLINE = (1 << 11), + ASS_SPLIT_FONT_STRIKEOUT = (1 << 12), + ASS_SPLIT_TEXT_BORDER = (1 << 13), + ASS_SPLIT_TEXT_SHADOW = (1 << 14), + ASS_SPLIT_TEXT_ROTATE = (1 << 15), + ASS_SPLIT_TEXT_BLUR = (1 << 16), + ASS_SPLIT_TEXT_WRAP = (1 << 17), + ASS_SPLIT_TEXT_ALIGNMENT = (1 << 18), + ASS_SPLIT_CANCELLING = (1 << 19), + ASS_SPLIT_MOVE = (1 << 20), + ASS_SPLIT_POS = (1 << 21), + ASS_SPLIT_ORIGIN = (1 << 22), + ASS_SPLIT_DRAW = (1 << 23), + ASS_SPLIT_ANIMATE = (1 << 24), + ASS_SPLIT_FADE = (1 << 25), + ASS_SPLIT_CLIP = (1 << 26), + ASS_SPLIT_UNKNOWN = (1 << 27), + + ASS_SPLIT_BASIC = ASS_SPLIT_TEXT2 | ASS_SPLIT_COLOR | ASS_SPLIT_ALPHA | ASS_SPLIT_FONT_NAME | ASS_SPLIT_FONT_SIZE | ASS_SPLIT_FONT_SCALE | ASS_SPLIT_FONT_SPACING | ASS_SPLIT_FONT_CHARSET | ASS_SPLIT_FONT_BOLD | ASS_SPLIT_FONT_ITALIC | ASS_SPLIT_FONT_UNDERLINE | ASS_SPLIT_FONT_STRIKEOUT | ASS_SPLIT_TEXT_BORDER | ASS_SPLIT_TEXT_SHADOW | ASS_SPLIT_TEXT_WRAP | ASS_SPLIT_TEXT_ALIGNMENT | ASS_SPLIT_POS | ASS_SPLIT_CANCELLING, + ASS_SPLIT_ALL_KNOWN = ASS_SPLIT_TEXT2 | ASS_SPLIT_COLOR | ASS_SPLIT_ALPHA | ASS_SPLIT_FONT_NAME | ASS_SPLIT_FONT_SIZE | ASS_SPLIT_FONT_SCALE | ASS_SPLIT_FONT_SPACING | ASS_SPLIT_FONT_CHARSET | ASS_SPLIT_FONT_BOLD | ASS_SPLIT_FONT_ITALIC | ASS_SPLIT_FONT_UNDERLINE | ASS_SPLIT_FONT_STRIKEOUT | ASS_SPLIT_TEXT_BORDER | ASS_SPLIT_TEXT_SHADOW | ASS_SPLIT_TEXT_ROTATE | ASS_SPLIT_TEXT_BLUR | ASS_SPLIT_TEXT_WRAP | ASS_SPLIT_TEXT_ALIGNMENT | ASS_SPLIT_CANCELLING | ASS_SPLIT_POS | ASS_SPLIT_MOVE | ASS_SPLIT_ORIGIN | ASS_SPLIT_DRAW | ASS_SPLIT_ANIMATE | ASS_SPLIT_FADE | ASS_SPLIT_CLIP, +}; + + /** + * fields extracted from the [Script Info] section + */ + typedef struct { + char *script_type; /**< SSA script format version (eg. v4.00) */ + char *collisions; /**< how subtitles are moved to prevent collisions */ + int play_res_x; /**< video width that ASS coords are referring to */ + int play_res_y; /**< video height that ASS coords are referring to */ + float timer; /**< time multiplier to apply to SSA clock (in %) */ +} ASSScriptInfo; + +/** + * fields extracted from the [V4(+) Styles] section + */ +typedef struct { + char *name; /**< name of the tyle (case sensitive) */ + char *font_name; /**< font face (case sensitive) */ + int font_size; /**< font height */ + int primary_color; /**< color that a subtitle will normally appear in */ + int secondary_color; + int outline_color; /**< color for outline in ASS, called tertiary in SSA */ + int back_color; /**< color of the subtitle outline or shadow */ + int bold; /**< whether text is bold (1) or not (0) */ + int italic; /**< whether text is italic (1) or not (0) */ + int underline; /**< whether text is underlined (1) or not (0) */ + int strikeout; + float scalex; + float scaley; + float spacing; + float angle; + int border_style; + float outline; + float shadow; + int alignment; /**< position of the text (left, center, top...), + defined after the layout of the numpad + (1-3 sub, 4-6 mid, 7-9 top) */ + int margin_l; + int margin_r; + int margin_v; + int alpha_level; + int encoding; +} ASSStyle; + +/** + * fields extracted from the [Events] section + */ +typedef struct { + int readorder; + int layer; /**< higher numbered layers are drawn over lower numbered */ + int start; /**< start time of the dialog in centiseconds */ + int end; /**< end time of the dialog in centiseconds */ + char *style; /**< name of the ASSStyle to use with this dialog */ + char *name; + int margin_l; + int margin_r; + int margin_v; + char *effect; + char *text; /**< actual text which will be displayed as a subtitle, + can include style override control codes (see + avpriv_ass_split_override_codes()) */ +} ASSDialog; + +/** + * structure containing the whole split ASS data + */ +typedef struct { + ASSScriptInfo script_info; /**< general information about the SSA script*/ + ASSStyle *styles; /**< array of split out styles */ + int styles_count; /**< number of ASSStyle in the styles array */ + ASSDialog *dialogs; /**< array of split out dialogs */ + int dialogs_count; /**< number of ASSDialog in the dialogs array*/ +} ASS; + +/** + * This struct can be casted to ASS to access to the split data. + */ +typedef struct ASSSplitContext ASSSplitContext; + +/** + * Split a full ASS file or a ASS header from a string buffer and store + * the split structure in a newly allocated context. + * + * @param buf String containing the ASS formatted data. + * @return Newly allocated struct containing split data. + */ +ASSSplitContext *avpriv_ass_split(const char *buf); + +/** + * Free a dialogue obtained from avpriv_ass_split_dialog(). + */ +void avpriv_ass_free_dialog(ASSDialog **dialogp); + +/** + * Split one ASS Dialogue line from a string buffer. + * + * @param ctx Context previously initialized by ff_ass_split(). + * @param buf String containing the ASS "Dialogue" line. + * @return Pointer to the split ASSDialog. Must be freed with + * ff_ass_free_dialog() + */ +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 avpriv_ass_split_free(ASSSplitContext *ctx); + +/** + * Set of callback functions corresponding to each override codes that can + * be encountered in a "Dialogue" Text field. + */ +typedef struct { + /** + * @defgroup ass_styles ASS styles + * @{ + */ + 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); + void (*alpha)(void *priv, int alpha, int alpha_id); + void (*font_name)(void *priv, const char *name); + void (*font_size)(void *priv, int size); + void (*alignment)(void *priv, int alignment); + void (*cancel_overrides)(void *priv, const char *style); + /** @} */ + + /** + * @defgroup ass_functions ASS functions + * @{ + */ + 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); + /** @} */ + + /** + * @defgroup ass_end end of Dialogue Event + * @{ + */ + void (*end)(void *priv); + /** @} */ +} ASSCodesCallbacks; + +/** + * Split override codes out of a ASS "Dialogue" Text field. + * + * @param callbacks Set of callback functions called for each override code + * encountered. + * @param priv Opaque pointer passed to the callback functions. + * @param buf The ASS "Dialogue" Text field to split. + * @param outbuffer The output buffer. + * @param keep_flags Flags for filtering ass codes. + * @return >= 0 on success otherwise an error code <0 + */ +int avpriv_ass_filter_override_codes(const ASSCodesCallbacks *callbacks, + void *priv, const char *buf, + AVBPrint *outbuffer, enum ASSSplitComponents keep_flags); + +/** + * Split override codes out of a ASS "Dialogue" Text field. + * + * @param callbacks Set of callback functions called for each override code + * encountered. + * @param priv Opaque pointer passed to the callback functions. + * @param buf The ASS "Dialogue" Text field to split. + * @return >= 0 on success otherwise an error code <0 + */ +int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, + void *priv, const char *buf); + +/** + * Find an ASSStyle structure by its name. + * + * @param ctx Context previously initialized by ff_ass_split(). + * @param style name of the style to search for. + * @return the ASSStyle corresponding to style, or NULL if style can't be found + */ +ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style); + +#endif /* AVUTIL_ASS_SPLIT_INTERNAL_H */ From patchwork Sun Jun 26 16:35:04 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 36466 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1540223pzh; Sun, 26 Jun 2022 09:37:32 -0700 (PDT) X-Google-Smtp-Source: AGRyM1tp8WMy7roZ9MmEuJV9QhW8zjAxSrILPvYtFTgFPrGkvUSVxZKiID6TEcK3g0lIY92lwDGZ X-Received: by 2002:a17:906:d7:b0:718:df95:985 with SMTP id 23-20020a17090600d700b00718df950985mr8830681eji.582.1656261452410; Sun, 26 Jun 2022 09:37:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261452; cv=none; d=google.com; s=arc-20160816; b=R8xE8wam7lKfNhOHtAO9wb3BmDmVL+dsU8qcVJE+b04RvpCufaWeT/q0GCQoMNWzK9 ePNoylgsf3lOYMDYhwajsNCPyyhX5Fl/1lQD98+pg2rEg/MHP6dTFwC/olzBqSAx+Ikw Em+mUc7x72S0gyBI26BLt9vlHUAWGC9jtv1ADHO25Jhq2rdeMokjkGqyjAgPV3WtW5Dp pzA/yVjWWprwkc02R2wHVl2BY28yrhLnwebNg82LKTMj4FsPyMLWP5DZnSLx1ZqfcpKL Q7coptJmIKNX+fHjIDXSIZvWbEE28qPwHEr+mwV1LnutC3yTFIgEJ4TUji+c2SINrBWZ RL6w== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=AdfbzB3AU13UignUU2Lkttd2WL1qzq5cv4x+aQwF+A0=; b=HxQPWm05fpsBQt6ZbihZ9snKjtdiQAtT1vhPR1EfSgcNWtIVx7HyDIm7GO7OSKX/+6 jgyqNt3qlAYXN01UWSeFun7hQ+PXgtM8WQ8+fGxvh1VY23CDx4ZKWoaw0HqehFDEojTU lEybwgo1gG+bTzxXof9s7oRVwnJ/8/SrxRWyaZFIxlxg9GZR4siv4TuH0MzYMOJHWDet IOqRVEZRqF7Iv1ydWECpuP+fp+JPd0u9+02RgUThopeUFms0jJESWijlTXgFVInStL8V 7FJA3xbMJIlbE7pivWK6HNO585cPbcpK5yP7gVKDSuaymRTAeSdBNhfa/RB8gglu4UTq dADw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=ZBLeMUz+; 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 y7-20020a056402270700b00435681044a9si9653181edd.372.2022.06.26.09.37.32; Sun, 26 Jun 2022 09:37:32 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=ZBLeMUz+; 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 C90B068B889; Sun, 26 Jun 2022 19:35:44 +0300 (EEST) 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 CDD4B68B808 for ; Sun, 26 Jun 2022 19:35:32 +0300 (EEST) Received: by mail-pf1-f181.google.com with SMTP id i64so6911795pfc.8 for ; Sun, 26 Jun 2022 09:35:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=+apdqQWCXjvbsiBGHTxHJr6NcaFsm4pI7Cn96n7SgWw=; b=ZBLeMUz+z4HtAOxPT2ljfFX2rQKYokcBwOpBitO3b2PdWCjnwU+143VR4NsBAgqrAb g26OCCZ7697tj/3RM4XSzW2lgX7AMeEu1onO5f5PUCTQEcnAwzfH462ReWxkJ5g/UfSW rchdqAibO/fa2eF+VzPxkgl762pGyoy0LOS23yblc+0pJq62ICcr3JRuMit48dYR7Hl3 3n2of3yeFYgLW2aSyjr/KAe/Oxbhs3spX/Z7i7deKSwN4u19UePgHmbcGYGmHOffA+s1 dSi5wVniQlXknXG1rCoF5ehix+WpfutBRMrlUYVtLhqQO3xpBM4KrxKcZ1KXYgJeVeM0 j6uw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=+apdqQWCXjvbsiBGHTxHJr6NcaFsm4pI7Cn96n7SgWw=; b=JL4Dg83M5H5LkyvViv/w1N1JgUvPhxR//2A8z1XqXyAFg4wDvP7gOLwt8rn/zRGh5K shlhqMDVeLX2MYpptbjgXBHK7I9S//u177hfguyzt8OtiSzTk+qCPg13fnqanFLi0/aK bGU9aWR1rd+IiwJTSpzJkNj+g5jvUuFMhJVzBnnXYUqKmqxcnx7j7ts+GFG+35A/o/wV PLTjHk9UhEYGOsf9idP3M5rVPpwJsEtiS/4xc/dgzJ6WT8HySKCCdwdWFxOunHbgpO2D bRj9Bv/Dby9W67mv0F+TqX0IPm26/XxmuUGBn/TuHGa5YqZP3S0SlCIKZuHWbFXrmiOg k3+w== X-Gm-Message-State: AJIora+KfKdPdAF/wCvrpylrdI/zkl9P2hMtjwjS2cZ1hdef/I5iNuwv NHHymyuECoUTMAarhotTNm3wxAmeym7e6w== X-Received: by 2002:a65:6c10:0:b0:380:437a:c154 with SMTP id y16-20020a656c10000000b00380437ac154mr8463931pgu.549.1656261331209; Sun, 26 Jun 2022 09:35:31 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id u2-20020a1709026e0200b001615f64aaabsm5390216plk.244.2022.06.26.09.35.30 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:30 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: <98f12ad7e90811c5eec7fd535afd916cca7af3b3.1656261322.git.ffmpegagent@gmail.com> In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:04 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 07/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: Yoln0GRpfCBq 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 1a1363471d..7bb60c9b26 100644 --- a/libavcodec/assdec.c +++ b/libavcodec/assdec.c @@ -53,7 +53,7 @@ static int ass_decode_frame(AVCodecContext *avctx, AVSubtitle *sub, 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 4d4007ffd9..8d44529b4a 100644 --- a/libavcodec/dvbsubdec.c +++ b/libavcodec/dvbsubdec.c @@ -796,7 +796,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 97f366cc74..cb6dbbbbbc 100644 --- a/libavcodec/dvdsubdec.c +++ b/libavcodec/dvdsubdec.c @@ -404,7 +404,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 d29db7d49c..24da94faee 100644 --- a/libavcodec/dvdsubenc.c +++ b/libavcodec/dvdsubenc.c @@ -269,7 +269,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx, if (rects == 0 || !h->rects) return AVERROR(EINVAL); for (i = 0; i < rects; i++) - if (h->rects[i]->type != SUBTITLE_BITMAP) { + if (h->rects[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 e50c6766c5..05399863b6 100644 --- a/libavcodec/pgssubdec.c +++ b/libavcodec/pgssubdec.c @@ -535,7 +535,7 @@ static int display_end_segment(AVCodecContext *avctx, AVSubtitle *sub, 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 d62fa164a5..30c3595c97 100644 --- a/libavcodec/xsubdec.c +++ b/libavcodec/xsubdec.c @@ -107,7 +107,7 @@ static int decode_frame(AVCodecContext *avctx, AVSubtitle *sub, 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 Sun Jun 26 16:35:05 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 36465 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1540178pzh; Sun, 26 Jun 2022 09:37:22 -0700 (PDT) X-Google-Smtp-Source: AGRyM1sA3njIRTyqRTm23OwdfMY2AXypPbyoS+VC4izoipPGXnnuYvVfy5a4+uQ3GebyAOzSlanw X-Received: by 2002:aa7:cdd1:0:b0:435:750f:7c75 with SMTP id h17-20020aa7cdd1000000b00435750f7c75mr11551565edw.91.1656261442744; Sun, 26 Jun 2022 09:37:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261442; cv=none; d=google.com; s=arc-20160816; b=sDi7tYr3MifNbVwzB4mMWKGOVqvQBp9bVUSm4ZC2531qjvjOTZfpos71iYBsJR4QKW FlApxGgRPdtX0lg9Kq2N8ynVFB8z29n3aTC8MN0aIAPCtQy7RLhE599+d0ImO8iuAvQA W24kybAhv9f/lB2m6//MghxjT0YrawrW4JUP19qHL2dEA2mNC8GZO4mmAhs24uQKl95k Nd9T69cf1olRcYaGV0d7qO1at9/teHJzsAtxAoNDGIi0qDnwDlACKaH6x4xs8e3oT94q BhIQt6TySa9fiO0C5dh2pSxqUQJU09Kg73PtbvNlzpN8LoYajJ3YFwGxuE1+mhktA/h3 Q7vQ== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=o/mFVfOkwoulHjJDK9farzHkFbM0T7YprNVjC/TO+0A=; b=yIqrb6X3XwpUWJvg+r5VwoSwbbRQva5obhYZT/PU0K6DwbJX6qjrNFN9qJphlYqzNe mNUduYB5eLYCNNqjq2Nj7tBWLcq91lBJIXFUTFpue6ZpaRZh7+WF55hDX5gFdUnw6XV8 RXRdV+lMHZZE7KlVypcHfN8hk81ZI+sCRzIlIePN/Kmn/eX3CXWSdVRrlPe+iTc3tOmL d7JSD71wk1qyF6fn87taz4E6o4CZxBzaItM+YDwvZB0ikSHZtRJuTe/8sDsbGLLNHOVo RZkbaYd0hSAIUNXA/s0pk2/Tl2vsVb89X3NYNgqhvkVGbN8mYPfBMVY3itXW5JqSqHs+ I87A== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=KCJSYd59; 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 z8-20020a50eb48000000b00435b8440360si10099957edp.399.2022.06.26.09.37.22; Sun, 26 Jun 2022 09:37:22 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=KCJSYd59; 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 0841668B87D; Sun, 26 Jun 2022 19:35:44 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pl1-f180.google.com (mail-pl1-f180.google.com [209.85.214.180]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id C955B68B81D for ; Sun, 26 Jun 2022 19:35:32 +0300 (EEST) Received: by mail-pl1-f180.google.com with SMTP id o18so6217039plg.2 for ; Sun, 26 Jun 2022 09:35:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=VzoJpNjbG02/wvEh/HIPjyyAMdLJE5aG+JEcPz6qwys=; b=KCJSYd59iho7hS8ktcuhhD/Zt+0JF6OguRVEhhZlT2XhwLw1/JIC9vLm5NarAAT+Ee q9PCoGnMwppdzHf6ADOhv0N8jwvsO3uGJsqSfN09ILZws3P6tFRZ5yMyP9ns7ZSlNxGd J6avzbVoSwuBNABdtKDCy6ub4yURKaBCFPrr+h+kGt+tey9svM+skxTL9GUUCilYTStA ER2t09fOOSstXDFIMa+6BgAY6Kb+4EjXa9X4jYnl6KU7yDN+P3XLwAgmEVV8p9LhosPI CRFzRYCYHHNv6AqDRWfTDuR4OpS8bFibFDU62ADkFSIzkG7TloUgZkINCusm7MAG7Owl 6U7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=VzoJpNjbG02/wvEh/HIPjyyAMdLJE5aG+JEcPz6qwys=; b=fDUgs5E6U0vJlcDV//GRc8kx3hz4UlGMg2rBLDt2tGlH0yB8Nq8OOLrufYU0whRe4M 3e0X/j1MFUPiEoOLvOf2CQ77yVwZ0CmKDVG4O289H9oGsdC0Dtwzc+mkWIvKx41r9GT8 TohTUHzQnKoRNJm6ytB8rSdwGNwFknn4yRxLQYyEb91AYVkh/Uptl77EYV1Yb7ppdp69 jkb1FXHPjPZ8cg/qzHOQwwr+LD1shXsdsr8ZSctrZ6KHwZK0oMkNGquG6M4+tFARbJ/e Th4kwDWrv5AOp1A4sfyBCFWW7LeDwowYGXQOIK8J7D5oJJ84NILKeehj5Ns/arWRlQyO A4gQ== X-Gm-Message-State: AJIora8ATEcsvRStODuwMOCbjXIG3L/xAy54bIPA6xhCbBGozGePhGjz 0AP8YdddPziyTqX7FmXiaGahAs9SRSFbKA== X-Received: by 2002:a17:90b:1c8e:b0:1ea:4b95:1348 with SMTP id oo14-20020a17090b1c8e00b001ea4b951348mr10731817pjb.153.1656261332205; Sun, 26 Jun 2022 09:35:32 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id q6-20020a170902a3c600b0016196bcf743sm5359196plb.275.2022.06.26.09.35.31 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:31 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: <12c8a308d32f6d565a60fa00eac003929917d7ed.1656261322.git.ffmpegagent@gmail.com> In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:05 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 08/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: JiRmr8N8vSuJ 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 040afa0189..111e157979 100644 --- a/fftools/ffplay.c +++ b/fftools/ffplay.c @@ -153,7 +153,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 */ @@ -574,7 +573,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 (;;) { @@ -608,6 +607,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; @@ -640,25 +642,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); } } } @@ -671,7 +659,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) @@ -969,7 +956,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]; @@ -981,25 +968,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); } } @@ -1026,16 +1015,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 } @@ -1639,19 +1630,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); } } @@ -1762,7 +1754,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) { @@ -2032,7 +2024,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) { @@ -2229,14 +2221,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; @@ -2246,7 +2238,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 5020ba484c..4668cf5dc6 100644 --- a/fftools/ffprobe.c +++ b/fftools/ffprobe.c @@ -2522,22 +2522,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); @@ -2705,7 +2725,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); @@ -2713,6 +2732,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)) { @@ -2731,12 +2751,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; } @@ -2751,12 +2765,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 Sun Jun 26 16:35:06 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 36460 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1539841pzh; Sun, 26 Jun 2022 09:36:25 -0700 (PDT) X-Google-Smtp-Source: AGRyM1u4p09qJv5Up3dn4VQVvNCYYB0EuGWI9XkfI004OSKFZwm8Y++XkMqbmFSoDM7AxbeVqsl+ X-Received: by 2002:a17:907:7202:b0:722:e4d6:2e17 with SMTP id dr2-20020a170907720200b00722e4d62e17mr8795127ejc.434.1656261384771; Sun, 26 Jun 2022 09:36:24 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261384; cv=none; d=google.com; s=arc-20160816; b=wXjovgHGkqgYqqpNT88Wkdvr5lY7zni4IpWBg4fKh+8lxU6NddWkFMU4ZQnQIfOOsm Ra2H9eGhODvUzG6a90ef/zdMj5vCM9wJp6zaPi8fiVhVUJL9VSNHIlitSnulIW4tPDKG AtwTJZWsWcV8w4Sa+ai4RVhHDe/9wzMZA1hnW8lL59h3H0rOluNOHQ0+Pd7BA0/H0Nt3 XwJ5x7kXFu+ZairTtp8KwV69H2Wxq93hfjSv/m65BzAUYCqWBtUE2Vc72q5DqnxdBsUA 8TCbIvczRBCNEtGp1S8OsD+xtl5xbRBKPFj+5w51XirTvYhOsi5/LM2LcMZ42lf86r3f +T4w== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=ArgArcddnW6CEjs+l93gVDuxxjwgCYReD9AZQavuJB4=; b=xn9eH+JFoxXama3541Wox9KlS3Ec9z+bL5nLvmbEJrXNEgVfPoQgYqy6vgBL3Tlb0s fcbf157ud32iQ/CvmUvxoTbXtwZM7Bn4bhs2j3uZKp9SsHdgT0f+j9FluPQD8flucQxg tsZOETmPOtp77hRcXN5gQoNJMIl1dphSt6hTjjT2iFicKtbDQX8Nc0xfo+7HeOOf6D+F xIT5sUSF6TwAVzsNJr5/rJa2VPxwmSdZiaHGZyztceiPZkc0A/QVZiCVjlxCVu8jyiUS zdH/qhmRHNgG/YI7bIa5XCbOk1NGjgT0QNTEjrPTmt4xH7LJz3pTHVPkMVsnfs4XsBGA 65fA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=FzQOPn6r; 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 ha1-20020a170906a88100b006f3be13d016si8717373ejb.37.2022.06.26.09.36.23; Sun, 26 Jun 2022 09:36:24 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=FzQOPn6r; 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 83A8368B81D; Sun, 26 Jun 2022 19:35:38 +0300 (EEST) 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 134EB68B7DF for ; Sun, 26 Jun 2022 19:35:34 +0300 (EEST) Received: by mail-pj1-f46.google.com with SMTP id k12-20020a17090a404c00b001eaabc1fe5dso10173574pjg.1 for ; Sun, 26 Jun 2022 09:35:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=oYfoR8RImfUhwn1dzHYY1AxSOuEjRY43nJnB6xnih24=; b=FzQOPn6rHobEt4HwGcTN2U7XdbriMZvY+x8Z9O4lEoJ3UyR6lXkUEY/QY/Z7tAEoZt h2GEcyeTCn1LcFZ4DEbrcldrEP8D7tABBJsieH5623q3X947G1NjjWKiGLvP/xmBCEUl Fs+ygGyW3qNkmzju41bHciGLhBnd7bIOXH9/RbabQ4BQFVi5CCn48HXx4rmv0vkJfLkm M4DyOrccenxmv/49JkfO0+RivXA0eG1rdlCsyCLcwzHOr8ML8k92A+j7AXYOjEEyKbwF gGaxLXKpaXslzwunO752gfEEPLCf3NSZVcWxv35g987OzGMvFak7TZmw0xmEZAm+tn+F B5fw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=oYfoR8RImfUhwn1dzHYY1AxSOuEjRY43nJnB6xnih24=; b=wE5TZVJ4uzkSqvmBRoBjSxviJR/62/OYV/tJ+1Jt11i9FEwcSnio8NkWSpZrnBQuvJ IDvRVPNuMSTRs9hqw39XJoA1Cps7RXkQZrYj045XYcyn7rMLhiHmdCClR9AQYaCoO0XN O3SHD8VnezFn40B6beNLoxAoLA47ztmcB2qwkL/+1yXJiU1Eym/pxMOXRyaCVx3xB/DE TP4f0elJqY8XBu2B2dNtAYoGB7qi3s3rKpgrmSyPY5Crv1pbSUP5+pNs45afMbfNW83s KHipoNiSn3jnfOf7HFITjH+qc0/gnW7KKpeFKG1MMn+PacEUrR5ETkvqnRy8lEb3Es/O rBRg== X-Gm-Message-State: AJIora9QO1FTdprCf3INQ/fckjJqm0JlkKr3eJtuuzdcbMXN/3vWSlxp zKcb2r62NJyJObCAxVzDkTXAk1kjbl/Llg== X-Received: by 2002:a17:902:f34a:b0:16a:e2d:3e9 with SMTP id q10-20020a170902f34a00b0016a0e2d03e9mr10190365ple.95.1656261333150; Sun, 26 Jun 2022 09:35:33 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id d10-20020a170902e14a00b0016196bd15f4sm5427794pla.15.2022.06.26.09.35.32 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:32 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: <2e55dbe180301e658249b4737b80995af2f6f93d.1656261322.git.ffmpegagent@gmail.com> In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:06 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 09/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: tGWgT+kUjGvW 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 7ba1c8a861..af52a77ebc 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -20,6 +20,7 @@ OBJS = allfilters.o \ framequeue.o \ graphdump.o \ graphparser.o \ + subtitles.o \ version.o \ video.o \ diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index 965f5d0f63..28c5430c3e 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -42,6 +42,7 @@ #include "formats.h" #include "framepool.h" #include "internal.h" +#include "subtitles.h" static void tlog_ref(void *ctx, AVFrame *ref, int end) { @@ -1452,6 +1453,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 0f8da367d0..6c8496879a 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -89,6 +89,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 Sun Jun 26 16:35:07 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 36461 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1539887pzh; Sun, 26 Jun 2022 09:36:32 -0700 (PDT) X-Google-Smtp-Source: AGRyM1vWAiK7KKyr5d2YbEEDX0bm2OTgdy0xfir2UQ1NtnpuxC3v3rfUjx8pj1jxA9XR8wXUGSkR X-Received: by 2002:a05:6402:2398:b0:435:9685:1581 with SMTP id j24-20020a056402239800b0043596851581mr11774509eda.333.1656261392775; Sun, 26 Jun 2022 09:36:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261392; cv=none; d=google.com; s=arc-20160816; b=UUQtVtTtwKzHVvx1gdQ5s0mpDXZxS0wTbSmj+dUjKSx8aosbfFgmHBwovablgEAmA/ l4RBPjQopI/clDYnJSr19QfEDO6H2N9piLh+eURD+YaUpWblGwYyyQRyD6rukNPALNY4 DNs35t2XAMY4uiS9Ql+4GZcAqZOr+xCseqdGTKMd4bWPh6Nf9dxdaY0bsP0mklYrCq6D 0meCA0XHDV1GEIEMRmJU8hzouvuj9Xtl+bF1FFXULWNH9MTrU1g2Fnuk911HDidRcIbz vbYiRs/tew/dbO7PqET8AHveQMUarDXQQlQnk6k6KWASJEi+GDg8vJS3oHalBmseUHlS kYWw== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=eld7nRYghlzcZ4NWGSmBHDbuFv1AVm4ZR+gyNluLdug=; b=VNRqLi2MfIO2uB0qDJVVVrRZG9Mxjvo/HwebDroFOmCNPFIEvFsIFX0hPjiBraQtsy +dKsN6Lidh5kESNrPMfGdGwBxkHrcxs+860cdT+q3A47cTVwz/+n/3db/kz/v/OnpTAA 2akIOue4J3dr9vP/4lbIx3io+RWPNHDh82bpfxID5kKxD4YZEToNl5nPQUPLduWnzwBJ 1HB0Uf4rSihQAWd/fzZgNFvN30IUYqF9KUKKU1YtXLtpJq/9VWMyEtWo3W2T4r+Lee6Z 1X7R5cuN1j7odRI23vrnN5u42vhVHbIkhzQbCEmaDDTnl7WG4zEzSOf08DBi+r+EJSyo //OQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=eupfpeks; 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 ji2-20020a170907980200b00718c1dc9107si8785486ejc.159.2022.06.26.09.36.32; Sun, 26 Jun 2022 09:36:32 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=eupfpeks; 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 81D6868B85B; Sun, 26 Jun 2022 19:35:39 +0300 (EEST) 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 BB72668B7FA for ; Sun, 26 Jun 2022 19:35:35 +0300 (EEST) Received: by mail-pg1-f172.google.com with SMTP id 68so6938997pgb.10 for ; Sun, 26 Jun 2022 09:35:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=gyXuqPnXRJRsQo7s/zduoUHSsEuiDDANa7ac6fVqnTI=; b=eupfpeks/uLkZtsiu3y6SgxDNa0Eb1qesEd7bO9AVM92/g8MgJ3afwknScw5Dt+7yL vrI0hq4pEQxED54DAF1M78CDOhUIdOEXCuTHXSjUO6kpK4JlsWydaREF10WLpyjIn7zj aTv27HVn/r5ePK5L3tZJlnfX7vsvsfTqAQ6FVEzVEKJ7/ighsAr5x9bq+Poihy6FuwDD 04Fz4wPiPOwzgr31Cg7jiQyUfsNjyuMgiZNg5mKbSlOz6o4jr66jahKGZk6DJPb/NGRt SNgBepUtfvYAl62PcOqCkdYTTNWTgUc+uPlU0CxdugIkwLMphuub21OZhv8bH/7i2NST Ogjg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=gyXuqPnXRJRsQo7s/zduoUHSsEuiDDANa7ac6fVqnTI=; b=dCK0zvzNxCvpL0rqZk638fH0zpAFfy9ufnfKoVxY6Um9JsLB2qgImVRL56lgcHWMfw jDyV3ZBqtZpQXFLNxYKguDtPRDDfwpYRXjBmbFufcmmXiFWddFBgCMan6bg6LGeo2+6a RZFwkdGd1lMUn8UPhTqfXjKph4k4ezF61c97i2CCVCUpVQV5zk8rOMmDZNVSl0jJ4kS8 dbHwj4zXld3Ph5XijR2IPKM5pvStTabVWsrYrUBUGukUnOUZaxapzMuAnGOZk/8fheQK 9Q/Hzk3yzYiw1DEe9LTqDn1MR8O2xhbZI/NwV/sy08kJf0p23cfjfdNDzKshf4dd/qD4 OwqQ== X-Gm-Message-State: AJIora9X0uSxQ2CAwAGM8il2F754nmVkaonJL5/yA2dVkDQtiVdjnCfi LBg51bAtgjFbM70qZnJj3DC2Bexu7veedw== X-Received: by 2002:a63:cb:0:b0:40c:a2b4:4890 with SMTP id 194-20020a6300cb000000b0040ca2b44890mr8682580pga.304.1656261334111; Sun, 26 Jun 2022 09:35:34 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id n18-20020a63f812000000b0040c33cb0ccasm5389718pgh.42.2022.06.26.09.35.33 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:33 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: <0d953dedcbde17a1bf9e75f46faf964b43a2c86d.1656261322.git.ffmpegagent@gmail.com> In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:07 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 10/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: XAu+zgI9yCHW From: softworkz Signed-off-by: softworkz --- libavfilter/avfilter.c | 20 +++++++++++++++++--- libavfilter/avfilter.h | 11 +++++++++++ libavfilter/avfiltergraph.c | 5 +++++ libavfilter/formats.c | 16 ++++++++++++++++ libavfilter/formats.h | 3 +++ libavfilter/internal.h | 18 +++++++++++++++--- 6 files changed, 67 insertions(+), 6 deletions(-) diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index 28c5430c3e..853e2b967f 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -52,7 +52,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, @@ -60,12 +61,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" : ""); @@ -348,6 +350,14 @@ int avfilter_config_links(AVFilterContext *filter) if (!link->time_base.num && !link->time_base.den) link->time_base = (AVRational) {1, link->sample_rate}; + + break; + + case AVMEDIA_TYPE_SUBTITLE: + if (!link->time_base.num && !link->time_base.den) + link->time_base = inlink ? inlink->time_base : AV_TIME_BASE_Q; + + break; } if (link->src->nb_inputs && link->src->inputs[0]->hw_frames_ctx && @@ -1013,6 +1023,10 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame) av_assert1(frame->width == link->w); av_assert1(frame->height == link->h); } + } else if (link->type == AVMEDIA_TYPE_SUBTITLE) { + if (frame->format != link->format) { + av_log(link->dst, AV_LOG_WARNING, "Subtitle format change from %d to %d\n", link->format, frame->format); + } } else { if (frame->format != link->format) { av_log(link->dst, AV_LOG_ERROR, "Format change is not supported\n"); diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index 2e8197c9a6..c436f304bc 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_major.h" @@ -349,6 +350,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. */ @@ -357,6 +364,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 b7dbfc063b..f37be0203b 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -309,6 +309,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"); } @@ -439,6 +441,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 e8c2888c0c..12585ed428 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" @@ -491,6 +492,13 @@ AVFilterFormats *ff_all_formats(enum AVMediaType type) return NULL; fmt++; } + } else if (type == AVMEDIA_TYPE_SUBTITLE) { + if (ff_add_format(&ret, AV_SUBTITLE_FMT_BITMAP) < 0) + return NULL; + if (ff_add_format(&ret, AV_SUBTITLE_FMT_ASS) < 0) + return NULL; + if (ff_add_format(&ret, AV_SUBTITLE_FMT_TEXT) < 0) + return NULL; } return ret; @@ -774,6 +782,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); @@ -782,6 +794,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 22224dce2d..6cf952a059 100644 --- a/libavfilter/formats.h +++ b/libavfilter/formats.h @@ -183,6 +183,9 @@ av_warn_unused_result int ff_add_channel_layout(AVFilterChannelLayouts **l, const AVChannelLayout *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 6c8496879a..7972c5c6de 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -148,9 +148,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. @@ -161,8 +163,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) \ @@ -174,16 +178,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 Sun Jun 26 16:35:08 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 36463 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1540050pzh; Sun, 26 Jun 2022 09:37:02 -0700 (PDT) X-Google-Smtp-Source: AGRyM1scZgCbzJjnDgyzXI7jgxXtODo/Yhc+6zDHwV1ZdBs/jPOvc6/xDieYxMThBDPoonOB/WDt X-Received: by 2002:a17:906:848b:b0:718:c04a:5131 with SMTP id m11-20020a170906848b00b00718c04a5131mr8882923ejx.70.1656261422313; Sun, 26 Jun 2022 09:37:02 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261422; cv=none; d=google.com; s=arc-20160816; b=M7O5rlGMcuM12fUBhtlEaFrMZQyaAlxtH4MqccB1TEIZdhCVS5Q9VMV1n1hfL/3vXj jwfQwgpVkZMsRQr9UPcIUkEE8q9Qh4MTVS08X4DbcQEKrHLAi5ZNUb+VpMHEmEaCGhZL APG8U+9urK828WmMFxo6AQmvKGdGevahR6dRn8XWys477r0+HAAn3ky7xhcFIT4iG0BU WGEaqHeb6UvlQyQEIZQdcXiERX55bV/3otCJfnslhO5aAHASe7NUcT+Hx/us9a0gHqyF S1tCvhC5n7rlWPm6iC6E/qaE5L85cBoQ4LZe+HqvFckpbApdYyo4cvTal4u6pXLuoqdb DNjg== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=4g61yKRBfTasaoOxMZCYhItZRj7VzC7+x39f2rAXY2s=; b=RW04+yxDXE5qEHx2C2qlhPA+LXD3KFYbOpt2INhXSCKk2RQc8A+UM7u89CBCbuN2jS eydrIO5fYDQwjVsMFE989KcmURFDv3lDKPv75dmMR6sVVqg+LXh87+5VaeDqM8OWZesy nBtO/MpHo7YME7NStRxyCblKUA3gWz2c4hL7leGOFRHEoP8yZfQ8AnhjN/2lHiE4FEKK bh/9THSmnbzpYVr8SYaDdtqvHCqR+JpzTPuQksuJo44z50jPxg5kcSX9MUWABKLoUMUH XjHaHJ3RKjEs8a6UN/joO4FUc2Acp6/2XsdKKaV8wHZYex4XpXDcQYXTU4hdyo6zOYnn GsYQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=VlnMynnL; 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 bp16-20020a170907919000b006ff278107bbsi8512604ejb.597.2022.06.26.09.37.00; Sun, 26 Jun 2022 09:37:02 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=VlnMynnL; 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 383A568B86B; Sun, 26 Jun 2022 19:35:42 +0300 (EEST) 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 E637768B808 for ; Sun, 26 Jun 2022 19:35:36 +0300 (EEST) Received: by mail-pg1-f181.google.com with SMTP id e63so6955349pgc.5 for ; Sun, 26 Jun 2022 09:35:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=MNcLRSLBqO7OXbNGwb+5j+sSx1ajfXoLKzl6ro5TTMI=; b=VlnMynnL1UIUfd/I3J3xG6PdBiPbRZmhVDf/VFiW7DLFQf2/FXTBq8hPTeEuPlhnI5 N43Ey7DMVZ/+QZMf9Gif+yohwuaWn2BiUoqIwM4gUFY2yEdjN16ikvHaD6g/6ZqmlRvA pSYWcIfWAbKs4ZGng2aS3Bok1hGh4cSwgIdYCD+7OsPKEDf+wIly9tsuBZhTwTy5yUEz HcJBB3iduC7WF5Eo60pMRIMN7XTrUDhzs+ac7M5s7FDIg6L2Bu98j71KhKyRZ0+UM+ne md3+HmY61M9peBgWpuz4P2Tf7y8bzGhfe5KYW6jemrolD+9P4ETo65XKdZu+Gfea+rwU +xrQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=MNcLRSLBqO7OXbNGwb+5j+sSx1ajfXoLKzl6ro5TTMI=; b=4+3+Fj2RTBXk5gl4aCFKQBu2T7XEZchP3qnMjxktJCs5Pq2OUq/cH0oMxrGAdwDaUW 7BCDF+0lVIz/rSM0V1jQAeSbXs2uzrzcVjFpSI0iHR4nTRC7DZKcNkdY5yPLQm4z3itP rZtwe3tpPKaZNsyK4HRFfcrEUkOGgWqUJ5kHgQDnGDtV+Lgtj/vKgFnRE2p39pRFgZwF IFm+iWW0kxpUkX9LDVvaU7I/BTfy0J+NmKd98mNhgvkiUC2nDEbfuAeUJQUSrEgg+skD H5tGTHsEf5w0EabNyXtPKlLS+8+gRYTHj9qr91Ejn75Hj5mYd0TyEAUhBmSJL06jpH+F K5AQ== X-Gm-Message-State: AJIora80Vn4hDaPhFdbT8C3T7GSICyjXO7pMhRnPN8WDnPyz3b81tidP QjfwH5eVGnmMw/7RsyU5/fn817zCF5M/uQ== X-Received: by 2002:a63:1b26:0:b0:3fd:8db8:9602 with SMTP id b38-20020a631b26000000b003fd8db89602mr8712956pgb.239.1656261335037; Sun, 26 Jun 2022 09:35:35 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id n17-20020a170903405100b0016a25ba1f46sm5350531pla.256.2022.06.26.09.35.34 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:34 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:08 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 11/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: r7mM0wtLxvhu 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 853e2b967f..e6217b60d6 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -451,7 +451,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; @@ -460,8 +460,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)); @@ -1161,6 +1161,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) { @@ -1170,7 +1178,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; } @@ -1207,7 +1215,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 Sun Jun 26 16:35:09 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 36455 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1539989pzh; Sun, 26 Jun 2022 09:36:51 -0700 (PDT) X-Google-Smtp-Source: AGRyM1uG4S6/t94yR6LZZsveg4ieO8Mx5tAPYrK/jB+ho7quMG4RBYIWgqT83qcym33M49K0okTJ X-Received: by 2002:a05:6402:3448:b0:435:71ca:b46b with SMTP id l8-20020a056402344800b0043571cab46bmr12204738edc.348.1656261411735; Sun, 26 Jun 2022 09:36:51 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261411; cv=none; d=google.com; s=arc-20160816; b=W6uV/qwWlGpgIKEiSyGm3ebNuNJLIUafqdx8JVuhaOvBH8yRNT4wLfhfFlnEYOYCL5 v7Y38FiIS7M75roR5/C3LbwML6pMGpRoDmncJYJActi1QsI2xpGAPPWsrU9Mt3SOtVqj EmX8kxf6IhtfYI1l5Q1uBIPbxQOtpVpCJKU51F6hOlhideTwUB4Ikw+SLRjrb5BTT2Dw OHM+v3zwDwvN5Nmmfico5jfsO8jIXGRfGRud2a9EMkIAEcmTTHlBiz6yRmeGAkXPBmq1 ohbE1qXDKFpJOH4or+QULTO6iFty7Tb6qlS5YEmgW0SU165FZtZJQXAlbyEgdffNrDB+ Br7Q== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=9aaEf624RMPyHvXhx3+SkLFqUBD7gssrGuhvg+5M9ew=; b=jgtQtFYzHiv5HBgLvEKC+JcNtvS+EWOGCJ8PGazdXTSHEO/mZcYVCDsHQdBDT+uBbx nJujItfBZYOK/5o9TYICvYZ8eWs3RooiVvPWioYzGuEdMKxlLd4nOEw8UFP8jCLQFsB0 akjMf7MJtBe+r0ddazgAZdmL7/MLAJpONsvPwqw84u0Mp9MTpeYqnppzyKO9XI7D4CAV BUPfD3j0MghPBmbl2rxb/RF+k6G3jGlMN4cyyc/ctGhM9G93Bw7xlbAcYsI9Cgn7kuMn QoXd8QZ69WAn/MjkjhA2m4N9pgVnqiCMN6Yer4nga31CL6IUD2jzafzoZWEewVGw7VOI s68A== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=YT0PKcVX; 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 dp16-20020a170906c15000b00726974e0eeesi2973599ejc.170.2022.06.26.09.36.51; Sun, 26 Jun 2022 09:36:51 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=YT0PKcVX; 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 55D8668B82F; Sun, 26 Jun 2022 19:35:41 +0300 (EEST) 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 A706568B808 for ; Sun, 26 Jun 2022 19:35:36 +0300 (EEST) Received: by mail-pj1-f46.google.com with SMTP id k12-20020a17090a404c00b001eaabc1fe5dso10173574pjg.1 for ; Sun, 26 Jun 2022 09:35:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=FxrAcgJ08tBYFhNfSk44ZfNTfmBt6AeLStifM9LJOwo=; b=YT0PKcVXmTmt88nPE5u8KMe7kGMSOnU7XofY1C7Hqxq8js6qQTdRukw6ZgVDlfzdUP HVgajzt7mradu27cT+W3HtKGWJNAAJ5gnlNXRfeu1bRRrLCD8dDADzpiy+gB/OK/VYeS alhH8dmIQaVJHFx/+sFXn8sZnrchbf/bCvENXk+58sP3b8naEYsDmtsw+gxsNej7rQjT 6vMY/jMrPpQPWI/ZhExAB/F4Ez92jQgSOxMgGTSaBJsA3gt/XmkIvjdTDO6s23AO0OnT v2//nk+vBYjde3rsThaXiOOvURJf2Eqah0hrqU9F2/SQLImnzXpy6E/nSPLVlTp8+lR9 NN6w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=FxrAcgJ08tBYFhNfSk44ZfNTfmBt6AeLStifM9LJOwo=; b=T8grZfBpqPHVlJXAGnsIXBPQ++VdQ7DFApad4C8IDxUDaT0PAPdrkAsftkS3XYT305 HDmA6YB09ZbzYHoibnyKtDlXNK1R+3HcuRoNK8yHzomkCHuqN8XbeZ6M5E5B55OGq7Ik sd/uBRS7tspDufzuzv296CIYlGgfLI1aLziCHlFx/7Xs/B8D/QlTiNqA08WULrzBMZHj a40jXODYI+YJhq8qEyv7X2Zf3lLSeH6a5yf88GsUjgAxG1uSHTOwnme6WYE1gGVn4JtE K8bdsT5UuEq/lT4Vqybx4H3JE/p7kgOUTF+/79fWmD47OiOHjMknn+GrzZ4QalpyuExk yeXg== X-Gm-Message-State: AJIora9PzEJkug0Drui0GEre9Daw9nZ/VfAfGziwRfwvsXG3O3ImcD5d OeRb5jw/XzBATXgoauzjl5ESzExscPmvhA== X-Received: by 2002:a17:90b:1e0e:b0:1ec:b2a6:c9d0 with SMTP id pg14-20020a17090b1e0e00b001ecb2a6c9d0mr16201780pjb.230.1656261336007; Sun, 26 Jun 2022 09:35:36 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id u13-20020a63454d000000b0040d2224ae04sm5345163pgk.76.2022.06.26.09.35.35 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:35 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:09 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 12/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: KywD2gOjNgko 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 3a97610209..0580e4a536 100755 --- a/configure +++ b/configure @@ -7880,7 +7880,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 ec70feef11..5288480a55 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -571,8 +571,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 e269cf72d1..204e9bad6c 100644 --- a/libavfilter/buffersink.c +++ b/libavfilter/buffersink.c @@ -30,6 +30,8 @@ #include "libavutil/internal.h" #include "libavutil/opt.h" +#include "libavcodec/avcodec.h" + #define FF_INTERNAL_FIELDS 1 #include "framequeue.h" @@ -61,6 +63,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; @@ -372,6 +378,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_format(&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[] = { @@ -395,9 +423,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[] = { { @@ -436,3 +471,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 01e7c747d8..42c164e670 100644 --- a/libavfilter/buffersink.h +++ b/libavfilter/buffersink.h @@ -128,6 +128,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 a3190468bb..1679c54f17 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 { char *channel_layout_str; AVChannelLayout ch_layout; + /* subtitle only */ + enum AVSubtitleType subtitle_type; + int eof; } BufferSourceContext; @@ -143,6 +147,13 @@ FF_ENABLE_DEPRECATION_WARNINGS return ret; } 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; } @@ -224,6 +235,8 @@ FF_ENABLE_DEPRECATION_WARNINGS CHECK_AUDIO_PARAM_CHANGE(ctx, s, frame->sample_rate, frame->ch_layout, frame->format, frame->pts); break; + case AVMEDIA_TYPE_SUBTITLE: + break; default: return AVERROR(EINVAL); } @@ -296,6 +309,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 }, @@ -325,6 +339,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; @@ -393,6 +417,21 @@ FF_ENABLE_DEPRECATION_WARNINGS 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; @@ -426,6 +465,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); } @@ -456,6 +500,11 @@ static int config_props(AVFilterLink *link) return ret; } break; + case AVMEDIA_TYPE_SUBTITLE: + link->format = c->subtitle_type; + link->w = c->w; + link->h = c->h; + break; default: return AVERROR(EINVAL); } @@ -520,3 +569,26 @@ const AVFilter ff_asrc_abuffer = { FILTER_QUERY_FUNC(query_formats), .priv_class = &abuffer_class, }; + +static const AVFilterPad avfilter_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(avfilter_ssrc_sbuffer_outputs), + FILTER_QUERY_FUNC(query_formats), + .priv_class = &sbuffer_class, +}; diff --git a/libavfilter/buffersrc.h b/libavfilter/buffersrc.h index 3b248b37cd..45a657bbb6 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 Sun Jun 26 16:35: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: 36467 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1540287pzh; Sun, 26 Jun 2022 09:37:42 -0700 (PDT) X-Google-Smtp-Source: AGRyM1vrHJgCyDdPtNgHmBwoqpG75IrgciW6R4P30x7utWtq+80poQKuW6IDzQoVzmIZglMGkegz X-Received: by 2002:a05:6402:3481:b0:435:b12d:a66d with SMTP id v1-20020a056402348100b00435b12da66dmr12010639edc.135.1656261462764; Sun, 26 Jun 2022 09:37:42 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261462; cv=none; d=google.com; s=arc-20160816; b=COspIDdZvy4yIjK4YSVt36jJWnmrjRHkwt8os6t78LdkKlD7nIv1SJmqbIKvrpE3YE AB5M7dw2I2wkrU5lmK57SH3O5pOhGnYy9izYssyV03zFWzvwayAHuAXGWXolG1tOxpVg e6JVfk8SGi7zUlbFfJUDFyqEechrhjXoHiFASDJVqpFwSzvMT7EN622twFvQUx0UY3eT SUx/y5JPqHGxTwXiil6jMsmuRAxQ8Mao7A/kJ1D7Wm1SuLGkwN9OsiMooDqPkQxT2Owm 3QhZtAM1gAN8E7xHJm3FhkZwQAA/DJhXVqV2ocpz6GXEkJL7Nf44b7NOsTpGn8fVaZqy GtDg== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=mpkX0TwrJ49fElCIew9TJZqSp46Tv4eV5PeW2qT1RmQ=; b=MR/P5V4R73lgKCO/rFdT/H7EpK0hOfCzlk8dgB2N+T8lF8NfD6RdArIhe3aMmtW4L4 ztVVvZEQOTKiXeQ0nvmUXiM8sIuOrK+n4gh+2PMAkUzKH2B7K57kLitXGKuEw6au/hcw Skdqrg9ZjdfDAJIY9OD1aOPPgrCNRJeia5PlzO+XOTtIPlC0MhH/OquPZytQ3Bs+p6O+ xO7iu0UOxYXyc9Ap4XgXNZPusWsAIfqOzC2ObovwJkH3WzRustUKwzeDOmWFBqKWV/X5 Xl/caai+WXf/qcu+FjMe8QF4kpnLcVgDGXuk/oYhXWhbSHeg4cx3GiU7f3zigqQQiS7g d1/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=i247N3RQ; 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 x10-20020aa7d6ca000000b0043580d730easi9192101edr.431.2022.06.26.09.37.42; Sun, 26 Jun 2022 09:37:42 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=i247N3RQ; 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 B5A0C68B898; Sun, 26 Jun 2022 19:35:45 +0300 (EEST) 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 3056868B7E7 for ; Sun, 26 Jun 2022 19:35:38 +0300 (EEST) Received: by mail-pg1-f174.google.com with SMTP id z14so7003438pgh.0 for ; Sun, 26 Jun 2022 09:35:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=3F3UVWxZk/DeRiLq0w95rYZDj70tmtsyHizhSbJzU7s=; b=i247N3RQ72gNl+qJUHTn3il7AyXeG/vjKjrlsmWUug1v4B1ugRM9w8Db9mBqgErmKG A0GicFSSTAT2CDjfVR4yZBRHwZLobf7v+qCCwbs26DuCvgBIF0OONe8D47ZnYsicjhZw /uNLpEdiegQ4257kte6BNwNXFNMk2b9okjCTQYZqxV8YG9IustiDpYP/U4Hg+9Aw8nbx 3GvrkKIaLIKJbllQa6wu+uwd6mQ/n1gf6LMVfoSLGncqE18GKCjeBn7Ph/7+APOBOaZY ZMOHdRerWezpACvdtm1flK+tckDfjo5rkdjdutfoh68jLwFCkMScY2hwPnCytjgUA0FT xN1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=3F3UVWxZk/DeRiLq0w95rYZDj70tmtsyHizhSbJzU7s=; b=R3d4ztPM1L8dVX+g3SGKoFl1mEhZ1Qk2iz/Gwn+9layR9bMl5+HlA41pxWl0T+YKc7 yYchK0qYWtYLJk68MTAmuJHBxQXh5n+74J+mz9XXW+3dDLpE34EcRBqim4vMJHnD9bjQ y4wXZ2p0ZlTdts/0CLq5NC0K14yb+zwZr7wsHr/fMqOGMVkCcsbIMf3bykXD+V9nX2qc u8kuzyv1UKS75bmuIJN4jE3+kdb3RmHejwRkcADg3gvKzE+qlPv0jGfJCzp4BaY0nVUf wW2NybaYFs3q8wohvQFHVV96IijEyqp1RBVhjNfgfO8UnRmb032WL9MZcGVHUrv7lPvM uZ8Q== X-Gm-Message-State: AJIora9SKMk665Tc0tAH0kqQv6WpOzPE4f9iix7QQsw9CFUGqXtT1YMK mdsRHPNeAxzq/7k3zzCIzZaXL6R/gWJziA== X-Received: by 2002:a63:ea58:0:b0:40d:fb08:5796 with SMTP id l24-20020a63ea58000000b0040dfb085796mr681664pgk.320.1656261337034; Sun, 26 Jun 2022 09:35:37 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id n11-20020a170903404b00b0016a12571537sm5338672pla.299.2022.06.26.09.35.36 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:36 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: <9e16dbcecd41b7f641a169128d5e337744f63aea.1656261322.git.ffmpegagent@gmail.com> In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:10 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 13/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: cxneozZhhTKt 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 e525e87b3c..a77546de3d 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -26990,6 +26990,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 'overlaygraphicsubs' 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 overlaygraphicsubs) +@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 af52a77ebc..97ec9fc3b3 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -307,6 +307,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 @@ -391,6 +392,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_VAAPI_FILTER) += vf_overlay_vaapi.o framesync.o vaapi_vpp.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 5288480a55..06ac93457d 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -371,6 +371,7 @@ extern const AVFilter ff_vf_overlay_qsv; extern const AVFilter ff_vf_overlay_vaapi; 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_owdenoise; extern const AVFilter ff_vf_pad; extern const AVFilter ff_vf_pad_opencl; @@ -558,6 +559,7 @@ extern const AVFilter ff_avf_showspectrumpic; extern const AVFilter ff_avf_showvolume; extern const AVFilter ff_avf_showwaves; extern const AVFilter ff_avf_showwavespic; +extern const AVFilter ff_svf_graphicsub2video; extern const AVFilter ff_vaf_spectrumsynth; /* multimedia sources */ 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 Sun Jun 26 16:35: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: 36469 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1540413pzh; Sun, 26 Jun 2022 09:38:05 -0700 (PDT) X-Google-Smtp-Source: AGRyM1t4Xb8B6SwcPqAM1jci+vREJYwFjCu31yXc/20R4hOY0ZDTzvgi80f86hgWBxaYTyXekTr9 X-Received: by 2002:a05:6402:1907:b0:435:c243:a66e with SMTP id e7-20020a056402190700b00435c243a66emr11657931edz.44.1656261485130; Sun, 26 Jun 2022 09:38:05 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261485; cv=none; d=google.com; s=arc-20160816; b=SY+upf4MMn+6ExjJuXuW+4tsdf5C4YKM4UNdIyB1bxjn5LWlAPYsTfme9IYlFd9rUs WD62MRbM7UnU5hn8MIJDZ0imClIbiWUmL2oXtk8isbwa9jFtZqBvX+8mr1PF5irKDRn+ I+7R2I7e4QqYxCRsNnUNOQHPbNJI23O8ubYf4qUEdUuaC9lgM6F6pjAkx07sJ1ktxCLK +6bcyJwdPcRRfAZuzAemRlij7ZgZtKaDvCK4VVKXlqddBTRSMGMO0oDoq/THQwXWYEkX NPJsNZbVFBcNZofxK2U8p1zYL+gmpcN4+h7h1NSLu/+tQvvFk4meE5JsElUyBKzYxDJm EnmQ== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=Bx51BcU8Rt7/FU6XgaHSeF04I8LwgbAvT1s1xdCSchY=; b=VxQ95IgQx0gxSM/lMMjk4Ap6OV9HpcO+nEab/uiwr1vc8YPFwSBYJs+Spc9DPapBUy 1sX1f369qV1aWEp8DSICM2U8QxDldAxL2W5ch+S1nU04r76Aaz9BNE0qDizSJpshLzOF Vnpsq8LudBO/qMH+YkcWj4MD8Zig2OxBV7orToxyPQtetshBBFu2h+EbGDgf+r8JN3eP pv0oOjusbc1Tv/J/jWEqNG9mFClpQWY7OaRso74PncLjmayWP5nm3+zNRBwWEcor5s3a D5cx0+Yevb/8IrKbAqfG508U7kHVSkweKd4siJMfQzcSSuyqdbybBPM+GrFBQ4K5hlwu g9wQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=WQp5rF5w; 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 gb9-20020a170907960900b007152a3ee4easi10068225ejc.664.2022.06.26.09.38.04; Sun, 26 Jun 2022 09:38:05 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=WQp5rF5w; 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 B858C68B8AC; Sun, 26 Jun 2022 19:35:47 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pl1-f174.google.com (mail-pl1-f174.google.com [209.85.214.174]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 4986268B862 for ; Sun, 26 Jun 2022 19:35:40 +0300 (EEST) Received: by mail-pl1-f174.google.com with SMTP id m2so6213479plx.3 for ; Sun, 26 Jun 2022 09:35:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=1+KYCXVxeXWjNPNLiwGGFitMvb11HY/vJm+0e0ZkyRc=; b=WQp5rF5wSbPdF/p63jbipT1ZVY418VDO5iu2E2RGvMwqSUxLhb3u3uhKuGfPO0K4+C kon+nZlEv2iEMJbmJynCtUqqH1p3xpVg7i9TlppVNz2JpgMbbK13mZHx8NxFpvN+PCiR fm+EheiVmLtifnXKmbYWM/3NnT5vQGxyI9PX6lVhFO4RaWR5VJf23fgh/vs80OUsfzqL /rKXdWPDMKcB2tkwSmXBSfdJmczO0N1ZsJ06vYKTlcxDpta23QA+gJHviOhF+19L2hve CMMiNoIENBK/kPcFClpNxdTsXECV+eDqMF3jnjlwvVHED2nZLYMtXuAaBeaH1SZ37wxB e4Bw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=1+KYCXVxeXWjNPNLiwGGFitMvb11HY/vJm+0e0ZkyRc=; b=4awW6rDHxF1OB5eY/6a5wDHrPpUe9DXlWoC0lMhY/jhyWHVxkt+JZI2WALRi4029KO 7XoqOsu5j7PLIpUiPTEtAPvVG3eg8smtRZiGhprLPv+te4KAKczaPb+qmzygylIqI5zu mR1mr/xgxvLqIDO5NTDjYkVG/6+EPsjGxqd8Znj2jrgYkVwX23oZ26Wpjvp3qVYuLXUv BpJ/H/UA5J1vE9vkv413XtCul8d3zvNFt0CIj41IvKPenIBTIZ+mQjPlJF5LV53wfxsD Qe4aFFhCNBitXVeA8vY7GLz/qfktzH3RHUnHJHkbDNbm2n4U+prMIWAj6S3dkbEa0IBn mxpg== X-Gm-Message-State: AJIora/BgvrP95R1fhu91/zfqi/0fjHA/pyFH/1NqV049kLUcSlbOyEC HPot6jCD1FLJuuClQVkP25SopspEObDn8g== X-Received: by 2002:a17:902:cf41:b0:16b:7c56:13f4 with SMTP id e1-20020a170902cf4100b0016b7c5613f4mr1325583plg.58.1656261338251; Sun, 26 Jun 2022 09:35:38 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id g15-20020a17090a128f00b001e31c7aad6fsm5380982pja.20.2022.06.26.09.35.37 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:37 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:11 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 14/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: L1JZi1HJzgAN 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 | 2 + libavfilter/vf_overlaytextsubs.c | 680 +++++++++++++++++++++++++++++++ 5 files changed, 799 insertions(+) create mode 100644 libavfilter/vf_overlaytextsubs.c diff --git a/configure b/configure index 0580e4a536..99b4d7c8d7 100755 --- a/configure +++ b/configure @@ -3692,6 +3692,7 @@ overlay_qsv_filter_deps="libmfx" overlay_qsv_filter_select="qsvvpp" overlay_vaapi_filter_deps="vaapi VAProcPipelineCaps_blend_flags" 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" @@ -3730,6 +3731,7 @@ stereo3d_filter_deps="gpl" subtitles_filter_deps="avformat avcodec libass" super2xsai_filter_deps="gpl" pixfmts_super2xsai_test_deps="super2xsai_filter" +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 a77546de3d..b73a380515 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -27106,6 +27106,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 overlaytextsubs 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 97ec9fc3b3..7482df7a2a 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -393,6 +393,7 @@ OBJS-$(CONFIG_OVERLAY_QSV_FILTER) += vf_overlay_qsv.o framesync.o OBJS-$(CONFIG_OVERLAY_VAAPI_FILTER) += vf_overlay_vaapi.o framesync.o vaapi_vpp.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 @@ -484,6 +485,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 06ac93457d..e540ec349f 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -372,6 +372,7 @@ extern const AVFilter ff_vf_overlay_vaapi; 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; @@ -560,6 +561,7 @@ extern const AVFilter ff_avf_showvolume; extern const AVFilter ff_avf_showwaves; extern const AVFilter ff_avf_showwavespic; extern const AVFilter ff_svf_graphicsub2video; +extern const AVFilter ff_svf_textsub2video; extern const AVFilter ff_vaf_spectrumsynth; /* multimedia sources */ diff --git a/libavfilter/vf_overlaytextsubs.c b/libavfilter/vf_overlaytextsubs.c new file mode 100644 index 0000000000..1e43289bec --- /dev/null +++ b/libavfilter/vf_overlaytextsubs.c @@ -0,0 +1,680 @@ +/* + * 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 "config_components.h" + +#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; + + + int64_t time_ms = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000); + int64_t time_ms1 = (int64_t)((double)ctx->inputs[1]->current_pts * av_q2d(ctx->inputs[1]->time_base) * 1000); + + if (time_ms1 < time_ms + 1000) + ff_request_frame(ctx->inputs[1]); + + av_log(ctx, AV_LOG_DEBUG, "filter_video_frame - video: %"PRId64"ms sub: %"PRId64"ms rel %d\n", time_ms, time_ms1, (time_ms1 < time_ms)); + + 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)); + const int64_t frame_time = (int64_t)((double)frame->pts * av_q2d(inlink->time_base) * 1000); + + // Postpone header processing until we receive a frame with content + if (!s->got_header && frame->num_subtitle_areas > 0) + process_header(ctx, frame); + + av_log(ctx, AV_LOG_DEBUG, "filter_subtitle_frame dur: %"PRId64"ms frame: %"PRId64"ms sub: %"PRId64"ms repeat_sub %d\n", duration, frame_time, start_time, frame->repeat_sub); + + 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 Sun Jun 26 16:35:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 36468 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1540353pzh; Sun, 26 Jun 2022 09:37:54 -0700 (PDT) X-Google-Smtp-Source: AGRyM1u4jMcaGM5D24i+mPNZ+LU1S+lSGPYhukxQt5FCBJvc2q9RXIQCTJMixlzPgJh1bhm20oAs X-Received: by 2002:a17:907:7d8e:b0:711:cf4b:9c5 with SMTP id oz14-20020a1709077d8e00b00711cf4b09c5mr8881673ejc.637.1656261473960; Sun, 26 Jun 2022 09:37:53 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261473; cv=none; d=google.com; s=arc-20160816; b=Ont/u0rTtN2W2exULI04+MbKLyjIlDHyWh46rR+VEFghoEM1lqfOh5x/oQoHSgKlpo rBL2YyO1id5kf2eCRCHaoDA9/58jaW8pkeOsydX+wBiY3xvuppzTJeYJp20/LhHMtBsD wBFyFhXLKZq3Esw2o16j4ncQgwhplRNQ3e8wiA6djPyLqkRAerA/Oqk11stw7MU1a7TJ 1pWo5p/GjeHKb0AykGJ5ycLBGeG6EqdZReJUfzEN5wPVEGqjTD6JWDBy4iu+58H9JHSx MY7xbm0NGWQyUp/bhVcfCaK8Qk/bZStovrUlIwrNHEhhVP96oQVv1BKE9XCFeNfiUQXP TW+g== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=E7E2wRfQ4XrdZdKQUoqd7sd2LMCjUMCCwZcH0tVtkK8=; b=knhqQevkR/O6vCVUs2nKHSvjK/fTOnVj8rDXda2CZcBevuGX6fuwt4TXWJH2cIU4Oq UCQh7lbVfdcThq6M4K6P3BnU3pw4JpTlxsRNxxyGOxgaE7ovwZMszDcla/3IetJ2Aj+d CFZcyFGEkvk2nrpW4VHm5tVsf//V3S2X+q1XKnB3SlhUA9NQ58Ulu2+kuxGAmYpSSb0y FJdsQcwJcmFuB8+gpKOm2HJsJaTRU8exVRz19Y0Lc+8YZVlc1Lg941SCGyjeqV7r+k9G x0FVWDWWfKMT+oJvau+S/LxHUJyz6sEI9kZR9TZDpS3A8Bsx8qmw0XprLtobjgO6ueX/ sFmg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b="el/+gzwQ"; 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 hg7-20020a1709072cc700b007075a3f9afbsi9816495ejc.377.2022.06.26.09.37.53; Sun, 26 Jun 2022 09:37:53 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b="el/+gzwQ"; 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 C762A68B8A4; Sun, 26 Jun 2022 19:35:46 +0300 (EEST) 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 08A7368B84B for ; Sun, 26 Jun 2022 19:35:39 +0300 (EEST) Received: by mail-pg1-f172.google.com with SMTP id 23so6948428pgc.8 for ; Sun, 26 Jun 2022 09:35:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=xpyUAz8Q7vEI2BSrprkI5z1Srlkx9YCbDoT86cBgDXs=; b=el/+gzwQhiFvbuQ+ckq56fLJ1upD3RbHy25Je2obIsdaX8qrW7PcKXKOESh5ayCr1l Sg3kYxkDlu1/Jbzvgcnp3nQPExN57gvMjsRlwgjQf0ThH2bapJW6lxH/yYLf1WaHMBq2 kB063RS9TZjrGE/BkbhNypRKYwFaCtx2FTW9MsmF9wxbzqXcWse0UfxP7y9GPS9fHz26 sBjKUiRJeivcVYI/n5hq6I8R6VQTeMblSpuNPYGDyKH5MnjipHbSjMdkT/X9wlF0ZQ7l sAwr6Q16DHb3an76uPpzNIoV6CR6oZZ/Bd4hVrEOQvR35P47UTaZOwDScUCU3pSwPRp4 Ao5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=xpyUAz8Q7vEI2BSrprkI5z1Srlkx9YCbDoT86cBgDXs=; b=UhLMIuUByxiVZ7/slHLFU22LK9hkfvZu47UgyXeA7g3kH2TlH7oxLEIEdrzpYh54hf lm6Jn8XGRI0pULUFPR2vvHROsIrh2z8KH4O4UrUkFq5IDql2CuUbK/5FK5dzLNscaI2K ufX3M/a9sSrDkt/h/Bj27JVGyVBldmITrIyDsCwM7AmoKfaKqefCVeCQwByrzMFVJtE/ g1e6E6WEuFOdoiGN/qFiMlB4KeSWLYN/fgT680PQqbNdIzAspYRt75oWtx8BOuK91Uxd dTXRzxRz90xFbfICsQ7eQNkNj8Qu6gX/IiBE9hxlNHG9UdDfCLaCMFoOarmd8B+QisFY R2Yw== X-Gm-Message-State: AJIora93oE67/MN/wZBaIFzDyAhEiOgD54xRghfHk4/Hu4pnkSmuDVx5 88cVlRbUH5ltjWvpaAhTD8gE40VWZJyUpw== X-Received: by 2002:a63:458:0:b0:40d:d763:3905 with SMTP id 85-20020a630458000000b0040dd7633905mr4204646pge.349.1656261339234; Sun, 26 Jun 2022 09:35:39 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id d9-20020a170902654900b0016357fd0fd1sm5431732pln.69.2022.06.26.09.35.38 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:38 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: <6330a337b21ca736fd88c60bc211a4b916112853.1656261322.git.ffmpegagent@gmail.com> In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:12 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 15/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: Ya8qRYBbLFaF 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 | 5 + libavfilter/sf_textmod.c | 710 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 926 insertions(+) create mode 100644 libavfilter/sf_textmod.c diff --git a/doc/filters.texi b/doc/filters.texi index b73a380515..0d078e2573 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -26998,6 +26998,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. @@ -27165,6 +27304,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]overlaytextsubs" +@end example +@end itemize + @section textsub2video Converts text subtitles to video frames. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 7482df7a2a..6a68c44e1c 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -577,6 +577,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 e540ec349f..2228e414db 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -569,6 +569,11 @@ extern const AVFilter ff_avsrc_avsynctest; extern const AVFilter ff_avsrc_amovie; extern const AVFilter ff_avsrc_movie; +/* subtitle filters */ +extern const AVFilter ff_sf_censor; +extern const AVFilter ff_sf_showspeaker; +extern const AVFilter ff_sf_textmod; + /* those filters are part of public or internal API, * they are formatted to not be found by the grep * as they are manually added again (due to their 'names' diff --git a/libavfilter/sf_textmod.c b/libavfilter/sf_textmod.c new file mode 100644 index 0000000000..9651e07c65 --- /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_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, dialog->effect, 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_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, dialog->effect, 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 Sun Jun 26 16:35: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: 36470 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1540457pzh; Sun, 26 Jun 2022 09:38:16 -0700 (PDT) X-Google-Smtp-Source: AGRyM1sxPEQuQmUGWd4gn6M7Bb7bhkxmw1bxtXDg/IWbbvFq3dya+1W/gBNICEVEZhnzyvJ9cQ2n X-Received: by 2002:a05:6402:27d1:b0:436:dd8:a6d5 with SMTP id c17-20020a05640227d100b004360dd8a6d5mr12072132ede.342.1656261496101; Sun, 26 Jun 2022 09:38:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261496; cv=none; d=google.com; s=arc-20160816; b=wH0721qLdzvH3v7eaHEX4SGzdawLTsh+Nh6nAPUDbPaMUBx8p+zl70/r8LqfuQB5+A Bik5w7Ssr66vsyFbATotKtDQ51c/OU66yj1bDGEFiiiPT3Pu5coswEWYvDyp/BfPiKER TZThO6GLwTYJe2x8InM40lgqvdy1oeMuZAvGapVKqwHY7uX3bkBcezNZ3E/Us68rfy+8 2Bzey28fzgPs2p8L/SolER9SPx0gZM15uBHbWMyyl+uUBXZlxbnQGjM9of0WesttigGp ppaDzlgAo2f0NpYHM107HmmJ41qzXoXotIEVXlS9EZ4Yu3GSLx0e8KHV9nT4kraht8BZ 6H9w== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=NSHFwqjXMMDrXuV5vJA9gn/H8YlQwVUkEFLZ5ydpMRo=; b=rH3wkPXnc/yz2CNbSflrIDc8Q1ZBPhzkwS3NVFYWor9hBRFRjKpszwlTNFLUWpmnhG O3Fblho0ecOQf3x8JO7c9PeafPKWZg0YlFsfQEIq9X77XgSQ8S7msRtzsP2DdLm/NZnv 1Sb7UHDUZStkIsuMotH5+ivseBlqZeCaSYUid0Z5d/g+ulUYVM1SW0L4dZq8NF/MtUUk YQW9o8Ox5J5ZbQVEBrM49PGFUWds+juTes0jTke1L6u7fV0t6sWzteUhlH3F6sCl8Vy8 kS0G9syxUWcq/J3VaiYQ1eYlfr7UmAMAVh83gID/qPwkPn0aCcF+auWh0DfkmaCBBQgb GxBg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=V6N1anlI; 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 e21-20020a17090658d500b006fe9e0df24dsi9622309ejs.876.2022.06.26.09.38.15; Sun, 26 Jun 2022 09:38:16 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=V6N1anlI; 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 9669B68B7B0; Sun, 26 Jun 2022 19:35:48 +0300 (EEST) 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 30D3C68B852 for ; Sun, 26 Jun 2022 19:35:41 +0300 (EEST) Received: by mail-pg1-f169.google.com with SMTP id 9so6949314pgd.7 for ; Sun, 26 Jun 2022 09:35:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=KcS7Q3khFQD66DqvSVlywm1B3KDmp/bJBqbANkefSNw=; b=V6N1anlIHyWxTKFCVMr7hqd4hzLs98XDxN75U/H9YImmJr23dOQ6EQUSyqF64p7Kr8 19U42J9RYi1LOoNICLg+kxFd58QHP2++6IzIsHLcJPK/9CIn3ofaYx7oEC5nfQqQH+dV j5wW9sBuhOWWpPJm7oVQf4yZ8rFoj07wdmYx9NYltyL6UN+5mtIfDv512uFI2rydq8Bb c7RWU0Ylfb4X/WnqdlfmsqZJGQ3bx3Zp6iBGoI0NO0/+eB4+Iqo7+qpt6Sm9nSdQCeH0 iqF7rKePLI+W/PjMcRFC+9Q08MjRtJfUFbsMlDxNqzAG6fcatX0K/6zjnoPRcFw3uHWv byZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=KcS7Q3khFQD66DqvSVlywm1B3KDmp/bJBqbANkefSNw=; b=mDdK6b3K/hhLNIFc1+LCesFlhdB/kI3g3dpIwhY3BteuKscTLSicM9Gxk1HqFoMG7R RWgknIwh+Tp1pPWdvmPOdY8VruOMOQg6V1Dz1DKQ5bkQFCHiNVgxmSZR3lD2oov6M9t1 qQaVS6ca5SunEw/bseDmJ1a/Nl6gxEr9oyqhHKeph8NeS+cWW+CxAsdlNBfuiULlO2Rb oXxYQskPGjmO8CUHjvfAOW4czkTZv4ecHIGReq9KPuvBLmd1iDdA6E9k7qmgGIMzUcIb u2AVdNmn7EvGbqwxUimZXwhOm0HvCjiCtsRIh3Okkl/OE1tN7QWli95CsLLiRdb9QLNe ySqQ== X-Gm-Message-State: AJIora8zo90Qc3uT22xMP3eAAxZsfX/TPLsNvysPMSU2RLhp5moeVafX CcGXgm7ZI1UdnHGxOQjc1Myztg21Jk+puQ== X-Received: by 2002:a63:1209:0:b0:3fc:e453:5424 with SMTP id h9-20020a631209000000b003fce4535424mr8922350pgl.131.1656261340209; Sun, 26 Jun 2022 09:35:40 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id b205-20020a621bd6000000b0051bb1785286sm5400389pfb.167.2022.06.26.09.35.39 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:39 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: <732e2fbf7dcb3005b011de78a4b94fbfeee7bd6e.1656261322.git.ffmpegagent@gmail.com> In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:13 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 16/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: WV9mNqpizpde 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 | 237 +++++++++++++++++++++++++++++++++++ 4 files changed, 276 insertions(+) create mode 100644 libavfilter/sf_stripstyles.c diff --git a/doc/filters.texi b/doc/filters.texi index 0d078e2573..1158df64d2 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -27058,6 +27058,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 6a68c44e1c..a99a0f6583 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -580,6 +580,7 @@ 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_STRIPSTYLES_FILTER) += sf_stripstyles.o OBJS-$(CONFIG_TEXTMOD_FILTER) += sf_textmod.o # multimedia filters diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 2228e414db..7a958b51d4 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -572,6 +572,7 @@ extern const AVFilter ff_avsrc_movie; /* subtitle filters */ extern const AVFilter ff_sf_censor; extern const AVFilter ff_sf_showspeaker; +extern const AVFilter ff_sf_stripstyles; extern const AVFilter ff_sf_textmod; /* those filters are part of public or internal API, diff --git a/libavfilter/sf_stripstyles.c b/libavfilter/sf_stripstyles.c new file mode 100644 index 0000000000..78dc6f3ef4 --- /dev/null +++ b/libavfilter/sf_stripstyles.c @@ -0,0 +1,237 @@ +/* + * 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_internal.h" +#include "libavutil/ass_split_internal.h" +#include "libavutil/bprint.h" + +typedef struct StripStylesContext { + const AVClass *class; + enum AVSubtitleType format; + int remove_animated; + enum ASSSplitComponents keep_flags; + int select_layer; +} StripStylesContext; + +typedef struct DialogContext { + StripStylesContext* ss_ctx; + AVBPrint buffer; + int drawing_scale; + int is_animated; + int plain_text_length; +} 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)) + s->plain_text_length += len; + ////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) + s->plain_text_length += 2; + ////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 *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) { + avpriv_ass_free_dialog(&dialog); + return NULL; + } + + dlg_ctx.ss_ctx = s; + + av_bprint_init(&dlg_ctx.buffer, 512, AV_BPRINT_SIZE_UNLIMITED); + + avpriv_ass_filter_override_codes(&dialog_callbacks, &dlg_ctx, dialog->text, &dlg_ctx.buffer, s->keep_flags); + + if (av_bprint_is_complete(&dlg_ctx.buffer) && dlg_ctx.buffer.len > 0 && dlg_ctx.plain_text_length > 0) + result = avpriv_ass_get_dialog_ex(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->margin_l, dialog->margin_r, dialog->margin_v, 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_DEBUG, "original: %d %s\n", i, tmp); + av_log(inlink->dst, AV_LOG_DEBUG, "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[] = { + { "keep_flags", "flags to control which override codes to keep", OFFSET(keep_flags), AV_OPT_TYPE_FLAGS, { .i64 = ASS_SPLIT_TEXT }, .flags = FLAGS, .unit = "keepflags" }, + { "basic", "keep static style tags only", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_BASIC }, .flags=FLAGS, .unit = "keepflags" }, + { "all_known", "keep all known tags", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ALL_KNOWN }, .flags=FLAGS, .unit = "keepflags" }, + { "text", "keep text content", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT }, .flags=FLAGS, .unit = "keepflags" }, + { "color", "keep color tags (\\c, \\c)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_COLOR }, .flags=FLAGS, .unit = "keepflags" }, + { "alpha", "keep color alpha tags (\\alpha, \\a)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ALPHA }, .flags=FLAGS, .unit = "keepflags" }, + { "font_name", "keep font name tags (\\fn)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_NAME }, .flags=FLAGS, .unit = "keepflags" }, + { "font_size", "keep font size tags (\\fs)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_SIZE }, .flags=FLAGS, .unit = "keepflags" }, + { "font_scale", "keep font scale tags (\\fscx, \\fscy)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_SCALE }, .flags=FLAGS, .unit = "keepflags" }, + { "font_spacing", "keep font spacing tags (\\fsp)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_SPACING }, .flags=FLAGS, .unit = "keepflags" }, + { "font_charset", "keep font charset tags (\\fe)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_CHARSET }, .flags=FLAGS, .unit = "keepflags" }, + { "font_bold", "keep font bold tags (\\b)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_BOLD }, .flags=FLAGS, .unit = "keepflags" }, + { "font_italic", "keep font italic tags (\\i)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_ITALIC }, .flags=FLAGS, .unit = "keepflags" }, + { "font_underline", "keep font underline tags (\\u)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_UNDERLINE }, .flags=FLAGS, .unit = "keepflags" }, + { "font_strikeout", "keep font strikeout tags (\\s)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FONT_STRIKEOUT }, .flags=FLAGS, .unit = "keepflags" }, + { "text_border", "keep text border tags (\\bord)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_BORDER }, .flags=FLAGS, .unit = "keepflags" }, + { "text_shadow", "keep text shadow tags (\\shad)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_SHADOW }, .flags=FLAGS, .unit = "keepflags" }, + { "text_rotate", "keep text rotate tags (\\fr)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_ROTATE }, .flags=FLAGS, .unit = "keepflags" }, + { "text_blur", "keep text blur tags (\\blur, \\be)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_BLUR }, .flags=FLAGS, .unit = "keepflags" }, + { "text_wrap", "keep text wrap tags (\\q)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_WRAP }, .flags=FLAGS, .unit = "keepflags" }, + { "text_align", "keep text align tags (\\a, \\an)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_TEXT_ALIGNMENT }, .flags=FLAGS, .unit = "keepflags" }, + { "reset_override", "keep override reset tags (\\r)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_CANCELLING }, .flags=FLAGS, .unit = "keepflags" }, + { "move", "keep move tags (\\move)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_MOVE }, .flags=FLAGS, .unit = "keepflags" }, + { "pos", "keep position tags (\\pos)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_POS }, .flags=FLAGS, .unit = "keepflags" }, + { "origin", "keep origin tags (\\org)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ORIGIN }, .flags=FLAGS, .unit = "keepflags" }, + { "draw", "keep drawing tags (\\p)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_DRAW }, .flags=FLAGS, .unit = "keepflags" }, + { "animate", "keep animation tags (\\t)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_ANIMATE }, .flags=FLAGS, .unit = "keepflags" }, + { "fade", "keep fade tags (\\fad, \\fade)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_FADE }, .flags=FLAGS, .unit = "keepflags" }, + { "clip", "keep clip tags (\\clip)", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_CLIP }, .flags=FLAGS, .unit = "keepflags" }, + { "unknown", "keep unknown tags", .type = AV_OPT_TYPE_CONST, { .i64 = ASS_SPLIT_UNKNOWN }, .flags=FLAGS, .unit = "keepflags" }, + { "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(select_layer), 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 Sun Jun 26 16:35: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: 36471 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1540517pzh; Sun, 26 Jun 2022 09:38:27 -0700 (PDT) X-Google-Smtp-Source: AGRyM1v1ZCh1uR91TnPdLieLMiMGEE5elj7rnEPxLsQ92rEMWaEn44rK8r2qaeKGFxkBjgjuOL7L X-Received: by 2002:a05:6402:3329:b0:435:8136:cb70 with SMTP id e41-20020a056402332900b004358136cb70mr11915681eda.106.1656261507303; Sun, 26 Jun 2022 09:38:27 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261507; cv=none; d=google.com; s=arc-20160816; b=auPc/0x52Fm03nzJtwxUeIqz3NqmpkyYoBSUiYt1/M+5RbaThFJ5s97FMCTGtyQAQw LWYl1Wm22/HXcw5kG0voCX5lxKjvdWQv3UXvowqy0oMHr5nqYYIAI8pTQhPQsmM+z6ai Boy+tj6bkYFAz2atAXWyLIr5gyPYEzuF4EDYLXDGd+ZAm+wFSXXzefjML5csYOURXVA6 6mBtxB2UqEQ74E23F+c58XZTglYi2pDzeikKlEW2bIT1IwfJ+8hmZmDOYnOpbDUSn0Om O0z63vKEBVsIrQmAiHnqh6rHv6zoO02mf8FQWKsTrEk5K4TmweYeQqIHwqK8wHtwvJzz 26Xw== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=zS5uPRozOCevnxnJ7nNehMYr5nk4qjSNNfod0zAXF78=; b=z5+Q5lw5bHiDuWN74212MhDB6LLFwhBXeyBAK5CTHboQyqeP+PiUJLpTE8p96mT4Un u0PrLBb7XNSAn9uKDxlpu04ASwsyRSSQdCdNr2UVWib9YbEHj1adDR/W9UTdFVYdTmVE OLrDImqi4P2tfTcfh2ZKss2Eq92WcOKZD7RsulzmyAdImOo4QPu3Mwj37rP1KbVQhHf0 mQ1bGIyWUAb8gmnTB/s32vQQaAsqlJWIE8lg//rO29gJyMkRMr0Bl5FDBEsqpfKkqcsO iIM3ioUVVdyc5S8OKzMDtYc6/vz7tdA+2aK+5DrXbEBJqYZ2V2Mn7nBt+F6vb5yRa3ur AYyQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=W3TzFGp5; 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 c39-20020a509faa000000b0043565c9afb3si10463926edf.93.2022.06.26.09.38.26; Sun, 26 Jun 2022 09:38:27 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=W3TzFGp5; 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 72D9A68B7C8; Sun, 26 Jun 2022 19:35:49 +0300 (EEST) 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 E4A8768B874 for ; Sun, 26 Jun 2022 19:35:42 +0300 (EEST) Received: by mail-pg1-f174.google.com with SMTP id r66so6970623pgr.2 for ; Sun, 26 Jun 2022 09:35:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=rkmz4twfn0Yw2fNrL5KS4peTQ7WiXLSM740WNhMrZbI=; b=W3TzFGp5nxaG9n9u/8+4yLbDesJ/0gpGcFUVg95CpXfeeeR/pG+XwkzXyIjT0haJB0 EGfyE5gYbOPjFoTOQobu0xc3wwh3/nXQp+v6xapcX2N/yrJCOvTizrSMPVLbvL1QdlcV g/6HC8w4ltYu/+d/ekW4jbLXEERCe2YuHXkbC/EvAevXmpbEyHm+U4Re5sx4NtKLJKLA A3F7z0NrLb0ZnIs9ARU3ysghKKIQwsTbsbBxFrOCUSNQgQhycRnU33FOoTPBznJCI45t cqzRtr/a+atZcIEBBzixoKWA/VRHFxGbrVCoOLfJI/bO0qj43W+NxtQURfrxVmi8vxYT 6BKg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=rkmz4twfn0Yw2fNrL5KS4peTQ7WiXLSM740WNhMrZbI=; b=KmZjMCu/ZD7ebk9bFYeU5KlTsz2VQV0T2xEuQJmp7ewn0YdfzDL8CQWQq+qnoDVJrQ 4jusFAcB+iUjlmzjnNjXNFgoL94fsP/lJqMKX8hAjbu55y0/v4Y2xQMLNWAniEY2P28W +IkJFfgM1ffYm/6+JDODGnqIce5M82kDhZqcBzcNu8MFgzMb/hM68JLLrzDnFC5Jlb0y /XtTn9LoNc6P33BE9APBxQz+lh19FGHfXnSomEHIUQ10W3xGdmSGIpRXcp3uRamz4fvX LJl+Ykeckl4WWdYVUiXg8zMU2db1afxuDy2LF/4deawq17eBY6W1khYwYldwjcOxpEuG ze9A== X-Gm-Message-State: AJIora+tfT6ATItgYAQTgACCi4dXK4DXRtTqpez2HHWkB9c5JHxKBw/l q9CXIZcyT+6S9akXDX+RW5OlrQ+rCvQWPw== X-Received: by 2002:aa7:814c:0:b0:51b:b3ee:6be2 with SMTP id d12-20020aa7814c000000b0051bb3ee6be2mr10379774pfn.3.1656261341193; Sun, 26 Jun 2022 09:35:41 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id u13-20020a63454d000000b0040d2224ae04sm5345268pgk.76.2022.06.26.09.35.40 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:40 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: <4df0d1213028716d30be0131de2a5421334eeb54.1656261322.git.ffmpegagent@gmail.com> In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:14 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 17/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: UnoPAkG+L5RZ 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 | 395 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 461 insertions(+) create mode 100644 libavfilter/sf_splitcc.c diff --git a/configure b/configure index 99b4d7c8d7..bde13f3e09 100755 --- a/configure +++ b/configure @@ -3728,6 +3728,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 1158df64d2..b4bbbace7f 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -27408,6 +27408,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 a99a0f6583..958da451ea 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -580,6 +580,7 @@ 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_SPLITCC_FILTER) += sf_splitcc.o OBJS-$(CONFIG_STRIPSTYLES_FILTER) += sf_stripstyles.o OBJS-$(CONFIG_TEXTMOD_FILTER) += sf_textmod.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 7a958b51d4..3aa1e5ebc0 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -572,6 +572,7 @@ extern const AVFilter ff_avsrc_movie; /* subtitle filters */ 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; diff --git a/libavfilter/sf_splitcc.c b/libavfilter/sf_splitcc.c new file mode 100644 index 0000000000..14235e822c --- /dev/null +++ b/libavfilter/sf_splitcc.c @@ -0,0 +1,395 @@ +/* + * 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; + int had_keyframe; + 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_NONE }; + 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 && (s->had_keyframe || frame->key_frame)) { + int got_output = 0; + + s->had_keyframe = 1; + pkt = av_packet_alloc(); + pkt->buf = av_buffer_ref(sd->buf); + if (!pkt->buf) { + ret = AVERROR(ENOMEM); + goto fail; + } + + pkt->data = pkt->buf->data; + pkt->size = pkt->buf->size; + pkt->pts = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q); + + 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 (got_output) { + //// av_log(inlink->dst, AV_LOG_INFO, "CC Packet PTS: %"PRId64" got_output: %d out_frame_pts: %"PRId64" out_sub_pts: %"PRId64"\n", pkt->pts, got_output, sub_out->pts, sub_out->subtitle_pts); + ////} + + av_packet_free(&pkt); + + if (ret < 0) { + av_log(inlink->dst, AV_LOG_ERROR, "Decode error: %d \n", ret); + 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 Sun Jun 26 16:35: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: 36472 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1540583pzh; Sun, 26 Jun 2022 09:38:37 -0700 (PDT) X-Google-Smtp-Source: AGRyM1tFJDMmmuAm+LwIsRqKvAudBoxteDQ/wzNRDtOzn3DgvNZAFsUVl5b60+4lbEUMLrE9Q9Fi X-Received: by 2002:a05:6402:13cc:b0:435:557e:6325 with SMTP id a12-20020a05640213cc00b00435557e6325mr11482166edx.83.1656261517394; Sun, 26 Jun 2022 09:38:37 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261517; cv=none; d=google.com; s=arc-20160816; b=NPr5DXfAH/hRgXjyS1SMJ7FVMwwBl7xvaoLm9sNV8xtBhgMucVZcUqlKcbmKaFsqvI wepjaFymEjLz8LGZwjER0bUa2AUG2qirz1TzICK27C3VsCpH9MnVFI0hc2endLSMjF1G 0FGZiaaiA2deVVz+pN/L7Cf61L12b3Ll1T6RiyAzsjIvQMN1sFbYVN8vVPUAEIM0WDNw KN9LTlRQiNiYL9uePnFvNDVvyfw1F/Ng2aaxDAj2F3ytOWaGFE2Bpd2ksjqR9gWBqPyG 3m0QOk+2uti1n0DLPGk/de4G+PtBHzDcooCg/fR43U046qYaeskSjDQSajO6QwhICr7p AH5A== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=TqY5V5Xnz2wRpxVaJ8tRVc3PJDHQtVciAVK3niTiZFo=; b=MkxhLoymgU9HEFdYVwVTefp18AY0A9kYVoeNOw4nzkMPKjxymVZEl/1HVgBWGZMzdE sasrKkTr9bOBpuUIWbIQTcurLZhxYgh23BhT4n2aMYFZvYvXToBR154gs60FJ2EZbcEQ KyC0UvMZG9gl809QbzP9cHebXvRcIh6pag45ybPy24xrgghFj3/7X5pcp8ivSYKy3+34 TfGMEnY23cYoT/jNQmF1+I3fXMj8bzipjK+i41lMGrvRBivLPfipwmwDcsR5roveE7sS E2zfydtEf1MohyI809zSvCqcE+wdy0HuFMqaI2gzXoIQI2K6EZ1vDcZRQVcEst3UEISE +YKQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=gSaH9cbM; 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 j2-20020a170906474200b00722dd2e9f39si8726133ejs.629.2022.06.26.09.38.36; Sun, 26 Jun 2022 09:38:37 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=gSaH9cbM; 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 5FB2568B892; Sun, 26 Jun 2022 19:35:50 +0300 (EEST) 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 2741668B874 for ; Sun, 26 Jun 2022 19:35:43 +0300 (EEST) Received: by mail-pf1-f181.google.com with SMTP id i64so6911795pfc.8 for ; Sun, 26 Jun 2022 09:35:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=e2q/G5wazDNWEZ9npE8egHv7ZQvjH2ovxz10OeugogM=; b=gSaH9cbMsW0Vsuvtlk/5sC5TRc8g53GAE3KjzSNWOVuALmcSEVd0Sla7Ay3c9qdAlO r7g1VIlxoUAp6+OBm8g+gv7RyzhA0CUpPx7ewXH6GSmWcRNmhiOakCcwhbtTfpd+RiDy 4n0FSic3vYoFC+khlNoGq30CmlELbkSbh9LQJagL3zf6Y8JuWA+9auwO5q53tJmTx1pj IzGrERQA3e+Dkf7I2aK+mY9V6o/sbpPfYSGDMNHmIMaW+kT+gi7xpnmOp7CvnWdnufjh 9QSo33c7tnrfMXgd8i4+FiBZZY6NkF4lgRLmX6Vxi2efptGL9jozsfH5ZUZdoiw6EgPk w1fg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=e2q/G5wazDNWEZ9npE8egHv7ZQvjH2ovxz10OeugogM=; b=xfFdRpNmEcMyDQukhQKBundo/huvriRT3XvhhadAzowi7QyRAU9BkOfmV8ZpnPCs8d ooo0W06Xql/xQqLmDfFXVDCpjoR8hh2EQzQztKE1qXyAfVOZFibR56Shz4NDeE3Cycoo rAxwf3Et+XMRmL66jZR2YCsFJs/dfLM649AXEI2X05rBsyhUWL8VJCYAqwBWEDbH3fmE 5fZXsgw9SDSd6GrjmV/+SoaXssLlGYSS9qs+5IclPmqWXMTIMFY7ZPC3IZkKcwQF5r3V 5AgIU8FsdrYhIobc1fjn03O/MFY9IdKW2krI+Tlt8wJIQyaDAT7IfabmkRaGjWfxqudl 3sgQ== X-Gm-Message-State: AJIora+3DlRs5OMmdr8j4PCnAqgBtPPz+hcOU53UBHVa1mjyjv/Trrsp Qqla5UCSQma2cQt81d2IuNDmGi7n8tV0Kw== X-Received: by 2002:a65:6d89:0:b0:3fa:5523:440e with SMTP id bc9-20020a656d89000000b003fa5523440emr8848971pgb.318.1656261342237; Sun, 26 Jun 2022 09:35:42 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id i14-20020a62870e000000b005251ce498cfsm5384735pfe.191.2022.06.26.09.35.41 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:41 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: <27bf505078f8fc4472bad35e769cbcf932d3a6bb.1656261322.git.ffmpegagent@gmail.com> In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:15 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 18/25] 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: v6Kq4qgFSHyq From: softworkz Signed-off-by: softworkz --- configure | 1 + doc/filters.texi | 55 ++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/sf_graphicsub2text.c | 1137 ++++++++++++++++++++++++++++++ 5 files changed, 1195 insertions(+) create mode 100644 libavfilter/sf_graphicsub2text.c diff --git a/configure b/configure index bde13f3e09..462d473c5f 100755 --- a/configure +++ b/configure @@ -3663,6 +3663,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 b4bbbace7f..4bbec62c02 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -27174,6 +27174,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 -loglevel verbose -i "https://streams.videolan.org/streams/ts/video_subs_ttxt%2Bdvbsub.ts" -filter_complex "[0:13]graphicsub2text=delay_when_no_duration=1" -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 958da451ea..6e6485c99a 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -307,6 +307,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_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 diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 3aa1e5ebc0..cbd93c4236 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -571,6 +571,7 @@ extern const AVFilter ff_avsrc_movie; /* subtitle filters */ 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..47c7030939 --- /dev/null +++ b/libavfilter/sf_graphicsub2text.c @@ -0,0 +1,1137 @@ +/* + * 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; + uint8_t current_index = 255; + + for (int x = left; x < right; x++, p++, porig++) { + + if (*p == current_index) { + if (*p == bg_color_index) + bg_score[*porig]++; + if (*p == text_color_index) + text_score[*porig]++; + if (*p == outline_color_index) + outline_score[*porig]++; + } + + current_index = *p; + } + } + + 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 > 0 && pointsize != cur_pointsize && s->recognize & RFLAGS_FONTSIZE) { + float change_factor = (float)(FFABS(pointsize - cur_pointsize)) / FFMAX(pointsize, cur_pointsize); + + // Avoid small changes due to recognition variance + if (change_factor > 0.12f) { + 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->repeat_sub) { + // 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, NULL, 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->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 Sun Jun 26 16:35: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: 36473 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp1540986pzh; Sun, 26 Jun 2022 09:39:50 -0700 (PDT) X-Google-Smtp-Source: AGRyM1uK6e7yfrjIYE954Fee23dgjM3NZ+mM5xdRVjFyRqrUU35xEgPDEr4W7lGKzBrhwcb1knaG X-Received: by 2002:a17:907:608b:b0:722:fb4c:5675 with SMTP id ht11-20020a170907608b00b00722fb4c5675mr9092539ejc.273.1656261590285; Sun, 26 Jun 2022 09:39:50 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656261590; cv=none; d=google.com; s=arc-20160816; b=aTQI1n+VP5lzMCMd8wVRMWDj6EB0W7sXNnBkBy8vlknW03vUU8+gvW8effVZHbdhNA PPBa7UV80wkHkUJ5TXqG/sFX2v0egzoaQMehoISXinPv71yFNkZJyBPwRVCHsYrVrfuQ Gfbz8wnGwbV8qXFtOCLQVU+pYZBZkdxOzIgxrdNPzBsL5bnguH1eEcpTOOtxOL8cxd7+ 1K8xowUJRhrLvkQWahR++DIpP7tlxXegRz/X38sBK1jfMoMaa9dhtV0VjBPwlGMZy44Z I6OarWU6wURld5lOfVFVt8Dg8XLYrENn/9XQLIN1d9jFYyDgi4Gip8paPB+sfJaf82G9 xsQA== 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:references :in-reply-to:message-id:from:dkim-signature:delivered-to; bh=+i1a7ZRd3DRdk67lx88LWuUwdmpgefQm/eoFvZMseIc=; b=ESKYxSLLYY23H2uqoZAeMdgnC5tK/+im94XqzqdqoWnFKvT9QIbbliTjSGfY8zBIHh vTGG1/gN4g4uCaNPmguJ6Qvb6LoAFoQt0BdhLbgXFwlUdQl+4ecZ1c6W+jnaUA0RZW2W Y1kcAoOxniGVS27RU0pl5IWo5oPeQS6z5fKIsJtOSS2dG/9pi4aGne8AUF0kARtn6tBn XZm5SshctYj0Hnti5IazV5QGLQX+WVmkVWvqtPC2vmcmIg8gBEu6CQA6nOJQbSp3Cddz 1vqmmtzN2FFA9jc5UIwlDjsOI5qGPX7HOANX1Up/M3DtHaF7ZZqye47rtlrTyvQD6VPS 3wiQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=jTnS0dim; 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 dm21-20020a170907949500b00722f8d20e73si4777732ejc.599.2022.06.26.09.39.49; Sun, 26 Jun 2022 09:39:50 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=jTnS0dim; 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 95E5B68B8E3; Sun, 26 Jun 2022 19:35:57 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f175.google.com (mail-pf1-f175.google.com [209.85.210.175]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 9F91268B8C4 for ; Sun, 26 Jun 2022 19:35:51 +0300 (EEST) Received: by mail-pf1-f175.google.com with SMTP id p14so6916546pfh.6 for ; Sun, 26 Jun 2022 09:35:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:message-id:in-reply-to:references:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=gVHv6ddRRQ9azO4fDcWa4q6rlwjTDh/Rci7CZZmYls4=; b=jTnS0dimWxsmJOF9lGcpVLnndlL35ySu9mAWCqe4JCG4RdpCkoTgxwmusma9P7a0xS jD6ekeImKtJcnxQAU14n25+q1GaTC2bl4reuC2o1IMGXeDg5xhYlxYUGy/rU0/muPKdt N3kB9I7nl8vY5tJHcXUgsXwffGL5GKz0GaoLmzQu2gohI5U3//PkpxsK8RUMzp1dbhxO XCCSiGwmwepOCR4OV9iGq66WO/6zmtE+IVNC2RVCY+HDXJ/WvXUQmJQzN8KjNce0gzWV 4q0BYW2Y6IuC5t7FkpGD6oEudHz/6D8/QRk2lWYyfCF9TuL3JjxuLU+BXoIiBnmI2/q9 75RQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:message-id:in-reply-to:references:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=gVHv6ddRRQ9azO4fDcWa4q6rlwjTDh/Rci7CZZmYls4=; b=jFWnA+27C3kTth6vCWACwNW4NWsULWBYXnImEXpDuKVwwHXeNbtizCen+W++fOxvBT Sw5Z89vRXVaakIHsTOKujIb1K1vVou9fs4JbPZ41ckhiewojFMV+knFbfOOpU9kS9PMo 6u7K7G18qF6JslkfaMwjUrcfmn27MO0cEfa4E2ift8jgZGnfBiIiJz36U29Y/yaDf2EN wEH43otCIcPJ8FGbN+QU6daPrdnynU79gnsG/JOs7SwR31aZ2dEdTQu1/ims8kpqHek/ FaoEcjXy+4rGJEI9FPDCxQhTmQ6sLlYuGTMFmBNbh1VJJ8ciiEXykAyNmfLd+JLHoKw3 o3YA== X-Gm-Message-State: AJIora/uBVqx6DbnthtqbipX+gUjpO1KvVF5WNJuniE9bQmLVkBk0nkd OZkY1UVAawlC8YnW0x01j+hY0UkuRKNgtw== X-Received: by 2002:a63:fc63:0:b0:405:34ac:920d with SMTP id r35-20020a63fc63000000b0040534ac920dmr8519254pgk.40.1656261348885; Sun, 26 Jun 2022 09:35:48 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id n4-20020a6546c4000000b0040c9df2b060sm5392117pgr.30.2022.06.26.09.35.47 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jun 2022 09:35:47 -0700 (PDT) From: softworkz X-Google-Original-From: softworkz Message-Id: <1e2fc0d09f8d195c87e6c0f075c12ee0d49c78ab.1656261322.git.ffmpegagent@gmail.com> In-Reply-To: References: Date: Sun, 26 Jun 2022 16:35:21 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v6 24/25] fftools/ffmpeg: Introduce subtitle filtering and 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: Michael Niedermayer , softworkz , Andriy Gelman , Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: klgAhE2DGOvw 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 previous results were incorrect. The command line for this test specifies -r 5 (5 frames per second), which is now fulfilled by the additional frames in the reference output. Example: The first subtitle time is 499000, the second is 15355000, which means 0.5s and 15.35s with a difference of 14.85s. 15s * 5fps = 75 frames and that's now the exact number of video frames between these two subtitle events. - 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 Subtitle frames are emitted to the filter graphs at a 5 fps rate by default. The time limit for this test is 15s * 5fps = 75 frames which matches the count in the new ref. - 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 - sub-scc The first entry is no longer in the output because it is before the actual start time and the strim filter removes such entries now (like for video and audio) Signed-off-by: softworkz --- fftools/ffmpeg.c | 613 +++++----- fftools/ffmpeg.h | 17 +- fftools/ffmpeg_filter.c | 270 +++-- fftools/ffmpeg_hw.c | 2 +- fftools/ffmpeg_opt.c | 28 +- tests/ref/fate/filter-overlay-dvdsub-2397 | 182 +-- tests/ref/fate/sub-dvb | 162 +-- tests/ref/fate/sub-scc | 1 - tests/ref/fate/sub2video | 1091 +++++++++++++++++- tests/ref/fate/sub2video_basic | 1238 +++++++++++++++++++-- tests/ref/fate/sub2video_time_limited | 78 +- 11 files changed, 3010 insertions(+), 672 deletions(-) diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index 09caa3e3c4..40d7868368 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -142,8 +142,6 @@ 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; @@ -168,163 +166,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 @@ -525,7 +366,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; if (ifilter->frame_queue) { AVFrame *frame; @@ -534,12 +374,6 @@ static void ffmpeg_cleanup(int ret) av_fifo_freep2(&ifilter->frame_queue); } av_freep(&ifilter->displaymatrix); - if (ist->sub2video.sub_queue) { - AVSubtitle sub; - while (av_fifo_read(ist->sub2video.sub_queue, &sub, 1) >= 0) - avsubtitle_free(&sub); - av_fifo_freep2(&ist->sub2video.sub_queue); - } av_buffer_unref(&ifilter->hw_frames_ctx); av_freep(&ifilter->name); av_freep(&fg->inputs[j]); @@ -560,7 +394,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++) @@ -613,15 +447,19 @@ static void ffmpeg_cleanup(int ret) for (i = 0; i < nb_input_streams; i++) { InputStream *ist = input_streams[i]; + if (ist->prev_sub.subtitle == ist->decoded_frame) + // Prevent double-free + ist->prev_sub.subtitle = NULL; + 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]); @@ -991,33 +829,83 @@ static void do_audio_out(OutputFile *of, OutputStream *ost, 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) +{ + 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 subtitle_out_max_size = 1024 * 1024; - int subtitle_out_size, nb, i; + 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; } + ////if (ost->last_subtitle_pts && ost->last_subtitle_pts == frame->subtitle_timing.start_pts) { + //// av_log(NULL, AV_LOG_DEBUG, "Ignoring subtitle frame with duplicate subtitle_pts\n"); + //// return; + ////} + + ost->last_subtitle_pts = frame->subtitle_timing.start_pts; + + 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 ? */ @@ -1027,50 +915,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; } } @@ -1375,8 +1251,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); } @@ -1928,7 +1822,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; @@ -1955,6 +1850,18 @@ static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame, int keep_ref case AVMEDIA_TYPE_VIDEO: need_reinit |= ifilter->width != frame->width || ifilter->height != frame->height; + + if (need_reinit) + need_reinit = 1; + break; + case AVMEDIA_TYPE_SUBTITLE: + need_reinit |= ifilter->width != frame->width || + ifilter->height != frame->height; + + need_reinit &= (ifilter->width == 0 || ifilter->height == 0); + + if (need_reinit) + need_reinit = 1; break; } @@ -2026,12 +1933,9 @@ static int ifilter_send_eof(InputFilter *ifilter, int64_t pts) return ret; } else { // the filtergraph was never configured - if (ifilter->format < 0) { - ret = ifilter_parameters_from_codecpar(ifilter, ifilter->ist->st->codecpar); - if (ret < 0) - return ret; - } - if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO)) { + 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 || 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; } @@ -2069,7 +1973,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++) { @@ -2270,79 +2174,200 @@ fail: return err < 0 ? err : ret; } -static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output, +static void subtitle_send_kickoff(InputStream *ist, int64_t heartbeat_pts) +{ + AVFrame *frame; + int64_t pts; + + frame = av_frame_alloc(); + if (!frame) { + av_log(ist->dec_ctx, AV_LOG_ERROR, "Unable to alloc frame (out of memory).\n"); + return; + } + + frame->type = AVMEDIA_TYPE_SUBTITLE; + av_frame_get_buffer2(frame, 0); + + frame->format = (uint16_t)ist->dec_ctx->subtitle_type; + + pts = FFMAX(heartbeat_pts, ist->subtitle_kickoff.last_pts) + av_rescale_q(10, (AVRational){ 1, 1000 }, ist->st->time_base); + + frame->width = ist->subtitle_kickoff.w; + frame->height = ist->subtitle_kickoff.h; + frame->subtitle_timing.start_pts = av_rescale_q(pts, ist->st->time_base, AV_TIME_BASE_Q); + frame->subtitle_timing.duration = av_rescale_q(10, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q); + frame->pts = pts; + + av_log(NULL, AV_LOG_WARNING, "subtitle_kickoff: call subtitle_resend_current %"PRId64" frame->format: %d\n", pts, frame->format); + + ist->subtitle_kickoff.last_pts = pts; + + send_frame_to_filters(ist, frame); + + av_frame_free(&frame); +} + +// Opposed to the earlier "subtitle hearbeat", this is primarily aimed at +// sending an initial subtitle frame to the filters for propagating the initial +// timing values and to avoid that too much time is spent on a single "HW" decoder +// which could overflow its buffer pool otherwise +static void subtitle_kickoff(InputStream *ist, int64_t pts) +{ + int i; + int64_t pts2; + int64_t diff; + + if (ist->st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) + return; + + for (i = 0; i < nb_input_streams; i++) { + InputStream *ist2 = input_streams[i]; + unsigned j, nb_reqs; + + if (!ist2->subtitle_kickoff.is_active) + continue; + /* It's primarily the initial send that matters and which propagates + * the right start times to subtitle filters depending on input from decoding */ + diff = av_rescale_q(5000, (AVRational){ 1, 1000 }, ist2->st->time_base); + pts2 = av_rescale_q(pts, ist->st->time_base, ist2->st->time_base); + + /* do not send a kickoff frame if the subtitle is already ahead */ + if (pts2 - diff <= ist2->subtitle_kickoff.last_pts) + continue; + + for (j = 0, nb_reqs = 0; j < ist2->nb_filters; j++) { + if (ist2->filters[j]->filter == NULL) { + subtitle_send_kickoff(ist2, pts2); + return; + } + nb_reqs += av_buffersrc_get_nb_failed_requests(ist2->filters[j]->filter); + } + if (nb_reqs) { + av_log(NULL, AV_LOG_WARNING, "subtitle_kickoff: resend - pts: %"PRIi64"\n", pts2); + subtitle_send_kickoff(ist2, pts2); + } + } +} + +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; + int64_t pts; + + 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 = (uint16_t)avctx->subtitle_type; + + 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); - check_decode_result(NULL, got_output, ret); + 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++) { + if (ist->filters[i]->filter != NULL) { + 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*, ist->decoded_frame, ist->prev_sub.subtitle); + decoded_frame = ist->decoded_frame; 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_alloc2(8, sizeof(AVSubtitle), AV_FIFO_FLAG_AUTO_GROW); - if (!ist->sub2video.sub_queue) - exit_program(1); + decoded_frame->type = AVMEDIA_TYPE_SUBTITLE; - ret = av_fifo_write(ist->sub2video.sub_queue, &subtitle, 1); - if (ret < 0) - exit_program(1); - free_sub = 0; - } + if (decoded_frame->format == AV_SUBTITLE_FMT_UNKNOWN) + decoded_frame->format = (uint16_t)avctx->subtitle_type; + + if ((ret = av_buffer_replace(&decoded_frame->subtitle_header, ist->subtitle_header)) < 0) + return ret; - if (!subtitle.num_rects) - goto out; + decoded_frame->pts = av_rescale_q(decoded_frame->subtitle_timing.start_pts, AV_TIME_BASE_Q, ist->st->time_base); - ist->frames_decoded++; + pts = av_rescale_q(decoded_frame->subtitle_timing.start_pts, + AV_TIME_BASE_Q, ist->st->time_base); - for (i = 0; i < nb_output_streams; i++) { - OutputStream *ost = output_streams[i]; + ist->subtitle_kickoff.last_pts = decoded_frame->pts = pts; - if (!check_output_constraints(ist, ost) || !ost->encoding_needed - || ost->enc->type != AVMEDIA_TYPE_SUBTITLE) - continue; + if (ist->nb_filters > 0) { + AVFrame *filter_frame = av_frame_clone(decoded_frame); + if (!filter_frame) { + err = AVERROR(ENOMEM); + goto end; + } - do_subtitle_out(output_files[ost->file_index], ost, &subtitle); + err = send_frame_to_filters(ist, filter_frame); + av_frame_free(&filter_frame); } -out: - if (free_sub) - avsubtitle_free(&subtitle); - return ret; + if (err >= 0) { + for (i = 0; i < nb_output_streams; i++) { + OutputStream *ost = output_streams[i]; + InputStream *ist_src = get_input_stream(ost); + + if (!ist_src || !check_output_constraints(ist, ost) + || ist_src != ist + || !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, decoded_frame); + } + } + +end: + av_frame_unref(decoded_frame); + return err < 0 ? err : ret; } static int send_filter_eof(InputStream *ist) @@ -2446,7 +2471,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); @@ -2663,13 +2688,6 @@ static int init_input_stream(int ist_index, char *error, int error_len) 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); @@ -3097,7 +3115,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; } @@ -3114,6 +3132,17 @@ static int init_output_stream_encode(OutputStream *ost, AVFrame *frame) return 0; } +static enum AVSubtitleType 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; +} + static int init_output_stream(OutputStream *ost, AVFrame *frame, char *error, int error_len) { @@ -3150,19 +3179,13 @@ 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 *output_descriptor = avcodec_descriptor_get(ost->enc_ctx->codec_id); + const enum AVSubtitleType in_subtitle_format = (uint16_t)dec->subtitle_type; + const enum AVSubtitleType out_subtitle_format = output_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; } } @@ -3326,7 +3349,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); @@ -3497,7 +3521,7 @@ static OutputStream *choose_output(void) av_rescale_q(ost->last_mux_dts, ost->st->time_base, AV_TIME_BASE_Q); if (ost->last_mux_dts == AV_NOPTS_VALUE) - av_log(NULL, AV_LOG_DEBUG, + av_log(NULL, AV_LOG_INFO, "cur_dts is invalid st:%d (%d) [init:%d i_done:%d finish:%d] (this is harmless if it occurs once at the start per stream)\n", ost->st->index, ost->st->id, ost->initialized, ost->inputs_done, ost->finished); @@ -4166,7 +4190,7 @@ 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); + subtitle_kickoff(ist, pkt->pts); process_input_packet(ist, pkt, 0); @@ -4378,6 +4402,7 @@ static int transcode(void) /* at the end of stream, we must flush the decoder buffers */ for (i = 0; i < nb_input_streams; i++) { ist = input_streams[i]; + ist->subtitle_kickoff.is_active = 0; if (!input_files[ist->file_index]->eof_reached) { process_input_packet(ist, NULL, 0); } diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 69a368b8d1..74feff734f 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -353,17 +353,16 @@ typedef struct InputStream { struct { /* previous decoded subtitle and related variables */ int got_output; int ret; - AVSubtitle subtitle; + AVFrame *subtitle; } prev_sub; - struct sub2video { + struct subtitle_kickoff { + int is_active; int64_t last_pts; - int64_t end_pts; - AVFifo *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; + } subtitle_kickoff; + + AVBufferRef *subtitle_header; /* decoded data from this stream goes into all those filters * currently video and audio only */ @@ -468,6 +467,8 @@ typedef struct OutputStream { int64_t first_pts; /* dts of the last packet sent to the muxer */ int64_t last_mux_dts; + /* subtitle_pts values of the last subtitle frame having arrived for encoding */ + int64_t last_subtitle_pts; // the timebase of the packets sent to the muxer AVRational mux_timebase; AVRational enc_timebase; @@ -675,8 +676,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 0845c631a5..fe03d06373 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, @@ -232,9 +232,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); } @@ -255,8 +254,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]; @@ -361,6 +361,21 @@ static int insert_trim(int64_t start_time, int64_t duration, const char *name = (type == AVMEDIA_TYPE_VIDEO) ? "trim" : "atrim"; int ret = 0; + switch (type) { + case AVMEDIA_TYPE_VIDEO: + name = "trim"; + break; + case AVMEDIA_TYPE_AUDIO: + name = "atrim"; + break; + case AVMEDIA_TYPE_SUBTITLE: + name = "strim"; + break; + default: + av_log(NULL, AV_LOG_ERROR, "insert_trim: Invalid media type: %d\n", type); + return AVERROR_INVALIDDATA; + } + if (duration == INT64_MAX && start_time == AV_NOPTS_VALUE) return 0; @@ -423,6 +438,40 @@ 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; + OutputFile *of = output_files[ost->file_index]; + 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; @@ -604,7 +653,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) { @@ -638,6 +688,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; } } @@ -657,51 +708,154 @@ 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; + InputFile *f = input_files[ist->file_index]; + AVBPrint args; + char name[255]; + int ret, pad_idx = 0; + int w, h; + int64_t tsoffset = 0; + 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; + } + + if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE && ist->dec_ctx) { + // 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 + ifilter->format = (uint16_t)ist->dec_ctx->subtitle_type; + } + + ist->subtitle_kickoff.is_active = 1; - /* 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); + w = ist->dec_ctx->width; + h = ist->dec_ctx->height; + } + + 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); + } + + ist->subtitle_kickoff.w = w; + ist->subtitle_kickoff.h = h; + av_log(ifilter, AV_LOG_INFO, "subtitle input filter: decoding size %dx%d\n", ist->subtitle_kickoff.w, ist->subtitle_kickoff.h); + + ifilter->width = w; + ifilter->height = h; + ist->dec_ctx->width = w; + ist->dec_ctx->height = h; + + ist->subtitle_kickoff.last_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 (!(w && h)) { - w = FFMAX(w, 720); - h = FFMAX(h, 576); - } - av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h); - } - 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 (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; + } - /* 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; + 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; + } - 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; + if (copy_ts) { + tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 : f->start_time; + if (!start_at_zero && f->ctx->start_time != AV_NOPTS_VALUE) + tsoffset += f->ctx->start_time; + } + ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) || !f->accurate_seek) ? + AV_NOPTS_VALUE : tsoffset, f->recording_time, + &last_filter, &pad_idx, name); + 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; + 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, @@ -720,8 +874,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)); @@ -736,12 +897,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}; @@ -753,7 +908,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); @@ -952,6 +1107,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; } } @@ -969,8 +1125,9 @@ static void cleanup_filtergraph(FilterGraph *fg) static int filter_is_buffersrc(const AVFilterContext *f) { return f->nb_inputs == 0 && - (!strcmp(f->filter->name, "buffer") || - !strcmp(f->filter->name, "abuffer")); + (!strcmp(f->filter->name, "buffersrc") || + !strcmp(f->filter->name, "abuffersrc") || + !strcmp(f->filter->name, "sbuffersrc")); } static int graph_is_meta(AVFilterGraph *graph) @@ -1147,18 +1304,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) { - AVSubtitle tmp; - while (av_fifo_read(ist->sub2video.sub_queue, &tmp, 1) >= 0) { - sub2video_update(ist, INT64_MIN, &tmp); - avsubtitle_free(&tmp); - } - } - } - return 0; fail: @@ -1178,6 +1323,7 @@ int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame) ifilter->width = frame->width; ifilter->height = frame->height; ifilter->sample_aspect_ratio = frame->sample_aspect_ratio; + ifilter->type = frame->type; ifilter->sample_rate = frame->sample_rate; ret = av_channel_layout_copy(&ifilter->ch_layout, &frame->ch_layout); 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 e08455478f..5a52f355a0 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -1000,6 +1000,7 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) av_log(NULL, AV_LOG_FATAL, "Invalid canvas size: %s.\n", canvas_size); exit_program(1); } + ist->subtitle_kickoff.is_active = 1; break; } case AVMEDIA_TYPE_ATTACHMENT: @@ -1715,11 +1716,15 @@ static char *get_ost_filters(OptionsContext *o, AVFormatContext *oc, if (ost->filters_script) return read_file(ost->filters_script); - else if (ost->filters) + if (ost->filters) return av_strdup(ost->filters); - return av_strdup(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ? - "null" : "anull"); + switch (st->codecpar->codec_type) { + case AVMEDIA_TYPE_VIDEO: return av_strdup("null"); + case AVMEDIA_TYPE_AUDIO: return av_strdup("anull"); + case AVMEDIA_TYPE_SUBTITLE: return av_strdup("snull"); + default: av_assert0(0); return NULL; + } } static void check_streamcopy_filters(OptionsContext *o, AVFormatContext *oc, @@ -2120,6 +2125,9 @@ static OutputStream *new_subtitle_stream(OptionsContext *o, AVFormatContext *oc, subtitle_enc->codec_type = AVMEDIA_TYPE_SUBTITLE; + MATCH_PER_STREAM_OPT(filter_scripts, str, ost->filters_script, oc, st); + MATCH_PER_STREAM_OPT(filters, str, ost->filters, oc, st); + if (!ost->stream_copy) { char *frame_size = NULL; @@ -2128,6 +2136,10 @@ static OutputStream *new_subtitle_stream(OptionsContext *o, AVFormatContext *oc, av_log(NULL, AV_LOG_FATAL, "Invalid frame size: %s.\n", frame_size); exit_program(1); } + + ost->avfilter = get_ost_filters(o, oc, ost); + if (!ost->avfilter) + exit_program(1); } return ost; @@ -2273,8 +2285,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); } @@ -2679,7 +2692,8 @@ loop_end: ist->processing_needed = 1; if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || - ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO || + ost->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) { err = init_simple_filtergraph(ist, ost); if (err < 0) { av_log(NULL, AV_LOG_ERROR, @@ -2724,6 +2738,10 @@ loop_end: } else if (ost->enc->ch_layouts) { f->ch_layouts = ost->enc->ch_layouts; } + break; + case AVMEDIA_TYPE_SUBTITLE: + f->format = ost->enc_ctx->subtitle_type; + break; } } diff --git a/tests/ref/fate/filter-overlay-dvdsub-2397 b/tests/ref/fate/filter-overlay-dvdsub-2397 index 7df4f50776..7b827956f1 100644 --- a/tests/ref/fate/filter-overlay-dvdsub-2397 +++ b/tests/ref/fate/filter-overlay-dvdsub-2397 @@ -489,368 +489,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/sub-scc b/tests/ref/fate/sub-scc index 62cbf6fa4a..b02e6e95b5 100644 --- a/tests/ref/fate/sub-scc +++ b/tests/ref/fate/sub-scc @@ -11,7 +11,6 @@ Style: Default,Monospace,16,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,3,1,0, [Events] Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text -Dialogue: 0,0:00:-2.-47,0:00:00.70,Default,,0,0,0,,{\an7}{\pos(76,228)}WE HAVE FOUND A WITCH !\N{\an7}{\pos(76,243)}MAY WE BURN HER ? Dialogue: 0,0:00:00.69,0:00:03.29,Default,,0,0,0,,{\an7}{\pos(115,228)}[ Crowd ]\N{\an7}{\pos(115,243)}BURN HER ! BURN HER ! Dialogue: 0,0:00:03.30,0:00:07.07,Default,,0,0,0,,{\an7}{\pos(38,197)}HOW DO YOU KNOW\N{\an7}{\pos(38,213)}SHE IS A WITCH ?\N{\an7}{\pos(153,243)}SHE LOOKS LIKE ONE ! Dialogue: 0,0:00:07.07,0:00:09.27,Default,,0,0,0,,{\an7}{\pos(192,228)}[ Shouting\N{\an7}{\pos(192,243)}\h\hAffirmations ] diff --git a/tests/ref/fate/sub2video b/tests/ref/fate/sub2video index 80abe9c905..7cc6549966 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, 0x34cdddee 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, 0xe6bc0ea9 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, 0xa8643af7 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, 0xa3782469 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, 0x129de2f8 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, 0x56f54e73 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, 0x300b5247 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, 0x6fd028fa 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, 0x01f80e9d 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, 0xb48d90c0 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, 0xc43518ba 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, 0x8780239e 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, 0x9d5b9d69 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, 0x2dc83609 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, 0xb046dd30 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..9bd4e8b114 100644 --- a/tests/ref/fate/sub2video_basic +++ b/tests/ref/fate/sub2video_basic @@ -1,95 +1,1149 @@ -#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, 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..2fbd91a7eb 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, 0xa87c518f +0, 1, 1, 1, 8294400, 0xa87c518f 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