@@ -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)
{
@@ -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.
*/