From patchwork Thu Oct 21 03:19:05 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 31187 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp127059ioa; Wed, 20 Oct 2021 20:19:50 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyvi/uJxwNUqBmTXBHOVwzUt+gJTptL9EeYpJ68WPqKa/5VT8Icg7eMn/Lw7x5BYcnvELhh X-Received: by 2002:a17:906:f747:: with SMTP id jp7mr4024469ejb.530.1634786390665; Wed, 20 Oct 2021 20:19:50 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id j11si8836799edw.462.2021.10.20.20.19.27; Wed, 20 Oct 2021 20:19:50 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=ctrkub0Z; 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 1581B68A498; Thu, 21 Oct 2021 06:19:14 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM11-BN8-obe.outbound.protection.outlook.com (mail-bn8nam11olkn2034.outbound.protection.outlook.com [40.92.20.34]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 8218168A498 for ; Thu, 21 Oct 2021 06:19:07 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=PRlg5CKkxR9+GLNgtrZhSKv57mcfMb6+MNv7PwT0KD7X9tpEx0k5ypx5aO6AeSZBmsqgJrafHnhi7PvFJ5HarQ6IRO/0HCCxSOzMxi93dSPz4wcNiY+52WtrqlQ5B/uLZcfEmTRfhr/mN2ga6o6k//7pzFuwTqsn7Qi/OTyXfAOItN5XOss8A8ImMqpS2UpBiw0tlpOweQmQqE8AOADi12A4c/oqKhd4wBy9wja8G5wf/bF4tgYKG0EH28aZglFqR+IyYjophANxQD+rNQ24Vo3Ry6dmYpnIHa96ZQtvCs2cgo05vaQPmEp/cnsQcM/PHG5go0Wc/MG97YhJVPV9DQ== 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=/LwDH5z/I+Y3fWc0jTYuWK6FCp+5CFMf5KdGjSvqDjY=; b=oWHJGe0NpR6BS8q+47XU526s2In019DW9r+IwBXktFqgP0X2g20nJT+neaaWADHgq4Lhke8uUQ72cN4mv5Wzybq4/SaAlrIhAgjjos0ia3YUCYocvoSWQKh6Du27cR8jaJHXfa9h/BkMzH7fLy57a07hguS2Cs9bghXIrMnszRqxe3deGCIIw0o5quClV2ytF8hjzVtZn/sh9EGgIfUnwD7R3A0yuNd6qyfXKn8kxeuLoiFnErwsaU6FdtDF9KbvNDZCSO75BSyIkrt5bY6qJRqCNJ4f0oXXdg0Q2DDJWkD+ariGlr66NFUvQihEnRt3oms9P+6jBpivjW3fQTx+fw== 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=/LwDH5z/I+Y3fWc0jTYuWK6FCp+5CFMf5KdGjSvqDjY=; b=ctrkub0ZHextQa1um0qkVCRkYwFjauKtCxKE3lFZGhGYUDgbp+fsm8SMNwRv0eQseLfpErH055wHvtp3xXwMeAWYd0q/DANkNmPTcdizSZzU67ufdk7bSLFn4r7Oc0MUtqzvS/+UawEAKX/Jkaq7j/J5hS/OzvYADX4yuMJ63Gjt6rmrZorBReLDzrWXDGbjVQa1LpWts718Cs29He+giO75GLYsZgi5UZGTS8UCipvYjJ6lQ+dCvQnTRFvHOtMW/pICrz+wLJV99BpyYf0yXX48w/nTke4yBsonVfBDLpVvpEwR0RbOILCPT1eNjK1K8LBpOgtm+OSfNuE+37gCGA== Received: from MN2PR04MB5981.namprd04.prod.outlook.com (2603:10b6:208:da::10) by MN2PR04MB6238.namprd04.prod.outlook.com (2603:10b6:208:e4::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4608.18; Thu, 21 Oct 2021 03:19:05 +0000 Received: from MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb]) by MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb%5]) with mapi id 15.20.4608.018; Thu, 21 Oct 2021 03:19:05 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v14 01/14] global: Prepare AVFrame for subtitle handling Thread-Index: AQHXxipnzRcymjFZ60iv1zl4wvXVzA== Date: Thu, 21 Oct 2021 03:19:05 +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: [LeyY7dPA8W62uQeH0YSa/ODnLWpgu3EH] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 3cc78a4f-5957-4dc6-6960-08d9944189ef x-ms-traffictypediagnostic: MN2PR04MB6238: x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: 1puMuTPHryhnWu7WVIYAxdZ/Pu1kdO4L2YQv9fr5aev2wPuxKHNqZ3nIYLNxk91i+K+lqJbS6ath9FPoB87YGUwvS29y33uWzru2mwtOW5vNa38ed3cwxtDvXeOpoju67hZZiM7jNksvNJSx7hn4957/6nNl2dLKgiIALg2OHhGUChI89C0NntdXx0hL4x1D4EIBANxqkixaTI1e2ag8gLh27ILN4scnIl+cTERrXzPLtSK/5ndVMaTJapOH27eCQ4PAKA/XBMZ0yXvmHg1amrIbCxICeYA5AZLF8Gb1wyvep1XLFgxkYWCwzoY5g0PRL7tulhQrxWN4fbOWyrve6A++pis8DD/OX4+QszYCLdRl5ubTvXCszSlSWsoqQlwtkuQctK+GUyed69NtLaoQD+e7pg8IvuAADkN/vUHkGg+Z6O09DXopniLi+hAhe0DXO2Kqy237AlMQjlrxtxGvlt6sVFywsk4OJHbXOURGlCRI+ipFfb0vcrMiDS645jLgchL8OvUArW4OocnDPu9Ktiax37jtG5xtBDB1uqDn4eE= x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: RHY/ayoctoEAIw6Qa38NYt/GzsL9xYl4odo95e8400WIuWn7wGjzYfaWd60L0Cw+kI2tVQjlhBcHr8oMRizB9nq3yMzvL7uT3UFHSPoEmoKpRDSljh66OjfJPsNJF6GvJTod7Sas9vgHDoA2kKwCjmibw4/+jcJvn+Rj7cTKF7X64JuvaxybjBQOG4k0iutBA6UUNv4RwTv24tv5nhqyr0VeIdHy9virxvypl7cN2QaRX74VlPhpsY1QsN+9eZrFelSWepwkRzegsbMaJaMsCS+2Vxa7s/27Wdx9Z1KjOyES8b2egEQV32+sPFgVDUw5nxWsW/++Kix+sIomCUQ9fRK2kUqLFutK1/xYob+9ysI66K+X0fu3r7lW3S1ZJoQLCLl6YqzWKlQf2E7VWicZwRJJHWXicSOki+IuBI5ATN6Ketc8f9TYb2ACENsPyZv2tDUhC3pb9dscbPwucQ4NhxEnQEqdmG+sWKMfRRYhb2Ca0TwaStndhaBPd6q5vPuk/sRJzy2siZVAjG1nt+NwTirHuHf2+BQA/wd7J+EwK86MHa1y93h0sO22EosBNuC0iZUPrWbpkKlS1rFULthSmVq1tkAY6dgnk02to/DZtIzsAbxxlsbzQ44mFYIHwPy69GvZidmzs9mpxKfYmLOefav4IGRQuCd8ookexqEjj861kuVlEmJSNU9x6j9XFUpu3fSQJWe0jN2MX4qt0d+whg== MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-3174-20-msonline-outlook-529c7.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: MN2PR04MB5981.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 3cc78a4f-5957-4dc6-6960-08d9944189ef X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Oct 2021 03:19:05.6660 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-userprincipalname: softworkz@hotmail.com X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR04MB6238 Subject: [FFmpeg-devel] [PATCH v14 01/14] global: 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: hRolxAzxNgnI 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 - Move enum AVSubtitleType, AVSubtitle and AVSubtitleRect to avutil - Add public-named members to enum AVSubtitleType (AV_SUBTITLE_FMT_) - 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.c | 19 --- libavcodec/avcodec.h | 75 ++---------- libavcodec/decode.c | 53 ++++++-- libavcodec/pgssubdec.c | 1 + libavcodec/utils.c | 11 ++ libavfilter/vf_subtitles.c | 50 ++++++-- libavformat/utils.c | 1 + libavutil/Makefile | 2 + libavutil/frame.c | 194 ++++++++++++++++++++++++++---- libavutil/frame.h | 93 +++++++++++++- libavutil/subfmt.c | 240 +++++++++++++++++++++++++++++++++++++ libavutil/subfmt.h | 185 ++++++++++++++++++++++++++++ 12 files changed, 799 insertions(+), 125 deletions(-) create mode 100644 libavutil/subfmt.c create mode 100644 libavutil/subfmt.h diff --git a/libavcodec/avcodec.c b/libavcodec/avcodec.c index ff3d73e237..52ce345d49 100644 --- a/libavcodec/avcodec.c +++ b/libavcodec/avcodec.c @@ -422,25 +422,6 @@ void avcodec_flush_buffers(AVCodecContext *avctx) av_bsf_flush(avci->bsf); } -void avsubtitle_free(AVSubtitle *sub) -{ - int i; - - for (i = 0; i < sub->num_rects; i++) { - av_freep(&sub->rects[i]->data[0]); - av_freep(&sub->rects[i]->data[1]); - av_freep(&sub->rects[i]->data[2]); - av_freep(&sub->rects[i]->data[3]); - av_freep(&sub->rects[i]->text); - av_freep(&sub->rects[i]->ass); - av_freep(&sub->rects[i]); - } - - av_freep(&sub->rects); - - memset(sub, 0, sizeof(*sub)); -} - av_cold int avcodec_close(AVCodecContext *avctx) { int i; diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 7ee8bc2b7c..0c5819b116 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" @@ -1674,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()) @@ -2238,63 +2239,8 @@ 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 { - int x; ///< top left corner of pict, undefined when pict is not set - int y; ///< top left corner of pict, undefined when pict is not set - int w; ///< width of pict, undefined when pict is not set - int h; ///< height of pict, undefined when pict is not set - int nb_colors; ///< number of colors in pict, undefined when pict is not set - - /** - * data+linesize for the bitmap of this subtitle. - * Can be set for text/ass as well once they are rendered. - */ - uint8_t *data[4]; - int linesize[4]; - - enum AVSubtitleType type; - - char *text; ///< 0 terminated plain UTF-8 text - - /** - * 0 terminated ASS/SSA compatible event line. - * The presentation of this is unaffected by the other values in this - * struct. - */ - char *ass; - - int flags; -} AVSubtitleRect; - -typedef struct AVSubtitle { - uint16_t format; /* 0 = graphics */ - uint32_t start_display_time; /* relative to packet pts, in ms */ - uint32_t end_display_time; /* relative to packet pts, in ms */ - unsigned num_rects; - AVSubtitleRect **rects; - int64_t pts; ///< Same as packet pts, in AV_TIME_BASE -} AVSubtitle; - /** * Return the LIBAVCODEC_VERSION_INT constant. */ @@ -2430,13 +2376,6 @@ int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **op */ int avcodec_close(AVCodecContext *avctx); -/** - * Free all allocated data in the given subtitle struct. - * - * @param sub AVSubtitle to free. - */ -void avsubtitle_free(AVSubtitle *sub); - /** * @} */ @@ -2527,6 +2466,8 @@ 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. */ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, int *got_sub_ptr, @@ -3125,6 +3066,14 @@ void avcodec_flush_buffers(AVCodecContext *avctx); */ int av_get_audio_frame_duration(AVCodecContext *avctx, int frame_bytes); +/** + * Return subtitle format from a codec descriptor + * + * @param codec_descriptor codec descriptor + * @return the subtitle type (e.g. bitmap, text) + */ +enum AVSubtitleType av_get_subtitle_format_from_codecdesc(const AVCodecDescriptor *codec_descriptor); + /* memory */ /** diff --git a/libavcodec/decode.c b/libavcodec/decode.c index 294c040716..b2495f8578 100644 --- a/libavcodec/decode.c +++ b/libavcodec/decode.c @@ -577,6 +577,34 @@ 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, const AVPacket *avpkt); + +static int decode_subtitle_shim(AVCodecContext *avctx, AVFrame *frame, const AVPacket *avpkt) +{ + int ret, got_sub_ptr = 0; + AVSubtitle subtitle = { 0 }; + + 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) + return ret; + av_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; @@ -591,6 +619,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); @@ -652,7 +683,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; @@ -724,7 +757,7 @@ static void get_subtitle_defaults(AVSubtitle *sub) #define UTF8_MAX_BYTES 4 /* 5 and 6 bytes sequences should not be used */ static int recode_subtitle(AVCodecContext *avctx, AVPacket **outpkt, - AVPacket *inpkt, AVPacket *buf_pkt) + const AVPacket *inpkt, AVPacket *buf_pkt) { #if CONFIG_ICONV iconv_t cd = (iconv_t)-1; @@ -803,9 +836,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, const AVPacket *avpkt) { int ret = 0; @@ -845,10 +877,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 = av_get_subtitle_format_from_codecdesc(avctx->codec_descriptor); for (unsigned i = 0; i < sub->num_rects; i++) { if (avctx->sub_charenc_mode != FF_SUB_CHARENC_MODE_IGNORE && @@ -872,6 +901,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/pgssubdec.c b/libavcodec/pgssubdec.c index 388639a110..5709058b1c 100644 --- a/libavcodec/pgssubdec.c +++ b/libavcodec/pgssubdec.c @@ -32,6 +32,7 @@ #include "libavutil/colorspace.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" +#include "libavutil/subfmt.h" #define RGBA(r,g,b,a) (((unsigned)(a) << 24) | ((r) << 16) | ((g) << 8) | (b)) #define MAX_EPOCH_PALETTES 8 // Max 8 allowed per PGS epoch diff --git a/libavcodec/utils.c b/libavcodec/utils.c index a91a54b0dc..efe14e5a43 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -824,6 +824,17 @@ int av_get_audio_frame_duration(AVCodecContext *avctx, int frame_bytes) return FFMAX(0, duration); } +enum AVSubtitleType av_get_subtitle_format_from_codecdesc(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; +} + int av_get_audio_frame_duration2(AVCodecParameters *par, int frame_bytes) { int duration = get_audio_frame_duration(par->codec_id, par->sample_rate, diff --git a/libavfilter/vf_subtitles.c b/libavfilter/vf_subtitles.c index 377160c72b..6b54cf669b 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 = av_get_subtitle_format_from_codecdesc(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,18 @@ 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 (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 +494,7 @@ static av_cold int init_subtitles(AVFilterContext *ctx) } } av_packet_unref(&pkt); - avsubtitle_free(&sub); + av_frame_free(&sub); } end: diff --git a/libavformat/utils.c b/libavformat/utils.c index 509c0ecdce..f1ec4c4c2f 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -31,6 +31,7 @@ #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "libavutil/pixfmt.h" +#include "libavutil/subfmt.h" #include "libavutil/thread.h" #include "libavutil/time.h" diff --git a/libavutil/Makefile b/libavutil/Makefile index 410ac636f7..04e1101bf3 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 \ @@ -159,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 2617cda2b5..733a6d2b7d 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) { if (frame->extended_data != frame->data) @@ -72,7 +76,12 @@ static void get_frame_defaults(AVFrame *frame) frame->colorspace = AVCOL_SPC_UNSPECIFIED; frame->color_range = AVCOL_RANGE_UNSPECIFIED; frame->chroma_location = AVCHROMA_LOC_UNSPECIFIED; - frame->flags = 0; + frame->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) @@ -243,23 +252,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; @@ -290,6 +331,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); @@ -331,6 +378,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; @@ -344,7 +392,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; @@ -366,6 +414,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)); @@ -436,7 +488,7 @@ AVFrame *av_frame_clone(const AVFrame *src) void av_frame_unref(AVFrame *frame) { - int i; + unsigned i, n; if (!frame) return; @@ -455,6 +507,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); + get_frame_defaults(frame); } @@ -472,7 +539,8 @@ void av_frame_move_ref(AVFrame *dst, AVFrame *src) int av_frame_is_writable(AVFrame *frame) { - int i, ret = 1; + AVSubtitleArea *area; + unsigned i, n, ret = 1; /* assume non-refcounted frames are not writable */ if (!frame->buf[0]) @@ -484,6 +552,15 @@ int av_frame_is_writable(AVFrame *frame) for (i = 0; i < frame->nb_extended_buf; i++) ret &= !!av_buffer_is_writable(frame->extended_buf[i]); + for (i = 0; i < frame->num_subtitle_areas; i++) { + area = frame->subtitle_areas[i]; + if (area) { + for (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,39 @@ static int frame_copy_audio(AVFrame *dst, const AVFrame *src) 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) @@ -831,3 +939,49 @@ int av_frame_apply_cropping(AVFrame *frame, int flags) return 0; } + +/** + * Copies subtitle data from AVSubtitle (deprecated) to AVFrame + * + * @note This is a compatibility method for conversion to the legacy API + */ +void av_frame_put_subtitle(AVFrame *frame, 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; + frame->num_subtitle_areas = sub->num_rects; + + if (sub->num_rects) { + frame->subtitle_areas = av_malloc_array(sub->num_rects, sizeof(AVSubtitleArea *)); + + for (unsigned i = 0; i < sub->num_rects; i++) { + frame->subtitle_areas[i] = av_mallocz(sizeof(AVSubtitleArea)); + av_subtitle_rect2area(frame->subtitle_areas[i], sub->rects[i]); + } + } +} + +/** + * Copies subtitle data from AVFrame to AVSubtitle (deprecated) + * + * @note This is a compatibility method for conversion to the legacy API + */ +void av_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; + sub->num_rects = frame->num_subtitle_areas; + + if (frame->num_subtitle_areas) { + sub->rects = av_malloc_array(frame->num_subtitle_areas, sizeof(AVSubtitle *)); + + for (unsigned i = 0; i < frame->num_subtitle_areas; i++) { + sub->rects[i] = av_mallocz(sizeof(AVSubtitleRect)); + av_subtitle_area2rect(sub->rects[i], frame->subtitle_areas[i]); + } + } +} + diff --git a/libavutil/frame.h b/libavutil/frame.h index ff2540a20f..21122819b1 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" @@ -271,7 +272,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 @@ -302,6 +303,13 @@ typedef struct AVRegionOfInterest { */ typedef struct AVFrame { #define AV_NUM_DATA_POINTERS 8 + /** + * Media type of the frame (audio, video, subtitles..) + * + * See AVMEDIA_TYPE_xxx + */ + enum AVMediaType type; + /** * pointer to the picture/channel planes. * This might be different from the first allocated byte @@ -371,7 +379,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; @@ -462,6 +470,39 @@ typedef struct AVFrame { */ uint64_t channel_layout; + /** + * 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; + + /** + * Usually the same as packet pts, in AV_TIME_BASE. + * + * @deprecated This is kept for compatibility reasons and corresponds to + * AVSubtitle->pts. Might be removed in the future. + */ + int64_t subtitle_pts; + + /** + * Header containing style information for text subtitles. + */ + AVBufferRef *subtitle_header; + /** * AVBuffer references backing the data for this frame. If all elements of * this array are NULL, then this frame is not reference counted. This array @@ -721,6 +762,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 @@ -740,9 +783,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. * @@ -844,6 +917,22 @@ AVFrameSideData *av_frame_get_side_data(const AVFrame *frame, */ void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type); +/** + * Copies subtitle data from AVSubtitle to AVFrame. + * + * @deprecated This is a compatibility method for interoperability with + * the legacy subtitle API. + */ +void av_frame_put_subtitle(AVFrame *frame, AVSubtitle *sub); + +/** + * Copies subtitle data from AVFrame to AVSubtitle. + * + * @deprecated This is a compatibility method for interoperability with + * the legacy subtitle API. + */ +void av_frame_get_subtitle(AVSubtitle *sub, AVFrame *frame); + /** * Flags for frame cropping. diff --git a/libavutil/subfmt.c b/libavutil/subfmt.c new file mode 100644 index 0000000000..24fbeffacd --- /dev/null +++ b/libavutil/subfmt.c @@ -0,0 +1,240 @@ +/* + * 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" + +typedef struct SubtitleFmtInfo { + enum AVSubtitleType fmt; + const char* name; +} SubtitleFmtInfo; + +static SubtitleFmtInfo sub_fmt_info[AV_SUBTITLE_FMT_NB] = { + {.fmt = AV_SUBTITLE_FMT_UNKNOWN, .name = "Unknown subtitle format", }, + {.fmt = AV_SUBTITLE_FMT_BITMAP, .name = "Graphical subtitles", }, + {.fmt = AV_SUBTITLE_FMT_TEXT, .name = "Text subtitles (plain)", }, + {.fmt = AV_SUBTITLE_FMT_ASS, .name = "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].name; +} + +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, name)) + return i; + return AV_SUBTITLE_FMT_NONE; +} + +int av_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; + + 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(ENOMEM); + } + + return 0; +} + +int av_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; + + 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(ENOMEM); + } + + return 0; +} + +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; + + switch (dst->type) { + case AV_SUBTITLE_FMT_BITMAP: + + if (src->h > 0 && src->w > 0 && src->buf[0]) { + dst->buf[0] = av_buffer_ref(src->buf[0]); + if (!dst->buf[0]) + return AVERROR(ENOMEM); + + if (copy_data) { + const int ret = av_buffer_make_writable(&dst->buf[0]); + if (ret < 0) + return ret; + } + + dst->linesize[0] = src->linesize[0]; + } + + for (unsigned i = 0; i < 256; i++) + dst->pal[i] = src->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(ENOMEM); + } + + return 0; +} + +void avsubtitle_free(AVSubtitle *sub) +{ + for (unsigned i = 0; i < sub->num_rects; i++) { + av_freep(&sub->rects[i]->data[0]); + av_freep(&sub->rects[i]->data[1]); + av_freep(&sub->rects[i]->data[2]); + av_freep(&sub->rects[i]->data[3]); + av_freep(&sub->rects[i]->text); + av_freep(&sub->rects[i]->ass); + av_freep(&sub->rects[i]); + } + + av_freep(&sub->rects); + + memset(sub, 0, sizeof(*sub)); +} + diff --git a/libavutil/subfmt.h b/libavutil/subfmt.h new file mode 100644 index 0000000000..372e3db413 --- /dev/null +++ b/libavutil/subfmt.h @@ -0,0 +1,185 @@ +/* + * 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 + +#include "buffer.h" + +enum AVSubtitleType { + + /** + * Subtitle format unknown. + */ + AV_SUBTITLE_FMT_NONE = -1, + + /** + * Subtitle format unknown. + */ + AV_SUBTITLE_FMT_UNKNOWN = 0, + + /** + * Bitmap area in AVSubtitleRect.data, pixfmt AV_PIX_FMT_PAL8. + */ + AV_SUBTITLE_FMT_BITMAP = 1, + SUBTITLE_BITMAP = 1, ///< Deprecated, use AV_SUBTITLE_FMT_BITMAP instead. + + /** + * Plain text in AVSubtitleRect.text. + */ + AV_SUBTITLE_FMT_TEXT = 2, + SUBTITLE_TEXT = 2, ///< Deprecated, use AV_SUBTITLE_FMT_TEXT instead. + + /** + * Text Formatted as per ASS specification, contained AVSubtitleRect.ass. + */ + AV_SUBTITLE_FMT_ASS = 3, + SUBTITLE_ASS = 3, ///< Deprecated, use AV_SUBTITLE_FMT_ASS instead. + + AV_SUBTITLE_FMT_NB, +}; + +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; + +typedef struct AVSubtitleRect { + unsigned nb_refs; + int x; ///< top left corner of pict, undefined when pict is not set + int y; ///< top left corner of pict, undefined when pict is not set + int w; ///< width of pict, undefined when pict is not set + int h; ///< height of pict, undefined when pict is not set + int nb_colors; ///< number of colors in pict, undefined when pict is not set + + /** + * data+linesize for the bitmap of this subtitle. + */ + uint8_t *data[4]; + int linesize[4]; + + enum AVSubtitleType type; + + char *text; ///< 0 terminated plain UTF-8 text + + /** + * 0-terminated ASS/SSA compatible event line. + */ + char *ass; + + int flags; +} AVSubtitleRect; + +typedef struct AVSubtitle { + uint16_t format; /* 0 = graphics */ + uint32_t start_display_time; /* relative to packet pts, in ms */ + uint32_t end_display_time; /* relative to packet pts, in ms */ + unsigned num_rects; + AVSubtitleRect **rects; + int64_t pts; ///< Same as packet pts, in AV_TIME_BASE +} AVSubtitle; + +/** + * 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); + +/** + * Copy a subtitle area. + * + * @param dst The target area. + * @param src The source area. + * @param copy_data Determines whether to copy references or actual + * data from @ref AVSubtitleArea.buf. + * + * @return 0 on success. + */ +int av_subtitle_area2area(AVSubtitleArea *dst, const AVSubtitleArea *src, int copy_data); + +/** + * Copy data from @ref AVSubtitleArea to @ref AVSubtitleRect. + * + * @param dst The target rect (@ref AVSubtitleRect). + * @param src The source area (@ref AVSubtitleArea). + * + * @return 0 on success. + * + * @deprecated This is a compatibility method for interoperability with + * the legacy subtitle API. + */ +int av_subtitle_area2rect(AVSubtitleRect *dst, const AVSubtitleArea *src); + +/** + * Copy data from @ref AVSubtitleRect to @ref AVSubtitleArea. + * + * @param dst The source area (@ref AVSubtitleArea). + * @param src The target rect (@ref AVSubtitleRect). + * + * @return 0 on success. + * + * @deprecated This is a compatibility method for interoperability with + * the legacy subtitle API. + */ +int av_subtitle_rect2area(AVSubtitleArea *dst, const AVSubtitleRect *src); + +/** + * Free all allocated data in the given subtitle struct. + * + * @param sub AVSubtitle to free. + */ +void avsubtitle_free(AVSubtitle *sub); + +#endif /* AVUTIL_SUBFMT_H */ From patchwork Thu Oct 21 03:19:09 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 31180 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp127016ioa; Wed, 20 Oct 2021 20:19:46 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxtMbkvZtpMN7e5hrk2VYG7orkm7ZnJYx5bzum94PvPMHztL75bpsaUhdY0WQVGAXq8SlOu X-Received: by 2002:a17:906:f2d0:: with SMTP id gz16mr4063387ejb.102.1634786386550; Wed, 20 Oct 2021 20:19:46 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id j5si5496988edp.203.2021.10.20.20.19.40; Wed, 20 Oct 2021 20:19:46 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=rdhgWoNa; 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 4389C68A58D; Thu, 21 Oct 2021 06:19:20 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM11-BN8-obe.outbound.protection.outlook.com (mail-bn8nam11olkn2034.outbound.protection.outlook.com [40.92.20.34]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 05D9D68A12D for ; Thu, 21 Oct 2021 06:19:13 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=UtlVMay3KaaOoVXuMY94DV8haulsQc2XF8V+o249oj59x8vZxa2xzaoNwudlLOhUvpoYmAgaCwKM3+IxEjwbiZ/DY/oHntusAVBt57QzOrkSEO5PmD9SxXZ3kligp3Awe90bg46uBdhKJ0dGRj1nQ+6ZjJYT1O/Pei+ocBq/biaFZcnLOE7+gIw806Xa3+DrGjwVz4YQFPH/+baKArKh6caTWTPu7/O18gNL+ED6Ihjd5JRflwEUG73HZj6hrfPMCrQfTUnpAbiGv5G7bM4FbuTxmJC3AMto3dhd9LMlF4/tv1PWLH+89Da0LaAQup+Y/DP0Kq7ZD6OQX0unK7Kf4A== 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=Becs9G6RKWBSF3koQ95dGhriVDX0btrLaZ+Ahs5Uaa4=; b=TukWjqDHBQ966xXftNYXGJRkaGbicmCwVI/iao92qbxUD0OZ/UqvuKltQxsX6rPm5QP3tPQ0/y2El2FVtvPSCs4Dqo+vtdqN1lt76cYRPVcQ79CkmGVM31R3OheYHOHOw9fva6RGYgOomrxawFkghA8ik8MR+W/pGBxXk/wGVp9tUigHeVo7CmbJqH5KS5A5H+Z9xVewXYmGFtvlNp7XJoOYFLqci6XiRcCXVEIqSroC91Mk4LwktqHc+a+q/VjsBlJWBr0BFSCwvRs7PN5qVKocVpOaSvyRXV1+MQfBH6vOi0opWzu64tfNkPDfnNS/T1j1eqf4PoadevHudm496w== 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=Becs9G6RKWBSF3koQ95dGhriVDX0btrLaZ+Ahs5Uaa4=; b=rdhgWoNayZvUFUVSVT4zkAe8IdCYwf+aLlIKOeZZ49XIMkT7qiXZrbf27JYWVpSBv3UOYvI9hM03lLB9SnJqn/i7xdLUL2wiopSYp8rSRQhNVQHe1h8Uy11rp0m1CyyZwekClmgqBHGxyRtQmS1FszwR6gd6uJ8YrHGQgn4Flr9IpUqZGgbtjOKNXrVXqrB67YTncGSFCJg/UpvCyEVf6dO87kCrt+k+41KvzqJjgTKxFn07daCzYX5AnQYS3EHm09YbR7+IHJ0Fv8ctvouY2N/3wK16j4wLMn0JMgWO8Ep/5ZBgfe5a4mzlI1iz/BXSfzUXSNdc2yxwZk1Gix280g== Received: from MN2PR04MB5981.namprd04.prod.outlook.com (2603:10b6:208:da::10) by MN2PR04MB6238.namprd04.prod.outlook.com (2603:10b6:208:e4::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4608.18; Thu, 21 Oct 2021 03:19:09 +0000 Received: from MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb]) by MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb%5]) with mapi id 15.20.4608.018; Thu, 21 Oct 2021 03:19:09 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v14 02/14] global: Move ass helper functions to avutil as avpriv_ and extend ass dialog parsing Thread-Index: AQHXxippqb87jThmbk6EvU20WAepZQ== Date: Thu, 21 Oct 2021 03:19:09 +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: [KltpT9QJLBnNUB3q5GriSVZNRINy4JPe] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: eaf161fa-38cb-4656-fa33-08d994418c08 x-ms-traffictypediagnostic: MN2PR04MB6238: x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: PjlrGLnhQ44cuFzPQI/IMllgBcagMOZ8SQwvlijS2sWZKkJzA78rnFs0mLwhZK3fDAsRe2AFFjsDmirwozUVTaGsQcogWeIX/NJbRzzyBCsBrGrbaEKV7S+LDEzvTUBSNxR1Fn/q7LLvDfL+qjvIBprmNU2zDNed5ZQa6yQ56AzWYQ5dOP0iGBS1w3g5Ky28FGWRFWYlCZuLS83ZBTMJr+SgNeeoEOHstPOQ6QI5TsWPyI0YCMgd41Og0SDsjQUieW7mutb53WOYW5OUSAqxQL3umnTaRlmBRth0Xodwre5S/qkep7K9fHTXn3h8mso0Bg28h2a5h0CwcTUKb2uRfCS3oqkoeUrCRczDiatro0cVxD/eQS7sE/VlEAlrdKamb17IEH1SwzXLqgaSWp5EgvB1sDOYJrgGDyx0zNOsO0bDhZLBOGdpq8QzGAmCDcXQBYifE7nLw6nIdk2G4v3Af3MtjzYvPRBRirIL2DDe/riy+11O+emUc4zEPfLfPLnYIMT3WsN5mBYO7IJSgd+dUusI4rHcyoE3V6QgKsGu+jM= x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: 1/JadgAIHUdTdXvpwLmyH4Y3mUh2eAuT3r4Sq4wuKOkGQbNWix7zzrV//Si96d+3cbrTmRKp4Wf4SfnQgypZ0d6+yluI5XS5gvSHq8mPSktZBFjdba7O+rUwuHlD60yr9R0rJMbbl2tWbF5gbqHehyP4JerJEBnrJxLtOY0ReJGdmLX9VkPk9KssO66yqo49gXCQ5inQi3Ydpx0y3+rQw40Kmi/4df7MrfHLP5pH/zpJEO/O9v8GS19koEwLZmEGOHcLvuc4MyRrE49L8LSfY29bWpOBM7++c0e0f3g15th7CLnVHTpt+zcDl5lgxPIYfurFQx+wPz3qbmKVx8ACBztzqrZ6XLS70KXQkxNodcPckK5c3JgDRkTb2SMyHwvlaeGqO+heqsvbloqjPkCOQvgFxnYuUExnRAy8iUhkTupoebufKAlf2EagVzVnU/+6R8SKX5FTKXoAaDR+5BrLuTPmtvlGJx6T9NumimSdew98+ima9rcdoDMNVYx1Mg+VVO8D6BCNvvRUqdkDo7f/T+u5TgYhbD2bhipdNdSqXihfifJomZ8BpPZT1dw9FgeJQHWzXmqcdulUTyO2ejRKFKGqoxosIMYCiD4G0vZAzFL6KXwQVrsyNwJ3baR1VovDJ+m1JSxVBMotOA0Pbi5cbvHTv4XmXWbGKf7k4Zipbl/NfvKg+qTngWad+zhgAsVEPTyZ2vdybSMnJB20fJuKVA== MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-3174-20-msonline-outlook-529c7.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: MN2PR04MB5981.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: eaf161fa-38cb-4656-fa33-08d994418c08 X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Oct 2021 03:19:09.1300 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-userprincipalname: softworkz@hotmail.com X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR04MB6238 Subject: [FFmpeg-devel] [PATCH v14 02/14] global: 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: 5kw1OWUaUbB8 Signed-off-by: softworkz --- libavcodec/Makefile | 56 +++---- libavcodec/ass.h | 129 ++++------------ 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 | 73 +++++---- libavutil/ass_internal.h | 139 ++++++++++++++++++ {libavcodec => libavutil}/ass_split.c | 30 ++-- .../ass_split_internal.h | 24 +-- 26 files changed, 331 insertions(+), 256 deletions(-) rename {libavcodec => libavutil}/ass.c (73%) create mode 100644 libavutil/ass_internal.h rename {libavcodec => libavutil}/ass_split.c (94%) rename libavcodec/ass_split.h => libavutil/ass_split_internal.h (89%) diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 14fbd2ecbc..df4fb53749 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 @@ -252,7 +252,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 @@ -424,7 +424,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 \ @@ -446,7 +446,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 @@ -461,8 +461,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 @@ -501,7 +501,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 @@ -554,7 +554,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 @@ -586,7 +586,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 @@ -601,7 +601,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 @@ -636,13 +636,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 @@ -652,8 +652,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 @@ -673,7 +673,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 @@ -728,15 +728,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 @@ -1021,7 +1021,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 @@ -1072,7 +1072,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..274c6d7988 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,47 @@ #define AVCODEC_ASS_H #include "avcodec.h" -#include "libavutil/bprint.h" - -#define ASS_DEFAULT_PLAYRESX 384 -#define ASS_DEFAULT_PLAYRESY 288 - -/** - * @name Default values for ASS style - * @{ - */ -#define ASS_DEFAULT_FONT "Arial" -#define ASS_DEFAULT_FONT_SIZE 16 -#define ASS_DEFAULT_COLOR 0xffffff -#define ASS_DEFAULT_BACK_COLOR 0 -#define ASS_DEFAULT_BOLD 0 -#define ASS_DEFAULT_ITALIC 0 -#define ASS_DEFAULT_UNDERLINE 0 -#define ASS_DEFAULT_ALIGNMENT 2 -#define ASS_DEFAULT_BORDERSTYLE 1 -/** @} */ +#include "libavutil/ass_internal.h" typedef struct FFASSDecoderContext { int readorder; } FFASSDecoderContext; -/** - * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS. - * Can specify all fields explicitly - * - * @param avctx pointer to the AVCodecContext - * @param play_res_x subtitle frame width - * @param play_res_y subtitle frame height - * @param font name of the default font face to use - * @param font_size default font size to use - * @param primary_color default text color to use (ABGR) - * @param secondary_color default secondary text color to use (ABGR) - * @param outline_color default outline color to use (ABGR) - * @param back_color default background color to use (ABGR) - * @param bold 1 for bold text, 0 for normal text - * @param italic 1 for italic text, 0 for normal text - * @param underline 1 for underline text, 0 for normal text - * @param border_style 1 for outline, 3 for opaque box - * @param alignment position of the text (left, center, top...), defined after - * the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top) - * @return >= 0 on success otherwise an error code <0 - */ -int ff_ass_subtitle_header_full(AVCodecContext *avctx, +static inline int ff_ass_subtitle_header_full(AVCodecContext *avctx, int play_res_x, int play_res_y, const char *font, int font_size, int primary_color, int secondary_color, int outline_color, int back_color, int bold, int italic, int underline, - int border_style, int alignment); -/** - * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS. - * - * @param avctx pointer to the AVCodecContext - * @param font name of the default font face to use - * @param font_size default font size to use - * @param color default text color to use (ABGR) - * @param back_color default background color to use (ABGR) - * @param bold 1 for bold text, 0 for normal text - * @param italic 1 for italic text, 0 for normal text - * @param underline 1 for underline text, 0 for normal text - * @param alignment position of the text (left, center, top...), defined after - * the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top) - * @return >= 0 on success otherwise an error code <0 - */ -int ff_ass_subtitle_header(AVCodecContext *avctx, - const char *font, int font_size, - int color, int back_color, - int bold, int italic, int underline, - int border_style, int alignment); + int border_style, int alignment) +{ + avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_full( + play_res_x, play_res_y, font, font_size, + primary_color, secondary_color, outline_color, + back_color, bold,italic,underline,border_style,alignment, + !(avctx->flags & AV_CODEC_FLAG_BITEXACT)); -/** - * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS - * with default style. - * - * @param avctx pointer to the AVCodecContext - * @return >= 0 on success otherwise an error code <0 - */ -int ff_ass_subtitle_header_default(AVCodecContext *avctx); + if (!avctx->subtitle_header) + return AVERROR(ENOMEM); + avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header); + return 0; +} -/** - * Craft an ASS dialog string. - */ -char *ff_ass_get_dialog(int readorder, int layer, const char *style, - const char *speaker, const char *text); +static inline int ff_ass_subtitle_header_default(AVCodecContext *avctx) +{ + avctx->subtitle_header = (uint8_t *)avpriv_ass_get_subtitle_header_default(!(avctx->flags & AV_CODEC_FLAG_BITEXACT)); -/** - * Add an ASS dialog to a subtitle. - */ -int ff_ass_add_rect(AVSubtitle *sub, const char *dialog, - int readorder, int layer, const char *style, - const char *speaker); + if (!avctx->subtitle_header) + return AVERROR(ENOMEM); + avctx->subtitle_header_size = strlen((char *)avctx->subtitle_header); + return 0; +} -/** - * Helper to flush a text subtitles decoder making use of the - * FFASSDecoderContext. - */ -void ff_ass_decoder_flush(AVCodecContext *avctx); +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; +} -/** - * 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 4e14ae5900..cd98e359ca 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" @@ -545,7 +544,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 2ae5a9bf0b..d50b34210f 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" @@ -168,7 +168,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); @@ -223,7 +223,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]; } @@ -330,7 +330,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); @@ -565,7 +565,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); @@ -579,7 +579,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); } @@ -654,12 +654,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); for (j = 0; j < box_count; j++) { box_types[j].encode(s); 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 04e1101bf3..e72b883fa6 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 73% rename from libavcodec/ass.c rename to libavutil/ass.c index 907e2d7b88..39ffedc59d 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,7 +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 avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog, int readorder, int layer, const char *style, const char *speaker) { @@ -129,7 +128,7 @@ int ff_ass_add_rect(AVSubtitle *sub, const char *dialog, 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); + ass_str = avpriv_ass_get_dialog(readorder, layer, style, speaker, dialog); if (!ass_str) return AVERROR(ENOMEM); rects[sub->num_rects]->ass = ass_str; @@ -137,14 +136,14 @@ int ff_ass_add_rect(AVSubtitle *sub, const char *dialog, 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_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..bcb8b5ae4a --- /dev/null +++ b/libavutil/ass_internal.h @@ -0,0 +1,139 @@ +/* + * 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); + +/** + * Add an ASS dialog to a subtitle. + */ +int avpriv_ass_add_rect(AVSubtitle *sub, const char *dialog, + int readorder, int layer, const char *style, + const char *speaker); + +/////** +//// * 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 89% rename from libavcodec/ass_split.h rename to libavutil/ass_split_internal.h index a45fb9b8a1..8e8e51115c 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); /** @@ -156,7 +156,9 @@ 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); /** @} */ /** @@ -176,7 +178,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 +188,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 Thu Oct 21 03:19:11 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 31182 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp127097ioa; Wed, 20 Oct 2021 20:19:54 -0700 (PDT) X-Google-Smtp-Source: ABdhPJx36lObNWJiWBIt5am4+ar0nzEaYyJdn656qT79eMSHzc56AmA2Z+6BVYUkrecPPfYuj+aU X-Received: by 2002:a05:6402:1c94:: with SMTP id cy20mr4142384edb.144.1634786393942; Wed, 20 Oct 2021 20:19:53 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id v20si5198688edi.84.2021.10.20.20.19.53; Wed, 20 Oct 2021 20:19:53 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=BDAiaBGm; 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 58DEF68A74F; Thu, 21 Oct 2021 06:19:25 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM11-BN8-obe.outbound.protection.outlook.com (mail-bn8nam11olkn2034.outbound.protection.outlook.com [40.92.20.34]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5F8CE68A6F6 for ; Thu, 21 Oct 2021 06:19:18 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=kgzwbXRNcBRhpZqlyJywkgpHFIeeBd+Uuxwcz5CL2D5VkOhrZZCCfs5TxcQl2meuXBwfH/8sPKgUZDlij7/PKqZMTBEwklsqTkKuUEKFk5DQIFAxpsT+R0JI2lOQLSUGcEjjR5u/LsOWbSYGcgDAQqXCfyDQqGtRZdv5PYM3BUYAuobPspRcHO9cKpneiC6jgGzaTWYRMnB89y7GjoUUtMjFu8I91HLJyoXGP9ARZf67wFfaLvK81fox9yMEW5nLfLkYYwOXtQMl0cYg71E9Q4Yv21Lzt41p/nlsrcIEhNYhXPUdC0SLmVN6Ao/HNmvR1KO2Y7rQiS3oHZYBAmjJ7w== 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=Iz5scr84kNWlhdYHKeWliE5SyIpogqNdGFk4El42w7Y=; b=PRUvGn03iQpMGQRZRTvrtJ2UsXmcwal1XiJZ6SpZdxRYhs5UmG1erkIBYz82euex6trGtpOKlC39qfuGFcFLsBPWkID7NK0o4LB30XFs61Sb/a6W55GBowaQQ5CgLINUNkeUGUQfDpJR9wZ3ONXW/Pquhs+Wf/GR1HO+bIvIHjh3uxWwtvvJbKN2mqkCA3FSyXhsLz2leH/tl+YhV6kTAF2I4b0dnp5x/eVJhACiafaPcGKLXVB1DBRx5M4t8vmgoOWZXT4WfDTo9WR261ZKIYlwWjRON1z/mtyI/7WIwILwdPAkzE2LSzgYzPObxQxzF6Dt/hA8aURAQfs3smJyzQ== 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=Iz5scr84kNWlhdYHKeWliE5SyIpogqNdGFk4El42w7Y=; b=BDAiaBGma57n4l8kRa08EmkNd/JegWboueRaqP0vXw41monyCgyivtN4+qMJ6i69BPpcXqG0b4hct9rb3culE671Mdj5NnapU3WtT7Kpzie0shNt9ZQVLWqE2gt4V9t0TI+2S5zcBDuBE7IJTWjBOwxRniyrM4b0oJmUSsTSzYyyyYoOSYULewfDe9MgAyw2UREc8Sc0hMlktA1F/BxdlQSXQM2yAbty2LsDbYGciT4x1QD/MHfjZBH0y4c/vpcaIO2Id12hM0+QDdHH13AFSlDc6z1SNDAxfoteQIjT32rRRrc7MzwkAcWkimhv87rbZ+iy2e3fs7EH6GANKfxLaQ== Received: from MN2PR04MB5981.namprd04.prod.outlook.com (2603:10b6:208:da::10) by MN2PR04MB6238.namprd04.prod.outlook.com (2603:10b6:208:e4::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4608.18; Thu, 21 Oct 2021 03:19:12 +0000 Received: from MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb]) by MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb%5]) with mapi id 15.20.4608.018; Thu, 21 Oct 2021 03:19:12 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v14 03/14] fftools/play,probe: Adjust for subtitle changes Thread-Index: AQHXxipr2IzOUhQ9zUCppbDpGZT1DA== Date: Thu, 21 Oct 2021 03:19:11 +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: [dn/IBQ3urDzJeanENJ+v+WsB/GowzqOR] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 56893e89-308d-45b9-8d72-08d994418dc1 x-ms-traffictypediagnostic: MN2PR04MB6238: x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: GMb+PbfmcmRJHQkpZwPmwXHekEJMU+v4Dyzp/drOLG3K7ei+eklnf99CmR8cSn2AM1CyhBqpbTekWn9sI0KXY/CAn8z1ToYDj/rp/0OIDY67LAroTK/DecDPrNBNqDTKuUJMN5Cxa5olK7v83BMhBTNhyuz62oK53x36g6/MgkudsqWlZUrVOL7aqUhgEWah98kiYlh09JdOANTYJsDRoUIGGWY+Vql1FYt+KxYqyI4ujlxWEtam2HecJ9r+qPxhJJK2k1kDp4n+STcVvTICl3X5bPzcpIWPNt0S4W/LcMvDJiOelc3WX+L/KChecjqvejPQbXjbr3isYdJsUAQ7Cf4iaHMNoWO0lH8XoXHSlDvFdKjOqZb6A+LhOYK2Virzw/goSXHmzyp0WinMhaDpG1f59oNEoSPlI95F9akWuMpTQ9HCyvXENcQRAC5DZeKIlqoM2yKQlHngUXkkQPsmyxQYsAcPKdbObGk86I0/dTetnEivreNtTW4Qekvk2oVkGWmlv4gRyeB505Eg/POf1oFR20t9RsSOipRFObyO4Go= x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: yfqCACfvQN6gXr5jEsSkp8SBEvZrKOi5+W1Lz7gyggziGjEv0T7mSqdmQQQTuvgqjLc1rELCevu247tHPT5ALLlBAiMOQq4ibXvxp3d+pJnXwSn6/WKONlYYWKF/eGeBCx9DryyGCoatQpLMahDknyohA+hioMvOsXt/l2v9Po7R47U2aM5zY20DjeQTA+dhOHYI456GyeMCpj1WcLNa3KUlm8asvMW3Bpbl+9RRWsQuSAJH6pw14xmaCCTCnIQtWkx6zTLknm6y76A3EI0wu/YP9kugMCjPjSMnRFm+zdFVBfaatcbk50aGrwJ0c7h1SaexZO4rnu/cyYVXktrrnNeEyMRNWwHHFlvD3vgQZ3OLdEiU32z2bWmzBDhgCL2ff7Omr3eTnBkY2F3MT/TVzpC6lXc34rMHB8OxbY2H55nuhHQMCYqYE2pW0MTa3OWtI/uq0qqGWKFeO3Nlp6+UL/BazYULedlU+iJx9QRG5/IZpOtKawXbofCKtesIY5mDHdyRH1w6K643jk0DOasp1/5aP/hj1st4P85QD/JXHC7/P1WulryiojpI9dj0bcOLEuRYtksHTQfnqdUK0Ofn5x881onjwDvh+f6WdJJqD1mtuo77qlmJcwUvXwW8QBTVygelYWszM5taq78trMgCM8gJy5Hy9OZ97EkEMF/h5fy2L+yPcLyT22i0Q6FWjwlgyTJQHXEkxJpcVh6lxR3zkg== MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-3174-20-msonline-outlook-529c7.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: MN2PR04MB5981.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 56893e89-308d-45b9-8d72-08d994418dc1 X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Oct 2021 03:19:12.0393 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-userprincipalname: softworkz@hotmail.com X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR04MB6238 Subject: [FFmpeg-devel] [PATCH v14 03/14] 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: YLBQcJ+oVayP 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 ccea0e4578..e8d36485ae 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 90e895bbf9..61e58139e5 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 Thu Oct 21 03:19: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: 31188 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp127216ioa; Wed, 20 Oct 2021 20:20:05 -0700 (PDT) X-Google-Smtp-Source: ABdhPJys3bad+Cg5EnblLwuBKoJfFOq87kuv8iXf1Rx2R6qgI1IVjqXApuny12falLqgZ6GiChBv X-Received: by 2002:a05:6402:26c5:: with SMTP id x5mr4043459edd.297.1634786405481; Wed, 20 Oct 2021 20:20:05 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id b10si6508403ede.76.2021.10.20.20.20.05; Wed, 20 Oct 2021 20:20:05 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=bgiSZhoA; 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 5ED4068A7C0; Thu, 21 Oct 2021 06:19:31 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM11-BN8-obe.outbound.protection.outlook.com (mail-bn8nam11olkn2034.outbound.protection.outlook.com [40.92.20.34]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 1A4E368A5FC for ; Thu, 21 Oct 2021 06:19:24 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=aQ+co/sQcxTJV0KiKzWE9MzycFiaw5XNJ4PGFyEUu5qSbuLF5scrJwkEUqNGc5iBp4iUcQVGYAiaIMarTJmz0DdGkUUv6Im7aXz8AdUluR8qRibLf0hHaoD643BF5JY05LHHnOnOqAf1q7vnvi6/0G3IepHLKR2MlxbAjxI8UJVzyZAOJm9h3RXoNO7WuFP/T561MTDzP/EuHyftK1XYUmE0FKlALse6px1/5W70CuR+scXos41GQVpZoi8FVk2GZqS1IDXFojS2+5R9OMkDw+OPvvykGVHX076MBR1nwdYJ8klZtO3ghnOgFmYho0nBh3R87g3m94a6kMatVAOvrw== 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=UE03tc8hxvDSm/bS1nJjmJxFLCoUuCvqiHNQe7+rTKs=; b=XAE/G11UShjtdC3L4BAlTD83Yno84HnT0CLiPoyxZO2H2jZsQDNJUdUbhvlTnHa684CTI5A/+l+EfhLYmQio1nWmxhexUMYgJIzk1yOx0HkWFPzbbwMIapt8lKNB+zFulbnm6c53/DnVWIOY5XrQ9CU/0TlUpdLm1QOiO9b520VjWt8JUrLoHxJalYLiiVx7v5y++b5keB2QoBSFCoEVxYJxsQk1UuvbpLtVXg9RUEeNp0YAUhTNn7oW7CdGRPnF2CABIoFE2yAXH7ECoHzVVjbfg1F7x26anvpJ6wEa5kWR0rhgTkt5FX3D1ZQkjFaxAKo+ez3TZ3KDvjFmtVLTYA== 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=UE03tc8hxvDSm/bS1nJjmJxFLCoUuCvqiHNQe7+rTKs=; b=bgiSZhoAnndptOfRbgzW42vXZ6cZQrImxsIGsgpFjOc2eSxr/stzU8CsmbirTRys7mOIgrtPA7WQkL2W3DruRoJpkD+VOht+HL8ye+PJYa1dm5B4/HWzwGEvkJiWzBKdew+FsHxMLWrE/hhYzlLS9EYXEfire2dSjbotvRZfRjAZSTv6gQjn+LK+6NMe64ZwZoEKBbrtca4O+QO5AeztCK/JoQZtV167NgnPWR9N8LIQd55E775NtoPVGNp8fGvaS1cBe8pACbMylMN+tCJGVAGAiwzFo8zbMcnfa0s0hFDW7oBnWfHLePZkkFTmeqFKWdL68yKKjpnmtMMsBmWtNQ== Received: from MN2PR04MB5981.namprd04.prod.outlook.com (2603:10b6:208:da::10) by MN2PR04MB6238.namprd04.prod.outlook.com (2603:10b6:208:e4::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4608.18; Thu, 21 Oct 2021 03:19:16 +0000 Received: from MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb]) by MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb%5]) with mapi id 15.20.4608.018; Thu, 21 Oct 2021 03:19:15 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v14 04/14] avfilter/subtitles: Add subtitles.c for subtitle frame allocation Thread-Index: AQHXxiptY6/25zLJGUaO67wZ4nGilw== Date: Thu, 21 Oct 2021 03:19:15 +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: [6oBV48q6PbCu5jBDEy4lWX37fjYZ7snJ] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: eb960891-55c3-4afe-2c7f-08d994419009 x-ms-traffictypediagnostic: MN2PR04MB6238: x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: ZiE06VGyOGlGRzFbyBRoalt4DrWJTxLXnpoNbXdokA1GOZXHzuaj/xWx4sptF4MhEKJVxgxT4NXCq82A77qPm/uNEreySq3uP2RGTQTwtWUpKRH51Zv9z6KHsPE8wQxgQ6dT3S0ShHauY/R7FuCR7qeyGH3Va9IcOS5RXEO7IzuLlcLU+KqGIyJpobVmP+pJ1HufyCw9pCtXJKlkuQsf/x1CVpb9HWw6JhwsxaVlgCC1aJOFkwi6SJisH4KNPfDwvWEw5lmxUh+w30QJPQ64SDl3+aSFORCse9FkmXxDFjW+9kcQt3GaBz/MP5/z1Jy1YBUOSp8LfxsnhhZ74UGo5NDiE/kqfJTXtmsXF+ZyT27M6yuhAqLEtv/opxcnh1bDCeBvLx0rNjCKKsYoM0ykq9Y/ychWzmicjK2aSdsJGgopsHsoxnxO2+c5HHGHVe6B6GhcClEq8D818F92P2cUZHvZ366/MebYIIUCloxkCItKtkzeIDhIAFW1IPbZs1WQDDGS1OaqvAmDx2egEYp+u0oigYaUurEL2YERkrS2Lt4= x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: t7fGbHrriDxm+ZBzawoJfX51BzrX1Pn0SzfNYg+fFKcWQMqkbajNzX+3ThZ21bGilHT6ZjZIoUcgRE0qo3CJ3obC/q+nPRsMra+L2NlzxaCwewqVFAjPay84K+8AtoGosBSqwTe723q1hdu35PJQRw5i5kFdDJT/ippNwaggBP6xDM6HN/Gp7D8vTKihQodj/sJtG0eQGvNRYJKz5HjWGINYtNEEbfnTNhSqCIbjx3SJLPwrTsgsqgUuvzM2CLgGsHDaf/HJ+AVzXlE1fzxkH7yucPVQ9gc0seckO0Y0ZSmUwiPhylQpzukzWnCjM3VYqIW8wF37rfeqMyMU3QGiYed8g9mlYc/Vk6peA0zbez9NoD89NmsTZC13nw+0/qmJJiQzz69HfdzbLBsj5QCCWOiEWF6Hm/9HJp2HVI5s3ojWhOdZ/oXODpKbc5UziLrSLEmIFfDmsDGzbJzRmnkoFwRsIcO/+GMUhRF6FHaISXe+QMxzVSIWzHjY7u1+ixegJf7RZvYFJWmSkoaulkyTiizafrextMD8JjMIfMPdl010V8QY+K5RNLdAPeU5GFLxT71gbLUmlwxJ17m+m3+EOLAJZc+YLs4WJ6dLyjQooDJ0/YEUMOsHyAeO88Rv8R9P7SQ6KMLXydHXBC8vj+BYJMvoVQhCRwwAFA5Ys6DSz3tDxeruX1LnslGG99eJHVS1RjM5Gc9WxJ/4x7QAFgkrMA== MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-3174-20-msonline-outlook-529c7.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: MN2PR04MB5981.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: eb960891-55c3-4afe-2c7f-08d994419009 X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Oct 2021 03:19:15.9481 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-userprincipalname: softworkz@hotmail.com X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR04MB6238 Subject: [FFmpeg-devel] [PATCH v14 04/14] 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: KsGPILSm9Az3 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 94772576b1..5e9a97ae55 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 Thu Oct 21 03:19:18 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 31186 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp127394ioa; Wed, 20 Oct 2021 20:20:21 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyZoGtZXwGVI2/xPf97sXOzFB/FFC+u64KQpjmEx7IOQ9SUZS6G8YiVRxH1hyIGzmEw03+E X-Received: by 2002:a50:ef10:: with SMTP id m16mr3994806eds.224.1634786420843; Wed, 20 Oct 2021 20:20:20 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id v5si7766536edi.595.2021.10.20.20.20.16; Wed, 20 Oct 2021 20:20:20 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=qqkTNmPM; 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 6D03B68A7D4; Thu, 21 Oct 2021 06:19:32 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM11-BN8-obe.outbound.protection.outlook.com (mail-bn8nam11olkn2034.outbound.protection.outlook.com [40.92.20.34]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 64CB368A5FC for ; Thu, 21 Oct 2021 06:19:29 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=W7G5TfG7Uh4cd+8WKwFK3hfs2fBJegS0kAehpK3ONHESQyfUEQLKf/hGsiHpw+Nsw2V+5RsWaKhahEmAV7/YsCDb8JFNtLnuBqoYMJRrOAtjirN90agS18A1xrdjdLlpuW308I/f96sisWGkb7dkO+hKeOzOVypURG+7+ryb60JI9Yfck7rHLKfsjum8lkNnLXEytCM/FGlyocGKR9Lgdnp7uLSUu0pp4eH0RMbLjs5R0+IZc4cQyZmmcOMDWhIs5XGkSPB6zvF5diFT6RqKiTqogyp+yKBU6/0daz39Cneicw/cHEcWc9tFKjye65ZZFJ1zOPhF0jBOal05Hrgagw== 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=bDL97GB4jejrgjmRzhDsP7shYz99S7YBdbklbSFNiW8=; b=J/eW81Df1iHX8PQtcC9CzlAgZAIp857/TSM7Cs4JMQ/eBYflWAp3RoVgrD9tMTsicSClILs4L/u94Dp81fU/fIE2eaewOUOx5jNkXG1pWp/db48meknPIurfYKAuTXTbzL3DKWH4zrkJ+TxaklaOQ0ozBaL+HNhK2BrCW7mKQbGT1j2NGlpLfvfNKXGw162c8Vo5nvAWaKeNk9CvR/XP0lGMKiDvY+X0yV7XWbo1qOcB8MgC1n0hJnWqjR8UQKqWjcACSQHsVaPMeHxGnCH2FA6pmHRPkfO3P76tU9VwmrcP9ak0flITj7WfX9Xq4p0ip2Yl9u05MVgF+gRUwd5r8Q== 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=bDL97GB4jejrgjmRzhDsP7shYz99S7YBdbklbSFNiW8=; b=qqkTNmPMuYGlhQ9TdNjj6d4sCrNdQSMC+rzWR5+oA4jD/Djp4EssUTqGvs2ypIQIJK8UC+hiZObfBTj7BEMILCs6eKskP59Nk55vfZp9sRdyk4IclM6q7jgoghYdZKy8Q2x3t72ywcNzp0ZgI699AllowiJFuAMEXnpNOXayCCgPDP56UBp3tUHfs9I5lCLnBU8uqtceuP8o9vnui/CpFjaTJIHsZKWCONlP2FQUO6mfb/NP0/01c+TcQ998jkBLqnSmmxBkbHOVWMSrHrBeqKQ0OPiKrsY2Lp18g8VZVCzHKim1vp58IU9AUXR8eaH2nf3A0PzL62+0pQA9P2E48A== Received: from MN2PR04MB5981.namprd04.prod.outlook.com (2603:10b6:208:da::10) by MN2PR04MB6238.namprd04.prod.outlook.com (2603:10b6:208:e4::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4608.18; Thu, 21 Oct 2021 03:19:18 +0000 Received: from MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb]) by MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb%5]) with mapi id 15.20.4608.018; Thu, 21 Oct 2021 03:19:18 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v14 05/14] avfilter/avfilter: Handle subtitle frames Thread-Index: AQHXxipvM15Dpqu6DkKxAyggORWVIQ== Date: Thu, 21 Oct 2021 03:19:18 +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: [mPmE6+//9bhry4s5iA5f2qSNbeIIr1h3] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: ef8dfa6e-893b-4dd9-a4e2-08d9944191cd x-ms-traffictypediagnostic: MN2PR04MB6238: x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: HipOdKxyT7d3AfLfvcc40Gx8rhFyQUmKKZrsLrAY4NCQg8T8kPiRf0R8/ABTc34mfpDdoYVzL931eKqgVFeJHyjgnQ9X0xO6bDXX4qi77/yCboKloPXYtsWjSfR81tcyr/1K1s/Q2lejELnX3yDgy3K4EncXZakWDPW1ed1Qt5jNTMewcnrcZYtyi/OdwgZbRjq+dtxNebM7IOd18/fjnfb8UzuR0Dp85D2gRiuhF8qzmPWWFTQr26QEC9Dw4EkSQXIOUH3SeXHGi221GUeKlzp207nMjIszSr+ypOI5x3tdrfIi4NLg0p22Mb+ueihAza+XRJ5npD/uazdFOGdF+AqpYo2Y7k65Uq3CS9VWvS0HhiIbPzMSUwvlyiXfGXR89FKpbcdSKQ28phlB9uoFrJVVbt/1GODAXaNSDb/MUi/01T46luorMo0s27TOVC0YlDdMvnLrMMBwv+2mgJ1ThGlr9x5K+LuN9XjFBAx42o1JzlnfBj3ihCHY3m7Yx7EuNMVrfrCxD9383nkdPQWAAnXkeX2y0v6g+kR08aqGjNc= x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: E6NHBhyC5TC16TJnItMvK7wRRlj9Oj7akIu6OaGRcpA3tpBp8gITfZIbpl0r4Z8yVQi1Xx5coDhehxAy+vqMT9XLkCJfBtV+z+4htNjAJI+DtqDoRZ8ihEL3ctHjmbm70urNd7Lzzrnh5kZCWgmHnqsiouoXP8A+eIg3jvY84T/C44TZ79GrZ4/h5hIFZifB7jXHxMdFXxkRVMefVoQRyL4vHL3uC6jDF5FcS8idLhSb+rgRUkVXUP4sN2d9sxDEApWs8ohud7NzTQdpfw8yhnqC466U/P2zascrSBCmDQ3r/BetejIg9S0fUHTHwA8Nqj+xEbggaXeYZwdPXnm+5sp6dZRzA2JcJWR4Nfx1gEvvH2hiJ/OzKrXG3XzBPy5uk+WHMLNOWngu2GJam/thYUzFjcYKTgaw0DxcCFTFJSxzNMoGUCJdI5unU35MvItDAlpfxey2G+CIhgNYu1SnFphTM2ZJYd3aHbw4ACwo1djNrUnEuGag/oTVHDy3DO5oq+Niw8HnOgbOsdeGBxfb3r94LloquOCkXPFBatt4xiYQ5yiMTg43egHbqeZRTcfo1WcRO2ceG3O4NAvEtEMMQsFNoJBCTP2QJvoZL1NGMYjBCX+0rM9LF9N+1gfZK9kLU02z7SOI0tnwaqkteQ9PQMlGAudTyJahrR3ETd3DXVBX4EtzBNEgc9j1eeatI67j9Xs4ESzzagLSfuIfaksAqg== MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-3174-20-msonline-outlook-529c7.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: MN2PR04MB5981.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: ef8dfa6e-893b-4dd9-a4e2-08d9944191cd X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Oct 2021 03:19:18.8754 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-userprincipalname: softworkz@hotmail.com X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR04MB6238 Subject: [FFmpeg-devel] [PATCH v14 05/14] 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: g2vbVbNCCTQ4 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 f7208754a7..ee2f5b594d 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" @@ -327,6 +328,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. */ @@ -335,6 +342,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..96f35a792c 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..46dbbd2975 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 "libavcodec/avcodec.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 Thu Oct 21 03:19:21 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 31190 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp127748ioa; Wed, 20 Oct 2021 20:20:49 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzqXYHY1Abd/tHUcpqBP86O7RKR+fikcKGhJOhUsdbAsGT865FTkjzf59fOdZRw/G/8FkCW X-Received: by 2002:a17:906:9554:: with SMTP id g20mr3929428ejy.173.1634786449723; Wed, 20 Oct 2021 20:20:49 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id o16si5372076edi.503.2021.10.20.20.20.28; Wed, 20 Oct 2021 20:20:49 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=LchisB9O; 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 8B68068A58C; Thu, 21 Oct 2021 06:19:33 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM11-BN8-obe.outbound.protection.outlook.com (mail-bn8nam11olkn2034.outbound.protection.outlook.com [40.92.20.34]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id C0DEB68A5FC for ; Thu, 21 Oct 2021 06:19:29 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=hBy56fBg0J6mnqWx9+zRKjVRc0ahTQBTIQmpTobRdyNOMAl8+MGuUSbe6kn9XfGo9PO1GfmFWb2mUsc+ugjc1nu4FbhkaBDnRWZXW2dm7jeXZtygtAue5gzGg1GuqQ+HYD8355uGjxU9aAGJhCf609Q2ov5FmTxePw9k/G0TzBPoOV4aZoCzhud+m+zyzjX+7/LhXFpOlbQaJF4ElDGNc2Ff4kYhTd1iFbLauhRbjsz0zcKfv7huJiTqPDGGGl9HY4+S34X2EYQnvXMJ7tUOrzpqdguVzqWx+nk+fjidFcQ+PoCfw9bB+GwsOFWw6CVevTO7hj+wZg73M5kDaliCXQ== 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=v+1X7D8o1uujsAjxsH32MPWjPWsF2JXB5O5GuNy4Ec0=; b=bQMVnWl9eyUOKBgv4/x9GkX+KSvgf3RaLNw54nEj/TmI09GAhfzHvLQsktzJzwpxE28RiK4jtWctsHA+bCuTClmLtl1gq1AZxJgXZYt0ppVNs5d86osjG40Dm+OPvEFndi9F9zllgKhAJjo7o3cRF+vy776/G+0eX+u0rYSTqPvbDzSKRK6cVt4v506i0/Egl32+F63Oc2Sr0OD8P94rtaTcDk0vd0i5ydm8QwsvtuLUCLLT37dwgcSWY8pFEJsGAal8oKytT3uAfxkABibF9EgKmTn7Y3pSPURKzhDzTEeuZg4Yh+IgQPH2YFtEnVo6++sUaYhuOIK5gJvjUBrdag== 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=v+1X7D8o1uujsAjxsH32MPWjPWsF2JXB5O5GuNy4Ec0=; b=LchisB9OJstIhyaCM12fG6X5R/ba9o+Qtv8xA24xJZHreBcKNGRbk+DhpD5WNi6Wtcady5Gyy2WyHIG/swKSqme8e+L6slhjMFDzoOqzvAPoWa+5NHEi7oyeUlafJ5RIliNn9BBad7hj9g/n/yzQnUevVpPV2MWNJNY1vAeSZgOZQmoCeh84WNypGOYCyLPLaTwrM6eIrI9CpOUMtUt3QMjC2hLDRoXpYuqBowSkTOmj7yaqvaJc7oHdsCZ2S8z224+euuuNNa1gpxN1QSCSUu0A4lgNJl5moU0yxR5TOK0suC6oBCFrV7pshqDhNf1IP52P4Wpq0Kxw8RsfhTCGeQ== Received: from MN2PR04MB5981.namprd04.prod.outlook.com (2603:10b6:208:da::10) by MN2PR04MB6238.namprd04.prod.outlook.com (2603:10b6:208:e4::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4608.18; Thu, 21 Oct 2021 03:19:22 +0000 Received: from MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb]) by MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb%5]) with mapi id 15.20.4608.018; Thu, 21 Oct 2021 03:19:22 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v14 06/14] avfilter/sbuffer: Add sbuffersrc and sbuffersink filters Thread-Index: AQHXxipxlybSVn1e2UWL0THt6jl6xQ== Date: Thu, 21 Oct 2021 03:19:21 +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: [ztpoefyMUxvJpSMEY30TGZnEEnZVM889] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 1538b099-1472-40ce-fcb9-08d9944193b1 x-ms-traffictypediagnostic: MN2PR04MB6238: x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: KnUUp/2sL4Zuxz5Zndbu+rLsbwV6+f94pUzAoyPCVMyqOSOJw9fFLlEUAQjlfB8SudXEwd6XGK5+sOnhUE9FajpB5p9Z7YBrNWNx38DaqenShVbPGpWbNcvEo7nBhrNa2C92h2dk/eNg+cvnFLyof4krhu1+U6Aze1ruS0zqHIK6NHHo6UWqPlc01Njh/5pO/zR1ZeO7NRoQvdHhkNAZ2xzM+K0chmhS7mSXbhUcDd5jLucoybuSXcYAZLL2eQ061BSlzDrV5+4m91my0VDbm9AAv6wIpXXx6bR+NP1FhtkM5CGqXGFrGJAvjsicfN55ibwPhAU0A4lf5hIZaBQxLWjZRDIu9uBtenO7/YCcgPxCJgaiVAKZ2u65D5iLnlGrHr4TgXzfR5hvBmQ0KmfAfh/ryCa4PGkAFFgjVXd4fwetFlUZGruDJbtC41bfbuc98i4NOyfmfq/QSMCSrL8BEqIjS+Ruj+/1QpfW/NcU/7C2JkjmDoU7TqOuEmHo+oJWmS8L4JCNOnUP7SvesV4VEPlHFftkIZYEDuvKNU82bvI= x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: rWoaDCCMbKg4ox8A2dZBkCzQI1PS84+QtS16tLOCFoEwhHcCEh2JpOZkADZKqlu+pEWHCpLR277ApfxWDZVBvMzEARoKi4+mYQhKiLAyMEFKGNPAiiYgJ3Fbj/SDKFp17XhuBPhVWoxOZ2/daaAgjAL7TXUjQdfhdtzvx34Bn/zBdB50Rfz931Ayj3Z5ywFUwcM9zhRjcF0HSbDuAeDayy4TR4E1bJdVhbbjj8iRglg0Dpr3a2WgEXApmjQh8ztgwr5juA+LwYtmaOgJq7B3NK9utl4RssPH+am0HP3XstR+bmLr9BEhPd7Ewypmfyy+65kaE7tMO2wEwgPJL4MzhB1XR435JZ0r/W7A4YJmXSAPVwhPKIMpXKLTzThFE8hsd2lDvy3kCl+aF4f8EL76VuqHBjyn6t7A+Gx6XcSYb+EiIiftIPexz0+NYMjZPPv3B9invQkM40nzH+Thk+0Au1Y18dzDJQHMi7RauLHoVYciL6hv0Kpdzv7h1CYAvHdUogHiZKNDa4HDMml168+JRn2tE79gj/AQLdXIEcULpYPq++a1TZcvxRwThZFQCW0vKouN4RkI0ofQvUt5y6yS/T9h0DN8GRY/Cz1bDegcS3NL9IobxATMBKwRVzWRe1/HTiuj+ZdRQi/UNnk2FH8cKXl22aOn37gCO252y8my2TN4HHoE5D5CbOgfV4/xQaLJEGTTSFbipvqJrbzQPXVYUQ== MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-3174-20-msonline-outlook-529c7.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: MN2PR04MB5981.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 1538b099-1472-40ce-fcb9-08d9944193b1 X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Oct 2021 03:19:22.0046 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-userprincipalname: softworkz@hotmail.com X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR04MB6238 Subject: [FFmpeg-devel] [PATCH v14 06/14] 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: xIbn1CdNdsTW Signed-off-by: softworkz --- configure | 2 +- libavfilter/allfilters.c | 2 ++ libavfilter/buffersink.c | 63 +++++++++++++++++++++++++++++++++++ libavfilter/buffersink.h | 15 +++++++++ libavfilter/buffersrc.c | 72 ++++++++++++++++++++++++++++++++++++++++ libavfilter/buffersrc.h | 1 + 6 files changed, 154 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 92610c7edc..a97cb41fc2 100755 --- a/configure +++ b/configure @@ -7724,7 +7724,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 9c99f816d3..a97c1c26b8 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -543,8 +543,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 b8ddafec35..8306312acc 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, terminated by -1 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; @@ -168,6 +174,15 @@ AVABufferSinkParams *av_abuffersink_params_alloc(void) return NULL; return params; } + +AVSBufferSinkParams *av_sbuffersink_params_alloc(void) +{ + AVSBufferSinkParams *params = av_mallocz(sizeof(AVSBufferSinkParams)); + + if (!params) + return NULL; + return params; +} #endif static av_cold int common_init(AVFilterContext *ctx) @@ -305,6 +320,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 +359,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 +407,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..b439b586c5 100644 --- a/libavfilter/buffersink.h +++ b/libavfilter/buffersink.h @@ -129,6 +129,21 @@ 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; + +/** + * Create an AVSBufferSinkParams structure. + * + * Must be freed with av_free(). + */ +attribute_deprecated +AVSBufferSinkParams *av_sbuffersink_params_alloc(void); #endif /** diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c index b0611872f1..8f22587f42 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 avfilter_ssrc_sbuffer_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_SUBTITLE, + .request_frame = request_frame, + .config_props = config_props, + }, +}; + +const AVFilter ff_ssrc_sbuffer = { + .name = "sbuffer", + .description = NULL_IF_CONFIG_SMALL("Buffer subtitle frames, and make them accessible to the filterchain."), + .priv_size = sizeof(BufferSourceContext), + + .init = init_subtitle, + .uninit = uninit, + + .inputs = NULL, + FILTER_OUTPUTS(avfilter_ssrc_sbuffer_outputs), + FILTER_QUERY_FUNC(query_formats), + .priv_class = &sbuffer_class, +}; diff --git a/libavfilter/buffersrc.h b/libavfilter/buffersrc.h index 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 Thu Oct 21 03:19:26 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 31184 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp127818ioa; Wed, 20 Oct 2021 20:20:57 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyd2iSW59LAdSN+HlydMfkP+bD9kKA63Rv6PThDgrouZk02pqH+JokZVL6VpU/o/7mN4OeN X-Received: by 2002:aa7:cd13:: with SMTP id b19mr3880378edw.219.1634786457064; Wed, 20 Oct 2021 20:20:57 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id y16si5361771ejp.320.2021.10.20.20.20.42; Wed, 20 Oct 2021 20:20:57 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=M1AXpS8G; 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 C7EB668A84C; Thu, 21 Oct 2021 06:19:34 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM11-BN8-obe.outbound.protection.outlook.com (mail-bn8nam11olkn2034.outbound.protection.outlook.com [40.92.20.34]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 17E2668A5FC for ; Thu, 21 Oct 2021 06:19:32 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=bO5699iRSD/TGSUQ5YcwPPA7XTUXbYipv/grDPYyNYFIGDpRWSe96SYZDfKcoWxRhG/F79Gz7g5gHxFoqzzl08GT91ESWD8f/22Vh+wyVK9AmK9U40Oc+sovaZcQ0S+RDBE5qH/rOpaOsaREdub7htP4cIQ45ttCGH7TYQFSyZ8SR4X2mEhP1pZT7zLeoXFdqsFPstViBADQBYl07VfsAme3X3Yrn58YytJzArllStNTkguFYTcBc2vqtS8yhDYCZ9x1c7/jpoNfv3Dg5jzq8XVcZIYQ/RzPh3wCKEyalv6+J1VQONSMZMYRhiFFrHCVH/ZD3iFh+I37U0QJOHDtFA== 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=typePDxlkZHse1Xc9O1iBVZXbzQfWnt3djHxZ7doY+M=; b=ViirV5Rp3CQ9sq1zeP51uLNQIkxQrgKUA/YpHAtJOoVlZCWpRq9TrW8Va3+Ylvr4QBBjNBjUitoqJ2Dp2WiPtuSgP1J8SbIR+mX3rWq4wkm09XvtWHQTzPjcZfNeGOlPJnIWs62tXI7XqGGux5ybKB5j10CB7HsO+uJ+6DPWkLzhlchdPivdDbCQI/oYelJhSeOydRo6uHAaslldZu62aLiMfLBk38HVfxD3+rzQdO9B5c9TyLFcABHizhFbioMwOHM4LMDQ2V4PoYUgEc/6OB/O0V6UTZQ85qdmCT7ICc2vcgVZQd+n26LnPoDD21ZfzvFaVfcsbLAxyb0gU5Z7ZQ== 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=typePDxlkZHse1Xc9O1iBVZXbzQfWnt3djHxZ7doY+M=; b=M1AXpS8GqDE0NjjqsB/e1B8gsDOEtORwuMJzObAzfrzbMR7a3uZ+P6bc1g7Nsv1m9aeXL5qe2T/6HBa4MNaPR0vPpRBsgT6NBTkurPLBHjNwU8hoPgnFVa8O3moYla9+zZoMP0preCMnMSIs9WCxFO4UiFJX4ktMOV7ElUOkClERibKPblXhXW0Sr2I6B4qkBnBx2902A8Iwj/uQsgO5B4DV12jtVKUISGWkDeoyFrazYuAEspAmpQHusvffmXTqQbf7XuBKfixKsIbOefq78l8FuSEhgJ7YyJMZsxAQ8s7fsq/JReqMfzRViOb+xZyACQg0S5HOcYJ/eopwSAi/0A== Received: from MN2PR04MB5981.namprd04.prod.outlook.com (2603:10b6:208:da::10) by MN2PR04MB6238.namprd04.prod.outlook.com (2603:10b6:208:e4::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4608.18; Thu, 21 Oct 2021 03:19:26 +0000 Received: from MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb]) by MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb%5]) with mapi id 15.20.4608.018; Thu, 21 Oct 2021 03:19:26 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v14 07/14] avfilter/overlaygraphicsubs: Add overlaygraphicsubs and graphicsub2video filters Thread-Index: AQHXxipzpYEyIry+T02imRQ4dsKaHw== Date: Thu, 21 Oct 2021 03:19:26 +0000 Message-ID: References: <4f225627a420d32b8766306e418c45bc8af41c47.1634785894.git.softworkz@hotmail.com> In-Reply-To: <4f225627a420d32b8766306e418c45bc8af41c47.1634785894.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: [FXzZZfWuBEMpQRvbieTTqZ3MHItlg9Ie] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 47ec075e-0945-4246-2f12-08d99441963b x-ms-traffictypediagnostic: MN2PR04MB6238: x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: gGuqSFQVYtxbbvljH8eX2kFOKUPL4CDRZDz74YZ9h3wsEIghE/1kLYh2eSOUZfcbeHlnwG/CtABLU76QHv8hXXeEmgNaG58Q1UPfxLofDRG6Mselj+nPDwOg8vc7e41hsU/51miut2WQjW0D3o7JGevy+tid19iWQ2xty7DI2nZ4pQkXwOm8fI77LkE74UMPXsl7YuvyXwa1TaVWqTs+MYXLMCb4HLBCpb6ugcxfb4ptjv5eV9r2iBWYmDOBV5D3d43rcY+Gab4/4X6lnqgASZ0fdPhjayUS2RXjJ2NoHapct/oL9VCJpGupS710P2DrhxMV7u3Im8ttSuUki33O1kRJt5gWpRj/x+ddIRg/ONTlJ6GhsnH5AxX5Az/cSVx3UYY1dh1dS/zhFszhMmD7ShSUtCuTYCKgWKY6MgHc6hb7HYeWy0Cw2yUkKMx2JQkU98Wm1GgwExETza4+MT0pvvRxeSiHM8CqoSI+KA0bEa7bORUgU5Obg8RHqk0CsTrEiSwB7jdkIP6+As/LvBEUmnf56Astuw3qEisX+B0H5r69z85bWEcefS0LshZ31qo0hcCapusIYjZJPO77Rlpmcw== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: iuJXXbimNGhxCX5BceXm/OiIjlWNRpCr29ig5SZ5xcXgwq0uEhqAzQot616/rET+2BoxrVAOsZ8imobFRfOyugzqjolymN0tSIxFx7hTrJUpJd8SNuZvYiPFTI/CSP6fzY+ElcYU6oDfzykyOBr8C3rPyylVsfPIuMzbcawo0NYYeLHInS0eeeBdeIci205qlLFya3Es9jJa291GfstDC9pXAcGwVYyBf0gTzfhueTnzUKg3MWgNfDMd7BYyPS2hHHqFY/k5itkf0gjKvAHUKCDUrC/9ffpVGPMh5k5BnxAxeOGh88A5wad/EazpiXP/BNeYpeZYk40g1vGZW2a0RvEYp+Fv5hlmJLFW1kVlCt6CJN7kfSI66SV+2tVubkYyTGzYXEHwzusaM7Z5HJZQoyQXzwPVk2ZMbPMG2nRHzHmWusx9OJ2FP4Ws4NXNfz6OnbPUvAabD480h2fjHmU1hua2RNT5s5AjMcYoIfyzuFOiUUlKSHOIK7xi6mHBnJS9U+fyz/6nGHxX3qfNEYDwHIzR5cBAqGbGRMZXFhe8WTNFZz/KSzkGkHCl4M10cgg75hc/BBl8U0Sbdq3pAoNp1W0aaxP/weRMUL3qhXVypbpbnaIYsTZ23wYVR6Lhs8EO4gvnTDZhGbUoWrWu6Be8O6vXGWTxk5w9nL4dNMEhnKl7Ix8LzOiDhNvs8fLz0wV5tuPb6NlJHXjyltpU79Sm4g== MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-3174-20-msonline-outlook-529c7.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: MN2PR04MB5981.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 47ec075e-0945-4246-2f12-08d99441963b X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Oct 2021 03:19:26.2352 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-userprincipalname: softworkz@hotmail.com X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR04MB6238 Subject: [FFmpeg-devel] [PATCH v14 07/14] 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: bjzDiPONaziX - 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 22e604f712..8b94d21661 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25316,6 +25316,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:9][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 5e9a97ae55..db06a42122 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -292,6 +292,7 @@ OBJS-$(CONFIG_FSPP_FILTER) += vf_fspp.o qp_table.o OBJS-$(CONFIG_GBLUR_FILTER) += vf_gblur.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 @@ -369,6 +370,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 +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 a97c1c26b8..7ace7b6ec6 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -350,6 +350,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; @@ -532,6 +533,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..fa0b7107c8 --- /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 Thu Oct 21 03:19:32 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 31189 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp127908ioa; Wed, 20 Oct 2021 20:21:09 -0700 (PDT) X-Google-Smtp-Source: ABdhPJw8+BGWDgkmWvkuPjiARAfukBE+V5o9oSkqo+OGVbnqK6i1ItUMDSmOZ/rNY1mPUXNZdpj1 X-Received: by 2002:a17:906:ecf6:: with SMTP id qt22mr4083911ejb.292.1634786469433; Wed, 20 Oct 2021 20:21:09 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id b14si6615188ede.47.2021.10.20.20.21.09; Wed, 20 Oct 2021 20:21:09 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=SCpSIdjF; 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 5A1AE68A7DE; Thu, 21 Oct 2021 06:19:42 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM04-DM6-obe.outbound.protection.outlook.com (mail-dm6nam08olkn2026.outbound.protection.outlook.com [40.92.45.26]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id C2A0A68A71A for ; Thu, 21 Oct 2021 06:19:39 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=GYz3YtpcpULG8lMxyiXhxPHnMW+EYo9VXayg5Jnvn2VQvK2E5P/Myv6vlix/+7rMzsy/3c88Mu2BNuNPzcLyzIMmdl+ztS4qA3LmUYLRnNm7sh06T2FkL7/UU9jYOyI5WL5Hj03UvLxLA4R790CjMxG4fUYARblA68cdNjLM4cM6IgtyqD8OP7Og5fNu/4Sw8HNp8hVoHJcxH1ZD33BdydmzN7YxOxb2YV0iyO3X117HpP5shaYf7vLthwJLzjZZQ+t+ggqRks0yMSww+Jy4Y1521GbAdT6Fy9LrFMRRrz/MifMCAg+A1mVb6bdbZxqaDfPHjBLRIX4ednagiiAqSw== 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=KNA5shOAKGcgBMZF8KtWVSCUUnKKxP5wyrHPlNYcB/0=; b=P3J/NwR8Y/DtER3JNRcVnDSDjsWQgFx6/SMudMEDxi37+qn+hLd/9CuBTzswAdsRx3PwHFnHb6HOExRoHBqpaCvTaQtiD/I9s9W90sYyKqo5olc+62XROPnQk1FP+1Pp1avYspNIT5DPGRwcjJ1XdZPOl1gi6/iD/iuODdb9vC+y2WdCXQq+5UHChfy9DEEr6Tlbigqb70EC2Ivd42XBl2I4YYdSueoJqJPxJhG2v6bHtT/utM4F+H2GnPYxM7fXjRdUfxuDhkp90SuNOyy75QOMokEZYT/0rrkh5CbnEqjaP+xWxtIWlCxSRuknS5U7raAnSAun6yQ4KaAxnqNz1w== 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=KNA5shOAKGcgBMZF8KtWVSCUUnKKxP5wyrHPlNYcB/0=; b=SCpSIdjF3AB1c4kB4WiDYvNrlc50Ofpdx23gTqZ1kJX+WBmsExU3gadzQYyjvHPQkAfQ11kxsGDWOcLEUiAZVjFfgUgoIRcY/S07VbVqwHA2xhq9DD/wHgPLA4yN4edxkIPW9B6kNjkkm207Ir+fWDJ8YVaTRPkbW6zkkIneV6MDw5CNTINVm4YorsZ2V1o8ljT6yQNt+ulP947EXXMS3nhrYt14v0utT6wG/mTvUtGYD4fp/rzhHv6fE8e3HosPaTD2S1CvMJNmiStf6rCvEs3Y33BMSFSC6B0R3KGr9GfB0ZSD9dzUl6vuI9doyEwnbkNWCQiv7NJQQxqEtbGPFQ== Received: from MN2PR04MB5981.namprd04.prod.outlook.com (2603:10b6:208:da::10) by MN2PR04MB6238.namprd04.prod.outlook.com (2603:10b6:208:e4::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4608.18; Thu, 21 Oct 2021 03:19:32 +0000 Received: from MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb]) by MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb%5]) with mapi id 15.20.4608.018; Thu, 21 Oct 2021 03:19:32 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v14 08/14] fftools/ffmpeg: Replace sub2video with subtitle frame filtering Thread-Index: AQHXxip3UPZOe4c3cUatkyNDalX3SA== Date: Thu, 21 Oct 2021 03:19:32 +0000 Message-ID: References: <4f225627a420d32b8766306e418c45bc8af41c47.1634785894.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: [onh725zyYX9WOoPr6vLrXG+hFjTFmNqm] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 12b23656-81ff-442a-0493-08d994419a16 x-ms-traffictypediagnostic: MN2PR04MB6238: x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: BVLJhPUJKDS8wfg/LpqPkevmW/Wn2+mLGId/+0gPK5jgZgP4PF5wM+OubeTdwDe/xNP2qsqsDV5AqKdQcljuN/Uq1JaWa8HVjGA/9vfhubv+ET0/tHyRly3SE9Zuoq1yAycnCcRcpHBnxQIjqv6RAcgvyty7J+6D4TSfaVd1Rzq4Cq976jVQG3HUKFMogF/jD3vKQhAF3zXl4eZe+UukBIdbgwLFK3bmfD+fK4n9TX8VZqNEEeeBWOuIdPcaYJGY1HtfxuoC/H8yUR8+xzM+lfvEhp2Ld3a0keEvNvleg83ZQL8+tJi8IjwdfTLjHVk4J+IttaDTBGS3DZF4WC6UwmH02VY6qjA0rj8LPd2DeMTmycNlD9U1v+6LY9vKTt68wJT2zUHcaewHQCf1Y18/BuWLkZ+djzJpeF+EkWUHHqc= x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: P71wi2j6klevZ17SKeWAJTs0AlBpcFSYxU85EE+l3G8SLE9iio1fTNIi4tpjLj1gL3UhNEq3k8ki4BtJff2DrvBUkDrUXdYj6QAJMv2SFW4MjQQeC8EsGq6NXUHsKMMLZfNzhqlZgS2b9BY86R+v1jTuuFjZmFEjWToABxYtzhKDF8HdowZenG9wG5CPh49pC1DZkbuDgMwKFgCezs48Oqd8Y/N2vdYC6OGMFz/aq1myK/ISgq0QyB6OjT8QYkJmx/uao7ULXddXK57GrodFBgymD3D/av+xyUnikic+QMhYwiqlzxTwpuNZu3j0hvXwm8abG770KlRGo/I78FGU65Cqf+CZf9MNP5wsVSM2P6rm9uayikZg3NfOdpqvUWeJ5PALGZ3N6n+4tor+eMj56kLVNqmPll3QX4vMUJ+C0UveKiwyq9lGXTjYhxZnEUdVcgWNP3aeqnhvs+md4+veBXL0IrN2XbdIHUiD+BOzJupoOlcooAQKFd9CKfDOttItotZyBI4Ro5Gw/YbyGvNtUIueWVD6LyIuIr94XmOFvk4aOK1qe2hX9xG6Fqn3l5TpBSI15hxoTakt26u7re5MHUVOqbuU0AZvJESyI1xvuIz0ghSP1dT/cGUsMfZtfpDIVZgD1GpsOwtirK3Awp6pqcz5Tb6I4oUgflpmHLBTnWy71jhREuR8bhwETZnAYF62E/8/mYcUh2a9U44vbYQPhg== MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-3174-20-msonline-outlook-529c7.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: MN2PR04MB5981.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 12b23656-81ff-442a-0493-08d994419a16 X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Oct 2021 03:19:32.7475 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-userprincipalname: softworkz@hotmail.com X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR04MB6238 Subject: [FFmpeg-devel] [PATCH v14 08/14] fftools/ffmpeg: Replace sub2video with subtitle frame filtering 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: aHkVTlj4Ckc9 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 | 525 +++++++++++----------- 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, 686 insertions(+), 674 deletions(-) diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index 9d4f9d7a2b..b4def3e568 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -169,163 +169,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 +369,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 +378,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]); @@ -636,12 +469,14 @@ static void ffmpeg_cleanup(int ret) av_frame_free(&ist->filter_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]); @@ -1059,17 +894,21 @@ error: exit_program(1); } -static void do_subtitle_out(OutputFile *of, - OutputStream *ost, - AVSubtitle *sub) +static void do_subtitle_out(OutputFile *of, OutputStream *ost, AVFrame *frame) { - int subtitle_out_max_size = 1024 * 1024; + const int subtitle_out_max_size = 1024 * 1024; int subtitle_out_size, nb, i; AVCodecContext *enc; AVPacket *pkt = ost->pkt; + AVSubtitle out_sub = { 0 }; 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); @@ -1095,51 +934,58 @@ 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; + + ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base); + if (!check_recording_time(ost)) + return; + + 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; + + av_frame_get_subtitle(&out_sub, frame); + for (i = 0; i < nb; i++) { - unsigned save_num_rects = sub->num_rects; + const unsigned save_num_rects = out_sub.num_rects; - ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base); - if (!check_recording_time(ost)) - return; + ost->frames_encoded++; - 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; + out_sub.num_rects = 0; - ost->frames_encoded++; + subtitle_out_size = avcodec_encode_subtitle(enc, subtitle_out, subtitle_out_max_size, &out_sub); - subtitle_out_size = avcodec_encode_subtitle(enc, subtitle_out, - subtitle_out_max_size, sub); if (i == 1) - sub->num_rects = save_num_rects; + out_sub.num_rects = save_num_rects; + if (subtitle_out_size < 0) { av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed\n"); exit_program(1); } - av_packet_unref(pkt); + //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); + 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); 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); + pkt->pts += 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); + pkt->pts += av_rescale_q(frame->subtitle_end_time, (AVRational){ 1, 1000 }, ost->mux_timebase); } pkt->dts = pkt->pts; output_packet(of, pkt, ost, 0); } + + avsubtitle_free(&out_sub); } static void do_video_out(OutputFile *of, @@ -1572,8 +1418,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); } @@ -1976,6 +1840,9 @@ static void flush_encoders(void) AVPacket *pkt = ost->pkt; int pkt_size; + if (!pkt) + break; + switch (enc->codec_type) { case AVMEDIA_TYPE_AUDIO: desc = "audio"; @@ -2168,7 +2035,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; @@ -2272,7 +2140,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; } @@ -2310,7 +2178,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; AVFrame *f; av_assert1(ist->nb_filters > 0); /* ensure ret is initialized */ @@ -2527,81 +2395,217 @@ 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); + + 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 = av_get_subtitle_format_from_codecdesc(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_WARNING, "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); +} + +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; - check_decode_result(NULL, got_output, ret); + if (!ist->decoded_frame && !(ist->decoded_frame = av_frame_alloc())) + return AVERROR(ENOMEM); + if (!ist->filter_frame && !(ist->filter_frame = av_frame_alloc())) + return AVERROR(ENOMEM); + + decoded_frame = ist->decoded_frame; + decoded_frame->type = AVMEDIA_TYPE_SUBTITLE; + decoded_frame->format = av_get_subtitle_format_from_codecdesc(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); + + 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); - } - av_fifo_generic_write(ist->sub2video.sub_queue, &subtitle, sizeof(subtitle), NULL); - free_sub = 0; - } + decoded_frame->type = AVMEDIA_TYPE_SUBTITLE; + decoded_frame->format = av_get_subtitle_format_from_codecdesc(avctx->codec_descriptor); - if (!subtitle.num_rects) - goto out; + ////if ((ret = av_frame_get_buffer2(decoded_frame, 0)) < 0) + //// return ret; - ist->frames_decoded++; + if ((ret = av_buffer_replace(&decoded_frame->subtitle_header, ist->subtitle_heartbeat.header)) < 0) + return ret; - for (i = 0; i < nb_output_streams; i++) { - OutputStream *ost = output_streams[i]; + 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); - if (!check_output_constraints(ist, ost) || !ost->encoding_needed - || ost->enc->type != AVMEDIA_TYPE_SUBTITLE) - continue; + ist->subtitle_heartbeat.last_pts = decoded_frame->pts = pts; + ist->subtitle_heartbeat.end_pts = end_pts; - do_subtitle_out(output_files[ost->file_index], ost, &subtitle); + if (ist->nb_filters > 0) { + AVFrame *filter_frame = av_frame_clone(decoded_frame); + if (!filter_frame) + err = AVERROR(ENOMEM); + else + err = send_frame_to_filters(ist, filter_frame); } -out: - if (free_sub) - avsubtitle_free(&subtitle); - return ret; + if (err >= 0) { + for (i = 0; i < nb_output_streams; i++) { + OutputStream *ost = output_streams[i]; + InputStream *ist_src = get_input_stream(ost); + + if (!ist_src || !check_output_constraints(ist, ost) + || ist_src != ist + || !ost->encoding_needed + || ost->enc->type != AVMEDIA_TYPE_SUBTITLE) + continue; + + if (ost->filter && ost->filter->filter->nb_inputs > 0) + continue; + + ////if (!ost->pkt && !((ost->pkt = av_packet_alloc()))) + //// exit_program(1); + do_subtitle_out(output_files[ost->file_index], ost, decoded_frame); + } + } + + av_frame_free(&ist->subtitle_heartbeat.recent_sub); + ist->subtitle_heartbeat.recent_sub = av_frame_clone(decoded_frame); + + av_frame_unref(ist->filter_frame); + av_frame_unref(decoded_frame); + return err < 0 ? err : ret; } static int send_filter_eof(InputStream *ist) @@ -2705,7 +2709,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); @@ -3013,13 +3017,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); @@ -3520,7 +3517,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; } @@ -3573,19 +3570,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 ? av_get_subtitle_format_from_codecdesc(input_descriptor) : AV_SUBTITLE_FMT_UNKNOWN; + const enum AVSubtitleType out_subtitle_format = output_descriptor ? av_get_subtitle_format_from_codecdesc(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; } } @@ -4618,7 +4610,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); @@ -4830,6 +4822,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 30225e9ffe..5efa5d4de0 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 */ @@ -661,8 +662,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 b798459946..c28528507a 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -22,6 +22,8 @@ #include "ffmpeg.h" +#include + #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, @@ -221,9 +221,8 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) enum AVMediaType type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx); 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); } @@ -244,8 +243,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]; @@ -294,6 +294,13 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) fg->inputs[fg->nb_inputs - 1]->type = ist->st->codecpar->codec_type; fg->inputs[fg->nb_inputs - 1]->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 = av_get_subtitle_format_from_codecdesc(codec_descriptor); + } + fg->inputs[fg->nb_inputs - 1]->frame_queue = av_fifo_alloc(8 * sizeof(AVFrame*)); if (!fg->inputs[fg->nb_inputs - 1]->frame_queue) exit_program(1); @@ -416,6 +423,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) { char *pix_fmts; @@ -594,7 +634,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) { @@ -628,6 +669,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; } } @@ -647,51 +689,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); + memset(par, 0, sizeof(*par)); + 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, @@ -710,8 +813,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)); @@ -726,12 +836,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}; @@ -743,7 +847,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); @@ -966,6 +1070,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 7aff43d917..561af8777c 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -2158,8 +2158,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 Thu Oct 21 03:19: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: 31191 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp127927ioa; Wed, 20 Oct 2021 20:21:12 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxMa8TIHrgIhp6uI7QD6w9O8eZ+4ILEWRNTSWv6BVhsmxaxHbygOPrTPz/CS8v28iRmIof6 X-Received: by 2002:a50:c343:: with SMTP id q3mr4016631edb.389.1634786472374; Wed, 20 Oct 2021 20:21:12 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id b20si8595064ede.275.2021.10.20.20.20.56; Wed, 20 Oct 2021 20:21:12 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=p+3wQaSd; 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 39AD468A888; Thu, 21 Oct 2021 06:19:41 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM04-DM6-obe.outbound.protection.outlook.com (mail-dm6nam08olkn2026.outbound.protection.outlook.com [40.92.45.26]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 1079968A71A for ; Thu, 21 Oct 2021 06:19:38 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=hH99MKmFLK4/WkorppVGac4PVIq3OeZWfZ5ZXEdSqR6CNI14ajuoLow+43JvZboRkgkzzVXo2BcsAsTzz8hgdbL311T8eIEzJEUxHTRm5mNTsH3GNgh8Z38jAVpWf8Qykz7tdrQsGAVBEAxk7kCvbjakzQa0nTfM81fZYRKLyDJldO5Rr/rntv0Hb1xgHOWgNAIjnY3FFtwiKuT8+Pc1vwAKXSiy9pLYCiD3/3HfYOxFlq4GBBS4vbnrl+etSATgYCtj8Nu0NGQHsenZh2P9bXK4+uslcMUR/I4cEkVWAg7LBnX6cHPvNT5KHmqfDBduwIytkcm/rGPFEtPHx0E8gA== 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=la7GtKVvLZpeZEoP1nUPqRRtRj7OPNdJkGAba1vKYMM=; b=UHV/7P+yhy3LKdKjRtM3/WA91EeQ+VH3qL16WGT6V2oTFBqDVuFbdgVU7ihe0CR8H+wAbk26mF9LskjfQ1w2SPQlxMVFVweaOx31wklgjS3ETuwwzYn66J8Xido9hdgxbolOdc+2INa6KihR8+KD6CBn/culNJ8bqKs/OipCeH4ehI2hw2hYSVl+MK+51aaVQXIcR/tpzBwMZ8a1zr/kAnauqjoBGYU2xVRamYGyDget7F4ga3rSG516mW69fFmjV3t7CA9FxBdGEI5s7E/xAfjhVDk+noE59VDjZqtEhp/a3+f89tNcWgNuAW4sJMHfZCst8GQn7qYvW1FiJLFUdw== 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=la7GtKVvLZpeZEoP1nUPqRRtRj7OPNdJkGAba1vKYMM=; b=p+3wQaSdudutXkXUZxOOft2Nbqv4wfZcaBlvejbu1oSMiVffVq0qDHKUBVYRtCR2FnP9yrpj6fLhdondK454hV0yB8CEZ+B+fx0IBOEWg814tzxmUBThfPwj+dcBfVyjfQLdLLsGXd6JBDQkteVApd48FGSH2gEeIYD3GwuPRLpgL6HiprEr6xm2OZY33vvCL1D73/7tvVP/JhmmXw1iHbNLzmdfSvkrukybDYkgWH7rqlrxav3MG6CCdL4iNQMxfD7SWErYt7VJT/Bf5HzhKdQkFQLkqfO02i7J65I5SUNNg6ymrCDrkQy287kufXI1styiZ5fPZCCKYNbyNaICBQ== Received: from MN2PR04MB5981.namprd04.prod.outlook.com (2603:10b6:208:da::10) by MN2PR04MB6238.namprd04.prod.outlook.com (2603:10b6:208:e4::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4608.18; Thu, 21 Oct 2021 03:19:35 +0000 Received: from MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb]) by MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb%5]) with mapi id 15.20.4608.018; Thu, 21 Oct 2021 03:19:35 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v14 09/14] avfilter/overlaytextsubs: Add overlaytextsubs and textsubs2video filters Thread-Index: AQHXxip5sPv/CDHhQE6bP1hdJs/kXg== Date: Thu, 21 Oct 2021 03:19:35 +0000 Message-ID: References: <4f225627a420d32b8766306e418c45bc8af41c47.1634785894.git.softworkz@hotmail.com> <4d6c9a34829586c3ad247e2423faa0d763fa01c4.1634785894.git.softworkz@hotmail.com> In-Reply-To: <4d6c9a34829586c3ad247e2423faa0d763fa01c4.1634785894.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: [lgvdIlbnz4AWa+gjNhQwkhamkIlfpBfV] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: c5639b6d-bf73-4cfb-0bc2-08d994419be1 x-ms-traffictypediagnostic: MN2PR04MB6238: x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: dOxeIJFAWEkbjUQ0mE+xnk2t8Je7gf7DFXjuMLdyt0AD0KhEW2yel/P89sfUWEG8obGhUH8WT/VqoN7F3/yIQQS7Hw4P5eTf/RIcPUaMNN+vrM6Xfud/xX9MzQb4IrHNs3MVCnPniUJNeJV3AMji9InEUWekooNidz6RhKufdkjbEVYoYvJJwcRCIvT4I8G1OtGBHdpd2dQ/xw4yDgAg0Sr1JAD+bvyGk7SysvaH9kYVWShGEjUG7z/VNC91Pq4EkE/HV8uUwILCHERce5HcSdLa1DflPKYhJmPbVm/Alhu+Ob1o9Huj3z3Vv6D+HJqxqoYrgZhYIMfcubQ/V39BwSuFd2FKedOh323KQ3CaxNreEK/PMdKMxJV1e4t0kUH7tUvkCGjwerbLiWfQcnEX8gDOOUN2IawXtEHxWB7rD6AfgyMVUhvOKUOfopbUODkq0SI/QVJ4CayaoaVBanTRs1qp6Pzd+G4gz2D0dj+vnv+MtoYmcdSlsX2W1vNWoGNTSdF8WSnj1iUDxeOa/Ba3BYweq0GpS14TCsn/b2GGGQT9xFeP7/gqdZJFwCQ+fp6PdU0+ysiKdsOubukfuPOMuA== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: g9MH32730NpmgnOO4Mu6/MRf3ghLkinh9zRDX4Mx5nbzglgRxHD4nty2mxzaAstm4HnqN1d0e4ygR2f6armtujKkiYndPgditL//uuxn/bjO6+URGMcyzD8YOlbrRAC4dTzh+h8lrlyV8v+tcAkFnohVMgliN6o6yiW9fKprWNYpLTt/2j59CY7bR0NhbINLwYlMT1T5n1hv7GWUj9kE35KkJHSo40FF1mxcgmuOR3z2kk9VvLPmHglCmCUnTuV54cwVnOAl09LPt0tYcNHz6zhtFAXF8UpddMbLSz21HpBTUMPOW57SOGPBxa1IEbVTOldwAmEAAuRxPop6JUEgzlaWZbzgKNDhC7kw4pSfLX2ubs8RT08Q3FaH/JJI9tSiaDmIWrdjKXHwjALvSYw9YKLh8ILp594k1SxKtt9PKlMkcZRWwIELBJUxfF2H2GpoEciGSbYywuaixwjsDIpRul+fnHA9DSOpc+uO9AgOvVj1PbptED+DhT+Y8sLqteH77GuH0D/S15GPuha5Te9psdZ4MNZVjfvr9zquOOnpJRJ644Z2uqCvQhm62ofFKnxQO25Mqi1kTDyrDzwc3OE5QMxzGD0EQw+PAqssY95IPEonfBFuBYhB34vugE46vgtIlqwLiSlAdE1TcpOnluhHFLGkNGS3GrhXqwXFF60fpIv7cJJ5lbgMrAP8S51UfvrOjpoHwAtICgWWwFH5yrkZpw== MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-3174-20-msonline-outlook-529c7.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: MN2PR04MB5981.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: c5639b6d-bf73-4cfb-0bc2-08d994419be1 X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Oct 2021 03:19:35.7118 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-userprincipalname: softworkz@hotmail.com X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR04MB6238 Subject: [FFmpeg-devel] [PATCH v14 09/14] 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: eCkyAjJdFaA+ - 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/avfilter.c | 18 +- libavfilter/vf_overlaytextsubs.c | 624 +++++++++++++++++++++++++++++++ 6 files changed, 757 insertions(+), 6 deletions(-) create mode 100644 libavfilter/vf_overlaytextsubs.c diff --git a/configure b/configure index a97cb41fc2..d32452934b 100755 --- a/configure +++ b/configure @@ -3629,6 +3629,7 @@ overlay_opencl_filter_deps="opencl" overlay_qsv_filter_deps="libmfx" overlay_qsv_filter_select="qsvvpp" overlay_vulkan_filter_deps="vulkan_lib libglslang" +overlaytextsubs_filter_deps="avcodec libass" owdenoise_filter_deps="gpl" pad_opencl_filter_deps="opencl" pan_filter_deps="swresample" @@ -3673,6 +3674,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 8b94d21661..5302f48f2d 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25432,6 +25432,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:9][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 db06a42122..536c823764 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -371,6 +371,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 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 @@ -459,6 +460,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 7ace7b6ec6..d669cb0317 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -350,9 +350,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; @@ -534,6 +535,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/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; diff --git a/libavfilter/vf_overlaytextsubs.c b/libavfilter/vf_overlaytextsubs.c new file mode 100644 index 0000000000..c4d54bc978 --- /dev/null +++ b/libavfilter/vf_overlaytextsubs.c @@ -0,0 +1,624 @@ +/* + * 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 +#include + +#include "drawutils.h" +#include "filters.h" + +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" + +typedef struct TextSubsContext { + const AVClass *class; + AVMutex mutex; + + 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; + + ff_mutex_destroy(&s->mutex); + + 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 *sub) +{ + TextSubsContext *s = link->priv; + ASS_Track *track = s->track; + ASS_Style *style; + int sid = 0; + + if (!track) + return; + + if (sub && sub->subtitle_header) { + char *subtitle_header = (char *)sub->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) +{ + 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); + } + + ff_mutex_init(&s->mutex, NULL); + + return 0; +} + +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) + last_pts = 0; // inlink->current_pts * av_q2d(inlink->time_base) / av_q2d(outlink->time_base); + + 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); + + image = ass_render_frame(s->renderer, s->track, time_ms, &detect_change); + + 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); + 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 Thu Oct 21 03:19:38 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 31179 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp128013ioa; Wed, 20 Oct 2021 20:21:23 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwImH3xCHvD9ypN4JNX23yqzcbW6DEP1D54JWkw6cvZzoHeDLE2ASz2+I+uj+bhkbGBhn1X X-Received: by 2002:a17:906:1d41:: with SMTP id o1mr4276022ejh.232.1634786483440; Wed, 20 Oct 2021 20:21:23 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id z15si8694881edd.214.2021.10.20.20.21.23; Wed, 20 Oct 2021 20:21:23 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=byU0oXgK; 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 8AFDC68A71A; Thu, 21 Oct 2021 06:19:43 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM04-DM6-obe.outbound.protection.outlook.com (mail-dm6nam08olkn2026.outbound.protection.outlook.com [40.92.45.26]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 74F5D68A71A for ; Thu, 21 Oct 2021 06:19:40 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=la5Xah3MKtjjZicd5B2nu043/7Uhz+D7UVUBbMOrXAkMEDhiwy+McUvjM7tC4XNVeLxEeyLzAXH7JWWd23ypLHIUw+Zi1H7YtnfdmAlQXnyxKq0QcbOZOxkxD5yBhteeIUjvApr2cQcwhNfAfrt2Aef5om2f62ILypx7yAkwzGqCHoEsyyJT4ELWPWvygkZ5KiLl56HQg/Uc6P3dQDqYETj0UdyTKrOJ5wRNfM0IAuY5jWUUezZAnr83IH+oFvJefM1ySpvwSM9pPuB22YamHMO42adkOFnTLrXhEopGy+0eF2jtfpgYPqgw+P4BjWULou9mQlQvxtAKrL3qcbXz3Q== 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=35LKKVU3gwTBekWj9DLxtzZAcTKYuOgKmKDhPH80HD0=; b=ARmL0kAf+nMKsPl5dMCJXNHUoW0fYkogOGqWQpWVrT473biL0yWYoBlgkLXc2Ig+WLJduO1thcxkiW1aJDtIlYj1rB9e28CLDW10b8FUbtCZBsJdPmnYLyMvY3ySs1tzMtsfjB8dOchBg5uk1hMra78FeZaFCEtLQ/BOGhNbjV4fwPvB6C3tGCpCS+bPisN80qxrVHO8dAMvxt3TbkSvZYnVfVvaxQJmDhgAFDdelNO+iW4jrBtyeqyRZTILiwznzFXaSt8dlbmgaWI9aGmn4MYI7IkS3T/Gf8GyOiJ8rR8O/kR8yVDJpmyA4c/B010AtrznJkJzekidA7mnJCO0MA== 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=35LKKVU3gwTBekWj9DLxtzZAcTKYuOgKmKDhPH80HD0=; b=byU0oXgKL6ebQqCerQWYO6+rfVYE0odjeypQJbUBegSuM1i25dCpWqPpU7XEST0coul+gsyamP14/WD004HJDy6r1KpZOA2QMjtqh5WmYQr5s1mur5VQcZLqcW15fH7IWV2zHWxmZ7BP5TbYkX3W8Wt1T5/GcUgJflO7QobtIMpbXBbHLm/u3AQ1hpxIIxV9dCL+Ns808RsDg3wGMgAuGW6O3X8jRu0oYiP9O6N+X0VTEbtNKVWQmZGdHAtRg+j7Tx/NfDuR3vkN26A/PeIVaDN6ak7av6a051Ufl5mJ/c0nzhnWnSMpFLb8Ri5AhXbvtpPrwPAtwOul7zJDzZAUNg== Received: from MN2PR04MB5981.namprd04.prod.outlook.com (2603:10b6:208:da::10) by MN2PR04MB6238.namprd04.prod.outlook.com (2603:10b6:208:e4::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4608.18; Thu, 21 Oct 2021 03:19:39 +0000 Received: from MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb]) by MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb%5]) with mapi id 15.20.4608.018; Thu, 21 Oct 2021 03:19:38 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v14 10/14] avfilter/textmod: Add textmod, censor and show_speaker filters Thread-Index: AQHXxip7K6NjPq30FEKOZtjXw1HLtw== Date: Thu, 21 Oct 2021 03:19:38 +0000 Message-ID: References: <4f225627a420d32b8766306e418c45bc8af41c47.1634785894.git.softworkz@hotmail.com> <4d6c9a34829586c3ad247e2423faa0d763fa01c4.1634785894.git.softworkz@hotmail.com> <541cf61d503ee53b51ebccada8f9cb087ae3c549.1634785895.git.softworkz@hotmail.com> In-Reply-To: <541cf61d503ee53b51ebccada8f9cb087ae3c549.1634785895.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: [a/TeoBcRbVpinxOw92AK47/eXtP0zyxp] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: c2e10b00-2eed-40fd-c397-08d994419dbd x-ms-traffictypediagnostic: MN2PR04MB6238: x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: G6mDZEzsyxmfYUl/p/IvrGSD7CsCXjLw61a+Ep7w00DyKsRNc93xEYghkslN6RUGPmjAOmF0af9n/Q1re13s7PJL9IIxpISnPApNZsmNaRfE5PIquZfElmG01tpMLeF4BkRlKlQ6NmFFK1A7fSjL9mhMpz25WXDUyXLNAiOjRfPlHPIg/hQIlKlk7nZUyJHyZtrucMZ4Rniea1rCAZktL+eIH4Rvx9nmsfnT3G/MTUtFTjRrxyAjkDtMjz/fNfqIAQX08Ic6HHf2Bvtwe0Bkq0nI4RdM4MzpCfgqH7aPFZDmiOEUi3eyp2Y+NqhnQQoQ6XfTiFp9I3sjjd+oo+XrYnC9OvYR0+DPNTENUx0SPIeB+Igz2ns58kTSnfoCTWF6mW59wgLEg1UGqg2pK+RAAOhRvd1cvfWnTWveGDf2sOS4NCCkqTY4hjdLGG0wMBGDrf/vCPRh8b3BnBoN6p9AN688X102Ng91rJLu/Df1ZhYVZwlzEodGu7rNgsICvVJEUJflbqmtV4ONEW/ABJKDpYqntM7xZag3+Ko9EGk7InIC4JOpocYIxyNjgEZ4URrDTJhhWhh3agsTm9Nk2wBlKg== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: 6H9oYXz0TgI8bgnjrW930LVdgmVGGotzcuwlGjCT7KgBVOLDiRf8VtyeaLFcyCKXrhpXvUwX3p3Qmw9pOANUPRqXoS8eQlQvdrWqU/gNah6cT1uIL+qlHI40D28USj84kJh3j4m5R+BlyZ+OUxg0DZcuQEsPVuM2wLHO86vovihcQ69OtUsioOzIZTw55dPsRGcYkO8G3rLV86/rszhYyX2Qc8ORrsHNW7AwejcZ2W9MTQ8HauLOhknIZ7vvhGkdt4ESAfl5B5SuXbLT/nkwai9OnMYwyR1911Wqc4XsQqrGldITteWLrYVzaS27le0AqjMSHpyqIyMKKLPR49FGZbUGIAGqdI6V7kakMYQALDbh8Oi19e+6MYRcAnOuruchnEkMmx9fTdKrjZbzVfSCKj9A2MLG9aRta5mWcharnUoCsOBWeHOu2++kkDbu3uYB/QoISKoC1ls7BBZT5ny2QB7y/Bei0Lev5OwRk1GoFiTtX89R5wXcjzfjEr0+HZZcHkUeBKW6forPY7IOhY7TQN5aKMfn6x7TphxnsEWWZxw9Rs7wGkiuBZWulMixJKginADaDBbQ7mEi52Agw66S/OWk7KK1GwdMUguKvhbKoLPA9A5eDKKx7LWP9veeRo3BET2xYBqatSP2MhH2kQDiKE1+jWSI4VLm1QWe4VpHLONj/zCHAwU1006RSbQQP4624RXnxn8LX/8MMFEx9vtnjg== MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-3174-20-msonline-outlook-529c7.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: MN2PR04MB5981.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: c2e10b00-2eed-40fd-c397-08d994419dbd X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Oct 2021 03:19:38.9440 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-userprincipalname: softworkz@hotmail.com X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR04MB6238 Subject: [FFmpeg-devel] [PATCH v14 10/14] 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: SoYP/Zzd/eFe - 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 | 211 ++++++++++++ libavfilter/Makefile | 5 + libavfilter/allfilters.c | 3 + libavfilter/sf_textmod.c | 697 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 916 insertions(+) create mode 100644 libavfilter/sf_textmod.c diff --git a/doc/filters.texi b/doc/filters.texi index 5302f48f2d..6e144acfcd 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25324,6 +25324,150 @@ 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 +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 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. @@ -25491,6 +25635,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 536c823764..45f4c67d2c 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -544,6 +544,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 d669cb0317..108ce550cf 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -534,6 +534,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..434e98e4a4 --- /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 Thu Oct 21 03:19:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 31185 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp128148ioa; Wed, 20 Oct 2021 20:21:37 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwHSHu8ChjoOyqD86rdKFiO9JhpHNpZ1KbGdYHiBiL2gi4+/cq98sJc3Oi3qNwCjMhxyDMv X-Received: by 2002:a17:906:5343:: with SMTP id j3mr4022624ejo.538.1634786496957; Wed, 20 Oct 2021 20:21:36 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id sc11si7616714ejc.129.2021.10.20.20.21.36; Wed, 20 Oct 2021 20:21:36 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=Jcroo8DG; 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 C5C8A68A12D; Thu, 21 Oct 2021 06:19:58 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM10-MW2-obe.outbound.protection.outlook.com (mail-mw2nam10olkn2026.outbound.protection.outlook.com [40.92.42.26]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 24D5368A12D for ; Thu, 21 Oct 2021 06:19:52 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=b7dWWb2w0Oi4t4ga7w0nApkMMxHX/+V5zQo32utv4/VR5fiw5dnavL1pMvaR4wVldJvkhnlj2OJJ+63HdcPv42KDYuwhZHtwdM8icdgEd6MDFrcCwr7SEahkMA00IdF4Wti3Cpi2F0Xjjzv5VtXkxbXhA7RZkQr8uhsrpcT1rMcRUUn3G7Df+dXRzYoSrKm8CPFxER7A6a702hNtaFCdoZYa8ochE2gnK0V71dAIvvHp7p/dMZtI/knWmc5jvkMj9/QNLMyEsaQ6uAPsoKxRo4VMQgk5CwgTe2NpODYuWbN/kdj3QhrR1gMRXFkA/9qES2qJwOL4AVSsHCFvcWmqtA== 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=4ia5LCjHZcpm/XAtAZvUeQE6QgUgPlxdw+c7YASiYG0=; b=MKA/pwmU4K0TjogHjZ3tO9F/8gV1guV0+9kmgJOysGPHBzcsfM3wb2XtBzgYwYnb7yxbNFWtEUGIgR9JlpG1B3NIjQnkZOADfrzuuVhI35o/iKyc9T3GqAweiyFy42vI9muYCLwHx4Wgviaf/HNyFNK4q0jjU9mVnn7w+3eqaz+sPapg/s/XI++/cGhsKzRbNj8+ppwreA3uZuv8uvU7Rv8urBDMd/UibalCPyPG+IPLfq9qIGk/rJqQJwc1cIlxvgt9nXc/qpUpkh8sFE7Pijx3xeTRbZMVpxMqTFcUl/O7/x/5x9muP8Vo4yNM4Y9qOV0ZrmAPHl/vLbFbsbXbSA== 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=4ia5LCjHZcpm/XAtAZvUeQE6QgUgPlxdw+c7YASiYG0=; b=Jcroo8DGbBdXmpq6v/MStS9hTHw5MBzfw+W/U1hn3a7o+BWVZJV3KvX81CtdNFF6wC0pS7nUYF7T2362vUO17bzAAlDATAYKnXhJ4mq4yATi2jDOg46/INpgHtJRzeQeJ0rZX45B2pcr5AQSD6sYShgSGo8JFvKd1phYjwx7oKUKbRljCUFqrjLiF9m2RkyNTnoCWUZl4tKxbuGTWnzBWKGkO4UiZnoEq5LbydQakZwQl6wbOYSV3sRNYvNODmgg9k6ajNzYa5qOvTcIkuALw9YeIHuNHJPnusDoAe4Vmg0aC/9cvBUBBqTw1Ri+ICGKtS+QwV7dxuVooZ4wx+3WYw== Received: from MN2PR04MB5981.namprd04.prod.outlook.com (2603:10b6:208:da::10) by MN2PR04MB5984.namprd04.prod.outlook.com (2603:10b6:208:d9::33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4608.16; Thu, 21 Oct 2021 03:19:49 +0000 Received: from MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb]) by MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb%5]) with mapi id 15.20.4608.018; Thu, 21 Oct 2021 03:19:48 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v14 11/14] avfilter/stripstyles: Add stripstyles filter Thread-Index: AQHXxiqBnPI/KYAPI0OgZhcN0GLmFA== Date: Thu, 21 Oct 2021 03:19:48 +0000 Message-ID: References: <4f225627a420d32b8766306e418c45bc8af41c47.1634785894.git.softworkz@hotmail.com> <4d6c9a34829586c3ad247e2423faa0d763fa01c4.1634785894.git.softworkz@hotmail.com> <541cf61d503ee53b51ebccada8f9cb087ae3c549.1634785895.git.softworkz@hotmail.com> <55d8df19375a269c01802b3ab9b0e4c30d9654eb.1634785895.git.softworkz@hotmail.com> In-Reply-To: <55d8df19375a269c01802b3ab9b0e4c30d9654eb.1634785895.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: [0XVxwLKXkXoRuvk2TyV3EI10WjZlAZTi] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: f2e21417-659c-4498-0fd8-08d99441a3b5 x-ms-traffictypediagnostic: MN2PR04MB5984: x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: shJAh8iTuiMF72FGOMV3aypVPRk3VrFqoRbAnz+00GPZlCxtWRi8u4eFBbjRP63nGW9Y05vxu6lAVHMHM1OjrM8ti3FaBRawIbNHin5STftdBgIHwszXu/8DfqwdaqPiz2dkk0v7+Y+xF0pQpkqm8HMxsXMCrAjLy1TUnQI7e7Vi3TKD/SGxGMUTV1lmBVPi2hO7BhOduFp4FGhv0X79fDXKDzAoNwQcfhLXGNDx9Muk23AxLbYrx4xMMovohUaDnxa9J9hwA3jkL9GGTAgrB6+/F0P3wdbYTM9UpXGshvX+VmM3jEU5QeHGAMb9cfM5z3yo9d8dKY+NaZsHC+9TwYLaouDFw0lHCt3rcBQcEZ6EH7TrR51IGXEjrY5f5V8dXVdfJGlsbo3D9ksUjSjD+QTUrZ2j6O8tl0qJHBiBNjyipDWpeYBPyvqq1hoag3VS2PQFYNU5NnWuZgrQyJXqc3HwzUsgEG9pD7G+lrfixVhoNdfyxC5XhQ/fb86kcL1SdUi/ULiIZwSGt96BLmvjjSnqai+OGizYsgBkulz89OQASIg3pT4kl5s0bpE6Q5PN8Ewo/wF1F2Glajubi/mU8g== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: KFu+3fa3e6JS+EZB09j5NXuT20Wk3uTJGk4PqxMVGJb0AF/DLmcGL1zGWrrUQBfvDXOKvGnhdYYUk51ihphhALE6RG01ZLA2aSS+gH3d082osso3PYQrGElyRkRPktAgVJ0+h477bQvKoEzljCRErh6YpIbFQdrHUWEYhpmSkdAslavDK5ClgerS53IsttxcLvqRwcUJV1SLDNbXDbF2JIc1qPi4WyLnpUHIe1STejmXtCClS1F7i6hJWYnjYlPuVMtBOHEPOqpV9X9n4TCUTQW4ONNPfyF8BV+vcLQGBP0HZI2djOi1FAZetfR37ZPamZT5BmOuDxPp6bMAS3h6Tv5sYinXNsOmw6u5+QA+RGD8rpNT+ynniERTZLzBbxBEheEZ293gzIQ0z3Cm8n0jfTm77e8apIimYyyZhOcuWNZk9hAj1VjM5WfAl0CCXkWvAfWYEc1m68bouP5rKH41Ahe5V5QOA52L4LUcQq+IFquap/0qlxhCqeCdimBce95ozmDA/WzPgaLzLi8OZ+CLMI438R99UiX5Yo8mf/GmKn//dcuxImXBZghcvFZcywjrqneGEEh6vU2txCbgnmnO7xdVals13t55qKy9TCcET0Pg/pnAVE5NxxnGOlh+EVsA8t9mCqh9gjOg0NJVVGsmEdXqtEpYBFj2VOipxn6UP3QwEAkO8oLShiiIpieOnQL4Lk9xqtrRdBYcZCMR/Y9XYA== MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-3174-20-msonline-outlook-529c7.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: MN2PR04MB5981.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: f2e21417-659c-4498-0fd8-08d99441a3b5 X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Oct 2021 03:19:48.8853 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-userprincipalname: softworkz@hotmail.com X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR04MB5984 Subject: [FFmpeg-devel] [PATCH v14 11/14] 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: O/f0AQZYXdYX - stripstyles {S -> S) Remove all inline styles from subtitle events Signed-off-by: softworkz --- doc/filters.texi | 34 +++++++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/sf_stripstyles.c | 191 +++++++++++++++++++++++++++++++++++ 4 files changed, 227 insertions(+) create mode 100644 libavfilter/sf_stripstyles.c diff --git a/doc/filters.texi b/doc/filters.texi index 6e144acfcd..c8ea318520 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25389,6 +25389,40 @@ ffmpeg -i "https://streams.videolan.org/ffmpeg/mkv_subtitles.mkv" -filter_comple @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. + +@end table + +@subsection Examples + +@itemize +@item +Remove all styles and animations from subtitles: +@example +ffmpeg -i "https://streams.videolan.org/samples/sub/SSA/subtitle_testing_complex.mkv" -filter_complex "[0:1]stripstyles" -map 0 output.mkv +@end example +@end itemize + @section textmod diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 45f4c67d2c..41e71e2db2 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -548,6 +548,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 108ce550cf..49b10b882d 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -536,6 +536,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..ed35c2afcc --- /dev/null +++ b/libavfilter/sf_stripstyles.c @@ -0,0 +1,191 @@ +/* + * 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; +} 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; + + 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_DEBUG, "original: %s\n", tmp); + av_log(inlink->dst, AV_LOG_DEBUG, "stripped: %s\n", 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 }, + { 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 Thu Oct 21 03:19:52 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 31193 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp128251ioa; Wed, 20 Oct 2021 20:21:49 -0700 (PDT) X-Google-Smtp-Source: ABdhPJytyqGw7zUmzSNMvkf6WYsdOL0OEvfBI8ZnJdRv1QDkaJzm9BpNyue1sjZYMlAtfWiQ6iL5 X-Received: by 2002:a17:906:789:: with SMTP id l9mr4001531ejc.61.1634786509266; Wed, 20 Oct 2021 20:21:49 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id kq16si5296633ejb.521.2021.10.20.20.21.48; Wed, 20 Oct 2021 20:21:49 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=EM5u6Ba2; 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 F40E968A956; Thu, 21 Oct 2021 06:19:59 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM10-MW2-obe.outbound.protection.outlook.com (mail-mw2nam10olkn2026.outbound.protection.outlook.com [40.92.42.26]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D2A2F68A12D for ; Thu, 21 Oct 2021 06:19:57 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=mUZeDQmIl83zidi34sKE9lqBjWgdHvLyp/jUvAkM847PSSUgPvf2A8D9fTSX/y8PsQoU2da+aGyVskBqOgORim0OPkhN5B1H5sVFASCt/Y3cl1nsIx+ESLhOxdcGfMYRLLpsvAbdsuqCuK2YhgIcFShdkcAPhQ1dRRrjL4JGhWNR7VWt3EBqRpUqpC60y0ZLL0z6Mpp+/FWl8AQ4JQ5CXM8xkh5BpKnXZWF6116h09DQBKGKrjsif49vhsIW8Z1MfxSPQiPIUBrfCVn5Xz7O+VFcCQpRqVIVeGRVmHMd/WjwiWlXT2/YkBU1azGyeu8dYJkYEMYrQYPJgG4gfaWmEg== 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=Xnlei/LuyFMjwuPottG1nJ/9zeEBc0yXSyqO9KuHVOM=; b=g3rW8B3teZOSTp5oeoez2KhfQRC/1XJcUJrAXYVlINISaYxFMnMJhNRrmXP2xhTs0z85B+j9oYZbWT6ImoDQsgdWnPNgjTHWR6JcnU26r4JR8jC20foP8bkxElh4j8S7mjlM/r1BdX4p/SqQK+HFp0pfJa13FwwJi6beR5dMCfocYAC0wIgCF32b5kMf/f/lV8IWYP4kQ+rtdV0VZpEC2PSktkql4KzHfydNbtmForQBLogO28g2HGu4EiNr/z2XdlHD0ty2iBOuY7qwJrlDb32/R20H95W30i7If/mUJvcRuwBWdyR9QGaiNeNDUv3ypniixKtqZxeskuUUW7twUA== 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=Xnlei/LuyFMjwuPottG1nJ/9zeEBc0yXSyqO9KuHVOM=; b=EM5u6Ba2BTJ25Ob7/t0NPn+bo8ZkhonfEqsKsyZ01j5y9DIDI+fLnIizso2xfU1cOexTEnw/+53VAjIo1roaExeLp+gtg1nLMfAN7OITeMcDG7Ca3mmiuV5ZCd3M/bPFeZ/jW0lwiBJNfd0t0BoLyG7D5KnMy9AUGHQNSDu35uau6OSYKqgx2ldJNgrdGJ82L1PTg84Pw3pTckky44OdEAG2HJcCMPOJutSd+B3yRzA31FrISk3Xs9uezYkMgZYXKmnlNtLHfi/Vre1iEGaDkcX1gR1bGAiGm1QUpqW3A1Pbm5HtpzIhw/UGGH6tBIv0zUlQuowjlNng6YDzob9nSg== Received: from MN2PR04MB5981.namprd04.prod.outlook.com (2603:10b6:208:da::10) by MN2PR04MB5984.namprd04.prod.outlook.com (2603:10b6:208:d9::33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4608.16; Thu, 21 Oct 2021 03:19:53 +0000 Received: from MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb]) by MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb%5]) with mapi id 15.20.4608.018; Thu, 21 Oct 2021 03:19:53 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v14 12/14] avfilter/splitcc: Add splitcc filter for closed caption handling Thread-Index: AQHXxiqD9/nuExSxk0mSiO9vA+arvQ== Date: Thu, 21 Oct 2021 03:19:52 +0000 Message-ID: References: <4f225627a420d32b8766306e418c45bc8af41c47.1634785894.git.softworkz@hotmail.com> <4d6c9a34829586c3ad247e2423faa0d763fa01c4.1634785894.git.softworkz@hotmail.com> <541cf61d503ee53b51ebccada8f9cb087ae3c549.1634785895.git.softworkz@hotmail.com> <55d8df19375a269c01802b3ab9b0e4c30d9654eb.1634785895.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: [szrOSVrFj+cYDn/WyuLcE5fnomhFVPTI] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 3be6d0ae-bd77-46a5-c60a-08d99441a626 x-ms-traffictypediagnostic: MN2PR04MB5984: x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: mtxov5k1ts+yz7MzpRX+o8kTuBHAlECBJg5A+8mRZwqG20rKjw+hNHyLJwZxPW1daT7fiREYqwRv/So6rMFqsJVkoGyWoXAw+ktFQ4OArvsVVFcg4IXSjIOH7xgzgsqpDWNm6TiVzZfTLZjgdq3nU6Q3vrfaemVljgckQfiHjKCeLR3Fp5+a1cjGmdCC2Puyt98loYD/5AdfCgUyvsFw2bmY7Es6ogt+NLTY3/iCvBrHJLJ3vpihnABmNul/FkS9zxKRF591h+tlX7xXOKz8+/dSgBFSnKcx3wKbz8nSe+C737fYzIhRk7rg+PXQFQeEVhodouqwug2wN6/0MpotS+0gY1ZVp8zMsWNziC5l1wkUr3p4UEMEyKYpmvk/9GxTpRm7leKdz6Kpgi2LrYCs8Ph85svf7feUkt0RR3jiE9EoWz/3i+pYSBuBOIRH1XrGlqUwitmhfNKHL4xCgLcrewGTqnbd39z9CE/xqshH2bStaxAwMdc4ekz8PWqycKKAZ+MI1KYNdfCc5gIzGKtbwO7cwxJqSlH5iIxeqSOMbFRR2rRjSLnYO88LQSmrBN7uJ4NitqNZYkoUdLquYAVWTQ== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: CzaAIdlUHhlVwrGd0hrMtwcrakNC8JuUZmCujyomgVR8fdc4y++HMXiTCUewfP60EqquSOD08YyAoE7Yz2VHM4yWYaY+rs4nxw1R/mqjlbmskSx0MIKalALQ7MsiBdDjRgvOthwqK5PnZmQx76hXwoIcx9CY9aYSbtnf2ZIvouxSQ73Ty5YhMZ82mdFnzpcEg3bRIJe2fvkyDE3KavirAu39iQUN5rJPu1anRHpxZevDIgQtvLnmOC46ZXS6s5o8/SxvKKRJnkKcOP26xKa6EzHhbVJc1wfTJbuA7VK9eD3sYxuzuQ21qGpkpBvmx+hmKHHaeeh2VU/PHBktSKbKipVAntMTSwftKULHmotzlzaBBWvfviAgmH0keTxhiQQ+RcuEu0fBrkYPNaMEtD4tSfV67fkLbp9VdC1AYaMSGOrlmYQvM5laswaRcP7SFzRRs2vEyg0DHqjO7oqCkbdgmoNHXXqaept5EXW1/24G97mGH45d1l0lwbUKgUz3rv2KSEYxLJhgl9RvFfkAq8MUclI5FKvpxDfDUxfI1FB2lMUKyKHxmaWg2biw+G43opD3Y4ZaeNQgleZ8/FRN9DW9BUOOCRgen7bPmxRTeiuSOBAjz+1aXsGY1DNMgLRHLlt708otaI91Kr2tMCnSQ1UzFT4xM/N8eqnGrCOJwZMno+PY6k4RGFWm3X1FDpJlg/b8Vim09o5ourdUa7UMhznK6Q== MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-3174-20-msonline-outlook-529c7.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: MN2PR04MB5981.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 3be6d0ae-bd77-46a5-c60a-08d99441a626 X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Oct 2021 03:19:52.9840 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-userprincipalname: softworkz@hotmail.com X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR04MB5984 Subject: [FFmpeg-devel] [PATCH v14 12/14] 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: 1o0sj96ec1UT - 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 | 57 +++++++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/sf_splitcc.c | 315 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 375 insertions(+) create mode 100644 libavfilter/sf_splitcc.c diff --git a/configure b/configure index d32452934b..31092ab38b 100755 --- a/configure +++ b/configure @@ -3667,6 +3667,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="cc_dec_decoder" 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 c8ea318520..7f7ccc3f2a 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25736,6 +25736,63 @@ 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: +@example +ffmpeg -i "https://streams.videolan.org/streams/ts/CC/NewsStream-608-ac3.ts" -filter_complex "[0:v:0]splitcc[vid1][sub1];[vid1][sub1]overlay_textsubs" output.mkv +@end example +@end itemize + + @section textsub2video Converts text subtitles to video frames. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 41e71e2db2..302bdaf416 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -548,6 +548,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 49b10b882d..f0eccbacd4 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -536,6 +536,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..b48dfd5fa6 --- /dev/null +++ b/libavfilter/sf_splitcc.c @@ -0,0 +1,315 @@ +/* + * 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 "libavutil/opt.h" +#include "subtitles.h" +#include "libavcodec/avcodec.h" + +typedef struct SplitCaptionsContext { + const AVClass *class; + enum AVSubtitleType format; + AVCodecContext *cc_dec; + int eof; + AVFrame *next_sub_frame; + int new_frame; + AVBufferRef *subtitle_header; + int use_cc_styles; + int real_time; + int real_time_latency_msec; + int data_field; +} 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); +} + +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_sub_output(AVFilterLink *outlink) +{ + const AVFilterLink *inlink = outlink->src->inputs[0]; + + outlink->time_base = inlink->time_base; + outlink->format = AV_SUBTITLE_FMT_ASS; + + return 0; +} + +static int request_sub_frame(AVFilterLink *outlink) +{ + SplitCaptionsContext *s = outlink->src->priv; + + if (s->eof) + return AVERROR_EOF; + + if (s->next_sub_frame && s->new_frame) { + AVFrame *out; + s->next_sub_frame->pts++; + + out = av_frame_clone(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; + AVFrame *sub_out; + + 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; + + if ((ret = av_buffer_replace(&s->next_sub_frame->subtitle_header, s->subtitle_header)) < 0) + goto fail; + + ret = request_sub_frame(outlink1); + if (ret < 0) + goto fail; + } + } + + 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; + } + else + s->next_sub_frame->pts = frame->pts; + + 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 }, + { "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, + }, + { + .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, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_QUERY_FUNC(query_formats), +}; From patchwork Thu Oct 21 03:19:55 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soft Works X-Patchwork-Id: 31183 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp128364ioa; Wed, 20 Oct 2021 20:22:02 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxMmFoy38XS78ylJ05as7sPRErRvJfsL/GJs3kPAjh93hk4soOEaj28KJDh/rzU/hTduJ4o X-Received: by 2002:a05:6402:3512:: with SMTP id b18mr3976708edd.15.1634786522146; Wed, 20 Oct 2021 20:22:02 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id y13si8609658eda.344.2021.10.20.20.22.01; Wed, 20 Oct 2021 20:22:02 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=tDpYXQ7v; 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 22BA068A8A1; Thu, 21 Oct 2021 06:20:01 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM10-MW2-obe.outbound.protection.outlook.com (mail-mw2nam10olkn2026.outbound.protection.outlook.com [40.92.42.26]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 6330368A12D for ; Thu, 21 Oct 2021 06:19:58 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=eDr9AFQe6vQigCbOWBTrYfChM2vogDBdDfY17Yy3jtT/aPsdMOPAX2BMpGFRcXeSqkinp2wrZ+9gDKgrM8VMRhNbn2FaQXfEkn6k+WWizqrtYcs91OehGbuluDTdKTdY90zgmZykjE69CGT/5+Yfx8c8DwNasKHpWVWLxlH2zeyIwcb6qTHG76xv1j//92SKed3HgbtpfPCu1mKFEVSfmUXZWJAAPXbXVSzJQ6j8kGZD2ky4i1QwSSaxesiUdyp+8lBvynOsIrf5aAWyQVKrGITMwRIobi38XvueVAC7bNPg8kOd/MdhFDdtzYbCSnTkg68fWzwH+rRKH/LKx0oscw== 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=sjAiYC1yrYwutoXF8e5YKG9rTvFGkjfN6fFRlm3NCYM=; b=LEAnqitQXDEPv6/DsLBIEiD2x9c/JSHaUeZLhayvgWz/e1jrTs6G1gVeUd8Xthoa0xpFDPnYgzYpqMilfwjq9Q4Y+e79+EbHpP94qwnuLB4NUEIOd9GCPuzWvIfSsHUccI3wIDJztdxrGJXlmjx6U4U57W3PJ3uXPE5uoG6j2TpCC2HZUAV526i1qGvutx3QkO+yjF08dC1B8UbQvt+UxQkN3XKFabLrYAmA3JhUuIQeiXBTwZbddAwQg5z8OUupzxQIY6izdyFg+RzMHpbK2+75dQMbKee8GZae2g+Y/mfgipWCUncEt/U14977urVSeP73x7y3XtIh4l0rVuXKvA== 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=sjAiYC1yrYwutoXF8e5YKG9rTvFGkjfN6fFRlm3NCYM=; b=tDpYXQ7v5swbaDjRoHZlQL98vRtcV2F8OOXUvoFj80owhLSfTWYrsVeYOu+GXCT6xfGv4JnlDg3GA1HWaHcXvnbElWrkoCi1bMcHB4qQGPERhs8qw3P3Owh+iBnsCnojVflNT8EFp642pfzqhZrjqnNfqI/0vtJKCWnhGtTJ58hK/aldRIgDkHY0QMIrLg7mUU8Pw/ETP6SA8uXXrzEU+2h7b22WTGk8PGa6Gr6dwVOxLZSNdZ1hvoQoGi9ftSWoibeI5b0vaNkRRQL/qbsixA1pcvu26aEOn2SK2+ymLq7xL5Iqaw96sFGb4+ngXoZbu7GlBR/VGi23lJ0cFa7hrw== Received: from MN2PR04MB5981.namprd04.prod.outlook.com (2603:10b6:208:da::10) by MN2PR04MB5984.namprd04.prod.outlook.com (2603:10b6:208:d9::33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4608.16; Thu, 21 Oct 2021 03:19:55 +0000 Received: from MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb]) by MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb%5]) with mapi id 15.20.4608.018; Thu, 21 Oct 2021 03:19:55 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v14 13/14] avfilter/graphicsub2text: Add new graphicsub2text filter (OCR) Thread-Index: AQHXxiqF+5ERbC4/z0ayHKHK04c4hw== Date: Thu, 21 Oct 2021 03:19:55 +0000 Message-ID: References: <4f225627a420d32b8766306e418c45bc8af41c47.1634785894.git.softworkz@hotmail.com> <4d6c9a34829586c3ad247e2423faa0d763fa01c4.1634785894.git.softworkz@hotmail.com> <541cf61d503ee53b51ebccada8f9cb087ae3c549.1634785895.git.softworkz@hotmail.com> <55d8df19375a269c01802b3ab9b0e4c30d9654eb.1634785895.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: [VGr7GDlaKBNi5pNg3itGywaJb6WVqzix] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: e0550490-f91d-4d2e-2c84-08d99441a7b6 x-ms-traffictypediagnostic: MN2PR04MB5984: x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: QbZF+DOpI/JxTSZWSO1UMiEh6STU1N8EGwXV/ex0c8bhKFpvJPxbdYzkccxLWPVMsueaN/pOt35GSTb3w1HL4Rmp1bwwVpUQe7GC7uVA8wQDTlOKYXpKkkBdTicSeDE71WtEGLG2TZmPhRJJdbaMHdnAWyRmDrs4q4mHyfrbC1wdjkRsjdupCG4dWBiZghvwhGWa8UkPcM/GiceXxepF9zwIGfbLX1cV0bA604PZDoTZxPfrWac34kUTmv4OaasONA9lhSTnPRcIjKwMfX276nUKjJDnpTwtFH6Pc35e0TSqkM6dkybr1FmKAvOommOtxRAR3l/Jy0/iM35essYDY5yjVYd3TFcJX1OB7rq3SKtfNMnHx4uEx/uA5JX9+TZYWDGNEAmFmiRzbH1GMG88PRGJv0JO8rMzvBcT7SpwCi0FazNdt+ZFOExT5KbvY3oop2rM4SzqlA0hnHPbVMP+4NAwIERJVPVo28oq2hLYw/S8V1HnJbt5uR0UANnhUpymVwKuFSgDIqxtNrttNTYaiWrWRpSOhPr52gWDaj03+MZZjJrAwwW72mtMog8CIfR9Xuk+4dVd0QSHs8sAkuOeOw== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: y8k/VI22fY2YCB2PHAyBOzg5SFHQ1BjcbRYTGIw95zO4uhOPyPRK7upkPCm7Js889+EIVRlDY73XD20gPPmeHsRruoXs75SPPLYdf5UvqyeggCyU7yqKLGgAwU0rCH7MdlCkq6y8EZPd905qWf2dxDepf7aTvCbP7fjuTuepqn8qFZjU/EtEPtg7ZInybgshk8OcFTZvbSTcIIuixythBgtCQNyE8/rq4Mg5LGMPkpMlogT7+evFW0BhcD5SCGvwlUXbJK7uRJsewPRSzJ+FBLWkyNfW/8MA9GnqjeMPjaiA8kd7q4Q0i/VbBvHEO7jyheHdirQSsQ64wIlcLqCgHDXdEo5wSW+axQCBHsSLCkPAw56Qypt1ieaj59l8pfD3rAcIol8rVPu5WCRhKNAjh1t/SP3SiaPcxF8yuoTJP/IEh0a18cQbK9x34HjoKjeH2Bf8CC31rESt0e/yGPNWrLhiGsqcf9eqIIaWYnfftHq78E0vfpm/T7RKX586vGfVQSaJ8nGb5vLsBUYzyKtdc3aA5EjxgescKNGe0xGDKi9LvgFf7v7hV/LIeNyky8R+aSIkNJh5we4ON+HosMlZmf6cuNLBAdkuWH4Ig3jz32iKyKMiRb5VxURVHZ73erFDIdt/UVa0PyWik/aLePNrSQjVlXQSkUjjKJZoEVJPXs0Oj/X+dpin7xmyb6gwOKeooONNRnJWczPERFhu9aqo3Q== MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-3174-20-msonline-outlook-529c7.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: MN2PR04MB5981.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: e0550490-f91d-4d2e-2c84-08d99441a7b6 X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Oct 2021 03:19:55.6295 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-userprincipalname: softworkz@hotmail.com X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR04MB5984 Subject: [FFmpeg-devel] [PATCH v14 13/14] 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: phXp3hioB1Os Signed-off-by: softworkz --- configure | 1 + doc/filters.texi | 55 ++++++ libavfilter/Makefile | 2 + libavfilter/allfilters.c | 1 + libavfilter/sf_graphicsub2text.c | 323 +++++++++++++++++++++++++++++++ 5 files changed, 382 insertions(+) create mode 100644 libavfilter/sf_graphicsub2text.c diff --git a/configure b/configure index 31092ab38b..3276264c0c 100755 --- a/configure +++ b/configure @@ -3605,6 +3605,7 @@ frei0r_deps_any="libdl LoadLibrary" frei0r_filter_deps="frei0r" frei0r_src_filter_deps="frei0r" fspp_filter_deps="gpl" +graphicsub2text_filter_deps="libtesseract" histeq_filter_deps="gpl" hqdn3d_filter_deps="gpl" interlace_filter_deps="gpl" diff --git a/doc/filters.texi b/doc/filters.texi index 7f7ccc3f2a..e75d095ea4 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25502,6 +25502,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 302bdaf416..6e5b04c250 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -293,6 +293,8 @@ OBJS-$(CONFIG_GBLUR_FILTER) += vf_gblur.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_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 f0eccbacd4..70399a77f4 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -535,6 +535,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..c963ba4384 --- /dev/null +++ b/libavfilter/sf_graphicsub2text.c @@ -0,0 +1,323 @@ +/* + * 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; + 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 + area->ass = av_asprintf("\\N%s", tmp); + + av_free(tmp); + } + } + + // 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 Thu Oct 21 03:19: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: 31192 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp128478ioa; Wed, 20 Oct 2021 20:22:17 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyKkEcFhVK3QwVkwHvA/7kBaFiGvhrExKhwRcjZnlnFjn2a/LZ5DEm/1qPCtuRx5M1TyJPz X-Received: by 2002:aa7:dbc9:: with SMTP id v9mr4029585edt.31.1634786536936; Wed, 20 Oct 2021 20:22:16 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id b7si1041530edz.148.2021.10.20.20.22.16; Wed, 20 Oct 2021 20:22:16 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@hotmail.com header.s=selector1 header.b=ivL3R26y; 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 4D6B568A987; Thu, 21 Oct 2021 06:20:03 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM10-MW2-obe.outbound.protection.outlook.com (mail-mw2nam10olkn2099.outbound.protection.outlook.com [40.92.42.99]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 37DBA68A94D for ; Thu, 21 Oct 2021 06:20:00 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=ZBEZPhZUc9XIPaANWiQu3xyAqdEcJvxIdx0HiHm0bMukc3zrThgZMmo+Tw3fziReJYFOinDaBYETTUYTrvYWF3ooKoHAcVR1A73kEG+2VcAkBlFG3X07PwXER+f2LSAFIT2kfg9kuNziLtPx7sAmwlzRrSEFJdNESr2hNBeTLynnrgOpl6zXBxXi9y+WaIHJjsU4fbM7LuUohZOJdMxxmQ2ouxiDwfYuoOcSNMX71odidNNA6edDyzzmzygAtDPlWqvS7I+mfRajDZcct6tZQSoW8yLYzXvjx3OMqLUfnGWmbtQtd/wFXmdsFLZSqBzolOY9+jJxaQdqy2nQ1VwxeQ== 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=jO/91xaefoLolALvAYDZgO9XLLLoY7EMaljBa58snSo=; b=nwzzpFF8RmPfdyzjQpw3QGcmFQm2mQ3EYHzRo+Zt0CKkxe93d4VZSxjyj6FSt7O3smPvcJgRJLQr7dVJTMHQFDekfM0xJH+YIohevPv0kL0DqClLyXBpr1acNArRz8146Du2BLTZ+8A47usOmMN2h+lMvjOVJyCHgV4X+WwvUCpwwChJmKLwiOuqTVpMH9wZz8zo7nicoXC9H+2kENZV/N1rBjZLqD23mxbt/ptzHfIGB8swG1noYOUxj9rPd8RjT+IZdiplzHHdUib/RSt6fcHrtQapPGBlmjjQuy1eItEnBLWX3q9a+XQ6p8U+40lWyAdstzfCWt4o1AM/ClL1dg== 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=jO/91xaefoLolALvAYDZgO9XLLLoY7EMaljBa58snSo=; b=ivL3R26ycyREiRX7E8bwTNKM5k8+rU457TMMUFG8GzxsaYRYeYlFCnNHabbUn7pqzWm9cnliXZIjOX2siOLtxDa20eNyVeejLeukGabrJWk13GyHaEBeso4NMqEn6jeW2uufdBbDuuC+E7AbA5jKbPLP9Byd4QmWOAMuvaC+al2VZGUYapg4mzUaHzw237+HJ1OAwCv1ucpG1By2gIpT4fpqPzIknbLl5T/q9Ym3kliSYp8DBY8xVcsgAA7612v1zHdvBbTBht+kcVTLRYGoO4iqXyNZgTGPQejm+iBkPTHDQEgDbOEeJse67rjWCyJfSBRLGYtA1HX/hgpu5IZPww== Received: from MN2PR04MB5981.namprd04.prod.outlook.com (2603:10b6:208:da::10) by MN2PR04MB5984.namprd04.prod.outlook.com (2603:10b6:208:d9::33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4608.16; Thu, 21 Oct 2021 03:19:58 +0000 Received: from MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb]) by MN2PR04MB5981.namprd04.prod.outlook.com ([fe80::ecfe:2528:2012:22cb%5]) with mapi id 15.20.4608.018; Thu, 21 Oct 2021 03:19:58 +0000 From: Soft Works To: "ffmpeg-devel@ffmpeg.org" Thread-Topic: [PATCH v14 14/14] avfilter/subscale: Add filter for scaling and/or re-arranging graphical subtitles Thread-Index: AQHXxiqG6sILWGuAyki+bvTR2MgBEQ== Date: Thu, 21 Oct 2021 03:19:58 +0000 Message-ID: References: <4f225627a420d32b8766306e418c45bc8af41c47.1634785894.git.softworkz@hotmail.com> <4d6c9a34829586c3ad247e2423faa0d763fa01c4.1634785894.git.softworkz@hotmail.com> <541cf61d503ee53b51ebccada8f9cb087ae3c549.1634785895.git.softworkz@hotmail.com> <55d8df19375a269c01802b3ab9b0e4c30d9654eb.1634785895.git.softworkz@hotmail.com> <889543fa8afa7d0db3d1dc4ee6f3e08a8f4e661e.1634785895.git.softworkz@hotmail.com> In-Reply-To: <889543fa8afa7d0db3d1dc4ee6f3e08a8f4e661e.1634785895.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: [DCxRKNHolARhoQOg5qDvk3kMentIwEzm] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: e2418143-cc81-49fe-4582-08d99441a93c x-ms-traffictypediagnostic: MN2PR04MB5984: x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: dL4k0nBvqsHnUqT/ZSXAQZXJcji5kBM1H0o3as0sKpKjcRlQswUtqyGAVEqli9uxSkIozOHKVkWTfa51M2KOKk8QKdQlvnREOPmYezhhSY0aAn18zzcEjqRUlz2StR+ZeTgDr+ltPemwP64N0PXnhcqGSYDA3gttMjwIgfNrzLRMyZbw3MasuQSjtPUAzTx5rZsDXMNg47PG6XziXk0E0BJ/rwbU+dlqzHRBc7kIo6IT+TyEv5hBpBjfxY0+20+LK0gPd9HWygWeCbhEtWN5BpHMJLCm0jEhWNil8btmWst8V/CbmbRCY0SQNEb4AD11IWVUzoSlfqxa1Qu6lBkJTtxl/BGYeTWBrWEhgePUrrMeNV1KIZ/H7Q4csd43ud6rAlEUreVjU14etHuvPcc7d5SmsGXd0t87chIhiCY9uhr3pzOzfr1+yEsL3WCCZUT4XeQXaAq/9ufCgANJBbQIkpCgEvaXUTryCxMPAHAUtJEenNktWZGZMx5jQ7C1k/m92EtLb6sxBGHVJVEpY9SImeWNSrCEAN8ZU9zEHENJ3VDg0TzhXNAzY1cxsfKyZulnlzF13Y6iksqf0Fc+ULwGvA== x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: QsAmM7vo0o+27XIolEIKH+uvtOZSaegghUEDEWx75QeKNwk00BVjFR8Xq3GpJNCSyijTKiZ/Dto6rrXff9s5CeIYzbsJl6US7yp2UR5RA4plmIX6DvDw4qGMDaDeyh56FB3xT7GV77OYZ1oRPbmgVkpMWve0y9/yMqUH0gT1c2rQ/7nZ9CFhZXBxFw2vqQsY8zTQ04lH04YkZI512mhqBuw+bzBR6dGEDUhh7LvBsSXDsD1thkG1PA//QQInq+759TrTpyVHtBNrI/+OPiBXBInGbp65FoaBKxWbuUoQb6kj8krpcSULTVgVEZx+elKQehE/q5vv0vSow7lB6s+9nwDftTH/0uoIBz1jY+UQRIXPcKD0ksSaeiYcOtqkU++fhXyONM7GWOSUXceVR1VESp/EcCPoa9hQm5eInem55sVOyk7dGoqia9T2EdwXqSC3/xcIP1R32/+Ip2hzr1CykC+jVJ+l9GIDLjcF3BhEvRYd7APtMskSsZ80xne3FfHJg4flg/e2Xa2P7plBvgoN6rQewha60OWrrYPlGTjVcWKokHRW2mMpbhrGvewe+jKMKF2uAKtQnKYJdSxBxM25WsuhPSPJLjTUSBrV8sE4p7P9H78HW0wDKz1WnDvRt1Fo8U8M35VfgpjdT4Dhi4jf86PJJFJXYQoxEeBi/2ZeYOdNfu6viHTiAdDycw+Edtb2/bj59z2nKqvzKMR4H/AjJg== MIME-Version: 1.0 X-OriginatorOrg: sct-15-20-3174-20-msonline-outlook-529c7.templateTenant X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: MN2PR04MB5981.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: e2418143-cc81-49fe-4582-08d99441a93c X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Oct 2021 03:19:58.1631 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-userprincipalname: softworkz@hotmail.com X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR04MB5984 Subject: [FFmpeg-devel] [PATCH v14 14/14] 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: 3Kbzuonns/L4 Signed-off-by: softworkz --- configure | 1 + doc/filters.texi | 159 +++++++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/sf_subscale.c | 883 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 1045 insertions(+) create mode 100644 libavfilter/sf_subscale.c diff --git a/configure b/configure index 3276264c0c..721a1bc06e 100755 --- a/configure +++ b/configure @@ -3669,6 +3669,7 @@ sr_filter_deps="avformat swscale" sr_filter_select="dnn" stereo3d_filter_deps="gpl" splitcc_filter_deps="cc_dec_decoder" +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 e75d095ea4..204cb2d8eb 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25902,6 +25902,165 @@ 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 +@end itemize + @c man end SUBTITLE FILTERS @chapter Multimedia Filters diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 6e5b04c250..d63feb2841 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -552,6 +552,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 70399a77f4..19b5414751 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -539,6 +539,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..fa7553354a --- /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), +}; +