From patchwork Fri Dec 10 21:36:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32264 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2372155iog; Fri, 10 Dec 2021 13:37:28 -0800 (PST) X-Google-Smtp-Source: ABdhPJyYNbCrOzR5mJrsfdH6xRsWki4vnvfhI0dbIx1BHF9xxyxf9E/zLBNNHz8Ma//+MtnepCal X-Received: by 2002:a05:6402:4301:: with SMTP id m1mr42532351edc.54.1639172248377; Fri, 10 Dec 2021 13:37:28 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id o2si6269818edc.544.2021.12.10.13.37.27; Fri, 10 Dec 2021 13:37:28 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=qJxA0HHR; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 774AA68AD86; Fri, 10 Dec 2021 23:37:16 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM02-DM3-obe.outbound.protection.outlook.com (mail-dm3nam07olkn2070.outbound.protection.outlook.com [40.92.43.70]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id C412D68AD86 for ; Fri, 10 Dec 2021 23:37:09 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=WTXw8YOHCHdVSymviNs4516AI+4GFJmHmI0zQOKNB7W17rg6ZVEYGfWZn2h8LH9KAhVOvXsAPuOByABqqrS/vNJwcYfwjsxkAcdAbUHSE2oCkbKKUbUvVLeQTNELl17UJfp43VcM82Y2cKu7MKuBkZtPeucoZotF3uvqYsPddR11Ma3yj8AYtm6pObBwjOVtxYC7BqOvWRx/jSFj9RUPGoCX+GDI8vwpFXDV+S62zRgoL+/LsVAL9NmVHoZhu7Fa7PDRndtu9w7PbyEU385Xy6f526fHTjD/jPe3ECWAzS/ZHhLlZja7wlOg6jyAcliCqVdGPthT2a24Nlk8jqcoBA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=oL37gDC1r/mBYD6qQL+ngrx8TZTBIBAesxuuxB32un4=; b=HRZge4lo8oQFc4+dtp5qCwO60WTB52v6dWaLDGsBI3zDuSvTNIXfgp0evxdwj/GQPqv2CMm6qVzi8GQ3W2zVFmm0ilWJCtTFd7PUq0v2xT4PehHAPfy4OlaE2YTn2nBev3OY73Q20273XwcZERM3Z6T9xLQ4K0yh/+Og4tbzasSKfFeRmhpr9v7qlxXyhehRHE9xEPKS3chGiBZfeVcsopi1OX3Uc5SZtQ1DoVBPfNiS1AAavgawCMmGy79E89RgB1ZKaatnqS5/zq90f95jI1PDwrYLR8u68pPTuv5GavXPg3cbz/MmPxzM9mY07XZZnnxP3UvfcAeZKrjzPDq0+Q== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=oL37gDC1r/mBYD6qQL+ngrx8TZTBIBAesxuuxB32un4=; b=qJxA0HHRHQBjG3rE97+c1G74UfO4/gT2X804slDfOWONfg6ACuuvJFARMaCa9jwHD0L01bmlprSPLD9j1o5irgjUYckmYh3L6HlZqNsdPihtB0xQqPN9CdWpumHC5yXEKTdx7gg6RbXTRXH9O+MlsMUSaAbCJ2/SA3+0oYwWO1uiPaz64Sp9gAYifp8WwvqQlYXySZC5QHE3MbsZ8QPp+8ieCi8NG9IN9o1swsSTD1HsSxXmvzHxIy0eVOGRWJQaNMoZp0FHeW43H8TY2uWGVyrIC6X35LR0TbO3maQndToSAulPp9MwJ2A+lYDzZPh7CnaBmlz8q1u0oEGQohWUVw== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0240.NAMP223.PROD.OUTLOOK.COM (2603:10b6:5:316::23) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.11; Fri, 10 Dec 2021 21:36:58 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:36:58 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 01/21] avcodec,avutil: Move enum AVSubtitleType to avutil, add new and deprecate old values Thread-Index: AQHX7g4P6uKnyl+plUOajX4vud+z1g== Date: Fri, 10 Dec 2021 21:36:58 +0000 Message-ID: References: In-Reply-To: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [3zJP/Dzg5r8ijHNowGP8SZTjnq5M7Dxq] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: f4354526-83ca-4f5c-c90b-08d9bc253206 x-ms-traffictypediagnostic: DM8P223MB0240:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: BkP448dkIah98c5wRkjooZC/SkZu9usuR+9k/UPpj0Z4Cz2GhsGqaDu7216loiSYG4EiEu9ai4PI7IBvRuPn4XscT1lnecc+AdnQaaszTZhk1ca6QE+dDMZXQ0Qjwths1mrJ5m7CG8e6UYBf6lu5nkxcoa+30XYp/77XGRAjBP39IWdBSQnRnfF+NidM+ROE4OnfkOycjh0eVoUCnb77Ivjwwv7QArWDB3lgH2gytGUKv2TBiIxCBpZGI0HvU/XsQMZlfVmzew9vTA9VJKokCjbtyBEXVFJJcSXJl1RIZVCB5YW17r7GA6RN2MgefDMymE5YENsdoAX5azbsHNwQtTQKioNEm0CjLewXRfXO9JQg2ci+NRVVtTVN6l2IEGWEb14g9FH4jG8YYkFahjgMZYscuZ8SqtNwTjqNusRuYLAzdjvsvDLjiT0Ejji/fNPybkGuQljD9KSYhrcArRi4aIlHxbNSxMQUfI2ZuDRPsFJ954ZYY+R9IhFtZKloq4SXEyEqdxAwdxPItASDlnUx6a5fXqXsr0uPpHxESBs2yOE+GKjNuMu/1qev3K1jwzwvnFyOubzP/rka6nKy5RKsCw== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?2s9WFxHzJZAP5AK4yXstEWLeFnZX?= =?utf-8?q?07YR7g2rfY3jB0kZZUhft3ceeQcZ7QS01f5v832Kayt36iGNEJlKEdBxPW3Mw1U/y?= =?utf-8?q?w3pljKmKXEqqndzwoNXgkdEgCarvKa74VIJbaC7nc85N7zFI8vwhajGPKbTD+uc8H?= =?utf-8?q?L2eNnaJdxHrVq7XWunaZ2FHRG0HCvdHSsrMRlXgv/MOV3d9bOpPazjKk9OL1k/hHc?= =?utf-8?q?FyBJ3dCl4NrvxRRvc6Uz/vd41QF2gBrFd6GyfT07fMUwS0LcU2MdEdkFIgPX6+Ddg?= =?utf-8?q?JZUjpKsj5xuUlTNioHAt6Jhn3I5EacYBz9UnC9LcpC1dElYQMwnA3ZKHGhRcNiFjX?= =?utf-8?q?CuyE6mgXQoGUVHN3vet7y6oZesYsUIE7Qhf7j9M4FLa8Pilc/yJgN0j21JDJnuMCy?= =?utf-8?q?YdDbzXMGfttCtWTLamxw9PS+BTGpd/VBrU0HoKWge9Q4v7xITAwhTqV0J4EBCbYqC?= =?utf-8?q?o7sofF4s3tp1cQAlGIPul1Pp280uPeKeweWEsCJC65Ct42p/L6JtTunL7QPis2Dpe?= =?utf-8?q?I3qBSfq8JHHNBDhVUu0SKI8ZmvP8unQSi3prEqFbxsKl8zgXqr8GVn0+vGVaU2kpA?= =?utf-8?q?GwszUqx+FNobRBR5JUgwQI26oYW7NY0dK4CKJCNCueZ0rTa2ZrgNHiy/whzdB+IDF?= =?utf-8?q?k76yX9VoxbeHLH8FuX1LwxPjtkvjZjpCmkoo/W/cGx48ha4MWRipbUcJPY8mFTJdN?= =?utf-8?q?Pz471N8xTNWny7OgeqJKXuP9nTD4n0ajmVMZSv+jrc3J8UxCldYCAZyNoKpe4J2Cb?= =?utf-8?q?0ABRzDImdiSkbxMZbA/n40GEAAOeotzslafUdAJIJIDsXdL00s6jj37mxZREEyojf?= =?utf-8?q?7bxriDvVQU7iobv+v5xqezyY4lTz4O+IMM5Q/Y3XIOf2uP0OkB6c1eXnSXP+20ll/?= =?utf-8?q?QS+XgVnOv47XEe37+KqfbstxeKHsHfj/or+6ZeolTXpw=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: f4354526-83ca-4f5c-c90b-08d9bc253206 X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:36:58.7896 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0240 Subject: [FFmpeg-devel] [PATCH v23 01/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 0GU7KJtVqpV1 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 7ee8bc2b7c..b05c12e47e 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -35,6 +35,7 @@ #include "libavutil/frame.h" #include "libavutil/log.h" #include "libavutil/pixfmt.h" +#include "libavutil/subfmt.h" #include "libavutil/rational.h" #include "codec.h" @@ -2238,24 +2239,6 @@ typedef struct AVHWAccel { * @} */ -enum AVSubtitleType { - SUBTITLE_NONE, - - SUBTITLE_BITMAP, ///< A bitmap, pict will be set - - /** - * Plain text, the text field must be set by the decoder and is - * authoritative. ass and pict fields may contain approximations. - */ - SUBTITLE_TEXT, - - /** - * Formatted text, the ass field must be set by the decoder and is - * authoritative. pict and text fields may contain approximations. - */ - SUBTITLE_ASS, -}; - #define AV_SUBTITLE_FLAG_FORCED 0x00000001 typedef struct AVSubtitleRect { diff --git a/libavutil/Makefile b/libavutil/Makefile index 529046dbc8..c7843db1e4 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -74,6 +74,7 @@ HEADERS = adler32.h \ sha512.h \ spherical.h \ stereo3d.h \ + subfmt.h \ threadmessage.h \ time.h \ timecode.h \ diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h new file mode 100644 index 0000000000..791b45519f --- /dev/null +++ b/libavutil/subfmt.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 softworkz + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_SUBFMT_H +#define AVUTIL_SUBFMT_H + +#include "version.h" + +enum AVSubtitleType { + + /** + * Subtitle format unknown. + */ + AV_SUBTITLE_FMT_NONE = -1, + + /** + * Subtitle format unknown. + */ + AV_SUBTITLE_FMT_UNKNOWN = 0, +#if FF_API_OLD_SUBTITLES + SUBTITLE_NONE = 0, ///< Deprecated, use AV_SUBTITLE_FMT_NONE instead. +#endif + + /** + * Bitmap area in AVSubtitleRect.data, pixfmt AV_PIX_FMT_PAL8. + */ + AV_SUBTITLE_FMT_BITMAP = 1, +#if FF_API_OLD_SUBTITLES + SUBTITLE_BITMAP = 1, ///< Deprecated, use AV_SUBTITLE_FMT_BITMAP instead. +#endif + + /** + * Plain text in AVSubtitleRect.text. + */ + AV_SUBTITLE_FMT_TEXT = 2, +#if FF_API_OLD_SUBTITLES + SUBTITLE_TEXT = 2, ///< Deprecated, use AV_SUBTITLE_FMT_TEXT instead. +#endif + + /** + * Text Formatted as per ASS specification, contained AVSubtitleRect.ass. + */ + AV_SUBTITLE_FMT_ASS = 3, +#if FF_API_OLD_SUBTITLES + SUBTITLE_ASS = 3, ///< Deprecated, use AV_SUBTITLE_FMT_ASS instead. +#endif + + AV_SUBTITLE_FMT_NB, ///< number of subtitle formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions. +}; + +#endif /* AVUTIL_SUBFMT_H */ diff --git a/libavutil/version.h b/libavutil/version.h index 017fc277a6..c6cd0b0d79 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -109,6 +109,7 @@ #define FF_API_DECLARE_ALIGNED (LIBAVUTIL_VERSION_MAJOR < 58) #define FF_API_COLORSPACE_NAME (LIBAVUTIL_VERSION_MAJOR < 58) #define FF_API_AV_MALLOCZ_ARRAY (LIBAVUTIL_VERSION_MAJOR < 58) +#define FF_API_OLD_SUBTITLES (LIBAVUTIL_VERSION_MAJOR < 58) /** * @} From patchwork Fri Dec 10 21:37:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32265 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2372324iog; Fri, 10 Dec 2021 13:37:39 -0800 (PST) X-Google-Smtp-Source: ABdhPJytZK967tDCg/fTUh3c7XjcdKroPeKbOjPh2dj1ttq6pt+9HC3MAHLooJ5XjhdkMaHsF3rt X-Received: by 2002:a17:907:7850:: with SMTP id lb16mr26880941ejc.67.1639172258994; Fri, 10 Dec 2021 13:37:38 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id d14si1722337ejp.788.2021.12.10.13.37.38; Fri, 10 Dec 2021 13:37:38 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=sJYwa4t7; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 96A5968AF2B; Fri, 10 Dec 2021 23:37:21 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM02-DM3-obe.outbound.protection.outlook.com (mail-dm3nam07olkn2070.outbound.protection.outlook.com [40.92.43.70]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 187EB68AEC8 for ; Fri, 10 Dec 2021 23:37:15 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=Cdsacu3oVnPzRQVRadjuy9G6Y7YYXBq/cKb3cORty3BZK1tUi8GfgexyEMG83Upse1Y2IyM3rq5OWqcd2uWOZZD5ovJU9EVRfiJ4BzN+45e4DVNmHMBWSTlO+BpKGlEs0ApKbnu6ybJ56Mv66kq+j7BzI3BzoCkrtHND/7MXDPmn4VEhi13TJ08YEIQIhmYC138KiyjnnAZNK/W4ztY8DHfgx5pHZC/tgwjGf5C9afEqOkeDF5M5Ra3bmWtL3w08epYOLWxyZGQJvzfxLsugsSpmYaAk8cbEtLwKLCOHD0YnEKhsa/PBzujoHZLA2AxOyGpGkzMU1UOG1+ggxGusPg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=sN2pMnhrEu115XwlmsrHoV4GbgnNiC8z/3LT9rTfDYA=; b=GlK1tcd2cka2E6d8JdGUG7OuHXTMNeMYSC4KcTJlsu9h5HuVTYOjxtP0QTvwPwUlwGx090+UDyLKkfTnUxSlVFWKIpn6ouefNZ7k127V1GOt57Myz/s7qrEuE9gSpO7nUGzoDCzOhJkNpU+eWH85oh5toAu9aaiyf8OoGB0FIROaNdl5BCKNxIqw0MR8OyQe+XVG+JHD6g209x9H4zZ6r7fS4CCxflYBDQ36KWlbDnRAMP2Wxuqw7966KLyoJX9dTVj4FxHHhTQAuHF3cg0ia/TAPoR6xOuhQTuQO3D/9gCZZg/4E8Q5CbAm1VpemN69NNPpq98u7VmpL+C1bzzdRA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=sN2pMnhrEu115XwlmsrHoV4GbgnNiC8z/3LT9rTfDYA=; b=sJYwa4t7Jy1WhDRGBJjgXSAGzKGXPnrbPkLpspb8vcxZzvoZD+ObDBVO7/Op2S1OY1Wob3vGAGsiCtdbsZ6E1vKIa/VDlFAsW4SJTRHkEehPHa24tvdYnh8s40Jublfu/uJqN2tbwuOnh9yrnU1t2DZnQefqYsYv+guy7+5bVb2Fu9lSOeLTZTJ3qNl+BgA+5zEjNdx1ARpZK4sBT/LLtizDtUrS62iCRtQyvdbnJ2bIU2m1TsKvvGA/tW9lr8zo7e0IR3MG0S2TOSx0xWL3F1y4tb2TU7UO5KQRxZj05G2skY2JVHChVTimKrBLsHWgNA8Vdaz9yxExy6wvJmVSnw== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0240.NAMP223.PROD.OUTLOOK.COM (2603:10b6:5:316::23) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.11; Fri, 10 Dec 2021 21:37:00 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:00 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 02/21] avutil/frame: Prepare AVFrame for subtitle handling Thread-Index: AQHX7g4QoNmaKVn7HkGmkwbGs1isDw== Date: Fri, 10 Dec 2021 21:37:00 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> In-Reply-To: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [2eCrZ4kUkr5iX3oQ/XLInFfkriDP4ywM] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: ffd602e6-b344-4857-c6c5-08d9bc253336 x-ms-traffictypediagnostic: DM8P223MB0240:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: cl+h0Nn9RRfqSOM2FwY7LcYa5pqurHiQb0Pm8X2eFURWq/IWLSN8nEI6r+2El6ZS7zfWzB4qkLMfeEuq/g+eGQDn/JFx4fetAWLd2VfY0yQBWwx1yD+N3z8imqYUl7hNqBnMlSv4Pd5ouKICwHweKaoVobW10/HUs0aNIJxY1rTUtvOnEqfrYEEAisQoe22aTx0SKx5keiUgYFcedcv5GoTVQdCu1CEQgmccyrflhq/h+0DkTbqHqFArYGhT51foCjs5vpVloeOtqk1RAh5YlalQZx++69231Lty8aijuYKt7DAqG64BgcYNlIvlBzUJTxKtHwf1chXeHbqyTUxAMAPbOGlPUsxRKzdaMZUPX0KyFC8TfwlTRkaUgmafziaP0LCRKkhfhJfqhUfLJB7nVBughtDOeO/tOfZSwc2kDGw/20LmF18lw28sKEd0GCt5nGNB0vEgfdeyeVhZnzcvAk/42F2/ODguc0GXK6ljykFqNhXjswcqRJzB/rkfGyKePBFXmucb3DF23TiZzNbyVc2g/kzN35T+W89rYM0Oql6GIJV3jV6D++5mjF4EbrlGMgCc8rzYlcoP9ih5dwTV3Q== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?+W6SfYf9T1X9no3l1W2aigk+uzjD?= =?utf-8?q?QXYxF2Crn7lG5R//qyNS+AxAjRmVwBB/L+qNTK1oNkOaB7D2teZzO5XvskLlt6CRg?= =?utf-8?q?KGXyN9RceZdc4pNYkxryTcTGIe50qGwQw92G6lvTVEvZU9d5Bsh/GC0ZYLGmMYzIL?= =?utf-8?q?IKyuFW+sYi5FT1wU/8i3M8WpCPK6FajHYO7uLV2X80QvjpPBY2E6oBB/JXq4ImBA/?= =?utf-8?q?8oChre47oyjaJT8M/YdVKQaTcmIOSIjsEMwouKXR6W1UR+dzb4ba4entCZFwDakxp?= =?utf-8?q?vSsHKqrAsYh+yWEcEWaKkSjoAXYG9gEfcpji9V1rWkiDGF1VT3efKppl2Yz4WjFUt?= =?utf-8?q?oi/ufDxEuNNuobrFBIIibp3FDYjtTHFte6NzrxYuGs+ftjSddeVbtM25Tv63qC43r?= =?utf-8?q?P7Ph+CODNP4s18RRi/GF0N9qGkqmK6KCQwK9rqAemVwQIGRyeId5lIT/QEJN8GXxH?= =?utf-8?q?TBe95Eq7oLf2urBPqrkkOWPSi2YpnUHyLTd/Y9G16uXXzdwXo1tY7tfzUQtk5Khy5?= =?utf-8?q?j2HSNPUVe6Myl2UR0bjAO0H3Dedp5j6GpDfVqQYWuw0Y00FQr/jKjqtTH5ZalCfIR?= =?utf-8?q?FpWMCt+SkUvbyc7ePqrYeb/GcidfZAygS4Ou9Tkj5jkkWBRrkIW7Fdlm72RPd4kzv?= =?utf-8?q?xG/nnwgbFq5b4Gn8WuoOt4a1BKqYa+N2W+RUBqPXvwnNlA8AIrZgMDnX39cnRnAeg?= =?utf-8?q?hTCfsT6zH4UTkQ1tGdVkF/fpoJnfhRhLy9v0fD5dUcERTj6kKsd7my6yVRuYFhRzg?= =?utf-8?q?FnljgUoExqxEZXrj39hgYoOSCQcT199PZDqJSSZw8XEtqE4oSUHdDI2d7SWsE+Z7n?= =?utf-8?q?TqFLz8t2QVl6wHvlK3E7yac1whQ46etwuLFSOZyYCoU9vno8ElO5KQlWcJkm5bD9X?= =?utf-8?q?924BJCF3NHF9xD4saYnmDsdk1GfiDY3G4S0c+avrH6sQ=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: ffd602e6-b344-4857-c6c5-08d9bc253336 X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:00.8287 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0240 Subject: [FFmpeg-devel] [PATCH v23 02/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: +V2QSMDsvzOc Root commit for adding subtitle filtering capabilities. In detail: - Add type (AVMediaType) field to AVFrame Replaces previous way of distinction which was based on checking width and height to determine whether a frame is audio or video - Add subtitle fields to AVFrame - Add new struct AVSubtitleArea, similar to AVSubtitleRect, but different allocation logic. Cannot and must not be used interchangeably, hence the new struct Signed-off-by: softworkz --- libavutil/Makefile | 1 + libavutil/frame.c | 211 ++++++++++++++++++++++++++++++++++++++++----- libavutil/frame.h | 78 ++++++++++++++++- libavutil/subfmt.c | 45 ++++++++++ libavutil/subfmt.h | 47 ++++++++++ 5 files changed, 358 insertions(+), 24 deletions(-) create mode 100644 libavutil/subfmt.c diff --git a/libavutil/Makefile b/libavutil/Makefile index c7843db1e4..7e79936876 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -160,6 +160,7 @@ OBJS = adler32.o \ slicethread.o \ spherical.o \ stereo3d.o \ + subfmt.o \ threadmessage.o \ time.o \ timecode.o \ diff --git a/libavutil/frame.c b/libavutil/frame.c index 0912ad9131..62d29ae7d6 100644 --- a/libavutil/frame.c +++ b/libavutil/frame.c @@ -26,6 +26,7 @@ #include "imgutils.h" #include "mem.h" #include "samplefmt.h" +#include "subfmt.h" #include "hwcontext.h" #define CHECK_CHANNELS_CONSISTENCY(frame) \ @@ -50,6 +51,9 @@ const char *av_get_colorspace_name(enum AVColorSpace val) return name[val]; } #endif + +static int frame_copy_subtitles(AVFrame *dst, const AVFrame *src, int copy_data); + static void get_frame_defaults(AVFrame *frame) { memset(frame, 0, sizeof(*frame)); @@ -70,7 +74,12 @@ static void get_frame_defaults(AVFrame *frame) frame->colorspace = AVCOL_SPC_UNSPECIFIED; frame->color_range = AVCOL_RANGE_UNSPECIFIED; frame->chroma_location = AVCHROMA_LOC_UNSPECIFIED; - frame->flags = 0; + frame->subtitle_start_time = 0; + frame->subtitle_end_time = 0; + frame->num_subtitle_areas = 0; + frame->subtitle_areas = NULL; + frame->subtitle_pts = 0; + frame->subtitle_header = NULL; } static void free_side_data(AVFrameSideData **ptr_sd) @@ -240,23 +249,55 @@ static int get_audio_buffer(AVFrame *frame, int align) } +static int get_subtitle_buffer(AVFrame *frame) +{ + // Buffers in AVFrame->buf[] are not used in case of subtitle frames. + // To accomodate with existing code, checking ->buf[0] to determine + // whether a frame is ref-counted or has data, we're adding a 1-byte + // buffer here, which marks the subtitle frame to contain data. + frame->buf[0] = av_buffer_alloc(1); + if (!frame->buf[0]) { + av_frame_unref(frame); + return AVERROR(ENOMEM); + } + + frame->extended_data = frame->data; + + return 0; +} + int av_frame_get_buffer(AVFrame *frame, int align) +{ + if (frame->width > 0 && frame->height > 0) + frame->type = AVMEDIA_TYPE_VIDEO; + else if (frame->nb_samples > 0 && (frame->channel_layout || frame->channels > 0)) + frame->type = AVMEDIA_TYPE_AUDIO; + + return av_frame_get_buffer2(frame, align); +} + +int av_frame_get_buffer2(AVFrame *frame, int align) { if (frame->format < 0) return AVERROR(EINVAL); - if (frame->width > 0 && frame->height > 0) + switch(frame->type) { + case AVMEDIA_TYPE_VIDEO: return get_video_buffer(frame, align); - else if (frame->nb_samples > 0 && (frame->channel_layout || frame->channels > 0)) + case AVMEDIA_TYPE_AUDIO: return get_audio_buffer(frame, align); - - return AVERROR(EINVAL); + case AVMEDIA_TYPE_SUBTITLE: + return get_subtitle_buffer(frame); + default: + return AVERROR(EINVAL); + } } static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy) { int ret, i; + dst->type = src->type; dst->key_frame = src->key_frame; dst->pict_type = src->pict_type; dst->sample_aspect_ratio = src->sample_aspect_ratio; @@ -288,6 +329,12 @@ static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy) dst->colorspace = src->colorspace; dst->color_range = src->color_range; dst->chroma_location = src->chroma_location; + dst->subtitle_start_time = src->subtitle_start_time; + dst->subtitle_end_time = src->subtitle_end_time; + dst->subtitle_pts = src->subtitle_pts; + + if ((ret = av_buffer_replace(&dst->subtitle_header, src->subtitle_header)) < 0) + return ret; av_dict_copy(&dst->metadata, src->metadata, 0); @@ -329,6 +376,7 @@ int av_frame_ref(AVFrame *dst, const AVFrame *src) av_assert1(dst->width == 0 && dst->height == 0); av_assert1(dst->channels == 0); + dst->type = src->type; dst->format = src->format; dst->width = src->width; dst->height = src->height; @@ -342,7 +390,7 @@ int av_frame_ref(AVFrame *dst, const AVFrame *src) /* duplicate the frame data if it's not refcounted */ if (!src->buf[0]) { - ret = av_frame_get_buffer(dst, 0); + ret = av_frame_get_buffer2(dst, 0); if (ret < 0) goto fail; @@ -364,6 +412,10 @@ int av_frame_ref(AVFrame *dst, const AVFrame *src) } } + /* copy subtitle areas */ + if (src->type == AVMEDIA_TYPE_SUBTITLE) + frame_copy_subtitles(dst, src, 0); + if (src->extended_buf) { dst->extended_buf = av_calloc(src->nb_extended_buf, sizeof(*dst->extended_buf)); @@ -434,7 +486,7 @@ AVFrame *av_frame_clone(const AVFrame *src) void av_frame_unref(AVFrame *frame) { - int i; + unsigned i, n; if (!frame) return; @@ -453,6 +505,21 @@ void av_frame_unref(AVFrame *frame) av_buffer_unref(&frame->opaque_ref); av_buffer_unref(&frame->private_ref); + av_buffer_unref(&frame->subtitle_header); + + for (i = 0; i < frame->num_subtitle_areas; i++) { + AVSubtitleArea *area = frame->subtitle_areas[i]; + + for (n = 0; n < FF_ARRAY_ELEMS(area->buf); n++) + av_buffer_unref(&area->buf[n]); + + av_freep(&area->text); + av_freep(&area->ass); + av_free(area); + } + + av_freep(&frame->subtitle_areas); + if (frame->extended_data != frame->data) av_freep(&frame->extended_data); @@ -472,18 +539,28 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src) int av_frame_is_writable(AVFrame *frame) { - int i, ret = 1; + AVSubtitleArea *area; + int ret = 1; /* assume non-refcounted frames are not writable */ if (!frame->buf[0]) return 0; - for (i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++) + for (unsigned i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++) if (frame->buf[i]) ret &= !!av_buffer_is_writable(frame->buf[i]); - for (i = 0; i < frame->nb_extended_buf; i++) + for (unsigned i = 0; i < frame->nb_extended_buf; i++) ret &= !!av_buffer_is_writable(frame->extended_buf[i]); + for (unsigned i = 0; i < frame->num_subtitle_areas; i++) { + area = frame->subtitle_areas[i]; + if (area) { + for (unsigned n = 0; n < FF_ARRAY_ELEMS(area->buf); n++) + if (area->buf[n]) + ret &= !!av_buffer_is_writable(area->buf[n]); + } + } + return ret; } @@ -499,6 +576,7 @@ int av_frame_make_writable(AVFrame *frame) return 0; memset(&tmp, 0, sizeof(tmp)); + tmp.type = frame->type; tmp.format = frame->format; tmp.width = frame->width; tmp.height = frame->height; @@ -509,7 +587,7 @@ int av_frame_make_writable(AVFrame *frame) if (frame->hw_frames_ctx) ret = av_hwframe_get_buffer(frame->hw_frames_ctx, &tmp, 0); else - ret = av_frame_get_buffer(&tmp, 0); + ret = av_frame_get_buffer2(&tmp, 0); if (ret < 0) return ret; @@ -544,14 +622,22 @@ AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int plane) uint8_t *data; int planes, i; - if (frame->nb_samples) { - int channels = frame->channels; - if (!channels) - return NULL; - CHECK_CHANNELS_CONSISTENCY(frame); - planes = av_sample_fmt_is_planar(frame->format) ? channels : 1; - } else + switch(frame->type) { + case AVMEDIA_TYPE_VIDEO: planes = 4; + break; + case AVMEDIA_TYPE_AUDIO: + { + int channels = frame->channels; + if (!channels) + return NULL; + CHECK_CHANNELS_CONSISTENCY(frame); + planes = av_sample_fmt_is_planar(frame->format) ? channels : 1; + break; + } + default: + return NULL; + } if (plane < 0 || plane >= planes || !frame->extended_data[plane]) return NULL; @@ -675,17 +761,98 @@ static int frame_copy_audio(AVFrame *dst, const AVFrame *src) return 0; } +static int av_subtitle_area2area(AVSubtitleArea *dst, const AVSubtitleArea *src, int copy_data) +{ + dst->x = src->x; + dst->y = src->y; + dst->w = src->w; + dst->h = src->h; + dst->nb_colors = src->nb_colors; + dst->type = src->type; + dst->flags = src->flags; + + switch (dst->type) { + case AV_SUBTITLE_FMT_BITMAP: + + for (unsigned i = 0; i < AV_NUM_BUFFER_POINTERS; i++) { + if (src->h > 0 && src->w > 0 && src->buf[i]) { + dst->buf[0] = av_buffer_ref(src->buf[i]); + if (!dst->buf[i]) + return AVERROR(ENOMEM); + + if (copy_data) { + const int ret = av_buffer_make_writable(&dst->buf[i]); + if (ret < 0) + return ret; + } + + dst->linesize[i] = src->linesize[i]; + } + } + + memcpy(&dst->pal[0], &src->pal[0], sizeof(src->pal[0]) * 256); + + break; + case AV_SUBTITLE_FMT_TEXT: + + if (src->text) { + dst->text = av_strdup(src->text); + if (!dst->text) + return AVERROR(ENOMEM); + } + + break; + case AV_SUBTITLE_FMT_ASS: + + if (src->ass) { + dst->ass = av_strdup(src->ass); + if (!dst->ass) + return AVERROR(ENOMEM); + } + + break; + default: + + av_log(NULL, AV_LOG_ERROR, "Subtitle area has invalid format: %d", dst->type); + return AVERROR(ENOMEM); + } + + return 0; +} + +static int frame_copy_subtitles(AVFrame *dst, const AVFrame *src, int copy_data) +{ + dst->format = src->format; + + dst->num_subtitle_areas = src->num_subtitle_areas; + + if (src->num_subtitle_areas) { + dst->subtitle_areas = av_malloc_array(src->num_subtitle_areas, sizeof(AVSubtitleArea *)); + + for (unsigned i = 0; i < src->num_subtitle_areas; i++) { + dst->subtitle_areas[i] = av_mallocz(sizeof(AVSubtitleArea)); + av_subtitle_area2area(dst->subtitle_areas[i], src->subtitle_areas[i], copy_data); + } + } + + return 0; +} + int av_frame_copy(AVFrame *dst, const AVFrame *src) { if (dst->format != src->format || dst->format < 0) return AVERROR(EINVAL); - if (dst->width > 0 && dst->height > 0) + switch(dst->type) { + case AVMEDIA_TYPE_VIDEO: return frame_copy_video(dst, src); - else if (dst->nb_samples > 0 && dst->channels > 0) + case AVMEDIA_TYPE_AUDIO: return frame_copy_audio(dst, src); - - return AVERROR(EINVAL); + case AVMEDIA_TYPE_SUBTITLE: + return frame_copy_subtitles(dst, src, 1); + default: + return AVERROR(EINVAL); + } } void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type) diff --git a/libavutil/frame.h b/libavutil/frame.h index 3f295f6b9e..7162a67f92 100644 --- a/libavutil/frame.h +++ b/libavutil/frame.h @@ -34,6 +34,7 @@ #include "rational.h" #include "samplefmt.h" #include "pixfmt.h" +#include "subfmt.h" #include "version.h" @@ -278,7 +279,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 @@ -392,7 +393,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; @@ -674,6 +675,47 @@ typedef struct AVFrame { * for the target frame's private_ref field. */ AVBufferRef *private_ref; + + /** + * Media type of the frame (audio, video, subtitles..) + * + * See AVMEDIA_TYPE_xxx + */ + enum AVMediaType type; + + + /** + * Display start time, relative to packet pts, in ms. + */ + uint32_t subtitle_start_time; + + /** + * Display end time, relative to packet pts, in ms. + */ + uint32_t subtitle_end_time; + + /** + * Number of items in the @ref subtitle_areas array. + */ + unsigned num_subtitle_areas; + + /** + * Array of subtitle areas, may be empty. + */ + AVSubtitleArea **subtitle_areas; + + /** + * 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 subtitle_pts; + + /** + * Header containing style information for text subtitles. + */ + AVBufferRef *subtitle_header; } AVFrame; @@ -750,6 +792,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 @@ -769,9 +813,39 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src); * recommended to pass 0 here unless you know what you are doing. * * @return 0 on success, a negative AVERROR on error. + * + * @deprecated Use @ref av_frame_get_buffer2 instead and set @ref AVFrame.type + * before calling. */ +attribute_deprecated int av_frame_get_buffer(AVFrame *frame, int align); +/** + * Allocate new buffer(s) for audio, video or subtitle data. + * + * The following fields must be set on frame before calling this function: + * - format (pixel format for video, sample format for audio) + * - width and height for video + * - nb_samples and channel_layout for audio + * - type (AVMediaType) + * + * This function will fill AVFrame.data and AVFrame.buf arrays and, if + * necessary, allocate and fill AVFrame.extended_data and AVFrame.extended_buf. + * For planar formats, one buffer will be allocated for each plane. + * + * @warning: if frame already has been allocated, calling this function will + * leak memory. In addition, undefined behavior can occur in certain + * cases. + * + * @param frame frame in which to store the new buffers. + * @param align Required buffer size alignment. If equal to 0, alignment will be + * chosen automatically for the current CPU. It is highly + * recommended to pass 0 here unless you know what you are doing. + * + * @return 0 on success, a negative AVERROR on error. + */ +int av_frame_get_buffer2(AVFrame *frame, int align); + /** * Check if the frame data is writable. * diff --git a/libavutil/subfmt.c b/libavutil/subfmt.c new file mode 100644 index 0000000000..c72ebe2a43 --- /dev/null +++ b/libavutil/subfmt.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 softworkz + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "common.h" +#include "subfmt.h" + +static const char sub_fmt_info[AV_SUBTITLE_FMT_NB][24] = { + [AV_SUBTITLE_FMT_UNKNOWN] = "Unknown subtitle format", + [AV_SUBTITLE_FMT_BITMAP] = "Graphical subtitles", + [AV_SUBTITLE_FMT_TEXT] = "Text subtitles (plain)", + [AV_SUBTITLE_FMT_ASS] = "Text subtitles (ass)", +}; + +const char *av_get_subtitle_fmt_name(enum AVSubtitleType sub_fmt) +{ + if (sub_fmt < 0 || sub_fmt >= AV_SUBTITLE_FMT_NB) + return NULL; + return sub_fmt_info[sub_fmt]; +} + +enum AVSubtitleType av_get_subtitle_fmt(const char *name) +{ + for (int i = 0; i < AV_SUBTITLE_FMT_NB; i++) + if (!strcmp(sub_fmt_info[i], name)) + return i; + return AV_SUBTITLE_FMT_NONE; +} diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h index 791b45519f..3b8a0613b2 100644 --- a/libavutil/subfmt.h +++ b/libavutil/subfmt.h @@ -21,6 +21,9 @@ #ifndef AVUTIL_SUBFMT_H #define AVUTIL_SUBFMT_H +#include + +#include "buffer.h" #include "version.h" enum AVSubtitleType { @@ -65,4 +68,48 @@ enum AVSubtitleType { AV_SUBTITLE_FMT_NB, ///< number of subtitle formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions. }; +typedef struct AVSubtitleArea { +#define AV_NUM_BUFFER_POINTERS 1 + + enum AVSubtitleType type; + int flags; + + int x; ///< top left corner of area. + int y; ///< top left corner of area. + int w; ///< width of area. + int h; ///< height of area. + int nb_colors; ///< number of colors in bitmap palette (@ref pal). + + /** + * Buffers and line sizes for the bitmap of this subtitle. + * + * @{ + */ + AVBufferRef *buf[AV_NUM_BUFFER_POINTERS]; + int linesize[AV_NUM_BUFFER_POINTERS]; + /** + * @} + */ + + uint32_t pal[256]; ///< RGBA palette for the bitmap. + + char *text; ///< 0-terminated plain UTF-8 text + char *ass; ///< 0-terminated ASS/SSA compatible event line. + +} AVSubtitleArea; + +/** + * Return the name of sub_fmt, or NULL if sub_fmt is not + * recognized. + */ +const char *av_get_subtitle_fmt_name(enum AVSubtitleType sub_fmt); + +/** + * Return a subtitle format corresponding to name, or AV_SUBTITLE_FMT_NONE + * on error. + * + * @param name Subtitle format name. + */ +enum AVSubtitleType av_get_subtitle_fmt(const char *name); + #endif /* AVUTIL_SUBFMT_H */ From patchwork Fri Dec 10 21:37:14 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32269 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2373016iog; Fri, 10 Dec 2021 13:38:24 -0800 (PST) X-Google-Smtp-Source: ABdhPJzmf1ueZAgM6VqxxTRUEprUSypwueASd0Q94gflzzTkq9ZRDj69e6OlIJR8+++Fnqb9jH+W X-Received: by 2002:a17:906:58c9:: with SMTP id e9mr26573646ejs.181.1639172304416; Fri, 10 Dec 2021 13:38:24 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id y17si8015937edc.516.2021.12.10.13.38.24; Fri, 10 Dec 2021 13:38:24 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=feRHGNfE; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id EF95968AF57; Fri, 10 Dec 2021 23:37:27 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM02-DM3-obe.outbound.protection.outlook.com (mail-dm3nam07olkn2070.outbound.protection.outlook.com [40.92.43.70]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 61D6868AF32 for ; Fri, 10 Dec 2021 23:37:20 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=n/O8qn+hgmzLYnNW60xrT7mkS+WIqknXWtXjGVtoB97qhSOSoN/q+8BeIwpdOMflgrjGCEkoX7O9IFVUcH/GDKXNlwbgSwH86dqQc3qESDRPpI0GEQB2GcwNxCY3ZE7uA9pVyZIEw0yJPcwWHLfOQd8JK1abVPzwozwoIB6grGyAJUuhoA09i7RxC9GVrrQXjmjSrEyB+g2zixS8D6euKm4kwiFnLc8JYMDl6J+5CtlwEIurH+hf5D5ok0n7KR1aKD6qwJ7wgF4ou8nEMJOHsD1pyiLZe0V8Pd2hMOFAEaXe4oTcx46zaubVWqUFK2y1k4aAgCIMzs+1/NCvjnMFZA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=fl73EsMXZWGEE/K9mbh43rersk6cV6tCZKgPZVJiKgo=; b=JrOT1Q8fUdtby7Y+bxL7jL1x/FGJkzAD2NUQEs+jh9ix9HdYa/LJZCRGm4Ynp0ep4JHH/Ea4cGPdDERzsyXOK1c1TXFV2mjskAiPDBzyexbpCQOey1aWA/Dnf1RQUmAM67dtkMzJlmtzlgsmcHMyP/1ZXuAMUjGJZsrSQaM31GrIJ01iC8TKzhj/RVBzwbaN7tN3EopBgd234BnQJgF7TKh1aBW7pistVLB2UEKIFlJHgOuGil8XDSraYiIg4iMjrx9XDTD2looykAXfHjS0nP4JpTjYqYVQHSD7UlgyfQhEzQiSM2nxOaPUBQSZ6C0IjdcP5mTQ1+PGiYTff+MKVw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=fl73EsMXZWGEE/K9mbh43rersk6cV6tCZKgPZVJiKgo=; b=feRHGNfEDvRlveBJdQ2vPHwUcscnsAvtkekYGs78DLy+eSkm/951FUfV9BCHqN+3jUDssCFIDpnWfVYrqHCEiwz0Xx7iUlhSn2+MRQydPqf+YXwiB+OzzFqPWnyoD/YMXVPPt/Q5qtBQVwSnqqvwjScmcdywhgsA9VzthCyUNJzv1sdrR7fIa1fShn++q241IZqozoRFg/T6R8U1sMkWCM6COV2vxcTvFc2i300083svSjLDARucgt3tuTrFjhZl82dpIMGw93qs4IOOVgG9bLbmSYAwI0kDgsYJkVb+wY2X5NIaEdKwiV9f7JS03hyn4mnMYmx7l2A7SjxvFFClSA== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0240.NAMP223.PROD.OUTLOOK.COM (2603:10b6:5:316::23) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.11; Fri, 10 Dec 2021 21:37:14 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:14 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 03/21] avcodec/subtitles: Introduce new frame-based subtitle decoding API Thread-Index: AQHX7g4Y7lRQ0rfVG0erVv9Xv29Rbw== Date: Fri, 10 Dec 2021 21:37:14 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> In-Reply-To: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [MuSjVfL3IT6nC43b0bMwjhiOvoxELJEj] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 81444bbf-59a7-4338-13f5-08d9bc253b64 x-ms-traffictypediagnostic: DM8P223MB0240:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: nyni5MwsARAJsc4IBN5bmVKtuE+fpNUXbH3wpdslIB7y7p0Uw5aVPzB17ZpBSHkkQKKe4lrURANuajgYEw5NHxqD82/M5y3xxfY9dcH7BgWWvSzt7jTfzbJxIglW9cbuaHfHtynDc//TSrcYqlAhr6dsMDACMhXl2nCCFNFELqAux9/GFSnAyAlrTW7VJb6OOIRykIi6EwgY+78MCI+CIuk9SJ4Z1Q94ibhpjeK5qpwLMGFQn+OYF8p5rthez40u9LSBjN0uPk0/ipB3PAQ5kO3JKV8k16bWp7A76J7PmvFLRZhwxWcu76afxV4Dpv/r5Wdob8FYjpdMS/75F6HcO09Puy4WNYzVc2/nUq2Eu37m+lWFZxFS08ad+WMwObnYRA3MsA1lRPzQqS2IvTBp6LRtOARXqYRbvdD8FbSfTVN51AUTlyMyRLV9tF/0vLzh8HuQrgFYlGoOPWDRcA5icJfYvkVgPt3jIJwzfraIBjedwCXxeWzR1DgcjZjxHMQ0rSRqNUuvIxtLFX4SQ98pE71L/qwl3QDsFAqKZ8JMI5VuR3bimwbLdlqJ6vlk3S6LUesTR4agpMD8TySPrAOjvQ== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?qV4GsoNeZjRkwztIKsgnwHi18APw?= =?utf-8?q?OVgp+o4z/ZjJfCxXYdV4UoTOJWvPWsqjnbWT0bbB3I3uIXburBL+c2hRTLYS0csUA?= =?utf-8?q?JrrAYQbOrIx9zWV6JMFHgfOpFuxxs1xHf7uRGdcmSUUvO+KETF66PBQrro0UAyRS5?= =?utf-8?q?lWw5A+ZmSRHa0W2+Uvo0FxkZAbWayiafUMfZZM8TPvkJc1B5sMfflsXJMy0Gf3HPe?= =?utf-8?q?ZglEEInd+JQlljlAjjUJcG2VH62Zycppvlo0RY5PipLpIJjitFEE0AEdneukZPcuI?= =?utf-8?q?GFi37adw+xOXJN2lzqyQgi+P5iXYU0n+YnneeHPzRW8AG/LtWvaeKjKvw2lrBXHmf?= =?utf-8?q?6Gsgb1pCjRVRB/ynvx2x7MXUeTCgif+7BfRv4X3x6J2tdy2hXcbzqRs+NyxRjb8Wl?= =?utf-8?q?cd+RMI2wYQwH3t+JzIm91qIF6PUz9s4sIup+FtLaMjAvsQ8rlUzjCq4qkRNV61Inc?= =?utf-8?q?WIv9KWNJ4IvTFpbHsbcMkNTd9EdRBRZBouE5LnvLTobHSWColc61kqxrmxlSKN/QL?= =?utf-8?q?bpA/GRP5Or8M6N1XQ+R4VzHxZKmHFlINkNnKI+lA/3xpl4OOl/Gg6st6MH6IARr2/?= =?utf-8?q?4/IDJ9NhTQAzYgtS6qJC0oCkViLovq0VdEzLoPIiKpP1MfxbZDwT+BCVlgsr2utB/?= =?utf-8?q?FDISIUS4eW7OPOJnrZImRQUr8M/77KQJ0u+LRkHC6qjxz3Ep6BeRPc+mcscPcej3J?= =?utf-8?q?hgqmN0SM8K1o6ZdKzxX1zoJrEVU/eV1LYx+C70ExH8j91QJRLtzsOXZl2P5QGNNhZ?= =?utf-8?q?z3sjPs5H5lBWLAto3QtNltEleGMLi/9hqcBkW0/ukwMlABOPRu9lv25tDSCAg1a8n?= =?utf-8?q?2mBijMrs+MAj7QJeQaJpIguxZ6hnV3Tz1WBFOIpJyKODJ0sVmLOIMzwYdpd1qVcnY?= =?utf-8?q?My4Qwl75bgncaTN7IUjbHfjLzuzHHfjUR+bhsqdGE4fg=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 81444bbf-59a7-4338-13f5-08d9bc253b64 X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:14.5434 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0240 Subject: [FFmpeg-devel] [PATCH v23 03/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: z+NOph9SVGvM - Add avcodec_decode_subtitle3 which takes subtitle frames, serving as compatibility shim to legacy subtitle decoding - Add additional methods for conversion between old and new API Signed-off-by: softworkz --- libavcodec/avcodec.h | 8 +- libavcodec/codec_desc.c | 11 +++ libavcodec/codec_desc.h | 8 ++ libavcodec/decode.c | 54 ++++++++++-- libavcodec/internal.h | 16 ++++ libavcodec/utils.c | 182 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 270 insertions(+), 9 deletions(-) diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index b05c12e47e..3e734d3003 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -1675,7 +1675,7 @@ typedef struct AVCodecContext { /** * Header containing style information for text subtitles. - * For SUBTITLE_ASS subtitle type, it should contain the whole ASS + * For AV_SUBTITLE_FMT_ASS subtitle type, it should contain the whole ASS * [Script Info] and [V4+ Styles] section, plus the [Events] line and * the Format line following. It shouldn't include any Dialogue line. * - encoding: Set/allocated/freed by user (before avcodec_open2()) @@ -2417,7 +2417,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); /** @@ -2510,7 +2513,10 @@ enum AVChromaLocation avcodec_chroma_pos_to_enum(int xpos, int ypos); * must be freed with avsubtitle_free if *got_sub_ptr is set. * @param[in,out] got_sub_ptr Zero if no subtitle could be decompressed, otherwise, it is nonzero. * @param[in] avpkt The input AVPacket containing the input buffer. + * + * @deprecated Use the new decode API (avcodec_send_packet, avcodec_receive_frame) instead. */ +attribute_deprecated int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, int *got_sub_ptr, AVPacket *avpkt); diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 0974ee03de..e48e4532ba 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -3548,3 +3548,14 @@ enum AVMediaType avcodec_get_type(enum AVCodecID codec_id) const AVCodecDescriptor *desc = avcodec_descriptor_get(codec_id); return desc ? desc->type : AVMEDIA_TYPE_UNKNOWN; } + +enum AVSubtitleType avcodec_descriptor_get_subtitle_format(const AVCodecDescriptor *codec_descriptor) +{ + if(codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB) + return AV_SUBTITLE_FMT_BITMAP; + + if(codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB) + return AV_SUBTITLE_FMT_ASS; + + return AV_SUBTITLE_FMT_UNKNOWN; +} diff --git a/libavcodec/codec_desc.h b/libavcodec/codec_desc.h index 126b52df47..ba68d24e0e 100644 --- a/libavcodec/codec_desc.h +++ b/libavcodec/codec_desc.h @@ -121,6 +121,14 @@ const AVCodecDescriptor *avcodec_descriptor_next(const AVCodecDescriptor *prev); */ const AVCodecDescriptor *avcodec_descriptor_get_by_name(const char *name); +/** + * Return subtitle format from a codec descriptor + * + * @param codec_descriptor codec descriptor + * @return the subtitle type (e.g. bitmap, text) + */ +enum AVSubtitleType avcodec_descriptor_get_subtitle_format(const AVCodecDescriptor *codec_descriptor); + /** * @} */ diff --git a/libavcodec/decode.c b/libavcodec/decode.c index 52bf5dcd33..ac267f0df6 100644 --- a/libavcodec/decode.c +++ b/libavcodec/decode.c @@ -576,6 +576,37 @@ 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->pkt_dts = avpkt->dts; + } + + avsubtitle_free(&subtitle); + + return ret; +} + int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt) { AVCodecInternal *avci = avctx->internal; @@ -590,6 +621,9 @@ int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacke if (avpkt && !avpkt->size && avpkt->data) return AVERROR(EINVAL); + if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) + return decode_subtitle_shim(avctx, avci->buffer_frame, avpkt); + av_packet_unref(avci->buffer_pkt); if (avpkt && (avpkt->data || avpkt->side_data_elems)) { ret = av_packet_ref(avci->buffer_pkt, avpkt); @@ -651,7 +685,9 @@ int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *fr if (avci->buffer_frame->buf[0]) { av_frame_move_ref(frame, avci->buffer_frame); - } else { + } else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) + return AVERROR(EAGAIN); + else { ret = decode_receive_frame_internal(avctx, frame); if (ret < 0) return ret; @@ -802,9 +838,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; @@ -844,10 +879,7 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, avctx->pkt_timebase, ms); } - if (avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB) - sub->format = 0; - else if (avctx->codec_descriptor->props & AV_CODEC_PROP_TEXT_SUB) - sub->format = 1; + sub->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor); for (unsigned i = 0; i < sub->num_rects; i++) { if (avctx->sub_charenc_mode != FF_SUB_CHARENC_MODE_IGNORE && @@ -871,6 +903,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 a62f8dbd4e..10443ed2d1 100644 --- a/libavcodec/internal.h +++ b/libavcodec/internal.h @@ -363,6 +363,22 @@ int ff_int_from_list_or_default(void *ctx, const char * val_name, int val, void ff_dvdsub_parse_palette(uint32_t *palette, const char *p); +/** + * Copies subtitle data from AVSubtitle to AVFrame. + * + * @deprecated This is a compatibility method for interoperability with + * the legacy subtitle API. + */ +int ff_frame_put_subtitle(AVFrame* frame, const AVSubtitle* sub); + +/** + * Copies subtitle data from AVFrame to AVSubtitle. + * + * @deprecated This is a compatibility method for interoperability with + * the legacy subtitle API. + */ +int ff_frame_get_subtitle(AVSubtitle* sub, AVFrame* frame); + #if defined(_WIN32) && CONFIG_SHARED && !defined(BUILDING_avcodec) # define av_export_avcodec __declspec(dllimport) #else diff --git a/libavcodec/utils.c b/libavcodec/utils.c index a91a54b0dc..613c580c19 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -824,6 +824,188 @@ int av_get_audio_frame_duration(AVCodecContext *avctx, int frame_bytes) return FFMAX(0, duration); } +static int subtitle_area2rect(AVSubtitleRect *dst, const AVSubtitleArea *src) +{ + dst->x = src->x; + dst->y = src->y; + dst->w = src->w; + dst->h = src->h; + dst->nb_colors = src->nb_colors; + dst->type = src->type; + dst->flags = src->flags; + + switch (dst->type) { + case AV_SUBTITLE_FMT_BITMAP: + + if (src->h > 0 && src->w > 0 && src->buf[0]) { + uint32_t *pal; + AVBufferRef *buf = src->buf[0]; + dst->data[0] = av_mallocz(buf->size); + memcpy(dst->data[0], buf->data, buf->size); + dst->linesize[0] = src->linesize[0]; + + dst->data[1] = av_mallocz(256 * 4); + pal = (uint32_t *)dst->data[1]; + + for (unsigned i = 0; i < 256; i++) { + pal[i] = src->pal[i]; + } + } + + break; + case AV_SUBTITLE_FMT_TEXT: + + if (src->text) + dst->text = av_strdup(src->text); + else + dst->text = av_strdup(""); + + if (!dst->text) + return AVERROR(ENOMEM); + + break; + case AV_SUBTITLE_FMT_ASS: + + if (src->ass) + dst->ass = av_strdup(src->ass); + else + dst->ass = av_strdup(""); + + if (!dst->ass) + return AVERROR(ENOMEM); + + break; + default: + + av_log(NULL, AV_LOG_ERROR, "Subtitle rect has invalid format: %d", dst->type); + return AVERROR(EINVAL); + } + + return 0; +} + +static int subtitle_rect2area(AVSubtitleArea *dst, const AVSubtitleRect *src) +{ + dst->x = src->x; + dst->y = src->y; + dst->w = src->w; + dst->h = src->h; + dst->nb_colors = src->nb_colors; + dst->type = src->type; + dst->flags = src->flags; + + switch (dst->type) { + case AV_SUBTITLE_FMT_BITMAP: + + if (src->h > 0 && src->w > 0 && src->data[0]) { + AVBufferRef *buf = av_buffer_allocz(src->h * src->linesize[0]); + memcpy(buf->data, src->data[0], buf->size); + + dst->buf[0] = buf; + dst->linesize[0] = src->linesize[0]; + } + + if (src->data[1]) { + uint32_t *pal = (uint32_t *)src->data[1]; + + for (unsigned i = 0; i < 256; i++) { + dst->pal[i] = pal[i]; + } + } + + break; + case AV_SUBTITLE_FMT_TEXT: + + if (src->text) { + dst->text = av_strdup(src->text); + if (!dst->text) + return AVERROR(ENOMEM); + } + + break; + case AV_SUBTITLE_FMT_ASS: + + if (src->ass) { + dst->ass = av_strdup(src->ass); + if (!dst->ass) + return AVERROR(ENOMEM); + } + + break; + default: + + av_log(NULL, AV_LOG_ERROR, "Subtitle area has invalid format: %d", dst->type); + return AVERROR(EINVAL); + } + + return 0; +} + +/** + * Copies subtitle data from AVSubtitle (deprecated) to AVFrame + * + * @note This is a compatibility method for conversion to the legacy API + */ +int ff_frame_put_subtitle(AVFrame *frame, const AVSubtitle *sub) +{ + frame->format = sub->format; + frame->subtitle_start_time = sub->start_display_time; + frame->subtitle_end_time = sub->end_display_time; + frame->subtitle_pts = sub->pts; + + 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) +{ + sub->start_display_time = frame->subtitle_start_time; + sub->end_display_time = frame->subtitle_end_time; + sub->pts = frame->subtitle_pts; + + if (frame->num_subtitle_areas) { + sub->rects = av_malloc_array(frame->num_subtitle_areas, sizeof(AVSubtitleRect*)); + if (!sub->rects) + return AVERROR(ENOMEM); + + for (unsigned i = 0; i < frame->num_subtitle_areas; i++) { + int ret; + sub->rects[i] = av_mallocz(sizeof(AVSubtitleRect)); + ret = subtitle_area2rect(sub->rects[i], frame->subtitle_areas[i]); + if (ret < 0) { + sub->num_rects = i; + return ret; + } + } + } + + sub->num_rects = frame->num_subtitle_areas; + return 0; +} + int av_get_audio_frame_duration2(AVCodecParameters *par, int frame_bytes) { int duration = get_audio_frame_duration(par->codec_id, par->sample_rate, From patchwork Fri Dec 10 21:37:15 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32266 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2372505iog; Fri, 10 Dec 2021 13:37:50 -0800 (PST) X-Google-Smtp-Source: ABdhPJwExMAVvX03cB6YcpYME6inBYDuZqezLh92uaUaYGpxGYUTlnJq2DX4q4R3pr0dgQxRHdLO X-Received: by 2002:a05:6402:34d:: with SMTP id r13mr41672129edw.208.1639172270336; Fri, 10 Dec 2021 13:37:50 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id z21si8867810edd.365.2021.12.10.13.37.49; Fri, 10 Dec 2021 13:37:50 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=klKmXwJK; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id CDEFC68AF3B; Fri, 10 Dec 2021 23:37:24 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM04-MW2-obe.outbound.protection.outlook.com (mail-mw2nam08olkn2073.outbound.protection.outlook.com [40.92.46.73]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5925B68AF22 for ; Fri, 10 Dec 2021 23:37:18 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=QLr0UsJOjen073oPksCDwbNMvgeUIxg4pDsFKlVhV6kIjcEVH6Sn0+4k7NGN8WMcoTW5ZdAX1zYBcar0QgOP4AGTJPfyccfHj4/NpY7AXJsTRUIoLvjrASs2uCyahPkR0W87t1riwylWDzyutji6MUts29yrvNSGrauzOTay/vzs2OJggjFNkJs6ryC0FdCH/ccxLbaDdkGG1x6UG+VWFMsknzRHeWnmpoiy7mxzqgZQFoVrK46gw54rFB8VQ7qpjWjjRF9aSSvLwspWZL9zMg7Esqa5yueaiPgW3JP9GMNUNtc0ao5Nq/JecUCqaHCq8aNPN7W/E+crANLVRRywHw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=AoI/EGlNnnc++ofMlMGwlI1pqDtONuV76wSE4f+/6Rs=; b=S1b1H6KBqP/r5Uuxd4P2qkvcsFD3WNdthIiDaCQHIoy4RixIWbvTh9GJKs/rcqyLSyH9/iMoylviojpmCzJrYpxLMoViTct81tjyt1DKF7aI25bu+l6SaFd9Aw5OPTE9Q05nSwv6mrD8G0KVz9ydi7LkTSJ8KpxNmRNhUH/NCsFlz6nM3K168oL5h1ADnFQIU18mMR5UygMAH5HmMjjpweYCzFEJAsXhNbuZxvV4ukiEnA1r8xCeJSdRm5C9Ew2BpqWwGrVnkSsfdmPVLEoIIH1iUyYSeflxEDlVq3m4Wplw7cmL0PRbBEPNxPUfX2C/kLI80GRwpFQjXdntLCvQEQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=AoI/EGlNnnc++ofMlMGwlI1pqDtONuV76wSE4f+/6Rs=; b=klKmXwJKlGafnUfcuZo143PDGcRMGiJv/ZTrQMimcBSVYZfHoqGFMyl2JdQXsAE6zli4tsmpQu2eKG5pFAAFKt3Xqbjze3HU0/GPfTAVqs6ml0R/rVqmovsvMC2R//GqADEVBb/HE7VfHndPHXQlIskp845DMRV9h5nAJ+9sRhCQZkjv7kPJ357fkGvkQ9oeciCi8WrGILFvvKj/CdJ0xiTS7JTta6iE8XMdlBwcp63JNe93JWi+T5DUCSQydGGFGT9gdi4mtuHlh8njT6JdrHLOgZqubiz/afc58E+VgO3LpvlN13mvejMp8P/gya2DKyk5LXU/gmPbhzyVI986Cg== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0397.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:15 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:15 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 04/21] avfilter/subtitles: Update vf_subtitles to use new decoding api Thread-Index: AQHX7g4ZrAdOWje2s0OY1e6pxX81hg== Date: Fri, 10 Dec 2021 21:37:15 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> In-Reply-To: <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [8nFNf0/dl2H27ktYUgVvseLtbr51/YYH] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: adbdeb40-af44-433c-1748-08d9bc253c1f x-ms-traffictypediagnostic: DM8P223MB0397:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: /hZjJjRKmstmHmSfx9/T9smMZcA43rN8JxM07LLergn8NFCDJafOY3snPK1x9MLAgaWukimia4vkHwcz14XZGimjQFYatWq++2BfSrhNqqxlixSOqxSunrM/aRV8SE7nZXLBRb2rJSRI1JLcwowH+BKYg5DpKSf2x76Q/RrrS3SkcZNy8TaCH+QM1iywZ+hKTdYuTulw9L68bbju8n/VWcTTiM0vmed081LDrOVlKzq5Xe41jU1duRuX96mRirf71KpyHGpFxiipamXFGDtWx5dYKLivh30LSLwMzVx4cuz2UpleMw308lGhSsuVkd7eBltR92xTXgEi2nmEo9Ax1nrgS3hfz9VUYoY1IiCUPe0QbogMz/scfBOOHtucXg/UbHka0kko2qSQTelc2MwhvV/WTPP/wNeb2dVK7G2/UkHX7B8zIG7rp5iw/shrZ6jy+oJYGj7E4XLrFnXM9Q/Jq5BQY3GqeBXBQSD9yVJPHh9K5pkh/zsW9GbujXeqr4GATiwBGnpjMvc8ihWZP41bqgnqOvz13z6W8yNJXoL22raFS0A3np56jjO51/oKWh3HNQNcZ3/53jn1k2xjgusR3Q== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?L8bgQeOV9CoCk56Y3ZDAHEthI1sm?= =?utf-8?q?urd2OLimQjXyr5HkqcVmCNadH+QR+HQSCNUoxWsdn84zggaW5o5ZWZJAGCEODr2zl?= =?utf-8?q?O9FU68u4FFiEBJwOGLvUG6HuSvAyc/84jzt6YA6HGV5rt9KjGdt6Au//mz/0N32Qs?= =?utf-8?q?qBf6L/e8jXaWhM8MbnUZgRaEKSCrWzI8qHb6CZd+flr3CDC7kZIXcbrneFsRTYhe3?= =?utf-8?q?M231oa5CpyLbkNBhUIoNiwWkXPK2LfXrFKFdt4k1akcfXSLYGRsr24cTMGFFYYMdg?= =?utf-8?q?mPAFyHq+y4yolRFlXHpiBwRFeGeH7hbJ6gI40HsTaydKjdUzacpCfKVORAqF5Xmo/?= =?utf-8?q?TWgz3aeVg3TkaTejvpkYhLY6kCasw/wO/HeX2WrLddZPjkZe+Hg91gBfJbDvxet3N?= =?utf-8?q?w5VJZEAIIAC9jMHdgAcfgs9HGQIsx0rewDeQOvw2M/apeU9RIOpI22Il/rAx39GJ4?= =?utf-8?q?IliTAajovQfLhv0qljiqMOMgqD74hKczFsY62utUHarNO4lDhLzlPhAH13dYyP37K?= =?utf-8?q?ma6hVdi2AjBvxQ4C+ik6DLaFln89VlsKsZNCgwUy2EYrqeOeLgyVjHdgmyRW5U53Z?= =?utf-8?q?eopt7tMU9yMuFqxwZFQv7awoU4DO6M0GlvXCpfSKng4HLe8S7OH2cRL+cjfkfd/JL?= =?utf-8?q?7DlzqHL0sYm9xiJ2TPoyURttha2vNnzgj7AUZDPeyv0z2XJ7ql+1yaGF7GBfRjeUS?= =?utf-8?q?t+FMJ+YVNYOvp5Egb8pVOpN2PLK9d8ntuX5fgAJuK4mfD2s/rLZSp/28dr8h9np7x?= =?utf-8?q?sUhCDNOV5dcbeNbyH6ITxwoHHE9oaJWBfG9Y98squB3q8Rq5kfTc7kMlCKgW7alMB?= =?utf-8?q?7wRPSLZrjJqp1pEjEJhnCLWxY6AxrWGr8ml7vCnYMUSKVrlx895pauXcJQ5+GHPCj?= =?utf-8?q?IM/TJXT5ACuIoQi1/AGl4dF2xmUnUYyu/wheT+3Wkn2g=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: adbdeb40-af44-433c-1748-08d9bc253c1f X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:15.7520 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0397 Subject: [FFmpeg-devel] [PATCH v23 04/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: Km2ruVw/BDI0 Signed-off-by: softworkz --- libavfilter/vf_subtitles.c | 54 +++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/libavfilter/vf_subtitles.c b/libavfilter/vf_subtitles.c index 3fc4eeb63d..66b2082894 100644 --- a/libavfilter/vf_subtitles.c +++ b/libavfilter/vf_subtitles.c @@ -35,14 +35,12 @@ # include "libavformat/avformat.h" #endif #include "libavutil/avstring.h" -#include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "drawutils.h" #include "avfilter.h" #include "internal.h" #include "formats.h" -#include "video.h" typedef struct AssContext { const AVClass *class; @@ -292,6 +290,29 @@ static int attachment_is_font(AVStream * st) return 0; } +static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt) +{ + int ret; + + *got_frame = 0; + + if (pkt) { + ret = avcodec_send_packet(avctx, pkt); + // In particular, we don't expect AVERROR(EAGAIN), because we read all + // decoded frames with avcodec_receive_frame() until done. + if (ret < 0 && ret != AVERROR_EOF) + return ret; + } + + ret = avcodec_receive_frame(avctx, frame); + if (ret < 0 && ret != AVERROR(EAGAIN)) + return ret; + if (ret >= 0) + *got_frame = 1; + + return 0; +} + AVFILTER_DEFINE_CLASS(subtitles); static av_cold int init_subtitles(AVFilterContext *ctx) @@ -306,6 +327,7 @@ static av_cold int init_subtitles(AVFilterContext *ctx) AVStream *st; AVPacket pkt; AssContext *ass = ctx->priv; + enum AVSubtitleType subtitle_format; /* Init libass */ ret = init(ctx); @@ -386,13 +408,17 @@ static av_cold int init_subtitles(AVFilterContext *ctx) ret = AVERROR_DECODER_NOT_FOUND; goto end; } + dec_desc = avcodec_descriptor_get(st->codecpar->codec_id); - if (dec_desc && !(dec_desc->props & AV_CODEC_PROP_TEXT_SUB)) { + subtitle_format = avcodec_descriptor_get_subtitle_format(dec_desc); + + if (subtitle_format != AV_SUBTITLE_FMT_ASS) { av_log(ctx, AV_LOG_ERROR, - "Only text based subtitles are currently supported\n"); - ret = AVERROR_PATCHWELCOME; + "Only text based subtitles are supported by this filter\n"); + ret = AVERROR_INVALIDDATA; goto end; } + if (ass->charenc) av_dict_set(&codec_opts, "sub_charenc", ass->charenc, 0); @@ -448,18 +474,22 @@ 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_pts, AV_TIME_BASE_Q, av_make_q(1, 1000)); + const int64_t duration = sub->subtitle_end_time; + for (i = 0; i < sub->num_subtitle_areas; i++) { + char *ass_line = sub->subtitle_areas[i]->ass; if (!ass_line) break; ass_process_chunk(ass->track, ass_line, strlen(ass_line), @@ -468,7 +498,7 @@ static av_cold int init_subtitles(AVFilterContext *ctx) } } av_packet_unref(&pkt); - avsubtitle_free(&sub); + av_frame_free(&sub); } end: From patchwork Fri Dec 10 21:37:17 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32267 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2372638iog; Fri, 10 Dec 2021 13:38:00 -0800 (PST) X-Google-Smtp-Source: ABdhPJxKVvV4ob2xnw1Oah2B+LzZ2IZ+4Sa1RaTmSGSb91MwELKNcr5nyxJshZpRTP8smMBMAGiR X-Received: by 2002:a17:907:2cc5:: with SMTP id hg5mr26787659ejc.138.1639172280501; Fri, 10 Dec 2021 13:38:00 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id j21si5900200edp.4.2021.12.10.13.38.00; Fri, 10 Dec 2021 13:38:00 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=pubUENXS; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id EC4BC68AF48; Fri, 10 Dec 2021 23:37:25 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM04-MW2-obe.outbound.protection.outlook.com (mail-mw2nam08olkn2073.outbound.protection.outlook.com [40.92.46.73]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D3E9768AF1E for ; Fri, 10 Dec 2021 23:37:23 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=kbqkO9F95bpavOsThmcj5X3M6EnMvVH2nJ2gGQ+B6QiGslwnBVmaTv7zhuKwU/pBOdUX9gNlkSVJlAJo/0d4uXXlQIrm3B4fGBhsYlBPDmQXNPvCakozVFUa14I0mwubMeRxWkI6xVzuj+lGK9sFJqcADGF5m6lc4HgYqRFeDbsC6fp0wmy6NAPvkVYyAPX35AB0ziqlf1rvNC+bZGKuz7L+iatWtjCTloiFKJM22EF6IiW/Nblea4iqiqRq+Kqg/U62oV7EtMWQA5BK9IrzAoG51D7Hka/LN85OidQOKsK7p213TekZW5aqyfnJQsz/LAsjrwzj4c5o+4ObB40eiA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=mlsZ7gbpFc4LO1X7wvArKAYHpZO9jqi6fJDiha5gmcw=; b=HS6W5Q4FXmxUf9TOGtIyAj/4QGeTrw92AieLvETyS9MiNmHMSiu+lMW1bqpaU0LUNAzJpFhQ0qJe0TZsou+SrrVP8JBfBlU31mrnXAuh4h8cQJk8q+s0B9YHuRRPf5ec4IozvZlXEBML8Iu42lInrc/291qoQ0IWRYTr8vnEXoWQx31lrPcEgkFnYPPkOlvWVrdm77KCaYjOoY2aPgd6dGBSBK3HS4Wdyt/fkR69PVYdoNY7Mnxx6oRIG/JgMlnsJaJ0mTDXfNMZaBtq/1o2cWrJpADi/2IBsgqLBJrDntAGZMATAsyCwSSoV5SHlBpVVQHwpGuycI4CYSNviHCMrw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=mlsZ7gbpFc4LO1X7wvArKAYHpZO9jqi6fJDiha5gmcw=; b=pubUENXSQjXDV1ef040R8FNYPWb0ncnUJJ1Mq5KE/ETh5xZsfBHocv45gnvx8TC9bzWTBy02BhvadQEeEa+Nq7LXIQZ7PUvxlB/UDK90xEv9dNYHOZ4PQxUVkVgH9S1v/G1iPfzD1GF9/mQuSyHQwftC+dxxEAQ66/rlxL9D5t7awVeBScDSWxZ7Q7nZAUuMp0G8AXAgbjw+5iYPQYwA4F4vB2NOVXfnbfiXuKf9lBi73y1BwuNJjooXkEgIbaiiBH6SogM680EwggeLH8ttAq6BJDvR98nIw/XmsF0vyPDgHEqaIhvZ5ALpzLfCljL9ac2Wmi261fwCQ58G23xIJQ== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0397.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:17 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:17 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 05/21] avcodec,avutil: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing Thread-Index: AQHX7g4a8ZLuzFmz1kqVi44meF18WQ== Date: Fri, 10 Dec 2021 21:37:17 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> In-Reply-To: <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [paO3ezjAzADLQmoCyypYpS0ofL7t3CiW] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: a14f93a7-1e5d-4222-752f-08d9bc253d31 x-ms-traffictypediagnostic: DM8P223MB0397:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: sQEOQoNYkVoRu9IKhUg9ebDwlBwr30A3HzGhGrD5ZnwVAvlzIYsXXphc9zEIFkjgbsXBOIW09pb9fHO9vJp8REUFCAPG/KQqVgpnbwWSiAXlCIsto2RM64h0RHaS1cTGS0QRttVGean+IhEpuDrV0rkXu5Q+0GVGFGTUbGpOeiEVN/8aupujsKhab7rrDKMkapHtzg1q329iT+Yr0unt62VW55Ta1PN7rAoRlBCisyVCpwXTu4IirwHzyJWRfIeSO4ol1blRF4waeLM4f5D7OGJOj3KF8JfNh3zA64cf5s4YrFKvZVmE8C4D1WGMyc0SAkOiy+p/7mwWnjEfXLQ+mNuWKYBR0M8aLg6eAqea5MUafUTmCBz9+3obYl6Z6B6sovaAfSjnIdFW4jF7/IbCXvxHTzan2/Wb7JgGk4t3T4zbXblr4lP+ZXIgG193yhPyUA4VyCSUc/CE8emPkGw7gExC6G88JccEwyGHabk6m7SKpmMlWpVGeNbtoc9g7s08fRkx32oq3i0eJlxhCfMYy2wPpd+tHE9peigmPEw3CddHzDGlPIkPHIPyBeWrybwRj2X/0o9mxAoLxAjr08Zrlg== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?zl+4fY/VhguT2xaQkH6LGWONCUYR?= =?utf-8?q?DByGQrUUwtGm4Q3iPdtOfgBcIQZ7bM/gdXIFGn1ECFxodlyvfXSlrs98sa7c+a7x6?= =?utf-8?q?/ajRlSE9HDpsvxLuy1gxLI6zrbr3OLT4xw96rewcKcQ35kQ1Vx8JSWenh6GAI6DdD?= =?utf-8?q?+yuoJGDqt+LfbyLMhv2zvdenlgv094kqfL1+0IiTPkiWK+Qsc4rWGrhbbBmXo5B2P?= =?utf-8?q?K88rSfbHqww8l3TI+s1kINoH8S5Ws3Sl9GRImybLNMRyzrRnjJHOaBRoUJWqBBZp5?= =?utf-8?q?QM4jHHQwCXop62HBNqrW/Z0vdpwxfcE3qMIpJwMEY8poBIenUUizXzkDA9Qvv4kIW?= =?utf-8?q?1no5m7t+c64EWsIXqfui1bNLHk+ikcNlgGvLNKg7g1kmR98dZLd0JkjydUqwy92v5?= =?utf-8?q?Yf+0fmPaVRuIkJP9kK5unrxXSk2eGdGuadE4GSrQhUvxw9BdcZqEqTQVqaDaGtr53?= =?utf-8?q?SiX6kOo9DsdDCSlHkaYrqBGiEfynhJ9vfbTV5HjMIm4jTlvJ/dtBD0XWBxWTqLer3?= =?utf-8?q?TgTMBuO0c0DJo7puxlD8mCTXgv1QK5tqEgtC2FnqObWXor5ZGDAPDTm0/eCIVBqTR?= =?utf-8?q?7U0JkJ1GbfsLY3JxwzCTlYAoAh+dQLjQ62HbP9sjeMCHMIS6BFzyIOeghz48FouuF?= =?utf-8?q?5bmbV336jGoGhzMTZFW7wBgk4owdan0BeFfgdfrpxr8dOVkjC6m2jHyPRPwIj2UoD?= =?utf-8?q?pHWgiOSgynaZDGYmp9agIocqJ9XKUYgE1ICREq3So/pLh6IjVhxXk5bxl6cNt7AeG?= =?utf-8?q?4l0yoyHDh1zJP63aQcHnoircYNzSU0QFuOovVHzXkkCJdbWHO50Rn4SpBpkNHGZfN?= =?utf-8?q?pXTPShS2VhtXsnV2PBgNbu57m0BqLYIreCyGs7MoM75YfCpjB9nafZwTbb10k8AXn?= =?utf-8?q?yijKaVoZYu68kBq73tjFO9VYvmfHJ4OXbw8yHf2UAhmw=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: a14f93a7-1e5d-4222-752f-08d9bc253d31 X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:17.4884 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0397 Subject: [FFmpeg-devel] [PATCH v23 05/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: SfqcJTIP7cSv Also add - hard_space callback (for upcoming fix) - extensible callback (for future extension) Signed-off-by: softworkz --- libavcodec/Makefile | 56 +++---- libavcodec/ass.h | 147 ++++++------------ libavcodec/assdec.c | 2 +- libavcodec/assenc.c | 2 +- libavcodec/ccaption_dec.c | 19 +-- libavcodec/jacosubdec.c | 2 +- libavcodec/libaribb24.c | 2 +- libavcodec/libzvbi-teletextdec.c | 14 +- libavcodec/microdvddec.c | 7 +- libavcodec/movtextdec.c | 3 +- libavcodec/movtextenc.c | 20 +-- libavcodec/mpl2dec.c | 2 +- libavcodec/realtextdec.c | 2 +- libavcodec/samidec.c | 2 +- libavcodec/srtdec.c | 2 +- libavcodec/srtenc.c | 16 +- libavcodec/subviewerdec.c | 2 +- libavcodec/textdec.c | 4 +- libavcodec/ttmlenc.c | 15 +- libavcodec/webvttdec.c | 2 +- libavcodec/webvttenc.c | 16 +- libavutil/Makefile | 2 + {libavcodec => libavutil}/ass.c | 87 ++++------- libavutil/ass_internal.h | 133 ++++++++++++++++ {libavcodec => libavutil}/ass_split.c | 30 ++-- .../ass_split_internal.h | 32 ++-- 26 files changed, 347 insertions(+), 274 deletions(-) rename {libavcodec => libavutil}/ass.c (65%) create mode 100644 libavutil/ass_internal.h rename {libavcodec => libavutil}/ass_split.c (94%) rename libavcodec/ass_split.h => libavutil/ass_split_internal.h (86%) diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 4122a9b144..b12638da5e 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -209,10 +209,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 @@ -253,7 +253,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 @@ -425,7 +425,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 \ @@ -447,7 +447,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 @@ -462,8 +462,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 @@ -502,7 +502,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 @@ -555,7 +555,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 @@ -587,7 +587,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 @@ -602,7 +602,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 @@ -637,13 +637,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 @@ -653,8 +653,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 @@ -674,7 +674,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 @@ -729,15 +729,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 @@ -1023,7 +1023,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 @@ -1074,7 +1074,7 @@ OBJS-$(CONFIG_LIBX265_ENCODER) += libx265.o OBJS-$(CONFIG_LIBXAVS_ENCODER) += libxavs.o OBJS-$(CONFIG_LIBXAVS2_ENCODER) += libxavs2.o OBJS-$(CONFIG_LIBXVID_ENCODER) += libxvid.o -OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER) += libzvbi-teletextdec.o ass.o +OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER) += libzvbi-teletextdec.o # parsers OBJS-$(CONFIG_AAC_LATM_PARSER) += latm_parser.o diff --git a/libavcodec/ass.h b/libavcodec/ass.h index 2c260e4e78..9c9cb01c8a 100644 --- a/libavcodec/ass.h +++ b/libavcodec/ass.h @@ -1,6 +1,5 @@ /* - * SSA/ASS common functions - * Copyright (c) 2010 Aurelien Jacobs + * Copyright (c) 2021 The FFmpeg Project * * This file is part of FFmpeg. * @@ -23,117 +22,73 @@ #define AVCODEC_ASS_H #include "avcodec.h" -#include "libavutil/bprint.h" - -#define ASS_DEFAULT_PLAYRESX 384 -#define ASS_DEFAULT_PLAYRESY 288 - -/** - * @name Default values for ASS style - * @{ - */ -#define ASS_DEFAULT_FONT "Arial" -#define ASS_DEFAULT_FONT_SIZE 16 -#define ASS_DEFAULT_COLOR 0xffffff -#define ASS_DEFAULT_BACK_COLOR 0 -#define ASS_DEFAULT_BOLD 0 -#define ASS_DEFAULT_ITALIC 0 -#define ASS_DEFAULT_UNDERLINE 0 -#define ASS_DEFAULT_ALIGNMENT 2 -#define ASS_DEFAULT_BORDERSTYLE 1 -/** @} */ +#include "libavutil/ass_internal.h" typedef struct FFASSDecoderContext { int readorder; } FFASSDecoderContext; -/** - * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS. - * Can specify all fields explicitly - * - * @param avctx pointer to the AVCodecContext - * @param play_res_x subtitle frame width - * @param play_res_y subtitle frame height - * @param font name of the default font face to use - * @param font_size default font size to use - * @param primary_color default text color to use (ABGR) - * @param secondary_color default secondary text color to use (ABGR) - * @param outline_color default outline color to use (ABGR) - * @param back_color default background color to use (ABGR) - * @param bold 1 for bold text, 0 for normal text - * @param italic 1 for italic text, 0 for normal text - * @param underline 1 for underline text, 0 for normal text - * @param border_style 1 for outline, 3 for opaque box - * @param alignment position of the text (left, center, top...), defined after - * the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top) - * @return >= 0 on success otherwise an error code <0 - */ -int ff_ass_subtitle_header_full(AVCodecContext *avctx, +static inline int ff_ass_subtitle_header_full(AVCodecContext *avctx, int play_res_x, int play_res_y, const char *font, int font_size, int primary_color, int secondary_color, int outline_color, int back_color, int bold, int italic, int underline, - int border_style, int alignment); -/** - * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS. - * - * @param avctx pointer to the AVCodecContext - * @param font name of the default font face to use - * @param font_size default font size to use - * @param color default text color to use (ABGR) - * @param back_color default background color to use (ABGR) - * @param bold 1 for bold text, 0 for normal text - * @param italic 1 for italic text, 0 for normal text - * @param underline 1 for underline text, 0 for normal text - * @param alignment position of the text (left, center, top...), defined after - * the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top) - * @return >= 0 on success otherwise an error code <0 - */ -int ff_ass_subtitle_header(AVCodecContext *avctx, - const char *font, int font_size, - int color, int back_color, - int bold, int italic, int underline, - int border_style, int alignment); + int border_style, int alignment) +{ + avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_full( + play_res_x, play_res_y, font, font_size, + primary_color, secondary_color, outline_color, + back_color, bold,italic,underline,border_style,alignment, + !(avctx->flags & AV_CODEC_FLAG_BITEXACT)); -/** - * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS - * with default style. - * - * @param avctx pointer to the AVCodecContext - * @return >= 0 on success otherwise an error code <0 - */ -int ff_ass_subtitle_header_default(AVCodecContext *avctx); + if (!avctx->subtitle_header) + return AVERROR(ENOMEM); + avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header); + return 0; +} -/** - * Craft an ASS dialog string. - */ -char *ff_ass_get_dialog(int readorder, int layer, const char *style, - const char *speaker, const char *text); +static inline int ff_ass_subtitle_header_default(AVCodecContext *avctx) +{ + avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_default(!(avctx->flags & AV_CODEC_FLAG_BITEXACT)); + + if (!avctx->subtitle_header) + return AVERROR(ENOMEM); + avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header); + return 0; +} + +static inline void ff_ass_decoder_flush(AVCodecContext *avctx) +{ + FFASSDecoderContext *s = avctx->priv_data; + if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP)) + s->readorder = 0; +} /** * Add an ASS dialog to a subtitle. */ -int ff_ass_add_rect(AVSubtitle *sub, const char *dialog, +static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog, int readorder, int layer, const char *style, - const char *speaker); + const char *speaker) +{ + char *ass_str; + AVSubtitleRect **rects; -/** - * Helper to flush a text subtitles decoder making use of the - * FFASSDecoderContext. - */ -void ff_ass_decoder_flush(AVCodecContext *avctx); + rects = av_realloc_array(sub->rects, sub->num_rects+1, sizeof(*sub->rects)); + if (!rects) + return AVERROR(ENOMEM); + sub->rects = rects; + rects[sub->num_rects] = av_mallocz(sizeof(*rects[0])); + if (!rects[sub->num_rects]) + return AVERROR(ENOMEM); + rects[sub->num_rects]->type = SUBTITLE_ASS; + ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog); + if (!ass_str) + return AVERROR(ENOMEM); + rects[sub->num_rects]->ass = ass_str; + sub->num_rects++; + return 0; +} -/** - * Escape a text subtitle using ASS syntax into an AVBPrint buffer. - * Newline characters will be escaped to \N. - * - * @param buf pointer to an initialized AVBPrint buffer - * @param p source text - * @param size size of the source text - * @param linebreaks additional newline chars, which will be escaped to \N - * @param keep_ass_markup braces and backslash will not be escaped if set - */ -void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size, - const char *linebreaks, int keep_ass_markup); #endif /* AVCODEC_ASS_H */ diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c index 319279490c..7802a44e71 100644 --- a/libavcodec/assdec.c +++ b/libavcodec/assdec.c @@ -22,7 +22,7 @@ #include #include "avcodec.h" -#include "ass.h" +#include "libavutil/ass_internal.h" #include "internal.h" #include "libavutil/internal.h" #include "libavutil/mem.h" diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c index a6d107ded2..b0e475834b 100644 --- a/libavcodec/assenc.c +++ b/libavcodec/assenc.c @@ -22,7 +22,7 @@ #include #include "avcodec.h" -#include "ass.h" +#include "libavutil/ass_internal.h" #include "internal.h" #include "libavutil/avstring.h" #include "libavutil/internal.h" diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c index 27c61527f6..27eef75657 100644 --- a/libavcodec/ccaption_dec.c +++ b/libavcodec/ccaption_dec.c @@ -272,15 +272,12 @@ static av_cold int init_decoder(AVCodecContext *avctx) ctx->bg_color = CCCOL_BLACK; ctx->rollup = 2; ctx->cursor_row = 10; - ret = ff_ass_subtitle_header(avctx, "Monospace", + ret = ff_ass_subtitle_header_full(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, "Monospace", ASS_DEFAULT_FONT_SIZE, - ASS_DEFAULT_COLOR, - ASS_DEFAULT_BACK_COLOR, - ASS_DEFAULT_BOLD, - ASS_DEFAULT_ITALIC, - ASS_DEFAULT_UNDERLINE, - 3, - ASS_DEFAULT_ALIGNMENT); + ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR, + ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, + ASS_DEFAULT_BOLD, ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE, + 3, ASS_DEFAULT_ALIGNMENT); if (ret < 0) { return ret; } @@ -886,7 +883,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp AV_TIME_BASE_Q, ms_tb); else sub->end_display_time = -1; - ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL); if (ret < 0) return ret; ctx->last_real_time = sub->pts; @@ -896,7 +893,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp if (!bptr && !ctx->real_time && ctx->buffer[!ctx->buffer_index].str[0]) { bidx = !ctx->buffer_index; - ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL); if (ret < 0) return ret; sub->pts = ctx->buffer_time[1]; @@ -914,7 +911,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp capture_screen(ctx); ctx->buffer_changed = 0; - ret = ff_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL); if (ret < 0) return ret; sub->end_display_time = -1; diff --git a/libavcodec/jacosubdec.c b/libavcodec/jacosubdec.c index 698895a86b..6a53ec3e34 100644 --- a/libavcodec/jacosubdec.c +++ b/libavcodec/jacosubdec.c @@ -183,7 +183,7 @@ static int jacosub_decode_frame(AVCodecContext *avctx, av_bprint_init(&buffer, JSS_MAX_LINESIZE, JSS_MAX_LINESIZE); jacosub_to_ass(avctx, &buffer, ptr); - ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buffer, NULL); if (ret < 0) return ret; diff --git a/libavcodec/libaribb24.c b/libavcodec/libaribb24.c index 0766c0079d..3fb7e5f16e 100644 --- a/libavcodec/libaribb24.c +++ b/libavcodec/libaribb24.c @@ -273,7 +273,7 @@ next_region: av_log(avctx, AV_LOG_DEBUG, "Styled ASS line: %s\n", buf.str); - ret = ff_ass_add_rect(sub, buf.str, b24->read_order++, + ret = avpriv_ass_add_rect(sub, buf.str, b24->read_order++, 0, NULL, NULL); } diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c index 1073d6a0bd..bd9edc34d7 100644 --- a/libavcodec/libzvbi-teletextdec.c +++ b/libavcodec/libzvbi-teletextdec.c @@ -152,12 +152,12 @@ static char *create_ass_text(TeletextContext *ctx, const char *text) AVBPrint buf; av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); - ff_ass_bprint_text_event(&buf, text, strlen(text), "", 0); + avpriv_ass_bprint_text_event(&buf, text, strlen(text), "", 0); if (!av_bprint_is_complete(&buf)) { av_bprint_finalize(&buf, NULL); return NULL; } - dialog = ff_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str); + dialog = avpriv_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str); av_bprint_finalize(&buf, NULL); return dialog; } @@ -224,7 +224,7 @@ static int gen_sub_text(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page } av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass); } else { - sub_rect->type = SUBTITLE_NONE; + sub_rect->type = AV_SUBTITLE_FMT_NONE; } av_bprint_finalize(&buf, NULL); return 0; @@ -394,7 +394,7 @@ static int gen_sub_ass(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page if (buf.len) { sub_rect->type = SUBTITLE_ASS; - sub_rect->ass = ff_ass_get_dialog(ctx->readorder++, 0, is_subtitle_page ? "Subtitle" : "Teletext", NULL, buf.str); + sub_rect->ass = avpriv_ass_get_dialog(ctx->readorder++, 0, is_subtitle_page ? "Subtitle" : "Teletext", NULL, buf.str); if (!sub_rect->ass) { av_bprint_finalize(&buf, NULL); @@ -402,7 +402,7 @@ static int gen_sub_ass(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page } av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass); } else { - sub_rect->type = SUBTITLE_NONE; + sub_rect->type = AV_SUBTITLE_FMT_NONE; } av_bprint_finalize(&buf, NULL); return 0; @@ -462,7 +462,7 @@ static int gen_sub_bitmap(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_pa if (vc >= vcend) { av_log(ctx, AV_LOG_DEBUG, "dropping empty page %3x\n", page->pgno); - sub_rect->type = SUBTITLE_NONE; + sub_rect->type = AV_SUBTITLE_FMT_NONE; return 0; } @@ -695,7 +695,7 @@ static int teletext_decode_frame(AVCodecContext *avctx, void *data, int *got_sub sub->num_rects = 0; sub->pts = ctx->pages->pts; - if (ctx->pages->sub_rect->type != SUBTITLE_NONE) { + if (ctx->pages->sub_rect->type != AV_SUBTITLE_FMT_NONE) { sub->rects = av_malloc(sizeof(*sub->rects)); if (sub->rects) { sub->num_rects = 1; diff --git a/libavcodec/microdvddec.c b/libavcodec/microdvddec.c index c45fe043bf..fc09db8997 100644 --- a/libavcodec/microdvddec.c +++ b/libavcodec/microdvddec.c @@ -310,7 +310,7 @@ static int microdvd_decode_frame(AVCodecContext *avctx, } } if (new_line.len) { - int ret = ff_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL); + int ret = avpriv_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&new_line, NULL); if (ret < 0) return ret; @@ -363,8 +363,9 @@ static int microdvd_init(AVCodecContext *avctx) } } } - return ff_ass_subtitle_header(avctx, font_buf.str, font_size, color, - ASS_DEFAULT_BACK_COLOR, bold, italic, + return ff_ass_subtitle_header_full(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, + font_buf.str, font_size, color, color, + ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, bold, italic, underline, ASS_DEFAULT_BORDERSTYLE, alignment); } diff --git a/libavcodec/movtextdec.c b/libavcodec/movtextdec.c index b0c54bf1d0..96a0598206 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" @@ -561,7 +560,7 @@ static int mov_text_decode_frame(AVCodecContext *avctx, } else text_to_ass(&buf, ptr, end, avctx); - ret = ff_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL); av_bprint_finalize(&buf, NULL); if (ret < 0) return ret; diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c index 5869942ec0..5b23deeedd 100644 --- a/libavcodec/movtextenc.c +++ b/libavcodec/movtextenc.c @@ -26,8 +26,8 @@ #include "libavutil/intreadwrite.h" #include "libavutil/mem.h" #include "libavutil/common.h" -#include "ass_split.h" -#include "ass.h" +#include "libavutil/ass_split_internal.h" +#include "libavutil/ass_internal.h" #include "bytestream.h" #include "internal.h" @@ -167,7 +167,7 @@ static int mov_text_encode_close(AVCodecContext *avctx) { MovTextContext *s = avctx->priv_data; - ff_ass_split_free(s->ass_ctx); + avpriv_ass_split_free(s->ass_ctx); av_freep(&s->style_attributes); av_freep(&s->fonts); av_bprint_finalize(&s->buffer, NULL); @@ -222,7 +222,7 @@ static int encode_sample_description(AVCodecContext *avctx) else s->font_scale_factor = 1; - style = ff_ass_style_get(s->ass_ctx, "Default"); + style = avpriv_ass_style_get(s->ass_ctx, "Default"); if (!style && ass->styles_count) { style = &ass->styles[0]; } @@ -329,7 +329,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx) av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); - s->ass_ctx = ff_ass_split(avctx->subtitle_header); + s->ass_ctx = avpriv_ass_split(avctx->subtitle_header); if (!s->ass_ctx) return AVERROR_INVALIDDATA; ret = encode_sample_description(avctx); @@ -564,7 +564,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); @@ -578,7 +578,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); } @@ -650,12 +650,12 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, return AVERROR(EINVAL); } - dialog = ff_ass_split_dialog(s->ass_ctx, ass); + dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); if (!dialog) return AVERROR(ENOMEM); mov_text_dialog(s, dialog); - ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text); - ff_ass_free_dialog(&dialog); + avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text); + avpriv_ass_free_dialog(&dialog); } if (s->buffer.len > UINT16_MAX) diff --git a/libavcodec/mpl2dec.c b/libavcodec/mpl2dec.c index 61e47050ec..fe806b4927 100644 --- a/libavcodec/mpl2dec.c +++ b/libavcodec/mpl2dec.c @@ -74,7 +74,7 @@ static int mpl2_decode_frame(AVCodecContext *avctx, void *data, av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); if (ptr && avpkt->size > 0 && *ptr && !mpl2_event_to_ass(&buf, ptr)) - ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buf, NULL); if (ret < 0) return ret; diff --git a/libavcodec/realtextdec.c b/libavcodec/realtextdec.c index 11b586d493..acd548b6b5 100644 --- a/libavcodec/realtextdec.c +++ b/libavcodec/realtextdec.c @@ -67,7 +67,7 @@ static int realtext_decode_frame(AVCodecContext *avctx, av_bprint_init(&buf, 0, 4096); if (ptr && avpkt->size > 0 && !rt_event_to_ass(&buf, ptr)) - ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buf, NULL); if (ret < 0) return ret; diff --git a/libavcodec/samidec.c b/libavcodec/samidec.c index 32d07447b4..1249b629d6 100644 --- a/libavcodec/samidec.c +++ b/libavcodec/samidec.c @@ -144,7 +144,7 @@ static int sami_decode_frame(AVCodecContext *avctx, if (ret < 0) return ret; // TODO: pass escaped sami->encoded_source.str as source - ret = ff_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL); if (ret < 0) return ret; } diff --git a/libavcodec/srtdec.c b/libavcodec/srtdec.c index 4f16226b83..847352eb37 100644 --- a/libavcodec/srtdec.c +++ b/libavcodec/srtdec.c @@ -78,7 +78,7 @@ static int srt_decode_frame(AVCodecContext *avctx, ret = srt_to_ass(avctx, &buffer, avpkt->data, x1, y1, x2, y2); if (ret >= 0) - ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buffer, NULL); if (ret < 0) return ret; diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c index 2e3ac55770..a7c5fccefe 100644 --- a/libavcodec/srtenc.c +++ b/libavcodec/srtenc.c @@ -23,8 +23,8 @@ #include "avcodec.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" -#include "ass_split.h" -#include "ass.h" +#include "libavutil/ass_split_internal.h" +#include "libavutil/ass_internal.h" #include "internal.h" @@ -94,7 +94,7 @@ static void srt_stack_push_pop(SRTContext *s, const char c, int close) static void srt_style_apply(SRTContext *s, const char *style) { - ASSStyle *st = ff_ass_style_get(s->ass_ctx, style); + ASSStyle *st = avpriv_ass_style_get(s->ass_ctx, style); if (st) { int c = st->primary_color & 0xFFFFFF; if (st->font_name && strcmp(st->font_name, ASS_DEFAULT_FONT) || @@ -135,7 +135,7 @@ static av_cold int srt_encode_init(AVCodecContext *avctx) { SRTContext *s = avctx->priv_data; s->avctx = avctx; - s->ass_ctx = ff_ass_split(avctx->subtitle_header); + s->ass_ctx = avpriv_ass_split(avctx->subtitle_header); av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); return s->ass_ctx ? 0 : AVERROR_INVALIDDATA; } @@ -245,14 +245,14 @@ static int encode_frame(AVCodecContext *avctx, return AVERROR(EINVAL); } - dialog = ff_ass_split_dialog(s->ass_ctx, ass); + dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); if (!dialog) return AVERROR(ENOMEM); s->alignment_applied = 0; if (avctx->codec_id == AV_CODEC_ID_SUBRIP) srt_style_apply(s, dialog->style); - ff_ass_split_override_codes(cb, s, dialog->text); - ff_ass_free_dialog(&dialog); + avpriv_ass_split_override_codes(cb, s, dialog->text); + avpriv_ass_free_dialog(&dialog); } if (!av_bprint_is_complete(&s->buffer)) @@ -284,7 +284,7 @@ static int text_encode_frame(AVCodecContext *avctx, static int srt_encode_close(AVCodecContext *avctx) { SRTContext *s = avctx->priv_data; - ff_ass_split_free(s->ass_ctx); + avpriv_ass_split_free(s->ass_ctx); av_bprint_finalize(&s->buffer, NULL); return 0; } diff --git a/libavcodec/subviewerdec.c b/libavcodec/subviewerdec.c index 5c650d0cde..04e9ae7c09 100644 --- a/libavcodec/subviewerdec.c +++ b/libavcodec/subviewerdec.c @@ -58,7 +58,7 @@ static int subviewer_decode_frame(AVCodecContext *avctx, av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); if (ptr && avpkt->size > 0 && !subviewer_event_to_ass(&buf, ptr)) - ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buf, NULL); if (ret < 0) return ret; diff --git a/libavcodec/textdec.c b/libavcodec/textdec.c index 308553660a..7556deefe8 100644 --- a/libavcodec/textdec.c +++ b/libavcodec/textdec.c @@ -54,8 +54,8 @@ static int text_decode_frame(AVCodecContext *avctx, void *data, av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); if (ptr && avpkt->size > 0 && *ptr) { - ff_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup); - ret = ff_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL); + avpriv_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup); + ret = avpriv_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL); } av_bprint_finalize(&buf, NULL); if (ret < 0) diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c index ad2eddfdd5..083f2dd67a 100644 --- a/libavcodec/ttmlenc.c +++ b/libavcodec/ttmlenc.c @@ -32,8 +32,7 @@ #include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/internal.h" -#include "ass_split.h" -#include "ass.h" +#include "libavutil/ass_split_internal.h" #include "ttmlenc.h" typedef struct { @@ -95,7 +94,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, return AVERROR(EINVAL); } - dialog = ff_ass_split_dialog(s->ass_ctx, ass); + dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); if (!dialog) return AVERROR(ENOMEM); @@ -107,7 +106,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, av_bprintf(&s->buffer, "\">"); } - ret = ff_ass_split_override_codes(&ttml_callbacks, s, dialog->text); + ret = avpriv_ass_split_override_codes(&ttml_callbacks, s, dialog->text); if (ret < 0) { int log_level = (ret != AVERROR_INVALIDDATA || avctx->err_recognition & AV_EF_EXPLODE) ? @@ -118,7 +117,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, av_err2str(ret)); if (log_level == AV_LOG_ERROR) { - ff_ass_free_dialog(&dialog); + avpriv_ass_free_dialog(&dialog); return ret; } } @@ -126,7 +125,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, if (dialog->style) av_bprintf(&s->buffer, ""); - ff_ass_free_dialog(&dialog); + avpriv_ass_free_dialog(&dialog); } if (!av_bprint_is_complete(&s->buffer)) @@ -148,7 +147,7 @@ static av_cold int ttml_encode_close(AVCodecContext *avctx) { TTMLContext *s = avctx->priv_data; - ff_ass_split_free(s->ass_ctx); + avpriv_ass_split_free(s->ass_ctx); av_bprint_finalize(&s->buffer, NULL); @@ -372,7 +371,7 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx) av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); - if (!(s->ass_ctx = ff_ass_split(avctx->subtitle_header))) { + if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) { return AVERROR_INVALIDDATA; } diff --git a/libavcodec/webvttdec.c b/libavcodec/webvttdec.c index 0093f328fa..d304edc705 100644 --- a/libavcodec/webvttdec.c +++ b/libavcodec/webvttdec.c @@ -91,7 +91,7 @@ static int webvtt_decode_frame(AVCodecContext *avctx, av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); if (ptr && avpkt->size > 0 && !webvtt_event_to_ass(&buf, ptr)) - ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); + ret = avpriv_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buf, NULL); if (ret < 0) return ret; diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c index 89b49e42bf..761099b69a 100644 --- a/libavcodec/webvttenc.c +++ b/libavcodec/webvttenc.c @@ -24,8 +24,8 @@ #include "avcodec.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" -#include "ass_split.h" -#include "ass.h" +#include "libavutil/ass_split_internal.h" +#include "libavutil/ass_internal.h" #include "internal.h" #define WEBVTT_STACK_SIZE 64 @@ -93,7 +93,7 @@ static void webvtt_stack_push_pop(WebVTTContext *s, const char c, int close) static void webvtt_style_apply(WebVTTContext *s, const char *style) { - ASSStyle *st = ff_ass_style_get(s->ass_ctx, style); + ASSStyle *st = avpriv_ass_style_get(s->ass_ctx, style); if (st) { if (st->bold != ASS_DEFAULT_BOLD) { webvtt_print(s, ""); @@ -172,12 +172,12 @@ static int webvtt_encode_frame(AVCodecContext *avctx, return AVERROR(EINVAL); } - dialog = ff_ass_split_dialog(s->ass_ctx, ass); + dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); if (!dialog) return AVERROR(ENOMEM); webvtt_style_apply(s, dialog->style); - ff_ass_split_override_codes(&webvtt_callbacks, s, dialog->text); - ff_ass_free_dialog(&dialog); + avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text); + avpriv_ass_free_dialog(&dialog); } if (!av_bprint_is_complete(&s->buffer)) @@ -197,7 +197,7 @@ static int webvtt_encode_frame(AVCodecContext *avctx, static int webvtt_encode_close(AVCodecContext *avctx) { WebVTTContext *s = avctx->priv_data; - ff_ass_split_free(s->ass_ctx); + avpriv_ass_split_free(s->ass_ctx); av_bprint_finalize(&s->buffer, NULL); return 0; } @@ -206,7 +206,7 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx) { WebVTTContext *s = avctx->priv_data; s->avctx = avctx; - s->ass_ctx = ff_ass_split(avctx->subtitle_header); + s->ass_ctx = avpriv_ass_split(avctx->subtitle_header); av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); return s->ass_ctx ? 0 : AVERROR_INVALIDDATA; } diff --git a/libavutil/Makefile b/libavutil/Makefile index 7e79936876..b81d8e0d35 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -101,6 +101,8 @@ BUILT_HEADERS = avconfig.h \ OBJS = adler32.o \ aes.o \ aes_ctr.o \ + ass.o \ + ass_split.o \ audio_fifo.o \ avstring.o \ avsscanf.o \ diff --git a/libavcodec/ass.c b/libavutil/ass.c similarity index 65% rename from libavcodec/ass.c rename to libavutil/ass.c index 907e2d7b88..d494de5fe2 100644 --- a/libavcodec/ass.c +++ b/libavutil/ass.c @@ -19,21 +19,22 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "avcodec.h" -#include "ass.h" +#include "ass_internal.h" + +#include "subfmt.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/common.h" -int ff_ass_subtitle_header_full(AVCodecContext *avctx, - int play_res_x, int play_res_y, - const char *font, int font_size, - int primary_color, int secondary_color, - int outline_color, int back_color, - int bold, int italic, int underline, - int border_style, int alignment) +char* avpriv_ass_get_subtitle_header_full(int play_res_x, int play_res_y, + const char *font, int font_size, + int primary_color, int secondary_color, + int outline_color, int back_color, + int bold, int italic, int underline, + int border_style, int alignment, + int print_av_version) { - avctx->subtitle_header = av_asprintf( + char* header = av_asprintf( "[Script Info]\r\n" "; Script generated by FFmpeg/Lavc%s\r\n" "ScriptType: v4.00+\r\n" @@ -68,34 +69,31 @@ int ff_ass_subtitle_header_full(AVCodecContext *avctx, "\r\n" "[Events]\r\n" "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n", - !(avctx->flags & AV_CODEC_FLAG_BITEXACT) ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "", + print_av_version ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "", play_res_x, play_res_y, font, font_size, primary_color, secondary_color, outline_color, back_color, -bold, -italic, -underline, border_style, alignment); - if (!avctx->subtitle_header) - return AVERROR(ENOMEM); - avctx->subtitle_header_size = strlen(avctx->subtitle_header); - return 0; + return header; } -int ff_ass_subtitle_header(AVCodecContext *avctx, - const char *font, int font_size, +char* avpriv_ass_get_subtitle_header(const char *font, int font_size, int color, int back_color, int bold, int italic, int underline, - int border_style, int alignment) + int border_style, int alignment, + int print_av_version) { - return ff_ass_subtitle_header_full(avctx, - ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, - font, font_size, color, color, - back_color, back_color, - bold, italic, underline, - border_style, alignment); + return avpriv_ass_get_subtitle_header_full(ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, + font, font_size, color, color, + back_color, back_color, + bold, italic, underline, + border_style, alignment, + print_av_version); } -int ff_ass_subtitle_header_default(AVCodecContext *avctx) +char* avpriv_ass_get_subtitle_header_default(int print_av_version) { - return ff_ass_subtitle_header(avctx, ASS_DEFAULT_FONT, + return avpriv_ass_get_subtitle_header(ASS_DEFAULT_FONT, ASS_DEFAULT_FONT_SIZE, ASS_DEFAULT_COLOR, ASS_DEFAULT_BACK_COLOR, @@ -103,10 +101,11 @@ int ff_ass_subtitle_header_default(AVCodecContext *avctx) ASS_DEFAULT_ITALIC, ASS_DEFAULT_UNDERLINE, ASS_DEFAULT_BORDERSTYLE, - ASS_DEFAULT_ALIGNMENT); + ASS_DEFAULT_ALIGNMENT, + print_av_version); } -char *ff_ass_get_dialog(int readorder, int layer, const char *style, +char *avpriv_ass_get_dialog(int readorder, int layer, const char *style, const char *speaker, const char *text) { return av_asprintf("%d,%d,%s,%s,0,0,0,,%s", @@ -114,37 +113,7 @@ char *ff_ass_get_dialog(int readorder, int layer, const char *style, speaker ? speaker : "", text); } -int ff_ass_add_rect(AVSubtitle *sub, const char *dialog, - int readorder, int layer, const char *style, - const char *speaker) -{ - char *ass_str; - AVSubtitleRect **rects; - - 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 = ff_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; -} - -void ff_ass_decoder_flush(AVCodecContext *avctx) -{ - FFASSDecoderContext *s = avctx->priv_data; - if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP)) - s->readorder = 0; -} - -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..833766eada --- /dev/null +++ b/libavutil/ass_internal.h @@ -0,0 +1,133 @@ +/* + * 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); + + +/////** +//// * Helper to flush a text subtitles decoder making use of the +//// * FFASSDecoderContext. +//// */ +////void ff_ass_decoder_flush(AVCodecContext *avctx); + +/** + * Escape a text subtitle using ASS syntax into an AVBPrint buffer. + * Newline characters will be escaped to \N. + * + * @param buf pointer to an initialized AVBPrint buffer + * @param p source text + * @param size size of the source text + * @param linebreaks additional newline chars, which will be escaped to \N + * @param keep_ass_markup braces and backslash will not be escaped if set + */ +void avpriv_ass_bprint_text_event(AVBPrint *buf, const char *p, int size, + const char *linebreaks, int keep_ass_markup); +#endif /* AVUTIL_ASS_INTERNAL_H */ diff --git a/libavcodec/ass_split.c b/libavutil/ass_split.c similarity index 94% rename from libavcodec/ass_split.c rename to libavutil/ass_split.c index 05c5453e53..c5963351fc 100644 --- a/libavcodec/ass_split.c +++ b/libavutil/ass_split.c @@ -22,7 +22,7 @@ #include "libavutil/common.h" #include "libavutil/error.h" #include "libavutil/mem.h" -#include "ass_split.h" +#include "ass_split_internal.h" typedef enum { ASS_STR, @@ -373,7 +373,7 @@ static int ass_split(ASSSplitContext *ctx, const char *buf) return buf ? 0 : AVERROR_INVALIDDATA; } -ASSSplitContext *ff_ass_split(const char *buf) +ASSSplitContext *avpriv_ass_split(const char *buf) { ASSSplitContext *ctx = av_mallocz(sizeof(*ctx)); if (!ctx) @@ -382,7 +382,7 @@ ASSSplitContext *ff_ass_split(const char *buf) buf += 3; ctx->current_section = -1; if (ass_split(ctx, buf) < 0) { - ff_ass_split_free(ctx); + avpriv_ass_split_free(ctx); return NULL; } return ctx; @@ -412,7 +412,7 @@ static void free_section(ASSSplitContext *ctx, const ASSSection *section) av_freep((uint8_t *)&ctx->ass + section->offset); } -void ff_ass_free_dialog(ASSDialog **dialogp) +void avpriv_ass_free_dialog(ASSDialog **dialogp) { ASSDialog *dialog = *dialogp; if (!dialog) @@ -424,7 +424,7 @@ void ff_ass_free_dialog(ASSDialog **dialogp) av_freep(dialogp); } -ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf) +ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf) { int i; static const ASSFields fields[] = { @@ -451,7 +451,7 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf) buf = skip_space(buf); len = last ? strlen(buf) : strcspn(buf, ","); if (len >= INT_MAX) { - ff_ass_free_dialog(&dialog); + avpriv_ass_free_dialog(&dialog); return NULL; } convert_func[type](ptr, buf, len); @@ -461,7 +461,7 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf) return dialog; } -void ff_ass_split_free(ASSSplitContext *ctx) +void avpriv_ass_split_free(ASSSplitContext *ctx) { if (ctx) { int i; @@ -474,7 +474,7 @@ void ff_ass_split_free(ASSSplitContext *ctx) } -int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, +int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, const char *buf) { const char *text = NULL; @@ -497,8 +497,8 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, while (*buf == '\\') { char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0}; unsigned int color = 0xFFFFFFFF; - int len, size = -1, an = -1, alpha = -1; - int x1, y1, x2, y2, t1 = -1, t2 = -1; + int len, size = -1, an = -1, alpha = -1, scale = 0; + int x1, y1, x2, y2, t1 = -1, t2 = -1, accel = 1; if (sscanf(buf, "\\%1[bisu]%1[01\\}]%n", style, c, &len) > 1) { int close = c[0] == '0' ? 1 : c[0] == '1' ? 0 : -1; len += close != -1; @@ -546,6 +546,14 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, } else if (sscanf(buf, "\\org(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) { if (callbacks->origin) callbacks->origin(priv, x1, y1); + } else if (sscanf(buf, "\\t(%d,%d,%1[\\}]%n", &t1, &t2, sep, &len) > 2 || + sscanf(buf, "\\t(%d,%d,%d,%1[\\}]%n", &t1, &t2, &accel, sep, &len) > 3) { + if (callbacks->animate) + callbacks->animate(priv, t1, t2, accel, tmp); + } else if (sscanf(buf, "\\p%1[\\}]%n", sep, &len) > 0 || + sscanf(buf, "\\p%u%1[\\}]%n", &scale, sep, &len) > 1) { + if (callbacks->drawing_mode) + callbacks->drawing_mode(priv, scale); } else { len = strcspn(buf+1, "\\}") + 2; /* skip unknown code */ } @@ -569,7 +577,7 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, return 0; } -ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style) +ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style) { ASS *ass = &ctx->ass; int i; diff --git a/libavcodec/ass_split.h b/libavutil/ass_split_internal.h similarity index 86% rename from libavcodec/ass_split.h rename to libavutil/ass_split_internal.h index a45fb9b8a1..eee49ef0f5 100644 --- a/libavcodec/ass_split.h +++ b/libavutil/ass_split_internal.h @@ -19,8 +19,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef AVCODEC_ASS_SPLIT_H -#define AVCODEC_ASS_SPLIT_H +#ifndef AVUTIL_ASS_SPLIT_INTERNAL_H +#define AVUTIL_ASS_SPLIT_INTERNAL_H /** * fields extracted from the [Script Info] section @@ -81,7 +81,7 @@ typedef struct { char *effect; char *text; /**< actual text which will be displayed as a subtitle, can include style override control codes (see - ff_ass_split_override_codes()) */ + avpriv_ass_split_override_codes()) */ } ASSDialog; /** @@ -107,12 +107,12 @@ typedef struct ASSSplitContext ASSSplitContext; * @param buf String containing the ASS formatted data. * @return Newly allocated struct containing split data. */ -ASSSplitContext *ff_ass_split(const char *buf); +ASSSplitContext *avpriv_ass_split(const char *buf); /** - * Free a dialogue obtained from ff_ass_split_dialog(). + * Free a dialogue obtained from avpriv_ass_split_dialog(). */ -void ff_ass_free_dialog(ASSDialog **dialogp); +void avpriv_ass_free_dialog(ASSDialog **dialogp); /** * Split one ASS Dialogue line from a string buffer. @@ -121,14 +121,14 @@ void ff_ass_free_dialog(ASSDialog **dialogp); * @param buf String containing the ASS "Dialogue" line. * @return Pointer to the split ASSDialog. Must be freed with ff_ass_free_dialog() */ -ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf); +ASSDialog *avpriv_ass_split_dialog(ASSSplitContext *ctx, const char *buf); /** * Free all the memory allocated for an ASSSplitContext. * * @param ctx Context previously initialized by ff_ass_split(). */ -void ff_ass_split_free(ASSSplitContext *ctx); +void avpriv_ass_split_free(ASSSplitContext *ctx); /** @@ -141,6 +141,7 @@ typedef struct { * @{ */ void (*text)(void *priv, const char *text, int len); + void (*hard_space)(void *priv); void (*new_line)(void *priv, int forced); void (*style)(void *priv, char style, int close); void (*color)(void *priv, unsigned int /* color */, unsigned int color_id); @@ -156,7 +157,16 @@ typedef struct { * @{ */ void (*move)(void *priv, int x1, int y1, int x2, int y2, int t1, int t2); + void (*animate)(void *priv, int t1, int t2, int accel, char *style); void (*origin)(void *priv, int x, int y); + void (*drawing_mode)(void *priv, int scale); + /** @} */ + + /** + * @defgroup ass_ext ASS extensible parsing callback + * @{ + */ + void (*ext)(void *priv, int ext_id, const char *text, int p1, int p2); /** @} */ /** @@ -176,7 +186,7 @@ typedef struct { * @param buf The ASS "Dialogue" Text field to split. * @return >= 0 on success otherwise an error code <0 */ -int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, +int avpriv_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, const char *buf); /** @@ -186,6 +196,6 @@ int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, * @param style name of the style to search for. * @return the ASSStyle corresponding to style, or NULL if style can't be found */ -ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style); +ASSStyle *avpriv_ass_style_get(ASSSplitContext *ctx, const char *style); -#endif /* AVCODEC_ASS_SPLIT_H */ +#endif /* AVUTIL_ASS_SPLIT_INTERNAL_H */ From patchwork Fri Dec 10 21:37:19 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32268 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2372845iog; Fri, 10 Dec 2021 13:38:13 -0800 (PST) X-Google-Smtp-Source: ABdhPJw6rDEC+5LzU3cb4n4ODykjtQFIOD8aLY7h8eXspHLfnZEIjJTI+aiFUj2Z2kitfy6mKYnQ X-Received: by 2002:aa7:c946:: with SMTP id h6mr43181270edt.190.1639172293246; Fri, 10 Dec 2021 13:38:13 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id z2si6989063edb.580.2021.12.10.13.38.12; Fri, 10 Dec 2021 13:38:13 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=VdX3jfAO; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id EFBC768AF51; Fri, 10 Dec 2021 23:37:26 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM04-MW2-obe.outbound.protection.outlook.com (mail-mw2nam08olkn2073.outbound.protection.outlook.com [40.92.46.73]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id B21BA68AF22 for ; Fri, 10 Dec 2021 23:37:24 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=MzAC1DRWdfSeIowvDwTHhA3Uq7HDX2DW8uWmKBMGs20Hq19sxZhHPfvHynhbQGT6xoL0gJJm2IOyqqrra5ddL1p1JUUln7dHZNleAcOrFUF83b3DJfKjTWzZQgvvuOxrbLL1Ur/czNW4D2CsVm3PsdJT3xgTM41bkkn4hhucUH4e3V7VNEfq+DSLQRnMyoBSKtAHmZyF+Ufn9l+YVeLARglgZJmEg7CjMz1I0WO/cxA0IKcI4FWujyEkxCbnuuji0flusHGekpPfWohVXWtUqCleI1UtonL4TNz3uwh9ZdFHWXxyOpwpeM2ATil3zc/76MC6TnQ1I9w2SS022h5nbQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=VGBE3q5uI9oC2UntzDR4VUhSkjkFjJIJPkOkQstqpCQ=; b=G6wmD0TWTARilFox1Yz/1wX9teSKZAc9KdizKmrb1sE6Z4Y1HVuvS2OHXfy59xorVDyNzfCLa8/+gTg2XTG0auLZmiuNj0OzWH65qRK1dgnmI0JAI1b6jplAmnecHmF25SPP13TGOeay2+ama87+ttV5NcMRQEJAgAFrqghGAB+TbZiZCoA/P0qjEX8caxs0pjOWya1pKDl7BHA72Vbis5dLKypJvv69BeUy41xqBhxq7Qkv+FZcEYEHojEoVkiG1m3coFkbZb5aq/shprsWD/AJsv4112Cl5Lb6iSjGRqlsO4mm+p+vv1JJqSfKLq9oWquBb2rgtNenQET4H0K5NQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=VGBE3q5uI9oC2UntzDR4VUhSkjkFjJIJPkOkQstqpCQ=; b=VdX3jfAOzc0lNqLcDt+BBmnH1OmbRxxle0AhY5RYZW9bm6cOu1IxihPbZwKq8sPtSrJbuX6qSjybUvgqa+pdJzEaouf5dvPqyo3VDXlrp7CUIgNd4USrFLt9ZG+go2oDOxO+XLEDbCSQ3+1XM9Ky2FfcquPDe+z3IQN4/t5325IvOT/0yQYiD+rUuk40m4lGMItoJnIs0F4+7c1vbPy0EtdzOK52xGaD8HQ8LwcG/5C48WpqWWZ2ltx06kd4YyD1wqmpAyuwvUAHv4XaortLH8momU5GFXn65U0OF7ZiQGgDF3yy1lsl1agVdXfnCR+gMgMNedBQdsEJCMnaJsK7kw== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0397.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:19 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:19 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 06/21] avcodec/subtitles: Migrate subtitle encoders to frame-based API and provide a compatibility shim for the legacy api Thread-Index: AQHX7g4bmCbim8pHOkCpcC6yjam0jA== Date: Fri, 10 Dec 2021 21:37:19 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> In-Reply-To: <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [XSZFa7eqrBhu+1m5cIqNXpSrgngEIvY2] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 91db4f8c-39f0-4435-9528-08d9bc253e38 x-ms-traffictypediagnostic: DM8P223MB0397:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: hIVwQDR0sQ5wyHVTZ4NxY5KrpuN82SUctdgxQ8AD9BVZYcPVN7cUg1W4Oy5hvMYBDhUbD0Osj/fCA3d0oR9ZpNDvApaC+0XB3WZwbGsCXsVpXOT9Msz2Yh1HRF4h6umzzTWXxtX1zEVzQOPwnHtXZiWDQpp5D6P6w6BD2Nt2HM3Z03WV/Vh+8t+HLcgm8V+2G56+usRzzWX1O7pZUNX5r6C+pLM8Kgrg/+PS8AKHYxRfFKDTsdnJL9h+XaL3Es3F7YgTr1WgcOKEvmgT8uyuRK4GE5EuY1LsT3owv8L5PGmrM7ndaqk7qDYwD5QpWbaDNZ9ygla+8lq9ggSTdHcJqQq+eFagctS018EM1qjt6Iwiv+XyBYEySpsrMr1hymvBM8e30R3MsUutsWZUl31sfgG+lEljQOM1kEcYQhx71dtBQ2vIjXU1xduPL1wwwBqH3SWZvykoMqHkIAj4hQWmSAYBqej3NADhmJMvwFtXr4hrJ9Ny/W9S3AVRcrXdkNPVxwYHrZu5gLkIF3LjFJLhTd80hfGvMBeVQItllhcprKADbbc6J46JfhsqAEmFi/UL4JQGP2RZJ4U2iQ3xCJdgig== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?wryeOxnXJB6IJIf4y7tbKVgA3RZm?= =?utf-8?q?VO7v7VA/xPM01GC7CG1X0jUudAnVD5jwgjPi3J3P8Ryqy4V+gLFzWcEx9X73EgadV?= =?utf-8?q?dXpvIwsIfMp50eNkBaPZrq7CMHEGfRSanrU3jVEcOYc1awIHjIsErRayqq4J7edav?= =?utf-8?q?G52dIDkl7Jn0xGXfz0DlZaee8FBWe5sw7brNOeWiHLIHP01twezAsqE7S4v43DX2k?= =?utf-8?q?4ngiTSe3kICNRzYXbVHpIaHDhSBQpx+u8IHUcdaAnpgHfIKmv2YzJMDQv5drk2/EU?= =?utf-8?q?m8jZ9epMTyE47joqx6xU8w7arf0UcxaD7HYGyd/o75mTd+kuWK5KtyMsMpqiQj0aE?= =?utf-8?q?Ld8tA6t0KU+t4+BT7MRIMwxBJsFgQX1gjsPaj/5Y8mVzN30leLSR7yq0A8XbC4QVh?= =?utf-8?q?NDggTBA8LkUZ8cIscsbuSod7VWkM2Bd83vZ65VbEWRqD1tQJCxI2lkRT9RIHsNmMv?= =?utf-8?q?FSBjRVslTv6mPj3pcorrs8zASptAGsqTX66k8esxDiXk4wTT3cIu9rtaSbpLkj4DT?= =?utf-8?q?m6WBIBJDSUFqlfEFO/T0YDD/IlA9372ecz9ayYYS89Lke/OJca58awXKM5ISbPtBz?= =?utf-8?q?jTnRQISjOH77I6aRGcME3FqS+tttsfpF6I0sZ+TBtxZmbb5DMRoM9oIVmUlIRxaTJ?= =?utf-8?q?2wrCp51AVaUpB557oYohtf+yqAzLFLEZtI/MK6YaVO49KantqqJe2Kk2Yo+xrydhx?= =?utf-8?q?ggPbNuMe6xCh2HUxkukzGjw4lK6i02VkunXJfXYl4lsb2pdQn7zfUrQ7X2TisgSRX?= =?utf-8?q?6OIS6SP6SjpXuDhJaviENoP8kVWg0O/XrrURUcg3KxAqeN1PDQjINqo0YxRoJSLEv?= =?utf-8?q?CkJVgnmpTn41QS9YT7tdmOUsZd8YS6MOJDqI+9u/Ab8c5MUyA92n7ZHZ05OfGng0p?= =?utf-8?q?IWeiq5sVNGG8z/sZA0pXXvjWw0NVez1g1PLEpCxU9Ydw=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 91db4f8c-39f0-4435-9528-08d9bc253e38 X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:19.2337 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0397 Subject: [FFmpeg-devel] [PATCH v23 06/21] avcodec/subtitles: Migrate subtitle encoders to frame-based API and provide a compatibility shim for the legacy 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: QHtDy1RipbEz Signed-off-by: softworkz --- libavcodec/assenc.c | 90 +++++++++++++++++++++-------- libavcodec/avcodec.h | 5 +- libavcodec/dvbsubenc.c | 96 +++++++++++++++++-------------- libavcodec/dvdsubenc.c | 100 +++++++++++++++++++-------------- libavcodec/encode.c | 63 ++++++++++++++++++++- libavcodec/movtextenc.c | 112 +++++++++++++++++++++++++++---------- libavcodec/srtenc.c | 104 ++++++++++++++++++++++------------ libavcodec/tests/avcodec.c | 2 - libavcodec/ttmlenc.c | 98 ++++++++++++++++++++++++-------- libavcodec/webvttenc.c | 82 +++++++++++++++++++-------- libavcodec/xsubenc.c | 87 +++++++++++++++++----------- 11 files changed, 584 insertions(+), 255 deletions(-) diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c index b0e475834b..94601bba68 100644 --- a/libavcodec/assenc.c +++ b/libavcodec/assenc.c @@ -22,48 +22,92 @@ #include #include "avcodec.h" +#include "encode.h" #include "libavutil/ass_internal.h" #include "internal.h" #include "libavutil/avstring.h" #include "libavutil/internal.h" #include "libavutil/mem.h" +static void check_write_header(AVCodecContext* avctx, const AVFrame* frame) +{ + if (avctx->extradata_size) + return; + + if (frame->subtitle_header && frame->subtitle_header->size > 0) { + const char* subtitle_header = (char*)frame->subtitle_header->data; + avctx->extradata_size = strlen(subtitle_header); + avctx->extradata = av_mallocz(frame->subtitle_header->size + 1); + memcpy(avctx->extradata, subtitle_header, avctx->extradata_size); + avctx->extradata[avctx->extradata_size] = 0; + } + + if (!avctx->extradata_size) { + const char* subtitle_header = avpriv_ass_get_subtitle_header_default(0); + if (!subtitle_header) + return; + + avctx->extradata_size = strlen(subtitle_header); + avctx->extradata = av_mallocz(avctx->extradata_size + 1); + memcpy(avctx->extradata, subtitle_header, avctx->extradata_size); + avctx->extradata[avctx->extradata_size] = 0; + av_freep(&subtitle_header); + } +} + static av_cold int ass_encode_init(AVCodecContext *avctx) { - avctx->extradata = av_malloc(avctx->subtitle_header_size + 1); - if (!avctx->extradata) - return AVERROR(ENOMEM); - memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size); - avctx->extradata_size = avctx->subtitle_header_size; - avctx->extradata[avctx->extradata_size] = 0; + if (avctx->subtitle_header_size) { + avctx->extradata = av_malloc(avctx->subtitle_header_size + 1); + if (!avctx->extradata) + return AVERROR(ENOMEM); + memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size); + avctx->extradata_size = avctx->subtitle_header_size; + avctx->extradata[avctx->extradata_size] = 0; + } + return 0; } -static int ass_encode_frame(AVCodecContext *avctx, - unsigned char *buf, int bufsize, - const AVSubtitle *sub) +static int ass_encode_frame(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet) { - int i, len, total_len = 0; + int ret; + size_t req_len = 0, total_len = 0; + + check_write_header(avctx, frame); - for (i=0; inum_rects; i++) { - const char *ass = sub->rects[i]->ass; + for (unsigned i = 0; i < frame->num_subtitle_areas; i++) { + const char *ass = frame->subtitle_areas[i]->ass; - if (sub->rects[i]->type != SUBTITLE_ASS) { - av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); + if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) { + av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n"); return AVERROR(EINVAL); } - len = av_strlcpy(buf+total_len, ass, bufsize-total_len); + if (ass) + req_len += strlen(ass); + } - if (len > bufsize-total_len-1) { - av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); - return AVERROR_BUFFER_TOO_SMALL; - } + ret = ff_get_encode_buffer(avctx, avpkt, req_len + 1, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); + return ret; + } - total_len += len; + for (unsigned i = 0; i < frame->num_subtitle_areas; i++) { + const char *ass = frame->subtitle_areas[i]->ass; + + if (ass) { + size_t len = av_strlcpy((char *)avpkt->data + total_len, ass, avpkt->size - total_len); + total_len += len; + } } - return total_len; + avpkt->size = total_len; + *got_packet = total_len > 0; + + return 0; } #if CONFIG_SSA_ENCODER @@ -73,7 +117,7 @@ const AVCodec ff_ssa_encoder = { .type = AVMEDIA_TYPE_SUBTITLE, .id = AV_CODEC_ID_ASS, .init = ass_encode_init, - .encode_sub = ass_encode_frame, + .encode2 = ass_encode_frame, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; #endif @@ -85,7 +129,7 @@ const AVCodec ff_ass_encoder = { .type = AVMEDIA_TYPE_SUBTITLE, .id = AV_CODEC_ID_ASS, .init = ass_encode_init, - .encode_sub = ass_encode_frame, + .encode2 = ass_encode_frame, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; #endif diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 3e734d3003..3f25fda708 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -2997,10 +2997,13 @@ void av_parser_close(AVCodecParserContext *s); * @{ */ + /** + * @deprecated Use @ref avcodec_encode_subtitle2() instead. + */ +attribute_deprecated int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, const AVSubtitle *sub); - /** * @} */ diff --git a/libavcodec/dvbsubenc.c b/libavcodec/dvbsubenc.c index 322fc27cb4..3b5a76daa7 100644 --- a/libavcodec/dvbsubenc.c +++ b/libavcodec/dvbsubenc.c @@ -20,6 +20,7 @@ */ #include "avcodec.h" #include "bytestream.h" +#include "encode.h" #include "libavutil/colorspace.h" typedef struct DVBSubtitleContext { @@ -268,21 +269,29 @@ static int dvb_encode_rle8(uint8_t **pq, int buf_size, return len; } -static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, - const AVSubtitle *h) +static int dvbsub_encode(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet) { DVBSubtitleContext *s = avctx->priv_data; uint8_t *q, *pseg_len; int page_id, region_id, clut_id, object_id, i, bpp_index, page_state; - - - q = outbuf; + size_t buf_size; + int ret; page_id = 1; - if (h->num_rects && !h->rects) + if (frame->num_subtitle_areas && !frame->subtitle_areas) return AVERROR(EINVAL); + ret = ff_get_encode_buffer(avctx, avpkt, 1024 * 1024, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); + return ret; + } + + buf_size = avpkt->size; + q = avpkt->data; + if (avctx->width > 0 && avctx->height > 0) { if (buf_size < 11) return AVERROR_BUFFER_TOO_SMALL; @@ -301,7 +310,7 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, /* page composition segment */ - if (buf_size < 8 + h->num_rects * 6) + if (buf_size < 8 + frame->num_subtitle_areas * 6) return AVERROR_BUFFER_TOO_SMALL; *q++ = 0x0f; /* sync_byte */ *q++ = 0x10; /* segment_type */ @@ -313,30 +322,30 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, /* page_version = 0 + page_state */ *q++ = (s->object_version << 4) | (page_state << 2) | 3; - for (region_id = 0; region_id < h->num_rects; region_id++) { + for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) { *q++ = region_id; *q++ = 0xff; /* reserved */ - bytestream_put_be16(&q, h->rects[region_id]->x); /* left pos */ - bytestream_put_be16(&q, h->rects[region_id]->y); /* top pos */ + bytestream_put_be16(&q, frame->subtitle_areas[region_id]->x); /* left pos */ + bytestream_put_be16(&q, frame->subtitle_areas[region_id]->y); /* top pos */ } bytestream_put_be16(&pseg_len, q - pseg_len - 2); - buf_size -= 8 + h->num_rects * 6; + buf_size -= 8 + frame->num_subtitle_areas * 6; - if (h->num_rects) { - for (clut_id = 0; clut_id < h->num_rects; clut_id++) { - if (buf_size < 6 + h->rects[clut_id]->nb_colors * 6) + if (frame->num_subtitle_areas) { + for (clut_id = 0; clut_id < frame->num_subtitle_areas; clut_id++) { + if (buf_size < 6 + frame->subtitle_areas[clut_id]->nb_colors * 6) return AVERROR_BUFFER_TOO_SMALL; /* CLUT segment */ - if (h->rects[clut_id]->nb_colors <= 4) { + if (frame->subtitle_areas[clut_id]->nb_colors <= 4) { /* 2 bpp, some decoders do not support it correctly */ bpp_index = 0; - } else if (h->rects[clut_id]->nb_colors <= 16) { + } else if (frame->subtitle_areas[clut_id]->nb_colors <= 16) { /* 4 bpp, standard encoding */ bpp_index = 1; - } else if (h->rects[clut_id]->nb_colors <= 256) { + } else if (frame->subtitle_areas[clut_id]->nb_colors <= 256) { /* 8 bpp, standard encoding */ bpp_index = 2; } else { @@ -353,12 +362,12 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, *q++ = clut_id; *q++ = (0 << 4) | 0xf; /* version = 0 */ - for(i = 0; i < h->rects[clut_id]->nb_colors; i++) { + for(i = 0; i < frame->subtitle_areas[clut_id]->nb_colors; i++) { *q++ = i; /* clut_entry_id */ *q++ = (1 << (7 - bpp_index)) | (0xf << 1) | 1; /* 2 bits/pixel full range */ { int a, r, g, b; - uint32_t x= ((uint32_t*)h->rects[clut_id]->data[1])[i]; + uint32_t x= ((uint32_t*)frame->subtitle_areas[clut_id]->pal)[i]; a = (x >> 24) & 0xff; r = (x >> 16) & 0xff; g = (x >> 8) & 0xff; @@ -372,22 +381,22 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, } bytestream_put_be16(&pseg_len, q - pseg_len - 2); - buf_size -= 6 + h->rects[clut_id]->nb_colors * 6; + buf_size -= 6 + frame->subtitle_areas[clut_id]->nb_colors * 6; } - if (buf_size < h->num_rects * 22) + if (buf_size < frame->num_subtitle_areas * 22) return AVERROR_BUFFER_TOO_SMALL; - for (region_id = 0; region_id < h->num_rects; region_id++) { + for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) { /* region composition segment */ - if (h->rects[region_id]->nb_colors <= 4) { + if (frame->subtitle_areas[region_id]->nb_colors <= 4) { /* 2 bpp, some decoders do not support it correctly */ bpp_index = 0; - } else if (h->rects[region_id]->nb_colors <= 16) { + } else if (frame->subtitle_areas[region_id]->nb_colors <= 16) { /* 4 bpp, standard encoding */ bpp_index = 1; - } else if (h->rects[region_id]->nb_colors <= 256) { + } else if (frame->subtitle_areas[region_id]->nb_colors <= 256) { /* 8 bpp, standard encoding */ bpp_index = 2; } else { @@ -401,8 +410,8 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, q += 2; /* segment length */ *q++ = region_id; *q++ = (s->object_version << 4) | (0 << 3) | 0x07; /* version , no fill */ - bytestream_put_be16(&q, h->rects[region_id]->w); /* region width */ - bytestream_put_be16(&q, h->rects[region_id]->h); /* region height */ + bytestream_put_be16(&q, frame->subtitle_areas[region_id]->w); /* region width */ + bytestream_put_be16(&q, frame->subtitle_areas[region_id]->h); /* region height */ *q++ = ((1 + bpp_index) << 5) | ((1 + bpp_index) << 2) | 0x03; *q++ = region_id; /* clut_id == region_id */ *q++ = 0; /* 8 bit fill colors */ @@ -416,9 +425,9 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, bytestream_put_be16(&pseg_len, q - pseg_len - 2); } - buf_size -= h->num_rects * 22; + buf_size -= frame->num_subtitle_areas * 22; - for (object_id = 0; object_id < h->num_rects; object_id++) { + for (object_id = 0; object_id < frame->num_subtitle_areas; object_id++) { int (*dvb_encode_rle)(uint8_t **pq, int buf_size, const uint8_t *bitmap, int linesize, int w, int h); @@ -427,13 +436,13 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, return AVERROR_BUFFER_TOO_SMALL; /* bpp_index maths */ - if (h->rects[object_id]->nb_colors <= 4) { + if (frame->subtitle_areas[object_id]->nb_colors <= 4) { /* 2 bpp, some decoders do not support it correctly */ dvb_encode_rle = dvb_encode_rle2; - } else if (h->rects[object_id]->nb_colors <= 16) { + } else if (frame->subtitle_areas[object_id]->nb_colors <= 16) { /* 4 bpp, standard encoding */ dvb_encode_rle = dvb_encode_rle4; - } else if (h->rects[object_id]->nb_colors <= 256) { + } else if (frame->subtitle_areas[object_id]->nb_colors <= 256) { /* 8 bpp, standard encoding */ dvb_encode_rle = dvb_encode_rle8; } else { @@ -463,19 +472,19 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, top_ptr = q; ret = dvb_encode_rle(&q, buf_size, - h->rects[object_id]->data[0], - h->rects[object_id]->w * 2, - h->rects[object_id]->w, - h->rects[object_id]->h >> 1); + frame->subtitle_areas[object_id]->buf[0]->data, + frame->subtitle_areas[object_id]->w * 2, + frame->subtitle_areas[object_id]->w, + frame->subtitle_areas[object_id]->h >> 1); if (ret < 0) return ret; buf_size -= ret; bottom_ptr = q; ret = dvb_encode_rle(&q, buf_size, - h->rects[object_id]->data[0] + h->rects[object_id]->w, - h->rects[object_id]->w * 2, - h->rects[object_id]->w, - h->rects[object_id]->h >> 1); + frame->subtitle_areas[object_id]->buf[0]->data + frame->subtitle_areas[object_id]->w, + frame->subtitle_areas[object_id]->w * 2, + frame->subtitle_areas[object_id]->w, + frame->subtitle_areas[object_id]->h >> 1); if (ret < 0) return ret; buf_size -= ret; @@ -502,7 +511,10 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size, buf_size -= 6; s->object_version = (s->object_version + 1) & 0xf; - return q - outbuf; + avpkt->size = q - avpkt->data; + *got_packet = 1; + + return 0; } const AVCodec ff_dvbsub_encoder = { @@ -511,5 +523,5 @@ const AVCodec ff_dvbsub_encoder = { .type = AVMEDIA_TYPE_SUBTITLE, .id = AV_CODEC_ID_DVB_SUBTITLE, .priv_data_size = sizeof(DVBSubtitleContext), - .encode_sub = dvbsub_encode, + .encode2 = dvbsub_encode, }; diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c index ff4fbed39d..4916410fc8 100644 --- a/libavcodec/dvdsubenc.c +++ b/libavcodec/dvdsubenc.c @@ -20,6 +20,7 @@ */ #include "avcodec.h" #include "bytestream.h" +#include "encode.h" #include "internal.h" #include "libavutil/avassert.h" #include "libavutil/bprint.h" @@ -114,15 +115,14 @@ static int color_distance(uint32_t a, uint32_t b) * Count colors used in a rectangle, quantizing alpha and grouping by * nearest global palette entry. */ -static void count_colors(AVCodecContext *avctx, unsigned hits[33], - const AVSubtitleRect *r) +static void count_colors(const AVCodecContext *avctx, unsigned hits[33], + const AVSubtitleArea *r) { DVDSubtitleContext *dvdc = avctx->priv_data; unsigned count[256] = { 0 }; - uint32_t *palette = (uint32_t *)r->data[1]; uint32_t color; int x, y, i, j, match, d, best_d, av_uninit(best_j); - uint8_t *p = r->data[0]; + uint8_t *p = r->buf[0]->data; for (y = 0; y < r->h; y++) { for (x = 0; x < r->w; x++) @@ -132,7 +132,7 @@ static void count_colors(AVCodecContext *avctx, unsigned hits[33], for (i = 0; i < 256; i++) { if (!count[i]) /* avoid useless search */ continue; - color = palette[i]; + color = r->pal[i]; /* 0: transparent, 1-16: semi-transparent, 17-33 opaque */ match = color < 0x33000000 ? 0 : color < 0xCC000000 ? 1 : 17; if (match) { @@ -232,13 +232,13 @@ static void build_color_map(AVCodecContext *avctx, int cmap[], } } -static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[]) +static void copy_rectangle(AVSubtitleArea*dst, AVSubtitleArea *src, int cmap[]) { int x, y; uint8_t *p, *q; - p = src->data[0]; - q = dst->data[0] + (src->x - dst->x) + + p = src->buf[0]->data; + q = dst->buf[0]->data + (src->x - dst->x) + (src->y - dst->y) * dst->linesize[0]; for (y = 0; y < src->h; y++) { for (x = 0; x < src->w; x++) @@ -248,51 +248,56 @@ static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[]) } } -static int encode_dvd_subtitles(AVCodecContext *avctx, - uint8_t *outbuf, int outbuf_size, - const AVSubtitle *h) +static int encode_dvd_subtitles(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet) { DVDSubtitleContext *dvdc = avctx->priv_data; uint8_t *q, *qq; int offset1, offset2; - int i, rects = h->num_rects, ret; + int ret = 0; + unsigned i, rects = frame->num_subtitle_areas; unsigned global_palette_hits[33] = { 0 }; int cmap[256]; int out_palette[4]; int out_alpha[4]; - AVSubtitleRect vrect; - uint8_t *vrect_data = NULL; + AVSubtitleArea vrect; + uint8_t* vrect_data = NULL, *outbuf; int x2, y2; int forced = 0; + int outbuf_size; - if (rects == 0 || !h->rects) + if (rects == 0) + return 0; + + if (!frame->subtitle_areas) return AVERROR(EINVAL); + for (i = 0; i < rects; i++) - if (h->rects[i]->type != SUBTITLE_BITMAP) { + if (frame->subtitle_areas[i]->type != SUBTITLE_BITMAP) { av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n"); return AVERROR(EINVAL); } /* Mark this subtitle forced if any of the rectangles is forced. */ for (i = 0; i < rects; i++) - if ((h->rects[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) { + if ((frame->subtitle_areas[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) { forced = 1; break; } - vrect = *h->rects[0]; + vrect = *frame->subtitle_areas[0]; if (rects > 1) { /* DVD subtitles can have only one rectangle: build a virtual rectangle containing all actual rectangles. The data of the rectangles will be copied later, when the palette is decided, because the rectangles may have different palettes. */ - int xmin = h->rects[0]->x, xmax = xmin + h->rects[0]->w; - int ymin = h->rects[0]->y, ymax = ymin + h->rects[0]->h; + int xmin = frame->subtitle_areas[0]->x, xmax = xmin + frame->subtitle_areas[0]->w; + int ymin = frame->subtitle_areas[0]->y, ymax = ymin + frame->subtitle_areas[0]->h; for (i = 1; i < rects; i++) { - xmin = FFMIN(xmin, h->rects[i]->x); - ymin = FFMIN(ymin, h->rects[i]->y); - xmax = FFMAX(xmax, h->rects[i]->x + h->rects[i]->w); - ymax = FFMAX(ymax, h->rects[i]->y + h->rects[i]->h); + xmin = FFMIN(xmin, frame->subtitle_areas[i]->x); + ymin = FFMIN(ymin, frame->subtitle_areas[i]->y); + xmax = FFMAX(xmax, frame->subtitle_areas[i]->x + frame->subtitle_areas[i]->w); + ymax = FFMAX(ymax, frame->subtitle_areas[i]->y + frame->subtitle_areas[i]->h); } vrect.x = xmin; vrect.y = ymin; @@ -304,27 +309,29 @@ static int encode_dvd_subtitles(AVCodecContext *avctx, /* Count pixels outside the virtual rectangle as transparent */ global_palette_hits[0] = vrect.w * vrect.h; for (i = 0; i < rects; i++) - global_palette_hits[0] -= h->rects[i]->w * h->rects[i]->h; + global_palette_hits[0] -= frame->subtitle_areas[i]->w * frame->subtitle_areas[i]->h; } for (i = 0; i < rects; i++) - count_colors(avctx, global_palette_hits, h->rects[i]); + count_colors(avctx, global_palette_hits, frame->subtitle_areas[i]); select_palette(avctx, out_palette, out_alpha, global_palette_hits); if (rects > 1) { - if (!(vrect_data = av_calloc(vrect.w, vrect.h))) + + vrect.buf[0] = av_buffer_allocz((size_t)vrect.w * vrect.h); + if (!vrect.buf[0]) return AVERROR(ENOMEM); - vrect.data [0] = vrect_data; + vrect.linesize[0] = vrect.w; for (i = 0; i < rects; i++) { - build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->data[1], + build_color_map(avctx, cmap, frame->subtitle_areas[i]->pal, out_palette, out_alpha); - copy_rectangle(&vrect, h->rects[i], cmap); + copy_rectangle(&vrect, frame->subtitle_areas[i], cmap); } for (i = 0; i < 4; i++) cmap[i] = i; } else { - build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->data[1], + build_color_map(avctx, cmap, frame->subtitle_areas[0]->pal, out_palette, out_alpha); } @@ -335,6 +342,16 @@ static int encode_dvd_subtitles(AVCodecContext *avctx, out_palette[i], out_alpha[i] >> 4); av_log(avctx, AV_LOG_DEBUG, "\n"); + + ret = ff_get_encode_buffer(avctx, avpkt, 4 + vrect.w * vrect.h / 2 + 17 + 21, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); + return ret; + } + + outbuf_size = avpkt->size; + outbuf = avpkt->data; + // encode data block q = outbuf + 4; offset1 = q - outbuf; @@ -344,10 +361,10 @@ static int encode_dvd_subtitles(AVCodecContext *avctx, ret = AVERROR_BUFFER_TOO_SMALL; goto fail; } - dvd_encode_rle(&q, vrect.data[0], vrect.w * 2, + dvd_encode_rle(&q, vrect.buf[0]->data, vrect.w * 2, vrect.w, (vrect.h + 1) >> 1, cmap); offset2 = q - outbuf; - dvd_encode_rle(&q, vrect.data[0] + vrect.w, vrect.w * 2, + dvd_encode_rle(&q, vrect.buf[0]->data + vrect.w, vrect.w * 2, vrect.w, vrect.h >> 1, cmap); if (dvdc->even_rows_fix && (vrect.h & 1)) { @@ -362,7 +379,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx, bytestream_put_be16(&qq, q - outbuf); // send start display command - bytestream_put_be16(&q, (h->start_display_time*90) >> 10); + bytestream_put_be16(&q, (frame->subtitle_start_time * 90) >> 10); bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12 + 2); *q++ = 0x03; // palette - 4 nibbles *q++ = (out_palette[3] << 4) | out_palette[2]; @@ -394,7 +411,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx, *q++ = 0xff; // terminating command // send stop display command last - bytestream_put_be16(&q, (h->end_display_time*90) >> 10); + bytestream_put_be16(&q, (frame->subtitle_end_time*90) >> 10); bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/); *q++ = 0x02; // set end *q++ = 0xff; // terminating command @@ -403,7 +420,9 @@ static int encode_dvd_subtitles(AVCodecContext *avctx, bytestream_put_be16(&qq, q - outbuf); av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%"PTRDIFF_SPECIFIER"\n", q - outbuf); - ret = q - outbuf; + avpkt->size = q - outbuf; + ret = 0; + *got_packet = 1; fail: av_free(vrect_data); @@ -467,14 +486,13 @@ static int dvdsub_init(AVCodecContext *avctx) return 0; } -static int dvdsub_encode(AVCodecContext *avctx, - unsigned char *buf, int buf_size, - const AVSubtitle *sub) +static int dvdsub_encode(struct AVCodecContext* avctx, struct AVPacket* avpkt, + const struct AVFrame* frame, int* got_packet) { //DVDSubtitleContext *s = avctx->priv_data; int ret; - ret = encode_dvd_subtitles(avctx, buf, buf_size, sub); + ret = encode_dvd_subtitles(avctx, avpkt, frame, got_packet); return ret; } @@ -499,7 +517,7 @@ const AVCodec ff_dvdsub_encoder = { .type = AVMEDIA_TYPE_SUBTITLE, .id = AV_CODEC_ID_DVD_SUBTITLE, .init = dvdsub_init, - .encode_sub = dvdsub_encode, + .encode2 = dvdsub_encode, .priv_class = &dvdsubenc_class, .priv_data_size = sizeof(DVDSubtitleContext), .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, diff --git a/libavcodec/encode.c b/libavcodec/encode.c index dd25cf999b..8c915cab1d 100644 --- a/libavcodec/encode.c +++ b/libavcodec/encode.c @@ -140,17 +140,76 @@ fail: return ret; } +/** + * \brief + * \param avctx + * \param buf q + * \param buf_size + * \param sub + * \return + */ int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, const AVSubtitle *sub) { - int ret; + int ret = 0, got_packet = 0; + AVFrame *frame = NULL; + AVPacket* avpkt = NULL; + if (sub->start_display_time) { av_log(avctx, AV_LOG_ERROR, "start_display_time must be 0.\n"); return -1; } - ret = avctx->codec->encode_sub(avctx, buf, buf_size, sub); + memset(buf, 0, buf_size); + // Create a temporary frame for calling the regular api: + frame = av_frame_alloc(); + if (!frame) { + ret = AVERROR(ENOMEM); + goto exit; + } + + frame->format = sub->format; + frame->type = AVMEDIA_TYPE_SUBTITLE; + ret = av_frame_get_buffer2(frame, 0); + if (ret < 0) + goto exit; + + // Create a temporary packet + avpkt = av_packet_alloc(); + if (!avpkt) { + ret = AVERROR(ENOMEM); + goto exit; + } + + // Copy legacy subtitle data to temp frame + ret = ff_frame_put_subtitle(frame, sub); + if (ret < 0) + goto exit; + + if (frame->subtitle_start_time) { + av_log(avctx, AV_LOG_ERROR, "start_display_time must be 0.\n"); + ret = AVERROR_INVALIDDATA; + goto exit; + } + + ret = avctx->codec->encode2(avctx, avpkt, frame, &got_packet); + avctx->frame_number++; + + if (got_packet) { + if (avpkt->size > buf_size) { + ret = AVERROR_BUFFER_TOO_SMALL; + goto exit; + } + + memcpy(buf, avpkt->data, avpkt->size); + ret = avpkt->size; + } + +exit: + + av_packet_free(&avpkt); + av_frame_free(&frame); return ret; } diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c index 5b23deeedd..25ba0fac0a 100644 --- a/libavcodec/movtextenc.c +++ b/libavcodec/movtextenc.c @@ -29,6 +29,7 @@ #include "libavutil/ass_split_internal.h" #include "libavutil/ass_internal.h" #include "bytestream.h" +#include "encode.h" #include "internal.h" #define STYLE_FLAG_BOLD (1<<0) @@ -73,6 +74,7 @@ typedef struct { AVCodecContext *avctx; ASSSplitContext *ass_ctx; + int is_default_ass_context; ASSStyle *ass_dialog_style; StyleBox *style_attributes; unsigned count; @@ -329,7 +331,7 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx) av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); - s->ass_ctx = avpriv_ass_split(avctx->subtitle_header); + s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header); if (!s->ass_ctx) return AVERROR_INVALIDDATA; ret = encode_sample_description(avctx); @@ -631,56 +633,108 @@ static const ASSCodesCallbacks mov_text_callbacks = { .end = mov_text_end_cb, }; -static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, - int bufsize, const AVSubtitle *sub) +static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame) +{ + MovTextContext* s = avctx->priv_data; + int ret; + + if (s->ass_ctx && !s->is_default_ass_context) + // We already have a (non-default context) + return; + + if (!frame->num_subtitle_areas) + // Don't need ass context for processing empty subtitle frames + return; + + // The frame has content, so we need to set up a context + if (frame->subtitle_header && frame->subtitle_header->size > 0) { + const char* subtitle_header = (char*)frame->subtitle_header->data; + avpriv_ass_split_free(s->ass_ctx); + s->ass_ctx = avpriv_ass_split(subtitle_header); + s->is_default_ass_context = 0; + } + else if (!s->ass_ctx) { + char* subtitle_header = avpriv_ass_get_subtitle_header_default(0); + if (!subtitle_header) + return; + + s->ass_ctx = avpriv_ass_split(subtitle_header); + s->is_default_ass_context = 1; + av_free(subtitle_header); + } + + if (s->ass_ctx && !avctx->extradata_size) { + ret = encode_sample_description(avctx); + if (ret < 0) + av_log(avctx, AV_LOG_ERROR, "Error during encode_sample_description().\n"); + } +} + +static int mov_text_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, + const AVFrame *frame, int *got_packet) { MovTextContext *s = avctx->priv_data; ASSDialog *dialog; - int i, length; + int i, ret = 0; + size_t j; + uint8_t* buf; + + ensure_ass_context(avctx, frame); + s->text_pos = 0; s->count = 0; s->box_flags = 0; av_bprint_clear(&s->buffer); - for (i = 0; i < sub->num_rects; i++) { - const char *ass = sub->rects[i]->ass; + for (i = 0; i < frame->num_subtitle_areas; i++) { + const char *ass = frame->subtitle_areas[i]->ass; - if (sub->rects[i]->type != SUBTITLE_ASS) { - av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); + if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) { + av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n"); return AVERROR(EINVAL); } - dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); - if (!dialog) - return AVERROR(ENOMEM); - mov_text_dialog(s, dialog); - avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text); - avpriv_ass_free_dialog(&dialog); + if (ass) { + dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); + if (!dialog) + return AVERROR(ENOMEM); + mov_text_dialog(s, dialog); + avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text); + avpriv_ass_free_dialog(&dialog); + + for (j = 0; j < box_count; j++) { + box_types[j].encode(s); + } + } } - if (s->buffer.len > UINT16_MAX) - return AVERROR(ERANGE); - AV_WB16(buf, s->buffer.len); - buf += 2; + if (!av_bprint_is_complete(&s->buffer)) { + return AVERROR(ENOMEM); + } - for (size_t j = 0; j < box_count; j++) - box_types[j].encode(s); + ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); + return ret; + } - if (!av_bprint_is_complete(&s->buffer)) - return AVERROR(ENOMEM); + buf = avpkt->data; - if (!s->buffer.len) - return 0; + AV_WB16(buf, s->buffer.len); + buf += 2; - if (s->buffer.len > bufsize - 3) { + if (s->buffer.len > avpkt->size - 3) { av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); - return AVERROR_BUFFER_TOO_SMALL; + ret = AVERROR_BUFFER_TOO_SMALL; + goto exit; } memcpy(buf, s->buffer.str, s->buffer.len); - length = s->buffer.len + 2; + avpkt->size = s->buffer.len + 2; + *got_packet = 1; - return length; +exit: + return ret; } #define OFFSET(x) offsetof(MovTextContext, x) @@ -705,7 +759,7 @@ const AVCodec ff_movtext_encoder = { .priv_data_size = sizeof(MovTextContext), .priv_class = &mov_text_encoder_class, .init = mov_text_encode_init, - .encode_sub = mov_text_encode_frame, + .encode2 = mov_text_encode_frame, .close = mov_text_encode_close, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c index a7c5fccefe..9b661da54d 100644 --- a/libavcodec/srtenc.c +++ b/libavcodec/srtenc.c @@ -21,6 +21,7 @@ #include #include "avcodec.h" +#include "encode.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/ass_split_internal.h" @@ -33,6 +34,7 @@ typedef struct { AVCodecContext *avctx; ASSSplitContext *ass_ctx; + int is_default_ass_context; AVBPrint buffer; char stack[SRT_STACK_SIZE]; int stack_ptr; @@ -130,14 +132,13 @@ static void srt_style_apply(SRTContext *s, const char *style) } } - static av_cold int srt_encode_init(AVCodecContext *avctx) { SRTContext *s = avctx->priv_data; s->avctx = avctx; - s->ass_ctx = avpriv_ass_split(avctx->subtitle_header); + s->ass_ctx = avpriv_ass_split((char *)avctx->subtitle_header); av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); - return s->ass_ctx ? 0 : AVERROR_INVALIDDATA; + return 0; } static void srt_text_cb(void *priv, const char *text, int len) @@ -227,58 +228,91 @@ static const ASSCodesCallbacks text_callbacks = { .new_line = srt_new_line_cb, }; -static int encode_frame(AVCodecContext *avctx, - unsigned char *buf, int bufsize, const AVSubtitle *sub, - const ASSCodesCallbacks *cb) +static void ensure_ass_context(SRTContext* s, const AVFrame* frame) +{ + if (s->ass_ctx && !s->is_default_ass_context) + // We already have a (non-default context) + return; + + if (!frame->num_subtitle_areas) + // Don't need ass context for processing empty subtitle frames + return; + + // The frame has content, so we need to set up a context + if (frame->subtitle_header && frame->subtitle_header->size > 0) { + const char* subtitle_header = (char*)frame->subtitle_header->data; + avpriv_ass_split_free(s->ass_ctx); + s->ass_ctx = avpriv_ass_split(subtitle_header); + s->is_default_ass_context = 0; + } + else if (!s->ass_ctx) { + char* subtitle_header = avpriv_ass_get_subtitle_header_default(0); + if (!subtitle_header) + return; + + s->ass_ctx = avpriv_ass_split(subtitle_header); + s->is_default_ass_context = 1; + av_free(subtitle_header); + } +} + +static int encode_frame(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet, const ASSCodesCallbacks* cb) { SRTContext *s = avctx->priv_data; ASSDialog *dialog; - int i; + int i, ret; + + ensure_ass_context(s, frame); av_bprint_clear(&s->buffer); - for (i=0; inum_rects; i++) { - const char *ass = sub->rects[i]->ass; + for (i=0; i< frame->num_subtitle_areas; i++) { + const char *ass = frame->subtitle_areas[i]->ass; - if (sub->rects[i]->type != SUBTITLE_ASS) { - av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); + if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) { + av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n"); return AVERROR(EINVAL); } - dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); - if (!dialog) - return AVERROR(ENOMEM); - s->alignment_applied = 0; - if (avctx->codec_id == AV_CODEC_ID_SUBRIP) - srt_style_apply(s, dialog->style); - avpriv_ass_split_override_codes(cb, s, dialog->text); - avpriv_ass_free_dialog(&dialog); + if (ass) { + dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); + if (!dialog) + return AVERROR(ENOMEM); + s->alignment_applied = 0; + if (avctx->codec_id == AV_CODEC_ID_SUBRIP) + srt_style_apply(s, dialog->style); + avpriv_ass_split_override_codes(cb, s, dialog->text); + avpriv_ass_free_dialog(&dialog); + } } if (!av_bprint_is_complete(&s->buffer)) return AVERROR(ENOMEM); - if (!s->buffer.len) - return 0; - if (s->buffer.len > bufsize) { - av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); - return AVERROR_BUFFER_TOO_SMALL; + ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); + return ret; } - memcpy(buf, s->buffer.str, s->buffer.len); - return s->buffer.len; + memcpy(avpkt->data, s->buffer.str, s->buffer.len); + avpkt->size = s->buffer.len; + *got_packet = 1; + + return 0; } -static int srt_encode_frame(AVCodecContext *avctx, - unsigned char *buf, int bufsize, const AVSubtitle *sub) +static int srt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet) { - return encode_frame(avctx, buf, bufsize, sub, &srt_callbacks); + return encode_frame(avctx, avpkt, frame, got_packet, &srt_callbacks); } -static int text_encode_frame(AVCodecContext *avctx, - unsigned char *buf, int bufsize, const AVSubtitle *sub) +static int text_encode_frame(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet) { - return encode_frame(avctx, buf, bufsize, sub, &text_callbacks); + return encode_frame(avctx, avpkt, frame, got_packet, &text_callbacks); } static int srt_encode_close(AVCodecContext *avctx) @@ -298,7 +332,7 @@ const AVCodec ff_srt_encoder = { .id = AV_CODEC_ID_SUBRIP, .priv_data_size = sizeof(SRTContext), .init = srt_encode_init, - .encode_sub = srt_encode_frame, + .encode2 = srt_encode_frame, .close = srt_encode_close, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; @@ -312,7 +346,7 @@ const AVCodec ff_subrip_encoder = { .id = AV_CODEC_ID_SUBRIP, .priv_data_size = sizeof(SRTContext), .init = srt_encode_init, - .encode_sub = srt_encode_frame, + .encode2 = srt_encode_frame, .close = srt_encode_close, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; @@ -326,7 +360,7 @@ const AVCodec ff_text_encoder = { .id = AV_CODEC_ID_TEXT, .priv_data_size = sizeof(SRTContext), .init = srt_encode_init, - .encode_sub = text_encode_frame, + .encode2 = text_encode_frame, .close = srt_encode_close, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; diff --git a/libavcodec/tests/avcodec.c b/libavcodec/tests/avcodec.c index 5d0ff9432c..bd979b2184 100644 --- a/libavcodec/tests/avcodec.c +++ b/libavcodec/tests/avcodec.c @@ -107,8 +107,6 @@ int main(void){ continue; } if (is_encoder) { - if (codec->type == AVMEDIA_TYPE_SUBTITLE ^ !!codec->encode_sub) - ERR("Encoder %s is both subtitle encoder and not subtitle encoder."); if (!!codec->encode_sub + !!codec->encode2 + !!codec->receive_packet != 1) ERR("Encoder %s does not implement exactly one encode API.\n"); if (codec->update_thread_context || codec->update_thread_context_for_user || codec->bsfs) diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c index 083f2dd67a..011b7aa753 100644 --- a/libavcodec/ttmlenc.c +++ b/libavcodec/ttmlenc.c @@ -33,11 +33,17 @@ #include "libavutil/bprint.h" #include "libavutil/internal.h" #include "libavutil/ass_split_internal.h" +#include "libavutil/ass_internal.h" #include "ttmlenc.h" +#include "encode.h" + + typedef struct { AVCodecContext *avctx; ASSSplitContext *ass_ctx; + int is_default_ass_context; + int extradata_written; AVBPrint buffer; } TTMLContext; @@ -76,24 +82,68 @@ static const ASSCodesCallbacks ttml_callbacks = { .new_line = ttml_new_line_cb, }; -static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, - int bufsize, const AVSubtitle *sub) +static int ttml_write_header_content(AVCodecContext* avctx); + +static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame) +{ + TTMLContext* s = avctx->priv_data; + int ret; + + if (s->ass_ctx && !s->is_default_ass_context) + // We already have a (non-default context) + return; + + if (!frame->num_subtitle_areas) + // Don't need ass context for processing empty subtitle frames + return; + + // The frame has content, so we need to set up a context + if (frame->subtitle_header && frame->subtitle_header->size > 0) { + const char* subtitle_header = (char*)frame->subtitle_header->data; + avpriv_ass_split_free(s->ass_ctx); + s->ass_ctx = avpriv_ass_split(subtitle_header); + s->is_default_ass_context = 0; + } + else if (!s->ass_ctx) { + char* subtitle_header = avpriv_ass_get_subtitle_header_default(0); + if (!subtitle_header) + return; + + s->ass_ctx = avpriv_ass_split(subtitle_header); + s->is_default_ass_context = 1; + av_free(subtitle_header); + } + + if (s->ass_ctx && !s->extradata_written) { + s->extradata_written = 1; + if ((ret = ttml_write_header_content(avctx)) < 0) { + av_log(avctx, AV_LOG_ERROR, "Error writing header content.\n"); + } + } +} + +static int ttml_encode_frame(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet) { TTMLContext *s = avctx->priv_data; ASSDialog *dialog; - int i; + int i, ret; + + ensure_ass_context(avctx, frame); av_bprint_clear(&s->buffer); - for (i=0; inum_rects; i++) { - const char *ass = sub->rects[i]->ass; - int ret; + for (i=0; i< frame->num_subtitle_areas; i++) { + const char *ass = frame->subtitle_areas[i]->ass; - if (sub->rects[i]->type != SUBTITLE_ASS) { - av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); + if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) { + av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n"); return AVERROR(EINVAL); } + if (!ass) + continue; + dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); if (!dialog) return AVERROR(ENOMEM); @@ -130,17 +180,19 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, if (!av_bprint_is_complete(&s->buffer)) return AVERROR(ENOMEM); - if (!s->buffer.len) - return 0; - - // force null-termination, so in case our destination buffer is - // too small, the return value is larger than bufsize minus null. - if (av_strlcpy(buf, s->buffer.str, bufsize) > bufsize - 1) { - av_log(avctx, AV_LOG_ERROR, "Buffer too small for TTML event.\n"); - return AVERROR_BUFFER_TOO_SMALL; + + ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); + return ret; } - return s->buffer.len; + av_strlcpy(avpkt->data, s->buffer.str, avpkt->size); + + avpkt->size = s->buffer.len; + *got_packet = 1; + + return 0; } static av_cold int ttml_encode_close(AVCodecContext *avctx) @@ -370,13 +422,13 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx) s->avctx = avctx; av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); + s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header); - if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) { - return AVERROR_INVALIDDATA; - } + if (s->ass_ctx) { + if (ret = ttml_write_header_content(avctx) < 0) + return ret; - if ((ret = ttml_write_header_content(avctx)) < 0) { - return ret; + s->extradata_written = 1; } return 0; @@ -389,7 +441,7 @@ const AVCodec ff_ttml_encoder = { .id = AV_CODEC_ID_TTML, .priv_data_size = sizeof(TTMLContext), .init = ttml_encode_init, - .encode_sub = ttml_encode_frame, + .encode2 = ttml_encode_frame, .close = ttml_encode_close, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c index 761099b69a..43dace6575 100644 --- a/libavcodec/webvttenc.c +++ b/libavcodec/webvttenc.c @@ -22,6 +22,7 @@ #include #include "avcodec.h" +#include "encode.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/ass_split_internal.h" @@ -32,6 +33,7 @@ typedef struct { AVCodecContext *avctx; ASSSplitContext *ass_ctx; + int is_default_ass_context; AVBPrint buffer; unsigned timestamp_end; int count; @@ -155,43 +157,77 @@ static const ASSCodesCallbacks webvtt_callbacks = { .end = webvtt_end_cb, }; -static int webvtt_encode_frame(AVCodecContext *avctx, - unsigned char *buf, int bufsize, const AVSubtitle *sub) +static void ensure_ass_context(WebVTTContext* s, const AVFrame* frame) +{ + if (s->ass_ctx && !s->is_default_ass_context) + // We already have a (non-default context) + return; + + if (!frame->num_subtitle_areas) + // Don't need ass context for processing empty subtitle frames + return; + + // The frame has content, so we need to set up a context + if (frame->subtitle_header && frame->subtitle_header->size > 0) { + avpriv_ass_split_free(s->ass_ctx); + const char* subtitle_header = (char*)frame->subtitle_header->data; + s->ass_ctx = avpriv_ass_split(subtitle_header); + s->is_default_ass_context = 0; + } + else if (!s->ass_ctx) { + char* subtitle_header = avpriv_ass_get_subtitle_header_default(0); + if (!subtitle_header) + return; + + s->ass_ctx = avpriv_ass_split(subtitle_header); + s->is_default_ass_context = 1; + av_free(subtitle_header); + } +} + +static int webvtt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet) { WebVTTContext *s = avctx->priv_data; ASSDialog *dialog; - int i; + int ret, i; + + ensure_ass_context(s, frame); av_bprint_clear(&s->buffer); - for (i=0; inum_rects; i++) { - const char *ass = sub->rects[i]->ass; + for (i=0; i< frame->num_subtitle_areas; i++) { + const char *ass = frame->subtitle_areas[i]->ass; - if (sub->rects[i]->type != SUBTITLE_ASS) { - av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); + if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) { + av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n"); return AVERROR(EINVAL); } - dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); - if (!dialog) - return AVERROR(ENOMEM); - webvtt_style_apply(s, dialog->style); - avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text); - avpriv_ass_free_dialog(&dialog); + if (ass) { + dialog = avpriv_ass_split_dialog(s->ass_ctx, ass); + if (!dialog) + return AVERROR(ENOMEM); + webvtt_style_apply(s, dialog->style); + avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text); + avpriv_ass_free_dialog(&dialog); + } } if (!av_bprint_is_complete(&s->buffer)) return AVERROR(ENOMEM); - if (!s->buffer.len) - return 0; - if (s->buffer.len > bufsize) { - av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); - return AVERROR_BUFFER_TOO_SMALL; + ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); + return ret; } - memcpy(buf, s->buffer.str, s->buffer.len); - return s->buffer.len; + memcpy(avpkt->data, s->buffer.str, s->buffer.len); + avpkt->size = s->buffer.len; + *got_packet = s->buffer.len > 0; + + return 0; } static int webvtt_encode_close(AVCodecContext *avctx) @@ -206,9 +242,9 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx) { WebVTTContext *s = avctx->priv_data; s->avctx = avctx; - s->ass_ctx = avpriv_ass_split(avctx->subtitle_header); + s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header); av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); - return s->ass_ctx ? 0 : AVERROR_INVALIDDATA; + return 0; } const AVCodec ff_webvtt_encoder = { @@ -218,7 +254,7 @@ const AVCodec ff_webvtt_encoder = { .id = AV_CODEC_ID_WEBVTT, .priv_data_size = sizeof(WebVTTContext), .init = webvtt_encode_init, - .encode_sub = webvtt_encode_frame, + .encode2 = webvtt_encode_frame, .close = webvtt_encode_close, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; diff --git a/libavcodec/xsubenc.c b/libavcodec/xsubenc.c index 03d0dc2d86..52fc7044da 100644 --- a/libavcodec/xsubenc.c +++ b/libavcodec/xsubenc.c @@ -22,6 +22,7 @@ #include "avcodec.h" #include "bytestream.h" +#include "encode.h" #include "internal.h" #include "put_bits.h" @@ -111,39 +112,55 @@ static int make_tc(uint64_t ms, int *tc) return ms > 99; } -static int xsub_encode(AVCodecContext *avctx, unsigned char *buf, - int bufsize, const AVSubtitle *h) +static int xsub_encode(AVCodecContext* avctx, AVPacket* avpkt, + const AVFrame* frame, int* got_packet) { - uint64_t startTime = h->pts / 1000; // FIXME: need better solution... - uint64_t endTime = startTime + h->end_display_time - h->start_display_time; + const uint64_t startTime = frame->subtitle_pts / 1000; // FIXME: need better solution... + const uint64_t endTime = startTime + frame->subtitle_end_time - frame->subtitle_start_time; int start_tc[4], end_tc[4]; - uint8_t *hdr = buf + 27; // Point behind the timestamp + uint8_t *hdr; uint8_t *rlelenptr; uint16_t width, height; - int i; + int i, ret; PutBitContext pb; + uint8_t* buf; + int64_t req_size; - if (bufsize < 27 + 7*2 + 4*3) { - av_log(avctx, AV_LOG_ERROR, "Buffer too small for XSUB header.\n"); - return AVERROR_BUFFER_TOO_SMALL; + if (!frame->num_subtitle_areas) { + // Don't encode empty sub events + return 0; } + // Estimate size (timestamp 27, header 7*2 + 4*3, padding 10) + req_size = 27 + 7*2 + 4*3 + 10; + req_size += frame->subtitle_areas[0]->linesize[0] * frame->subtitle_areas[0]->h; + req_size += 256; // Palette + + ret = ff_get_encode_buffer(avctx, avpkt, req_size, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); + return ret; + } + + buf = avpkt->data; + hdr = avpkt->data + 27; // Point behind the timestamp + // TODO: support multiple rects - if (h->num_rects != 1) - av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", h->num_rects); + if (frame->num_subtitle_areas != 1) + av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", frame->num_subtitle_areas); // TODO: render text-based subtitles into bitmaps - if (!h->rects[0]->data[0] || !h->rects[0]->data[1]) { + if (!frame->subtitle_areas[0]->buf[0]->data || !frame->subtitle_areas[0]->pal) { av_log(avctx, AV_LOG_WARNING, "No subtitle bitmap available.\n"); return AVERROR(EINVAL); } // TODO: color reduction, similar to dvdsub encoder - if (h->rects[0]->nb_colors > 4) - av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", h->rects[0]->nb_colors); + if (frame->subtitle_areas[0]->nb_colors > 4) + av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", frame->subtitle_areas[0]->nb_colors); // TODO: Palette swapping if color zero is not transparent - if (((uint32_t *)h->rects[0]->data[1])[0] & 0xff000000) + if (((uint32_t *)frame->subtitle_areas[0]->pal)[0] & 0xff000000) av_log(avctx, AV_LOG_WARNING, "Color index 0 is not transparent. Transparency will be messed up.\n"); if (make_tc(startTime, start_tc) || make_tc(endTime, end_tc)) { @@ -151,7 +168,7 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf, return AVERROR(EINVAL); } - snprintf(buf, 28, + snprintf((char *)avpkt->data, 28, "[%02d:%02d:%02d.%03d-%02d:%02d:%02d.%03d]", start_tc[3], start_tc[2], start_tc[1], start_tc[0], end_tc[3], end_tc[2], end_tc[1], end_tc[0]); @@ -160,45 +177,47 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf, // 2 pixels required on either side of subtitle. // Possibly due to limitations of hardware renderers. // TODO: check if the bitmap is already padded - width = FFALIGN(h->rects[0]->w, 2) + PADDING * 2; - height = FFALIGN(h->rects[0]->h, 2); + width = FFALIGN(frame->subtitle_areas[0]->w, 2) + PADDING * 2; + height = FFALIGN(frame->subtitle_areas[0]->h, 2); bytestream_put_le16(&hdr, width); bytestream_put_le16(&hdr, height); - bytestream_put_le16(&hdr, h->rects[0]->x); - bytestream_put_le16(&hdr, h->rects[0]->y); - bytestream_put_le16(&hdr, h->rects[0]->x + width -1); - bytestream_put_le16(&hdr, h->rects[0]->y + height -1); + bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x); + bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y); + bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x + width -1); + bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y + height -1); rlelenptr = hdr; // Will store length of first field here later. hdr+=2; // Palette for (i=0; i<4; i++) - bytestream_put_be24(&hdr, ((uint32_t *)h->rects[0]->data[1])[i]); + bytestream_put_be24(&hdr, ((uint32_t *)frame->subtitle_areas[0]->pal)[i]); // Bitmap // RLE buffer. Reserve 2 bytes for possible padding after the last row. - init_put_bits(&pb, hdr, bufsize - (hdr - buf) - 2); - if (xsub_encode_rle(&pb, h->rects[0]->data[0], - h->rects[0]->linesize[0] * 2, - h->rects[0]->w, (h->rects[0]->h + 1) >> 1)) + init_put_bits(&pb, hdr, avpkt->size - (hdr - buf) - 2); + if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data, + frame->subtitle_areas[0]->linesize[0] * 2, + frame->subtitle_areas[0]->w, (frame->subtitle_areas[0]->h + 1) >> 1)) return AVERROR_BUFFER_TOO_SMALL; bytestream_put_le16(&rlelenptr, put_bytes_count(&pb, 0)); // Length of first field - if (xsub_encode_rle(&pb, h->rects[0]->data[0] + h->rects[0]->linesize[0], - h->rects[0]->linesize[0] * 2, - h->rects[0]->w, h->rects[0]->h >> 1)) + if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data + frame->subtitle_areas[0]->linesize[0], + frame->subtitle_areas[0]->linesize[0] * 2, + frame->subtitle_areas[0]->w, frame->subtitle_areas[0]->h >> 1)) return AVERROR_BUFFER_TOO_SMALL; // Enforce total height to be a multiple of 2 - if (h->rects[0]->h & 1) { - put_xsub_rle(&pb, h->rects[0]->w, PADDING_COLOR); + if (frame->subtitle_areas[0]->h & 1) { + put_xsub_rle(&pb, frame->subtitle_areas[0]->w, PADDING_COLOR); } flush_put_bits(&pb); - return hdr - buf + put_bytes_output(&pb); + avpkt->size = hdr - buf + put_bytes_output(&pb); + *got_packet = 1; + return 0; } static av_cold int xsub_encoder_init(AVCodecContext *avctx) @@ -217,6 +236,6 @@ const AVCodec ff_xsub_encoder = { .type = AVMEDIA_TYPE_SUBTITLE, .id = AV_CODEC_ID_XSUB, .init = xsub_encoder_init, - .encode_sub = xsub_encode, + .encode2 = xsub_encode, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; From patchwork Fri Dec 10 21:37:27 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32270 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2373174iog; Fri, 10 Dec 2021 13:38:35 -0800 (PST) X-Google-Smtp-Source: ABdhPJxmHiHbupXmPYV9obFCe9Tjypi0E52/V0H0Mq56Jn9VHaA6xk/uRhlbVgdbSlUQYt2xD9L+ X-Received: by 2002:a50:d74e:: with SMTP id i14mr41355455edj.243.1639172315207; Fri, 10 Dec 2021 13:38:35 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id qw32si8214421ejc.13.2021.12.10.13.38.34; Fri, 10 Dec 2021 13:38:35 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=rGHgEMPq; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id EB7FD68AF37; Fri, 10 Dec 2021 23:37:31 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM12-BN8-obe.outbound.protection.outlook.com (mail-bn8nam12olkn2098.outbound.protection.outlook.com [40.92.21.98]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 3B77C68AF32 for ; Fri, 10 Dec 2021 23:37:29 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=khTlQEuS3aPH3AAzYqB9ulJg/xj7WvkB6bpb9QPw5DDe4F06iXseqcbX9yNEnycbAe5n068ZmHHDod3eSqLfHuFpuSWyDUoCvXVN8SCltcSioZphiZPbzR/WCRIz5v0PyZ20LOCWyiPj7bWMhk0a5DyeV1bqPpwmx7GMT9hCrCYzqHfccPnsYHZOQBBE7JMF4yKhhJD7O5tF9mle7JyRv1K9hBOZfZ85L103CEFDtjlf0HyyyyvdU7FFIHzA5ZdlmccEC5R/IrmwehMj4dHbsP5U7oX9P/MqGPzRNxdNZFv5kyMNgwMSxZuVZHoosjngzyW+P/CHFOrnC2KY4LD//g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=zABAXZwKS+fGKLetN9nU1YP+cWesDOAoqYEA0xTWjtA=; b=ZzpQ6U9H0LQ8jDH62lnqffVGbc3us46qzxPmNhPDLbtH0sJCgAqxzY3+izz4IFLt9GtDeLKZsS+JhwUt0Kk7q4VXvmhusNL5dIoKG8cRI+DWbrFH4I1fbCdmUkU5+Pm9R396xkGpee9j28nx3P7Ivm/Atym3TWn3HrD5zCr+9N3NA0Wlcp4yJt7SdDYAQRS3sgcVHcu2iMasohxqkrs62vd/5Z8dnC4TOJqcMrB/+AwBUz+Wrb/Opr/Fz1pyF+bYAN7pA2CJJ2xBRzjYr1vYBiG3bebVT0hI6AAZOcKxaGkZnQ3Qbud4ii5DWp6dpdJ4qZTAIHACVRmaoZzQMu5+kQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=zABAXZwKS+fGKLetN9nU1YP+cWesDOAoqYEA0xTWjtA=; b=rGHgEMPqCQGizOMd9tBsXVXLM/2oe69kDH3hLm1q6PTHnDR6D6J0JxmX46/T/EoSGZlEkcJtij/c2/njJPL4kK0SSLO8L/7pL+bfA3zp4jNxT164Nh+VZv1BhWcYUCZw5DuNIHhuPnOoUmMIV47d0TG0z5dxws7eyxnFZtOynyhD0CrhihjeKetZTDbZAXJ0O/32JZF0rKHTZxUkHmn0dkWgT9M2TQbXTH9uM/kS0Udgo7jX7wPZyG6s5NCmcQRMg0dUd31GuJ36do7OyC/B80qyj/qPM/DoX+xyjrrAQuloBh1UQDzf4bpTCBVv1Zg8khfXPnMLhyNJQPSaHfvhpA== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0397.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:27 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:27 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 07/21] avcodec/subtitles: Replace deprecated enum values Thread-Index: AQHX7g4gWzxNZ4R7+Ea/SX8cUFv0uw== Date: Fri, 10 Dec 2021 21:37:27 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> In-Reply-To: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [6JNfzz3MHXfWv8BLkxIEuuRdN9uBY6qT] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: d86564b4-07f7-4255-d0a5-08d9bc254301 x-ms-traffictypediagnostic: DM8P223MB0397:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: 2kCXVppQ3j6T87407cfs1LaWNd8WdWJ71zflyWQ2LaBaQPRm9WCOx+M35Lp20LXabHdaGfLZF/LPjyok5iFjHWcpWjtfSyPGqcS8zlGNWh/O66dXaCg4kHBNce9p166cWUVxZbut7MXNIEAbCc7nqLoVeW1FTJU6lXHd8jrFjWPmaupHJyyTVwlWzTlE4L+vY2vy/N8W+Xn4LAfOz0+I3NJBSCnhvGexJTHYR7QnNypOiqvuVnACpUiWR4zkvE7HqHsNMIHqR9G7qMUcU1HPouGkW26yHNdrLm/g+0d+MWSVyaKIfqDT9mGyQcEAE4PteT4of2Se0qkdLMoXjTHTjuf2Uxd1B/ey5k5/lMpfqgfpmY6CfTLeFtgtgfVLqXcv/KGNylHY6xqisJ9Sa/WukEOsxfl4Erhfh8Sj48jssBe2dt3yWpKJmg4TQcvfRDLJFlMFB+zRWtRbzU5MmJUvxTT8l2VjODwWOTT52SPV46kjPE0Ah0PP274UtMcAGCHICXnMIhvyhkM70HsvL74839IlUTf0+QDD1Exzri0GtI82Asow2R3vaqpW+cFF75HUApric5KmUzIW5BbU7Jx7rA== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?VJ5xJWpa4lgWeRw9PJOm0j+0A058?= =?utf-8?q?3EpgxPmNnq8xi/Zr47BxPaojE6fn//OAH1EKQMDU3Wh47jojZTvTcAkUYpxHVII9P?= =?utf-8?q?PMmuoSI7DjfnVlk5gama5cCbtl3eFTYt9N4pHVSIvzgCXCFmifyiRdzmUSyKS9CtP?= =?utf-8?q?fwn3LBB8QC5jONZ2QpYzXMkdw88sOmlFm7Pt6HRd1LomXOJTY6Mat6/ypvLuGVul5?= =?utf-8?q?pXPu6LLglvC3HN8W5TGt5+ONHT4sGT29Sih//A1FXouMP+1Ave3XNbNeluNLxSii5?= =?utf-8?q?SMCqeXscFvPBeF4Mpl2oX3WyDJ+0q+YbD0jBbcrfStGYfAg6uCrJNRiYoj9dxStBX?= =?utf-8?q?oiQqgYxPf7tI+BnK5e3utMGllYKoEMOPReCMCAEzq8Bw++IM9Zc7QWRgC6tp7o+uw?= =?utf-8?q?ALWigcBFxx30tKse6HSl+zZRGxw57HSjpMLsS18FiG1kW5ZMMpZeL9T1cS6FKHJPw?= =?utf-8?q?TbtThXfWSd6anpWDxiK15c0ur7fD9V5RU3pQCHmC8joj6354gOgzYrVUWmRmlW53U?= =?utf-8?q?dbaRqH1B3aRtbxJ3MUvgUSbUCswcfMs8xaDSWbZZnr3uH8SbLbhgVMEflBqhHK62K?= =?utf-8?q?Qh8Nijl3ghsZkZ7eCgNJnq+vX/jAA9lEcKbCbBuC7YWMgcm3jSrOqWKoQgNm1DM46?= =?utf-8?q?7s5v7EM3AHxN+7Tt3fvLUaaCRyEMDl4GmukrYD7OwzqoXj4zxGww/76KDCWzH9TAn?= =?utf-8?q?JLb7rvypmxuGbvNdOilFloYRiiTOgY89Ye5IQypeHu3MgYrudJ2aQOSiwLUzPWRg2?= =?utf-8?q?LBAwdYUDzpFR/HC1BncDEKL19Ad3DPr/VWhq6ETsiE7prBOHxsj0ckKcpYKOFfvjD?= =?utf-8?q?2JMBtlmuXcQ3eNvITxx8gt4KviAQojy9BYkmaDMXAwY3PtjIjfokl1sBRrwThNrD7?= =?utf-8?q?QBe8zExPLOCe7q1xC8ZgVUaIoQyC8wcr8Q/tqX7Yofng=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: d86564b4-07f7-4255-d0a5-08d9bc254301 X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:27.3172 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0397 Subject: [FFmpeg-devel] [PATCH v23 07/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: JN/vNwzxf6p6 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 9c9cb01c8a..8e885b3ae2 100644 --- a/libavcodec/ass.h +++ b/libavcodec/ass.h @@ -82,7 +82,7 @@ static inline int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog, rects[sub->num_rects] = av_mallocz(sizeof(*rects[0])); if (!rects[sub->num_rects]) return AVERROR(ENOMEM); - rects[sub->num_rects]->type = SUBTITLE_ASS; + rects[sub->num_rects]->type = AV_SUBTITLE_FMT_ASS; ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog); if (!ass_str) return AVERROR(ENOMEM); diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c index 7802a44e71..fd321e7004 100644 --- a/libavcodec/assdec.c +++ b/libavcodec/assdec.c @@ -54,7 +54,7 @@ static int ass_decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr, if (!sub->rects[0]) return AVERROR(ENOMEM); sub->num_rects = 1; - sub->rects[0]->type = SUBTITLE_ASS; + sub->rects[0]->type = AV_SUBTITLE_FMT_ASS; sub->rects[0]->ass = av_strdup(avpkt->data); if (!sub->rects[0]->ass) return AVERROR(ENOMEM); diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c index 81ccaf4c57..b13244c803 100644 --- a/libavcodec/dvbsubdec.c +++ b/libavcodec/dvbsubdec.c @@ -795,7 +795,7 @@ static int save_subtitle_set(AVCodecContext *avctx, AVSubtitle *sub, int *got_ou rect->w = region->width; rect->h = region->height; rect->nb_colors = (1 << region->depth); - rect->type = SUBTITLE_BITMAP; + rect->type = AV_SUBTITLE_FMT_BITMAP; rect->linesize[0] = region->width; clut = get_clut(ctx, region->clut); diff --git a/libavcodec/dvdsubdec.c b/libavcodec/dvdsubdec.c index 52259f0730..b39b3d1838 100644 --- a/libavcodec/dvdsubdec.c +++ b/libavcodec/dvdsubdec.c @@ -406,7 +406,7 @@ static int decode_dvd_subtitles(DVDSubContext *ctx, AVSubtitle *sub_header, sub_header->rects[0]->y = y1; sub_header->rects[0]->w = w; sub_header->rects[0]->h = h; - sub_header->rects[0]->type = SUBTITLE_BITMAP; + sub_header->rects[0]->type = AV_SUBTITLE_FMT_BITMAP; sub_header->rects[0]->linesize[0] = w; sub_header->rects[0]->flags = is_menu ? AV_SUBTITLE_FLAG_FORCED : 0; } diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c index 4916410fc8..fbab88a992 100644 --- a/libavcodec/dvdsubenc.c +++ b/libavcodec/dvdsubenc.c @@ -273,7 +273,7 @@ static int encode_dvd_subtitles(AVCodecContext* avctx, AVPacket* avpkt, return AVERROR(EINVAL); for (i = 0; i < rects; i++) - if (frame->subtitle_areas[i]->type != SUBTITLE_BITMAP) { + if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_BITMAP) { av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n"); return AVERROR(EINVAL); } diff --git a/libavcodec/pgssubdec.c b/libavcodec/pgssubdec.c index 388639a110..4829babb7c 100644 --- a/libavcodec/pgssubdec.c +++ b/libavcodec/pgssubdec.c @@ -539,7 +539,7 @@ static int display_end_segment(AVCodecContext *avctx, void *data, return AVERROR(ENOMEM); } sub->num_rects++; - sub->rects[i]->type = SUBTITLE_BITMAP; + sub->rects[i]->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 979399bae6..5c9e65f737 100644 --- a/libavcodec/xsubdec.c +++ b/libavcodec/xsubdec.c @@ -107,7 +107,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr, } sub->rects[0]->x = x; sub->rects[0]->y = y; sub->rects[0]->w = w; sub->rects[0]->h = h; - sub->rects[0]->type = SUBTITLE_BITMAP; + sub->rects[0]->type = AV_SUBTITLE_FMT_BITMAP; sub->rects[0]->linesize[0] = w; sub->rects[0]->data[0] = av_malloc(w * h); sub->rects[0]->nb_colors = 4; From patchwork Fri Dec 10 21:37:29 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32271 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2373376iog; Fri, 10 Dec 2021 13:38:46 -0800 (PST) X-Google-Smtp-Source: ABdhPJyviqiFTEaFkm7a5MqQ40uviBzw9reC8yAHpfp6ZdmHzpAvgBw6VesDVp7rxk16k0JJ3P+a X-Received: by 2002:aa7:c301:: with SMTP id l1mr42410473edq.20.1639172326295; Fri, 10 Dec 2021 13:38:46 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id z2si6740335ejm.238.2021.12.10.13.38.45; Fri, 10 Dec 2021 13:38:46 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=ClHFehFq; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id EB33868AF71; Fri, 10 Dec 2021 23:37:32 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM12-BN8-obe.outbound.protection.outlook.com (mail-bn8nam12olkn2098.outbound.protection.outlook.com [40.92.21.98]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 97B6A68AF32 for ; Fri, 10 Dec 2021 23:37:30 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=QAVdNHqVuUKKMBMN6s1kOUioh3AgBaxJqzgpM/4hkgl6SJbqaP/aybG5N1dx16lOqt2Cdmq7mD4DSNi2LyumpmBw8qkbSRAXkgv+Aqr03DSEruqRyGNZ+wzHxQ4Xo/fTKPKmMzYUlprLM/lCZapnJdlX7+eHfiO2BzrcuCqqMFzPYFJ24V46ozZFyIdeY37pmgQAm45BSHFVV1IR6VnzQwRr3CLJITy25iQVwiH5tG10xtTJ9oiWRHo1nGHvACxfwKX+Y2/k3Y/E4KQJJTKU3koRxIyEEYZsK03QVrBQVHfMzdbvoFOs7GlyAdhKOS/tgvVbgAwUrJ9K6hNWKuTxMg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=agoTNQuVB2Ais+uQPxc+g18TNwhkXmRPdQt0SQn6ozo=; b=C79dB2h9pQBQQSKK6avNLPka7X1znRKy7ZF1XvfwF61G5hQv/0vVIHPc+TfIQRFCbgF4imR4CsnwXVXtD3TONq+mpkILHGQRgn4nJEypGMfJEV4WX66a9ME3xgJUMCab3n3fNZ7dav/UF8hKhrRZ33bdslzKgYb1bcJ2PDiyYE9Kes06IeKRizw3XBxmRUORH30wd4as+uTDGRbh8BbQGgUlv1brMtrPVjX+vgVUMX36EL13XBtaVQ4jZ/hmi+nhMVKqRyzCZvtEuojmMzHsAAbkQxN/Lfb4hHEccOBNKznGSsTeQmgdVmekWEY/yJzNmdtjrTyX6TAnMsptnnk2dQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=agoTNQuVB2Ais+uQPxc+g18TNwhkXmRPdQt0SQn6ozo=; b=ClHFehFqOwdKZMk3sc9r9iGqmTpEq1x9IZZ5ZMfOILTGD/ieUDmpHNG0askNC5fVCttLNKIzXsjoxZYcLCc4PJR6bBXoKbAd7IV1/Qml9FcQ+fPyw2owHoHOA3VDEPeDmcDYFsXleM4PpoPcGTNhibv0dLTwGum5UuyWt2KXF23UKQJ5DFJnesyKyG1TQYNQRK6sILGZ7NH+9eA7cN1IwA1dSQv31/DYlXMdxbtMXVozRY8sxMCZqFolBF4yd+tkM/2noFmgf/mUSmwl54N9omXTh7vHqtR3m2+EeWJJL39Ds1CG4k2/d52T3PFuJeLovVc9S9tQG3eRAdsC2qpZyg== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0397.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:29 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:29 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 08/21] fftools/play,probe: Adjust for subtitle changes Thread-Index: AQHX7g4hU0IlXKdhWEmj86BKdqxa5g== Date: Fri, 10 Dec 2021 21:37:29 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> In-Reply-To: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [zKdZOXo8A0IVSTHxMhUPR9PvFNXolTDK] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 8bdb3784-a76c-4333-9b14-08d9bc254441 x-ms-traffictypediagnostic: DM8P223MB0397:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: CXKCihHiGj1uY8UlkIz9GCKPQeCiSq/BNHoxi8xEzSs+b990nDUU05Wg2n3C7P8yI4nDYT2PZQzqvfL2uyAwjKjpWjyT6r2tG4BnIvhjPDo+VrKzeVj5iI3tT5SGPjIoukqk4PWsCB+YTzinO3UBxndVDqxbBawAd3rfg93jUX+TRA/XSuA3TqCD+ctlrRK55MHINZg6OOVwiBG77suEE14QGI2r/LPygyddjKeey7qvB/2fSMIq8Bg/xlr4myKK2WqurOZqMT4alUjuSDxldCaaGxpQsCLJM7iftvhXjVrpZFH6G4FK4CAeq9YndSpPTRhpPyGA5o0Mpw4hRvHqzxL7XbX2JhDW7oosD6YK6a3rrqIsoFrtUhqGHEWYVOLv16zeuJtvX4u5Iqm0PethZTY8DquIDzRWtB7Hgdoa771Q55tfhwDPMVgZuFN2AFPfnzrIQxd/8p9YlynO3MfKqxahsORM1QQD4F4FbH+yY1Yh+xZ8EH7Y4dBThkP7mYJejHvB06uZnbC5iyHl4kjtWUH5ma3h7td53SuJKG7KMiTSYfK//RIzWDkoqErRpHRrfzc66SIweoQ9WzqH1plndw== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?YkU3NUh8uZki8v2SjTN9w7DUEuiz?= =?utf-8?q?y4Enfz7qtN3sI6WB6wzZJb6+Aj7hv465tLC7QGaus8vDmHY7zPVYSGtJXUU3dgGsK?= =?utf-8?q?jCzcLSy6UuhjUXhZT/i8EPpaqxksdUweDFN6fNZtBTctj8wj7Wiua+Cka5hcF/NJd?= =?utf-8?q?cOnastPKyH7ZC5ZmzPw+aeCzjoE4oxdCSENWln9uMlbLjCt79ezXjaXqZifzJS37H?= =?utf-8?q?D6X9PMBnHMld8IofqPIr/5tJjV2p1SS0ldWrhw+Sm3xrgZLxmwWH54h3gdirdogLG?= =?utf-8?q?ASOa+d+uziJe6QE3qPFtxhaflW10RUjtKC3MYyo+urSnsoKGp6mo/CKLfWcm+IGSu?= =?utf-8?q?7NUe8ph/rUMwThbH/PH8jo7bgHz6WTv4DZqvRShu61Zc93ARXXVe6QuHRH04sY8BK?= =?utf-8?q?mN6HSlwcNdSCpEZhubtmkO7qDClzX4+1WDziSWOtfGVN9qamv39zKj1IgXZyY2ZSc?= =?utf-8?q?x+LBjx4vN02IUPFZFeCPYPl6qRsWKQ/9/wZg3guRevWFi6uWITIcFTMhkPkG5dHi+?= =?utf-8?q?vQhkdow94ScOboCQ7ah127wlx39voLubqxeWwWKMhdROrAIXJ3h274ZaE8d9mnN2i?= =?utf-8?q?pmDxh3fm/fptkZv/gynFcPHwiwt6gQ4DIGJ2CpdyhZ/Fn5Uy313XTCbF/KdLZdv/n?= =?utf-8?q?/bM5VtrxDm1GCri4EPPJN5kSGU6hYB6TOQteDcz5dN3p7YHiub2StkAh4wedlKs7Y?= =?utf-8?q?WhRIsJtSIB3lDQLEKQLdlIJZBF8bSPdKH7yY/w507G04oPk7bLtGlcp1t13PV8xWE?= =?utf-8?q?+zEGoT8tVerliflo6yB4cWOUDt8h3dWAn+Dtw7Mhjl4km4SmYJiFw1sDrZZlI4TrT?= =?utf-8?q?yyaPDTo6Ocw34Uu5aAN5UJ55gP24rqVHJp/eApsesK28Y4P/zpBFKapYTngGP0snL?= =?utf-8?q?zzOR5X93LLPELxwdXY2R+cpRN9wmVpZuxss8trxOPJzg=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 8bdb3784-a76c-4333-9b14-08d9bc254441 X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:29.4189 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0397 Subject: [FFmpeg-devel] [PATCH v23 08/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: bZHXFzGjmf48 Signed-off-by: softworkz --- fftools/ffplay.c | 102 +++++++++++++++++++++------------------------- fftools/ffprobe.c | 48 ++++++++++++++-------- 2 files changed, 78 insertions(+), 72 deletions(-) diff --git a/fftools/ffplay.c b/fftools/ffplay.c index e7b20be76b..0af32888da 100644 --- a/fftools/ffplay.c +++ b/fftools/ffplay.c @@ -152,7 +152,6 @@ typedef struct Clock { /* Common struct for handling all types of decoded data and allocated render buffers. */ typedef struct Frame { AVFrame *frame; - AVSubtitle sub; int serial; double pts; /* presentation timestamp for the frame */ double duration; /* estimated duration of the frame */ @@ -586,7 +585,7 @@ static int decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue, S return 0; } -static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) { +static int decoder_decode_frame(Decoder *d, AVFrame *frame) { int ret = AVERROR(EAGAIN); for (;;) { @@ -620,6 +619,9 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) { } } break; + case AVMEDIA_TYPE_SUBTITLE: + ret = avcodec_receive_frame(d->avctx, frame); + break; } if (ret == AVERROR_EOF) { d->finished = d->pkt_serial; @@ -652,25 +654,11 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) { av_packet_unref(d->pkt); } while (1); - if (d->avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { - int got_frame = 0; - ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, d->pkt); - if (ret < 0) { - ret = AVERROR(EAGAIN); - } else { - if (got_frame && !d->pkt->data) { - d->packet_pending = 1; - } - ret = got_frame ? 0 : (d->pkt->data ? AVERROR(EAGAIN) : AVERROR_EOF); - } - av_packet_unref(d->pkt); + if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) { + av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n"); + d->packet_pending = 1; } else { - if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) { - av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n"); - d->packet_pending = 1; - } else { - av_packet_unref(d->pkt); - } + av_packet_unref(d->pkt); } } } @@ -683,7 +671,6 @@ static void decoder_destroy(Decoder *d) { static void frame_queue_unref_item(Frame *vp) { av_frame_unref(vp->frame); - avsubtitle_free(&vp->sub); } static int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last) @@ -981,7 +968,7 @@ static void video_image_display(VideoState *is) if (frame_queue_nb_remaining(&is->subpq) > 0) { sp = frame_queue_peek(&is->subpq); - if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) { + if (vp->pts >= sp->pts + ((float) sp->frame->subtitle_start_time / 1000)) { if (!sp->uploaded) { uint8_t* pixels[4]; int pitch[4]; @@ -993,25 +980,27 @@ static void video_image_display(VideoState *is) if (realloc_texture(&is->sub_texture, SDL_PIXELFORMAT_ARGB8888, sp->width, sp->height, SDL_BLENDMODE_BLEND, 1) < 0) return; - for (i = 0; i < sp->sub.num_rects; i++) { - AVSubtitleRect *sub_rect = sp->sub.rects[i]; + for (i = 0; i < sp->frame->num_subtitle_areas; i++) { + AVSubtitleArea *area = sp->frame->subtitle_areas[i]; + SDL_Rect sdl_rect = { .x = area->x, .y = area->y, .w = area->w, .h = area->h }; - sub_rect->x = av_clip(sub_rect->x, 0, sp->width ); - sub_rect->y = av_clip(sub_rect->y, 0, sp->height); - sub_rect->w = av_clip(sub_rect->w, 0, sp->width - sub_rect->x); - sub_rect->h = av_clip(sub_rect->h, 0, sp->height - sub_rect->y); + area->x = av_clip(area->x, 0, sp->width ); + area->y = av_clip(area->y, 0, sp->height); + area->w = av_clip(area->w, 0, sp->width - area->x); + area->h = av_clip(area->h, 0, sp->height - area->y); is->sub_convert_ctx = sws_getCachedContext(is->sub_convert_ctx, - sub_rect->w, sub_rect->h, AV_PIX_FMT_PAL8, - sub_rect->w, sub_rect->h, AV_PIX_FMT_BGRA, + area->w, area->h, AV_PIX_FMT_PAL8, + area->w, area->h, AV_PIX_FMT_BGRA, 0, NULL, NULL, NULL); if (!is->sub_convert_ctx) { av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n"); return; } - if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)pixels, pitch)) { - sws_scale(is->sub_convert_ctx, (const uint8_t * const *)sub_rect->data, sub_rect->linesize, - 0, sub_rect->h, pixels, pitch); + if (!SDL_LockTexture(is->sub_texture, &sdl_rect, (void **)pixels, pitch)) { + const uint8_t* data[2] = { area->buf[0]->data, (uint8_t *)&area->pal }; + sws_scale(is->sub_convert_ctx, data, area->linesize, + 0, area->h, pixels, pitch); SDL_UnlockTexture(is->sub_texture); } } @@ -1038,16 +1027,18 @@ static void video_image_display(VideoState *is) #if USE_ONEPASS_SUBTITLE_RENDER SDL_RenderCopy(renderer, is->sub_texture, NULL, &rect); #else - int i; + unsigned i; double xratio = (double)rect.w / (double)sp->width; double yratio = (double)rect.h / (double)sp->height; - for (i = 0; i < sp->sub.num_rects; i++) { - SDL_Rect *sub_rect = (SDL_Rect*)sp->sub.rects[i]; - SDL_Rect target = {.x = rect.x + sub_rect->x * xratio, - .y = rect.y + sub_rect->y * yratio, - .w = sub_rect->w * xratio, - .h = sub_rect->h * yratio}; - SDL_RenderCopy(renderer, is->sub_texture, sub_rect, &target); + for (i = 0; i < sp->frame->num_subtitle_areas; i++) { + AVSubtitleArea *area = sp->frame->subtitle_areas[i]; + SDL_Rect sub_rect = { .x = area->x, .y = area->y, + .w = area->w, .h = area->h}; + SDL_Rect target = {.x = rect.x + sub_rect.x * xratio, + .y = rect.y + sub_rect.y * yratio, + .w = sub_rect.w * xratio, + .h = sub_rect.h * yratio}; + SDL_RenderCopy(renderer, is->sub_texture, &sub_rect, &target); } #endif } @@ -1651,19 +1642,20 @@ retry: sp2 = NULL; if (sp->serial != is->subtitleq.serial - || (is->vidclk.pts > (sp->pts + ((float) sp->sub.end_display_time / 1000))) - || (sp2 && is->vidclk.pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000)))) + || (is->vidclk.pts > (sp->pts + ((float) sp->frame->subtitle_end_time / 1000))) + || (sp2 && is->vidclk.pts > (sp2->pts + ((float) sp2->frame->subtitle_start_time / 1000)))) { if (sp->uploaded) { int i; - for (i = 0; i < sp->sub.num_rects; i++) { - AVSubtitleRect *sub_rect = sp->sub.rects[i]; + for (i = 0; i < sp->frame->num_subtitle_areas; i++) { + AVSubtitleArea *area = sp->frame->subtitle_areas[i]; + SDL_Rect sdl_rect = { .x = area->x, .y = area->y, .w = area->w, .h = area->h }; uint8_t *pixels; int pitch, j; - if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)&pixels, &pitch)) { - for (j = 0; j < sub_rect->h; j++, pixels += pitch) - memset(pixels, 0, sub_rect->w << 2); + if (!SDL_LockTexture(is->sub_texture, &sdl_rect, (void **)&pixels, &pitch)) { + for (j = 0; j < area->h; j++, pixels += pitch) + memset(pixels, 0, area->w << 2); SDL_UnlockTexture(is->sub_texture); } } @@ -1774,7 +1766,7 @@ static int get_video_frame(VideoState *is, AVFrame *frame) { int got_picture; - if ((got_picture = decoder_decode_frame(&is->viddec, frame, NULL)) < 0) + if ((got_picture = decoder_decode_frame(&is->viddec, frame)) < 0) return -1; if (got_picture) { @@ -2048,7 +2040,7 @@ static int audio_thread(void *arg) return AVERROR(ENOMEM); do { - if ((got_frame = decoder_decode_frame(&is->auddec, frame, NULL)) < 0) + if ((got_frame = decoder_decode_frame(&is->auddec, frame)) < 0) goto the_end; if (got_frame) { @@ -2246,14 +2238,14 @@ static int subtitle_thread(void *arg) if (!(sp = frame_queue_peek_writable(&is->subpq))) return 0; - if ((got_subtitle = decoder_decode_frame(&is->subdec, NULL, &sp->sub)) < 0) + if ((got_subtitle = decoder_decode_frame(&is->subdec, sp->frame)) < 0) break; pts = 0; - if (got_subtitle && sp->sub.format == 0) { - if (sp->sub.pts != AV_NOPTS_VALUE) - pts = sp->sub.pts / (double)AV_TIME_BASE; + if (got_subtitle && sp->frame->format == AV_SUBTITLE_FMT_BITMAP) { + if (sp->frame->subtitle_pts != AV_NOPTS_VALUE) + pts = sp->frame->subtitle_pts / (double)AV_TIME_BASE; sp->pts = pts; sp->serial = is->subdec.pkt_serial; sp->width = is->subdec.avctx->width; @@ -2263,7 +2255,7 @@ static int subtitle_thread(void *arg) /* now we can update the picture count */ frame_queue_push(&is->subpq); } else if (got_subtitle) { - avsubtitle_free(&sp->sub); + av_frame_free(&sp->frame); } } return 0; diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c index 0711e02922..367c708e52 100644 --- a/fftools/ffprobe.c +++ b/fftools/ffprobe.c @@ -2208,22 +2208,43 @@ 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_pts); + print_time("pts_time", sub->subtitle_pts, &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 ("start_display_time", sub->subtitle_start_time); + print_int ("end_display_time", sub->subtitle_end_time); + print_int ("num_subtitle_rects", sub->num_subtitle_areas); writer_print_section_footer(w); @@ -2388,7 +2409,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); @@ -2396,6 +2416,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)) { @@ -2414,12 +2435,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; } @@ -2434,12 +2449,11 @@ static av_always_inline int process_frame(WriterContext *w, nb_streams_frames[pkt->stream_index]++; if (do_show_frames) if (is_sub) - show_subtitle(w, &sub, ifile->streams[pkt->stream_index].st, fmt_ctx); + show_subtitle(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx); else show_frame(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx); - if (is_sub) - avsubtitle_free(&sub); } + return got_frame || *packet_new; } From patchwork Fri Dec 10 21:37:31 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32272 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2373548iog; Fri, 10 Dec 2021 13:38:58 -0800 (PST) X-Google-Smtp-Source: ABdhPJx7HNvhTctNWjT0OPqxItH1aIoHWJwvz3bkH5v65MVChaA8QCqWtLBjjMJcjzGqsS+jQsMB X-Received: by 2002:a17:906:eb8a:: with SMTP id mh10mr25800478ejb.198.1639172337913; Fri, 10 Dec 2021 13:38:57 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id sb42si7493425ejc.480.2021.12.10.13.38.57; Fri, 10 Dec 2021 13:38:57 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=IjBzYHcq; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 0E4A568AF2D; Fri, 10 Dec 2021 23:37:36 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM12-BN8-obe.outbound.protection.outlook.com (mail-bn8nam12olkn2011.outbound.protection.outlook.com [40.92.21.11]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 1251768AF3A for ; Fri, 10 Dec 2021 23:37:33 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=JbGhLvlq5RpRlGrLIwre32A4KRm0ddej/GLVZFSQ6EWhPioLWMV0L9CGvJnzadLW9CJ16l2VLKPNWKgupAfJfIc7FTabrbuF3QI+9gL/CYX5a1mz93w667GX37nUuR+l4Ma2IlrEyRdv7VPHBUI1s5+2EwtvPFIW1Oamy+sXAb6M+v9TnFn+Dw6sBqF/YufIZH3wSZGDiRBIFmv3WWf6cbcfIbkwJ4FUKBSyaSET1j4ijgd/xlPiJZjlMBtSodBL7cYTfVkGo3waKX7xdBS3ogeRPPhSH/Qv7ZjBOyC6e5OERyA2l/YLWgUqW1eLm0O8Hn9ZVxdHBrBicCydGAGWtw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=MRY/E5+NIlzJScNIyQUozlVnJtCJhDhtHCVpijJk8uM=; b=MeSEAEJ1oOzdQLPao86lmxW/YZbKFJ/YwdZSk1gFqdDcVUvfwMPMRr6QUaT5CRNPXGRO4W8WEpAitzIRENDMb+s2UzMOCDMfu3wY12t8vtB6X9K4zjJgRSpDtfai7R6bGRP2KRofuV0WRWfA8hpw1pWjI2hxexg+9NXOsBq3z/ZzEl1tCxlO1wCnt/weqglmH9cuVidqU8+/3PmhKaBzWQr3Xqev1YzqsQMHm6CQfwizNMblxdpfpm1b2hhVCGxpfZ89LUSvZU93wxw5Sa5fUnVBkWuUyTHEyAZQ48I9fjIKYimWqdDQy14iWrDuDXKtmGYFHdpAqQTvymP2bu58EQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=MRY/E5+NIlzJScNIyQUozlVnJtCJhDhtHCVpijJk8uM=; b=IjBzYHcqfvpBbJ9VhxH5JTQ9UbQJBuB2q7/RutzSyNXFWdJ0Kq9UpIoedrOxVwZsDqcVRAe4f4fAkB2QFYzibs8WCoyNT7vXZ9LJAy/AzX4b56SKSmIhm9JSghZegaZrSOb94o9cT/6fDswTjkp1HakfS+1r8WQ4mpkGTvoA4peZkxgfJTr1JKF1zliKnULBke8GY50aKfaTFUcsIe3IzYiB1mxqmUju6MjDK71nwy8wi8GerSVu6PGTOEKvC83yqhkymhlaWxi9u6YFIjqryrX9wbDcGXXWE2iMOv1LwGeJ7ejsjSeEjNMR7bR4z9g8ja1C3RUL8SWbYhzGGitN3w== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0397.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:31 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:31 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 09/21] avfilter/subtitles: Add subtitles.c for subtitle frame allocation Thread-Index: AQHX7g4iiGzA5lq7Dku29RpPb/MJTw== Date: Fri, 10 Dec 2021 21:37:31 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> <75bb9ceffef9e8e2d05ffce8a51c1991d5d3a973.1639172095.git.softworkz@hotmail.com> In-Reply-To: <75bb9ceffef9e8e2d05ffce8a51c1991d5d3a973.1639172095.git.softworkz@hotmail.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [/0TjQrLdZS4o3XAstQv6FRdx2htRb/ub] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 4271f08f-bed2-4e6c-dcc9-08d9bc25456e x-ms-traffictypediagnostic: DM8P223MB0397:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: Kyz+82qESiHy/oopYAhr7oaxBzssVFGRDa71HsUyGqwKXttbRYECIXWgvtsoVKdb2pt4Bns3XoN3IfE5Acq+KyeSWtBXYstKTHgEk9QTOG+wKod9C+CsqZ+fv7U7uZ4pZE1lrQKBtbRX5s+F1asV9slVuNI2dr24uXnNmxLCTnCsW1Ute8N+3ZAPtsCUGwoKCtzm+rf+XOsTQtRT7QFZ5pFaU5AgZ2n8n8hi2g24mrEt8BrVRPdZEDJ5/Z29Ar/SpjuPr2NrRLJ4wJ237QsQvn7t4Klr3lmCPJzH7ew16Y9LjssRIlUYxrAYCZnbgs2LH1iKJ1bP/vXiuJ29p7CeSL9zhycQ8UvuJqe0c/ibqj7mc5dnpYwf5UT0gbNuFr9a3aOZOo3N13BhXfbOuA5OQa1rGT1dJuW58klmeng1hOCTB8e4b1BF8oGIJlLBD9l5gkWF1OEcRp1EM2Dl0gHftL8IEUfEHtv9/flUbt+zwFC2IqERYFfGLLtaBmD9rAYUXgSxvaUhBBgfHPFJaLn5OkBi4BmYOT5eD1Ez4yqS9gm2EKnKATOfMz1SajqDWZQ9575kAoyrVKWkQcoOMSS+qQ== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?oX7hOCZ0HFACOeQKE97fpTIlaBeo?= =?utf-8?q?Ew46pt67DbZirNst1E6VVqPWvcqrrLJ0X98c5vWfZ6FvXYkHI20mNZqcBv6imZsdb?= =?utf-8?q?pcdpjW86JRPrSqvNe7i3YYmQpvjXj8hYMcWC1XZV6dmekNu2KYs8qTD9R+nxUT9y/?= =?utf-8?q?hOWHBMri1ICtFBMHr/MPFVDKm6k02bDU55mBE6TP1qfqFLTdY8VtrRB97qOsI0bsX?= =?utf-8?q?YLN/Zl3IVPBZFCzKJFVH2KSJlgiGkfCUqg4YlmghrwGTcus3MAcSMQilgofQrXg60?= =?utf-8?q?55T96zG6T/ISD1rpsG7Maa71Yt8nkbSqtzQfdb8bi4JGb20g7A/ctMijdusZxVHsx?= =?utf-8?q?bBQcwDo/UYUlxTPGUhpboGQ5pgcXjJe2hRYl/jF1aYA57bSjX065KF15khWhcQtVc?= =?utf-8?q?8SVIB0op2pzl7kYY2U7EeiDxzBAMAfKFMhPtjLWxtsYsNL/ZckI7TiR4CZytSzJXu?= =?utf-8?q?kVEQldVKyrGow9l60XdgmIwRrMCDKUMPYA0seeaE/EaOk+FPm/cPuSsDtDmdSGEaE?= =?utf-8?q?eOfUtSdPuGbvp62LCYNcTy5pcgIsVN/OFO4o4UuT4QjOKLnl7rAho4XLBeKny7WBa?= =?utf-8?q?lFASRiOaCClHMGEyVHCYQSmwQirMVGWw4jpOMY/sYtIPm3gPKbfgauDIaZH74noHp?= =?utf-8?q?ESHthXJbGRSNk2LRXWD9LdboERexXq9AwMB9/ahus9ZROt49pm36pBFnbv/Bg+k5J?= =?utf-8?q?WkNXiwETwQanWXVV42T/4vZQ0kyuAKq5BGDfufiY8rqBCD8alvp1rBqve5b6zANDU?= =?utf-8?q?a4oaor3VvwjEOlAOLeWKN5gMoDd7o1A3nGoCa5q4er9wjHcFtLg9idMuijThNEI9L?= =?utf-8?q?27AXtvkV058+eo1a1S+bFacTVhe+ZrufA+NbqBb+fZwOSfVmQHrzsVkLZrFewukla?= =?utf-8?q?2SEjqPetpTlwKdUwi6GrOroLhY0sG/RjdckevERMMkpA=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 4271f08f-bed2-4e6c-dcc9-08d9bc25456e X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:31.3156 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0397 Subject: [FFmpeg-devel] [PATCH v23 09/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: BNg+blDLpSIC 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 c8082c4a2f..664566a18a 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -19,6 +19,7 @@ OBJS = allfilters.o \ framequeue.o \ graphdump.o \ graphparser.o \ + subtitles.o \ video.o \ OBJS-$(HAVE_THREADS) += pthread.o diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index 7362bcdab5..df5b8f483c 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -43,6 +43,7 @@ #include "formats.h" #include "framepool.h" #include "internal.h" +#include "subtitles.h" #include "libavutil/ffversion.h" const char av_filter_ffversion[] = "FFmpeg version " FFMPEG_VERSION; @@ -1475,6 +1476,9 @@ int ff_inlink_make_frame_writable(AVFilterLink *link, AVFrame **rframe) case AVMEDIA_TYPE_AUDIO: out = ff_get_audio_buffer(link, frame->nb_samples); break; + case AVMEDIA_TYPE_SUBTITLE: + out = ff_get_subtitles_buffer(link, link->format); + break; default: return AVERROR(EINVAL); } diff --git a/libavfilter/internal.h b/libavfilter/internal.h index 1099b82b4b..fc09ef574c 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -90,6 +90,7 @@ struct AVFilterPad { union { AVFrame *(*video)(AVFilterLink *link, int w, int h); AVFrame *(*audio)(AVFilterLink *link, int nb_samples); + AVFrame *(*subtitle)(AVFilterLink *link, int format); } get_buffer; /** diff --git a/libavfilter/subtitles.c b/libavfilter/subtitles.c new file mode 100644 index 0000000000..951bfd612c --- /dev/null +++ b/libavfilter/subtitles.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 softworkz + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/common.h" + +#include "subtitles.h" +#include "avfilter.h" +#include "internal.h" + + +AVFrame *ff_null_get_subtitles_buffer(AVFilterLink *link, int format) +{ + return ff_get_subtitles_buffer(link->dst->outputs[0], format); +} + +AVFrame *ff_default_get_subtitles_buffer(AVFilterLink *link, int format) +{ + AVFrame *frame; + + frame = av_frame_alloc(); + if (!frame) + return NULL; + + frame->format = format; + frame->type = AVMEDIA_TYPE_SUBTITLE; + + if (av_frame_get_buffer2(frame, 0) < 0) { + av_frame_free(&frame); + return NULL; + } + + return frame; +} + +AVFrame *ff_get_subtitles_buffer(AVFilterLink *link, int format) +{ + AVFrame *ret = NULL; + + if (link->dstpad->get_buffer.subtitle) + ret = link->dstpad->get_buffer.subtitle(link, format); + + if (!ret) + ret = ff_default_get_subtitles_buffer(link, format); + + return ret; +} diff --git a/libavfilter/subtitles.h b/libavfilter/subtitles.h new file mode 100644 index 0000000000..4a9115126e --- /dev/null +++ b/libavfilter/subtitles.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 softworkz + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFILTER_SUBTITLES_H +#define AVFILTER_SUBTITLES_H + +#include "avfilter.h" +#include "internal.h" + +/** default handler for get_subtitles_buffer() for subtitle inputs */ +AVFrame *ff_default_get_subtitles_buffer(AVFilterLink *link, int format); + +/** get_subtitles_buffer() handler for filters which simply pass subtitles along */ +AVFrame *ff_null_get_subtitles_buffer(AVFilterLink *link, int format); + +/** + * Request a subtitles frame with a specific set of permissions. + * + * @param link the output link to the filter from which the buffer will + * be requested + * @param format The subtitles format. + * @return A reference to the frame. This must be unreferenced with + * av_frame_free when you are finished with it. +*/ +AVFrame *ff_get_subtitles_buffer(AVFilterLink *link, int format); + +#endif /* AVFILTER_SUBTITLES_H */ From patchwork Fri Dec 10 21:37:33 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32275 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2373686iog; Fri, 10 Dec 2021 13:39:07 -0800 (PST) X-Google-Smtp-Source: ABdhPJxRwSG1uL3faz446j495rqu7tk9wMuBG1yLSNVd90hjoGGaAA6+mtkQZrfW1B9amVXcZfWs X-Received: by 2002:a05:6402:5146:: with SMTP id n6mr42381181edd.126.1639172347134; Fri, 10 Dec 2021 13:39:07 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id d9si5150875ejr.221.2021.12.10.13.39.06; Fri, 10 Dec 2021 13:39:07 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=OR3jhfSL; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 1719E68AF81; Fri, 10 Dec 2021 23:37:37 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM12-BN8-obe.outbound.protection.outlook.com (mail-bn8nam12olkn2011.outbound.protection.outlook.com [40.92.21.11]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 879EA68AF3A for ; Fri, 10 Dec 2021 23:37:34 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=NKFko3nfS2B2tbLIrMJ9olofSRJ9yroInOWa0bvInfec1KiIRHH2jiPCCQP98uH/KP4mRR9QQ2wO5hwXkQMXl82MP/tMSOiMWn0cqaZvUb31SDEdZoYAWCxo8OvmAyMTCnJa+5wvCM6/DnqnzLqXqWDHs9zLIQCmwDNmOEIMDDg92MK484IpAEtbl0sM7uVwiVuZYjki/dUC/GYpDuKQPAxMXgMYPWVw1l9erVHeeJ+8rYhPOWKIM4v+csHUCKWIMkLonwn8HAAGvu0ySnZ4enfKWpBKen6J53tUGxawgv3jEeJJ9tI1ke/OrEqDROmuiMzp9IPxBCfUiBLcql3xlw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=qgJmS0GpwRFmLIz6mjkSSnEW3v7UTXOe8XP6GYa7hBQ=; b=BX0ESGDBr25IabGLIpOpyKWazPhPfJvTbhXjXSwJVYpiJ+858Sq8uq/pn2nuXHmK0vuDuqyB11f7tX9CFlT1an1yDYNu9u/2u0APYq2B1PEqF5PGbjlC0xBB9u/PqHh6IKtsbqa+JjtB84yf6wDqs5UYESWbi+Q4xjtBbvuTgCLLHkWKpUpXXk6dFQQEBzT1MCbWXYTyH3e55QwVm/eQV3cWKdF+IQ9FW/4GQp6rREU5oUfFKVK8JUJPN+wJCp4hiW9DLQnOE/0rFaMBqLlleaPDexyZZ/6z5qIiR/0BhkfVdh2OtKhZocUZmbJ/Rh6Oasao6imZ4fhLHhWoYjjXNA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=qgJmS0GpwRFmLIz6mjkSSnEW3v7UTXOe8XP6GYa7hBQ=; b=OR3jhfSLnRzGFNpZ9enzjoZNEo5vsISqEOfuALFSpeJA4KxbukmuYrRBce23LGrFDlHNO9LzVs9NVqbJl8rxhCkzUHTT3izLYhqzNkt9xunyKfh+U7w7RaMCZeiQ+0kJAktgTB51zeZLjrs6y/CiG3dORThtMA1xPWO0s4N6eVShza/znWyLWVTh730kynxMKjnU8gNdSNqKGuTqHxUmApKPNpXTiDQHdFOKmM3CHADqXjEdgvs+L4VPe05tohUnu+q2M7ySOnJ6DbJRenbLZ/RfS+k6/dxtAQHKA1520SIeha+ufHoFiI7ia4np8e4QR48niQYK03pm50i3VrAFyA== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0397.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:33 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:33 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 10/21] avfilter/avfilter: Handle subtitle frames Thread-Index: AQHX7g4k9dFhErHf0UGtxozAko+wIQ== Date: Fri, 10 Dec 2021 21:37:33 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> <75bb9ceffef9e8e2d05ffce8a51c1991d5d3a973.1639172095.git.softworkz@hotmail.com> <554aab35996d92c90ab3b0ecb2c0c6c1278479d6.1639172095.git.softworkz@hotmail.com> In-Reply-To: <554aab35996d92c90ab3b0ecb2c0c6c1278479d6.1639172095.git.softworkz@hotmail.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [Ph/yrxjC5cTs02uv9PHscSBaKfbtQNFj] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 26af0159-676d-45da-9942-08d9bc25468e x-ms-traffictypediagnostic: DM8P223MB0397:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: 38Fe+PBEJhSzhIno+fR8ARMcM5RTlny9Bl7l4AhIbPdEnIkQYGuDZEEA4CDB1JRntY46EhMpu7qYLcZliEaKHjXZmf7kpQh7sL/J221W4htdaFH8WLHFVMqpgqWG0D2Wp14FB7MCvzqaclNdc2gOsLg5Mcv4qe7DzEZdZkDP8oao0w4CcW3XTLzKMULLJOBodlgbVqFNp6QtKU7cmvG3VQeuvhLLSH1SDskHTLG92ScaXr4I0r+2HFOvM0BpoxegPJIo9NyMk0RwFJUFGfUDPMoRpijCgpRD9E5Qmps8Uj14XhKM5RAcZv2n21ME6qT9clk2AoTEu9QpCNQCDJpE1SN5eDWfGLYHdagNzxbDW9PUhu3IfuTxijBTeg1eBXuuZvfjftqpom5YefJ1qOyrL74PXzwCEsRrBhZ4yMpK0YcvAOa2a11oyb0YgJ+j7jq3z6MRw8h6G+mK7qCH9mPPHI4Pe30QdViQKPNZGbaCvwXr1pTaXpI7g8r7EDNhpYujfNJ4oT91Q4Tft72aS75oyfPsWjbCy9QDbqeLxaghML+2WiMXabPh3W4bTiYdXaQ6kii8Z6M0xAqfjA2iwtMWGg== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?y0v4tRaH6yubFvLsHJQshFwG4eNe?= =?utf-8?q?Vbzia7PxTk+tZ7x4iHzr271N1MFdqsnOjVC2rT2S6H2cBCpMeMNW+z126AATrZUkj?= =?utf-8?q?DwDvreCRVs86wiGbjNIThzz5wCFeJ2mPie5Iw277f4YSk7IOGd7P16blPIHh810rd?= =?utf-8?q?pQwVsOrottvTPpYu1BnG6/YcHcqspQmiYgVtOr1YPePoAtlE7FnU8NnWJdsw6Eng4?= =?utf-8?q?IarLgAXMrCG1o+/XMHCVhRz1iQwbF52S5YejrFxwBomqC724+LnUdyslGMUBdY/vg?= =?utf-8?q?n+dxa1hB1Yk91kh+b4L5aPGUCEErw5rD3rDnbjOQVZ1JWSalVT9u+nZagc780kQO+?= =?utf-8?q?B/KcsJGOXECLWIl+GZw6cljLPhLbtfSXC9TWNA1weWnXDH3KrarAnjTCgtcazCcvN?= =?utf-8?q?9J1C+5MfSMWqegLTEnCNuXFK+sh+Fefn2i2Mx3jNmFvVkWTV5P9mLQTCWJ6xJNgnF?= =?utf-8?q?pYzVg81kZ0XrouNbUHY/cTfjKCnBPbkVtxIo9qziUQ5zjQ6ABeDKDTDnqUJekn0WA?= =?utf-8?q?JJlRm+RfYOqziNRBZ/pt12fRYpmsmHQCM+M7BAaYKANCYLq7VHXX3tz7pFUzENoc5?= =?utf-8?q?M1+D0L1JVqn6Eg419lySOnwVYETGPce5i/hAXUzOFwpqkptuBU8lp6E4vBD+UmmYS?= =?utf-8?q?irQ0Cu34yAaDHsNSWyC2kCuVUODIamS35ubPJ55Frqbg+cUDxZ6B9u2q0bICFALew?= =?utf-8?q?Hh5CaMobCfVPVRnUD6rn4S6aeCpYfAZBC13d288VCnkiAUyqsuDdJd42Quyth5Fgf?= =?utf-8?q?ih3d7YJj8qNoBJmsk1P6+uQccr8Uw585ZYXJVRx4487jKm478qWYua0LxukwEDMy9?= =?utf-8?q?toibSM+0w4SgjvaBqcPDMiS2q8qlk1LclmZ797yeuJN4YQ0UuwZ46dwH5BlF2oTOE?= =?utf-8?q?q73MhKV5tExx7nsEV45fDdb9Jg7GpkLrBD0P+WrnmWWg=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 26af0159-676d-45da-9942-08d9bc25468e X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:33.2142 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0397 Subject: [FFmpeg-devel] [PATCH v23 10/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: xE10Tjgu+7Ro Signed-off-by: softworkz --- libavfilter/avfilter.c | 8 +++++--- libavfilter/avfilter.h | 11 +++++++++++ libavfilter/avfiltergraph.c | 5 +++++ libavfilter/formats.c | 22 ++++++++++++++++++++++ libavfilter/formats.h | 3 +++ libavfilter/internal.h | 18 +++++++++++++++--- 6 files changed, 61 insertions(+), 6 deletions(-) diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index df5b8f483c..75d5e86539 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -56,7 +56,8 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end) ref->linesize[0], ref->linesize[1], ref->linesize[2], ref->linesize[3], ref->pts, ref->pkt_pos); - if (ref->width) { + switch(ref->type) { + case AVMEDIA_TYPE_VIDEO: ff_tlog(ctx, " a:%d/%d s:%dx%d i:%c iskey:%d type:%c", ref->sample_aspect_ratio.num, ref->sample_aspect_ratio.den, ref->width, ref->height, @@ -64,12 +65,13 @@ static void tlog_ref(void *ctx, AVFrame *ref, int end) ref->top_field_first ? 'T' : 'B', /* Top / Bottom */ ref->key_frame, av_get_picture_type_char(ref->pict_type)); - } - if (ref->nb_samples) { + break; + case AVMEDIA_TYPE_AUDIO: ff_tlog(ctx, " cl:%"PRId64"d n:%d r:%d", ref->channel_layout, ref->nb_samples, ref->sample_rate); + break; } ff_tlog(ctx, "]%s", end ? "\n" : ""); diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index b105dc3159..9f917deb41 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -45,6 +45,7 @@ #include "libavutil/log.h" #include "libavutil/samplefmt.h" #include "libavutil/pixfmt.h" +#include "libavutil/subfmt.h" #include "libavutil/rational.h" #include "libavfilter/version.h" @@ -343,6 +344,12 @@ typedef struct AVFilter { * and outputs use the same sample rate and channel count/layout. */ const enum AVSampleFormat *samples_list; + /** + * Analogous to pixels, but delimited by AV_SUBTITLE_FMT_NONE + * and restricted to filters that only have AVMEDIA_TYPE_SUBTITLE + * inputs and outputs. + */ + const enum AVSubtitleType *subs_list; /** * Equivalent to { pix_fmt, AV_PIX_FMT_NONE } as pixels_list. */ @@ -351,6 +358,10 @@ typedef struct AVFilter { * Equivalent to { sample_fmt, AV_SAMPLE_FMT_NONE } as samples_list. */ enum AVSampleFormat sample_fmt; + /** + * Equivalent to { sub_fmt, AV_SUBTITLE_FMT_NONE } as subs_list. + */ + enum AVSubtitleType sub_fmt; } formats; int priv_size; ///< size of private data to allocate for the filter diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index b8b432e98b..f4987654af 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -311,6 +311,8 @@ static int filter_link_check_formats(void *log, AVFilterLink *link, AVFilterForm return ret; break; + case AVMEDIA_TYPE_SUBTITLE: + return 0; default: av_assert0(!"reached"); } @@ -441,6 +443,9 @@ static int query_formats(AVFilterGraph *graph, void *log_ctx) if (!link) continue; + if (link->type == AVMEDIA_TYPE_SUBTITLE) + continue; + neg = ff_filter_get_negotiation(link); av_assert0(neg); for (neg_step = 1; neg_step < neg->nb_mergers; neg_step++) { diff --git a/libavfilter/formats.c b/libavfilter/formats.c index ba62f73248..5c972bb183 100644 --- a/libavfilter/formats.c +++ b/libavfilter/formats.c @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/subfmt.h" #include "libavutil/avassert.h" #include "libavutil/channel_layout.h" #include "libavutil/common.h" @@ -431,6 +432,12 @@ int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout) return 0; } +int ff_add_subtitle_type(AVFilterFormats **avff, int64_t fmt) +{ + ADD_FORMAT(avff, fmt, ff_formats_unref, int, formats, nb_formats); + return 0; +} + AVFilterFormats *ff_make_formats_list_singleton(int fmt) { int fmts[2] = { fmt, -1 }; @@ -450,6 +457,13 @@ AVFilterFormats *ff_all_formats(enum AVMediaType type) return NULL; fmt++; } + } else if (type == AVMEDIA_TYPE_SUBTITLE) { + if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_BITMAP) < 0) + return NULL; + if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_ASS) < 0) + return NULL; + if (ff_add_subtitle_type(&ret, AV_SUBTITLE_FMT_TEXT) < 0) + return NULL; } return ret; @@ -724,6 +738,10 @@ int ff_default_query_formats(AVFilterContext *ctx) type = AVMEDIA_TYPE_AUDIO; formats = ff_make_format_list(f->formats.samples_list); break; + case FF_FILTER_FORMATS_SUBFMTS_LIST: + type = AVMEDIA_TYPE_SUBTITLE; + formats = ff_make_format_list(f->formats.subs_list); + break; case FF_FILTER_FORMATS_SINGLE_PIXFMT: type = AVMEDIA_TYPE_VIDEO; formats = ff_make_formats_list_singleton(f->formats.pix_fmt); @@ -732,6 +750,10 @@ int ff_default_query_formats(AVFilterContext *ctx) type = AVMEDIA_TYPE_AUDIO; formats = ff_make_formats_list_singleton(f->formats.sample_fmt); break; + case FF_FILTER_FORMATS_SINGLE_SUBFMT: + type = AVMEDIA_TYPE_SUBTITLE; + formats = ff_make_formats_list_singleton(f->formats.sub_fmt); + break; default: av_assert2(!"Unreachable"); /* Intended fallthrough */ diff --git a/libavfilter/formats.h b/libavfilter/formats.h index a884d15213..94754ebd88 100644 --- a/libavfilter/formats.h +++ b/libavfilter/formats.h @@ -180,6 +180,9 @@ int ff_set_common_formats_from_list(AVFilterContext *ctx, const int *fmts); av_warn_unused_result int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout); +av_warn_unused_result +int ff_add_subtitle_type(AVFilterFormats **avff, int64_t fmt); + /** * Add *ref as a new reference to f. */ diff --git a/libavfilter/internal.h b/libavfilter/internal.h index fc09ef574c..192b0ae196 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -149,9 +149,11 @@ static av_always_inline int ff_filter_execute(AVFilterContext *ctx, avfilter_act enum FilterFormatsState { /** - * The default value meaning that this filter supports all formats - * and (for audio) sample rates and channel layouts/counts as long - * as these properties agree for all inputs and outputs. + * The default value meaning that this filter supports + * - For video: all formats + * - For audio: all sample rates and channel layouts/counts + * - For subtitles: all subtitle formats + * as long as these properties agree for all inputs and outputs. * This state is only allowed in case all inputs and outputs actually * have the same type. * The union is unused in this state. @@ -162,8 +164,10 @@ enum FilterFormatsState { FF_FILTER_FORMATS_QUERY_FUNC, ///< formats.query active. FF_FILTER_FORMATS_PIXFMT_LIST, ///< formats.pixels_list active. FF_FILTER_FORMATS_SAMPLEFMTS_LIST, ///< formats.samples_list active. + FF_FILTER_FORMATS_SUBFMTS_LIST, ///< formats.subs_list active. FF_FILTER_FORMATS_SINGLE_PIXFMT, ///< formats.pix_fmt active FF_FILTER_FORMATS_SINGLE_SAMPLEFMT, ///< formats.sample_fmt active. + FF_FILTER_FORMATS_SINGLE_SUBFMT, ///< formats.sub_fmt active. }; #define FILTER_QUERY_FUNC(func) \ @@ -175,16 +179,24 @@ enum FilterFormatsState { #define FILTER_SAMPLEFMTS_ARRAY(array) \ .formats.samples_list = array, \ .formats_state = FF_FILTER_FORMATS_SAMPLEFMTS_LIST +#define FILTER_SUBFMTS_ARRAY(array) \ + .formats.subs_list = array, \ + .formats_state = FF_FILTER_FORMATS_SUBFMTS_LIST #define FILTER_PIXFMTS(...) \ FILTER_PIXFMTS_ARRAY(((const enum AVPixelFormat []) { __VA_ARGS__, AV_PIX_FMT_NONE })) #define FILTER_SAMPLEFMTS(...) \ FILTER_SAMPLEFMTS_ARRAY(((const enum AVSampleFormat[]) { __VA_ARGS__, AV_SAMPLE_FMT_NONE })) +#define FILTER_SUBFMTS(...) \ + FILTER_SUBFMTS_ARRAY(((const enum AVSubtitleType[]) { __VA_ARGS__, AV_SUBTITLE_FMT_NONE })) #define FILTER_SINGLE_PIXFMT(pix_fmt_) \ .formats.pix_fmt = pix_fmt_, \ .formats_state = FF_FILTER_FORMATS_SINGLE_PIXFMT #define FILTER_SINGLE_SAMPLEFMT(sample_fmt_) \ .formats.sample_fmt = sample_fmt_, \ .formats_state = FF_FILTER_FORMATS_SINGLE_SAMPLEFMT +#define FILTER_SINGLE_SUBFMT(sub_fmt_) \ + .formats.sub_fmt = sub_fmt_, \ + .formats_state = FF_FILTER_FORMATS_SINGLE_SUBFMT #define FILTER_INOUTPADS(inout, array) \ .inout = array, \ From patchwork Fri Dec 10 21:37:35 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32276 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2373840iog; Fri, 10 Dec 2021 13:39:17 -0800 (PST) X-Google-Smtp-Source: ABdhPJy56WZdlu0r8sQwbcAcYreaBLV+BoOtf1Z698gG1Bopz6xLCBJMwU2BozSfn3AIbj6xFS5C X-Received: by 2002:a17:906:35d7:: with SMTP id p23mr26193892ejb.32.1639172357131; Fri, 10 Dec 2021 13:39:17 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id dn23si5343304edb.556.2021.12.10.13.39.16; Fri, 10 Dec 2021 13:39:17 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=jgrMK9rN; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 1BDC368AF8A; Fri, 10 Dec 2021 23:37:39 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM12-BN8-obe.outbound.protection.outlook.com (mail-bn8nam12olkn2080.outbound.protection.outlook.com [40.92.21.80]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 2AB9668AF82 for ; Fri, 10 Dec 2021 23:37:36 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=bgUgyJuWm3HzETd79+z3EmsXsm0U2JalUVc9WJEIouQ8UEHmuZmRh3A/N5zKVDp1HSC+1371NHPQo0IIJqQfXnUNr4FOakNZ6WNI2/wyQEItNjLkDClVBeRRNrmP2g07ddHkIZDzBW11o644Qw1H3eOxBGR1oE3aniV+AYx7b3TVu3iyuo80xu4Dz+Q7k2JVqIH574+/ktc6xPMUIQCpX6eZ2aiYP2jGO2DpaQ5fCE/NxpOkNQOAIxUgNSRoEMXTzvG6ovcmFtrnNdtNN9o81m445RdJft8lYlePqjdJ3MBw96KuVO+ZYm7jb5O2myQCk9QNmRYUENpSuAMr2PozJg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=8FrYfeO855uTD0NwkfrTZcJlp/ew+nvLVgGbsWPpMh4=; b=M/d/uO+T5t927zD0RpKoC/LmHKJBRfWXi93ZGvn9KBgKjT+N+uw7Gixx1qLx5INJ0VNCGDn/p74H47cENihW749RBooOanifsAy8hbTWxEzQv96bY+JATOrsdiNNlzh2jxmsBK8sRTwiNPO8OJuIcz3u0bNxESj+/qesmEQVvcxHhJFYOyV+0UZqVe//XtlQ/CCSSxDWsHTcvjFz5F9ZC81VoxjUHs+KexI/yuu12Dk1WeF3jSM//cW5bZrooIi3C4Gur+LQYnCvYwvoJPQ2TZ33GTwJ4kwZRQwrSQ9ONQakM1nnbkqg9/BGYgSuen0K8kYMWeJvylMYmVB3z3ffsg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=8FrYfeO855uTD0NwkfrTZcJlp/ew+nvLVgGbsWPpMh4=; b=jgrMK9rNMeOXwJyIdYhqXIgGZdxS2vqrrc6lw6XVmUA9IdhhTuOqYzwdT6cwApLxbXNV6WDJBf3fz0WInKWne1ZscYTWk61UoHotUjfJLSVpt9KGNhJ5vpl9lqitexBPzWR0HlrlI2rzjIMt1YPZosdRVfcWMfx6ivXfCB4N230Yddx3TnL7BjB/1+WxAaqgy7U1VmSfz0T4L48mBu44RHmIseJzSspaqJxp77MCEuGCz8U7f16dlNRM4HYiuiT0m+GIAECjaZVgFD2yMPVfEsnDb3cXHSLxtemnedql1mM2kWZdvNLSeAqqP80JwuE0euOf5aDJFv7HdUG0QAKamA== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0397.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:35 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:35 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 11/21] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters Thread-Index: AQHX7g4l1lazAZfQ20GquGHvndvkXQ== Date: Fri, 10 Dec 2021 21:37:35 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> <75bb9ceffef9e8e2d05ffce8a51c1991d5d3a973.1639172095.git.softworkz@hotmail.com> <554aab35996d92c90ab3b0ecb2c0c6c1278479d6.1639172095.git.softworkz@hotmail.com> In-Reply-To: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [IqLaWpFBdcljDjCAFKoMJ17SjGBnzBDj] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: ff85966a-8c04-46a7-3b1a-08d9bc25479e x-ms-traffictypediagnostic: DM8P223MB0397:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: pkPZNzwYV6dPKdz8POHbZALa9kzeRAwhiXcTu/nEaDHNcoJrFBAk3r+fKNsttjLUl2jcU9NWxu0U0oufRWs9tOuNyCfcoV1MU3Y5jlUjapQ8dboA6JneGntZDAcXnekUEUwV0zbYr4iP4y+0Qc5TwNog6zUV2SyLZ1QHlVQDZeMEm8C1xRTTru0UoF3po2g8uaThVk6vUb2PBWwzd2oHgvNv8t6uYDAPwPkUVG46JOrstmlcnODxM/5uka7otyyCqsFOWtxKBDXmPLvP8ve5aH+/R6dHAEzg1Ecml5AHfUf2POJyeM2JfOMQ5/kEwJ6pUtcWU1esesFOqYfkgl9teI3c0GNEAmGNqIU9gIjYWBUTDoI2fAF6KicqdtWkSFW8CSf773HoZzWNr83Gjt03R/0LED53Nfhtu8GtKUpycSzwbTh0Uv0XvkOU6lYi5E/hqG1ofYPS8iW2KRnTaVj4A94+GUT65qsVMfC4CInFUElERhpqXxDnvii6Pw1BZ/V07cw7oJ/UNHtP6r2xzpTbLc5PCDXhAVsUTh9eco9/Ustmm7zTFu4Z2TgOkrDdSxUdUe0UKXDTk9NlH1yRVOTgsg== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?M8JjTO5qWe/J5klIYBfxQfu3HJvi?= =?utf-8?q?IsRYz98rNfWCs7FB+p98aE7S64Ln3VQg65WMJ956RLzCxFVf7mLKajfoZf6keaetv?= =?utf-8?q?zVL9XdwGK7I7ZyKme8FzQbn6jLGteu+EgHeytHgVRKKA8LpHIyBGr4ZgjyZ+HZGEJ?= =?utf-8?q?mOowNr2Q9W8zX+hQbKt3pnEvKAgL2gk0n3SXWj9klNURwUTB7v8Hjp3keZ2IxcHzs?= =?utf-8?q?G0BiXQHRvHQxinctUGv3XmH4Nx2mSGARGqlx/INFFifacfJfqlzEwoNC1PTsHGn4y?= =?utf-8?q?1r7oupTOnn1XGtIVnmY5AhSBtLoyoCzSP1Y21ZVMR8SvqbhDCmXIwZDMEsLa9cgH0?= =?utf-8?q?L49V5Iemz+CXMaTgjs6mWa6x7jfsnhGm1SjrdY8cpQIPv9lM8mhuxWOWhQgTMQ9s8?= =?utf-8?q?hH6zSxPa1HNw7caDSEzlsCkIrcoE0cB/C9LeY41X3H7XTDG22hyTrbZVEHISG99sc?= =?utf-8?q?BrkTUhcEgHqoE/V09hLS1+zlDIDOol/ORIEAa8ORYntuhaVJ2cnE234LA5LFjvQTx?= =?utf-8?q?VfY/iFAr7dnsrgY4rD1pZXSLP41DXnqnknN3OLFt94CSfAuLcCRFWgH7khPRK6VFP?= =?utf-8?q?MVEJ2XznulWhLRey4CIb7atXXGfQ5BjdrGbJud7BR2DLq4X6o/W9nl9Pws8kD4pSm?= =?utf-8?q?gxIByxO6jxn3URQNqWKAUqnOB+wWzTKK74m4Hssp66oIQ/brRHf/IBAWuBN6Fd6jF?= =?utf-8?q?R/+QiH5HbHdX6ez7B8k6T2wEIu+wb3LuUVnU1dLHTueeo3JuxL6Xj5VONA9Qaoig7?= =?utf-8?q?TW+F8R5glQDO/sEnINSlNOjOkLgiuzM+Syccp9v7rDngXbpxQw007DAxvs4Wc45u3?= =?utf-8?q?zdZGrtCEDWzo+rlxZJ5G2tOaCL8QQfbJru8n0nRhrMJzzIFLX/+vy3YGKhV8vEhUS?= =?utf-8?q?vUOQ3J2Bku6Aa6H+quPt/o6ab6hdtTHCv6Heg8MFP2Gw=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: ff85966a-8c04-46a7-3b1a-08d9bc25479e X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:35.0711 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0397 Subject: [FFmpeg-devel] [PATCH v23 11/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: zy8Lzv6l7mh7 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 04f319b197..7b203d2bf2 100755 --- a/configure +++ b/configure @@ -7808,7 +7808,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 b1af2cbcc8..e134ac8059 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -553,8 +553,10 @@ extern const AVFilter ff_avsrc_movie; * being the same while having different 'types'). */ extern const AVFilter ff_asrc_abuffer; extern const AVFilter ff_vsrc_buffer; +extern const AVFilter ff_ssrc_sbuffer; extern const AVFilter ff_asink_abuffer; extern const AVFilter ff_vsink_buffer; +extern const AVFilter ff_ssink_sbuffer; extern const AVFilter ff_af_afifo; extern const AVFilter ff_vf_fifo; diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c index c0215669e7..0b268c2fa4 100644 --- a/libavfilter/buffersink.c +++ b/libavfilter/buffersink.c @@ -29,6 +29,8 @@ #include "libavutil/internal.h" #include "libavutil/opt.h" +#include "libavcodec/avcodec.h" + #define FF_INTERNAL_FIELDS 1 #include "framequeue.h" @@ -57,6 +59,10 @@ typedef struct BufferSinkContext { int *sample_rates; ///< list of accepted sample rates int sample_rates_size; + /* only used for subtitles */ + enum AVSubtitleType *subtitle_types; ///< list of accepted subtitle types, must be terminated with -1 + int subtitle_types_size; + AVFrame *peeked_frame; } BufferSinkContext; @@ -305,6 +311,28 @@ static int asink_query_formats(AVFilterContext *ctx) return 0; } +static int ssink_query_formats(AVFilterContext *ctx) +{ + BufferSinkContext *buf = ctx->priv; + AVFilterFormats *formats = NULL; + unsigned i; + int ret; + + CHECK_LIST_SIZE(subtitle_types) + if (buf->subtitle_types_size) { + for (i = 0; i < NB_ITEMS(buf->subtitle_types); i++) + if ((ret = ff_add_subtitle_type(&formats, buf->subtitle_types[i])) < 0) + return ret; + if ((ret = ff_set_common_formats(ctx, formats)) < 0) + return ret; + } else { + if ((ret = ff_default_query_formats(ctx)) < 0) + return ret; + } + + return 0; +} + #define OFFSET(x) offsetof(BufferSinkContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption buffersink_options[] = { @@ -322,9 +350,16 @@ static const AVOption abuffersink_options[] = { { NULL }, }; #undef FLAGS +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM +static const AVOption sbuffersink_options[] = { + { "subtitle_types", "set the supported subtitle formats", OFFSET(subtitle_types), AV_OPT_TYPE_BINARY, .flags = FLAGS }, + { NULL }, +}; +#undef FLAGS AVFILTER_DEFINE_CLASS(buffersink); AVFILTER_DEFINE_CLASS(abuffersink); +AVFILTER_DEFINE_CLASS(sbuffersink); static const AVFilterPad avfilter_vsink_buffer_inputs[] = { { @@ -363,3 +398,22 @@ const AVFilter ff_asink_abuffer = { .outputs = NULL, FILTER_QUERY_FUNC(asink_query_formats), }; + +static const AVFilterPad avfilter_ssink_sbuffer_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + }, +}; + +const AVFilter ff_ssink_sbuffer = { + .name = "sbuffersink", + .description = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them available to the end of the filter graph."), + .priv_class = &sbuffersink_class, + .priv_size = sizeof(BufferSinkContext), + .init = common_init, + .activate = activate, + FILTER_INPUTS(avfilter_ssink_sbuffer_inputs), + .outputs = NULL, + FILTER_QUERY_FUNC(ssink_query_formats), +}; diff --git a/libavfilter/buffersink.h b/libavfilter/buffersink.h index 69ed0f29a8..11905abdc5 100644 --- a/libavfilter/buffersink.h +++ b/libavfilter/buffersink.h @@ -129,6 +129,13 @@ typedef struct AVABufferSinkParams { */ attribute_deprecated AVABufferSinkParams *av_abuffersink_params_alloc(void); + +/** + * Deprecated and unused struct to use for initializing an sbuffersink context. + */ +typedef struct AVSBufferSinkParams { + const int *subtitle_type; +} AVSBufferSinkParams; #endif /** diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c index b0611872f1..d2362999a2 100644 --- a/libavfilter/buffersrc.c +++ b/libavfilter/buffersrc.c @@ -39,6 +39,7 @@ #include "formats.h" #include "internal.h" #include "video.h" +#include "libavcodec/avcodec.h" typedef struct BufferSourceContext { const AVClass *class; @@ -63,6 +64,9 @@ typedef struct BufferSourceContext { uint64_t channel_layout; char *channel_layout_str; + /* subtitle only */ + enum AVSubtitleType subtitle_type; + int eof; } BufferSourceContext; @@ -130,6 +134,13 @@ int av_buffersrc_parameters_set(AVFilterContext *ctx, AVBufferSrcParameters *par if (param->channel_layout) s->channel_layout = param->channel_layout; break; + case AVMEDIA_TYPE_SUBTITLE: + s->subtitle_type = param->format; + if (param->width > 0) + s->w = param->width; + if (param->height > 0) + s->h = param->height; + break; default: return AVERROR_BUG; } @@ -197,6 +208,8 @@ int attribute_align_arg av_buffersrc_add_frame_flags(AVFilterContext *ctx, AVFra CHECK_AUDIO_PARAM_CHANGE(ctx, s, frame->sample_rate, frame->channel_layout, frame->channels, frame->format, frame->pts); break; + case AVMEDIA_TYPE_SUBTITLE: + break; default: return AVERROR(EINVAL); } @@ -269,6 +282,7 @@ unsigned av_buffersrc_get_nb_failed_requests(AVFilterContext *buffer_src) #define OFFSET(x) offsetof(BufferSourceContext, x) #define A AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM #define V AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM +#define S AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM static const AVOption buffer_options[] = { { "width", NULL, OFFSET(w), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, V }, @@ -298,6 +312,16 @@ static const AVOption abuffer_options[] = { AVFILTER_DEFINE_CLASS(abuffer); +static const AVOption sbuffer_options[] = { + { "time_base", NULL, OFFSET(time_base), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, INT_MAX, S }, + { "subtitle_type", NULL, OFFSET(subtitle_type), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, S }, + { "width", NULL, OFFSET(w), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, V }, + { "height", NULL, OFFSET(h), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, V }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(sbuffer); + static av_cold int init_audio(AVFilterContext *ctx) { BufferSourceContext *s = ctx->priv; @@ -347,6 +371,21 @@ static av_cold int init_audio(AVFilterContext *ctx) return ret; } +static av_cold int init_subtitle(AVFilterContext *ctx) +{ + BufferSourceContext *c = ctx->priv; + + if (c->subtitle_type == AV_SUBTITLE_FMT_BITMAP) + av_log(ctx, AV_LOG_VERBOSE, "graphical subtitles - w:%d h:%d tb:%d/%d\n", + c->w, c->h, c->time_base.num, c->time_base.den); + else + av_log(ctx, AV_LOG_VERBOSE, "text subtitles - w:%d h:%d tb:%d/%d\n", + c->w, c->h, c->time_base.num, c->time_base.den); + + return 0; +} + + static av_cold void uninit(AVFilterContext *ctx) { BufferSourceContext *s = ctx->priv; @@ -381,6 +420,11 @@ static int query_formats(AVFilterContext *ctx) if ((ret = ff_set_common_channel_layouts(ctx, channel_layouts)) < 0) return ret; break; + case AVMEDIA_TYPE_SUBTITLE: + if ((ret = ff_add_format (&formats, c->subtitle_type)) < 0 || + (ret = ff_set_common_formats (ctx , formats )) < 0) + return ret; + break; default: return AVERROR(EINVAL); } @@ -408,6 +452,11 @@ static int config_props(AVFilterLink *link) if (!c->channel_layout) c->channel_layout = link->channel_layout; break; + case AVMEDIA_TYPE_SUBTITLE: + link->format = c->subtitle_type; + link->w = c->w; + link->h = c->h; + break; default: return AVERROR(EINVAL); } @@ -472,3 +521,26 @@ const AVFilter ff_asrc_abuffer = { FILTER_QUERY_FUNC(query_formats), .priv_class = &abuffer_class, }; + +static const AVFilterPad ssrc_sbuffer_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .request_frame = request_frame, + .config_props = config_props, + }, +}; + +const AVFilter ff_ssrc_sbuffer = { + .name = "sbuffer", + .description = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them accessible to the filterchain."), + .priv_size = sizeof(BufferSourceContext), + + .init = init_subtitle, + .uninit = uninit, + + .inputs = NULL, + FILTER_OUTPUTS(ssrc_sbuffer_outputs), + FILTER_QUERY_FUNC(query_formats), + .priv_class = &sbuffer_class, +}; diff --git a/libavfilter/buffersrc.h b/libavfilter/buffersrc.h index 08fbd18a47..929a2fa249 100644 --- a/libavfilter/buffersrc.h +++ b/libavfilter/buffersrc.h @@ -74,6 +74,7 @@ typedef struct AVBufferSrcParameters { /** * video: the pixel format, value corresponds to enum AVPixelFormat * audio: the sample format, value corresponds to enum AVSampleFormat + * subtitles: the subtitle format, value corresponds to enum AVSubtitleType */ int format; /** From patchwork Fri Dec 10 21:37:36 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32277 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2374053iog; Fri, 10 Dec 2021 13:39:28 -0800 (PST) X-Google-Smtp-Source: ABdhPJwr0fJaoCFFSb3wHEIw/9smQDTgAFARL5XqcgI2sxM8M4l9UPpwHvIaBM4SqCGKBJl4GfMt X-Received: by 2002:a17:906:52cf:: with SMTP id w15mr27219060ejn.122.1639172368714; Fri, 10 Dec 2021 13:39:28 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id mp30si6279056ejc.461.2021.12.10.13.39.28; Fri, 10 Dec 2021 13:39:28 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=o7+lLvKk; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 6FEBC68AF69; Fri, 10 Dec 2021 23:37:41 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM12-BN8-obe.outbound.protection.outlook.com (mail-bn8nam12olkn2108.outbound.protection.outlook.com [40.92.21.108]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 0A71668AF87 for ; Fri, 10 Dec 2021 23:37:38 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=jeeEnXPAsbIudxbGRY0bsTNMlI4NfR09DlxXt34D/imwZ26qrdCG/t1OMe7+hvtOWFLvHprT9uU5gn1QtcD68WusDLT88DfqBuuRyHx/JDrzD8tEF9et6kl83qrQlyFydLZzQki7ZRHCUfJdlPlKesy3ySsxxfsJ6ZHP9wBJtL7XrEcFVYIOzkXl7eRdVYgdNcJHyr4Iq6F9vgifcvV1m1uFO664YgATik8YwBHy9eFsG7i+sOtIfD3aVGVtsOY8QEp8HJFB/cWACSjmBbquYM4sBSw2PRr0hVAvqwbL3Xw7GN3ZZ048Z1gVLhVv1MGIcrqvTLC3Vs/E3dGLXU1qaA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=OWZG2kWSeqf2RCWO4SHSoKhoZQaPSLu5yKa0XvfnZLQ=; b=I4cmZ+9H0jWOxTcDowWRglQTGWtEC+rues21h75qBrT2+gsOVeTgDvQwCSOBEzWfcexL7/9BykQal5cZna47sTvlc+C2nV+oBTCOnFdPdOJlzxdyiKlr7dDMi5EtQQ3kutdI7iU3MjBG9CnMZWy3XEagn+9bf0z+uaXafvkX5/qmmTICtSIPLBqcQnOVpo4xK/ZEy4ZDfOmltM8mJl9ZF3/2nrzxWJQCKiyccuBiUpy2iMfToE5VDQjKLYNhKx2AiZnRf/S/DUgLTnOLl70Q68CvrG9iGdXomEHFswEYGEszYt4cGUtskZ7Hy8kfynBP8CUmKxygYJlOAQcYVUw1aQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=OWZG2kWSeqf2RCWO4SHSoKhoZQaPSLu5yKa0XvfnZLQ=; b=o7+lLvKkFdMlsOICnei3MCkLnE9sGr4Sg+WG9RB33773h4fuu9fql98adziMKiBltzl1Y4URnkPKHhnVq6l7IoRPbFnwTCbRGxmeAfVK6S3XlL6tHBQcUjZjeHRML7zraJA27Q2NCHGJDTpsf1FuBmB62N2flj9v4VFUdAyR9BqfSCT/RNPFTYJuA7CqsMYAb0X4J2LOgHVdJSU2cZjtcHFDNofWFKw1eysattQ43PuLcq9rCY7KuV4H0oTM0QZlAEDAhbXcPEXcAHLMmtdb0mvzS2bIppc4I2VHqoaWCT2FwS8Zko5aiaze/0pMhP7+eUMMlhrmYMpGOmD2Iq3T7w== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0397.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:36 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:36 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 12/21] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters Thread-Index: AQHX7g4mnnQHsNmYtUu1dH69At7Ehg== Date: Fri, 10 Dec 2021 21:37:36 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> <75bb9ceffef9e8e2d05ffce8a51c1991d5d3a973.1639172095.git.softworkz@hotmail.com> <554aab35996d92c90ab3b0ecb2c0c6c1278479d6.1639172095.git.softworkz@hotmail.com> In-Reply-To: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [pm9DNxQYxKYTHcpCspv2jUMqHVSm8/6s] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: b8f4693e-7f94-468f-a932-08d9bc2548b1 x-ms-traffictypediagnostic: DM8P223MB0397:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: m4Y4y9FlXEMiUjcPrb7DrVuknGZi+1NfeMqGFPXvj/96zulcCWu0X345rzsfIUwTotNpVy4He7eHM3Hss+URzU/Kuc5ryOwYFtiG+tfIH/x2iQf+CtCeo00nYM2RpVK45rv4E7cEwKI166fSfFSpxog42f6anXhC9NeW7ROjoGm6/G0HLMn0s7uUlSMV8qM1nywNInVrjqjhuIfgwzYdsEeC6ysLeC47lyyIWXDT8J6b7709MQJ2qemc+XkZfwN0Qzd0HNGIs5KCGCvmJOvl3z24ebpqkWdBelCR6GW2FVvVhM9rkTW6CdU35Sy9OJ1qtJ+fvfp9EOvYblHLVaCM5nKirMMTHtJM3USNnJtO+CjMsTRptIYGUn7jVi20pU8djEH5M3Whge22QQt6o6TDVF0/+LOn9JGH6ozIm5aWHZe8oqUzYjkI5RAfmcGN6jCQmA5omCRZKHiD2LqQ7MlEbrDQmp2aktCTsGRndyJ1k9p8lO/yrWALZHk19seNBXfzyC1vfLD7kXx0SRb2uYpzUy2xj9J1oJxvOLYKkRHjdOgABn0ymww2kAWFxItXFnvECw/Fzo7EWaL8xpxGUg9cupEKdCE+GvOOotP2ReWGhGAg1cAccGRbLBtJSGcVKLWB x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?+EYOF/r2fJlrnDUmmbI+XGN8wQI2?= =?utf-8?q?Xux8L9qPF30aWU9FqIaq/xLUqn73709yIw5kT5JHNIKbr6/UkJeHJwuVD+DJ1ntEh?= =?utf-8?q?HmVBPnyZEowHR67lBjuo2xEu4YQoX34TfeLzzeeWK/tOR2ADN01opqMDRRvAFUcwk?= =?utf-8?q?+IRKHAQymBCu94d5+EKt5z5jYZEA4QELPx9Hgjc3v+3Axv0SFd1b3JmRqsl9mnzj9?= =?utf-8?q?L6+VQ0SOtlxQOXfON2jzp09CG/1dOXaVQ7Fntl0QUGfd7ilpeS9IkaTx2mBJz+ZkZ?= =?utf-8?q?M6RY8e+rc6fgp+Km8bWpnTd+dT8AEXjkjq+2ax0lhBzWoSBoXPZrgufrtqC+S0tQd?= =?utf-8?q?vMDCt9JprFj55Ju1dU+onzKm5fxYK2QNUlo+sDsiwk4hRQlfvdSQaAbE9V4I/cNe4?= =?utf-8?q?gDkm9pzx0JWT4q9MQxTj//HtFJnf78kz9jvZLeerNtJxJgrb4WWcYMgk3NIF/YkDp?= =?utf-8?q?obCZSgkYdaaA/jmz5Ay4n+Tw6BtquJWxqa4fzawPO1cPBXo86+q3ABM/6L1fFOR5F?= =?utf-8?q?TSYa+VLw7F6FkZrD1+ri5nZtR6rXuPgGxWO2NlrD7fnqu+53QLMJoiUD8RI6pbRUm?= =?utf-8?q?FJzAcmdAQhqRhL2kQOqJV2InJAwxP3i11k0wc8dzAwXWaKLEziGM1DokT5nKcY4lE?= =?utf-8?q?XfahtrE9c7KXJeV38oGaeWUgAttS737W9eF3MxRlVZIo8XIRiIll3V6WmO3genW/a?= =?utf-8?q?IgME0660m0e7ZzSPmRLzpSC7Q/u8rohpmhLNZjcBBGDGzOdnlMsU5PeFJAqg5mjoH?= =?utf-8?q?ZjS/GHuBeH3D9DK9AbDUPjKhMwJ2ytk1FynnPjaWRCh2sfbuHG1EtDdMVu0d3TjzH?= =?utf-8?q?wyZgBnHHahIw1qp9gctjjsgW5Om/woPNvpUjLRZCpa2vjFpkGSeyH6rgWp0b7cha0?= =?utf-8?q?B2ME3hfaOtSiZ6vo6iFY+WBmSVTEqZl4MAu7bd5rvILQ=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: b8f4693e-7f94-468f-a932-08d9bc2548b1 X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:36.8393 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0397 Subject: [FFmpeg-devel] [PATCH v23 12/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: RVvr4McfM1/O - 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 | 737 ++++++++++++++++++++++++++++ 4 files changed, 859 insertions(+) create mode 100644 libavfilter/vf_overlaygraphicsubs.c diff --git a/doc/filters.texi b/doc/filters.texi index 8eff460cd9..db4552ba10 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25636,6 +25636,124 @@ tools. @c man end VIDEO SINKS +@chapter Subtitle Filters +@c man begin SUBTITLE FILTERS + +When you configure your FFmpeg build, you can disable any of the +existing filters using @code{--disable-filters}. + +Below is a description of the currently available subtitle filters. + +@section graphicsub2video + +Renders graphic subtitles as video frames. + +This filter replaces the previous "sub2video" hack which did the conversion implicitly and up-front as subtitle filtering wasn't possible at that time. +To retain compatibility with earlier sub2video command lines, this filter is being auto-inserted in those cases. + +For overlaying graphicsal subtitles it is recommended to use the 'overlay_graphicsubs' filter which is more efficient and takes less processing resources. + +This filter is still useful in cases where the overlay is done with hardware acceleration (e.g. overlay_qsv, overlay_vaapi, overlay_cuda) for preparing the overlay frames. + +Inputs: +@itemize +@item 0: Subtitles [BITMAP] +@end itemize + +Outputs: +@itemize +@item 0: Video [RGB32] +@end itemize + + +It accepts the following parameters: + +@table @option +@item size, s +Set the size of the output video frame. + +@end table + +@subsection Examples + +@itemize +@item +Overlay PGS subtitles +(not recommended - better use overlay_graphicsubs) +@example +ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:1]graphicsub2video[subs];[0:0][subs]overlay" output.mp4 +@end example + +@item +Overlay PGS subtitles implicitly +The graphicsub2video is inserted automatically for compatibility with legacy command lines. +@example +ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlay" output.mp4 +@end example +@end itemize + +@section overlaygraphicsubs + +Overlay graphic subtitles onto a video stream. + +This filter can blend graphical subtitles on a video stream directly, i.e. without creating full-size alpha images first. +The blending operation is limited to the area of the subtitle rectangles, which also means that no processing is done at times where no subtitles are to be displayed. + +Inputs: +@itemize +@item 0: Video [YUV420P, YUV422P, YUV444P, ARGB, RGBA, ABGR, BGRA, RGB24, BGR24] +@item 1: Subtitles [BITMAP] +@end itemize + +Outputs: +@itemize +@item 0: Video (same as input) +@end itemize + +It accepts the following parameters: + +@table @option +@item x +@item y +Set the expression for the x and y coordinates of the overlaid video +on the main video. Default value is "0" for both expressions. In case +the expression is invalid, it is set to a huge value (meaning that the +overlay will not be displayed within the output visible area). + +@item eof_action +See @ref{framesync}. + +@item eval +Set when the expressions for @option{x}, and @option{y} are evaluated. + +It accepts the following values: +@table @samp +@item init +only evaluate expressions once during the filter initialization or +when a command is processed + +@item frame +evaluate expressions for each incoming frame +@end table + +Default value is @samp{frame}. + +@item shortest +See @ref{framesync}. + +@end table + +@subsection Examples + +@itemize +@item +Overlay PGS subtitles +@example +ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlaygraphicsubs" output.mp4 +@end example +@end itemize +@c man end SUBTITLE FILTERS + @chapter Multimedia Filters @c man begin MULTIMEDIA FILTERS diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 664566a18a..525b3a6e3c 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -295,6 +295,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 @@ -375,6 +376,7 @@ OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER) += vf_overlay_opencl.o opencl.o \ opencl/overlay.o framesync.o OBJS-$(CONFIG_OVERLAY_QSV_FILTER) += vf_overlay_qsv.o framesync.o OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER) += vf_overlay_vulkan.o vulkan.o vulkan_filter.o +OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER) += vf_overlaygraphicsubs.o framesync.o OBJS-$(CONFIG_OWDENOISE_FILTER) += vf_owdenoise.o OBJS-$(CONFIG_PAD_FILTER) += vf_pad.o OBJS-$(CONFIG_PAD_OPENCL_FILTER) += vf_pad_opencl.o opencl.o opencl/pad.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index e134ac8059..1bd3060ba3 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -357,6 +357,7 @@ extern const AVFilter ff_vf_oscilloscope; extern const AVFilter ff_vf_overlay; extern const AVFilter ff_vf_overlay_opencl; extern const AVFilter ff_vf_overlay_qsv; +extern const AVFilter ff_vf_overlaygraphicsubs; extern const AVFilter ff_vf_overlay_vulkan; extern const AVFilter ff_vf_overlay_cuda; extern const AVFilter ff_vf_owdenoise; @@ -542,6 +543,7 @@ extern const AVFilter ff_avf_showvolume; extern const AVFilter ff_avf_showwaves; extern const AVFilter ff_avf_showwavespic; extern const AVFilter ff_vaf_spectrumsynth; +extern const AVFilter ff_svf_graphicsub2video; /* multimedia sources */ extern const AVFilter ff_avsrc_amovie; diff --git a/libavfilter/vf_overlaygraphicsubs.c b/libavfilter/vf_overlaygraphicsubs.c new file mode 100644 index 0000000000..37153baaf5 --- /dev/null +++ b/libavfilter/vf_overlaygraphicsubs.c @@ -0,0 +1,737 @@ +/* + * 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 + + 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; + + 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; + + 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_graphicsub: 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; + outlink->sample_aspect_ratio = (AVRational){1,1}; + outlink->time_base = ctx->inputs[0]->time_base; + + 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 *src_frame) +{ + AVFilterLink *outlink = inlink->dst->outputs[0]; + const AVFilterContext *ctx = outlink->src; + OverlaySubsContext *s = ctx->priv; + AVFrame *out; + const unsigned num_rects = src_frame->num_subtitle_areas; + unsigned int i; + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&src_frame); + return AVERROR(ENOMEM); + } + + // Maybe we should set these.. + ////out->color_primaries = AVCOL_PRI_BT709; + ////out->color_range = AVCOL_RANGE_MPEG; + ////out->color_trc = AVCOL_TRC_BT709; + + memset(out->data[0], 0, (size_t)out->linesize[0] * out->height); + + out->pts = out->pkt_dts = out->best_effort_timestamp = src_frame->pts; + out->coded_picture_number = out->display_picture_number = s->pic_counter++; + + for (i = 0; i < num_rects; i++) { + const AVSubtitleArea *sub_rect = src_frame->subtitle_areas[i]; + + if (sub_rect->type != AV_SUBTITLE_FMT_BITMAP) { + av_log(NULL, AV_LOG_WARNING, "sub2video: non-bitmap subtitle\n"); + av_frame_free(&src_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, src_frame->num_subtitle_areas); + + av_frame_free(&src_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 video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS }, + { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS }, + { NULL } +}; + +FRAMESYNC_DEFINE_CLASS(overlaygraphicsubs, OverlaySubsContext, fs); + +static const AVFilterPad overlaygraphicsubs_inputs[] = { + { + .name = "main", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input_main, + .flags = AVFILTERPAD_FLAG_NEEDS_WRITABLE, + }, + { + .name = "overlay", + .type = AVMEDIA_TYPE_SUBTITLE, + }, +}; + +static const AVFilterPad overlaygraphicsubs_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, +}; + +const AVFilter ff_vf_overlaygraphicsubs = { + .name = "overlaygraphicsubs", + .description = NULL_IF_CONFIG_SMALL("Overlay graphical subtitles on top of the input."), + .preinit = overlaygraphicsubs_framesync_preinit, + .init = overlay_graphicsubs_init, + .uninit = overlay_graphicsubs_uninit, + .priv_size = sizeof(OverlaySubsContext), + .priv_class = &overlaygraphicsubs_class, + .activate = overlay_graphicsubs_activate, + FILTER_INPUTS(overlaygraphicsubs_inputs), + FILTER_OUTPUTS(overlaygraphicsubs_outputs), + FILTER_QUERY_FUNC(overlay_graphicsubs_query_formats), +}; + +AVFILTER_DEFINE_CLASS(graphicsub2video); + +static const AVFilterPad graphicsub2video_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .filter_frame = graphicsub2video_filter_frame, + .config_props = graphicsub2video_config_input, + }, +}; + +static const AVFilterPad graphicsub2video_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = graphicsub2video_config_output, + }, +}; + +const AVFilter ff_svf_graphicsub2video = { + .name = "graphicsub2video", + .description = NULL_IF_CONFIG_SMALL("Convert graphical subtitles to video"), + .priv_size = sizeof(OverlaySubsContext), + .priv_class = &graphicsub2video_class, + FILTER_INPUTS(graphicsub2video_inputs), + FILTER_OUTPUTS(graphicsub2video_outputs), + FILTER_QUERY_FUNC(graphicsub2video_query_formats), +}; From patchwork Fri Dec 10 21:37:39 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32278 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2374251iog; Fri, 10 Dec 2021 13:39:42 -0800 (PST) X-Google-Smtp-Source: ABdhPJzJHjSte8U8Ab1Zl3uudQAZ3bquszZCYflfL1ihgxfa8PTSUFrXr+0xKgMNbQLsaOPsg0oh X-Received: by 2002:a17:906:9753:: with SMTP id o19mr27561437ejy.243.1639172382060; Fri, 10 Dec 2021 13:39:42 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id y21si8084265eda.80.2021.12.10.13.39.41; Fri, 10 Dec 2021 13:39:42 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=nw8IGZ35; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 7224368AFA1; Fri, 10 Dec 2021 23:37:45 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM12-BN8-obe.outbound.protection.outlook.com (mail-bn8nam12olkn2062.outbound.protection.outlook.com [40.92.21.62]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 9595968AF97 for ; Fri, 10 Dec 2021 23:37:42 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=alHcl4+nGlyltT9TAjQuashNBrqRCg7RpRJ5vxoFv4lDgOH8gtoQysOyO4q2iSTcrPBaviSqY2QMR9Ds3BUumcS2hfSevWVB473BajM8iEnk+uyWOWjglbWaR3U6qLLHd0QV41gjDQ8JOhHtvYrxzmUAQlgcImLYjzJkBR2xK5zhwoL14LVTJK0nWer3Z6CkU+Yz0auNxHHWgsvJDwzijdM73hDOjyAtCXC4t2UKbYGKvREAFe/yQ7c1e1QukL/SO00Z/Z4H8LUtEOLMkLPwTULoqcu61e47CxrxJhIuvuZfR2Hh495hP1ejX5D8AKMy66D8x5tNuykp89zxSZsNHw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=oNttsWQ6mVryUc7Z6hWCQdPS05n7s8WhTxwYsLPitas=; b=LQadM1HhIRkMRnY43g2I7MSYecRUxEJRiYwj1ozFxRsh91Ij8iL6QNhyqGeT2wlMeF19SjRqVOY0cJPmXZgOWhy0dBehVg1+0PprJjABNH3hLGy3rg4m86gv6hBo1gIXzOu83gK0RrwXRO5szF8cK1Y7PatTbW9mlqT3x6qRNQklTnA/6qdWaFRebCoPJm/AoeBhYzv3JZ+/3HRyoQAyGJUP+1ojQDlTBnRKsKvyazbWTb5TeDVAMXNs8XXz6jnutW/lKCFSZA8QWHvvd+aPo/dKtL15YpyOgexNYPXpHiR7WlMisqNsYKVkuXBn5vLw38tQd/supjvFD5BUsZV3jw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=oNttsWQ6mVryUc7Z6hWCQdPS05n7s8WhTxwYsLPitas=; b=nw8IGZ35RSShzQWbG68QfioR1cMI2S4IzUerNbbjxeid67cllRB8zXS46XffC5aZ1RUfM9FxgeHirryt/b1v+WDAOhE9Kdv+Ab2IS0i8NisPwGP/ofEGAJ1G0oLC40A7CVUokr+XbQxV8p/ibnBhOgpKwTQN5MSYPto3YMVSnQTc3JlNm1Q6X2JcNuuKvxN1IjFIZvPrlX94mjnIGi/aFNY8Xv46ygXO83VdGy0Q+YuvfZ2IWSj12Rk93u/6kFgWw5rz79mMPbZ8w4f3VHqP4qZnjL1ea5zhqCNjfjs6wNEbPCXrZ5pKBZwD0EM3fjldjnCWAQLcNw5lljIa937wGA== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0397.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:39 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:39 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 13/21] fftools/ffmpeg: Replace sub2video with subtitle frame filtering and use new frame-based subtitle encoding API Thread-Index: AQHX7g4nX/gxbsWiski1JHdINlSuJg== Date: Fri, 10 Dec 2021 21:37:39 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> <75bb9ceffef9e8e2d05ffce8a51c1991d5d3a973.1639172095.git.softworkz@hotmail.com> <554aab35996d92c90ab3b0ecb2c0c6c1278479d6.1639172095.git.softworkz@hotmail.com> <65992926f9356948d7f937ab852f8320b3da4bd9.1639172095.git.softworkz@hotmail.com> In-Reply-To: <65992926f9356948d7f937ab852f8320b3da4bd9.1639172095.git.softworkz@hotmail.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [HO9uW90lAkutpzKCmS6U3M6ZCkcW4vFG] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 126e3f92-e939-43ec-3178-08d9bc254a24 x-ms-traffictypediagnostic: DM8P223MB0397:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: Ohg8Py93csZgQuBxsSCgyhbyqMt6TO1s2aAwjj2SAHqwKCuw3KlgC/k5CvTdwoB4/pCm0EB9xV8GGThCfcdjZLefn2vVKbkxs0W8OHjOC1GHTFe16q0jJc4ahvBKRxV0TrDbmsMWo9Rh9ay7dwqbeGzVPNf2mUWTMt8LqsPZi39ttL4ZgssTMGEMVhkr59whrYaUhlc9XSsx/tdIGb4wJB+CcvmI6bUOUp+X/GIAQc1PEfRY5VzQhYOXLnlUCTAKb45Vr0USriQPm0xp/qnTCKcRie4LkRDEeQlpFyuOLBFnPzlDBWz3czDKNiruZMpZcHgRIgTUVpE6LSvpy0yH+k4Auv2DW8FIEXAtHA0VhzwUxW9DEgw8AHE9e78CNB5ztuKW/SIL11NsTrqeo91GYZF6IkguRAjxUVnDKCte8vIK7Iwyhxi5yKygMzU11MdDCQqANiGgNkz0nXEz8AtdAc8PWGNPYaQ2SKD/kjUIzTmqoFmNYJ3x1W/AZb3YlARrNjHnt0WcjiSDFXC3k3QucuJLZZKWZMuZzU3jei+zaGA2zlkAXi6V4+IcTKFh0617Yb5PYRgVqZOa5xJFrweUSg== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?YfMhFZVnN1Gwf5oNQ984ZfgJadmb?= =?utf-8?q?TkfpP3/P4A3BtMJRR+WQrNhD8NGBBkUfOPZjUPvK20T0kzVwNU9lyr7mqp+HK+yEf?= =?utf-8?q?7Og0udETdMS08LmPfzmzFo0XWUdda196VZJ1/7851sPG1WmC0HLmHU/z0u5egTFv7?= =?utf-8?q?48MZOO5fr7VBTNKvVwYVvafkW5CskmNPCgZQnwZTcaY0iEco5gaubjDm18z7V1fRr?= =?utf-8?q?iir4gE9gPleLo7e8z2DDk7BetLNBXHSj4vKJSUJZYpFHd1VtkNCkvcvhEYa1NIumc?= =?utf-8?q?ZPEIJjmeJHWsEtDmOW74utwRQfsxfE38Tf5MA7ZUPduD0lf2zvlKxw2l/o6Rs5Xql?= =?utf-8?q?reS3tYahUJeppaaBbhlM9Cp2F33JqlvQrEOPTii3C+8r0p8En1SDbnF8kGIyOjnYS?= =?utf-8?q?GkkqNJGXm9eEpJ/52g4OLVvTjTDkthsyO88yIZB1gLiR2seuF9uN8tyuoy9h/ad2E?= =?utf-8?q?NTrMKcLYmRuDXszYVZBfGHymD7VOn1OrC+xJ19Wi0xf5Q4Cz2j1UwtbZUUg8Izx/F?= =?utf-8?q?RrytKDGXcTc9QPUp6EP8PAcNgtID3mYVH41ydLLYYxsOHM8Kcmz8zZYGuYbHqEXGF?= =?utf-8?q?l3d46I4sSnugE6lZalPO4QRFj3rfKhVtlVQAYG5yeuotFcmGQhy8IJIPygmFzWztl?= =?utf-8?q?sbA/uZfxeStbznJ120Ouh6TuSDw740WfN5ncHAp6lqFlSiW9AC9P3BW9v6uSRKx7c?= =?utf-8?q?GpP6OX7gUQgEfdqcqi5LVnlBNhLfLde/FHvpIYe0abBSdOFd1xUBXq53bZj+N2v/S?= =?utf-8?q?wSavW+JdRertkmXLswfubbo/emTbKUKM5tdSV01Az3HdnLKK56EZ/uCruiU/xtqaP?= =?utf-8?q?aXjZT/LgwQVSTuN2OCXnIrX2m980cvczh2nHgvs+zGLbzBvnT7ajw7fjWwDc8HrhB?= =?utf-8?q?sFIIXm0ZkqtfsMpX+9zez3C1lNy7qia4QCILa0m/x43w=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 126e3f92-e939-43ec-3178-08d9bc254a24 X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:39.1979 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0397 Subject: [FFmpeg-devel] [PATCH v23 13/21] fftools/ffmpeg: Replace sub2video with subtitle frame filtering and use new frame-based subtitle encoding 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: MpfzW8yHOSMj 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 retained and applied to all subtitle frames (bitmap, text, ..). The other part of sub2video functionality is retained by auto-insertion of the new graphicsub2video filter. Justification for changed test refs: - sub2video The new results are identical excepting the last frame which is due to the implementation changes - sub2video_basic The previous results had some incorrect output because multiple frames had the same dts The non-empty content frames are visually identical, the different CRC is due to the different blending algorithm that is being used. - sub2video_time_limited The third frame in the previous ref was a repetition, which doesn't happen anymore with the new subtitle filtering. - sub-dvb Running ffprobe -show_frames on the source file shows that there are 7 subtitle frames with 0 rects in the source at the start and 2 at the end. This translates to the 14 and 4 additional entries in the new test results. - filter-overlay-dvdsub-2397 Overlay results have slightly different CRCs due to different blending implementation Signed-off-by: softworkz --- fftools/ffmpeg.c | 584 +++++++++++----------- fftools/ffmpeg.h | 15 +- fftools/ffmpeg_filter.c | 217 +++++--- fftools/ffmpeg_hw.c | 2 +- fftools/ffmpeg_opt.c | 3 +- tests/ref/fate/filter-overlay-dvdsub-2397 | 181 ++++--- tests/ref/fate/sub-dvb | 162 +++--- tests/ref/fate/sub2video | 116 ++--- tests/ref/fate/sub2video_basic | 135 ++--- tests/ref/fate/sub2video_time_limited | 4 +- 10 files changed, 725 insertions(+), 694 deletions(-) diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index 0f1834cdbe..0eee4163ac 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -143,8 +143,6 @@ static int want_sdp = 1; static BenchmarkTimeStamps current_time; AVIOContext *progress_avio = NULL; -static uint8_t *subtitle_out; - InputStream **input_streams = NULL; int nb_input_streams = 0; InputFile **input_files = NULL; @@ -169,163 +167,6 @@ static int restore_tty; static void free_input_threads(void); #endif -/* sub2video hack: - Convert subtitles to video with alpha to insert them in filter graphs. - This is a temporary solution until libavfilter gets real subtitles support. - */ - -static int sub2video_get_blank_frame(InputStream *ist) -{ - int ret; - AVFrame *frame = ist->sub2video.frame; - - av_frame_unref(frame); - ist->sub2video.frame->width = ist->dec_ctx->width ? ist->dec_ctx->width : ist->sub2video.w; - ist->sub2video.frame->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h; - ist->sub2video.frame->format = AV_PIX_FMT_RGB32; - if ((ret = av_frame_get_buffer(frame, 0)) < 0) - return ret; - memset(frame->data[0], 0, frame->height * frame->linesize[0]); - return 0; -} - -static void sub2video_copy_rect(uint8_t *dst, int dst_linesize, int w, int h, - AVSubtitleRect *r) -{ - uint32_t *pal, *dst2; - uint8_t *src, *src2; - int x, y; - - if (r->type != SUBTITLE_BITMAP) { - av_log(NULL, AV_LOG_WARNING, "sub2video: non-bitmap subtitle\n"); - return; - } - if (r->x < 0 || r->x + r->w > w || r->y < 0 || r->y + r->h > h) { - av_log(NULL, AV_LOG_WARNING, "sub2video: rectangle (%d %d %d %d) overflowing %d %d\n", - r->x, r->y, r->w, r->h, w, h - ); - return; - } - - dst += r->y * dst_linesize + r->x * 4; - src = r->data[0]; - pal = (uint32_t *)r->data[1]; - for (y = 0; y < r->h; y++) { - dst2 = (uint32_t *)dst; - src2 = src; - for (x = 0; x < r->w; x++) - *(dst2++) = pal[*(src2++)]; - dst += dst_linesize; - src += r->linesize[0]; - } -} - -static void sub2video_push_ref(InputStream *ist, int64_t pts) -{ - AVFrame *frame = ist->sub2video.frame; - int i; - int ret; - - av_assert1(frame->data[0]); - ist->sub2video.last_pts = frame->pts = pts; - for (i = 0; i < ist->nb_filters; i++) { - ret = av_buffersrc_add_frame_flags(ist->filters[i]->filter, frame, - AV_BUFFERSRC_FLAG_KEEP_REF | - AV_BUFFERSRC_FLAG_PUSH); - if (ret != AVERROR_EOF && ret < 0) - av_log(NULL, AV_LOG_WARNING, "Error while add the frame to buffer source(%s).\n", - av_err2str(ret)); - } -} - -void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub) -{ - AVFrame *frame = ist->sub2video.frame; - int8_t *dst; - int dst_linesize; - int num_rects, i; - int64_t pts, end_pts; - - if (!frame) - return; - if (sub) { - pts = av_rescale_q(sub->pts + sub->start_display_time * 1000LL, - AV_TIME_BASE_Q, ist->st->time_base); - end_pts = av_rescale_q(sub->pts + sub->end_display_time * 1000LL, - AV_TIME_BASE_Q, ist->st->time_base); - num_rects = sub->num_rects; - } else { - /* If we are initializing the system, utilize current heartbeat - PTS as the start time, and show until the following subpicture - is received. Otherwise, utilize the previous subpicture's end time - as the fall-back value. */ - pts = ist->sub2video.initialize ? - heartbeat_pts : ist->sub2video.end_pts; - end_pts = INT64_MAX; - num_rects = 0; - } - if (sub2video_get_blank_frame(ist) < 0) { - av_log(ist->dec_ctx, AV_LOG_ERROR, - "Impossible to get a blank canvas.\n"); - return; - } - dst = frame->data [0]; - dst_linesize = frame->linesize[0]; - for (i = 0; i < num_rects; i++) - sub2video_copy_rect(dst, dst_linesize, frame->width, frame->height, sub->rects[i]); - sub2video_push_ref(ist, pts); - ist->sub2video.end_pts = end_pts; - ist->sub2video.initialize = 0; -} - -static void sub2video_heartbeat(InputStream *ist, int64_t pts) -{ - InputFile *infile = input_files[ist->file_index]; - int i, j, nb_reqs; - int64_t pts2; - - /* When a frame is read from a file, examine all sub2video streams in - the same file and send the sub2video frame again. Otherwise, decoded - video frames could be accumulating in the filter graph while a filter - (possibly overlay) is desperately waiting for a subtitle frame. */ - for (i = 0; i < infile->nb_streams; i++) { - InputStream *ist2 = input_streams[infile->ist_index + i]; - if (!ist2->sub2video.frame) - continue; - /* subtitles seem to be usually muxed ahead of other streams; - if not, subtracting a larger time here is necessary */ - pts2 = av_rescale_q(pts, ist->st->time_base, ist2->st->time_base) - 1; - /* do not send the heartbeat frame if the subtitle is already ahead */ - if (pts2 <= ist2->sub2video.last_pts) - continue; - if (pts2 >= ist2->sub2video.end_pts || ist2->sub2video.initialize) - /* if we have hit the end of the current displayed subpicture, - or if we need to initialize the system, update the - overlayed subpicture and its start/end times */ - sub2video_update(ist2, pts2 + 1, NULL); - for (j = 0, nb_reqs = 0; j < ist2->nb_filters; j++) - nb_reqs += av_buffersrc_get_nb_failed_requests(ist2->filters[j]->filter); - if (nb_reqs) - sub2video_push_ref(ist2, pts2); - } -} - -static void sub2video_flush(InputStream *ist) -{ - int i; - int ret; - - if (ist->sub2video.end_pts < INT64_MAX) - sub2video_update(ist, INT64_MAX, NULL); - for (i = 0; i < ist->nb_filters; i++) { - ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL); - if (ret != AVERROR_EOF && ret < 0) - av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n"); - } -} - -/* end of sub2video hack */ - static void term_exit_sigsafe(void) { #if HAVE_TERMIOS_H @@ -526,7 +367,6 @@ static void ffmpeg_cleanup(int ret) avfilter_graph_free(&fg->graph); for (j = 0; j < fg->nb_inputs; j++) { InputFilter *ifilter = fg->inputs[j]; - struct InputStream *ist = ifilter->ist; while (av_fifo_size(ifilter->frame_queue)) { AVFrame *frame; @@ -536,15 +376,6 @@ static void ffmpeg_cleanup(int ret) } av_fifo_freep(&ifilter->frame_queue); av_freep(&ifilter->displaymatrix); - if (ist->sub2video.sub_queue) { - while (av_fifo_size(ist->sub2video.sub_queue)) { - AVSubtitle sub; - av_fifo_generic_read(ist->sub2video.sub_queue, - &sub, sizeof(sub), NULL); - avsubtitle_free(&sub); - } - av_fifo_freep(&ist->sub2video.sub_queue); - } av_buffer_unref(&ifilter->hw_frames_ctx); av_freep(&ifilter->name); av_freep(&fg->inputs[j]); @@ -564,7 +395,7 @@ static void ffmpeg_cleanup(int ret) } av_freep(&filtergraphs); - av_freep(&subtitle_out); + ////av_freep(&subtitle_out); /* close files */ for (i = 0; i < nb_output_files; i++) { @@ -632,12 +463,14 @@ static void ffmpeg_cleanup(int ret) av_frame_free(&ist->decoded_frame); av_packet_free(&ist->pkt); av_dict_free(&ist->decoder_opts); - avsubtitle_free(&ist->prev_sub.subtitle); - av_frame_free(&ist->sub2video.frame); + av_frame_free(&ist->prev_sub.subtitle); av_freep(&ist->filters); av_freep(&ist->hwaccel_device); av_freep(&ist->dts_buffer); + av_frame_free(&ist->subtitle_heartbeat.recent_sub); + av_buffer_unref(&ist->subtitle_heartbeat.header); + avcodec_free_context(&ist->dec_ctx); av_freep(&input_streams[i]); @@ -1055,17 +888,61 @@ error: exit_program(1); } -static void do_subtitle_out(OutputFile *of, - OutputStream *ost, - AVSubtitle *sub) +static void encode_subtitle_frame(OutputFile *of, OutputStream *ost, AVFrame *frame, AVPacket *pkt, int64_t pts_offset) +{ + 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_pts, AV_TIME_BASE_Q, ost->mux_timebase); + pkt->duration = av_rescale_q(frame->subtitle_end_time, (AVRational){ 1, 1000 }, 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_TRACE, "do_subtitle_out: sub->pts: %"PRId64" frame->pts: %"PRId64"\n", frame->subtitle_pts, frame->pts); + + if (frame->subtitle_pts == AV_NOPTS_VALUE) { av_log(NULL, AV_LOG_ERROR, "Subtitle packets must have a pts\n"); if (exit_on_error) exit_program(1); @@ -1074,14 +951,6 @@ static void do_subtitle_out(OutputFile *of, 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); - } - } - /* Note: DVB subtitle need one packet to draw them and one other packet to clear them */ /* XXX: signal it in the codec context ? */ @@ -1091,50 +960,43 @@ 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_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_pts = pts; + // subtitle_start_time is required to be 0 + frame->subtitle_pts += av_rescale_q(frame->subtitle_start_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q); + frame->subtitle_end_time -= frame->subtitle_start_time; + frame->subtitle_start_time = 0; + + for (i = 0; i < nb; i++) { + int ret, got_packet = 0; + 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 = av_rescale_q(frame->subtitle_start_time, (AVRational){ 1, 1000 }, ost->mux_timebase); else - pkt->pts += av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->mux_timebase); + pts_offset = av_rescale_q(frame->subtitle_end_time, (AVRational){ 1, 1000 }, ost->mux_timebase); } - pkt->dts = pkt->pts; - output_packet(of, pkt, ost, 0); + + encode_subtitle_frame(of, ost, frame, pkt, pts_offset); + + if (i == 1) + frame->num_subtitle_areas = save_num_rects; } } @@ -1544,8 +1406,26 @@ static int reap_filters(int flush) } do_audio_out(of, ost, filtered_frame); break; + case AVMEDIA_TYPE_SUBTITLE: + + if (filtered_frame->format == AV_SUBTITLE_FMT_ASS && !enc->subtitle_header + && filtered_frame->subtitle_header) { + const char *subtitle_header = (char *)filtered_frame->subtitle_header->data; + enc->subtitle_header = (uint8_t *)av_strdup(subtitle_header); + if (!enc->subtitle_header) + return AVERROR(ENOMEM); + enc->subtitle_header_size = strlen(subtitle_header); + } + + if ((ost->enc_ctx->width == 0 || ost->enc_ctx->height == 0) + && filter->inputs[0]->w > 0 && filter->inputs[0]->h > 0 ) { + ost->enc_ctx->width = filter->inputs[0]->w; + ost->enc_ctx->height = filter->inputs[0]->h; + } + + do_subtitle_out(of, ost, filtered_frame); + break; default: - // TODO support subtitle filters av_assert0(0); } @@ -2138,7 +2018,8 @@ static int ifilter_has_all_input_formats(FilterGraph *fg) int i; for (i = 0; i < fg->nb_inputs; i++) { if (fg->inputs[i]->format < 0 && (fg->inputs[i]->type == AVMEDIA_TYPE_AUDIO || - fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO)) + fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO || + fg->inputs[i]->type == AVMEDIA_TYPE_SUBTITLE)) return 0; } return 1; @@ -2243,7 +2124,7 @@ static int ifilter_send_eof(InputFilter *ifilter, int64_t pts) // the filtergraph was never configured if (ifilter->format < 0) ifilter_parameters_from_codecpar(ifilter, ifilter->ist->st->codecpar); - if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO)) { + if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO || ifilter->type == AVMEDIA_TYPE_SUBTITLE)) { av_log(NULL, AV_LOG_ERROR, "Cannot determine format of input stream %d:%d after EOF\n", ifilter->ist->file_index, ifilter->ist->st->index); return AVERROR_INVALIDDATA; } @@ -2281,7 +2162,7 @@ static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacke static int send_frame_to_filters(InputStream *ist, AVFrame *decoded_frame) { - int i, ret; + int i, ret = 0; av_assert1(ist->nb_filters > 0); /* ensure ret is initialized */ for (i = 0; i < ist->nb_filters; i++) { @@ -2482,81 +2363,223 @@ fail: return err < 0 ? err : ret; } -static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output, +static void subtitle_resend_current(InputStream *ist, int64_t heartbeat_pts) +{ + AVFrame *frame; + int64_t pts, end_pts; + + if (ist->subtitle_heartbeat.recent_sub) { + frame = av_frame_clone(ist->subtitle_heartbeat.recent_sub); + if (!frame) { + av_log(ist->dec_ctx, AV_LOG_ERROR, "Unable to alloc frame (out of memory).\n"); + return; + } + + pts = heartbeat_pts; //av_rescale_q(frame->subtitle_pts + frame->subtitle_start_time * 1000LL, AV_TIME_BASE_Q, ist->st->time_base); + end_pts = av_rescale_q(frame->subtitle_pts + frame->subtitle_end_time * 1000LL, AV_TIME_BASE_Q, ist->st->time_base); + } else { + 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; + frame->format = avcodec_descriptor_get_subtitle_format(ist->dec_ctx->codec_descriptor); + + av_frame_get_buffer2(frame, 0); + + frame->width = ist->subtitle_heartbeat.w; + frame->height = ist->subtitle_heartbeat.h; + + pts = (ist->subtitle_heartbeat.end_pts < INT64_MAX && ist->subtitle_heartbeat.end_pts > 0) + ? ist->subtitle_heartbeat.end_pts : heartbeat_pts; + end_pts = INT64_MAX; + + frame->subtitle_pts = av_rescale_q(pts, ist->st->time_base, AV_TIME_BASE_Q); + frame->subtitle_end_time = 1000; + } + + av_log(NULL, AV_LOG_DEBUG, "subtitle_heartbeat: call subtitle_resend_current %"PRId64" \n", pts); + + frame->pts = pts; + ist->subtitle_heartbeat.last_pts = pts; + ist->subtitle_heartbeat.end_pts = end_pts; + + send_frame_to_filters(ist, frame); + + av_frame_free(&frame); +} + +static void subtitle_heartbeat(InputStream *ist, int64_t pts) +{ + int i; + int64_t pts2; + + if (ist->st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) + return; + + /* When a frame is read from a file, examine all subtitle streams in + the same file and send the subtitle 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 < nb_input_streams; i++) { + InputStream *ist2 = input_streams[i]; + if (!ist2->subtitle_heartbeat.is_active) + 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->subtitle_heartbeat.last_pts) + continue; + if (pts2 >= ist2->subtitle_heartbeat.end_pts) { + /* 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 */ + if (ist2->subtitle_heartbeat.recent_sub) + av_frame_free(&ist2->subtitle_heartbeat.recent_sub); + + av_log(NULL, AV_LOG_DEBUG, "subtitle_heartbeat: clear + resend - pts: %"PRIi64"\n", pts2 + 1); + subtitle_resend_current(ist2, pts2 + 1); + continue; + } + if (!ist2->subtitle_heartbeat.check_buffer_requests) { + unsigned j, nb_reqs; + 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) { + av_log(NULL, AV_LOG_DEBUG, "subtitle_heartbeat: resend - pts: %"PRIi64"\n", pts2); + subtitle_resend_current(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, end_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 = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor); + + if (!ist->subtitle_heartbeat.header && avctx->subtitle_header && avctx->subtitle_header_size > 0) { + ist->subtitle_heartbeat.header = av_buffer_allocz(avctx->subtitle_header_size + 1); + if (!ist->subtitle_heartbeat.header) + return AVERROR(ENOMEM); + memcpy(ist->subtitle_heartbeat.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++) { + 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, + if (ist->prev_sub.got_output && ist->prev_sub.subtitle) { + end = av_rescale(decoded_frame->subtitle_pts - ist->prev_sub.subtitle->subtitle_pts, 1000, AV_TIME_BASE); - if (end < ist->prev_sub.subtitle.end_display_time) { - av_log(ist->dec_ctx, AV_LOG_DEBUG, + if (end < ist->prev_sub.subtitle->subtitle_end_time) { + av_log(avctx, AV_LOG_DEBUG, "Subtitle duration reduced from %"PRId32" to %d%s\n", - ist->prev_sub.subtitle.end_display_time, end, + ist->prev_sub.subtitle->subtitle_end_time, end, end <= 0 ? ", dropping it" : ""); - ist->prev_sub.subtitle.end_display_time = end; + ist->prev_sub.subtitle->subtitle_end_time = 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(AVFrame*, decoded_frame, ist->prev_sub.subtitle); if (end <= 0) - goto out; + return end; } - if (!*got_output) + if (!*got_output || !decoded_frame) return ret; - if (ist->sub2video.frame) { - sub2video_update(ist, INT64_MIN, &subtitle); - } else if (ist->nb_filters) { - if (!ist->sub2video.sub_queue) - ist->sub2video.sub_queue = av_fifo_alloc(8 * sizeof(AVSubtitle)); - if (!ist->sub2video.sub_queue) - exit_program(1); - if (!av_fifo_space(ist->sub2video.sub_queue)) { - ret = av_fifo_realloc2(ist->sub2video.sub_queue, 2 * av_fifo_size(ist->sub2video.sub_queue)); - if (ret < 0) - exit_program(1); + decoded_frame->type = AVMEDIA_TYPE_SUBTITLE; + decoded_frame->format = avcodec_descriptor_get_subtitle_format(avctx->codec_descriptor); + + ////if ((ret = av_frame_get_buffer2(decoded_frame, 0)) < 0) + //// return ret; + + if ((ret = av_buffer_replace(&decoded_frame->subtitle_header, ist->subtitle_heartbeat.header)) < 0) + return ret; + + pts = av_rescale_q(decoded_frame->subtitle_pts + decoded_frame->subtitle_start_time * 1000LL, + AV_TIME_BASE_Q, ist->st->time_base); + end_pts = av_rescale_q(decoded_frame->subtitle_pts + decoded_frame->subtitle_end_time * 1000LL, + AV_TIME_BASE_Q, ist->st->time_base); + + ist->subtitle_heartbeat.last_pts = decoded_frame->pts = pts; + ist->subtitle_heartbeat.end_pts = end_pts; + + if (ist->nb_filters > 0) { + AVFrame *filter_frame = av_frame_clone(decoded_frame); + if (!filter_frame) { + err = AVERROR(ENOMEM); + goto end; } - av_fifo_generic_write(ist->sub2video.sub_queue, &subtitle, sizeof(subtitle), NULL); - free_sub = 0; - } - if (!subtitle.num_rects) - goto out; + err = send_frame_to_filters(ist, filter_frame); + av_frame_free(&filter_frame); + } - ist->frames_decoded++; + if (err >= 0) { + for (i = 0; i < nb_output_streams; i++) { + OutputStream *ost = output_streams[i]; + InputStream *ist_src = get_input_stream(ost); - for (i = 0; i < nb_output_streams; i++) { - OutputStream *ost = output_streams[i]; + if (!ist_src || !check_output_constraints(ist, ost) + || ist_src != ist + || !ost->encoding_needed + || ost->enc->type != AVMEDIA_TYPE_SUBTITLE) + continue; - if (!check_output_constraints(ist, ost) || !ost->encoding_needed - || ost->enc->type != AVMEDIA_TYPE_SUBTITLE) - continue; + if (ost->filter && ost->filter->filter->nb_inputs > 0) + continue; - do_subtitle_out(output_files[ost->file_index], ost, &subtitle); + ////if (!ost->pkt && !((ost->pkt = av_packet_alloc()))) + //// exit_program(1); + do_subtitle_out(output_files[ost->file_index], ost, decoded_frame); + } } -out: - if (free_sub) - avsubtitle_free(&subtitle); - return ret; +end: + av_frame_free(&ist->subtitle_heartbeat.recent_sub); + ist->subtitle_heartbeat.recent_sub = av_frame_clone(decoded_frame); + + + av_frame_unref(decoded_frame); + return err < 0 ? err : ret; } static int send_filter_eof(InputStream *ist) @@ -2660,7 +2683,7 @@ static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eo case AVMEDIA_TYPE_SUBTITLE: if (repeating) break; - ret = transcode_subtitles(ist, avpkt, &got_output, &decode_failed); + ret = decode_subtitles(ist, avpkt, &got_output, &decode_failed); if (!pkt && ret >= 0) ret = AVERROR_EOF; av_packet_unref(avpkt); @@ -2928,13 +2951,6 @@ FF_ENABLE_DEPRECATION_WARNINGS return 0; } -static InputStream *get_input_stream(OutputStream *ost) -{ - if (ost->source_index >= 0) - return input_streams[ost->source_index]; - return NULL; -} - static int compare_int64(const void *a, const void *b) { return FFDIFFSIGN(*(const int64_t *)a, *(const int64_t *)b); @@ -3413,7 +3429,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; } @@ -3466,19 +3482,14 @@ static int init_output_stream(OutputStream *ost, AVFrame *frame, } if (ist && ist->dec->type == AVMEDIA_TYPE_SUBTITLE && ost->enc->type == AVMEDIA_TYPE_SUBTITLE) { - int input_props = 0, output_props = 0; - AVCodecDescriptor const *input_descriptor = - avcodec_descriptor_get(dec->codec_id); - AVCodecDescriptor const *output_descriptor = - avcodec_descriptor_get(ost->enc_ctx->codec_id); - if (input_descriptor) - input_props = input_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB); - if (output_descriptor) - output_props = output_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB); - if (input_props && output_props && input_props != output_props) { - snprintf(error, error_len, - "Subtitle encoding currently only possible from text to text " - "or bitmap to bitmap"); + AVCodecDescriptor const *input_descriptor = avcodec_descriptor_get(dec->codec_id); + AVCodecDescriptor const *output_descriptor = avcodec_descriptor_get(ost->enc_ctx->codec_id); + const enum AVSubtitleType in_subtitle_format = output_descriptor ? avcodec_descriptor_get_subtitle_format(input_descriptor) : AV_SUBTITLE_FMT_UNKNOWN; + const enum AVSubtitleType out_subtitle_format = output_descriptor ? avcodec_descriptor_get_subtitle_format(output_descriptor) : AV_SUBTITLE_FMT_UNKNOWN; + + if (ist->nb_filters == 0 && in_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN && out_subtitle_format != AV_SUBTITLE_FMT_UNKNOWN + && in_subtitle_format != out_subtitle_format) { + snprintf(error, error_len, "Subtitle encoding is only possible from text to text or bitmap to bitmap"); return AVERROR_INVALIDDATA; } } @@ -4479,7 +4490,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_heartbeat(ist, pkt->pts); process_input_packet(ist, pkt, 0); @@ -4691,6 +4702,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_heartbeat.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 9b200b806a..51da5ab151 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -349,17 +349,18 @@ 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_heartbeat { + int is_active; + int check_buffer_requests; int64_t last_pts; int64_t end_pts; - AVFifoBuffer *sub_queue; ///< queue of AVSubtitle* before filter init - AVFrame *frame; + AVFrame *recent_sub; int w, h; - unsigned int initialize; ///< marks if sub2video_update should force an initialization - } sub2video; + AVBufferRef *header; + } subtitle_heartbeat; /* decoded data from this stream goes into all those filters * currently video and audio only */ @@ -659,8 +660,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 501a0acd61..09932bec1d 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -22,6 +22,8 @@ #include "ffmpeg.h" +#include "libavutil/ass_split_internal.h" + #include "libavfilter/avfilter.h" #include "libavfilter/buffersink.h" #include "libavfilter/buffersrc.h" @@ -30,11 +32,9 @@ #include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/channel_layout.h" -#include "libavutil/display.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/pixfmt.h" -#include "libavutil/imgutils.h" #include "libavutil/samplefmt.h" // FIXME: YUV420P etc. are actually supported with full color range, @@ -215,9 +215,8 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) InputFilter *ifilter; int i; - // TODO: support other filter types - if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) { - av_log(NULL, AV_LOG_FATAL, "Only video and audio filters supported " + if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO && type != AVMEDIA_TYPE_SUBTITLE) { + av_log(NULL, AV_LOG_FATAL, "Only video, audio and subtitle filters supported " "currently.\n"); exit_program(1); } @@ -238,8 +237,9 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) for (i = 0; i < s->nb_streams; i++) { enum AVMediaType stream_type = s->streams[i]->codecpar->codec_type; if (stream_type != type && - !(stream_type == AVMEDIA_TYPE_SUBTITLE && - type == AVMEDIA_TYPE_VIDEO /* sub2video hack */)) + // in the followng case we auto-insert the graphicsub2video conversion filter + // for retaining compatibility with the previous sub2video hack + !(stream_type == AVMEDIA_TYPE_SUBTITLE && type == AVMEDIA_TYPE_VIDEO)) continue; if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : p) == 1) { st = s->streams[i]; @@ -286,6 +286,13 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) ifilter->type = ist->st->codecpar->codec_type; ifilter->name = describe_filter_link(fg, in, 1); + if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE && ist->dec_ctx) { + const AVCodecDescriptor *codec_descriptor = ist->dec_ctx->codec_descriptor; + if (!codec_descriptor) + codec_descriptor = avcodec_descriptor_get(ist->dec_ctx->codec_id); + fg->inputs[fg->nb_inputs - 1]->format = avcodec_descriptor_get_subtitle_format(codec_descriptor); + } + ifilter->frame_queue = av_fifo_alloc(8 * sizeof(AVFrame*)); if (!ifilter->frame_queue) exit_program(1); @@ -405,6 +412,39 @@ static int insert_filter(AVFilterContext **last_filter, int *pad_idx, return 0; } +static int configure_output_subtitle_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) +{ + OutputStream *ost = ofilter->ost; + AVFilterContext *last_filter = out->filter_ctx; + int pad_idx = out->pad_idx; + int ret; + char name[255]; + + snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index); + ret = avfilter_graph_create_filter(&ofilter->filter, + avfilter_get_by_name("sbuffersink"), + name, NULL, NULL, fg->graph); + + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "Unable to create filter sbuffersink\n"); + return ret; + } + + ////snprintf(name, sizeof(name), "trim_out_%d_%d", + //// ost->file_index, ost->index); + ////ret = insert_trim(of->start_time, of->recording_time, + //// &last_filter, &pad_idx, name); + ////if (ret < 0) + //// return ret; + + ////ost->st->codecpar->codec_tag = MKTAG('a', 's', 's', 's'); + + if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0) + return ret; + + return 0; +} + static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) { OutputStream *ost = ofilter->ost; @@ -585,7 +625,8 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, int i; for (i=0; ictx->nb_streams; i++) - if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) + if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || + of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) break; if (ictx->nb_streams) { @@ -619,6 +660,7 @@ static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) { case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, ofilter, out); case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, ofilter, out); + case AVMEDIA_TYPE_SUBTITLE: return configure_output_subtitle_filter(fg, ofilter, out); default: av_assert0(0); return 0; } } @@ -638,51 +680,112 @@ void check_filter_outputs(void) } } -static int sub2video_prepare(InputStream *ist, InputFilter *ifilter) +static int configure_input_subtitle_filter(FilterGraph *fg, InputFilter *ifilter, + AVFilterInOut *in) { - AVFormatContext *avf = input_files[ist->file_index]->ctx; - int i, w, h; + AVFilterContext *last_filter; + const AVFilter *buffer_filt = avfilter_get_by_name("sbuffer"); + InputStream *ist = ifilter->ist; + AVBPrint args; + char name[255]; + int ret, pad_idx = 0; + int w, h; + AVBufferSrcParameters *par = av_buffersrc_parameters_alloc(); + enum AVMediaType media_type; + + if (!par) + return AVERROR(ENOMEM); + + par->format = AV_PIX_FMT_NONE; + + if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { + av_log(NULL, AV_LOG_ERROR, "Cannot connect subtitle filter to audio input\n"); + ret = AVERROR(EINVAL); + goto fail; + } + + if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { + av_log(NULL, AV_LOG_ERROR, "Cannot connect subtitle filter to video input\n"); + ret = AVERROR(EINVAL); + goto fail; + } + + if (!ist->subtitle_heartbeat.header && ist->dec_ctx->subtitle_header && ist->dec_ctx->subtitle_header_size > 0) { + ist->subtitle_heartbeat.header = av_buffer_allocz(ist->dec_ctx->subtitle_header_size + 1); + if (!ist->subtitle_heartbeat.header) + return AVERROR(ENOMEM); + memcpy(ist->subtitle_heartbeat.header->data, ist->dec_ctx->subtitle_header, ist->dec_ctx->subtitle_header_size); + } + + ist->subtitle_heartbeat.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); - } - } - if (!(w && h)) { - w = FFMAX(w, 720); - h = FFMAX(h, 576); - } - av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h); + w = ist->dec_ctx->width; + h = ist->dec_ctx->height; } - ist->sub2video.w = ifilter->width = w; - ist->sub2video.h = ifilter->height = h; - ifilter->width = ist->dec_ctx->width ? ist->dec_ctx->width : ist->sub2video.w; - ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h; + if (!(w && h) && ist->dec_ctx->subtitle_header) { + ASSSplitContext *ass_ctx = avpriv_ass_split((char *)ist->dec_ctx->subtitle_header); + ASS *ass = (ASS *)ass_ctx; + w = ass->script_info.play_res_x; + h = ass->script_info.play_res_y; + avpriv_ass_split_free(ass_ctx); + } - /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the - palettes for all rectangles are identical or compatible */ - ifilter->format = AV_PIX_FMT_RGB32; + ist->subtitle_heartbeat.w = w; + ist->subtitle_heartbeat.h = h; + av_log(ifilter, AV_LOG_INFO, "subtitle input filter: decoding size %dx%d\n", ist->subtitle_heartbeat.w, ist->subtitle_heartbeat.h); - ist->sub2video.frame = av_frame_alloc(); - if (!ist->sub2video.frame) - return AVERROR(ENOMEM); - ist->sub2video.last_pts = INT64_MIN; - ist->sub2video.end_pts = INT64_MIN; + ifilter->width = w; + ifilter->height = h; + ist->dec_ctx->width = w; + ist->dec_ctx->height = h; - /* 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; + ist->subtitle_heartbeat.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; + + media_type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx); + if (media_type == AVMEDIA_TYPE_VIDEO) { + av_log(NULL, AV_LOG_INFO, "Auto-inserting graphicsub2video filter\n"); + ret = insert_filter(&last_filter, &pad_idx, "graphicsub2video", NULL); + if (ret < 0) + return ret; + } + + if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0) + return ret; return 0; +fail: + av_freep(&par); + + return ret; } static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, @@ -701,8 +804,15 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, char name[255]; int ret, pad_idx = 0; int64_t tsoffset = 0; - AVBufferSrcParameters *par = av_buffersrc_parameters_alloc(); + AVBufferSrcParameters *par; + + if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { + // Automatically insert conversion filter to retain compatibility + // with sub2video command lines + return configure_input_subtitle_filter(fg, ifilter, in); + } + par = av_buffersrc_parameters_alloc(); if (!par) return AVERROR(ENOMEM); memset(par, 0, sizeof(*par)); @@ -717,12 +827,6 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, if (!fr.num) fr = av_guess_frame_rate(input_files[ist->file_index]->ctx, ist->st, NULL); - if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { - ret = sub2video_prepare(ist, ifilter); - if (ret < 0) - goto fail; - } - sar = ifilter->sample_aspect_ratio; if(!sar.den) sar = (AVRational){0,1}; @@ -734,7 +838,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); @@ -940,6 +1044,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; } } @@ -1133,19 +1238,6 @@ int configure_filtergraph(FilterGraph *fg) } } - /* process queued up subtitle packets */ - for (i = 0; i < fg->nb_inputs; i++) { - InputStream *ist = fg->inputs[i]->ist; - if (ist->sub2video.sub_queue && ist->sub2video.frame) { - while (av_fifo_size(ist->sub2video.sub_queue)) { - AVSubtitle tmp; - av_fifo_generic_read(ist->sub2video.sub_queue, &tmp, sizeof(tmp), NULL); - sub2video_update(ist, INT64_MIN, &tmp); - avsubtitle_free(&tmp); - } - } - } - return 0; fail: @@ -1168,6 +1260,7 @@ int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame) ifilter->sample_rate = frame->sample_rate; ifilter->channels = frame->channels; ifilter->channel_layout = frame->channel_layout; + ifilter->type = frame->type; av_freep(&ifilter->displaymatrix); sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX); diff --git a/fftools/ffmpeg_hw.c b/fftools/ffmpeg_hw.c index 14e702bd92..be69d54aaf 100644 --- a/fftools/ffmpeg_hw.c +++ b/fftools/ffmpeg_hw.c @@ -449,7 +449,7 @@ int hw_device_setup_for_encode(OutputStream *ost) AVBufferRef *frames_ref = NULL; int i; - if (ost->filter) { + if (ost->filter && ost->filter->filter) { frames_ref = av_buffersink_get_hw_frames_ctx(ost->filter->filter); if (frames_ref && ((AVHWFramesContext*)frames_ref->data)->format == diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index 9c820ab73f..476ef628e4 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -2211,8 +2211,9 @@ static void init_output_filter(OutputFilter *ofilter, OptionsContext *o, switch (ofilter->type) { case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(o, oc, -1); break; case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream(o, oc, -1); break; + case AVMEDIA_TYPE_SUBTITLE: ost = new_subtitle_stream(o, oc, -1); break; default: - av_log(NULL, AV_LOG_FATAL, "Only video and audio filters are supported " + av_log(NULL, AV_LOG_FATAL, "Only video, audio and subtitle filters are supported " "currently.\n"); exit_program(1); } diff --git a/tests/ref/fate/filter-overlay-dvdsub-2397 b/tests/ref/fate/filter-overlay-dvdsub-2397 index 483e5fa4e0..3bf198d6ad 100644 --- a/tests/ref/fate/filter-overlay-dvdsub-2397 +++ b/tests/ref/fate/filter-overlay-dvdsub-2397 @@ -490,368 +490,367 @@ 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, 0x61e0f688 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, 0xa47de755 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 diff --git a/tests/ref/fate/sub-dvb b/tests/ref/fate/sub-dvb index cbd1801d64..8f33c75d70 100644 --- a/tests/ref/fate/sub-dvb +++ b/tests/ref/fate/sub-dvb @@ -1,75 +1,93 @@ #tb 0: 1/1000000 #media_type 0: subtitle #codec_id 0: dvb_subtitle -0, 15600000, 15600000, 159000, 1168, 0xd0f89d82 -0, 15759000, 15759000, 159000, 14, 0x064900eb -0, 15760000, 15760000, 239000, 1544, 0xe60f1751 -0, 15999000, 15999000, 239000, 14, 0x0729010b -0, 16000000, 16000000, 339000, 1658, 0xbe343093 -0, 16339000, 16339000, 339000, 14, 0x0809012b -0, 16340000, 16340000, 599000, 2343, 0xc68f07ef -0, 16939000, 16939000, 599000, 14, 0x08e9014b -0, 16940000, 16940000, 459000, 2568, 0x0ee657b1 -0, 17399000, 17399000, 459000, 14, 0x09c9016b -0, 17400000, 17400000, 359000, 3422, 0xba5b63ce -0, 17759000, 17759000, 359000, 14, 0x0aa9018b -0, 17760000, 17760000, 219000, 5078, 0x95b19902 -0, 17979000, 17979000, 219000, 14, 0x0b8901ab -0, 17980000, 17980000, 959000, 5808, 0xc9717b89 -0, 18939000, 18939000, 959000, 14, 0x0c6901cb -0, 18940000, 18940000, 219000, 6015, 0x0becbfac -0, 19159000, 19159000, 219000, 14, 0x064900eb -0, 19160000, 19160000, 259000, 6519, 0xfcd24d26 -0, 19419000, 19419000, 259000, 14, 0x0729010b -0, 19420000, 19420000, 99000, 7061, 0xf0320408 -0, 19519000, 19519000, 99000, 14, 0x0809012b -0, 19520000, 19520000, 219000, 4773, 0x66c93074 -0, 19739000, 19739000, 219000, 14, 0x08e9014b -0, 19740000, 19740000, 219000, 5546, 0x06052c81 -0, 19959000, 19959000, 219000, 14, 0x09c9016b -0, 19960000, 19960000, 239000, 5754, 0x904f7325 -0, 20199000, 20199000, 239000, 14, 0x0aa9018b -0, 20200000, 20200000, 139000, 6099, 0xe30cde07 -0, 20339000, 20339000, 139000, 14, 0x0b8901ab -0, 20340000, 20340000, 799000, 6839, 0x770fcb6c -0, 21139000, 21139000, 799000, 14, 0x0c6901cb -0, 21140000, 21140000, 239000, 4744, 0xa91e1b41 -0, 21379000, 21379000, 239000, 14, 0x064900eb -0, 21380000, 21380000, 339000, 5824, 0xcf6d782b -0, 21719000, 21719000, 339000, 14, 0x0729010b -0, 21720000, 21720000, 1439000, 6212, 0xabf8f7cf -0, 23159000, 23159000, 1439000, 14, 0x0809012b -0, 23160000, 23160000, 1319000, 7082, 0xd7ca10f2 -0, 24479000, 24479000, 1319000, 14, 0x08e9014b -0, 24480000, 24480000, 219000, 5345, 0x12b2cae0 -0, 24699000, 24699000, 219000, 14, 0x09c9016b -0, 24700000, 24700000, 219000, 5765, 0xc7d46192 -0, 24919000, 24919000, 219000, 14, 0x0aa9018b -0, 24920000, 24920000, 599000, 6557, 0xcb995d30 -0, 25519000, 25519000, 599000, 14, 0x0b8901ab -0, 25520000, 25520000, 219000, 7091, 0xe6ea0559 -0, 25739000, 25739000, 219000, 14, 0x0c6901cb -0, 25740000, 25740000, 239000, 7305, 0xb66c404e -0, 25979000, 25979000, 239000, 14, 0x064900eb -0, 25980000, 25980000, 359000, 7590, 0x0cc2a481 -0, 26339000, 26339000, 359000, 14, 0x0729010b -0, 26340000, 26340000, 219000, 4629, 0xe18cfea8 -0, 26559000, 26559000, 219000, 14, 0x0809012b -0, 26560000, 26560000, 719000, 4785, 0x82043fc0 -0, 27279000, 27279000, 719000, 14, 0x08e9014b -0, 27280000, 27280000, 459000, 6061, 0xbde7d245 -0, 27739000, 27739000, 459000, 14, 0x09c9016b -0, 27740000, 27740000, 239000, 6301, 0x92d01a51 -0, 27979000, 27979000, 239000, 14, 0x0aa9018b -0, 27980000, 27980000, 99000, 6736, 0xbd25a134 -0, 28079000, 28079000, 99000, 14, 0x0b8901ab -0, 28080000, 28080000, 219000, 7214, 0x7ef93c13 -0, 28299000, 28299000, 219000, 14, 0x0c6901cb -0, 28300000, 28300000, 239000, 7366, 0x5bed7fcd -0, 28539000, 28539000, 239000, 14, 0x064900eb -0, 28540000, 28540000, 599000, 4564, 0x7f4c014b -0, 29139000, 29139000, 599000, 14, 0x0729010b -0, 29140000, 29140000, 219000, 4637, 0x682626b7 -0, 29359000, 29359000, 219000, 14, 0x0809012b -0, 29360000, 29360000, 1679000, 5358, 0x29e30c48 -0, 31039000, 31039000, 1679000, 14, 0x08e9014b +0, 0, 0, 279000, 14, 0x05d900db +0, 279000, 279000, 279000, 14, 0x064900eb +0, 280000, 280000, 4999000, 14, 0x06b900fb +0, 5279000, 5279000, 4999000, 14, 0x0729010b +0, 5280000, 5280000, 5019000, 14, 0x0799011b +0, 10299000, 10299000, 5019000, 14, 0x0809012b +0, 10300000, 10300000, 3599000, 14, 0x0879013b +0, 13899000, 13899000, 3599000, 14, 0x08e9014b +0, 13900000, 13900000, 219000, 14, 0x0959015b +0, 14119000, 14119000, 219000, 14, 0x09c9016b +0, 14120000, 14120000, 1439000, 14, 0x0a39017b +0, 15559000, 15559000, 1439000, 14, 0x0aa9018b +0, 15560000, 15560000, 39000, 14, 0x0b19019b +0, 15599000, 15599000, 39000, 14, 0x0b8901ab +0, 15600000, 15600000, 159000, 1168, 0xd69da022 +0, 15759000, 15759000, 159000, 14, 0x0c6901cb +0, 15760000, 15760000, 239000, 1544, 0xc5f116f1 +0, 15999000, 15999000, 239000, 14, 0x064900eb +0, 16000000, 16000000, 339000, 1658, 0x73563033 +0, 16339000, 16339000, 339000, 14, 0x0729010b +0, 16340000, 16340000, 599000, 2343, 0x7ac2078f +0, 16939000, 16939000, 599000, 14, 0x0809012b +0, 16940000, 16940000, 459000, 2568, 0x6eaa5751 +0, 17399000, 17399000, 459000, 14, 0x08e9014b +0, 17400000, 17400000, 359000, 3422, 0xd9d0636e +0, 17759000, 17759000, 359000, 14, 0x09c9016b +0, 17760000, 17760000, 219000, 5078, 0x722c9862 +0, 17979000, 17979000, 219000, 14, 0x0aa9018b +0, 17980000, 17980000, 959000, 5808, 0x38dd7ae9 +0, 18939000, 18939000, 959000, 14, 0x0b8901ab +0, 18940000, 18940000, 219000, 6015, 0xd4d2c40c +0, 19159000, 19159000, 219000, 14, 0x0c6901cb +0, 19160000, 19160000, 259000, 6519, 0x08af4c86 +0, 19419000, 19419000, 259000, 14, 0x064900eb +0, 19420000, 19420000, 99000, 7061, 0xecf10368 +0, 19519000, 19519000, 99000, 14, 0x0729010b +0, 19520000, 19520000, 219000, 4773, 0xbee42fd4 +0, 19739000, 19739000, 219000, 14, 0x0809012b +0, 19740000, 19740000, 219000, 5546, 0xdb822be1 +0, 19959000, 19959000, 219000, 14, 0x08e9014b +0, 19960000, 19960000, 239000, 5754, 0xfdcc7285 +0, 20199000, 20199000, 239000, 14, 0x09c9016b +0, 20200000, 20200000, 139000, 6099, 0xa409dd67 +0, 20339000, 20339000, 139000, 14, 0x0aa9018b +0, 20340000, 20340000, 799000, 6839, 0xc5eecacc +0, 21139000, 21139000, 799000, 14, 0x0b8901ab +0, 21140000, 21140000, 239000, 4744, 0x4e451fa1 +0, 21379000, 21379000, 239000, 14, 0x0c6901cb +0, 21380000, 21380000, 339000, 5824, 0x5299778b +0, 21719000, 21719000, 339000, 14, 0x064900eb +0, 21720000, 21720000, 1439000, 6212, 0x6d15f72f +0, 23159000, 23159000, 1439000, 14, 0x0729010b +0, 23160000, 23160000, 1319000, 7082, 0xe5c91052 +0, 24479000, 24479000, 1319000, 14, 0x0809012b +0, 24480000, 24480000, 219000, 5345, 0x2e5eca40 +0, 24699000, 24699000, 219000, 14, 0x08e9014b +0, 24700000, 24700000, 219000, 5765, 0x118060f2 +0, 24919000, 24919000, 219000, 14, 0x09c9016b +0, 24920000, 24920000, 599000, 6557, 0x89275c90 +0, 25519000, 25519000, 599000, 14, 0x0aa9018b +0, 25520000, 25520000, 219000, 7091, 0x996904b9 +0, 25739000, 25739000, 219000, 14, 0x0b8901ab +0, 25740000, 25740000, 239000, 7305, 0xc23e44ae +0, 25979000, 25979000, 239000, 14, 0x0c6901cb +0, 25980000, 25980000, 359000, 7590, 0xc5a3a3e1 +0, 26339000, 26339000, 359000, 14, 0x064900eb +0, 26340000, 26340000, 219000, 4629, 0x7ad6fe08 +0, 26559000, 26559000, 219000, 14, 0x0729010b +0, 26560000, 26560000, 719000, 4785, 0xcd3f3f20 +0, 27279000, 27279000, 719000, 14, 0x0809012b +0, 27280000, 27280000, 459000, 6061, 0x8b04d1a5 +0, 27739000, 27739000, 459000, 14, 0x08e9014b +0, 27740000, 27740000, 239000, 6301, 0xe7de19b1 +0, 27979000, 27979000, 239000, 14, 0x09c9016b +0, 27980000, 27980000, 99000, 6736, 0x38b3a094 +0, 28079000, 28079000, 99000, 14, 0x0aa9018b +0, 28080000, 28080000, 219000, 7214, 0x0b783b73 +0, 28299000, 28299000, 219000, 14, 0x0b8901ab +0, 28300000, 28300000, 239000, 7366, 0x98bf842d +0, 28539000, 28539000, 239000, 14, 0x0c6901cb +0, 28540000, 28540000, 599000, 4564, 0x3d9600ab +0, 29139000, 29139000, 599000, 14, 0x064900eb +0, 29140000, 29140000, 219000, 4637, 0x01f02617 +0, 29359000, 29359000, 219000, 14, 0x0729010b +0, 29360000, 29360000, 1679000, 5358, 0x5b0f0ba8 +0, 31039000, 31039000, 1679000, 14, 0x0809012b +0, 31040000, 31040000, 359000, 14, 0x0879013b +0, 31399000, 31399000, 359000, 14, 0x08e9014b +0, 31400000, 31400000, 479000, 14, 0x0959015b +0, 31879000, 31879000, 479000, 14, 0x09c9016b diff --git a/tests/ref/fate/sub2video b/tests/ref/fate/sub2video index 80abe9c905..01b4800967 100644 --- a/tests/ref/fate/sub2video +++ b/tests/ref/fate/sub2video @@ -36,151 +36,109 @@ 0, 25, 25, 1, 518400, 0x7322e11c 0, 26, 26, 1, 518400, 0x45af1a84 0, 27, 27, 1, 518400, 0x7b781071 -0, 28, 28, 1, 518400, 0x4f7c706c -0, 29, 29, 1, 518400, 0xb227603b -0, 30, 30, 1, 518400, 0x7b4b89c2 -0, 31, 31, 1, 518400, 0x456da21e -0, 32, 32, 1, 518400, 0xb691979f -0, 33, 33, 1, 518400, 0x0dfaa66d -0, 34, 34, 1, 518400, 0x191a6f23 -0, 35, 35, 1, 518400, 0xa03b2605 -0, 36, 36, 1, 518400, 0xb36aff87 -0, 37, 37, 1, 518400, 0xf5f0bc4a -0, 38, 38, 1, 518400, 0x863d701a -0, 39, 39, 1, 518400, 0xd11b4dce -0, 40, 40, 1, 518400, 0x969236bd -0, 41, 41, 1, 518400, 0xb60a485c -0, 42, 42, 1, 518400, 0xe9796621 -0, 43, 43, 1, 518400, 0x3e8fc04b -0, 44, 44, 1, 518400, 0xac9944e3 -0, 45, 45, 1, 518400, 0x01452b4d -0, 46, 46, 1, 518400, 0xb384f6d2 -0, 47, 47, 1, 518400, 0xde69683f -0, 48, 48, 1, 518400, 0x7df08fba -0, 49, 49, 1, 518400, 0xbab197ea +0, 28, 28, 1, 518400, 0x08a0ed62 +0, 29, 29, 1, 518400, 0x39b1e7eb +0, 30, 30, 1, 518400, 0x20a11073 +0, 31, 31, 1, 518400, 0x7e19198d +0, 32, 32, 1, 518400, 0xeed206eb +0, 33, 33, 1, 518400, 0xcf451b3d +0, 34, 34, 1, 518400, 0xa0a2eb3c +0, 35, 35, 1, 518400, 0xd598a9e3 +0, 36, 36, 1, 518400, 0x10908608 +0, 37, 37, 1, 518400, 0xa3a03b86 +0, 38, 38, 1, 518400, 0x0190e39a +0, 39, 39, 1, 518400, 0x5d39b978 +0, 40, 40, 1, 518400, 0x5ba29f40 +0, 41, 41, 1, 518400, 0x86c1b3ed +0, 42, 42, 1, 518400, 0x77b1d91b +0, 43, 43, 1, 518400, 0xa28d38d6 +0, 44, 44, 1, 518400, 0xc86abb34 +0, 45, 45, 1, 518400, 0xe753a664 +0, 46, 46, 1, 518400, 0xc7ea803c +0, 47, 47, 1, 518400, 0x797eff2d +0, 48, 48, 1, 518400, 0xc69c300c +0, 49, 49, 1, 518400, 0x8d21303f 1, 15355000, 15355000, 4733000, 2094, 0x3c171425 0, 77, 77, 1, 518400, 0x902285d9 -0, 100, 100, 1, 518400, 0xbab197ea 1, 48797000, 48797000, 2560000, 2480, 0x7c0edf21 0, 244, 244, 1, 518400, 0x7a11c812 -0, 257, 257, 1, 518400, 0xbab197ea +0, 257, 257, 1, 518400, 0x34cdddee 1, 51433000, 51433000, 2366000, 3059, 0xc95b8a05 -0, 258, 258, 1, 518400, 0x34cdddee -0, 269, 269, 1, 518400, 0xbab197ea 1, 53910000, 53910000, 2696000, 2095, 0x61bb15ed 0, 270, 270, 1, 518400, 0x4db4ce51 -0, 283, 283, 1, 518400, 0xbab197ea +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, 290, 290, 1, 518400, 0xa8643af7 1, 58014000, 58014000, 1661000, 969, 0xe01878f0 -0, 291, 291, 1, 518400, 0xa8643af7 -0, 298, 298, 1, 518400, 0xbab197ea 1, 67724000, 67724000, 1365000, 844, 0xe7db4fc1 0, 339, 339, 1, 518400, 0xb1885c67 -0, 345, 345, 1, 518400, 0xbab197ea 1, 69175000, 69175000, 1558000, 802, 0xf48531ba 0, 346, 346, 1, 518400, 0x378e3fd0 -0, 354, 354, 1, 518400, 0xbab197ea +0, 354, 354, 1, 518400, 0xa3782469 1, 70819000, 70819000, 1865000, 1709, 0xb4d5a1bd -0, 355, 355, 1, 518400, 0xa3782469 -0, 363, 363, 1, 518400, 0xbab197ea 1, 72762000, 72762000, 1968000, 2438, 0x99d7bc82 0, 364, 364, 1, 518400, 0xba23a0d5 -0, 374, 374, 1, 518400, 0xbab197ea +0, 374, 374, 1, 518400, 0x129de2f8 1, 74806000, 74806000, 1831000, 2116, 0x96514097 -0, 375, 375, 1, 518400, 0x129de2f8 -0, 383, 383, 1, 518400, 0xbab197ea 1, 76716000, 76716000, 1262000, 1822, 0xefccc72e 0, 384, 384, 1, 518400, 0x19772f0f -0, 390, 390, 1, 518400, 0xbab197ea +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, 398, 398, 1, 518400, 0x300b5247 1, 79644000, 79644000, 2662000, 2956, 0x190778f7 -0, 399, 399, 1, 518400, 0x300b5247 1, 82380000, 82380000, 2764000, 3094, 0xc021b7d3 -0, 412, 412, 1, 518400, 0xbab197ea -0, 413, 413, 1, 518400, 0x6fd028fa -0, 426, 426, 1, 518400, 0xbab197ea +0, 412, 412, 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, 438, 438, 1, 518400, 0xb48d90c0 1, 87652000, 87652000, 1831000, 634, 0x8832fda1 -0, 439, 439, 1, 518400, 0xb48d90c0 -0, 447, 447, 1, 518400, 0xbab197ea 1, 91531000, 91531000, 2332000, 2080, 0x97a1146f 0, 458, 458, 1, 518400, 0xcb5a0173 -0, 469, 469, 1, 518400, 0xbab197ea 1, 95510000, 95510000, 3299000, 2964, 0x8b8f6684 0, 478, 478, 1, 518400, 0xb8a323e4 -0, 494, 494, 1, 518400, 0xbab197ea +0, 494, 494, 1, 518400, 0xc43518ba 1, 98872000, 98872000, 2161000, 1875, 0x9002ef71 -0, 495, 495, 1, 518400, 0xc43518ba -0, 505, 505, 1, 518400, 0xbab197ea 1, 101124000, 101124000, 4096000, 3872, 0x20c6ed9c 0, 506, 506, 1, 518400, 0x04e38692 -0, 526, 526, 1, 518400, 0xbab197ea 1, 105303000, 105303000, 2730000, 3094, 0xf203a663 0, 527, 527, 1, 518400, 0x856b0ee5 -0, 540, 540, 1, 518400, 0xbab197ea 1, 108106000, 108106000, 2059000, 2404, 0x41a7b429 0, 541, 541, 1, 518400, 0x3e5beee2 -0, 551, 551, 1, 518400, 0xbab197ea 1, 141556000, 141556000, 1661000, 1088, 0xde20aa20 0, 708, 708, 1, 518400, 0xb8bc1365 -0, 716, 716, 1, 518400, 0xbab197ea 0, 817, 817, 1, 518400, 0x83efa32d 1, 163445000, 163445000, 1331000, 339, 0x8bd186ef -0, 824, 824, 1, 518400, 0xbab197ea 0, 840, 840, 1, 518400, 0x03ea0e90 1, 168049000, 168049000, 1900000, 1312, 0x0bf20e8d -0, 850, 850, 1, 518400, 0xbab197ea +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, 861, 861, 1, 518400, 0x6eb72347 1, 172203000, 172203000, 1695000, 1826, 0x9a1ac769 -0, 869, 869, 1, 518400, 0xbab197ea 1, 173947000, 173947000, 1934000, 1474, 0xa9b03cdc 0, 870, 870, 1, 518400, 0x9c4a3a3d -0, 879, 879, 1, 518400, 0xbab197ea 1, 175957000, 175957000, 1763000, 1019, 0x20409355 0, 880, 880, 1, 518400, 0xc9ebfa89 -0, 889, 889, 1, 518400, 0xbab197ea 0, 946, 946, 1, 518400, 0xbaf801ef 1, 189295000, 189295000, 1968000, 1596, 0x408c726e -0, 956, 956, 1, 518400, 0xbab197ea 1, 191356000, 191356000, 1228000, 1517, 0xae8c5c2b 0, 957, 957, 1, 518400, 0x59f4e72f -0, 963, 963, 1, 518400, 0xbab197ea +0, 963, 963, 1, 518400, 0x9d5b9d69 1, 192640000, 192640000, 1763000, 2506, 0xa458d6d4 -0, 964, 964, 1, 518400, 0x9d5b9d69 -0, 972, 972, 1, 518400, 0xbab197ea 1, 195193000, 195193000, 1092000, 1074, 0x397ba9a8 0, 976, 976, 1, 518400, 0x923d1ce7 -0, 981, 981, 1, 518400, 0xbab197ea 1, 196361000, 196361000, 1524000, 1715, 0x695ca41e 0, 982, 982, 1, 518400, 0x6e652cd2 -0, 989, 989, 1, 518400, 0xbab197ea 1, 197946000, 197946000, 1160000, 789, 0xc63a189e 0, 990, 990, 1, 518400, 0x25113966 -0, 996, 996, 1, 518400, 0xbab197ea +0, 996, 996, 1, 518400, 0x2dc83609 1, 199230000, 199230000, 1627000, 1846, 0xeea8c599 -0, 997, 997, 1, 518400, 0x2dc83609 -0, 1004, 1004, 1, 518400, 0xbab197ea 1, 200924000, 200924000, 1763000, 922, 0xd4a87222 0, 1005, 1005, 1, 518400, 0x90483bc6 -0, 1013, 1013, 1, 518400, 0xbab197ea 0, 1053, 1053, 1, 518400, 0x3de86ab7 1, 210600000, 210600000, 1831000, 665, 0x55580135 -0, 1062, 1062, 1, 518400, 0xbab197ea 1, 214771000, 214771000, 1558000, 1216, 0x50d1f6c5 0, 1074, 1074, 1, 518400, 0x8c320e68 -0, 1082, 1082, 1, 518400, 0xbab197ea 0, 1128, 1128, 1, 518400, 0x81e977b2 1, 225640000, 225640000, 2127000, 2133, 0x670c11a5 -0, 1139, 1139, 1, 518400, 0xbab197ea +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..41e02c057c 100644 --- a/tests/ref/fate/sub2video_basic +++ b/tests/ref/fate/sub2video_basic @@ -2,94 +2,47 @@ #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, 3312, 3312, 1, 1382400, 0xc637b893 +0, 3684, 3684, 1, 1382400, 0x4c2960ca +0, 4520, 4520, 1, 1382400, 0x5fa18966 +0, 4586, 4586, 1, 1382400, 0x55f4b7b1 +0, 4648, 4648, 1, 1382400, 0xdfa4cf32 +0, 4717, 4717, 1, 1382400, 0x35023df8 +0, 4750, 4750, 1, 1382400, 0xed933219 +0, 4993, 4993, 1, 1382400, 0x1b26389a +0, 5029, 5029, 1, 1382400, 0xf0c7028b +0, 5070, 5070, 1, 1382400, 0x395f521d +0, 5119, 5119, 1, 1382400, 0x1ea87415 +0, 5170, 5170, 1, 1382400, 0xc6effdc1 +0, 5218, 5218, 1, 1382400, 0xba6846f8 +0, 5251, 5251, 1, 1382400, 0x033c5d5b +0, 5291, 5291, 1, 1382400, 0xef5abf66 +0, 5360, 5360, 1, 1382400, 0xec747954 +0, 5431, 5431, 1, 1382400, 0xfa34bcaf +0, 5491, 5491, 1, 1382400, 0x8b7a709b +0, 5588, 5588, 1, 1382400, 0xc333382f +0, 5688, 5688, 1, 1382400, 0xabe5dfcf +0, 5772, 5772, 1, 1382400, 0x56948101 +0, 5828, 5828, 1, 1382400, 0xb747834a +0, 5933, 5933, 1, 1382400, 0x3448baad +0, 6003, 6003, 1, 1382400, 0xaabe4f37 +0, 6839, 6839, 1, 1382400, 0x8a48cd6f +0, 7386, 7386, 1, 1382400, 0x49518c43 +0, 7501, 7501, 1, 1382400, 0x4a72fa21 +0, 7551, 7551, 1, 1382400, 0xa82f7de8 +0, 7605, 7605, 1, 1382400, 0xeba0b5f3 +0, 7649, 7649, 1, 1382400, 0xd6a91770 +0, 7699, 7699, 1, 1382400, 0x222f827c +0, 8032, 8032, 1, 1382400, 0x3270f4ff +0, 8084, 8084, 1, 1382400, 0x40813cb3 +0, 8116, 8116, 1, 1382400, 0x9d8fde41 +0, 8180, 8180, 1, 1382400, 0xc6d7a701 +0, 8209, 8209, 1, 1382400, 0x9d45f2dc +0, 8249, 8249, 1, 1382400, 0x8525ee40 +0, 8281, 8281, 1, 1382400, 0x5b26b98b +0, 8323, 8323, 1, 1382400, 0x51be311f +0, 8565, 8565, 1, 1382400, 0x00a4f2a3 +0, 8669, 8669, 1, 1382400, 0x40a445e8 +0, 8941, 8941, 1, 1382400, 0x43ef5128 +0, 8996, 8996, 1, 1382400, 0x3c3e3819 diff --git a/tests/ref/fate/sub2video_time_limited b/tests/ref/fate/sub2video_time_limited index 9fb6fb06f9..715af02fee 100644 --- a/tests/ref/fate/sub2video_time_limited +++ b/tests/ref/fate/sub2video_time_limited @@ -2,7 +2,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, 2, 2, 1, 8294400, 0xa87c518f -0, 10, 10, 1, 8294400, 0xa87c518f From patchwork Fri Dec 10 21:37:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32279 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2374420iog; Fri, 10 Dec 2021 13:39:54 -0800 (PST) X-Google-Smtp-Source: ABdhPJytNSiYUUjdCe/dsuTh3gjDeKtJzCeS2+viMIWs4nZ40ALzDmAJJZmt7biFDtG3wwTL4mv5 X-Received: by 2002:a05:6402:1849:: with SMTP id v9mr42517511edy.335.1639172394652; Fri, 10 Dec 2021 13:39:54 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id he37si6326502ejc.529.2021.12.10.13.39.54; Fri, 10 Dec 2021 13:39:54 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=W14rKF+P; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 8DE2268AF18; Fri, 10 Dec 2021 23:37:54 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM12-MW2-obe.outbound.protection.outlook.com (mail-mw2nam12olkn2068.outbound.protection.outlook.com [40.92.23.68]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D4EC068AEC8 for ; Fri, 10 Dec 2021 23:37:52 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=naONqsZSjjuT6rmM7tpyeUe/ii6YuHvuPzACWV5sgparVgzftld4bo2AoH6iNk6QxE5ZhewpoGjfOCTHpwpJLyHzNPzydVxmQQRpRUMmYdx9oS7bv70lLn8kNA5JgUG3SiKcBrJokk9yLxz9JMCBQVFBWeCCh6/q+ybJ9rngV8DCMYAovtOizY9VrOFVSwX8WvapVt9/TapCxm4h6YVL5L8i4vGGfJFVLYKqWh46Y6QLIQBBcU3FhFGQ0WSSYHXXjy3VJ5+W1jRTM+HLaD0bv5VLxqzjUYl3s0QS552l2QAFARYBAPd8JdQqji5BMJ/jf4SD84Fb3fVJJiwEAuxdsA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=PEr5v4/HVAs6sNUIpVFS6ON8WAePVTYCgWJ0N59LIZQ=; b=ZrCo+ubzvPlkFr8aPuJJpGu7PRHLaaVFeJaq4oYF9HEItugmvlkQuieNseYOjbZsPW8zRiB0hHODAAuZx1S4VuoN6Y2P9zjW8FYNWPS0PfJZFwf6wLDxGCdMLPnuyISj8cFFtqaogJig/iODlGTcX7WXfvF3IIrGOsFAZl14DB18Mac5SvxwcXFLMRiTUG9bcCC1c98UJKyKj6Gr8K9KjwolQpEAWhsoYfwHSBuZ/VwTS4g3u8IoMSnzVf/UueqBpGfv2i7iCyQdX/ZYbarS0YBTPERBfHNVof1JkuIO+gnotpdzL8d+8LDCt3J+ZD7nINy/+dHzFQ24f/9Pi4p1ZA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=PEr5v4/HVAs6sNUIpVFS6ON8WAePVTYCgWJ0N59LIZQ=; b=W14rKF+P6S4yuZLEVoqVqPnd8ppOnfG1vRyaTe36yYu1RTQfbbt0E1P1iKr3SnFUpmSn7WEjIost1fLmQLR/opoYyQ6IVjj3KhNZEns669Pdq1FE8j2/G4GFe0TP+i5Y3dXh5XMoaYnhLNYgFyLi+qtOqr6diavHKch6Y9sdUTrCYKT57n7rpPonv7w0mQ91nfM5+D09VPYVTPGXRkpr0lyavUOoO52ZJUQKEFtdTM4+tYG9hPRKe18Gr1QYoaUrlASaD/NNi0+jWYtTWMUtPrUklKNiIN8w9DLjXeZ9d8Z3MHxeIlc28LhvmqnE5JmVyH47q775EeGfVQ51toTgNw== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0191.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:8::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:50 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:50 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 14/21] avfilter/avfilter: Fix hardcoded input index Thread-Index: AQHX7g4tiZ2d9LmtJki1tqjVDMbQ2g== Date: Fri, 10 Dec 2021 21:37:49 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> <75bb9ceffef9e8e2d05ffce8a51c1991d5d3a973.1639172095.git.softworkz@hotmail.com> <554aab35996d92c90ab3b0ecb2c0c6c1278479d6.1639172095.git.softworkz@hotmail.com> <65992926f9356948d7f937ab852f8320b3da4bd9.1639172095.git.softworkz@hotmail.com> In-Reply-To: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [N+TT3NZv42Z6ao4TMtUhdC9ctr2P4EtR] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: b0ba1a75-bafd-4995-7b33-08d9bc255085 x-ms-traffictypediagnostic: DM8P223MB0191:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: caWhnt/ZxhBTtvmdUd76oD32ihF/YNBR3qOT8EDeWJM3BDRbbXrejEbQOo3FNXlWw1VBxOqCqoH1PpTefHFFZ3boOMPawYxcdfWvvnzDLYbYT3rK/YQ1d54mvFROJ0YdpE3Wx+TvnzCZxjkvEh8aL2TBGYajLaBwC9Y0meeISlYOYGKaJcWsFSXKzFI2QWOJMZGkYB1gI67rMoiy4anHqWpVOE3P1vkH8M6LNusrKVe1OR+tNaY4jJW+FjYfQ27SrDAvqcJP4p7kAo3d0VJND5IHE15edSSMXMd/vQM5YQ+GaKquT0/MbTqD5n9xf0ktKJDjhSgpCiV43JRDBzepogY4Sw6wjqzeR55GsM32s9H+Zw3ywuF/Olhpwin6XGcSU6bNO0B7Kyx9MCsTNihMNTA8o33/2MXCgc/JzOJBSITKVwBAcpgnLGDkFJLgkpKYjPuA3lqG5AhAScM25YGdRkCIaAR58bsly7IywJGBMfZfr+LdCjdGkWPVZiAGXahqB/fdxFJPaPOhn3h1Y1Rgwun+6EiN8pFh55o0ndWVDywUpfS4ppWe3sYGnf3lCdrFddICsWfwh2NbAwdi3uV4yQ== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?PkJHHwJPj2m3y2Yt3miO1Y8N73FP?= =?utf-8?q?RjX+nroq71TDFaCgtqxQSl/3z5lT7aIrmEwpvH7BqPjIzWWyAVTsB3weneUzCmgvQ?= =?utf-8?q?TjiVuL9IdCSL/hYHLkq9wDrjTDTxzEzKVv3a//iEL3ffYTEOJD33jveZMa/o0Ayl3?= =?utf-8?q?soPhgyN24Qeub6GGh3U6gPQtJhW3R64ED4a9Ly76yGU4/cXP8pQUnKABFszJAXy8Z?= =?utf-8?q?ZlWSygeNuA7x4Lwub+19Z4/sxUY4ZXaM8kLtQm9JKtSONVH2G064hUrFWEh1Jr2Ai?= =?utf-8?q?6p3muxy8t7conqpLDuI/fEdoTKmasNBGZxF5I91wq9crkwVuBUzg/wl4sRSklG/Gu?= =?utf-8?q?36w4ASeoSdRr/ZUlccfq22PtB9FLjqRZXkWEMHS9zl2RodqhEOX7eTwz8qWRFX9ZO?= =?utf-8?q?9YC2bQlczrTu/wfNR0kUFRPAjZ2RgVYl6VFS/nOt91POMixWj4fzyRJCP0JawAmRX?= =?utf-8?q?yaVP5AvdFhEFCE7E4yYNuYwj9O9gPvaH/dkYTAE0OVshwVvmc2DBg2ue0+jV2gFiF?= =?utf-8?q?E8AN3q2qxPq90cwPF8mRK4Tcjkre7z1I3nqBsxegFHU0FlJLHLChdfMYQQ51ejeCf?= =?utf-8?q?RoujoMGl8SZarJg/ZsdX0VL+jdXRm7A7MjBaj5ICUtuYc7IMaeDQdNymRIfXzi7Bj?= =?utf-8?q?OPwDcaOKo85kF0sN2XjOlmemC9YiIJqs78uq01884ple78Sa7YCI2Erh6mjYHfvE7?= =?utf-8?q?Moj3VPJbeDSVSl6c08+tuXihUt6GE27Ef0l0ah4gSIl9iueFf9Rt9r8rnpftblgBk?= =?utf-8?q?ByNBusAuH7YyFGkyOLbT5LcybHkuDXVA32Di0m6VVnI8mIFHSSEQttJHOHLIrBQs1?= =?utf-8?q?/UruuPO6VLDVjKpayPeTRtgOcPyzucehqrzzv0FWMqjidsL09AzDyIDsRxwDqzFm/?= =?utf-8?q?H69YDbqfDnKhivMGdM8NP9AhS0rsmv5OEwWo3NFap3tw=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: b0ba1a75-bafd-4995-7b33-08d9bc255085 X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:49.9516 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0191 Subject: [FFmpeg-devel] [PATCH v23 14/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: aufimgqOgMUh This fix targets (rare) cases where multiple input pads have a .filter_frame function. ff_request_frame_to_filter needs to call ff_request_frame with the correct input pad instead of the hardcoded first one. Signed-off-by: softworkz --- libavfilter/avfilter.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index 75d5e86539..aa9aa71f53 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -463,7 +463,7 @@ static int64_t guess_status_pts(AVFilterContext *ctx, int status, AVRational lin return AV_NOPTS_VALUE; } -static int ff_request_frame_to_filter(AVFilterLink *link) +static int ff_request_frame_to_filter(AVFilterLink *link, int input_index) { int ret = -1; @@ -472,8 +472,8 @@ static int ff_request_frame_to_filter(AVFilterLink *link) link->frame_blocked_in = 1; if (link->srcpad->request_frame) ret = link->srcpad->request_frame(link); - else if (link->src->inputs[0]) - ret = ff_request_frame(link->src->inputs[0]); + else if (link->src->inputs[input_index]) + ret = ff_request_frame(link->src->inputs[input_index]); if (ret < 0) { if (ret != AVERROR(EAGAIN) && ret != link->status_in) ff_avfilter_link_set_in_status(link, ret, guess_status_pts(link->src, ret, link->time_base)); @@ -1172,6 +1172,14 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in) { unsigned out = 0, progress = 0; int ret; + int input_index = 0; + + for (int i = 0; i < in->dst->nb_inputs; i++) { + if (&in->dst->input_pads[i] == in->dstpad) { + input_index = i; + break; + } + } av_assert0(!in->status_out); if (!filter->nb_outputs) { @@ -1181,7 +1189,7 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in) while (!in->status_out) { if (!filter->outputs[out]->status_in) { progress++; - ret = ff_request_frame_to_filter(filter->outputs[out]); + ret = ff_request_frame_to_filter(filter->outputs[out], input_index); if (ret < 0) return ret; } @@ -1218,7 +1226,7 @@ static int ff_filter_activate_default(AVFilterContext *filter) for (i = 0; i < filter->nb_outputs; i++) { if (filter->outputs[i]->frame_wanted_out && !filter->outputs[i]->frame_blocked_in) { - return ff_request_frame_to_filter(filter->outputs[i]); + return ff_request_frame_to_filter(filter->outputs[i], 0); } } return FFERROR_NOT_READY; From patchwork Fri Dec 10 21:37:51 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32273 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2374623iog; Fri, 10 Dec 2021 13:40:06 -0800 (PST) X-Google-Smtp-Source: ABdhPJxdnk2jmg3tA3oQZuO/5WHTC6UMVPipe9IoLq1zKESV8rouR/WYmeEdtV8IRClez91F3i5m X-Received: by 2002:a17:907:72c7:: with SMTP id du7mr27418940ejc.424.1639172406479; Fri, 10 Dec 2021 13:40:06 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id go42si6028217ejc.767.2021.12.10.13.40.06; Fri, 10 Dec 2021 13:40:06 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=Np5QqV2b; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 76F5068AFCD; Fri, 10 Dec 2021 23:37:55 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM12-MW2-obe.outbound.protection.outlook.com (mail-mw2nam12olkn2068.outbound.protection.outlook.com [40.92.23.68]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5B03468AF89 for ; Fri, 10 Dec 2021 23:37:53 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=ZkX5DmM65bTQiJT05jCo60n4qnBAO6L3y+2O5aIiZ61Za7Ca48A+bdpAocNvunQGSizQ+tdRqOsqsTy2iUp1lHs8h0dHMg2Z3k2SYMfvTqXzNFayKiVLvZUN8F2Neo2TDFONxVFdHD1FTEaIyRE9cyWvWMIwUP5kRC70T8dbTBO/FSqdUFRZg0HchvAimRNYhK3GARXFhLDCLvEmGSLOh3j2CzFcK9sEazD1RZFPAYs2gePDGiWFQmEwZKtbji+HK9gP398jyvALFCYl2MuqT9+luvIjhXljo58ildKsbyma4oXMIjs+z8ysuKIIJyXEWktiybPuZEiSdlxoEnLA/A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=tS71s26w0nI9bMeOzD+6wQIz4oOV1EZxI0MU3jgaSXE=; b=JtH56BKqNwZ3/9MILJ2+xEA/kJVK1ndq6DNVtm8qUMZr6Xx2lxILzD5Vyn8ETFQCvpLbrp9tnaarrkfsoj2gI5HSxdMyWUZwyFZu7Mt28C30fLJVr1Qt9uVSTD1Jbtf7Mos94Jb5twl84E5dJZEDvrTlPLnJ8ql65D+CaFS4I8IMwPX5xgdGLuoPTk7+/MHdMMX2H/Ly+/feh8vt6gitw+lZqGYOc8cEZiGCFy5zZyhEwdUwLMEm9wE11pCY2ZV2VhYNo0sC0wpYvsLAE9Vs4AXkhf/hRZXcymleJMpVJPO1dElAb2Gdl6t92mFb/QkTTVq2q4I8caDLUDN8mxEOuA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=tS71s26w0nI9bMeOzD+6wQIz4oOV1EZxI0MU3jgaSXE=; b=Np5QqV2bgpSIdq8XPu1z5edl88hrF2IA0Q1ISwegrIk9+p7pNmp1t3beOfn7L9k4zV9kRsC9Qp5SyVg7yg2P7+v6uHJ34ox+RC2ZvnmHpLeUYZ3negQSOFqArOeM5Wep1VKZ8/EtHGWus23jGAfWEkZYFMvLTbGV1dOrySjhGQYY1AGQ4PPejSymqVmzIvVTj+MXc//9MsxcuR9SDpnQ3WkUrAt9Ceb+xdtrsOP0iLtF9+OqEmaU0eaHX5cNjbNDWqPRYTB+Ry/cHjrGSVDYcbY2KPBik7+OR7RZ0BnckcaMrVVdJ58waIqVZxyLzFxUqH6anD3cwTiSdFM355m2+A== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0191.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:8::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:51 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:51 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 15/21] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters Thread-Index: AQHX7g4uaLYoobHjCU+fq2RTcgavQA== Date: Fri, 10 Dec 2021 21:37:51 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> <75bb9ceffef9e8e2d05ffce8a51c1991d5d3a973.1639172095.git.softworkz@hotmail.com> <554aab35996d92c90ab3b0ecb2c0c6c1278479d6.1639172095.git.softworkz@hotmail.com> <65992926f9356948d7f937ab852f8320b3da4bd9.1639172095.git.softworkz@hotmail.com> In-Reply-To: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [jjboF18btCYvM0C5n54q+Fie6HvSJgPB] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 7dcae76b-a127-4e37-8fb6-08d9bc255159 x-ms-traffictypediagnostic: DM8P223MB0191:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: HzxGvRkb5+gr1Onem9nLpS6eDah4h5KKIcPT/Ffu49RzNX3jq7T5yhRTXiiwLOfeJyJkwI1of4Eq5lOIdwTmhklF+ugzg+0qLmXLKvd6QHFTeZ86tdQJJi8wHhT2WH+f8lxzzbsrtT11jmzV6WMxEM0GAc+fwi9sHZqla8pN1AlojaUR3GJgUYdIrLJ8gZl8RIYTGaJ0fczapmLPBEE1SaAJEuxOznAtMZE3e6TabaEFfmAL+ZPytmstTlgUPNEpRF4JLOrffXWqMka5+lQZ8OnUBrUWSBKrZpSWIA9tkE/YpDY5i7HZPFZBVC9WiH6hHjczY8dH1ZjdFYFqLnWog2VUF/rFK4SYvgFTQ8Z1Hw7BlV0VI24Rg/C3FSRDnd5VZ35ZtGq1I/bbDoLVJcW57sfkWl3qAkWcUHgGC/rWCTTFodWm+ielUO9VYTe3Oa91jyrQooQDZKePNmF8lotGbJ/37DeUByRcBw59WulefmUJN1fh2yjoqrMUq69LVcuR59VQ8/YB1Hm3QKx+xfTuKCb/5NXxaVUxP3/YtnfPlXtbAa3QrQrksb47zDFhCFHC1MDWjQZoalp4WHJET1dg5igsM0j6ebfL6OUqGGmd5nMwiDnB/fFV+WDIr6dvCKevcm5+ZEibl/rJp0/nlIGsWwzAWi/LI4psSMPgc/QkjlI= x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?qXkd8DwowOZwVGOzjzG6PSlzhO3U?= =?utf-8?q?B8Tu5+9Xi4tANW+rZXU6YK0ODBjeskjZO0E+wlS8Z3gyxplSNusXXygFxwJ61Gbj/?= =?utf-8?q?fD1ZhoOFkTXEiMGywzhnuCpq22DzgpHYQbrWiK9/7qpZRpRg7jy8XqIbBx7r0ezXP?= =?utf-8?q?OI+qWVZJgbw3LGWUaW1hcm2msh0mN9ZN+bgAYegG8VvwkWPkuQjseSHweSuV+8o2V?= =?utf-8?q?CB6gCAv6bKQvIKiNBTrn9AG1D274yKHJyoVGJ4BxmAQRRYDD5wtOdIfcpY++n9LdF?= =?utf-8?q?RdheDkExTQljmdLRiv/txsXSb8AnS5FEB51/Dbq4EHCELaQxJyqRBzdhGUjdo7kIY?= =?utf-8?q?UQXY0GBc8c3Qk5PHRUi50M51fcYRDLQuC/FLYWt8zE7pM8V1PAP9kNFQE7fWz4RM1?= =?utf-8?q?qrv8ZwhpPyR5NsvZYl5KTZy4KK4Nxa1TwS4rP7hGGQ0FyUZCmdbfLp61mLXXpSmDs?= =?utf-8?q?Ridt1cqXtUNLuPNx0cW23uMrruKh1N3vHB3FZskrhdlVfNaUvTZb3gIeXEvpjWmFN?= =?utf-8?q?6XI+pENoPXX6VLuTEu7KUM1uriTcId52Ulzf+YArTmdtV0HPGpXLeaHAqOn3M+NZg?= =?utf-8?q?Q6k24higMD197tGcO2BKSrP0bdEz6G4kOiBY2t9XjdnsMYYFrx7QTNUn43yKbG8Nu?= =?utf-8?q?xapL4j1PHlwxI5om07eOwcnzSP8wyhVLV2R/9n7EamndqqqDaICr+2upgT9UlcWum?= =?utf-8?q?LhYGAakAozAtA7oEhyFFv6c09qJZMzv4cvLetx/EnxObMUifNarFXI9VGSSFNfe5J?= =?utf-8?q?lNnZPvfXzQA/6oWeU/iwwCEH8dX6WSiFOUu0X0GcL5HqXPUUuWqLbIGY6tyaURhsV?= =?utf-8?q?OVx1cwHiKlIw2EnlKL+S8l/M2fK+YND0d0vue6QxO+PV4Li0m6M4CxTdw0mmguDHg?= =?utf-8?q?aHh1rh3B8+96Oqqh1LhoE2+badrsq+2rEsw8AMTPeeFg=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 7dcae76b-a127-4e37-8fb6-08d9bc255159 X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:51.3405 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0191 Subject: [FFmpeg-devel] [PATCH v23 15/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 1ZBuD5GsgPSq - overlaytextsubs {VS -> V) Overlay text subtitles onto a video stream. - textsubs2video {S -> V) Converts text subtitles to video frames Signed-off-by: softworkz --- configure | 2 + doc/filters.texi | 113 ++++++ libavfilter/Makefile | 2 + libavfilter/allfilters.c | 4 +- libavfilter/vf_overlaytextsubs.c | 646 +++++++++++++++++++++++++++++++ 5 files changed, 766 insertions(+), 1 deletion(-) create mode 100644 libavfilter/vf_overlaytextsubs.c diff --git a/configure b/configure index 7b203d2bf2..16ab6b46c3 100755 --- a/configure +++ b/configure @@ -3666,6 +3666,7 @@ overlay_opencl_filter_deps="opencl" overlay_qsv_filter_deps="libmfx" overlay_qsv_filter_select="qsvvpp" overlay_vulkan_filter_deps="vulkan spirv_compiler" +overlaytextsubs_filter_deps="avcodec libass" owdenoise_filter_deps="gpl" pad_opencl_filter_deps="opencl" pan_filter_deps="swresample" @@ -3710,6 +3711,7 @@ superequalizer_filter_deps="avcodec" superequalizer_filter_select="rdft" surround_filter_deps="avcodec" surround_filter_select="rdft" +textsub2video_filter_deps="avcodec libass" tinterlace_filter_deps="gpl" tinterlace_merge_test_deps="tinterlace_filter" tinterlace_pad_test_deps="tinterlace_filter" diff --git a/doc/filters.texi b/doc/filters.texi index db4552ba10..7041f9031e 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25752,6 +25752,119 @@ Overlay PGS subtitles ffmpeg -i "https://streams.videolan.org/samples/sub/PGS/Girl_With_The_Dragon_Tattoo_2%3A23%3A56.mkv" -filter_complex "[0:0][0:1]overlaygraphicsubs" output.mp4 @end example @end itemize + +@section overlaytextsubs + +Overlay text subtitles onto a video stream. + +This filter supersedes the classic @ref{subtitles} filter opposed to which it does no longer require to open and access the source stream separately, which is often causing problems or doesn't even work for non-local or slow sources. + +Inputs: +@itemize +@item 0: Video [YUV420P, YUV422P, YUV444P, ARGB, RGBA, ABGR, BGRA, RGB24, BGR24] +@item 1: Subtitles [TEXT] +@end itemize + +Outputs: +@itemize +@item 0: Video (same as input) +@end itemize + +It accepts the following parameters: + +@table @option + +@item alpha +Process alpha channel, by default alpha channel is untouched. + +@item fonts_dir +Set a directory path containing fonts that can be used by the filter. +These fonts will be used in addition to whatever the font provider uses. + +@item default_font_path +Path to a font file to be used as the default font. + +@item font_size +Set the default font size. + +@item fontconfig_file +Path to ASS fontconfig configuration file. + +@item force_style +Override default style or script info parameters of the subtitles. It accepts a +string containing ASS style format @code{KEY=VALUE} couples separated by ",". + +@item margin +Set the rendering margin in pixels. + +@item render_latest_only +For rendering, alway use the latest event only, which is covering the given point in time +@end table + +@subsection Examples + +@itemize +@item +Overlay ASS subtitles with animations: +@example +ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:v]overlaytextsubs" -map 0 -y out.mkv +@end example +@end itemize + +@section textsub2video + +Converts text subtitles to video frames. + +For overlaying text subtitles onto video frames it is recommended to use the overlay_textsubs filter. +The textsub2video is useful for for creating transparent text-frames when overlay is done via hw acceleration + +Inputs: +@itemize +@item 0: Subtitles [TEXT] +@end itemize + +Outputs: +@itemize +@item 0: Video [RGB32] +@end itemize + +It accepts the following parameters: + +@table @option + +@item rate, r +Set the framerate for updating overlay frames. +Normally, overlay frames will only be updated each time when the subtitles to display are changing. +In cases where subtitles include advanced features (like animation), this parameter determines the frequency by which the overlay frames should be updated. + +@item size, s +Set the output frame size. +Allows to override the size of output video frames. + +@item fonts_dir +Set a directory path containing fonts that can be used by the filter. +These fonts will be used in addition to whatever the font provider uses. + +@item default_font_path +Path to a font file to be used as the default font. + +@item font_size +Set the default font size. + +@item fontconfig_file +Path to ASS fontconfig configuration file. + +@item force_style +Override default style or script info parameters of the subtitles. It accepts a +string containing ASS style format @code{KEY=VALUE} couples separated by ",". + +@item margin +Set the rendering margin in pixels. + +@item render_latest_only +For rendering, alway use the latest event only, which is covering the given point in time. +@end table + @c man end SUBTITLE FILTERS @chapter Multimedia Filters diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 525b3a6e3c..a49369e7f5 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -377,6 +377,7 @@ OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER) += vf_overlay_opencl.o opencl.o \ OBJS-$(CONFIG_OVERLAY_QSV_FILTER) += vf_overlay_qsv.o framesync.o OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER) += vf_overlay_vulkan.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_OVERLAYGRAPHICSUBS_FILTER) += vf_overlaygraphicsubs.o framesync.o +OBJS-$(CONFIG_OVERLAYTEXTSUBS_FILTER) += vf_overlaytextsubs.o OBJS-$(CONFIG_OWDENOISE_FILTER) += vf_owdenoise.o OBJS-$(CONFIG_PAD_FILTER) += vf_pad.o OBJS-$(CONFIG_PAD_OPENCL_FILTER) += vf_pad_opencl.o opencl.o opencl/pad.o @@ -466,6 +467,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 1bd3060ba3..dca38cd57b 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -357,9 +357,10 @@ extern const AVFilter ff_vf_oscilloscope; extern const AVFilter ff_vf_overlay; extern const AVFilter ff_vf_overlay_opencl; extern const AVFilter ff_vf_overlay_qsv; -extern const AVFilter ff_vf_overlaygraphicsubs; extern const AVFilter ff_vf_overlay_vulkan; extern const AVFilter ff_vf_overlay_cuda; +extern const AVFilter ff_vf_overlaygraphicsubs; +extern const AVFilter ff_vf_overlaytextsubs; extern const AVFilter ff_vf_owdenoise; extern const AVFilter ff_vf_pad; extern const AVFilter ff_vf_pad_opencl; @@ -544,6 +545,7 @@ extern const AVFilter ff_avf_showwaves; extern const AVFilter ff_avf_showwavespic; extern const AVFilter ff_vaf_spectrumsynth; extern const AVFilter ff_svf_graphicsub2video; +extern const AVFilter ff_svf_textsub2video; /* multimedia sources */ extern const AVFilter ff_avsrc_amovie; diff --git a/libavfilter/vf_overlaytextsubs.c b/libavfilter/vf_overlaytextsubs.c new file mode 100644 index 0000000000..9130dfa9de --- /dev/null +++ b/libavfilter/vf_overlaytextsubs.c @@ -0,0 +1,646 @@ +/* + * Copyright (c) 2021 softworkz + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * overlay text subtitles on top of a video frame + */ + +#include +#include "libavutil/ass_internal.h" +#include "libavutil/thread.h" + +#include "drawutils.h" +#include "filters.h" + +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" + +typedef struct TextSubsContext { + const AVClass *class; + AVMutex mutex; + int is_mutex_initialized; + + ASS_Library *library; + ASS_Renderer *renderer; + ASS_Track *track; + + char *default_font_path; + char *fonts_dir; + char *fc_file; + double font_size; + char *force_style; + char *language; + int margin; + int render_latest_only; + + int alpha; + FFDrawContext draw; + + int got_header; + int out_w, out_h; + AVRational frame_rate; + AVFrame *last_frame; + int need_frame; + int eof; +} TextSubsContext; + +/* libass supports a log level ranging from 0 to 7 */ +static const int ass_libavfilter_log_level_map[] = { + AV_LOG_QUIET, /* 0 */ + AV_LOG_PANIC, /* 1 */ + AV_LOG_FATAL, /* 2 */ + AV_LOG_ERROR, /* 3 */ + AV_LOG_WARNING, /* 4 */ + AV_LOG_INFO, /* 5 */ + AV_LOG_VERBOSE, /* 6 */ + AV_LOG_DEBUG, /* 7 */ +}; + +static void ass_log(int ass_level, const char *fmt, va_list args, void *ctx) +{ + const int ass_level_clip = av_clip(ass_level, 0, FF_ARRAY_ELEMS(ass_libavfilter_log_level_map) - 1); + const int level = ass_libavfilter_log_level_map[ass_level_clip]; + + av_vlog(ctx, level, fmt, args); + av_log(ctx, level, "\n"); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + TextSubsContext *s = ctx->priv; + + if (s->track) + ass_free_track(s->track); + if (s->renderer) + ass_renderer_done(s->renderer); + if (s->library) + ass_library_done(s->library); + + s->track = NULL; + s->renderer = NULL; + s->library = NULL; + + if (s->is_mutex_initialized) { + ff_mutex_destroy(&s->mutex); + s->is_mutex_initialized = 0; + } + + av_frame_free(&s->last_frame); +} + +static int overlay_textsubs_query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats; + AVFilterLink *inlink0 = ctx->inputs[0]; + AVFilterLink *inlink1 = ctx->inputs[1]; + AVFilterLink *outlink = ctx->outputs[0]; + static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE }; + int ret; + + /* set input0 video formats */ + formats = ff_draw_supported_pixel_formats(0); + if ((ret = ff_formats_ref(formats, &inlink0->outcfg.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; + + /* set output0 video formats */ + formats = ff_draw_supported_pixel_formats(0); + if ((ret = ff_formats_ref(formats, &outlink->incfg.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; + + 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); + + 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 *sub) +{ + AVFilterContext *ctx = inlink->dst; + TextSubsContext *s = ctx->priv; + const int64_t start_time = av_rescale_q(sub->subtitle_pts, AV_TIME_BASE_Q, av_make_q(1, 1000)); + const int64_t duration = sub->subtitle_end_time; + + // Postpone header processing until we receive a frame with content + if (!s->got_header && sub->num_subtitle_areas > 0) + process_header(ctx, sub); + + for (unsigned i = 0; i < sub->num_subtitle_areas; i++) { + char *ass_line = sub->subtitle_areas[i]->ass; + if (!ass_line) + break; + + ff_mutex_lock(&s->mutex); + ass_process_chunk(s->track, ass_line, strlen(ass_line), start_time, duration); + + if (s->render_latest_only && s->track->n_events > 1) { + const int64_t diff = s->track->events[s->track->n_events - 1].Start + - s->track->events[s->track->n_events - 2].Start; + if (s->track->events[s->track->n_events - 2].Duration > diff) + s->track->events[s->track->n_events - 2].Duration = diff; + } + + ff_mutex_unlock(&s->mutex); + } + + av_frame_free(&sub); + 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_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 *sub) +{ + AVFilterContext *ctx = inlink->dst; + TextSubsContext *s = ctx->priv; + const int64_t start_time = av_rescale_q(sub->subtitle_pts, AV_TIME_BASE_Q, av_make_q(1, 1000)); + const int64_t duration = sub->subtitle_end_time; + + av_log(ctx, AV_LOG_VERBOSE, "textsub2video_filter_frame num_subtitle_rects: %d, start_time_ms: %"PRId64"\n", sub->num_subtitle_areas, start_time); + + if (!s->got_header && sub->num_subtitle_areas > 0) + process_header(ctx, sub); + + for (unsigned i = 0; i < sub->num_subtitle_areas; i++) { + char *ass_line = sub->subtitle_areas[i]->ass; + if (!ass_line) + break; + + ff_mutex_lock(&s->mutex); + ass_process_chunk(s->track, ass_line, strlen(ass_line), start_time, duration); + + if (s->render_latest_only && s->track->n_events > 1) { + const int64_t diff = s->track->events[s->track->n_events - 1].Start + - s->track->events[s->track->n_events - 2].Start; + if (s->track->events[s->track->n_events - 2].Duration > diff) + s->track->events[s->track->n_events - 2].Duration = diff; + } + + ff_mutex_unlock(&s->mutex); + } + + av_frame_free(&sub); + + if (s->need_frame) { + s->need_frame = 0; + return textsub2video_request_frame(ctx->outputs[0]); + } + + return 0; +} + +#define OFFSET(x) offsetof(TextSubsContext, x) +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM) + +static const AVOption overlaytextsubs_options[] = { + {"alpha", "enable processing of alpha channel", OFFSET(alpha), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, .flags = FLAGS}, + {"font_size", "default font size", OFFSET(font_size), AV_OPT_TYPE_DOUBLE, {.dbl = 18.0}, 0.0, 100.0, .flags = FLAGS}, + {"force_style", "force subtitle style", OFFSET(force_style), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, .flags = FLAGS}, + {"margin", "default margin", OFFSET(margin), AV_OPT_TYPE_INT, {.i64 = 20 }, 0, INT_MAX, .flags = FLAGS}, + {"default_font_path", "path to default font", OFFSET(default_font_path), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"fonts_dir", "directory to scan for fonts", OFFSET(fonts_dir), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"fontsdir", "directory to scan for fonts", OFFSET(fonts_dir), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"fontconfig_file", "fontconfig file to load", OFFSET(fc_file), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"language", "default language", OFFSET(language), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"render_latest_only", "newest sub event for each time", OFFSET(render_latest_only), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, .flags = FLAGS}, + { .name = NULL } +}; + +static const AVOption textsub2video_options[] = { + {"rate", "set frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="8"}, 0, INT_MAX, .flags = FLAGS}, + {"r", "set frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="8"}, 0, INT_MAX, .flags = FLAGS}, + {"size", "set video size", OFFSET(out_w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS}, + {"s", "set video size", OFFSET(out_w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS}, + {"font_size", "default font size", OFFSET(font_size), AV_OPT_TYPE_DOUBLE, {.dbl = 18.0}, 0.0, 100.0, .flags = FLAGS}, + {"force_style", "force subtitle style", OFFSET(force_style), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, .flags = FLAGS}, + {"margin", "default margin", OFFSET(margin), AV_OPT_TYPE_INT, {.i64 = 20 }, 0, INT_MAX, .flags = FLAGS}, + {"default_font_path", "path to default font", OFFSET(default_font_path), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"fonts_dir", "directory to scan for fonts", OFFSET(fonts_dir), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"fontsdir", "directory to scan for fonts", OFFSET(fonts_dir), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"fontconfig_file", "fontconfig file to load", OFFSET(fc_file), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"language", "default language", OFFSET(language), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, .flags = FLAGS}, + {"render_latest_only", "newest sub event for each time", OFFSET(render_latest_only), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, .flags = FLAGS}, + { .name = NULL } +}; + +#if CONFIG_OVERLAYTEXTSUBS_FILTER + +AVFILTER_DEFINE_CLASS(overlaytextsubs); + +static const AVFilterPad overlaytextsubs_inputs[] = { + { + .name = "main", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input_main, + .flags = AVFILTERPAD_FLAG_NEEDS_WRITABLE, + .filter_frame = filter_video_frame, + }, + { + .name = "overlay", + .type = AVMEDIA_TYPE_SUBTITLE, + .filter_frame = filter_subtitle_frame, + }, +}; + +static const AVFilterPad overlaytextsubs_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, +}; + +const AVFilter ff_vf_overlaytextsubs = { + .name = "overlaytextsubs", + .description = NULL_IF_CONFIG_SMALL("Overlay textual subtitles on top of the input."), + .init = init, + .uninit = uninit, + .priv_size = sizeof(TextSubsContext), + .priv_class = &overlaytextsubs_class, + FILTER_INPUTS(overlaytextsubs_inputs), + FILTER_OUTPUTS(overlaytextsubs_outputs), + FILTER_QUERY_FUNC(overlay_textsubs_query_formats), +}; +#endif + +#if CONFIG_TEXTSUB2VIDEO_FILTER + +AVFILTER_DEFINE_CLASS(textsub2video); + +static const AVFilterPad textsub2video_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .config_props = textsub2video_config_input, + .filter_frame = textsub2video_filter_frame, + }, +}; + +static const AVFilterPad textsub2video_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = textsub2video_config_output, + .request_frame = textsub2video_request_frame, + }, +}; + +const AVFilter ff_svf_textsub2video = { + .name = "textsub2video", + .description = NULL_IF_CONFIG_SMALL("Convert textual subtitles to video frames"), + .init = init, + .uninit = uninit, + .priv_size = sizeof(TextSubsContext), + .priv_class = &textsub2video_class, + FILTER_INPUTS(textsub2video_inputs), + FILTER_OUTPUTS(textsub2video_outputs), + FILTER_QUERY_FUNC(textsub2video_query_formats), +}; +#endif From patchwork Fri Dec 10 21:37:53 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32274 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2374802iog; Fri, 10 Dec 2021 13:40:19 -0800 (PST) X-Google-Smtp-Source: ABdhPJzhXMfKIkQyDF4fDjS9E4R8oLs5wLsjsCpIpO/x39g6mRk2XWgXhMsx6vrNq5J1Vd8xH+zL X-Received: by 2002:a50:c34d:: with SMTP id q13mr42120542edb.65.1639172419648; Fri, 10 Dec 2021 13:40:19 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id go43si6092684ejc.141.2021.12.10.13.40.19; Fri, 10 Dec 2021 13:40:19 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=hxGPp35d; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 8CB3568AF34; Fri, 10 Dec 2021 23:38:03 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM12-MW2-obe.outbound.protection.outlook.com (mail-mw2nam12olkn2073.outbound.protection.outlook.com [40.92.23.73]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 0267E6809C4 for ; Fri, 10 Dec 2021 23:37:56 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=Cr3UqembL5Vajg9uNs7CQqWgRb7HwNpmxjziSSVsQr5QdcVCFiDj/lSS/f5cVpA2Vde9bl43tC9j0MS8fOE8Zjt7wH1lBUjAB1bsYAZt44EhtUiBzobTIs3RsiqqGGGDClSA51CP4ha8llniKrCPZ2hQUXWLvIyFUffCV/xF+7CHC9jzCD5Soh8YQPC2ktTWfXQArsxS5PMKPLs9O2MHKp6T102rXJGaFzWakX5aM+tsS4pH4f8CGIzwMN1WxThHlCp5UPpZI/PbmWeDFcYj0PZ7Ii0G9jT7TmZa6AJbGpVINNQHAXdNbiopFdzALIRHChP0Yz/u2oUIJP25Snhlwg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=gOLWAObn69s8KidAB3jpSFd91VZHXCFEcrruGC0at9c=; b=McuObZoV5bujF3SKePIxBjvnAL643NuONZqDDZAIjvUSgKdWTECK0QlxX7oyVK4hiB3tkVOl83c39z6tUKGE+xM3FxLgamWmei+SlFlUK9dU/1Jt3um46fTE5Vz3WSrkeW45hePLvXBFvX7HfJcLXOjcHjP8ydcwU87RihbZ5NGmCV3em96Zghn36jK8SStBJWQ78KNqGICsuEtqS3kTspu/Y+RfyYfqFJtqqZ/2aGz2EUoChyEfXMqiDZfmRov42Op1Zhmip5YvIPHOmaW/ohQaCdYna+pBIe1u4FEufgT3b5crKbqH+DFuM4b5OJkFmHgSig+zE8ZTV+v5q7UnPw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=gOLWAObn69s8KidAB3jpSFd91VZHXCFEcrruGC0at9c=; b=hxGPp35dveiUX1Mx7k6n2C+DCIPhOg15Y/02lHVMDr/RNpMSd+JTFVhWNsxDB8aABX6wSAV5GUQtzEcYOb5wwVeDJvy11L88AD+rUnfUgeRy2iDeRL/KTUn0f5v2anUDnvqnf/OIDTmN/0dojeUdjJv767Ry8lcP3kduns6g55P/ezQo+dPsbOpmwMk3B0l2QUTqYlB7Dy+aSKTQvQZV8VaGEYmBXm2A+GJn0H3WRwaNIRBRso80VqJ9Y8vEB0GVEf0/2GpDPw+u3jmP9zsh4nUpb2SNq6HJs5n4ZrF50h0pLL++EdCaKAhB6RfqxQibCejkhpCPqooDY//DPHYXrQ== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0191.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:8::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:53 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:53 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 16/21] avfilter/textmod: Add textmod, censor and show_speaker filters Thread-Index: AQHX7g4wl7xULOJej0SepU3ILLrp5w== Date: Fri, 10 Dec 2021 21:37:53 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> <75bb9ceffef9e8e2d05ffce8a51c1991d5d3a973.1639172095.git.softworkz@hotmail.com> <554aab35996d92c90ab3b0ecb2c0c6c1278479d6.1639172095.git.softworkz@hotmail.com> <65992926f9356948d7f937ab852f8320b3da4bd9.1639172095.git.softworkz@hotmail.com> In-Reply-To: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [M7yLtIEgIvpvW4FeIsEZAYfrVn5cyB2f] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: e06a4500-e6da-4020-44c1-08d9bc2552be x-ms-traffictypediagnostic: DM8P223MB0191:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: wz22zxLcHN6WEiePy7OXmmp7ePQqdZoUX5CqSLru/hFS9Im0NefzQJCaWRVH+tQL2Vg+zuVTR5N0ycZPeEJGKvlyFMcyy9C+y7uqnU/Mmm4ojXxSBapA8gloSw9EYqUjYfWBQJWyKhIsLSLq3Ly+0cOO3O7FCh57oIamFhTDIzNwHqmN1S+kbb0ZYNz7t3OSmRtZ24kxvbwWz/uxdQb1Xf0lEfwM9B8gheJBevwOkE4pgY01/N6miaIGM3/bIbowHlxb3N48fUD+ogSLp94XfK4uij0btg4hSbJAGcpwGZB6a3RnnseEFAz2Nbhd/05Wk2fRWuyHbVJpiwb9MFqaTmtuJ6eKk4Sw4rvidqrUGWQ4jd+4dBOS9UjGHbih6ld5v3wvem7jpafH5Q28o5LTwXgATpY3bv05TCI9+4EHIbC19Qy9aN9De7XBUIfq0xQkNZcoP/ue5cZHfePMPXrjXi4D8J85aJzHJ1AJdpYdA2ZVqBY1ata9EyHQc29JFiJVwpvAaTMcfcoZEqJYDQLm/cj9gOP0kJ8H72PMtbRKlojSaSQIzR37kFoavP2bE8Bph3N1+aH+MtkBFgd6wvJCJrUju8p3kgMhU8uVGkavR4e6nayEA71v/i3L9taqVG/LEPSrcgy7BXxcSbhYmhcaduqazmkWwQxSwdeL/HWbSPE= x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?kSOabH2MqNq8DxfYs3XK8Rvjcm+z?= =?utf-8?q?II8pmP0BF0mxuw7yGw5lWn5CLI0fXA1Liq1M3neOXoKp40/Y0JOtpDUtN2l/CFY9T?= =?utf-8?q?j/wFHkab/rlDtog7evckCn2hZ6mAxsDZAhTQjxm85BdgzFIH1yWVek6Ooe09mANRT?= =?utf-8?q?fHO+rM/urqtqZiyqTtREVTV/YIj3stq0B2cxQ68znQmXcTFPERNXgJdHNLtVj07pW?= =?utf-8?q?ZXQCSd6m1zmbHTfO3tf6PyqahSdw0Z9nyz7WxK3cnWV+VMrvM+OyrdXLoibgOwhL3?= =?utf-8?q?7Eff8ZNL2ASTa9dCXzch82L4h43Q9lmX3JES+h8Nvl1KcoVdYCT6zjix9pyz9IFPD?= =?utf-8?q?PVoahZUSkHKCnR1TPah2NXN2HJk3x7RYcweQqKuateRPsIiM6AwLwTny5UQcprdp1?= =?utf-8?q?W/aBjnzV8KSIh7d0EfSJ9Y7+flOp5c+xAoqFLbswtHDfvWKCYLn/K5XMB0ZNRkhvp?= =?utf-8?q?7CLt4bvhCHHjJUP/MF+CTMNDPuaJ27dXIMC4cIJ6mUOiscJ0RpUn6UWp7OD5ChWIR?= =?utf-8?q?1euOMxeuhKATXngCKQVhkG8H/UPtvza5DBN76PsmUnOyT8WEHsmzwNgGciNjRQBA0?= =?utf-8?q?OYlfU8G6lJPexVRoxKf/FTCHty6VGT3pvSIBckU++dybi4vlz7Z92GZhFVUrCZvIg?= =?utf-8?q?XsBnW02eMuRG+2BM8HRQ4jB2m6vltVjTnpVlBYxkX3cFpraefrvxTEV6B9MHGWdj8?= =?utf-8?q?pch2v44A3TkoCSUy0/OwvnOGLdyFoArmb7C6YbyyL2hHDxXffZHApXu1OjErmoTCj?= =?utf-8?q?gb7zDbMs2IBoNT2agUhpjjqTKsVDCc9d3XpBKwaknJQIHURQaZehJ47DwKz8Y30Mp?= =?utf-8?q?r+mpMr1mkqq5JXfegbyY3Nm87/dPg6HOBZkurfMs/yRUe+MAZ/HSEbtVseOLnhVhC?= =?utf-8?q?y+nRVp1SqiIdU36HolO2gpCj+Kea+912hvH5souemnrQ=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: e06a4500-e6da-4020-44c1-08d9bc2552be X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:53.6324 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0191 Subject: [FFmpeg-devel] [PATCH v23 16/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: c9ofs6Ca0pT0 - textmod {S -> S) Modify subtitle text in a number of ways - censor {S -> S) Censor subtitles using a word list - show_speaker {S -> S) Prepend speaker names from ASS subtitles to the visible text lines Signed-off-by: softworkz --- doc/filters.texi | 206 ++++++++++++ libavfilter/Makefile | 5 + libavfilter/allfilters.c | 3 + libavfilter/sf_textmod.c | 697 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 911 insertions(+) create mode 100644 libavfilter/sf_textmod.c diff --git a/doc/filters.texi b/doc/filters.texi index 7041f9031e..e87d3993df 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25644,6 +25644,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. @@ -25811,6 +25950,73 @@ ffmpeg -i "http://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex. @end example @end itemize +@section showspeaker + +Prepend speaker names to subtitle lines (when available). + +Subtitles in ASS/SSA format are often including the names of the persons +or character for each subtitle line. The showspeaker filter adds those names +to the actual subtitle text to make it visible on playback. + +Inputs: +@itemize +@item 0: Subtitles[TEXT] +@end itemize + +Outputs: +@itemize +@item 0: Subtitles[TEXT] +@end itemize + +It accepts the following parameters: + +@table @option +@item format +The format for prepending speaker names. Default is 'square_brackets'. + +Supported operation modes are: + +@table @var +@item 0, square_brackets +Enclose the speaker name in square brackets, followed by space ('[speaker] text'). +@item 1, round_brackets +Enclose the speaker name in round brackets, followed by space ('(speaker) text'). +@item 2, colon +Separate the speaker name with a colon and space ('speaker: text'). +@item 3, plain +Separate the speaker name with a space only ('speaker text'). +@end table + +@item line_break +Set thís parameter to insert a line break between speaker name and text instead of the space character. + +@item style +Allows to set a specific style for the speaker name text. + +This can be either a named style that exists in the ass subtitle header script (e.g. 'Default') or an ass style override code. +Example: @{\\c&HDD0000&\\be1\\i1\\bord10@} +This sets the color to blue, enables edge blurring, italic font and a border of size 10. + +The behavior is as follows: + +- When the style parameter is not provided, the filter will find the first position in the event string that is actual text. + The speaker name will be inserted at this position. This allows to have the speaker name shown in the same style like the + regular text, in case the string would start with a sequence of style codes. +- When the style parameter is provided, everything will be prepended to the original text: + Style Code or Style name >> Speaker Name >> Style Reset Code >> Original Text + +@end table + +@subsection Examples + +@itemize +@item +Prepend speaker names with blue text, smooth edges and blend/overlay that onto the video. +@example +ffmpeg -i INPUT -filter_complex "showspeaker=format=colon:style='@{\\c&HDD0000&\\be1@}',[0:v]overlay_textsubs" +@end example +@end itemize + @section textsub2video Converts text subtitles to video frames. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index a49369e7f5..d2995fbec6 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -553,6 +553,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 dca38cd57b..673f9fb839 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -544,6 +544,9 @@ extern const AVFilter ff_avf_showvolume; extern const AVFilter ff_avf_showwaves; extern const AVFilter ff_avf_showwavespic; extern const AVFilter ff_vaf_spectrumsynth; +extern const AVFilter ff_sf_censor; +extern const AVFilter ff_sf_showspeaker; +extern const AVFilter ff_sf_textmod; extern const AVFilter ff_svf_graphicsub2video; extern const AVFilter ff_svf_textsub2video; diff --git a/libavfilter/sf_textmod.c b/libavfilter/sf_textmod.c new file mode 100644 index 0000000000..d73f329b87 --- /dev/null +++ b/libavfilter/sf_textmod.c @@ -0,0 +1,697 @@ +/* + * Copyright (c) 2021 softworkz + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * text subtitle filter which allows to modify subtitle text in several ways + */ + +#include + +#include "libavutil/opt.h" +#include "internal.h" +#include "libavutil/ass_split_internal.h" +#include "libavutil/bprint.h" +#include "libavutil/file.h" + +static const char* leet_src = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +static const char* leet_dst = "abcd3f6#1jklmn0pq257uvwxyzAB(D3F6#1JKLMN0PQ257UVWXYZ"; + +enum TextModFilterType { + TM_TEXTMOD, + TM_CENSOR, + TM_SHOW_SPEAKER, +}; + +enum TextModOperation { + OP_LEET, + OP_TO_UPPER, + OP_TO_LOWER, + OP_REPLACE_CHARS, + OP_REMOVE_CHARS, + OP_REPLACE_WORDS, + OP_REMOVE_WORDS, + NB_OPS, +}; + +enum CensorMode { + CM_KEEP_FIRST_LAST, + CM_KEEP_FIRST, + CM_ALL, +}; + +enum ShowSpeakerMode { + SM_SQUARE_BRACKETS, + SM_ROUND_BRACKETS, + SM_COLON, + SM_PLAIN, +}; + +typedef struct TextModContext { + const AVClass *class; + enum AVSubtitleType format; + enum TextModFilterType filter_type; + enum TextModOperation operation; + enum CensorMode censor_mode; + enum ShowSpeakerMode speaker_mode; + char *find; + char *find_file; + char *style; + char *replace; + char *replace_file; + char *separator; + char *censor_char; + char **find_list; + int line_break; + int nb_find_list; + char **replace_list; + int nb_replace_list; +} TextModContext; + +static char **split_string(char *source, int *nb_elems, const char *delim) +{ + char **list = NULL; + char *temp = NULL; + char *ptr = av_strtok(source, delim, &temp); + + while (ptr) { + if (strlen(ptr)) { + av_dynarray_add(&list, nb_elems, ptr); + if (!list) + return NULL; + } + + ptr = av_strtok(NULL, delim, &temp); + } + + if (!list) + return NULL; + + for (int i = 0; i < *nb_elems; i++) { + list[i] = av_strdup(list[i]); + if (!list[i]) { + for (int n = 0; n < i; n++) + av_free(list[n]); + av_free(list); + return NULL; + } + } + + return list; +} + +static int load_text_from_file(AVFilterContext *ctx, const char *file_name, char **text, char separator) +{ + int err; + uint8_t *textbuf; + char *tmp; + size_t textbuf_size; + int offset = 0; + + if ((err = av_file_map(file_name, &textbuf, &textbuf_size, 0, ctx)) < 0) { + av_log(ctx, AV_LOG_ERROR, "The text file '%s' could not be read or is empty\n", file_name); + return err; + } + + if (textbuf_size > 1 && + (textbuf[0] == 0xFF && textbuf[1] == 0xFE + || textbuf[0] == 0xFE && textbuf[1] == 0xFF)) { + av_log(ctx, AV_LOG_ERROR, "UTF text files are not supported. File: %s\n", file_name); + return AVERROR(EINVAL); + } + + if (textbuf_size > 2 && textbuf[0] == 0xEF && textbuf[1] == 0xBB && textbuf[2] == 0xBF) + offset = 3; // UTF-8 + + if (textbuf_size > SIZE_MAX - 1 || !((tmp = av_strndup((char *)textbuf + offset, textbuf_size - offset)))) { + av_file_unmap(textbuf, textbuf_size); + return AVERROR(ENOMEM); + } + + av_file_unmap(textbuf, textbuf_size); + + for (size_t i = 0; i < strlen(tmp); i++) { + switch (tmp[i]) { + case '\n': + case '\r': + case '\f': + case '\v': + tmp[i] = separator; + } + } + + *text = tmp; + + return 0; +} + +static int load_files(AVFilterContext *ctx) +{ + TextModContext *s = ctx->priv; + int ret; + + if (!s->separator || strlen(s->separator) != 1) { + av_log(ctx, AV_LOG_ERROR, "A single character needs to be specified for the separator parameter.\n"); + return AVERROR(EINVAL); + } + + if (s->find_file && strlen(s->find_file)) { + ret = load_text_from_file(ctx, s->find_file, &s->find, s->separator[0]); + if (ret < 0 ) + return ret; + } + + if (s->replace_file && strlen(s->replace_file)) { + ret = load_text_from_file(ctx, s->replace_file, &s->replace, s->separator[0]); + if (ret < 0 ) + return ret; + } + + return 0; +} + +static int init_censor(AVFilterContext *ctx) +{ + TextModContext *s = ctx->priv; + int ret; + + s->filter_type = TM_CENSOR; + s->operation = OP_REPLACE_WORDS; + + ret = load_files(ctx); + if (ret < 0 ) + return ret; + + if (!s->find || !strlen(s->find)) { + av_log(ctx, AV_LOG_ERROR, "Either the 'words' or the 'words_file' parameter needs to be specified\n"); + return AVERROR(EINVAL); + } + + if (!s->censor_char || strlen(s->censor_char) != 1) { + av_log(ctx, AV_LOG_ERROR, "A single character needs to be specified for the censor_char parameter\n"); + return AVERROR(EINVAL); + } + + s->find_list = split_string(s->find, &s->nb_find_list, s->separator); + if (!s->find_list) + return AVERROR(ENOMEM); + + s->replace_list = av_calloc(s->nb_find_list, sizeof(char *)); + if (!s->replace_list) + return AVERROR(ENOMEM); + + for (int i = 0; i < s->nb_find_list; i++) { + size_t len, start = 0, end; + char *item = av_strdup(s->find_list[i]); + if (!item) + return AVERROR(ENOMEM); + + len = end = strlen(item); + + switch (s->censor_mode) { + case CM_KEEP_FIRST_LAST: + + if (len > 2) + start = 1; + if (len > 3) + end--; + + break; + case CM_KEEP_FIRST: + + if (len > 2) + start = 1; + + break; + } + + for (size_t n = start; n < end; n++) + item[n] = s->censor_char[0]; + + s->replace_list[i] = item; + } + + return 0; +} + +static int init_showspeaker(AVFilterContext *ctx) +{ + TextModContext *s = ctx->priv; + s->filter_type = TM_SHOW_SPEAKER; + + return 0; +} + +static int init(AVFilterContext *ctx) +{ + TextModContext *s = ctx->priv; + int ret; + + ret = load_files(ctx); + if (ret < 0 ) + return ret; + + switch (s->operation) { + case OP_REPLACE_CHARS: + case OP_REMOVE_CHARS: + case OP_REPLACE_WORDS: + case OP_REMOVE_WORDS: + if (!s->find || !strlen(s->find)) { + av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'find' parameter to be specified\n"); + return AVERROR(EINVAL); + } + break; + } + + switch (s->operation) { + case OP_REPLACE_CHARS: + case OP_REPLACE_WORDS: + if (!s->replace || !strlen(s->replace)) { + av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'replace' parameter to be specified\n"); + return AVERROR(EINVAL); + } + break; + } + + if (s->operation == OP_REPLACE_CHARS && strlen(s->find) != strlen(s->replace)) { + av_log(ctx, AV_LOG_ERROR, "Selected mode requires the 'find' and 'replace' parameters to have the same length\n"); + return AVERROR(EINVAL); + } + + if (s->operation == OP_REPLACE_WORDS || s->operation == OP_REMOVE_WORDS) { + if (!s->separator || strlen(s->separator) != 1) { + av_log(ctx, AV_LOG_ERROR, "Selected mode requires a single separator char to be specified\n"); + return AVERROR(EINVAL); + } + + s->find_list = split_string(s->find, &s->nb_find_list, s->separator); + if (!s->find_list) + return AVERROR(ENOMEM); + + if (s->operation == OP_REPLACE_WORDS) { + + s->replace_list = split_string(s->replace, &s->nb_replace_list, s->separator); + if (!s->replace_list) + return AVERROR(ENOMEM); + + if (s->nb_find_list != s->nb_replace_list) { + av_log(ctx, AV_LOG_ERROR, "The number of words in 'find' and 'replace' needs to be equal\n"); + return AVERROR(EINVAL); + } + } + } + + return 0; +} + +static void uninit(AVFilterContext *ctx) +{ + TextModContext *s = ctx->priv; + + for (int i = 0; i < s->nb_find_list; i++) + av_freep(&s->find_list[i]); + + s->nb_find_list = 0; + av_freep(&s->find_list); + + for (int i = 0; i < s->nb_replace_list; i++) + av_freep(&s->replace_list[i]); + + s->nb_replace_list = 0; + av_freep(&s->replace_list); +} + +static char *process_text(TextModContext *s, char *text) +{ + const char *char_src = s->find; + const char *char_dst = s->replace; + char *result = NULL; + int escape_level = 0, k = 0; + + switch (s->operation) { + case OP_LEET: + case OP_REPLACE_CHARS: + + if (s->operation == OP_LEET) { + char_src = leet_src; + char_dst = leet_dst; + } + + result = av_strdup(text); + if (!result) + return NULL; + + for (size_t n = 0; n < strlen(result); n++) { + if (result[n] == '{') + escape_level++; + + if (!escape_level) { + size_t len = strlen(char_src); + for (size_t t = 0; t < len; t++) { + if (result[n] == char_src[t]) { + result[n] = char_dst[t]; + break; + } + } + } + + if (result[n] == '}') + escape_level--; + } + + break; + case OP_TO_UPPER: + case OP_TO_LOWER: + + result = av_strdup(text); + if (!result) + return NULL; + + for (size_t n = 0; n < strlen(result); n++) { + if (result[n] == '{') + escape_level++; + if (!escape_level) + result[n] = s->operation == OP_TO_LOWER ? av_tolower(result[n]) : av_toupper(result[n]); + if (result[n] == '}') + escape_level--; + } + + break; + case OP_REMOVE_CHARS: + + result = av_strdup(text); + if (!result) + return NULL; + + for (size_t n = 0; n < strlen(result); n++) { + int skip_char = 0; + + if (result[n] == '{') + escape_level++; + + if (!escape_level) { + size_t len = strlen(char_src); + for (size_t t = 0; t < len; t++) { + if (result[n] == char_src[t]) { + skip_char = 1; + break; + } + } + } + + if (!skip_char) + result[k++] = result[n]; + + if (result[n] == '}') + escape_level--; + } + + result[k] = 0; + + break; + case OP_REPLACE_WORDS: + case OP_REMOVE_WORDS: + + result = av_strdup(text); + if (!result) + return NULL; + + for (int n = 0; n < s->nb_find_list; n++) { + char *tmp = result; + const char *replace = (s->operation == OP_REPLACE_WORDS) ? s->replace_list[n] : ""; + + result = av_strireplace(result, s->find_list[n], replace); + if (!result) + return NULL; + + av_free(tmp); + } + + break; + } + + return result; +} + +static char *process_dialog_show_speaker(TextModContext *s, char *ass_line) +{ + ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line); + int escape_level = 0; + unsigned pos = 0, len; + char *result, *text; + AVBPrint pbuf; + + if (!dialog) + return NULL; + + text = process_text(s, dialog->text); + if (!text) + return NULL; + + if (!dialog->name || !strlen(dialog->name) || !dialog->text || !strlen(dialog->text)) + return av_strdup(ass_line); + + // Find insertion point in case the line starts with style codes + len = (unsigned)strlen(dialog->text); + for (unsigned i = 0; i < len; i++) { + + if (dialog->text[i] == '{') + escape_level++; + + if (dialog->text[i] == '}') + escape_level--; + + if (escape_level == 0) { + pos = i; + break; + } + } + + if (s->style && strlen(s->style)) + // When a style is specified reset the insertion point + // (always add speaker plus style at the start in that case) + pos = 0; + + if (pos >= len - 1) + return av_strdup(ass_line); + + av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED); + + if (pos > 0) { + av_bprint_append_data(&pbuf, dialog->text, pos); + } + + if (s->style && strlen(s->style)) { + if (s->style[0] == '{') + // Assume complete and valid style code, e.g. {\c&HFF0000&} + av_bprintf(&pbuf, "%s", s->style); + else + // Otherwise it must be a style name + av_bprintf(&pbuf, "{\\r%s}", s->style); + } + + switch (s->speaker_mode) { + case SM_SQUARE_BRACKETS: + av_bprintf(&pbuf, "[%s]", dialog->name); + break; + case SM_ROUND_BRACKETS: + av_bprintf(&pbuf, "(%s)", dialog->name); + break; + case SM_COLON: + av_bprintf(&pbuf, "%s:", dialog->name); + break; + case SM_PLAIN: + av_bprintf(&pbuf, "%s", dialog->name); + break; + } + + if (s->style && strlen(s->style)) { + // Reset line style + if (dialog->style && strlen(dialog->style) && !av_strcasecmp(dialog->style, "default")) + av_bprintf(&pbuf, "{\\r%s}", dialog->style); + else + av_bprintf(&pbuf, "{\\r}"); + } + + if (s->line_break) + av_bprintf(&pbuf, "\\N"); + else + av_bprintf(&pbuf, " "); + + av_bprint_append_data(&pbuf, dialog->text + pos, len - pos); + + av_bprint_finalize(&pbuf, &text); + + result = avpriv_ass_get_dialog(dialog->readorder, dialog->layer, dialog->style, dialog->name, text); + + av_free(text); + avpriv_ass_free_dialog(&dialog); + return result; +} + +static char *process_dialog(TextModContext *s, char *ass_line) +{ + ASSDialog *dialog; + char *result, *text; + + if (s->filter_type == TM_SHOW_SPEAKER) + return process_dialog_show_speaker(s, ass_line); + + dialog = avpriv_ass_split_dialog(NULL, ass_line); + if (!dialog) + return NULL; + + text = process_text(s, dialog->text); + if (!text) + return NULL; + + result = avpriv_ass_get_dialog(dialog->readorder, dialog->layer, dialog->style, dialog->name, text); + + av_free(text); + avpriv_ass_free_dialog(&dialog); + return result; +} + +static int 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, + }, +}; + +const AVFilter ff_sf_textmod = { + .name = "textmod", + .description = NULL_IF_CONFIG_SMALL("Modify subtitle text in several ways"), + .init = init, + .uninit = uninit, + .priv_size = sizeof(TextModContext), + .priv_class = &textmod_class, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS), +}; + +const AVFilter ff_sf_censor = { + .name = "censor", + .description = NULL_IF_CONFIG_SMALL("Censor words in subtitle text"), + .init = init_censor, + .uninit = uninit, + .priv_size = sizeof(TextModContext), + .priv_class = &censor_class, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS), +}; + +const AVFilter ff_sf_showspeaker = { + .name = "showspeaker", + .description = NULL_IF_CONFIG_SMALL("Prepend speaker names to text subtitles (when available)"), + .init = init_showspeaker, + .uninit = uninit, + .priv_size = sizeof(TextModContext), + .priv_class = &showspeaker_class, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS), +}; From patchwork Fri Dec 10 21:37:54 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32280 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2374957iog; Fri, 10 Dec 2021 13:40:29 -0800 (PST) X-Google-Smtp-Source: ABdhPJzVNIl25PTusqVsd5rvF5TKJwmKuNG7Z5E7aeZcIPSnEVquz2L08fbi0SJQR2WRX9NfEn4s X-Received: by 2002:a17:906:c112:: with SMTP id do18mr27659216ejc.103.1639172429568; Fri, 10 Dec 2021 13:40:29 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id j16si7910775edw.195.2021.12.10.13.40.29; Fri, 10 Dec 2021 13:40:29 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=npODForV; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 8A47568AFDE; Fri, 10 Dec 2021 23:38:04 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM12-MW2-obe.outbound.protection.outlook.com (mail-mw2nam12olkn2073.outbound.protection.outlook.com [40.92.23.73]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D4A4968AFA8 for ; Fri, 10 Dec 2021 23:38:01 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=cGPJER49kx7747WIoleO64f4DdOCvFLeQdcR2/cc5XlDbrklyfavTZEkWsvSdxx9pXiu3Zq2gGysnE8XsV2t2Qy4rylmcRG+kFSjmZQqozF3s4Kj0tWME30fnlgWEAtMP6tAbfGUclxJCQO73aMfefHWwA5TIE1AxzE9wpSgLKFxRiaerNX/DEyzC2rS9j0rvpCNVqBZ10O1UQGJ2oNKN4FgHd9ZMG+1XoA6t+JE0JD9bHd6wOepX7hjJFBZcFKHfpb2rKuwxB3nqj+lrhXnxc7WE5Zoy1qQUm3dlWIrD3k1p7+06p3gktebuMASulMD6o/7gXtdcPgxeSAHfpiDSw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=ONe0BeXKFaab6qUj4aHizHq2se3kCsgnKqD3ec85bLE=; b=mmYpjvDSjixY3Np9bWWH1KxZKuRz93u82GAvdn6jQoC+uhmsXTzI1mnlo24BGAnVaLS3u63za5J/7AqeHPBsh7nU9xcTLNYf17hBBWtjJP2Ct63at/f0g3jtrbSRyY/TGbnLzg12OqmoKEdsgO1nwoBodz6yWyZcs6DeednbdpFOmm3+y7xBUqXVZfXQpvH3WeHVx3mZbeWDrk3W6g2SLCTk2Ik0j2s+5SCmExWdYthvnH1tFLaGVK1EoEQycOHv48+9d+S0BUDLaJ7ywLE3ZUN4PreYuIXjLqheeHGcHMBSV+Ff975MkLeBdTw7GjT26vf+NGokFCjbk0kBctaVGg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=ONe0BeXKFaab6qUj4aHizHq2se3kCsgnKqD3ec85bLE=; b=npODForVPLvyzivaKOLOyA65Mgv1mlfpmnKglGWkrRODcCkhwFTz58r5Z4iomzjUHE4yl6ybZFcj88yl2BYoT5/e3VqZ4DOIy+hXI3JOxWI5VQlRvQOng2yjafwbJRXtKiHvIC6pf3ZeZwVfmrZfYXNPKfkPlAeCE93Kbn3sfOmjdL/waWoBq0Hw5JWro8vNBkD8hsReGHurKiHAntJtsM+GT+st2R0IsU7i72GmWRN+VavoFlRbtJ5ZB6ooZ/CDRuc2XF9j+TzH1bz04N6xJ7EhaXDjUbsQ+x+moG1+PTN1GZLOY0yZ+uvopF7LnVsMCNMTq29sfewvcAf58WuJQQ== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0191.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:8::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:54 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:54 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 17/21] avfilter/stripstyles: Add stripstyles filter Thread-Index: AQHX7g4wrwuwdQtrGUelxTmb4gG6hg== Date: Fri, 10 Dec 2021 21:37:54 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> <75bb9ceffef9e8e2d05ffce8a51c1991d5d3a973.1639172095.git.softworkz@hotmail.com> <554aab35996d92c90ab3b0ecb2c0c6c1278479d6.1639172095.git.softworkz@hotmail.com> <65992926f9356948d7f937ab852f8320b3da4bd9.1639172095.git.softworkz@hotmail.com> <4446daddbc3631c50d69005b790a24e60797a357.1639172095.git.softworkz@hotmail.com> In-Reply-To: <4446daddbc3631c50d69005b790a24e60797a357.1639172095.git.softworkz@hotmail.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [YDHbaZN1wJHljP51wP2cOmAOTCOhN2jT] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: de565e82-2383-4b20-7ecf-08d9bc25536e x-ms-traffictypediagnostic: DM8P223MB0191:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: wLQ/tnHz1PKmE/NA0oMSGWa2J6eZwFGfx4v0p8y9Nzao1FNEmgr+cJZutuNRoHo9doEwttBo1E9SWssteSDmQ9XFBjjBChId28l8xPFAOHx/144N3f7E6y2IWR5tjcuX4Gxmf9ekyiOplHv4zz8hzES+7PUzCysiGddfCM+PKXR6l1Zbukz1TuN9HgCafuVYUbERpuTM3i+Qlh1QV4FnxfvXgzg3RT6tiVBDwbmKCfiAsxY326zIAczkz6W0DJwD7oIEoLmfBwhGj+GFlYoF0U9e4JdNPXwlDrdCA42+ZcmnqlqO5NtqJaQUcFR8gYkpAkueWA1kqVnhvkQ1LZm0x9ZywqgpTTpsNc16HCLIoW0LPoiyGMtqJLq5ARhtqfqvyVCN6GMG7g9tmlX/EQa7Yh9XXkI8fpuGmt7frvowqA4Pkm7aTVuZtMZUfxKQXNdRJMWYEeATAnRuECsoFkJSRltLWdYHUhycLWFF/IFAYBl4h1noaeBYkOCkUnGn6rgvCurKtjhnnf6BkMnnRriXwegfaTvCSmaLSevUqUGgFFBTgtbaeOoDNFH5+u/849/FQccQOFUv2KVhNCyS/KGGukDWPPhRDzifTneTvYfC+vYLUjZZq3V4oVvZconJYzSr/Jk8qnRFGo2HwIAHoFSdTNOg7jrCp1Y8zQVfaLnw5rk= x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?VPCMyBcbSBzdRS8L0cYgNo1OTNDu?= =?utf-8?q?g22jsAa8LJwKv3aIEqIfDQiDazkr0sMc4S6ZC/6pU1DU618mo1zmlix7k+9JCX3Dm?= =?utf-8?q?ZeCwpg+Mj6Ud3Hw7y1mfJvNpZ7nsM5SJ6huEpCXv7uNwDYDLkCPf8FjdkIbX5RUWf?= =?utf-8?q?qKF9Q4Os1hg/si3GHI5pRKNyZcW23q81FbAEXkCXQtGQ1iTQWSzCZTRj8n75X0Jpr?= =?utf-8?q?1u2UjDY11Llw5uiKgnszciOVq74f1kFezG+6k3dqzu5z/5/IMWQag3nzNAJWdpj6z?= =?utf-8?q?yLZVLXOwpfbEPai8rn+JIc0F+ltRazDgw9eHd6VwtN6hqKLz0hu4KDT+8qFTCcgoE?= =?utf-8?q?+X2w0Gq1RWT1nUvcqrc82rWjcqf1JCQsZZJwjbbz150YEzlflCP6qMLdWrQW64enl?= =?utf-8?q?wQsX9Up/FUwzQ9OBeh9BhYnoKfTxYQKl8B/Uo3jDpvSWppvuO0OGfdeDrig2ap7wt?= =?utf-8?q?L9tbiu10seo6vskWckMVNpY2QcoSYA2QJk1lvHrHQAPVLEMcRIDVynhbijzOLWz5b?= =?utf-8?q?2orkQGgApv7327PBq1nx6Bom/iP50NFn3BTN0mpxTikAxPcipELba9KOl9DIgvVE1?= =?utf-8?q?JIIIiBZNgnJFsMkNMFgJ+SU470JP0FLjRFcxe2T40cm1hL0Cbht3p3RMNT3xVP9XN?= =?utf-8?q?5e0cZrtMoVc+mzEkrZzvsFeqRE3JU9BBV+NaQCm+DHoviLFEuZG3c7aVwx/mZ3XWz?= =?utf-8?q?6QUjnmmVD/XDG8kTbRmvAqPtYKgv1kBVBm+FU6p1+ejTemEeWPepzaA/rxrva4KKo?= =?utf-8?q?aQmJ3YPTETRMO/2Inc/cKg04ctHrSyIHcW2jtiyLn1KiUMBSaA6h3AH123QIFM412?= =?utf-8?q?2c3KjD9LJJuzdkAdfdOqGH3N7gBJDF6h4AhK+WZDk7tx9FIJqBC4tkT6A4tFHc4DJ?= =?utf-8?q?rtXFeuJaeLQdZfGZVovNXSq1uGhJshEgB/UR11GU1inQ=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: de565e82-2383-4b20-7ecf-08d9bc25536e X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:54.8809 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0191 Subject: [FFmpeg-devel] [PATCH v23 17/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: oOfseP8Vx3kw - 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 | 196 +++++++++++++++++++++++++++++++++++ 4 files changed, 235 insertions(+) create mode 100644 libavfilter/sf_stripstyles.c diff --git a/doc/filters.texi b/doc/filters.texi index e87d3993df..241c5d469b 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25704,6 +25704,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 d2995fbec6..7d7da0c59a 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -557,6 +557,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER) += vsink_nullsink.o OBJS-$(CONFIG_CENSOR_FILTER) += sf_textmod.o OBJS-$(CONFIG_SHOW_SPEAKER_FILTER) += sf_textmod.o OBJS-$(CONFIG_TEXTMOD_FILTER) += sf_textmod.o +OBJS-$(CONFIG_STRIPSTYLES_FILTER) += sf_stripstyles.o # multimedia filters OBJS-$(CONFIG_ABITSCOPE_FILTER) += avf_abitscope.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 673f9fb839..27680a2f00 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -546,6 +546,7 @@ extern const AVFilter ff_avf_showwavespic; extern const AVFilter ff_vaf_spectrumsynth; extern const AVFilter ff_sf_censor; extern const AVFilter ff_sf_showspeaker; +extern const AVFilter ff_sf_stripstyles; extern const AVFilter ff_sf_textmod; extern const AVFilter ff_svf_graphicsub2video; extern const AVFilter ff_svf_textsub2video; diff --git a/libavfilter/sf_stripstyles.c b/libavfilter/sf_stripstyles.c new file mode 100644 index 0000000000..82cb9c7647 --- /dev/null +++ b/libavfilter/sf_stripstyles.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2021 softworkz + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * text subtitle filter which removes inline-styles from subtitles + */ + +#include "libavutil/opt.h" +#include "internal.h" +#include "libavutil/ass_split_internal.h" +#include "libavutil/bprint.h" + +typedef struct StripStylesContext { + const AVClass *class; + enum AVSubtitleType format; + int remove_animated; + int select_layer; +} StripStylesContext; + +typedef struct DialogContext { + StripStylesContext* ss_ctx; + AVBPrint buffer; + int drawing_scale; + int is_animated; +} DialogContext; + +static void dialog_text_cb(void *priv, const char *text, int len) +{ + DialogContext *s = priv; + + av_log(s->ss_ctx, AV_LOG_DEBUG, "dialog_text_cb: %s\n", text); + + if (!s->drawing_scale && (!s->is_animated || !s->ss_ctx->remove_animated)) + av_bprint_append_data(&s->buffer, text, len); +} + +static void dialog_new_line_cb(void *priv, int forced) +{ + DialogContext *s = priv; + if (!s->drawing_scale && !s->is_animated) + av_bprint_append_data(&s->buffer, forced ? "\\N" : "\\n", 2); +} + +static void dialog_drawing_mode_cb(void *priv, int scale) +{ + DialogContext *s = priv; + s->drawing_scale = scale; +} + +static void dialog_animate_cb(void *priv, int t1, int t2, int accel, char *style) +{ + DialogContext *s = priv; + s->is_animated = 1; +} + +static void dialog_move_cb(void *priv, int x1, int y1, int x2, int y2, int t1, int t2) +{ + DialogContext *s = priv; + if (t1 >= 0 || t2 >= 0) + s->is_animated = 1; +} + +static const ASSCodesCallbacks dialog_callbacks = { + .text = dialog_text_cb, + .new_line = dialog_new_line_cb, + .drawing_mode = dialog_drawing_mode_cb, + .animate = dialog_animate_cb, + .move = dialog_move_cb, +}; + +static char *ass_get_line(int readorder, int layer, const char *style, + const char *speaker, const char *effect, const char *text) +{ + return av_asprintf("%d,%d,%s,%s,0,0,0,%s,%s", + readorder, layer, style ? style : "Default", + speaker ? speaker : "", effect, text); +} + +static char *process_dialog(StripStylesContext *s, const char *ass_line) +{ + DialogContext dlg_ctx = { .ss_ctx = s }; + ASSDialog *dialog = avpriv_ass_split_dialog(NULL, ass_line); + char *result = NULL; + + if (!dialog) + return NULL; + + if (s->select_layer >= 0 && dialog->layer != s->select_layer) + return NULL; + + dlg_ctx.ss_ctx = s; + + av_bprint_init(&dlg_ctx.buffer, 0, AV_BPRINT_SIZE_UNLIMITED); + + avpriv_ass_split_override_codes(&dialog_callbacks, &dlg_ctx, dialog->text); + + if (av_bprint_is_complete(&dlg_ctx.buffer) + && dlg_ctx.buffer.len > 0) + result = ass_get_line(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->effect, dlg_ctx.buffer.str); + + av_bprint_finalize(&dlg_ctx.buffer, NULL); + avpriv_ass_free_dialog(&dialog); + + return result; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + StripStylesContext *s = inlink->dst->priv; + AVFilterLink *outlink = inlink->dst->outputs[0]; + int ret; + + outlink->format = inlink->format; + + ret = av_frame_make_writable(frame); + if (ret <0 ) { + av_frame_free(&frame); + return AVERROR(ENOMEM); + } + + for (unsigned i = 0; i < frame->num_subtitle_areas; i++) { + + AVSubtitleArea *area = frame->subtitle_areas[i]; + + if (area->ass) { + char *tmp = area->ass; + area->ass = process_dialog(s, area->ass); + + if (area->ass) { + av_log(inlink->dst, AV_LOG_INFO, "original: %d %s\n", i, tmp); + av_log(inlink->dst, AV_LOG_INFO, "stripped: %d %s\n", i, area->ass); + } + else + area->ass = NULL; + + av_free(tmp); + } + } + + return ff_filter_frame(outlink, frame); +} + +#define OFFSET(x) offsetof(StripStylesContext, x) +#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM) + +static const AVOption stripstyles_options[] = { + { "remove_animated", "remove animated text (default: yes)", OFFSET(remove_animated), AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, FLAGS, 0 }, + { "select_layer", "process a specific ass layer only", OFFSET(remove_animated), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, FLAGS, 0 }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(stripstyles); + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .filter_frame = filter_frame, + }, +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + }, +}; + +const AVFilter ff_sf_stripstyles = { + .name = "stripstyles", + .description = NULL_IF_CONFIG_SMALL("Strip subtitle inline styles"), + .priv_size = sizeof(StripStylesContext), + .priv_class = &stripstyles_class, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_ASS), +}; + From patchwork Fri Dec 10 21:37:56 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32281 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2375157iog; Fri, 10 Dec 2021 13:40:39 -0800 (PST) X-Google-Smtp-Source: ABdhPJw9mTrJOUJbCjiYFmURvT4b5DM8lkmq6rJLNsl2K6epLd6HVB10/UDmCuK7nUQ0sS8V1FZ6 X-Received: by 2002:a05:6402:b82:: with SMTP id cf2mr42137895edb.40.1639172439061; Fri, 10 Dec 2021 13:40:39 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id gc38si5227240ejc.493.2021.12.10.13.40.38; Fri, 10 Dec 2021 13:40:39 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=hYMaN1Zm; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 968AC68AFBE; Fri, 10 Dec 2021 23:38:06 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM12-MW2-obe.outbound.protection.outlook.com (mail-mw2nam12olkn2073.outbound.protection.outlook.com [40.92.23.73]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5A12268AF10 for ; Fri, 10 Dec 2021 23:38:02 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=dGXvtBMYxAU5zAt4hHI4qx69NCH44TDdO8wWSUiwXOj0HLZC9KZLaIyL7udylEXnUizE0MUuyiZlgS327EFwIKgrdYOf3xZmb9NTaxlMhImWG/dABLzDzrjK0sQhUvHgnfyQ6S1kecyMOE2GvearlDtsWnZEajB+E81rwRtd5DLAQG7Q9cKbwShyQaSKesDLaYyX/vwm3/QjwyXH5q276EvppI63vkGuGIPwLb/HJIhAc5/aArKdZC1lUM9PDJGqDR45nL1p+2n3ihBMPb82MFRAMG3kskrtRyAlOokeyI1os/879zUAi+jraRaGcAjXKhV+OEdU3cGUBagZHLeFcw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=RcmlA8jhol8Mnj8PGvLPrcUF0lNcYZcQJrG8IjDO8bU=; b=ZAZsEAvXPGFwL4zLm8XdDe/tXQ2D5Skc4cxQFcoG5gm2QwpKSFG3apbYEg5txh/02DI2BK0bPaINm8wr2VB7Eh6ghG7vSFPEpX7X/gUtWqK2SUc0A/zaHuVOj0o4KGlsTFNWATkVp5HEYpJ26gfrHO2HXh89RKhXewf6az1bnSlBsHI5fiVcJB6FPSC8t/cQf0VmCy8CnKrdNGKZLpaB4fgWq+UUMXM3Ejm52G1UqDpMeYdiX97Im4OnWRrfcYZA7Sn0N2bHuXe1PtRknjEHGts52uMTImJGcYs7xNqJ6++xk47dcNDoE2ascIXaucJMrunDzrmg0U5jgeZhqe4Nkw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=RcmlA8jhol8Mnj8PGvLPrcUF0lNcYZcQJrG8IjDO8bU=; b=hYMaN1ZmbVDBOIham6d+Fgfw24oPDMKZT1KNnJ7GuGsyFFp/Iot4sO8EvkE83HuosRQQnOF6mUIYfiWt4hH2RGUA+9OmHVsHGTyH1LXBho8UKoS8DAjZkPk5rX/qaNHh6UQ1Vx/KybUE4c7bSV6BfIxQ4ilaLNgmgA8toTpWgz+EBQqCTD20Uz6IuE7e4Uf5b741qz+0PhGluG9lNOffxIuWB5CW/k4LyPqtRkb0pz+1NfUZNjvtcuPDT1Q8OCdJuMW+tzK46sMz/MoMnxAo7Ih7RHjpT0xNDcBICfUszL4ltAhkc5r39Ye1e8Vvgom6gSvn6kFdbunzu9l6Z2BxqQ== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0191.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:8::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:56 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:56 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 18/21] avfilter/splitcc: Add splitcc filter for closed caption handling Thread-Index: AQHX7g4xgqym92w7vEyeVTNKuDNASQ== Date: Fri, 10 Dec 2021 21:37:56 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> <75bb9ceffef9e8e2d05ffce8a51c1991d5d3a973.1639172095.git.softworkz@hotmail.com> <554aab35996d92c90ab3b0ecb2c0c6c1278479d6.1639172095.git.softworkz@hotmail.com> <65992926f9356948d7f937ab852f8320b3da4bd9.1639172095.git.softworkz@hotmail.com> <4446daddbc3631c50d69005b790a24e60797a357.1639172095.git.softworkz@hotmail.com> In-Reply-To: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [K7UdszdGxUSjbvt9yjIUaTWWJW75xuaz] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: f513017e-2072-4d6c-1b96-08d9bc255449 x-ms-traffictypediagnostic: DM8P223MB0191:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: ElAmqZaCGgMbRlgXqJX2EwfI0r0m5Y5xDrB4wrX2pQpCBS2ich/lG4hDGVDamOlQQIv3jhtA3HsC2rg+elB8mRsJ1Z7olWwRE6YVVFCSscg2ZcP9coYud1xW2D6C4TtMGtPzU7uOBzS92PERp3y6Utag3hZQDw4X6s3JdjWX4/FiyzLtVUQIurX9PchqvSK/GJSuQx+d7s3dlXcSz7XAVB1QgF1HisoaZ4fmB+tmxdS89DMcXzqv41CzCWvn59CeHRJp+w3LruuhHMv4xdgb2xykG/dbn4h0FsvSAadMZiW0qMaDSIpdQ7I7l6AzlOpnKaLG1TlloNOPx0nvT1J3PPjZSRpI7cPArm2mLEnQBmOsCRQgG0i5GPFShi9/CTXyAncgYShqtN6hjf9MkJ3CyIRmX8ZyR0SGYEjkgQtkRJMehS2OYVlL+4qVQc2N6qhB5gHwlNoqCcuqeC4BIYrrTUm3ylnxJEgG4D0J+lFbfULVOc5Z0qDptrkyLT842sjIZleLKjA4ANghvFZHUbS2PwRJHPN7+4QF+wgOwctvyHwGIPIGVq1cXFmTdJYWHgmeA9kQ9cSaODNfzs0NLuDS0Mutr/p6n4eLN5MjA5hIQSEdq0Oato2TUfDee81c7CGO x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?+vpAkMlUeS2dFDxLS1ZyBX8ykebd?= =?utf-8?q?VsULesVNXquz54MM9mEEva4VtMd3MomCbxGTdJCQlnF6u1pBaxgC9A2j2j1jOqCVx?= =?utf-8?q?jkvmKgKW7z+MGCEIgPQ6BBYM4hNyapWmJNbRLueBws4oCZH78aBxCPYEXa11Phl/c?= =?utf-8?q?ZlwyQ9uTCMu57eP177CyEu5LwtnMUfzT1jADGQc7v0Dga1Rh9R8/BMhKMa/MSPEto?= =?utf-8?q?WKLlIHt4FbWmS5Lg7inM0rshbqUhmwVznPKElyUwx0w/5picMTyelwZDxRvY9M8so?= =?utf-8?q?JdHjAAOL8nBNMlJ1ERYjEiQ5ZJPTHbNVs//bS7PCvseLloB9XM2uUOdH5M0V3BrDC?= =?utf-8?q?f1iMu2ghxEwT2PG6phjTXoV/hTkmKhZ5TfFgrxQm94V1qz48TOG9M7Ffe460ezULL?= =?utf-8?q?dNCEqQVYSYAKP/bImHO2Y1tfw+Jfq6pLmEa3C+PEB+dWAgcMSQLbCnZLbqu8RHLIG?= =?utf-8?q?s6oOZcSxlgLZQTO1bw7yTica4e1tkWfidvownakxMUULpDEbHvv3goVTf2KdcdjXY?= =?utf-8?q?LnrvXj15LZ/9mQh/rBzAmFc1T1pbqi+5zn77dm55EPaff1YBfUYQFZ635o0SNaFln?= =?utf-8?q?DVVdA0w+Q/mgMv5ai78WXRhxCMIpuCNUQ2BndD1mNpxtoMzclt4CRrTLYfuxAHDZ8?= =?utf-8?q?7uK6/0kYvRtMHHFwd4bsxNGysTx2/1kqFXp9ORKWW5nTukoDOjOLIO6zxu0mn1FY8?= =?utf-8?q?hWdPga5ZuF8N4rZav76LWG70dRjLWcbTXeOlZkjshyimHmwSuJjyTNv7TQye7vBkD?= =?utf-8?q?+/Tc3wSXt4oI9+S8/iRw8r0h+qphHs8eksciBk/Ca0x90T3ww5jnkHPeukA5Ikj/P?= =?utf-8?q?a59GTLtRvMhOqtq0kaYA1Oe2lbSKYVYUJxW1IFpD5t4YNnxMLiXFTGqxiXQOJ73nB?= =?utf-8?q?a96q6W2psQwG4jFGRBJKMt8XvQXUU/0VBuOH/WvTWY2A=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: f513017e-2072-4d6c-1b96-08d9bc255449 X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:56.2838 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0191 Subject: [FFmpeg-devel] [PATCH v23 18/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 1SOT5C7wrR6s - 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 | 378 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 444 insertions(+) create mode 100644 libavfilter/sf_splitcc.c diff --git a/configure b/configure index 16ab6b46c3..73a7267cf1 100755 --- a/configure +++ b/configure @@ -3704,6 +3704,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 241c5d469b..743c36c432 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -26054,6 +26054,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 7d7da0c59a..2224e5fe5f 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -557,6 +557,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER) += vsink_nullsink.o OBJS-$(CONFIG_CENSOR_FILTER) += sf_textmod.o OBJS-$(CONFIG_SHOW_SPEAKER_FILTER) += sf_textmod.o OBJS-$(CONFIG_TEXTMOD_FILTER) += sf_textmod.o +OBJS-$(CONFIG_SPLITCC_FILTER) += sf_splitcc.o OBJS-$(CONFIG_STRIPSTYLES_FILTER) += sf_stripstyles.o # multimedia filters diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 27680a2f00..6adde2b9f6 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -546,6 +546,7 @@ extern const AVFilter ff_avf_showwavespic; extern const AVFilter ff_vaf_spectrumsynth; extern const AVFilter ff_sf_censor; extern const AVFilter ff_sf_showspeaker; +extern const AVFilter ff_sf_splitcc; extern const AVFilter ff_sf_stripstyles; extern const AVFilter ff_sf_textmod; extern const AVFilter ff_svf_graphicsub2video; diff --git a/libavfilter/sf_splitcc.c b/libavfilter/sf_splitcc.c new file mode 100644 index 0000000000..3556d084d9 --- /dev/null +++ b/libavfilter/sf_splitcc.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2021 softworkz + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * subtitle filter for splitting out closed-caption/A53 subtitles from video frame side data + */ + +#include "filters.h" +#include "libavutil/opt.h" +#include "subtitles.h" +#include "libavcodec/avcodec.h" + +static const AVRational ms_tb = {1, 1000}; + +typedef struct SplitCaptionsContext { + const AVClass *class; + enum AVSubtitleType format; + AVCodecContext *cc_dec; + int eof; + AVFrame *next_sub_frame; + AVFrame *empty_sub_frame; + int new_frame; + int64_t next_repetition_pts; + AVBufferRef *subtitle_header; + int use_cc_styles; + int real_time; + int real_time_latency_msec; + int data_field; + int scatter_realtime_output; +} SplitCaptionsContext; + +static int init(AVFilterContext *ctx) +{ + SplitCaptionsContext *s = ctx->priv; + AVDictionary *options = NULL; + + int ret; + const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_EIA_608); + if (!codec) { + av_log(ctx, AV_LOG_ERROR, "failed to find EIA-608/708 decoder\n"); + return AVERROR_DECODER_NOT_FOUND; + } + + if (!((s->cc_dec = avcodec_alloc_context3(codec)))) { + av_log(ctx, AV_LOG_ERROR, "failed to allocate EIA-608/708 decoder\n"); + return AVERROR(ENOMEM); + } + + av_dict_set_int(&options, "real_time", s->real_time, 0); + av_dict_set_int(&options, "real_time_latency_msec", s->real_time_latency_msec, 0); + av_dict_set_int(&options, "data_field", s->data_field, 0); + + if ((ret = avcodec_open2(s->cc_dec, codec, &options)) < 0) { + av_log(ctx, AV_LOG_ERROR, "failed to open EIA-608/708 decoder: %i\n", ret); + return ret; + } + + if (s->use_cc_styles && s->cc_dec->subtitle_header && s->cc_dec->subtitle_header[0] != 0) { + char* subtitle_header = av_strdup((char *)s->cc_dec->subtitle_header); + if (!subtitle_header) + return AVERROR(ENOMEM); + s->subtitle_header = av_buffer_create((uint8_t *)subtitle_header, strlen(subtitle_header) + 1, NULL, NULL, 0); + if (!s->subtitle_header) { + av_free(subtitle_header); + return AVERROR(ENOMEM); + } + } + + return 0; +} + +static void uninit(AVFilterContext *ctx) +{ + SplitCaptionsContext *s = ctx->priv; + av_frame_free(&s->next_sub_frame); + av_frame_free(&s->empty_sub_frame); + av_buffer_unref(&s->subtitle_header); +} + +static int config_input(AVFilterLink *link) +{ + const SplitCaptionsContext *context = link->dst->priv; + + if (context->cc_dec) + context->cc_dec->pkt_timebase = link->time_base; + + return 0; +} + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats; + AVFilterLink *inlink0 = ctx->inputs[0]; + AVFilterLink *outlink0 = ctx->outputs[0]; + AVFilterLink *outlink1 = ctx->outputs[1]; + static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NB }; + int ret; + + /* set input0 video formats */ + formats = ff_all_formats(AVMEDIA_TYPE_VIDEO); + if ((ret = ff_formats_ref(formats, &inlink0->outcfg.formats)) < 0) + return ret; + + /* set output0 video formats */ + if ((ret = ff_formats_ref(formats, &outlink0->incfg.formats)) < 0) + return ret; + + /* set output1 subtitle formats */ + formats = ff_make_format_list(subtitle_fmts); + if ((ret = ff_formats_ref(formats, &outlink1->incfg.formats)) < 0) + return ret; + + return 0; +} + +static int config_video_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + const AVFilterLink *inlink = outlink->src->inputs[0]; + + outlink->w = ctx->inputs[0]->w; + outlink->h = ctx->inputs[0]->h; + outlink->frame_rate = ctx->inputs[0]->frame_rate; + outlink->time_base = ctx->inputs[0]->time_base; + outlink->sample_aspect_ratio = ctx->inputs[0]->sample_aspect_ratio; + + if (inlink->hw_frames_ctx) + outlink->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx); + + return 0; +} + +static int config_sub_output(AVFilterLink *outlink) +{ + const AVFilterLink *inlink = outlink->src->inputs[0]; + + outlink->time_base = inlink->time_base; + outlink->format = AV_SUBTITLE_FMT_ASS; + outlink->frame_rate = (AVRational){5, 1}; + + 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); + } + + if (!out) + return AVERROR(ENOMEM); + + out->subtitle_pts = av_rescale_q(s->next_sub_frame->pts, outlink->time_base, AV_TIME_BASE_Q); + s->new_frame = 0; + + return ff_filter_frame(outlink, out); + } + + return 0; +} + +static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt) +{ + int ret; + + *got_frame = 0; + + if (pkt) { + ret = avcodec_send_packet(avctx, pkt); + // In particular, we don't expect AVERROR(EAGAIN), because we read all + // decoded frames with avcodec_receive_frame() until done. + if (ret < 0 && ret != AVERROR_EOF) + return ret; + } + + ret = avcodec_receive_frame(avctx, frame); + if (ret < 0 && ret != AVERROR(EAGAIN)) + return ret; + if (ret >= 0) + *got_frame = 1; + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFrameSideData *sd; + SplitCaptionsContext *s = inlink->dst->priv; + AVFilterLink *outlink0 = inlink->dst->outputs[0]; + AVFilterLink *outlink1 = inlink->dst->outputs[1]; + AVPacket *pkt = NULL; + AVFrame *sub_out = NULL; + + int ret; + + outlink0->format = inlink->format; + + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_A53_CC); + + if (sd) { + int got_output = 0; + + pkt = av_packet_alloc(); + pkt->buf = av_buffer_ref(sd->buf); + if (!pkt->buf) { + ret = AVERROR(ENOMEM); + goto fail; + } + + pkt->data = sd->data; + pkt->size = (int)sd->size; + pkt->pts = frame->pts; + + sub_out = ff_get_subtitles_buffer(outlink1, AV_SUBTITLE_FMT_ASS); + if (!sub_out) { + ret = AVERROR(ENOMEM); + goto fail; + } + + if ((ret = av_buffer_replace(&sub_out->subtitle_header, s->subtitle_header)) < 0) + goto fail; + + ret = decode(s->cc_dec, sub_out, &got_output, pkt); + + if (ret < 0) + goto fail; + + if (got_output) { + sub_out->pts = frame->pts; + av_frame_free(&s->next_sub_frame); + s->next_sub_frame = sub_out; + sub_out = NULL; + s->new_frame = 1; + s->next_sub_frame->pts = frame->pts; + + if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0) + goto fail; + + if (s->real_time && s->scatter_realtime_output) { + if (s->next_repetition_pts) + s->next_sub_frame->pts = s->next_repetition_pts; + + s->next_sub_frame->subtitle_end_time = 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_end_time = 100; + s->next_sub_frame->pts = frame->pts; + s->new_frame = 1; + + if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0) + goto fail; + } + + ret = ff_filter_frame(outlink0, frame); + +fail: + av_packet_free(&pkt); + av_frame_free(&sub_out); + return ret; +} + +#define OFFSET(x) offsetof(SplitCaptionsContext, x) +#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM) + +static const AVOption split_cc_options[] = { + { "use_cc_styles", "Emit closed caption style header", OFFSET(use_cc_styles), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, NULL }, + { "real_time", "emit subtitle events as they are decoded for real-time display", OFFSET(real_time), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, + { "real_time_latency_msec", "minimum elapsed time between emitting real-time subtitle events", OFFSET(real_time_latency_msec), AV_OPT_TYPE_INT, { .i64 = 200 }, 0, 500, FLAGS }, + { "scatter_realtime_output", "scatter output events to a duration of real_time_latency_msec", OFFSET(scatter_realtime_output), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, + { "data_field", "select data field", OFFSET(data_field), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, FLAGS, "data_field" }, + { "auto", "pick first one that appears", 0, AV_OPT_TYPE_CONST, { .i64 =-1 }, 0, 0, FLAGS, "data_field" }, + { "first", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "data_field" }, + { "second", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "data_field" }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(split_cc); + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + .config_props = config_input, + }, +}; + +static const AVFilterPad outputs[] = { + { + .name = "video_passthrough", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_video_output, + }, + { + .name = "subtitles", + .type = AVMEDIA_TYPE_SUBTITLE, + .request_frame = request_sub_frame, + .config_props = config_sub_output, + }, +}; + +const AVFilter ff_sf_splitcc = { + .name = "splitcc", + .description = NULL_IF_CONFIG_SMALL("Extract closed-caption (A53) data from video as subtitle stream."), + .init = init, + .uninit = uninit, + .priv_size = sizeof(SplitCaptionsContext), + .priv_class = &split_cc_class, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_QUERY_FUNC(query_formats), +}; From patchwork Fri Dec 10 21:37:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32282 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2375352iog; Fri, 10 Dec 2021 13:40:48 -0800 (PST) X-Google-Smtp-Source: ABdhPJxXX5euV8b8fbcJHobokxoM4ZeSHE/sVDu6CHlIPSaE8kgGxz+HDpgE6f07WrVpqhBtzIn1 X-Received: by 2002:a05:6402:4c4:: with SMTP id n4mr43067849edw.30.1639172448559; Fri, 10 Dec 2021 13:40:48 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id ee4si5973062edb.456.2021.12.10.13.40.48; Fri, 10 Dec 2021 13:40:48 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=KeVaiSWd; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 9EC4168AFF0; Fri, 10 Dec 2021 23:38:07 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM12-MW2-obe.outbound.protection.outlook.com (mail-mw2nam12olkn2073.outbound.protection.outlook.com [40.92.23.73]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id C7A9468AF10 for ; Fri, 10 Dec 2021 23:38:02 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=WIxc+ZpC1ITNYCP0Gqkq3clrOfahB6zofbiDDzmYUK4VDYvf+fpgLRTjkgLrCdAk4Gqa6yO6Yn17acNJEi0CEkJvKPZIvxbcTp3vpf1kbo/A16L2fEkpTSBT8XnzinO0e8bLj22YmS4ovPUl62pQhYxrGeKsROjfu8WvImzAIRgsu6Yh8L2H1Kl2mTMgM3zVs3MccONV7heJ9aleR8dyOzCMy0FOWg+wkCxcitZYpablEn/9mkibYL4z4Ea1FKgkI13l/wxNF1bVrIxKx2kBwVTKGTIsyGUvnELMrVtP59UGSH8+yidb+d3g35zRNa+JvENvVgEHN9Qro5/UfI5SBg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=QCxC6xxivqvTXedAckE/p6WgwLlkGk7HdbTrLzQqEdc=; b=GAa0Bqu9R1vb5DNiI+oKLDhu+wkmSlrsbfNK6yMv7pe8KRizZNdLY368gvJmqJmbDo4QOS91rkgJ+9q5z7LqyV6uIJEbwhhdXe/Ozmud8udQZAutlJTyt1kxJ6XXins3Ikht9aKdH+SRWh6FUMYJEPMeud+oBKiJ3FY8ggODd/uvT+45aREfNIzahVHJ7N+CVjH1kghPNPxOKCSGGyEkHuqFizEx7J55q34r1Jo4MboA5z0dsKkOsd358LQdYmz+d0ELRI85yHN9IXa2ott5O4j3ttfAm+TK2+B1G4fajeFUaLl8gA9O0Rc9Ft3AcjspTpd/aBeuOj8mTMN+ODKfww== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=QCxC6xxivqvTXedAckE/p6WgwLlkGk7HdbTrLzQqEdc=; b=KeVaiSWdvIK/qZGednVLNXQcg3BcU4qUTLRXiH/WR49UsQ+5n67Jev3vo4BejvU+eDRFLeb2uSUpxHfbEFX0OKZDkEzQwFFqnfBFaFnr7RCE7x298gC1jdITfCMWUoJsk3C214tJnsDjM8ZcQ4dU67d4Ii5z3ZYCSKNRlnD3WqCIMeO0PLL6hnThaNr3uH7uQZJnW9jd5yDSUkH0jr/d7HL4qLmM3FdlzYwubTVBk332AcIgf2eYkiwZxlEGTar6C0mfm5C8Dn42vdZOAQl4zw2aVrxq+3u2Xe2jX27DW3PUAlxbCrW+sMF6Hw8mJwn183g170QdEjI8X2iKZqRzxw== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0191.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:8::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:57 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:57 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 19/21] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) Thread-Index: AQHX7g4yHdRe7mBb00mU72zjRDziiA== Date: Fri, 10 Dec 2021 21:37:57 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> <75bb9ceffef9e8e2d05ffce8a51c1991d5d3a973.1639172095.git.softworkz@hotmail.com> <554aab35996d92c90ab3b0ecb2c0c6c1278479d6.1639172095.git.softworkz@hotmail.com> <65992926f9356948d7f937ab852f8320b3da4bd9.1639172095.git.softworkz@hotmail.com> <4446daddbc3631c50d69005b790a24e60797a357.1639172095.git.softworkz@hotmail.com> <947dfa704e42ef5c706880efbe9daa17503c8f87.1639172095.git.softworkz@hotmail.com> In-Reply-To: <947dfa704e42ef5c706880efbe9daa17503c8f87.1639172095.git.softworkz@hotmail.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [b+yzAnqRvlLYFnKggfhVLAiqHtx/AuWd] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 3b758e4d-61a9-4ece-3070-08d9bc255529 x-ms-traffictypediagnostic: DM8P223MB0191:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: FCy8IfAQ6cIoJcNb1BNWuAO6OwG0sPOvbZsKFwhxFgOv+tgMvQP9MdqbUN5I4mChU8dc9t7vj9+x65oMjsvqrz3dGTOIZXJIfwP8EeS0TW0xzaWM7pFgEYJqUggC4JHYLQrT8S4aE+c30IOseeNrewHe8137LnNHOxId8x2PEvXO87pMQYlb8B4mN6hCFr+3iYQ0ba6L0rAK/3ZDBxPQuzD2YGhImWKWpEXN8sp9rI101xq2I4bxuHGg4zST1zbmeuyN7KSRSE45QvLV4ZNlcdOHudQ+fdq92AXrL/5vbC+PSjBP9TAnHgP5AKaEByYI0TV3Jlqy5sTI75XZZKi34UopMhhcelfdDl5ZkWNnkmGzHr/CIMbNAg2OTU9eBteRg00vm3NN7YmbHWJAXb0irduX51OtcgttDqkdE5k+1lwRl13YxnsmtUeZTDn+CdPzcG4J3wHJ+MTJzk+oVnm/3bTyDGE3jyyB9Y686KANltzCyqhR60TUAlYi66kU7qNKz+FZRJ2HtFCXAKzzqlxWXhK/ro9xpO5nLeuGvmxOKn1rqi2GPC1vp4Fs0Uh2gUzgp9gv41KixB3NF0pAQpzTKT8/TWMiHGEIX9aOpsBui1Ci40MJrfTu79H4jtzehvIJ2YLqDWLhXOQ3/0a5Zwx5jAAgouFLfDkm5T8WOlOPrdQ= x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?VjWFCuPVUASI48Ax6LbiItOPFeYP?= =?utf-8?q?755D34Wogzdkf4zVJGcytCOp7TEIq5bGlEDrcA4mjRVzGm3U0DpiBtzsZNnSHR4Ru?= =?utf-8?q?1Z8g076MKJ1I4YqJW6bxaKfmbvt2bZn5gwurU5vS7oBgcmGSfwLpODJsyPaNlVLTf?= =?utf-8?q?GbcOCJNuk7Y3pNA/FVOHiRDA422LnlVnlgj7N9WLoq2zsqX1CY14xyoHZ1EZ4obOn?= =?utf-8?q?vjQU6muhyHKzBY1UjMZbkddffdlcumaZffsI3VI6TuSOzxC+vtgzJXIXAry9Hrhe7?= =?utf-8?q?WLRmViZ7meLzoIVl9SppUVAgMneADSKwC6bgZWcRpjydM2/0TeDxdXhKjnPrDIx+I?= =?utf-8?q?zbpwKFbrdGarIzGuAXt2cXOSY7ip7NwfGUNJMJgzm32wQzTm9ISLXM01d6k6KG0Ui?= =?utf-8?q?h/EObrq79zskd+XLp3Hk558eitGS01inHKlFjUsVQ/JivJsEL90skLSHmO2Eu1fdy?= =?utf-8?q?9SAG2lxNjskKE0Gv+Sk/DHAiocIVzAAB1sjFkER8JWmkYLhA7E6yR9mrbYYeZCi9l?= =?utf-8?q?inDJPkIdv8ujlp8P2O6jGo8vZyVQLP4Ivm7shkLwdUeHAg96sIU3kbikV1Z10Y0BL?= =?utf-8?q?cgZeNXDDOGi9127NNs++zbEuwK0Ws9sTtFPkCQSzMmHsqtGKs4Ie6SkU1LU0BEjyy?= =?utf-8?q?c5t2DCSYsQ1Ly1zr5kGajx0585UEa+ntUFx9xWIMSN1j4XHZGEc1Hbnld7Fx8VPi+?= =?utf-8?q?l9JSzCyhgpz435AQXeoIe377Fd4rYP1kwX2bfODMzHAMiSKINRNNll9SYZnlGLePu?= =?utf-8?q?oY/HUKcRQHgNqHLmL3M4Hqut4XzgTSPlWxNu9Mlt56pUcMlpVOCAZLKhyDzp0/Mzf?= =?utf-8?q?cISbQiXUz+E5dWlzjXeddaBTI0UHK21yDZGKoCPMCoNtwQJVlhHs+/XOG4xvI2boT?= =?utf-8?q?9HCiBiGG4f+f6Z47EjbUjUq4toUHTYATz18kCVTQ9XkA=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 3b758e4d-61a9-4ece-3070-08d9bc255529 X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:57.7125 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0191 Subject: [FFmpeg-devel] [PATCH v23 19/21] 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: sckVKoMi9a2I Signed-off-by: softworkz --- configure | 1 + doc/filters.texi | 55 +++++ libavfilter/Makefile | 2 + libavfilter/allfilters.c | 1 + libavfilter/sf_graphicsub2text.c | 354 +++++++++++++++++++++++++++++++ 5 files changed, 413 insertions(+) create mode 100644 libavfilter/sf_graphicsub2text.c diff --git a/configure b/configure index 73a7267cf1..360e91d762 100755 --- a/configure +++ b/configure @@ -3640,6 +3640,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 743c36c432..26bf6014d0 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25820,6 +25820,61 @@ ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_comple @end example @end itemize +@section graphicsub2text + +Converts graphic subtitles to text subtitles by performing OCR. + +For this filter to be available, ffmpeg needs to be compiled with libtesseract (see https://github.com/tesseract-ocr/tesseract). +Language models need to be downloaded from https://github.com/tesseract-ocr/tessdata and put into as subfolder named 'tessdata' or into a folder specified via the environment variable 'TESSDATA_PREFIX'. +The path can also be specified via filter option (see below). + +Note: These models are including the data for both OCR modes. + +Inputs: +- 0: Subtitles [bitmap] + +Outputs: +- 0: Subtitles [text] + +It accepts the following parameters: + +@table @option +@item ocr_mode +The character recognition mode to use. + +Supported OCR modes are: + +@table @var +@item 0, tesseract +This is the classic libtesseract operation mode. It is fast but less accurate than LSTM. +@item 1, lstm +Newer OCR implementation based on ML models. Provides usually better results, requires more processing resources. +@item 2, both +Use a combination of both modes. +@end table + +@item tessdata_path +The path to a folder containing the language models to be used. + +@item language +The recognition language. It needs to match the first three characters of a language model file in the tessdata path. + +@end table + + +@subsection Examples + +@itemize +@item +Convert DVB graphic subtitles to ASS (text) subtitles + +Note: For this to work, you need to have the data file 'eng.traineddata' in a 'tessdata' subfolder (see above). +@example +ffmpeg ffmpeg -loglevel verbose -i "https://streams.videolan.org/streams/ts/video_subs_ttxt%2Bdvbsub.ts" -filter_complex "[0:13]graphicsub2text=ocr_mode=both" -c:s ass -y output.mkv +@end example +@end itemize + + @section graphicsub2video Renders graphic subtitles as video frames. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 2224e5fe5f..3b972e134b 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -296,6 +296,8 @@ OBJS-$(CONFIG_GBLUR_VULKAN_FILTER) += vf_gblur_vulkan.o vulkan.o vulka OBJS-$(CONFIG_GEQ_FILTER) += vf_geq.o OBJS-$(CONFIG_GRADFUN_FILTER) += vf_gradfun.o OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER) += vf_overlaygraphicsubs.o framesync.o +OBJS-$(CONFIG_GRAPHICSUB2TEXT_FILTER) += sf_graphicsub2text.o +OBJS-$(CONFIG_GRAPHICSUB2VIDEO_FILTER) += vf_overlaygraphicsubs.o framesync.o OBJS-$(CONFIG_GRAPHMONITOR_FILTER) += f_graphmonitor.o OBJS-$(CONFIG_GRAYWORLD_FILTER) += vf_grayworld.o OBJS-$(CONFIG_GREYEDGE_FILTER) += vf_colorconstancy.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 6adde2b9f6..f70f08dc5a 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -545,6 +545,7 @@ extern const AVFilter ff_avf_showwaves; extern const AVFilter ff_avf_showwavespic; extern const AVFilter ff_vaf_spectrumsynth; extern const AVFilter ff_sf_censor; +extern const AVFilter ff_sf_graphicsub2text; extern const AVFilter ff_sf_showspeaker; extern const AVFilter ff_sf_splitcc; extern const AVFilter ff_sf_stripstyles; diff --git a/libavfilter/sf_graphicsub2text.c b/libavfilter/sf_graphicsub2text.c new file mode 100644 index 0000000000..ef10d60efd --- /dev/null +++ b/libavfilter/sf_graphicsub2text.c @@ -0,0 +1,354 @@ +/* + * 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 "libavutil/opt.h" +#include "subtitles.h" + +typedef struct SubOcrContext { + const AVClass *class; + int w, h; + + TessBaseAPI *tapi; + TessOcrEngineMode ocr_mode; + char *tessdata_path; + char *language; + + int readorder_counter; + + AVFrame *pending_frame; +} SubOcrContext; + + +static int init(AVFilterContext *ctx) +{ + SubOcrContext *s = ctx->priv; + const char* tver = TessVersion(); + 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); + } + + return 0; +} + +static void uninit(AVFilterContext *ctx) +{ + SubOcrContext *s = ctx->priv; + + if (s->tapi) { + TessBaseAPIEnd(s->tapi); + TessBaseAPIDelete(s->tapi); + } +} + +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 0; +} + +static int config_output(AVFilterLink *outlink) +{ + const AVFilterContext *ctx = outlink->src; + SubOcrContext *s = ctx->priv; + + outlink->format = AV_SUBTITLE_FMT_ASS; + outlink->w = s->w; + outlink->h = s->h; + + return 0; +} + +static uint8_t* create_grayscale_image(AVFilterContext *ctx, AVSubtitleArea *area) +{ + 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); + } + + for (unsigned i = 0; i < img_size; i++) + gs_img[i] = 255 - gray_pal[img[i]]; + + return gs_img; +} + +static int convert_area(AVFilterContext *ctx, AVSubtitleArea *area) +{ + SubOcrContext *s = ctx->priv; + char *ocr_text = NULL; + int ret; + uint8_t *gs_img = create_grayscale_image(ctx, area); + + if (!gs_img) + return AVERROR(ENOMEM); + + area->type = AV_SUBTITLE_FMT_ASS; + TessBaseAPISetImage(s->tapi, gs_img, area->w, area->h, 1, area->linesize[0]); + TessBaseAPISetSourceResolution(s->tapi, 70); + + ret = TessBaseAPIRecognize(s->tapi, NULL); + if (ret == 0) + ocr_text = TessBaseAPIGetUTF8Text(s->tapi); + + if (!ocr_text) { + av_log(ctx, AV_LOG_WARNING, "OCR didn't return a text. ret=%d\n", ret); + area->ass = NULL; + } + else { + 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); + } + + av_freep(&gs_img); + av_buffer_unref(&area->buf[0]); + area->type = AV_SUBTITLE_FMT_ASS; + + return 0; +} + +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) { + const uint64_t pts_diff = frame->subtitle_pts - s->pending_frame->subtitle_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_end_time = (uint32_t)(pts_diff / 1000); + + ret = ff_filter_frame(outlink, s->pending_frame); + s->pending_frame = NULL; + if (ret < 0) + return ret; + + frame_sent = 1; + + if (frame->num_subtitle_areas == 0) { + // No need to forward this empty frame + av_frame_free(&frame); + return 0; + } + } + + 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_DEBUG, "filter_frame sub_pts: %"PRIu64", start_time: %d, end_time: %d, num_areas: %d\n", + frame->subtitle_pts, frame->subtitle_start_time, frame->subtitle_end_time, 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 (unsigned i = 0; i < frame->num_subtitle_areas; i++) { + AVSubtitleArea *area = frame->subtitle_areas[i]; + + ret = convert_area(ctx, area); + if (ret < 0) + return ret; + + if (area->ass && area->ass[0] != '\0') { + char *tmp = area->ass; + + if (i == 0) + area->ass = avpriv_ass_get_dialog(s->readorder_counter++, 0, "Default", NULL, tmp); + else { + AVSubtitleArea* area0 = frame->subtitle_areas[0]; + char* tmp2 = area0->ass; + area0->ass = av_asprintf("%s\\N%s", area0->ass, tmp); + av_free(tmp2); + area->ass = NULL; + } + + av_free(tmp); + } + } + + if (frame->num_subtitle_areas > 1) { + for (unsigned i = 1; i < frame->num_subtitle_areas; i++) { + AVSubtitleArea* area = frame->subtitle_areas[i]; + + 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_freep(&frame->subtitle_areas[i]); + } + + AVSubtitleArea* area0 = frame->subtitle_areas[0]; + av_freep(&frame->subtitle_areas); + frame->subtitle_areas = av_malloc_array(1, sizeof(AVSubtitleArea*)); + frame->subtitle_areas[0] = area0; + frame->num_subtitle_areas = 1; + } + + // When decoders can't determine the end time, they are setting it either to UINT32_NAX + // or 30s (dvbsub). + if (frame->num_subtitle_areas > 0 && frame->subtitle_end_time >= 30000) { + // 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_end_time = 1; + } + + 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[] = { + { "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" }, + { "tessdata_path", "path to tesseract data", OFFSET(tessdata_path), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS, NULL }, + { "language", "ocr language", OFFSET(language), AV_OPT_TYPE_STRING, {.str = "eng"}, 0, 0, FLAGS, NULL }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(graphicsub2text); + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .filter_frame = filter_frame, + .config_props = config_input, + }, +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .config_props = config_output, + }, +}; + +const AVFilter ff_sf_graphicsub2text = { + .name = "graphicsub2text", + .description = NULL_IF_CONFIG_SMALL("Convert graphical subtitles to text subtitles via OCR"), + .init = init, + .uninit = uninit, + .priv_size = sizeof(SubOcrContext), + .priv_class = &graphicsub2text_class, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_QUERY_FUNC(query_formats), +}; From patchwork Fri Dec 10 21:37:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32284 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2375708iog; Fri, 10 Dec 2021 13:41:07 -0800 (PST) X-Google-Smtp-Source: ABdhPJzpwsh892Av6KnBZBiYNY0VFujNzq5k+7DO1jIUZqvwuhOrqXh+ELY47KPxqL+sE/APuHdV X-Received: by 2002:a05:6402:1ca2:: with SMTP id cz2mr40939638edb.302.1639172467430; Fri, 10 Dec 2021 13:41:07 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id t12si5277364ejy.366.2021.12.10.13.41.06; Fri, 10 Dec 2021 13:41:07 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=k0S79l6X; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 8151468AFFB; Fri, 10 Dec 2021 23:38:10 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM12-MW2-obe.outbound.protection.outlook.com (mail-mw2nam12olkn2077.outbound.protection.outlook.com [40.92.23.77]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 244E968AF10 for ; Fri, 10 Dec 2021 23:38:06 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=KWQSGSDXQRVooXr2X922Nv3MAw4CphwFRToQJT8y35ntLTgNit+po1mJ/biWjzHAufrjM1uw5pu9As1/demoObivLcjAiPEtp9znHHZ5+Tef9W+L2rWjx1SEpjH+4XUzBnU440+q9D3capM5if67hxgkQM9jhQitBY2gkgBcrAXtF8Xjv8b1wVRmPaCOR28O14XJad/isTpY6UqsBTH0f4xyQPCG9gBXCei1E17aC5cJ1xbJTavrbzMQNxO2/VV6Yw2nm9wqelOq58LHXvYI8tpjOiHXnT/x81f+nH/7zXRlYFWBucITgOBYww7+G4gyBo+85EkDiOCOXw1z+aPyTg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=WELUG5+U6wF1sL6LbvJfNMZ3VhC2CrqxaAU3hx/XIxQ=; b=NW0zEDU89Fke/PSryQXo01yFcY/uFo2nUZEb2uYr0FAzVZs40HWbMa6SaBiu/2z2ysn/k9W5PCmwYcR41aIi4UED7UpNnToief3lLSwcG62NdZR4Yy8rWndno9lCI+qs9FF3riE2nrB8OmUXH/qQJa+Tl6yaxHSRyr1+Rd/S797/fOfz6pWZJwwrnq8D0SrG0ZNK1MgpVui5itRC5PWKjhZDxxgbtCdmRpNJ+g1v/IQAGVgfNDH21VB4+ySbyhMaRBbfqi21l8GbeJOEi/xAkOvP3ROtd4qODNxJ6nai/ZhdkEyAyicGdsizGc8MQDL26iK8M7J+qy1HHl5WmbqAIw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=WELUG5+U6wF1sL6LbvJfNMZ3VhC2CrqxaAU3hx/XIxQ=; b=k0S79l6XV2l/JsYQL3LnXrTswFpMmwtyEOWt7GzU/psgi91uca2QTvp15Z44U13PR1OyEEMh6oYCUiELe6XVCgzDN1WuVHkHUuCkCKTyL9qvQA6+N8bbGjmWUsnld0wQ0g21g9rtZ/73oPN9HpbSEFyBZ+63atI3FJn4zNOMoVXUxL9sX9Gk4p/yDCAkG2bYwEp9Dfe9CMUTSJ4i1+vq92+7DCuULPCTqI5IqZ4JC8t/A5P5Y4DL5uFVbz1+asXu8r9GzHa7tEY0YlW8S4477F2h7Rm76/3OTs4Q4SqOSWqZzi8Tw6kV2LhojyujziZrEAHkK8f2rqH6oXd3d5rNWQ== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0191.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:8::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:37:59 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:37:59 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 20/21] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles Thread-Index: AQHX7g4zmUX/ecR95Ea19Z1j4vOU/Q== Date: Fri, 10 Dec 2021 21:37:59 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> <75bb9ceffef9e8e2d05ffce8a51c1991d5d3a973.1639172095.git.softworkz@hotmail.com> <554aab35996d92c90ab3b0ecb2c0c6c1278479d6.1639172095.git.softworkz@hotmail.com> <65992926f9356948d7f937ab852f8320b3da4bd9.1639172095.git.softworkz@hotmail.com> <4446daddbc3631c50d69005b790a24e60797a357.1639172095.git.softworkz@hotmail.com> <947dfa704e42ef5c706880efbe9daa17503c8f87.1639172095.git.softworkz@hotmail.com> <778a5b5f127d420c2e518303518efc56568efc60.1639172095.git.softworkz@hotmail.com> In-Reply-To: <778a5b5f127d420c2e518303518efc56568efc60.1639172095.git.softworkz@hotmail.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [0JV5gHbiv2QHEYjAcIcqToDKXFojsFWV] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 6c69bc3f-d957-49ce-beb1-08d9bc255632 x-ms-traffictypediagnostic: DM8P223MB0191:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: zhzTutZCIGFqwxigAMThBwtA6zsUZL35oxa0sRbVycxQSbh/0OtDwBj5jPXMzasRXMMAmugmAYQoKsoZHdFIWXuwQfjp8xsShlyAGlC0AjxuPKoRMR2A2Pi7kSHUeUIOLVb+9j6enzIYgUn2mooPIgdyDtKX80UotwtqkjcDD8BV7D+rZJSpo60kxrO49I1L2zEFa1ZNN/e36jcEr5FPOD/f85fG4iWOH+PeRV0yIVLXKUBTBAtO4xyaQpfAI0xDCCL8Q/LFJjHcB9C6O4U3JnsmU/llaP/af7/JpI8N5zVgBpKLNF3PE2WZKz8dECZkFSi/SlHNLb239OnRPza5nG1YZX8keMwohD7fQMuxirvdLeL97yPsNXt2VEVikVALN3FYH8LnqfBtbKryByhxaajV1RSqcXqGupSj/72fLNEKhNi76PUlOUc4CGosGKQJ0mVjp9wfOzTLsv/0axBwvPM/KUayTUOlSn22d9LtFuWWk46qbRq9nPsuQJkIv+rR9tuWyl67GuzxJMzSGySSuuH3Gg6p/15MgdGQOpXquMuKD+mQnOEM/kyIhlkdZgq/wJVAjulDWx0kU2Qq+6D5FbHP6nS0hrcezSybCY1Fr9rUMIdoSI4741XXY4pymkiO x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?dPFm4zLhb+7TP4cm/XM4Od/7oyfL?= =?utf-8?q?uY02IwJ1r9366p5Cm2IABOqmg31QOMyg11iqXD0TgFHQkWX4B3WGEuYpNKevMZF6K?= =?utf-8?q?p9S3kgBVWdDpHvWm8DTPv9HkviSiYppx6Cz7yaddlaA1ormKFgEfAfgZvO9l2e47g?= =?utf-8?q?D/CvWPxeh2lgK4T7qULcwsGAraeI887rpA3pymqWhnpEJhhvV9L5fv9YbZREsgOFx?= =?utf-8?q?pIJMHc5Unj1FNMDcdJEWjYZFI8+8uqlILJ1/zA9NVhfI0m2srCDb/5r2zj6rkPkV3?= =?utf-8?q?VOBRth+6fw9WQrVgoM67hChs2nKa711peFQ3LlheEwc25DxSOam9qA8CInndoobv/?= =?utf-8?q?iLUilXNYbWlbo+Wvx7NQlXdv6KVr0ZOGnzX0adbG+uQKcXLbrVT/p9scF1araVQc6?= =?utf-8?q?Ec4Jo3N5BrLBVktmMXcgsZkfV4VZlTkorTQcLZ9ogo11/gL6S1QLzff5ttJzw4yMl?= =?utf-8?q?l9+IuscKskotj+gGSjn9yu0H7OFR+mJCRYi+NNdS0vwEbQcW42PZA/4l+ihqIjL4z?= =?utf-8?q?oxlvDFFQkJ4GZgRfUvQSpfwxx1WAsIV975XOUCMHK2iZYD/7EglDd2bG/hTMKbSwp?= =?utf-8?q?8N3vCpI/Mlu+c8SWAgti7XkwYIQxtBh7NFTLfzUUYYAakDAb+ilPRg0+iRaEFj+cx?= =?utf-8?q?U+8gnFJdRJavt4U1ZbAaXj8QnwNz1HQrLUJfJTANbj2ZBjFXv+9/ZuueraLDng4Dt?= =?utf-8?q?62H5j2sOBJ8ZSlr1zog0203wvXH78qsLyW5MDdVSoa1alhrIsD0QdC+bah+o0Cq5H?= =?utf-8?q?B5QBSZTFR2+gMXV3wSiMrrBu1yXbeuy45CbdYu58mUQhQW+2cMpK+XlSWzFF+Ckb2?= =?utf-8?q?mLIA8WZ5zohMmEGMUO9Rxys3lbr1RF5+5+L0bR9qYJoji8E6jVFNemzSmUvt1+Gtu?= =?utf-8?q?pKg8GtJJHFhsFtsX1PIhmBbnrYnQUTpy867uo65/WjyA=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 6c69bc3f-d957-49ce-beb1-08d9bc255632 X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:37:59.4309 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0191 Subject: [FFmpeg-devel] [PATCH v23 20/21] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: P+9p4csj2xiu Signed-off-by: softworkz --- configure | 1 + doc/filters.texi | 164 +++++++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/sf_subscale.c | 883 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 1050 insertions(+) create mode 100644 libavfilter/sf_subscale.c diff --git a/configure b/configure index 360e91d762..4be4303380 100755 --- a/configure +++ b/configure @@ -3706,6 +3706,7 @@ sr_filter_deps="avformat swscale" sr_filter_select="dnn" stereo3d_filter_deps="gpl" splitcc_filter_deps="avcodec" +subscale_filter_deps="swscale avcodec" subtitles_filter_deps="avformat avcodec libass" super2xsai_filter_deps="gpl" pixfmts_super2xsai_test_deps="super2xsai_filter" diff --git a/doc/filters.texi b/doc/filters.texi index 26bf6014d0..8c49f79f86 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -26226,6 +26226,170 @@ Set the rendering margin in pixels. For rendering, alway use the latest event only, which is covering the given point in time. @end table +@section subscale + +Provides high-quality scaling and rearranging functionality for graphical subtitles. + +The subscale filter provides multiple approaches for manipulating +the size and position of graphical subtitle rectangles wich can +be combined or used separately. +Scaling is performed by converting the palettized subtitle bitmaps +to RGBA and re-quantization to palette colors afterwards via elbg algorithm. + +The two major operations are 'scale' and 're-arrange' with the +latter being separated as 'arrange_h' and 'arrange_v'. + + +Inputs: +- 0: Subtitles [bitmap] + +Outputs: +- 0: Subtitles [bitmap] + +It accepts the following parameters: + +@table @option + +@item w, width +Set the width of the output. +Width and height in case of graphical subtitles are just indicating +a virtual size for which the output (consisting of 0-n bitmap rectangles) +is intended to be displayed on. + +@item h, height +Set the height of the output. + +@item margin_h +Sets a horizontal margin to be preserverved when using any +of the arrange modes. + +@item margin_v +Sets a vertical margin to be preserverved when using any +of the arrange modes. + +@item force_original_aspect_ratio +Enable decreasing or increasing output video width or height if necessary to +keep the original aspect ratio. Possible values: + +@table @samp +@item disable +Scale the video as specified and disable this feature. + +@item decrease +The output video dimensions will automatically be decreased if needed. + +@item increase +The output video dimensions will automatically be increased if needed. + +@end table + + +@item scale_mode +Specifies how subtitle bitmaps should be scaled. +The scale factor is determined by the the factor between input +and output size. + +@table @samp +@item none +Do not apply any common scaling. + +@item uniform +Uniformly scale all subtitle bitmaps including their positions. + +@item uniform_no_reposition +Uniformly scale all subtitle bitmaps without changing positions. + +@end table + + +@item arrange_h +Specifies how subtitle bitmaps should be arranged horizontally. + +@item arrange_v +Specifies how subtitle bitmaps should be arranged vertically. + + +@table @samp +@item none +Do not rearrange subtitle bitmaps. + +@item margin_no_scale +Move subtitle bitmaps to be positioned inside the specified +margin (margin_h or margin_v) when possible and without scaling. + +@item margin_and_scale +Move subtitle bitmaps to be positioned inside the specified +margin (margin_h or margin_v) and scale in case it doesn't fit. + +@item snapalign_no_scale +Categorize subtitle bitmap positions as one of left/center/right +or top/bottom/middle based on original positioning and apply +these alignments for the target positioning. +No scaling will be applied. + +@item snapalign_and_scale +Categorize subtitle bitmap positions as one of left/center/right +or top/bottom/middle based on original positioning and apply +these alignments for the target positioning. +Bitmaps that do not fit inside the margins borders are +scaled to fit. +@end table + +@item eval +Set evaluation mode for the expressions (@option{width}, @option{height}). + +It accepts the following values: +@table @samp +@item init +Evaluate expressions only once during the filter initialization. + +@item frame +Evaluate expressions for each incoming frame. This is way slower than the +@samp{init} mode since it requires all the scalers to be re-computed, but it +allows advanced dynamic expressions. +@end table + +Default value is @samp{init}. + + +@item num_colors +Set the number of palette colors for output images. +Choose the maximum (256) when further processing is done (e.g. +overlaying on a video). +When subtitles will be encoded as bitmap subtitles (e.g. dvbsub), +a smaller number of palette colors (e.g. 4-16) might need to be used, depending +on the target format and codec. + +@item bitmap_width_align +@item bitmap_height_align +Make sure that subtitle bitmap sizes are a multiple of this +value. Default is 2. + +@end table + +@subsection Examples + +@itemize +@item +Uniformly scale down video and bitmap subtitles and encode +subtitles as dvbsub. +@example +ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=480x270[vid1];[0:10]subscale=w=480:h=270:num_colors=16[sub1]" -map [vid1] -map [sub1] -c:s dvbsub -c:v libx265 output.ts +@end example +@item +Squeeze video vertically and arrange subtitle bitmaps +inside the video area without scaling, then overlay +subtitles onto the video. +@example +ffmpeg -y -loglevel verbose -ss 32 -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=1920x400,setsar=1[vid1];[0:10]subscale=w=1920:h=400:scale_mode=none:margin_h=50:arrange_h=margin_no_scale:arrange_v=margin_no_scale:margin_v=50:num_colors=256[sub1];[vid1][sub1]overlay_graphicsubs" -c:v libx265 output.ts +@end example +@item +Scale both, a video and its embedded VOB subs, then encode them as separate streams into an MKV. +@example +ffmpeg -y -y -loglevel verbose -i "https://streams.videolan.org/samples/sub/largeres_vobsub.mkv" -filter_complex "[0:0]scale=720x576[vid1];[0:7]subscale=w=720:h=576:num_colors=16[sub1]" -map [vid1] -map [sub1] -c:s dvdsub out.mkv +@end example +@end itemize + @c man end SUBTITLE FILTERS @chapter Multimedia Filters diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 3b972e134b..122e87f25d 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -561,6 +561,7 @@ OBJS-$(CONFIG_SHOW_SPEAKER_FILTER) += sf_textmod.o OBJS-$(CONFIG_TEXTMOD_FILTER) += sf_textmod.o OBJS-$(CONFIG_SPLITCC_FILTER) += sf_splitcc.o OBJS-$(CONFIG_STRIPSTYLES_FILTER) += sf_stripstyles.o +OBJS-$(CONFIG_SUBSCALE_FILTER) += sf_subscale.o # multimedia filters OBJS-$(CONFIG_ABITSCOPE_FILTER) += avf_abitscope.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index f70f08dc5a..246272f5cd 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -549,6 +549,7 @@ extern const AVFilter ff_sf_graphicsub2text; extern const AVFilter ff_sf_showspeaker; extern const AVFilter ff_sf_splitcc; extern const AVFilter ff_sf_stripstyles; +extern const AVFilter ff_sf_subscale; extern const AVFilter ff_sf_textmod; extern const AVFilter ff_svf_graphicsub2video; extern const AVFilter ff_svf_textsub2video; diff --git a/libavfilter/sf_subscale.c b/libavfilter/sf_subscale.c new file mode 100644 index 0000000000..072312c913 --- /dev/null +++ b/libavfilter/sf_subscale.c @@ -0,0 +1,883 @@ +/* + * Copyright (c) 2021 softworkz + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * scale graphical subtitles filter + */ + +#include + +#include "drawutils.h" +#include "internal.h" +#include "scale_eval.h" +#include "libavutil/eval.h" +#include "libavutil/opt.h" +#include "libswscale/swscale.h" + +#include "libavcodec/elbg.h" + +static const char *const var_names[] = { + "in_w", "iw", + "in_h", "ih", + "out_w", "ow", + "out_h", "oh", + "a", + "sar", + "dar", + "margin_h", + "margin_v", + NULL +}; + +enum var_name { + VAR_IN_W, VAR_IW, + VAR_IN_H, VAR_IH, + VAR_OUT_W, VAR_OW, + VAR_OUT_H, VAR_OH, + VAR_A, + VAR_SAR, + VAR_DAR, + VARS_B_H, + VARS_B_V, + VARS_NB +}; + +enum EvalMode { + EVAL_MODE_INIT, + EVAL_MODE_FRAME, + EVAL_MODE_NB +}; + +enum SubScaleMode { + SSM_NONE, + SSM_UNIFORM, + SSM_UNIFORM_NO_REPOSITION, +}; + +enum SubArrangeMode { + SAM_NONE, + SAM_ENSUREMARGIN_NO_SCALE, + SAM_ENSUREMARGIN_AND_SCALE, + SAM_SNAPALIGNMENT_NO_SCALE, + SAM_SNAPALIGNMENT_AND_SCALE, +}; + +typedef struct SubScaleContext { + const AVClass *class; + struct SwsContext *sws; + AVDictionary *opts; + + int w, h; + + char *w_expr; ///< width expression string + char *h_expr; ///< height expression string + AVExpr *w_pexpr; + AVExpr *h_pexpr; + double var_values[VARS_NB]; + + int force_original_aspect_ratio; + int eval_mode; ///< expression evaluation mode + + int use_caching; + + // Scale Options + enum SubScaleMode scale_mode; + + // Arrange Options + enum SubArrangeMode arrange_mode_h; + enum SubArrangeMode arrange_mode_v; + int margin_h; + int margin_v; + char *margin_h_expr; + char *margin_v_expr; + AVExpr *margin_h_pexpr; + AVExpr *margin_v_pexpr; + + // Bitmap Options + int num_output_colors; + int bitmap_width_align; + int bitmap_height_align; + + // Color Quantization Fields + struct ELBGContext *ctx; + AVLFG lfg; + int *codeword; + int *codeword_closest_codebook_idxs; + int *codebook; + int r_idx, g_idx, b_idx, a_idx; + AVFrame *cache_frame; +} SubScaleContext; + + +static int config_output(AVFilterLink *outlink); + +static int check_exprs(AVFilterContext *ctx) +{ + const SubScaleContext *s = ctx->priv; + unsigned vars_w[VARS_NB] = { 0 }, vars_h[VARS_NB] = { 0 }; + + if (!s->w_pexpr && !s->h_pexpr) + return AVERROR(EINVAL); + + if (s->w_pexpr) + av_expr_count_vars(s->w_pexpr, vars_w, VARS_NB); + if (s->h_pexpr) + av_expr_count_vars(s->h_pexpr, vars_h, VARS_NB); + + if (vars_w[VAR_OUT_W] || vars_w[VAR_OW]) { + av_log(ctx, AV_LOG_ERROR, "Width expression cannot be self-referencing: '%s'.\n", s->w_expr); + return AVERROR(EINVAL); + } + + if (vars_h[VAR_OUT_H] || vars_h[VAR_OH]) { + av_log(ctx, AV_LOG_ERROR, "Height expression cannot be self-referencing: '%s'.\n", s->h_expr); + return AVERROR(EINVAL); + } + + if ((vars_w[VAR_OUT_H] || vars_w[VAR_OH]) && + (vars_h[VAR_OUT_W] || vars_h[VAR_OW])) { + av_log(ctx, AV_LOG_WARNING, "Circular references detected for width '%s' and height '%s' - possibly invalid.\n", s->w_expr, s->h_expr); + } + + if (s->margin_h_pexpr) + av_expr_count_vars(s->margin_h_pexpr, vars_w, VARS_NB); + if (s->margin_v_pexpr) + av_expr_count_vars(s->margin_v_pexpr, vars_h, VARS_NB); + + return 0; +} + +static int scale_parse_expr(AVFilterContext *ctx, char *str_expr, AVExpr **pexpr_ptr, const char *var, const char *args) +{ + SubScaleContext *s = ctx->priv; + int ret, is_inited = 0; + char *old_str_expr = NULL; + AVExpr *old_pexpr = NULL; + + if (str_expr) { + old_str_expr = av_strdup(str_expr); + if (!old_str_expr) + return AVERROR(ENOMEM); + av_opt_set(s, var, args, 0); + } + + if (*pexpr_ptr) { + old_pexpr = *pexpr_ptr; + *pexpr_ptr = NULL; + is_inited = 1; + } + + ret = av_expr_parse(pexpr_ptr, args, var_names, + NULL, NULL, NULL, NULL, 0, ctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Cannot parse expression for %s: '%s'\n", var, args); + goto revert; + } + + ret = check_exprs(ctx); + if (ret < 0) + goto revert; + + if (is_inited && (ret = config_output(ctx->outputs[0])) < 0) + goto revert; + + av_expr_free(old_pexpr); + av_freep(&old_str_expr); + + return 0; + +revert: + av_expr_free(*pexpr_ptr); + *pexpr_ptr = NULL; + if (old_str_expr) { + av_opt_set(s, var, old_str_expr, 0); + av_free(old_str_expr); + } + if (old_pexpr) + *pexpr_ptr = old_pexpr; + + return ret; +} + +static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts) +{ + SubScaleContext *s = ctx->priv; + uint8_t rgba_map[4]; + int ret; + + if (!s->w_expr) + av_opt_set(s, "w", "iw", 0); + if (!s->h_expr) + av_opt_set(s, "h", "ih", 0); + + ret = scale_parse_expr(ctx, NULL, &s->w_pexpr, "width", s->w_expr); + if (ret < 0) + return ret; + + ret = scale_parse_expr(ctx, NULL, &s->h_pexpr, "height", s->h_expr); + if (ret < 0) + return ret; + + av_log(ctx, AV_LOG_VERBOSE, "w:%s h:%s\n", + s->w_expr, s->h_expr); + + if (!s->margin_h_expr) + av_opt_set(s, "margin_h", "0", 0); + if (!s->margin_v_expr) + av_opt_set(s, "margin_v", "0", 0); + + ret = scale_parse_expr(ctx, NULL, &s->margin_h_pexpr, "margin_h", s->margin_h_expr); + if (ret < 0) + return ret; + + ret = scale_parse_expr(ctx, NULL, &s->margin_v_pexpr, "margin_v", s->margin_v_expr); + if (ret < 0) + return ret; + + s->opts = *opts; + *opts = NULL; + + ff_fill_rgba_map(&rgba_map[0], AV_PIX_FMT_RGB32); + + s->r_idx = rgba_map[0]; // R + s->g_idx = rgba_map[1]; // G + s->b_idx = rgba_map[2]; // B + s->a_idx = rgba_map[3]; // A + + av_lfg_init(&s->lfg, 123456789); + + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + SubScaleContext *s = ctx->priv; + + av_frame_free(&s->cache_frame); + + av_expr_free(s->w_pexpr); + av_expr_free(s->h_pexpr); + s->w_pexpr = s->h_pexpr = NULL; + + av_expr_free(s->margin_h_pexpr); + av_expr_free(s->margin_v_pexpr); + s->margin_h_pexpr = s->margin_v_pexpr = NULL; + + sws_freeContext(s->sws); + s->sws = NULL; + av_dict_free(&s->opts); + + avpriv_elbg_free(&s->ctx); + + av_freep(&s->codebook); + av_freep(&s->codeword); + av_freep(&s->codeword_closest_codebook_idxs); +} + +static int config_input(AVFilterLink *inlink) +{ + ////const AVFilterContext *ctx = inlink->dst; + ////SubScaleContext *s = ctx->priv; + + ////if (s->w <= 0 || s->h <= 0) { + //// s->w = inlink->w; + //// s->h = inlink->h; + ////} + return 0; +} + +static int scale_eval_dimensions(AVFilterContext *ctx) +{ + SubScaleContext *s = ctx->priv; + const AVFilterLink *inlink = ctx->inputs[0]; + char *expr; + int eval_w, eval_h, margin_h, margin_v; + int ret; + double res; + + s->var_values[VAR_IN_W] = s->var_values[VAR_IW] = inlink->w; + s->var_values[VAR_IN_H] = s->var_values[VAR_IH] = inlink->h; + s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = NAN; + s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = NAN; + s->var_values[VARS_B_H] = s->var_values[VARS_B_V] = 0; + s->var_values[VAR_A] = (double) inlink->w / inlink->h; + s->var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? + (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1; + s->var_values[VAR_DAR] = s->var_values[VAR_A] * s->var_values[VAR_SAR]; + + res = av_expr_eval(s->w_pexpr, s->var_values, NULL); + s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res; + + res = av_expr_eval(s->h_pexpr, s->var_values, NULL); + if (isnan(res)) { + expr = s->h_expr; + ret = AVERROR(EINVAL); + goto fail; + } + eval_h = s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = (int) res == 0 ? inlink->h : (int) res; + + res = av_expr_eval(s->w_pexpr, s->var_values, NULL); + if (isnan(res)) { + expr = s->w_expr; + ret = AVERROR(EINVAL); + goto fail; + } + eval_w = s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res; + + s->w = eval_w; + s->h = eval_h; + + res = av_expr_eval(s->margin_h_pexpr, s->var_values, NULL); + s->var_values[VARS_B_H] = (int)res; + + res = av_expr_eval(s->margin_v_pexpr, s->var_values, NULL); + if (isnan(res)) { + expr = s->margin_v_expr; + ret = AVERROR(EINVAL); + goto fail; + } + margin_v = s->var_values[VARS_B_V] = (int)res; + + res = av_expr_eval(s->margin_h_pexpr, s->var_values, NULL); + if (isnan(res)) { + expr = s->margin_h_expr; + ret = AVERROR(EINVAL); + goto fail; + } + margin_h = s->var_values[VARS_B_H] = (int)res; + + s->margin_h = margin_h; + s->margin_v = margin_v; + + return 0; + +fail: + av_log(ctx, AV_LOG_ERROR, + "Error when evaluating the expression '%s'.\n", expr); + return ret; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AVFilterLink *inlink = outlink->src->inputs[0]; + SubScaleContext *s = ctx->priv; + int ret; + + outlink->format = AV_SUBTITLE_FMT_BITMAP; + outlink->time_base = ctx->inputs[0]->time_base; + + if ((ret = scale_eval_dimensions(ctx)) < 0) + goto fail; + + ff_scale_adjust_dimensions(inlink, &s->w, &s->h, + s->force_original_aspect_ratio, 2); + + if (s->w > INT_MAX || + s->h > INT_MAX || + (s->h * inlink->w) > INT_MAX || + (s->w * inlink->h) > INT_MAX) + av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n"); + + outlink->w = s->w; + outlink->h = s->h; + + if (s->sws) + sws_freeContext(s->sws); + + s->sws = sws_alloc_context(); + if (!s->sws) + return AVERROR(ENOMEM); + + av_opt_set_pixel_fmt(s->sws, "src_format", AV_PIX_FMT_PAL8, 0); + av_opt_set_int(s->sws, "dst_format", AV_PIX_FMT_RGB32, 0); + av_opt_set_int(s->sws, "threads", ff_filter_get_nb_threads(ctx), 0); + + if (s->opts) { + const AVDictionaryEntry *e = NULL; + while ((e = av_dict_get(s->opts, "", e, AV_DICT_IGNORE_SUFFIX))) { + if ((ret = av_opt_set(s->sws, e->key, e->value, 0)) < 0) + return ret; + } + } + + if ((ret = sws_init_context(s->sws, NULL, NULL)) < 0) + return ret; + + if (inlink->sample_aspect_ratio.num){ + outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h * inlink->w, outlink->w * inlink->h}, inlink->sample_aspect_ratio); + } else + outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; + + av_log(ctx, AV_LOG_VERBOSE, "Output size set to %dx%d.\n", outlink->w, outlink->h); + + return 0; +fail: + return ret; +} + +static int palettize_image(SubScaleContext *const s, const int w, const int h, const int src_linesize, uint8_t *src_data, + int dst_linesize, uint8_t *dst_data, uint32_t *dst_pal) +{ + int k, ret; + const int codeword_length = w * h; + + /* Re-Initialize */ + s->codeword = av_realloc_f(s->codeword, codeword_length, 4 * sizeof(*s->codeword)); + if (!s->codeword) + return AVERROR(ENOMEM); + + s->codeword_closest_codebook_idxs = + av_realloc_f(s->codeword_closest_codebook_idxs, codeword_length, + sizeof(*s->codeword_closest_codebook_idxs)); + if (!s->codeword_closest_codebook_idxs) + return AVERROR(ENOMEM); + + s->codebook = av_realloc_f(s->codebook, s->num_output_colors, 4 * sizeof(*s->codebook)); + if (!s->codebook) + return AVERROR(ENOMEM); + + /* build the codeword */ + k = 0; + for (int i = 0; i < h; i++) { + uint8_t *p = src_data; + for (int j = 0; j < w; j++) { + s->codeword[k++] = p[s->b_idx]; + s->codeword[k++] = p[s->g_idx]; + s->codeword[k++] = p[s->r_idx]; + s->codeword[k++] = p[s->a_idx]; + p += 4; + } + src_data += src_linesize; + } + + /* compute the codebook */ + ret = avpriv_elbg_do(&s->ctx, s->codeword, 4, + codeword_length, s->codebook, + s->num_output_colors, 1, + s->codeword_closest_codebook_idxs, &s->lfg, 0); + + if (ret < 0) + return ret; + + /* Write Palette */ + for (int i = 0; i < s->num_output_colors; i++) { + dst_pal[i] = s->codebook[i*4+3] << 24 | + (s->codebook[i*4+2] << 16) | + (s->codebook[i*4+1] << 8) | + s->codebook[i*4 ]; + } + + /* Write Image */ + k = 0; + for (int i = 0; i < h; i++) { + uint8_t *p = dst_data; + for (int j = 0; j < w; j++, p++) { + p[0] = s->codeword_closest_codebook_idxs[k++]; + } + + dst_data += dst_linesize; + } + + return ret; +} + +static int rescale_size(int64_t a, AVRational factor) +{ + const int64_t res = av_rescale_rnd(a, factor.num, factor.den, AV_ROUND_NEAR_INF); + if (res > INT32_MAX || res < 0) + return 0; + + return (int)res; +} + + +static int scale_area(AVFilterLink *link, AVSubtitleArea *area, const int target_width, const int target_height) +{ + const AVFilterContext *ctx = link->dst; + SubScaleContext *s = ctx->priv; + int ret; + + AVBufferRef *dst_buffer; + const uint8_t* data[2] = { area->buf[0]->data, (uint8_t *)&area->pal }; + const int dstW = FFALIGN(target_width, s->bitmap_width_align); + const int dstH = FFALIGN(target_height, s->bitmap_height_align); + const int tmp_linesize[2] = { FFALIGN(dstW * 4, 32), 0 }; + const int dst_linesize[2] = { dstW, 0 }; + uint8_t* tmp[2] = { 0, 0 }; + + AVBufferRef *tmp_buffer = av_buffer_allocz(tmp_linesize[0] * dstH); + if (!tmp_buffer) + return AVERROR(ENOMEM); + + if (!s->sws) + return 0; + + tmp[0] = tmp_buffer->data; + + s->sws = sws_getCachedContext(s->sws, area->w, area->h, AV_PIX_FMT_PAL8, + dstW, dstH, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL); + if (!s->sws) { + av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context. dstW=%d dstH=%d\n", dstW, dstH); + return AVERROR(EINVAL); + } + + // Rescale to ARGB + ret = sws_scale(s->sws, data, area->linesize, 0, area->h, tmp, tmp_linesize); + if (ret < 0) { + av_buffer_unref(&tmp_buffer); + return ret; + } + + // Alloc output buffer + dst_buffer = av_buffer_allocz(dst_linesize[0] * dstH); + if (!dst_buffer) { + av_buffer_unref(&tmp_buffer); + return AVERROR(ENOMEM); + } + + // Quantize to palettized image + ret = palettize_image(s, dstW, dstH, tmp_linesize[0], tmp[0], dst_linesize[0], dst_buffer->data, area->pal); + av_buffer_unref(&tmp_buffer); + + if (ret < 0) { + av_buffer_unref(&dst_buffer); + return ret; + } + + av_buffer_unref(&area->buf[0]); + ret = av_buffer_replace(&area->buf[0], dst_buffer); + if (ret < 0) { + av_buffer_unref(&dst_buffer); + return ret; + } + + area->w = dstW; + area->h = dstH; + area->linesize[0] = dst_linesize[0]; + area->nb_colors = s->num_output_colors; + + return ret; +} + +static int process_area(AVFilterLink *inlink, AVSubtitleArea *area, AVRational x_factor, AVRational y_factor) +{ + AVFilterContext *ctx = inlink->dst; + const SubScaleContext *s = ctx->priv; + int target_w, target_h, target_x, target_y; + const int border_l = s->margin_h; + const int border_r = s->w - s->margin_h; + const int border_t = s->margin_v; + const int border_b = s->h - s->margin_v; + + av_log(ctx, AV_LOG_DEBUG, "process_area - start: x/y: (%d:%d) size: %dx%d scale_mode: %d x-factor: %d:%d y-factor: %d:%d\n", + area->x, area->y, area->w, area->h, s->scale_mode, x_factor.num, x_factor.den, y_factor.num, y_factor.den); + + switch (s->scale_mode) { + case SSM_NONE: + target_w = area->w; + target_h = area->h; + target_x = area->x; + target_y = area->y; + break; + case SSM_UNIFORM: + target_w = rescale_size(area->w, x_factor); + target_h = rescale_size(area->h, y_factor); + target_x = rescale_size(area->x, x_factor); + target_y = rescale_size(area->y, y_factor); + break; + case SSM_UNIFORM_NO_REPOSITION: + target_w = rescale_size(area->w, x_factor); + target_h = rescale_size(area->h, y_factor); + target_x = area->x; + target_y = area->y; + break; + default: + av_log(ctx, AV_LOG_ERROR, "Invalid scale_mode: %d\n", s->scale_mode); + return AVERROR(EINVAL); + } + + av_log(ctx, AV_LOG_DEBUG, "process_area - scaled: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h); + + + switch (s->arrange_mode_h) { + case SAM_ENSUREMARGIN_AND_SCALE: + case SAM_SNAPALIGNMENT_AND_SCALE: + { + // IF it doesn't fit - scale it + int max_width = s->w - 2 * s->margin_h; + + if (max_width < 2) + max_width = 2; + + if (target_w > max_width) { + target_h = (int)av_rescale(target_h, max_width, target_w); + target_w = max_width; + target_x = s->margin_h; + } + } + break; + } + + switch (s->arrange_mode_h) { + case SAM_NONE: + break; + case SAM_ENSUREMARGIN_NO_SCALE: + case SAM_ENSUREMARGIN_AND_SCALE: + // Left border + if (target_x < border_l) + target_x = border_l; + + // Right border + if (target_x + target_w > border_r) + target_x = border_r - target_w; + + break; + case SAM_SNAPALIGNMENT_NO_SCALE: + case SAM_SNAPALIGNMENT_AND_SCALE: + { + // Use original values to detect alignment + const int left_margin = area->x; + const int right_margin = inlink->w - area->x - area->w; + const AVRational diff_factor_r = { left_margin - right_margin, area->w }; + const float diff_factor = (float)av_q2d(diff_factor_r); + + if (diff_factor > 0.2f) { + // Right aligned + target_x = border_r - target_w; + } else if (diff_factor < -0.2f) { + // Left aligned + target_x = border_l; + } else { + // Centered + target_x = (inlink->w - area->w) / 2; + } + } + + break; + default: + av_log(ctx, AV_LOG_ERROR, "Invalid arrange_mode_h: %d\n", s->arrange_mode_h); + return AVERROR(EINVAL); + } + + av_log(ctx, AV_LOG_DEBUG, "process_area - arr_h: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h); + + + switch (s->arrange_mode_v) { + case SAM_ENSUREMARGIN_AND_SCALE: + case SAM_SNAPALIGNMENT_AND_SCALE: + { + // IF it doesn't fit - scale it + int max_height = s->h - 2 * s->margin_v; + + if (max_height < 2) + max_height = 2; + + if (target_h > max_height) { + target_w = (int)av_rescale(target_w, max_height, target_h); + target_h = max_height; + target_y = s->margin_v; + } + } + break; + } + + switch (s->arrange_mode_v) { + case SAM_NONE: + break; + case SAM_ENSUREMARGIN_NO_SCALE: + case SAM_ENSUREMARGIN_AND_SCALE: + // Top border + if (target_y < border_t) + target_y = border_t; + + // Bottom border + if (target_y + target_h > border_b) + target_y = border_b - target_h; + + break; + case SAM_SNAPALIGNMENT_NO_SCALE: + case SAM_SNAPALIGNMENT_AND_SCALE: + { + // Use original values to detect alignment + const int top_margin = area->y; + const int bottom_margin = inlink->h - area->y - area->h; + const AVRational diff_factor_r = { top_margin - bottom_margin, area->h }; + const float diff_factor = (float)av_q2d(diff_factor_r); + + if (diff_factor > 0.2f) { + // Bottom aligned + target_y = border_b - target_h; + } else if (diff_factor < -0.2f) { + // Top aligned + target_y = border_t; + } else { + // Centered + target_y = (inlink->h - area->h) / 2; + } + } + + break; + default: + av_log(ctx, AV_LOG_ERROR, "Invalid arrange_mode_v: %d\n", s->arrange_mode_v); + return AVERROR(EINVAL); + } + + av_log(ctx, AV_LOG_VERBOSE, "process_area - arr_v: x/y: (%d:%d) size: %dx%d.\n", target_x, target_y, target_w, target_h); + + area->x = target_x; + area->y = target_y; + + if (area->w != target_w || area->h != target_h) + return scale_area(inlink, area, target_w, target_h); + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + const AVFilterContext *ctx = inlink->dst; + SubScaleContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + int ret; + + // just forward empty frames + if (frame->num_subtitle_areas == 0) { + av_frame_free(&s->cache_frame); + return ff_filter_frame(outlink, frame); + } + + if (s->use_caching && s->cache_frame && s->cache_frame->subtitle_pts + && s->cache_frame->subtitle_pts == frame->subtitle_pts) { + AVFrame *out = av_frame_clone(s->cache_frame); + if (!out) + return AVERROR(ENOMEM); + + ret = av_frame_copy_props(out, frame); + if (ret < 0) + return ret; + + av_log(inlink->dst, AV_LOG_DEBUG, "subscale CACHED - size %dx%d pts: %"PRId64" areas: %d\n", frame->width, frame->height, frame->subtitle_pts, frame->num_subtitle_areas); + av_frame_free(&frame); + return ff_filter_frame(outlink, out); + } + + ret = av_frame_make_writable(frame); + if (ret >= 0) { + const AVRational x_factor = { .num = outlink->w, .den = inlink->w} ; + const AVRational y_factor = { .num = outlink->h, .den = inlink->h} ; + + for (unsigned i = 0; i < frame->num_subtitle_areas; ++i) { + AVSubtitleArea *area = frame->subtitle_areas[i]; + + ret = process_area(inlink, area, x_factor, y_factor); + if (ret < 0) + return ret; + } + + av_log(inlink->dst, AV_LOG_DEBUG, "subscale output - size %dx%d pts: %"PRId64" areas: %d\n", frame->width, frame->height, frame->subtitle_pts, frame->num_subtitle_areas); + + if (s->use_caching) { + av_frame_free(&s->cache_frame); + s->cache_frame = av_frame_clone(frame); + } + + return ff_filter_frame(outlink, frame); + } + + return ret; +} + +#define OFFSET(x) offsetof(SubScaleContext, x) +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM) + +static const AVOption subscale_options[] = { + { "margin_h", "horizontal border", OFFSET(margin_h_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, + { "margin_v", "vertical border", OFFSET(margin_v_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, + { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, + { "width", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, + { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, + { "height","Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, + { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, FLAGS, "force_oar" }, + { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "force_oar" }, + { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "force_oar" }, + { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, "force_oar" }, + { "scale_mode", "specify how to scale subtitles", OFFSET(scale_mode), AV_OPT_TYPE_INT, {.i64 = SSM_UNIFORM}, 0, SSM_UNIFORM_NO_REPOSITION, FLAGS, "scale_mode" }, + { "none", "no common scaling", 0, AV_OPT_TYPE_CONST, {.i64=SSM_NONE}, .flags = FLAGS, .unit = "scale_mode" }, + { "uniform", "uniformly scale and reposition", 0, AV_OPT_TYPE_CONST, {.i64=SSM_UNIFORM}, .flags = FLAGS, .unit = "scale_mode" }, + { "uniform_no_reposition", "uniformly scale but keep positions", 0, AV_OPT_TYPE_CONST, {.i64=SSM_UNIFORM_NO_REPOSITION}, .flags = FLAGS, .unit = "scale_mode" }, + { "use_caching", "Cache output frames", OFFSET(use_caching), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, .flags = FLAGS }, + + { "arrange_h", "specify how to arrange subtitles horizontally", OFFSET(arrange_mode_h), AV_OPT_TYPE_INT, {.i64 = SAM_NONE}, 0, SAM_SNAPALIGNMENT_AND_SCALE, FLAGS, "arrange" }, + { "arrange_v", "specify how to arrange subtitles vertically", OFFSET(arrange_mode_v), AV_OPT_TYPE_INT, {.i64 = SAM_NONE}, 0, SAM_SNAPALIGNMENT_AND_SCALE, FLAGS, "arrange" }, + { "none", "no repositioning", 0, AV_OPT_TYPE_CONST, {.i64=SAM_NONE}, .flags = FLAGS, .unit = "arrange" }, + { "margin_no_scale", "move subs inside border when possible", 0, AV_OPT_TYPE_CONST, {.i64=SAM_ENSUREMARGIN_NO_SCALE,}, .flags = FLAGS, .unit = "arrange" }, + { "margin_and_scale", "move subs inside border and scale as needed", 0, AV_OPT_TYPE_CONST, {.i64=SAM_ENSUREMARGIN_AND_SCALE,}, .flags = FLAGS, .unit = "arrange" }, + { "snapalign_no_scale", "snap subs to near/far/center when possible", 0, AV_OPT_TYPE_CONST, {.i64=SAM_SNAPALIGNMENT_NO_SCALE,}, .flags = FLAGS, .unit = "arrange" }, + { "snapalign_and_scale", "snap subs to near/far/center and scale as needed", 0, AV_OPT_TYPE_CONST, {.i64=SAM_SNAPALIGNMENT_AND_SCALE,}, .flags = FLAGS, .unit = "arrange" }, + + { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" }, + { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" }, + { "frame", "eval expressions during initialization and per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" }, + { "num_colors", "number of palette colors in output", OFFSET(num_output_colors), AV_OPT_TYPE_INT, {.i64 = 256 }, 2, 256, .flags = FLAGS }, + { "bitmap_width_align", "Bitmap width alignment", OFFSET(bitmap_width_align), AV_OPT_TYPE_INT, {.i64 = 2 }, 1, 256, .flags = FLAGS }, + { "bitmap_height_align", "Bitmap height alignment", OFFSET(bitmap_height_align), AV_OPT_TYPE_INT, {.i64 = 2 }, 1, 256, .flags = FLAGS }, + { .name = NULL } +}; + +static const AVClass subscale_class = { + .class_name = "subscale", + .item_name = av_default_item_name, + .option = subscale_options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_FILTER, +}; + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .filter_frame = filter_frame, + .config_props = config_input, + }, +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .config_props = config_output, + }, +}; + +const AVFilter ff_sf_subscale = { + .name = "subscale", + .description = NULL_IF_CONFIG_SMALL("Scale graphical subtitles."), + .init_dict = init_dict, + .uninit = uninit, + .priv_size = sizeof(SubScaleContext), + .priv_class = &subscale_class, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_SINGLE_SUBFMT(AV_SUBTITLE_FMT_BITMAP), +}; + From patchwork Fri Dec 10 21:38:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 32283 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp2375511iog; Fri, 10 Dec 2021 13:40:57 -0800 (PST) X-Google-Smtp-Source: ABdhPJw+Qa+IXyAT1AEAVy2ZjTO8BpZmd/jwwezKNaNn0FiIZFgBW1q97XCn/GDbJThjFqz46edf X-Received: by 2002:a05:6402:1a58:: with SMTP id bf24mr42159793edb.16.1639172457472; Fri, 10 Dec 2021 13:40:57 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id hd42si5752526ejc.443.2021.12.10.13.40.57; Fri, 10 Dec 2021 13:40:57 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=QiR+1L7W; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=hotmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 99E7468AFF7; Fri, 10 Dec 2021 23:38:09 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM12-MW2-obe.outbound.protection.outlook.com (mail-mw2nam12olkn2077.outbound.protection.outlook.com [40.92.23.77]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 9873F68AF10 for ; Fri, 10 Dec 2021 23:38:05 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=GON9v8GFBwolvAHqIS3al+/EJJI7WR5eTSNRcSQuN/jGCYaPWoFuY5p5zwkMQ94VEdNdYDt5NHfxPvS42x6Y80jJsE5TMpsz2uPjMUd1wzVamRrZio/uJvAV330feT0n2mgjb2qltmGfyDFILr40fr7vot4O1YNfdNs1A9wV9oRuSHXDeCJep8UjCU9B3+2Q7thlzLUI5LZJx0vd/a6pZdX7T8jD0BBhadtjykqgX6HqZnkfR8Uo7cK5ihaAisbkfgstJl5EvHkYBDVTLYc2jSKXO7EJDXWLFjtRTUxTVZrM/vDsSDWmwDOF78w6Z3AzuRUuYS/nIjmX9mRFIi/mFQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=Qg87VHD7J7ohvcOJRW+ZSOV9CU+3q4y2bTOGOc46EtM=; b=LcuScQjwS/X+G/4M5sddtns3LFQZ58iB8sESiySS3S/WQaHzD83ljV36e68tfROCSnloOgoRgZU27ODBCrd9vwK/JuafL2Y74aYZ0yz7mQd6yr3dDpGxq4XMckT/90JToVrMIiXiNZLouE0g/xsbw7pAZ9fnqmNUYJWwrBGFUfTBTIiSQxniKfgKgJqlNEZQgZgWmGGMGkOMYnNxUvmd6epbr+iogOv999DFOs2KtGK30J1evbl9HHT/tMtvHOZskyNqjXyHpNk9GHaI8VfpYJGty32SknG9urG21FeqAhAvoyOdgf3oxKzDV0PCdOtVgPIfeKayNvHl7taTieCkgQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Qg87VHD7J7ohvcOJRW+ZSOV9CU+3q4y2bTOGOc46EtM=; b=QiR+1L7Wqf7mORwnZoZRZYb8HkYLpCDtGi48AC7XoLrbvl4nifK765IrvowKcuNiurCu+KzWXWvVlOHZCqhV2yrH95uvUuTw3m2RGMYrewppfYAwr691/Fr8fyib5966NFQCpoln6GUeRsKNxuTIdAzxB3Kjc/R+30ZAv/KNeUA/6pkujZenHzkcA5MnI+kcBgxH+vYpA1ZlokqKf1oL9psL7EO2EhaNEMNnBweTO0CLoNgCVLsLsoxTN9wecHLkUuUQCWLDRDFPG9y0iQPmXnamg3YCLjNjLEoMB6DEV9GWClbOCGIGFAjHl/Hav5xrkPHeLBb1TZ5xPtDltfvyNw== Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:b::20) by DM8P223MB0191.NAMP223.PROD.OUTLOOK.COM (2603:10b6:8:8::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4755.20; Fri, 10 Dec 2021 21:38:00 +0000 Received: from DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775]) by DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM ([fe80::9c8d:fc63:9488:9775%7]) with mapi id 15.20.4778.015; Fri, 10 Dec 2021 21:38:00 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v23 21/21] doc/APIchanges: update for subtitle filtering changes Thread-Index: AQHX7g40uJLecXlWj0m+hp1Jx6EVBA== Date: Fri, 10 Dec 2021 21:38:00 +0000 Message-ID: References: <84c6ba3b99bf387e818d59f2161009ead8fb5767.1639172095.git.softworkz@hotmail.com> <0b41271ed30b530c1d55a9b824fc8bbbbd14fe5e.1639172095.git.softworkz@hotmail.com> <9f2875d043147119351f59b6410d718d170574e1.1639172095.git.softworkz@hotmail.com> <62a926e2d86310039653324bdf2a856f09a0fb46.1639172095.git.softworkz@hotmail.com> <75bb9ceffef9e8e2d05ffce8a51c1991d5d3a973.1639172095.git.softworkz@hotmail.com> <554aab35996d92c90ab3b0ecb2c0c6c1278479d6.1639172095.git.softworkz@hotmail.com> <65992926f9356948d7f937ab852f8320b3da4bd9.1639172095.git.softworkz@hotmail.com> <4446daddbc3631c50d69005b790a24e60797a357.1639172095.git.softworkz@hotmail.com> <947dfa704e42ef5c706880efbe9daa17503c8f87.1639172095.git.softworkz@hotmail.com> <778a5b5f127d420c2e518303518efc56568efc60.1639172095.git.softworkz@hotmail.com> <8909b78a659fb714d5b6932a3dc2809860bb2d55.1639172095.git.softworkz@hotmail.com> In-Reply-To: <8909b78a659fb714d5b6932a3dc2809860bb2d55.1639172095.git.softworkz@hotmail.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [GlRMOvauIDw3dvSyM5FyQuTUyrovOSnY] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 9df21d19-2679-4030-a802-08d9bc2556e9 x-ms-traffictypediagnostic: DM8P223MB0191:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: 559eMKg/BQUPBZDezTc5/6ztW1cURAPERJtWHaNvRHgPbhLyYYw54a2z8OcOrOALOvUFjvuK0uODzYh9e5HF186+U3k1oouHSM9faj5mSH3R/Lfh0bqMRv0dY0KzgfQ6aBhdM+NAG8Hoh28hTB42eAEJJ1qvwT2sEa5NTMTVr3lcUH+lfJmWPWF5cU15Fr2gXt3InOTwdzIoFUqL6/ozxTTM/LMUkoXAmKb5LL2PFQoER7lz9S8uuOTuc+alp/c/rrbIAD/kKLEldPkQVG0bhGeO3VluTi/ygaTXcXSsFQliHYMw3C/M65CX8RdTmwhzIoWjIoDlTbtnXvXRES+hZdJfFfuuTf/7YXZ0KrDQkfonOuOAeJbfdG3LrCFavmlKWoDwBev9QUkJHlvOJtadnCnh3P82vIg24/SgCHhnKMc11V2Ka7dHtpl6CibySQNrBsynfqfUfeb3U6TYrHHTjm+VQjZqrr8wgBasSZ2/l/TZQZ5r/2qSLi3HuHYwMKCq49Y9ij59fbuedX3JcLlVM0SGMHiGN/v9j60vhLtZO8440AS0SO1PIYQHqssR4CzD3AsCQA9+/gZRncy1lDfzGg== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?jn54T3FP3bpsUCwwQZKm/cIJFaeq?= =?utf-8?q?V821o4TcjIXJFf4uqiEx/MD2ZHIXHQ8Vg8nCkT+aX54HEUCx5O3MbaqPbiqdIcL/v?= =?utf-8?q?6RHnMO/3xYaWGmwyH+wjIGF7Sz3/ekkT5zl0bHMpCh6UX8K75m5+gIfk8Tqk5o5u5?= =?utf-8?q?y2/mPDbj1jk1hjHjcs0UnrAwiNMDirSLlAAf0Bs8Dg/RqNaEGCPTlIil32/r7++5V?= =?utf-8?q?ZNn12MTPLqXyZUJ0/Wc7v8qjKMS038a9Yqkga+ZRWz945leg9onW/DoAhHC8AjBM1?= =?utf-8?q?Vjl4GsijZwB+YfojTjV7Y9RlVSIruSnOtFIMHtjh9iDj3Z3dWxhhv2dCMivn3Jqsd?= =?utf-8?q?aHM/D8TzpJZas62Q5LQE+Oo05Bo3p7owQCGiB317QEjht6xBTbnj3J7ODN+IGO4WY?= =?utf-8?q?MgQY7cXjrIWTi2pGc5BC6k7cBtmhPDjpNxe/gd5F4LeeyWsNaDvGYHA0oAZTqbFbx?= =?utf-8?q?eS1rRfUA/FNVkt8avUGM553T1uyA4RkFlKgf2EhBh7fYCeKFsUnMBwViJB6vhheZX?= =?utf-8?q?AD3fythqQmV6+N45WSsRUozRX0K5O4/Yb7RF7csWODqYiOMnwLndJRKQ6vPpMGZX7?= =?utf-8?q?8U2BWp7+TVZlvmWjLdZkxSey2lk5RqTqB5IPr+wqWJiaFde6rcre+uoPdh4sDor0U?= =?utf-8?q?6j2gPZxl9ib75TaX4m1OTcJU8d/5eT2etrQ9rVbKQhFGKKCNvUxB/wl0xqM42yFhY?= =?utf-8?q?WZwxTEzMOb1teG6nOjWHWtfcZ4r+xqyFkIgYO6xahztPPr4zmNdgYp9hQ5Dfk0Trn?= =?utf-8?q?WpflWhgPmJug8EDYS7W405EvJgxhx8vJhR/3+vGNSKyY3W0K+X4dVgadvP7b7ouyT?= =?utf-8?q?0QNHoxVNr3YTLZhBM3K5rEu0wsm27hxYtXIMW7msQTkqPHLyflkXp46YQe+pLFzjp?= =?utf-8?q?yKmUeGaBz9eBqcVaS1R48cR+TYNgyWw5Y1klAIGQNlAQ=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-1ff67.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 9df21d19-2679-4030-a802-08d9bc2556e9 X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Dec 2021 21:38:00.6456 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8P223MB0191 Subject: [FFmpeg-devel] [PATCH v23 21/21] doc/APIchanges: update for subtitle filtering changes X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: MJjfWAHT4AG8 Signed-off-by: softworkz --- doc/APIchanges | 23 +++++++++++++++++++++++ libavcodec/version.h | 2 +- libavutil/version.h | 4 ++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/doc/APIchanges b/doc/APIchanges index 2914ad6734..bb47faf13d 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -14,6 +14,29 @@ libavutil: 2021-04-27 API changes, most recent first: +2021-12-05 - xxxxxxxxxx - lavc 59.15.100 - avcodec.h + Deprecate avcodec_encode_subtitle(), use regular encode api now + +2021-12-05 - xxxxxxxxxx - lavc 59.15.100 - codec_desc.h + Add avcodec_descriptor_get_subtitle_format() + +2021-12-05 - xxxxxxxxxx - lavc 59.15.100 - avcodec.h + Deprecate avsubtitle_free() + Deprecate avcodec_decode_subtitle2(), use regular decode api now + +2021-12-05 - xxxxxxxxxx - lavu 57.11.100 - frame.h + Add AVMediaType field to AVFrame + Add Fields for carrying subtitle data to AVFrame + (subtitle_areas, subtitle_header, subtitle_pts, start/end time, etc.) + Add av_frame_get_buffer2() and deprecate av_frame_get_buffer() + +2021-12-05 - xxxxxxxxxx - lavu 57.11.100 - subfmt.h + Add struct AVSubtitleArea (replaces AVSubtitle) + Add av_get_subtitle_fmt_name() and av_get_subtitle_fmt() + +2021-12-05 - xxxxxxxxxx - lavu 57.11.100 - subfmt.h + Add enum AVSubtitleType (moved from lavc), add new values, deprecate existing + 2021-11-xx - xxxxxxxxxx - lavfi 8.19.100 - avfilter.h Add AVFILTER_FLAG_METADATA_ONLY. diff --git a/libavcodec/version.h b/libavcodec/version.h index 8a0b94f5aa..db71fb1130 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -28,7 +28,7 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 59 -#define LIBAVCODEC_VERSION_MINOR 14 +#define LIBAVCODEC_VERSION_MINOR 15 #define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ diff --git a/libavutil/version.h b/libavutil/version.h index c6cd0b0d79..cc8caadf50 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -79,8 +79,8 @@ */ #define LIBAVUTIL_VERSION_MAJOR 57 -#define LIBAVUTIL_VERSION_MINOR 10 -#define LIBAVUTIL_VERSION_MICRO 101 +#define LIBAVUTIL_VERSION_MINOR 11 +#define LIBAVUTIL_VERSION_MICRO 100 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ LIBAVUTIL_VERSION_MINOR, \