From patchwork Fri Dec 4 14:46:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Jan_Ekstr=C3=B6m?= X-Patchwork-Id: 24343 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 8306244A3AF for ; Fri, 4 Dec 2020 17:10:07 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 661FC68A16A; Fri, 4 Dec 2020 17:10:07 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-lf1-f42.google.com (mail-lf1-f42.google.com [209.85.167.42]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id DE1AF68A148 for ; Fri, 4 Dec 2020 17:10:00 +0200 (EET) Received: by mail-lf1-f42.google.com with SMTP id s27so8074845lfp.5 for ; Fri, 04 Dec 2020 07:10:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=5KwDxfDDdlvGXR3dNSHf1Tgeal5wKQxGSYaTMSUowEw=; b=rP253kpjqZiljs92b+CrfwTGY1ujjJ2mgme94djPKrY55M6fUQ3XHsj073lZV+Sf5P iou4Mqt0OLAh1ei0Hav+3fyqGm1OrMPLm1l7aApdRDgolzae1/i1CgEUe4SWkzrO/JC7 6TiNgi6MVBkhMoOPsMxXRYnKi9Hsi4EogtZ7lW0j4rl55rpNAk87wP8K0ASTqZ4lb16o 93Du7Ammgk91CsbQ4b0f5WBi4cdi9XnRq4w39+8JWIFbkpxNAxLpHlIt+MloonRES17u xaPLuF2fvX67PzQZLotnH4goaABfjeMwSOMHs8JkmAkiIQKRbg4vf/jD6YJc0jQ7YNRT V2SQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=5KwDxfDDdlvGXR3dNSHf1Tgeal5wKQxGSYaTMSUowEw=; b=rXtpa+/I/buiO4He2Vl42yPVxxUib9Lb4+km+cB6BA013HzWD2zDWViROA350Zk6hf izvTlbbHTK+tqxGndkM3CORihaEAzwS7viqWeucRF8UyxDylrygc+a5dOUvMeMjTYfAl g5zSmHDzwmXY0DWQB4kasLbm4qX1Q9Gyo3HZAqUHa+4/WM6TNogo8D9uja6YzSktOet2 3mUErECmOPfEP7p/VeJP3tmldgVs5OLg9ukugE8MpOoKgu/AEsW0xufPB2UDtWB6GS6V gnDRACMVNr7G1ElOLYmP9M/k7Gyz9JA5WSYvh1/9QgOU3EuE4+hp0ABjejLslliSgNnp HMQQ== X-Gm-Message-State: AOAM531SENOxsycsUQMDdp2mYvlzP6yFPaxufS6WssYlKsSRjVnz6QSt WUIBoMQ7cOBw5sf3WDjP7VjKCCDaz+cm+g== X-Google-Smtp-Source: ABdhPJxWEJRzGIrW4WpW6RqV8zNR+Ml0zL4fZ0PAG/0KP7RtG242t9+wDjgdEKrIgy7Bx3T9/0Kyeg== X-Received: by 2002:ac2:41d9:: with SMTP id d25mr3634568lfi.377.1607093207807; Fri, 04 Dec 2020 06:46:47 -0800 (PST) Received: from localhost.localdomain (91-159-194-103.elisa-laajakaista.fi. [91.159.194.103]) by smtp.gmail.com with ESMTPSA id t2sm1750308lfd.59.2020.12.04.06.46.46 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Dec 2020 06:46:47 -0800 (PST) From: =?utf-8?q?Jan_Ekstr=C3=B6m?= To: ffmpeg-devel@ffmpeg.org Date: Fri, 4 Dec 2020 16:46:41 +0200 Message-Id: <20201204144643.73279-2-jeebjp@gmail.com> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20201204144643.73279-1-jeebjp@gmail.com> References: <20201204144643.73279-1-jeebjp@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 1/3] avutil/{avstring, bprint}: add XML escaping from ffprobe to avutil X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: Stefano Sabatini --- libavutil/avstring.h | 1 + libavutil/bprint.c | 14 ++++++++++++++ tools/ffescape.c | 1 + 3 files changed, 16 insertions(+) diff --git a/libavutil/avstring.h b/libavutil/avstring.h index ee225585b3..79bb920a70 100644 --- a/libavutil/avstring.h +++ b/libavutil/avstring.h @@ -324,6 +324,7 @@ enum AVEscapeMode { AV_ESCAPE_MODE_AUTO, ///< Use auto-selected escaping mode. AV_ESCAPE_MODE_BACKSLASH, ///< Use backslash escaping. AV_ESCAPE_MODE_QUOTE, ///< Use single-quote escaping. + AV_ESCAPE_MODE_XML, ///< Use XML non-markup character data escaping. }; /** diff --git a/libavutil/bprint.c b/libavutil/bprint.c index 2f059c5ba6..d825b61b14 100644 --- a/libavutil/bprint.c +++ b/libavutil/bprint.c @@ -283,6 +283,20 @@ void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_cha av_bprint_chars(dstbuf, '\'', 1); break; + case AV_ESCAPE_MODE_XML: + /* escape XML non-markup character data as per 2.4 */ + for (; *src; src++) { + switch (*src) { + case '&' : av_bprintf(dstbuf, "%s", "&"); break; + case '<' : av_bprintf(dstbuf, "%s", "<"); break; + case '>' : av_bprintf(dstbuf, "%s", ">"); break; + case '"' : av_bprintf(dstbuf, "%s", """); break; + case '\'': av_bprintf(dstbuf, "%s", "'"); break; + default: av_bprint_chars(dstbuf, *src, 1); + } + } + break; + /* case AV_ESCAPE_MODE_BACKSLASH or unknown mode */ default: /* \-escape characters */ diff --git a/tools/ffescape.c b/tools/ffescape.c index 0530d28c6d..8537235d5e 100644 --- a/tools/ffescape.c +++ b/tools/ffescape.c @@ -104,6 +104,7 @@ int main(int argc, char **argv) if (!strcmp(optarg, "auto")) escape_mode = AV_ESCAPE_MODE_AUTO; else if (!strcmp(optarg, "backslash")) escape_mode = AV_ESCAPE_MODE_BACKSLASH; else if (!strcmp(optarg, "quote")) escape_mode = AV_ESCAPE_MODE_QUOTE; + else if (!strcmp(optarg, "xml")) escape_mode = AV_ESCAPE_MODE_XML; else { av_log(NULL, AV_LOG_ERROR, "Invalid value '%s' for option -m, " From patchwork Fri Dec 4 14:46:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Jan_Ekstr=C3=B6m?= X-Patchwork-Id: 24342 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 4DE8D44A3AF for ; Fri, 4 Dec 2020 17:10:03 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 2345868A17B; Fri, 4 Dec 2020 17:10:03 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f41.google.com (mail-wr1-f41.google.com [209.85.221.41]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id A2391689AA0 for ; Fri, 4 Dec 2020 17:09:55 +0200 (EET) Received: by mail-wr1-f41.google.com with SMTP id u12so5679172wrt.0 for ; Fri, 04 Dec 2020 07:09:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=V2t3zos98zMFsXiprrEo3RI+l8uvFtw2X4kHozSKzPg=; b=C9QZ40ocCfRIGBgyS/d1Y5OFxSAe7XiMpb+zveeu9zCTZwVilTw9ZCFoK80Hh/2/eW 070AgohLGTLuGRlGr7Xvz9AMFjiaaflYddF9JXrJPsMGEASism/Lc0/Irb3LdAEjb9Wv YWEEvNVnDCRF57qq4MpHsOcGexqhdhWN+DQcfy+jG50qdTDEQpUjD9YFevbYFCQnR6wN Ml0vlGeoNkigEiBnzUCYc51D4X+JjWJxxNLSkGA+xgCebl5fjwLzlWkBYiyNV90cL+Rj ixrcg+PqqRiA7BHuFAst+On/PWroVaGdjkk/7b7BR2BbCcj1QFVsAb1O7kurWxFsfNnM eWjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=V2t3zos98zMFsXiprrEo3RI+l8uvFtw2X4kHozSKzPg=; b=DGvmy32GLaS3/zmn5+TgIdRyq0sIZfmW9zgd3/EXM44pMe58KgN0iUaXA9SXnTZr6L vrMbcjMj031vv4VbQmZYsCgYiGm/GcL3JIfrHAy7AxRAJavun0mfEa38FovRQdRTbLB3 vSnqQ42GcjT54MwPPn+/raWNe14E0OJlwv/K4YCGdQ7LdKnkzPvN3bYih7w17qklHrZI QQY8u17Z18kMp361aYz/AywZ9UkwLvUP4LowOFvkmR2yweqc0BtcfTMpORmVp0nangQH SbQckg8Wi7Z7MiBSlYsAggzfLmsGFMHDTl9bOc2uFYdQKNnU/3UGsWIrvtiko2NwNX9P vV6g== X-Gm-Message-State: AOAM533/7Qjzdcf3GOaYY2pfzpOWDTtbu+mud7eBO4TJOrJadO21fY71 i3Qn0yxizerIME+E09HjujeSNJC9mV5/3Q== X-Google-Smtp-Source: ABdhPJxzMks0MUVVitpv3+smVORpyKNoJY1kL7Znaw0SGiyQkuWJv67vJc7+vBlmuj+RHRTXbluD4g== X-Received: by 2002:a2e:b4c6:: with SMTP id r6mr3626655ljm.248.1607093208822; Fri, 04 Dec 2020 06:46:48 -0800 (PST) Received: from localhost.localdomain (91-159-194-103.elisa-laajakaista.fi. [91.159.194.103]) by smtp.gmail.com with ESMTPSA id t2sm1750308lfd.59.2020.12.04.06.46.47 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Dec 2020 06:46:48 -0800 (PST) From: =?utf-8?q?Jan_Ekstr=C3=B6m?= To: ffmpeg-devel@ffmpeg.org Date: Fri, 4 Dec 2020 16:46:42 +0200 Message-Id: <20201204144643.73279-3-jeebjp@gmail.com> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20201204144643.73279-1-jeebjp@gmail.com> References: <20201204144643.73279-1-jeebjp@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 2/3] ffprobe: switch to av_bprint_escape for XML escaping X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: Jan Ekström Signed-off-by: Jan Ekström --- fftools/ffprobe.c | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c index 86bd23d36d..a127a3f9bb 100644 --- a/fftools/ffprobe.c +++ b/fftools/ffprobe.c @@ -1671,24 +1671,6 @@ static av_cold int xml_init(WriterContext *wctx) return 0; } -static const char *xml_escape_str(AVBPrint *dst, const char *src, void *log_ctx) -{ - const char *p; - - for (p = src; *p; p++) { - switch (*p) { - case '&' : av_bprintf(dst, "%s", "&"); break; - case '<' : av_bprintf(dst, "%s", "<"); break; - case '>' : av_bprintf(dst, "%s", ">"); break; - case '"' : av_bprintf(dst, "%s", """); break; - case '\'': av_bprintf(dst, "%s", "'"); break; - default: av_bprint_chars(dst, *p, 1); - } - } - - return dst->str; -} - #define XML_INDENT() printf("%*c", xml->indent_level * 4, ' ') static void xml_print_section_header(WriterContext *wctx) @@ -1760,14 +1742,19 @@ static void xml_print_str(WriterContext *wctx, const char *key, const char *valu if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) { XML_INDENT(); + av_bprint_escape(&buf, key, NULL, AV_ESCAPE_MODE_XML, 0); printf("<%s key=\"%s\"", - section->element_name, xml_escape_str(&buf, key, wctx)); + section->element_name, buf.str); av_bprint_clear(&buf); - printf(" value=\"%s\"/>\n", xml_escape_str(&buf, value, wctx)); + + av_bprint_escape(&buf, value, NULL, AV_ESCAPE_MODE_XML, 0); + printf(" value=\"%s\"/>\n", buf.str); } else { if (wctx->nb_item[wctx->level]) printf(" "); - printf("%s=\"%s\"", key, xml_escape_str(&buf, value, wctx)); + + av_bprint_escape(&buf, value, NULL, AV_ESCAPE_MODE_XML, 0); + printf("%s=\"%s\"", key, buf.str); } av_bprint_finalize(&buf, NULL); From patchwork Fri Dec 4 14:46:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Jan_Ekstr=C3=B6m?= X-Patchwork-Id: 24341 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 4290E449FDE for ; Fri, 4 Dec 2020 16:55:16 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 2DE9368824C; Fri, 4 Dec 2020 16:55:16 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-lj1-f182.google.com (mail-lj1-f182.google.com [209.85.208.182]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id F25CF68A119 for ; Fri, 4 Dec 2020 16:55:08 +0200 (EET) Received: by mail-lj1-f182.google.com with SMTP id j10so6922764lja.5 for ; Fri, 04 Dec 2020 06:55:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=qAF7hutXkErcqJK5+uJj5njwp0F5NWa/OYdYrSn04Do=; b=AOxADKrUEKn2BzOJbtvaEw+de+rpdrYZomlWv5wbtvzo5DZM0wwsao1vZX1oK9jotc qagmpXmhdzLtlub44FariXgMTdLUo/HHCDQ6lLso4JEho46nogeyMj5uRUoUlEPjP6CO frHNc2eqjZTdOsB9M6itS1q9Ok1uZnnwC69PDSxNV2r+PJB0MRi6N9qK1JMp/J17ZsMo 0btndlyT+GTl5g2sbtiKJGdSj3nojejdzJtsCKC1JJj8ilma6CAWiL7UaDQLtN4oJozs PXL9NUjZo2MceicP4USrMpjQvDtw5entpSmUBdbz0qiR0xfV6BtRs8YTxZ745pgB9lvK es1w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=qAF7hutXkErcqJK5+uJj5njwp0F5NWa/OYdYrSn04Do=; b=YTlvyvURN7ToiQD92prfN9WE+t0SzwMVrdNy+mL7TJh+tjbn0QNRZRLJMerUviMUnm N0ow3h755CnIHkVDITuqojGDoSzC0mq4DoskuNuXY9Z5BFd8fr1cFuEhaqBqF/UHNbpc NrUDT75i1fNHxw5adRI87m5cr1bvCV8byEhQCfX+XcXkFjcXdoe6W+wDFF3gB39PWN5M LsbM8RrVlJEutmRDUC63aSOCp40aubhxd5OsEmHijcta5x93S1v9eC8LZwcKtyCzoSLR R6b5QfLOBHTxV2De5vrxl+DNjm71/XcsNnh4Y8tMn2+Z6ZuBnNBg/C9w7bMiOGiabunE nqRg== X-Gm-Message-State: AOAM531Zbjz/B86OkDDkzciqfYEY8FYBwvB3UMtHToiJNGXbw1Bix2Xn m1kbaW2r3xmIMj1E0sKft6od6qhFgxrlLA== X-Google-Smtp-Source: ABdhPJz33obzAjn4RqAJKhsv88DRaTywJUgOnciq9jjWYJNOhVAkC9aeYRopaS4+8prPnG57FKMOtA== X-Received: by 2002:a05:6512:33b9:: with SMTP id i25mr3402652lfg.526.1607093209809; Fri, 04 Dec 2020 06:46:49 -0800 (PST) Received: from localhost.localdomain (91-159-194-103.elisa-laajakaista.fi. [91.159.194.103]) by smtp.gmail.com with ESMTPSA id t2sm1750308lfd.59.2020.12.04.06.46.48 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Dec 2020 06:46:49 -0800 (PST) From: =?utf-8?q?Jan_Ekstr=C3=B6m?= To: ffmpeg-devel@ffmpeg.org Date: Fri, 4 Dec 2020 16:46:43 +0200 Message-Id: <20201204144643.73279-4-jeebjp@gmail.com> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20201204144643.73279-1-jeebjp@gmail.com> References: <20201204144643.73279-1-jeebjp@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 3/3] TTML encoder and muxer X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: Jan Ekström Enables encoding of other subtitle formats into TTML and writing them out as such documents. Signed-off-by: Jan Ekström --- Changelog | 1 + doc/general_contents.texi | 1 + libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/ttmlenc.c | 154 +++++++++++++++++++++++++++++++++++++ libavcodec/version.h | 2 +- libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/ttmlenc.c | 123 +++++++++++++++++++++++++++++ libavformat/version.h | 2 +- tests/fate/subtitles.mak | 3 + tests/ref/fate/sub-ttmlenc | 122 +++++++++++++++++++++++++++++ 12 files changed, 410 insertions(+), 2 deletions(-) create mode 100644 libavcodec/ttmlenc.c create mode 100644 libavformat/ttmlenc.c create mode 100644 tests/ref/fate/sub-ttmlenc diff --git a/Changelog b/Changelog index ebb1727875..71476eb366 100644 --- a/Changelog +++ b/Changelog @@ -48,6 +48,7 @@ version : - speechnorm filter - SpeedHQ encoder - asupercut filter +- TTML subtitle encoder and muxer version 4.3: diff --git a/doc/general_contents.texi b/doc/general_contents.texi index 1be6f9b683..dca183e9ca 100644 --- a/doc/general_contents.texi +++ b/doc/general_contents.texi @@ -1332,6 +1332,7 @@ performance on systems without hardware floating point support). @item SubViewer v1 @tab @tab X @tab @tab X @item SubViewer @tab @tab X @tab @tab X @item TED Talks captions @tab @tab X @tab @tab X +@item TTML @tab X @tab @tab X @tab @item VobSub (IDX+SUB) @tab @tab X @tab @tab X @item VPlayer @tab @tab X @tab @tab X @item WebVTT @tab X @tab X @tab X @tab X diff --git a/libavcodec/Makefile b/libavcodec/Makefile index a6435c9e85..9d2b62a263 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -665,6 +665,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_TWINVQ_DECODER) += twinvqdec.o twinvq.o OBJS-$(CONFIG_TXD_DECODER) += txd.o OBJS-$(CONFIG_ULTI_DECODER) += ulti.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 774d5670bf..b12538905b 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -685,6 +685,7 @@ extern AVCodec ff_subviewer_decoder; extern AVCodec ff_subviewer1_decoder; extern AVCodec ff_text_encoder; extern AVCodec ff_text_decoder; +extern AVCodec ff_ttml_encoder; extern AVCodec ff_vplayer_decoder; extern AVCodec ff_webvtt_encoder; extern AVCodec ff_webvtt_decoder; diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c new file mode 100644 index 0000000000..7eb89e73f4 --- /dev/null +++ b/libavcodec/ttmlenc.c @@ -0,0 +1,154 @@ +/* + * TTML subtitle encoder + * Copyright (c) 2020 24i + * + * 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 + * TTML subtitle encoder + * @see https://www.w3.org/TR/ttml1/ + * @see https://www.w3.org/TR/ttml2/ + * @see https://www.w3.org/TR/ttml-imsc/rec + */ + +#include "avcodec.h" +#include "libavutil/avstring.h" +#include "libavutil/bprint.h" +#include "ass_split.h" +#include "ass.h" + +typedef struct { + AVCodecContext *avctx; + ASSSplitContext *ass_ctx; + AVBPrint buffer; +} TTMLContext; + +static void ttml_text_cb(void *priv, const char *text, int len) +{ + TTMLContext *s = priv; + AVBPrint cur_line = { 0 }; + AVBPrint *buffer = &s->buffer; + + av_bprint_init(&cur_line, len, AV_BPRINT_SIZE_UNLIMITED); + + av_bprint_append_data(&cur_line, text, len); + if (!av_bprint_is_complete(&cur_line)) { + av_log(s->avctx, AV_LOG_ERROR, + "Failed to move the current subtitle dialog to AVBPrint!\n"); + av_bprint_finalize(&cur_line, NULL); + return; + } + + + av_bprint_escape(buffer, cur_line.str, NULL, AV_ESCAPE_MODE_XML, 0); + + av_bprint_finalize(&cur_line, NULL); +} + +static void ttml_new_line_cb(void *priv, int forced) +{ + TTMLContext *s = priv; + + av_bprintf(&s->buffer, "
"); +} + +static const ASSCodesCallbacks ttml_callbacks = { + .text = ttml_text_cb, + .new_line = ttml_new_line_cb, +}; + +static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, + int bufsize, const AVSubtitle *sub) +{ + TTMLContext *s = avctx->priv_data; + ASSDialog *dialog; + int i; + + av_bprint_clear(&s->buffer); + + for (i=0; inum_rects; i++) { + const char *ass = sub->rects[i]->ass; + + if (sub->rects[i]->type != SUBTITLE_ASS) { + av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); + return AVERROR(ENOSYS); + } + +#if FF_API_ASS_TIMING + if (!strncmp(ass, "Dialogue: ", 10)) { + int num; + dialog = ff_ass_split_dialog(s->ass_ctx, ass, 0, &num); + + for (; dialog && num--; dialog++) { + ff_ass_split_override_codes(&ttml_callbacks, s, dialog->text); + } + } else { +#endif + dialog = ff_ass_split_dialog2(s->ass_ctx, ass); + if (!dialog) + return AVERROR(ENOMEM); + + ff_ass_split_override_codes(&ttml_callbacks, s, dialog->text); + ff_ass_free_dialog(&dialog); +#if FF_API_ASS_TIMING + } +#endif + } + + 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 -1; + } + memcpy(buf, s->buffer.str, s->buffer.len); + + return s->buffer.len; +} + +static av_cold int ttml_encode_close(AVCodecContext *avctx) +{ + TTMLContext *s = avctx->priv_data; + ff_ass_split_free(s->ass_ctx); + av_bprint_finalize(&s->buffer, NULL); + return 0; +} + +static av_cold int ttml_encode_init(AVCodecContext *avctx) +{ + TTMLContext *s = avctx->priv_data; + s->avctx = avctx; + s->ass_ctx = ff_ass_split(avctx->subtitle_header); + av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); + return s->ass_ctx ? 0 : AVERROR_INVALIDDATA; +} + +AVCodec ff_ttml_encoder = { + .name = "ttml", + .long_name = NULL_IF_CONFIG_SMALL("TTML subtitle"), + .type = AVMEDIA_TYPE_SUBTITLE, + .id = AV_CODEC_ID_TTML, + .priv_data_size = sizeof(TTMLContext), + .init = ttml_encode_init, + .encode_sub = ttml_encode_frame, + .close = ttml_encode_close, +}; diff --git a/libavcodec/version.h b/libavcodec/version.h index e4b81da7cb..4ee221b7f2 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -28,7 +28,7 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 58 -#define LIBAVCODEC_VERSION_MINOR 114 +#define LIBAVCODEC_VERSION_MINOR 115 #define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ diff --git a/libavformat/Makefile b/libavformat/Makefile index be5a482b01..cbf9de0fc6 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -542,6 +542,7 @@ OBJS-$(CONFIG_TRUEHD_DEMUXER) += rawdec.o mlpdec.o OBJS-$(CONFIG_TRUEHD_MUXER) += rawenc.o OBJS-$(CONFIG_TTA_DEMUXER) += tta.o apetag.o img2.o OBJS-$(CONFIG_TTA_MUXER) += ttaenc.o apetag.o img2.o +OBJS-$(CONFIG_TTML_MUXER) += ttmlenc.o OBJS-$(CONFIG_TTY_DEMUXER) += tty.o sauce.o OBJS-$(CONFIG_TY_DEMUXER) += ty.o OBJS-$(CONFIG_TXD_DEMUXER) += txd.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 53e5374255..ce0ff0e2d3 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -441,6 +441,7 @@ extern AVInputFormat ff_truehd_demuxer; extern AVOutputFormat ff_truehd_muxer; extern AVInputFormat ff_tta_demuxer; extern AVOutputFormat ff_tta_muxer; +extern AVOutputFormat ff_ttml_muxer; extern AVInputFormat ff_txd_demuxer; extern AVInputFormat ff_tty_demuxer; extern AVInputFormat ff_ty_demuxer; diff --git a/libavformat/ttmlenc.c b/libavformat/ttmlenc.c new file mode 100644 index 0000000000..6ba248ee30 --- /dev/null +++ b/libavformat/ttmlenc.c @@ -0,0 +1,123 @@ +/* + * TTML subtitle muxer + * Copyright (c) 2020 24i + * + * 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 + * TTML subtitle muxer + * @see https://www.w3.org/TR/ttml1/ + * @see https://www.w3.org/TR/ttml2/ + * @see https://www.w3.org/TR/ttml-imsc/rec + */ + +#include "avformat.h" +#include "internal.h" + +static const char ttml_header_text[] = +"\n" +"\n" +" \n" +"
\n"; + +static const char ttml_footer_text[] = +"
\n" +" \n" +"\n"; + +static void ttml_write_time(AVIOContext *pb, const char tag[], + int64_t millisec) +{ + int64_t sec, min, hour; + sec = millisec / 1000; + millisec -= 1000 * sec; + min = sec / 60; + sec -= 60 * min; + hour = min / 60; + min -= 60 * hour; + + avio_printf(pb, "%s=\"%02"PRId64":%02"PRId64":%02"PRId64".%03"PRId64"\"", + tag, hour, min, sec, millisec); +} + +static int ttml_write_header(AVFormatContext *ctx) +{ + if (ctx->nb_streams != 1 || + ctx->streams[0]->codecpar->codec_id != AV_CODEC_ID_TTML) { + av_log(ctx, AV_LOG_ERROR, "Exactly one TTML stream is required!\n"); + return AVERROR(EINVAL); + } + + { + AVStream *s = ctx->streams[0]; + AVIOContext *pb = ctx->pb; + + AVDictionaryEntry *lang = av_dict_get(s->metadata, "language", NULL, 0); + const char *printed_lang = (lang && lang->value) ? lang->value : ""; + + avpriv_set_pts_info(s, 64, 1, 1000); + + avio_printf(pb, ttml_header_text, printed_lang); + + avio_flush(pb); + } + + return 0; +} + +static int ttml_write_packet(AVFormatContext *ctx, AVPacket *pkt) +{ + AVIOContext *pb = ctx->pb; + + avio_printf(pb, " pts); + avio_printf(pb, "\n"); + ttml_write_time(pb, " end", pkt->pts + pkt->duration); + avio_printf(pb, ">"); + avio_write(pb, pkt->data, pkt->size); + avio_printf(pb, "

\n"); + + return 0; +} + +static int ttml_write_trailer(AVFormatContext *ctx) +{ + AVIOContext *pb = ctx->pb; + + avio_printf(pb, ttml_footer_text); + avio_flush(pb); + + return 0; +} + +AVOutputFormat ff_ttml_muxer = { + .name = "ttml", + .long_name = NULL_IF_CONFIG_SMALL("TTML subtitle"), + .extensions = "ttml", + .mime_type = "text/ttml", + .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT, + .subtitle_codec = AV_CODEC_ID_TTML, + .write_header = ttml_write_header, + .write_packet = ttml_write_packet, + .write_trailer = ttml_write_trailer, +}; diff --git a/libavformat/version.h b/libavformat/version.h index ddcca9ae50..b43193bcb1 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -32,7 +32,7 @@ // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium) // Also please add any ticket numbers that you believe might be affected here #define LIBAVFORMAT_VERSION_MAJOR 58 -#define LIBAVFORMAT_VERSION_MINOR 64 +#define LIBAVFORMAT_VERSION_MINOR 65 #define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ diff --git a/tests/fate/subtitles.mak b/tests/fate/subtitles.mak index 6323d0f93d..ee65afe35b 100644 --- a/tests/fate/subtitles.mak +++ b/tests/fate/subtitles.mak @@ -106,6 +106,9 @@ fate-sub-scc: CMD = fmtstdout ass -ss 57 -i $(TARGET_SAMPLES)/sub/witch.scc FATE_SUBTITLES-$(call ALLYES, MPEGTS_DEMUXER DVBSUB_DECODER DVBSUB_ENCODER) += fate-sub-dvb fate-sub-dvb: CMD = framecrc -i $(TARGET_SAMPLES)/sub/dvbsubtest_filter.ts -map s:0 -c dvbsub +FATE_SUBTITLES-$(call ALLYES, FILE_PROTOCOL PIPE_PROTOCOL SRT_DEMUXER SUBRIP_DECODER TTML_ENCODER TTML_MUXER) += fate-sub-ttmlenc +fate-sub-ttmlenc: CMD = fmtstdout ttml -i $(TARGET_SAMPLES)/sub/SubRip_capability_tester.srt + FATE_SUBTITLES-$(call ENCMUX, ASS, ASS) += $(FATE_SUBTITLES_ASS-yes) FATE_SUBTITLES += $(FATE_SUBTITLES-yes) diff --git a/tests/ref/fate/sub-ttmlenc b/tests/ref/fate/sub-ttmlenc new file mode 100644 index 0000000000..624f37d092 --- /dev/null +++ b/tests/ref/fate/sub-ttmlenc @@ -0,0 +1,122 @@ + + + +
+

Don't show this text it may be used to insert hidden data

+

SubRip subtitles capability tester 1.3o by ale5000
Use VLC 1.1 or higher as reference for most things and MPC Home Cinema for others
This text should be blue
This text should be red
This text should be black
If you see this with the normal font, the player don't (fully) support font face

+

Hidden

+

This text should be small
This text should be normal
This text should be big

+

This should be an E with an accent: È
日本語
This text should be bold, italics and underline
This text should be small and green
This text should be small and red
This text should be big and brown

+

This line should be bold
This line should be italics
This line should be underline
This line should be strikethrough
Both lines
should be underline

+

>
It would be a good thing to
hide invalid html tags that are closed and show the text in them
but show un-closed invalid html tags
Show not opened tags
<

+

and also
hide invalid html tags with parameters that are closed and show the text in them
but show un-closed invalid html tags
This text should be showed underlined without problems also: 2<3,5>1,4<6
This shouldn't be underlined

+

This text should be in the normal position...

+

This text should NOT be in the normal position

+

Implementation is the same of the ASS tag
This text should be at the
top and horizontally centered

+

This text should be at the
middle and horizontally centered

+

This text should be at the
bottom and horizontally centered

+

This text should be at the
top and horizontally at the left

+

This text should be at the
middle and horizontally at the left
(The second position must be ignored)

+

This text should be at the
bottom and horizontally at the left

+

This text should be at the
top and horizontally at the right

+

This text should be at the
middle and horizontally at the right

+

This text should be at the
bottom and horizontally at the right

+

This could be the most difficult thing to implement

+

First text

+

Second, it shouldn't overlap first

+

Third, it should replace second

+

Fourth, it shouldn't overlap first and third

+

Fifth, it should replace third

+

Sixth, it shouldn't be
showed overlapped

+

TEXT 1 (bottom)

+

text 2

+

Hide these tags:
also hide these tags:
but show this: {normal text}

+


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

+


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

+

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

+


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

^--Forced line break

+

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

+

It shouldn't be strikethrough,
not opened tag showed as text.
Not opened tag showed as text.

+

Three lines should be strikethrough,
yes.
Not closed tags showed as text

+

Both line should be strikethrough but
the wrong closing tag should be showed

+
+ +