From patchwork Sat Jan 25 02:01:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: rcombs X-Patchwork-Id: 17529 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id D4DD044BC55 for ; Sat, 25 Jan 2020 04:02:44 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id B332368B0FC; Sat, 25 Jan 2020 04:02:44 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-yw1-f46.google.com (mail-yw1-f46.google.com [209.85.161.46]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 0B06468AECA for ; Sat, 25 Jan 2020 04:02:37 +0200 (EET) Received: by mail-yw1-f46.google.com with SMTP id n184so1858952ywc.3 for ; Fri, 24 Jan 2020 18:02:36 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=toIx6thYwb7QtnkslPSo4gDSFsZ0XmleszzrmEjnoWg=; b=obORyImEOv/6mXHEGbyZsXfod9qan9EmsJi+0GODZU1EMKw+WsXkP9bdGpT+RfM7rH OXZWszaymu186Qp5er0CdviYtRu4xbQfMm5z44nhU2MVRllzAziofPOJIlnDhZbhLbQN Ni1IY+wu3HBTifEjle1FrPYA8P0Nhl/jVTLdojKnJOT+UKENdEpI/CTT6/rJuGa/kLF5 FSJP9VbPm1BKOwXEOumYXtu++nd/X6K1nhxdXSTfDnV+ODUwq6QMdFYDRgPpRS64JwBb sYYNgJhqG5YZxwQGgw9hiZDp+AirqQv0ly9deAbSOFcTSjVC1MqdlLKywghONB+EY/OI SUlQ== X-Gm-Message-State: APjAAAWM6KlerizIgrdmCvSbXts27RoA7eTHshs5/0ZXVePeEyrEHtDT 3BDwHt+Y11oeIP2Kz6SThYWUqXAkpV8= X-Google-Smtp-Source: APXvYqwi+lOi79FeuWJvaj3vrdFqNbjzER1esushPgX0eGZvTjILBdIFst5eBjJ4w4mM1mvzGl8t3w== X-Received: by 2002:a81:a7c9:: with SMTP id e192mr4802928ywh.421.1579917755249; Fri, 24 Jan 2020 18:02:35 -0800 (PST) Received: from MacBook-Pro.localdomain ([24.14.135.13]) by smtp.gmail.com with ESMTPSA id h184sm3181072ywa.70.2020.01.24.18.02.33 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 24 Jan 2020 18:02:34 -0800 (PST) From: rcombs To: ffmpeg-devel@ffmpeg.org Date: Fri, 24 Jan 2020 20:01:49 -0600 Message-Id: <20200125020200.2049-2-rcombs@rcombs.me> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200125020200.2049-1-rcombs@rcombs.me> References: <20200125020200.2049-1-rcombs@rcombs.me> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 02/13] lavc/ass: add support for configuring default style via AVOptions X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" --- libavcodec/ass.c | 154 ++++++++++++++++++++++++++++++++++------------- libavcodec/ass.h | 106 +++++++++++++++++++++++++++++++- 2 files changed, 215 insertions(+), 45 deletions(-) diff --git a/libavcodec/ass.c b/libavcodec/ass.c index da05a83d69..65942a2567 100644 --- a/libavcodec/ass.c +++ b/libavcodec/ass.c @@ -24,57 +24,119 @@ #include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" +#include "libavutil/bswap.h" #include "libavutil/common.h" +static int invert_ass_alpha(uint32_t c) +{ + uint32_t a = c >> 24; + return ((255 - a) << 24) | (c & 0xffffff); +} + +#define CL_FF2ASS(c) invert_ass_alpha(av_le2ne32(c)) +#define CL_ASS2FF(c) av_le2ne32(invert_ass_alpha(c)) + +int ff_ass_bprint_style(AVBPrint *buf, const FFASSStyle *style) +{ + av_bprintf(buf, + "Style: " + "%s," /* Name */ + "%s,%g," /* Font{name,size} */ + "&H%x,&H%x,&H%x,&H%x," /* {Primary,Secondary,Outline,Back}Colour */ + "%d,%d,%d,%d," /* Bold, Italic, Underline, StrikeOut */ + "%g,%g," /* Scale{X,Y} */ + "%g,%g," /* Spacing, Angle */ + "%d,%g,%g," /* BorderStyle, Outline, Shadow */ + "%d,%d,%d,%d," /* Alignment, Margin[LRV] */ + "0\r\n", /* Encoding */ + style->name ? style->name : "Default", + style->font ? style->font : ASS_DEFAULT_FONT, style->font_size, + CL_FF2ASS(style->color), CL_FF2ASS(style->color2), + CL_FF2ASS(style->outline_color), CL_FF2ASS(style->back_color), + style->bold, style->italic, style->underline, style->strikeout, + style->scale_x, style->scale_y, + style->spacing, style->angle, + style->border_style, style->outline, style->shadow, + style->alignment, style->margin_l, style->margin_r, style->margin_v); + + return 0; // Not currently possible to detect bprintf errors +} + +int ff_ass_subtitle_header2(AVCodecContext *avctx, int res_x, int res_y, const FFASSStyle *style) +{ + int ret = 0; + AVBPrint buf; + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC); + av_bprintf(&buf, + "[Script Info]\r\n" + "; Script generated by FFmpeg/Lavc%s\r\n" + "ScriptType: v4.00+\r\n" + "PlayResX: %d\r\n" + "PlayResY: %d\r\n" + "\r\n" + "[V4+ Styles]\r\n" + + /* ASSv4+ header */ + "Format: Name, " + "Fontname, Fontsize, " + "PrimaryColour, SecondaryColour, OutlineColour, BackColour, " + "Bold, Italic, Underline, StrikeOut, " + "ScaleX, ScaleY, " + "Spacing, Angle, " + "BorderStyle, Outline, Shadow, " + "Alignment, MarginL, MarginR, MarginV, " + "Encoding\r\n", + !(avctx->flags & AV_CODEC_FLAG_BITEXACT) ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "", + res_x, res_y); + + if ((ret = ff_ass_bprint_style(&buf, style) < 0)) + goto fail; + + av_bprintf(&buf, + "\r\n" + "[Events]\r\n" + "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n"); + + if ((ret = av_bprint_finalize(&buf, (char**)&avctx->subtitle_header)) < 0) + return ret; + + avctx->subtitle_header_size = buf.len; + return 0; + +fail: + av_bprint_finalize(&buf, NULL); + return ret; +} + +int ff_ass_subtitle_header_from_opts(AVCodecContext *avctx, const FFASSHeaderOptions *opts) +{ + return ff_ass_subtitle_header2(avctx, opts->res_x, opts->res_y, &opts->style); +} + 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) { - avctx->subtitle_header = av_asprintf( - "[Script Info]\r\n" - "; Script generated by FFmpeg/Lavc%s\r\n" - "ScriptType: v4.00+\r\n" - "PlayResX: %d\r\n" - "PlayResY: %d\r\n" - "\r\n" - "[V4+ Styles]\r\n" - - /* ASSv4 header */ - "Format: Name, " - "Fontname, Fontsize, " - "PrimaryColour, SecondaryColour, OutlineColour, BackColour, " - "Bold, Italic, Underline, StrikeOut, " - "ScaleX, ScaleY, " - "Spacing, Angle, " - "BorderStyle, Outline, Shadow, " - "Alignment, MarginL, MarginR, MarginV, " - "Encoding\r\n" - - "Style: " - "Default," /* Name */ - "%s,%d," /* Font{name,size} */ - "&H%x,&H%x,&H%x,&H%x," /* {Primary,Secondary,Outline,Back}Colour */ - "%d,%d,%d,0," /* Bold, Italic, Underline, StrikeOut */ - "100,100," /* Scale{X,Y} */ - "0,0," /* Spacing, Angle */ - "%d,1,0," /* BorderStyle, Outline, Shadow */ - "%d,10,10,10," /* Alignment, Margin[LRV] */ - "0\r\n" /* Encoding */ - - "\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) : "", - ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, - font, font_size, color, color, back_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 ff_ass_subtitle_header2(avctx, ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, + &(FFASSStyle){ + .name = "Default", + .font = font, .font_size = font_size, + .color = CL_ASS2FF(color), .color2 = CL_ASS2FF(color), + .outline_color = CL_ASS2FF(back_color), .back_color = CL_ASS2FF(back_color), + .bold = bold, .italic = italic, + .underline = underline, .strikeout = 0, + .scale_x = 100, .scale_y = 100, + .spacing = 0, .angle = 0, + .border_style = border_style, + .outline = ASS_DEFAULT_OUTLINE, + .shadow = ASS_DEFAULT_SHADOW, + .alignment = alignment, + .margin_l = ASS_DEFAULT_MARGINL, + .margin_r = ASS_DEFAULT_MARGINR, + .margin_v = ASS_DEFAULT_MARGINV, + }); } int ff_ass_subtitle_header_default(AVCodecContext *avctx) @@ -90,6 +152,12 @@ int ff_ass_subtitle_header_default(AVCodecContext *avctx) ASS_DEFAULT_ALIGNMENT); } +int ff_ass_subtitle_header_options(AVCodecContext *avctx) +{ + FFASSDecoderContext *s = avctx->priv_data; + return ff_ass_subtitle_header_from_opts(avctx, &s->common); +} + char *ff_ass_get_dialog(int readorder, int layer, const char *style, const char *speaker, const char *text) { diff --git a/libavcodec/ass.h b/libavcodec/ass.h index 314b43b686..454e5663ed 100644 --- a/libavcodec/ass.h +++ b/libavcodec/ass.h @@ -24,9 +24,11 @@ #include "avcodec.h" #include "libavutil/bprint.h" +#include "libavutil/opt.h" #define ASS_DEFAULT_PLAYRESX 384 #define ASS_DEFAULT_PLAYRESY 288 +#define ASS_DEFAULT_PLAYRES_STR "384x288" /** * @name Default values for ASS style @@ -35,29 +37,120 @@ #define ASS_DEFAULT_FONT "Arial" #define ASS_DEFAULT_FONT_SIZE 16 #define ASS_DEFAULT_COLOR 0xffffff +#define ASS_DEFAULT_COLOR_STR "#ffffff" #define ASS_DEFAULT_BACK_COLOR 0 +#define ASS_DEFAULT_BCOLOR_STR "#000000" #define ASS_DEFAULT_BOLD 0 #define ASS_DEFAULT_ITALIC 0 #define ASS_DEFAULT_UNDERLINE 0 -#define ASS_DEFAULT_ALIGNMENT 2 +#define ASS_DEFAULT_STRIKEOUT 0 +#define ASS_DEFAULT_SCALE_X 100. +#define ASS_DEFAULT_SCALE_Y 100. #define ASS_DEFAULT_BORDERSTYLE 1 +#define ASS_DEFAULT_OUTLINE 1 +#define ASS_DEFAULT_SHADOW 0 +#define ASS_DEFAULT_ALIGNMENT 2 +#define ASS_DEFAULT_MARGINL 10 +#define ASS_DEFAULT_MARGINR 10 +#define ASS_DEFAULT_MARGINV 10 /** @} */ +typedef struct FFASSStyle { + const char *name; + const char *font; double font_size; + uint32_t color, color2, outline_color, back_color; // Big-endian RGBA + int bold, italic, underline, strikeout; + double scale_x, scale_y; + double spacing, angle; + int border_style; + double outline, shadow; + int alignment; + int margin_l, margin_r, margin_v; +} FFASSStyle; + +typedef struct FFASSHeaderOptions { + int res_x, res_y; + FFASSStyle style; +} FFASSHeaderOptions; + +#define ASS_DS AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_SUBTITLE_PARAM + +#define ASS_HEADER_AVOPTIONS(cls, obj) \ + { "res", "script resolution", offsetof(cls, obj.res_x), AV_OPT_TYPE_IMAGE_SIZE, { .str = ASS_DEFAULT_PLAYRES_STR }, 1, INT_MAX, ASS_DS }, \ + { "font", "default font name", offsetof(cls, obj.style.font), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, ASS_DS }, \ + { "font_size", "default font name", offsetof(cls, obj.style.font_size), AV_OPT_TYPE_DOUBLE, { .dbl = ASS_DEFAULT_FONT_SIZE }, 0, INT_MAX, ASS_DS }, \ + { "color", "default text color", offsetof(cls, obj.style.color), AV_OPT_TYPE_COLOR, { .str = ASS_DEFAULT_COLOR_STR }, 0, 0, ASS_DS }, \ + { "color2", "default secondary text color", offsetof(cls, obj.style.color2), AV_OPT_TYPE_COLOR, { .str = ASS_DEFAULT_COLOR_STR }, 0, 0, ASS_DS }, \ + { "outline_color", "default text outline color", offsetof(cls, obj.style.outline_color), AV_OPT_TYPE_COLOR, { .str = ASS_DEFAULT_BCOLOR_STR }, 0, 0, ASS_DS }, \ + { "back_color", "default text background/shadow color", offsetof(cls, obj.style.back_color), AV_OPT_TYPE_COLOR, { .str = ASS_DEFAULT_BCOLOR_STR }, 0, 0, ASS_DS }, \ + { "bold", "default text boldness (0/1/weight value)", offsetof(cls, obj.style.bold), AV_OPT_TYPE_INT, { .i64 = ASS_DEFAULT_BOLD }, 0, INT_MAX, ASS_DS }, \ + { "italic", "default text italics", offsetof(cls, obj.style.bold), AV_OPT_TYPE_BOOL, { .i64 = ASS_DEFAULT_ITALIC }, 0, 1, ASS_DS }, \ + { "underline", "default text italics", offsetof(cls, obj.style.underline), AV_OPT_TYPE_BOOL, { .i64 = ASS_DEFAULT_UNDERLINE }, 0, 1, ASS_DS }, \ + { "strikeout", "default text strikeout", offsetof(cls, obj.style.strikeout), AV_OPT_TYPE_BOOL, { .i64 = ASS_DEFAULT_STRIKEOUT }, 0, 1, ASS_DS }, \ + { "scale_x", "default horizontal text scale", offsetof(cls, obj.style.scale_x), AV_OPT_TYPE_DOUBLE, { .dbl = ASS_DEFAULT_SCALE_X }, 0, INT_MAX, ASS_DS }, \ + { "scale_y", "default vertical text scale", offsetof(cls, obj.style.scale_y), AV_OPT_TYPE_DOUBLE, { .dbl = ASS_DEFAULT_SCALE_Y }, 0, INT_MAX, ASS_DS }, \ + { "border_style", "default text border style", offsetof(cls, obj.style.border_style), AV_OPT_TYPE_INT, { .i64 = ASS_DEFAULT_BORDERSTYLE }, 1, 4, ASS_DS }, \ + { "outline", "default text outline width", offsetof(cls, obj.style.outline), AV_OPT_TYPE_DOUBLE, { .dbl = ASS_DEFAULT_OUTLINE }, 0, INT_MAX, ASS_DS }, \ + { "shadow", "default text shadow drop", offsetof(cls, obj.style.shadow), AV_OPT_TYPE_DOUBLE, { .dbl = ASS_DEFAULT_SHADOW }, 0, INT_MAX, ASS_DS }, \ + { "alignment", "default text alignment", offsetof(cls, obj.style.alignment), AV_OPT_TYPE_INT, { .i64 = ASS_DEFAULT_ALIGNMENT }, 1, 9, ASS_DS }, \ + { "margin_l", "default left margin", offsetof(cls, obj.style.margin_l), AV_OPT_TYPE_INT, { .i64 = ASS_DEFAULT_MARGINL }, 0, INT_MAX, ASS_DS }, \ + { "margin_r", "default right margin", offsetof(cls, obj.style.margin_r), AV_OPT_TYPE_INT, { .i64 = ASS_DEFAULT_MARGINR }, 0, INT_MAX, ASS_DS }, \ + { "margin_v", "default vertical margin", offsetof(cls, obj.style.margin_v), AV_OPT_TYPE_INT, { .i64 = ASS_DEFAULT_MARGINV }, 0, INT_MAX, ASS_DS }, \ + +#define ASS_GENERIC_OPTIONS(name) \ +static const AVOption name##_options[] = { \ + ASS_HEADER_AVOPTIONS(FFASSDecoderContext, common) \ + { NULL }, \ +}; + typedef struct FFASSDecoderContext { + AVClass *class; int readorder; + FFASSHeaderOptions common; } FFASSDecoderContext; +#define ASS_GENERIC_CLASS(name, StringName) \ +ASS_GENERIC_OPTIONS(name) \ +\ +static const AVClass name##_decoder_class = { \ + .class_name = #StringName " subtitle decoder", \ + .item_name = av_default_item_name, \ + .option = name##_options, \ + .version = LIBAVUTIL_VERSION_INT, \ +}; + + +/** + * Convert an FFASSStyle to ASS text + */ +int ff_ass_bprint_style(AVBPrint *buf, const FFASSStyle *style); + /** * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS. * * @param avctx pointer to the AVCodecContext + * @param res_x horizontal script resolution + * @param res_y vertical script resolution + * @param style the default style to use + * @return >= 0 on success otherwise an error code <0 + */ +int ff_ass_subtitle_header2(AVCodecContext *avctx, + int res_x, int res_y, + const FFASSStyle *style); + +int ff_ass_subtitle_header_from_opts(AVCodecContext *avctx, const FFASSHeaderOptions *opts); + +/** + * Simple form of ff_ass_subtitle_header2 + * + * @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 underline 1 for underlined 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 @@ -77,6 +170,15 @@ int ff_ass_subtitle_header(AVCodecContext *avctx, */ int ff_ass_subtitle_header_default(AVCodecContext *avctx); +/** + * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS + * with style configured by FFASSDecoderContext AVOptions. + * + * @param avctx pointer to the AVCodecContext + * @return >= 0 on success otherwise an error code <0 + */ +int ff_ass_subtitle_header_options(AVCodecContext *avctx); + /** * Craft an ASS dialog string. */