2 * SubRip subtitle encoder
3 * Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.org>
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 #include "libavutil/avstring.h"
25 #include "libavutil/bprint.h"
26 #include "ass_split.h"
30 #define SRT_STACK_SIZE 64
33 AVCodecContext
*avctx
;
34 ASSSplitContext
*ass_ctx
;
36 char stack
[SRT_STACK_SIZE
];
38 int alignment_applied
;
43 __attribute__ ((__format__ (__printf__
, 2, 3)))
45 static void srt_print(SRTContext
*s
, const char *str
, ...)
49 av_vbprintf(&s
->buffer
, str
, vargs
);
53 static int srt_stack_push(SRTContext
*s
, const char c
)
55 if (s
->stack_ptr
>= SRT_STACK_SIZE
)
57 s
->stack
[s
->stack_ptr
++] = c
;
61 static char srt_stack_pop(SRTContext
*s
)
63 if (s
->stack_ptr
<= 0)
65 return s
->stack
[--s
->stack_ptr
];
68 static int srt_stack_find(SRTContext
*s
, const char c
)
71 for (i
= s
->stack_ptr
-1; i
>= 0; i
--)
77 static void srt_close_tag(SRTContext
*s
, char tag
)
79 srt_print(s
, "</%c%s>", tag
, tag
== 'f' ? "ont" : "");
82 static void srt_stack_push_pop(SRTContext
*s
, const char c
, int close
)
85 int i
= c
? srt_stack_find(s
, c
) : 0;
88 while (s
->stack_ptr
!= i
)
89 srt_close_tag(s
, srt_stack_pop(s
));
90 } else if (srt_stack_push(s
, c
) < 0)
91 av_log(s
->avctx
, AV_LOG_ERROR
, "tag stack overflow\n");
94 static void srt_style_apply(SRTContext
*s
, const char *style
)
96 ASSStyle
*st
= ff_ass_style_get(s
->ass_ctx
, style
);
98 int c
= st
->primary_color
& 0xFFFFFF;
99 if (st
->font_name
&& strcmp(st
->font_name
, ASS_DEFAULT_FONT
) ||
100 st
->font_size
!= ASS_DEFAULT_FONT_SIZE
||
101 c
!= ASS_DEFAULT_COLOR
) {
102 srt_print(s
, "<font");
103 if (st
->font_name
&& strcmp(st
->font_name
, ASS_DEFAULT_FONT
))
104 srt_print(s
, " face=\"%s\"", st
->font_name
);
105 if (st
->font_size
!= ASS_DEFAULT_FONT_SIZE
)
106 srt_print(s
, " size=\"%d\"", st
->font_size
);
107 if (c
!= ASS_DEFAULT_COLOR
)
108 srt_print(s
, " color=\"#%06x\"",
109 (c
& 0xFF0000) >> 16 | c
& 0xFF00 | (c
& 0xFF) << 16);
111 srt_stack_push(s
, 'f');
113 if (st
->bold
!= ASS_DEFAULT_BOLD
) {
115 srt_stack_push(s
, 'b');
117 if (st
->italic
!= ASS_DEFAULT_ITALIC
) {
119 srt_stack_push(s
, 'i');
121 if (st
->underline
!= ASS_DEFAULT_UNDERLINE
) {
123 srt_stack_push(s
, 'u');
125 if (st
->alignment
!= ASS_DEFAULT_ALIGNMENT
) {
126 srt_print(s
, "{\\an%d}", st
->alignment
);
127 s
->alignment_applied
= 1;
133 static av_cold
int srt_encode_init(AVCodecContext
*avctx
)
135 SRTContext
*s
= avctx
->priv_data
;
137 s
->ass_ctx
= ff_ass_split(avctx
->subtitle_header
);
138 av_bprint_init(&s
->buffer
, 0, AV_BPRINT_SIZE_UNLIMITED
);
139 return s
->ass_ctx
? 0 : AVERROR_INVALIDDATA
;
142 static void srt_text_cb(void *priv
, const char *text
, int len
)
144 SRTContext
*s
= priv
;
145 av_bprint_append_data(&s
->buffer
, text
, len
);
148 static void srt_new_line_cb(void *priv
, int forced
)
150 srt_print(priv
, "\r\n");
153 static void srt_style_cb(void *priv
, char style
, int close
)
155 srt_stack_push_pop(priv
, style
, close
);
157 srt_print(priv
, "<%c>", style
);
160 static void srt_color_cb(void *priv
, unsigned int color
, unsigned int color_id
)
164 srt_stack_push_pop(priv
, 'f', color
== 0xFFFFFFFF);
165 if (color
!= 0xFFFFFFFF)
166 srt_print(priv
, "<font color=\"#%06x\">",
167 (color
& 0xFF0000) >> 16 | color
& 0xFF00 | (color
& 0xFF) << 16);
170 static void srt_font_name_cb(void *priv
, const char *name
)
172 srt_stack_push_pop(priv
, 'f', !name
);
174 srt_print(priv
, "<font face=\"%s\">", name
);
177 static void srt_font_size_cb(void *priv
, int size
)
179 srt_stack_push_pop(priv
, 'f', size
< 0);
181 srt_print(priv
, "<font size=\"%d\">", size
);
184 static void srt_alignment_cb(void *priv
, int alignment
)
186 SRTContext
*s
= priv
;
187 if (!s
->alignment_applied
&& alignment
>= 0) {
188 srt_print(s
, "{\\an%d}", alignment
);
189 s
->alignment_applied
= 1;
193 static void srt_cancel_overrides_cb(void *priv
, const char *style
)
195 srt_stack_push_pop(priv
, 0, 1);
196 srt_style_apply(priv
, style
);
199 static void srt_move_cb(void *priv
, int x1
, int y1
, int x2
, int y2
,
202 // TODO: add a AV_PKT_DATA_SUBTITLE_POSITION side data when a new subtitles
203 // encoding API passing the AVPacket is available.
206 static void srt_end_cb(void *priv
)
208 srt_stack_push_pop(priv
, 0, 1);
211 static const ASSCodesCallbacks srt_callbacks
= {
213 .new_line
= srt_new_line_cb
,
214 .style
= srt_style_cb
,
215 .color
= srt_color_cb
,
216 .font_name
= srt_font_name_cb
,
217 .font_size
= srt_font_size_cb
,
218 .alignment
= srt_alignment_cb
,
219 .cancel_overrides
= srt_cancel_overrides_cb
,
224 static int srt_encode_frame(AVCodecContext
*avctx
,
225 unsigned char *buf
, int bufsize
, const AVSubtitle
*sub
)
227 SRTContext
*s
= avctx
->priv_data
;
231 av_bprint_clear(&s
->buffer
);
233 for (i
=0; i
<sub
->num_rects
; i
++) {
235 if (sub
->rects
[i
]->type
!= SUBTITLE_ASS
) {
236 av_log(avctx
, AV_LOG_ERROR
, "Only SUBTITLE_ASS type supported.\n");
237 return AVERROR(ENOSYS
);
240 dialog
= ff_ass_split_dialog(s
->ass_ctx
, sub
->rects
[i
]->ass
, 0, &num
);
241 for (; dialog
&& num
--; dialog
++) {
242 s
->alignment_applied
= 0;
243 srt_style_apply(s
, dialog
->style
);
244 ff_ass_split_override_codes(&srt_callbacks
, s
, dialog
->text
);
248 if (!av_bprint_is_complete(&s
->buffer
))
249 return AVERROR(ENOMEM
);
253 if (s
->buffer
.len
> bufsize
) {
254 av_log(avctx
, AV_LOG_ERROR
, "Buffer too small for ASS event.\n");
257 memcpy(buf
, s
->buffer
.str
, s
->buffer
.len
);
259 return s
->buffer
.len
;
262 static int srt_encode_close(AVCodecContext
*avctx
)
264 SRTContext
*s
= avctx
->priv_data
;
265 ff_ass_split_free(s
->ass_ctx
);
266 av_bprint_finalize(&s
->buffer
, NULL
);
270 #if CONFIG_SRT_ENCODER
271 /* deprecated encoder */
272 AVCodec ff_srt_encoder
= {
274 .long_name
= NULL_IF_CONFIG_SMALL("SubRip subtitle"),
275 .type
= AVMEDIA_TYPE_SUBTITLE
,
276 .id
= AV_CODEC_ID_SUBRIP
,
277 .priv_data_size
= sizeof(SRTContext
),
278 .init
= srt_encode_init
,
279 .encode_sub
= srt_encode_frame
,
280 .close
= srt_encode_close
,
284 #if CONFIG_SUBRIP_ENCODER
285 AVCodec ff_subrip_encoder
= {
287 .long_name
= NULL_IF_CONFIG_SMALL("SubRip subtitle"),
288 .type
= AVMEDIA_TYPE_SUBTITLE
,
289 .id
= AV_CODEC_ID_SUBRIP
,
290 .priv_data_size
= sizeof(SRTContext
),
291 .init
= srt_encode_init
,
292 .encode_sub
= srt_encode_frame
,
293 .close
= srt_encode_close
,