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 unsigned timestamp_end
;
38 char stack
[SRT_STACK_SIZE
];
40 int alignment_applied
;
45 __attribute__ ((__format__ (__printf__
, 2, 3)))
47 static void srt_print(SRTContext
*s
, const char *str
, ...)
51 av_vbprintf(&s
->buffer
, str
, vargs
);
55 static int srt_stack_push(SRTContext
*s
, const char c
)
57 if (s
->stack_ptr
>= SRT_STACK_SIZE
)
59 s
->stack
[s
->stack_ptr
++] = c
;
63 static char srt_stack_pop(SRTContext
*s
)
65 if (s
->stack_ptr
<= 0)
67 return s
->stack
[--s
->stack_ptr
];
70 static int srt_stack_find(SRTContext
*s
, const char c
)
73 for (i
= s
->stack_ptr
-1; i
>= 0; i
--)
79 static void srt_close_tag(SRTContext
*s
, char tag
)
81 srt_print(s
, "</%c%s>", tag
, tag
== 'f' ? "ont" : "");
84 static void srt_stack_push_pop(SRTContext
*s
, const char c
, int close
)
87 int i
= c
? srt_stack_find(s
, c
) : 0;
90 while (s
->stack_ptr
!= i
)
91 srt_close_tag(s
, srt_stack_pop(s
));
92 } else if (srt_stack_push(s
, c
) < 0)
93 av_log(s
->avctx
, AV_LOG_ERROR
, "tag stack overflow\n");
96 static void srt_style_apply(SRTContext
*s
, const char *style
)
98 ASSStyle
*st
= ff_ass_style_get(s
->ass_ctx
, style
);
100 int c
= st
->primary_color
& 0xFFFFFF;
101 if (st
->font_name
&& strcmp(st
->font_name
, ASS_DEFAULT_FONT
) ||
102 st
->font_size
!= ASS_DEFAULT_FONT_SIZE
||
103 c
!= ASS_DEFAULT_COLOR
) {
104 srt_print(s
, "<font");
105 if (st
->font_name
&& strcmp(st
->font_name
, ASS_DEFAULT_FONT
))
106 srt_print(s
, " face=\"%s\"", st
->font_name
);
107 if (st
->font_size
!= ASS_DEFAULT_FONT_SIZE
)
108 srt_print(s
, " size=\"%d\"", st
->font_size
);
109 if (c
!= ASS_DEFAULT_COLOR
)
110 srt_print(s
, " color=\"#%06x\"",
111 (c
& 0xFF0000) >> 16 | c
& 0xFF00 | (c
& 0xFF) << 16);
113 srt_stack_push(s
, 'f');
115 if (st
->bold
!= ASS_DEFAULT_BOLD
) {
117 srt_stack_push(s
, 'b');
119 if (st
->italic
!= ASS_DEFAULT_ITALIC
) {
121 srt_stack_push(s
, 'i');
123 if (st
->underline
!= ASS_DEFAULT_UNDERLINE
) {
125 srt_stack_push(s
, 'u');
127 if (st
->alignment
!= ASS_DEFAULT_ALIGNMENT
) {
128 srt_print(s
, "{\\an%d}", st
->alignment
);
129 s
->alignment_applied
= 1;
135 static av_cold
int srt_encode_init(AVCodecContext
*avctx
)
137 SRTContext
*s
= avctx
->priv_data
;
139 s
->ass_ctx
= ff_ass_split(avctx
->subtitle_header
);
140 av_bprint_init(&s
->buffer
, 0, AV_BPRINT_SIZE_UNLIMITED
);
141 return s
->ass_ctx
? 0 : AVERROR_INVALIDDATA
;
144 static void srt_text_cb(void *priv
, const char *text
, int len
)
146 SRTContext
*s
= priv
;
147 av_bprint_append_data(&s
->buffer
, text
, len
);
150 static void srt_new_line_cb(void *priv
, int forced
)
152 srt_print(priv
, "\r\n");
155 static void srt_style_cb(void *priv
, char style
, int close
)
157 srt_stack_push_pop(priv
, style
, close
);
159 srt_print(priv
, "<%c>", style
);
162 static void srt_color_cb(void *priv
, unsigned int color
, unsigned int color_id
)
166 srt_stack_push_pop(priv
, 'f', color
== 0xFFFFFFFF);
167 if (color
!= 0xFFFFFFFF)
168 srt_print(priv
, "<font color=\"#%06x\">",
169 (color
& 0xFF0000) >> 16 | color
& 0xFF00 | (color
& 0xFF) << 16);
172 static void srt_font_name_cb(void *priv
, const char *name
)
174 srt_stack_push_pop(priv
, 'f', !name
);
176 srt_print(priv
, "<font face=\"%s\">", name
);
179 static void srt_font_size_cb(void *priv
, int size
)
181 srt_stack_push_pop(priv
, 'f', size
< 0);
183 srt_print(priv
, "<font size=\"%d\">", size
);
186 static void srt_alignment_cb(void *priv
, int alignment
)
188 SRTContext
*s
= priv
;
189 if (!s
->alignment_applied
&& alignment
>= 0) {
190 srt_print(s
, "{\\an%d}", alignment
);
191 s
->alignment_applied
= 1;
195 static void srt_cancel_overrides_cb(void *priv
, const char *style
)
197 srt_stack_push_pop(priv
, 0, 1);
198 srt_style_apply(priv
, style
);
201 static void srt_move_cb(void *priv
, int x1
, int y1
, int x2
, int y2
,
204 SRTContext
*s
= priv
;
206 if (s
->avctx
->codec
->id
== AV_CODEC_ID_SRT
) {
208 int len
= snprintf(buffer
, sizeof(buffer
),
209 " X1:%03u X2:%03u Y1:%03u Y2:%03u", x1
, x2
, y1
, y2
);
210 unsigned char *dummy
;
213 av_bprint_get_buffer(&s
->buffer
, len
, &dummy
, &room
);
215 memmove(s
->buffer
.str
+ s
->timestamp_end
+ len
,
216 s
->buffer
.str
+ s
->timestamp_end
,
217 s
->buffer
.len
- s
->timestamp_end
+ 1);
218 memcpy(s
->buffer
.str
+ s
->timestamp_end
, buffer
, len
);
220 /* Increment even if av_bprint_get_buffer() did not return enough room:
221 the bprint structure will be treated as truncated. */
222 s
->buffer
.len
+= len
;
226 static void srt_end_cb(void *priv
)
228 SRTContext
*s
= priv
;
230 srt_stack_push_pop(priv
, 0, 1);
231 if (s
->avctx
->codec
->id
== AV_CODEC_ID_SRT
)
232 srt_print(priv
, "\r\n\r\n");
235 static const ASSCodesCallbacks srt_callbacks
= {
237 .new_line
= srt_new_line_cb
,
238 .style
= srt_style_cb
,
239 .color
= srt_color_cb
,
240 .font_name
= srt_font_name_cb
,
241 .font_size
= srt_font_size_cb
,
242 .alignment
= srt_alignment_cb
,
243 .cancel_overrides
= srt_cancel_overrides_cb
,
248 static int srt_encode_frame(AVCodecContext
*avctx
,
249 unsigned char *buf
, int bufsize
, const AVSubtitle
*sub
)
251 SRTContext
*s
= avctx
->priv_data
;
255 av_bprint_clear(&s
->buffer
);
257 for (i
=0; i
<sub
->num_rects
; i
++) {
259 if (sub
->rects
[i
]->type
!= SUBTITLE_ASS
) {
260 av_log(avctx
, AV_LOG_ERROR
, "Only SUBTITLE_ASS type supported.\n");
261 return AVERROR(ENOSYS
);
264 dialog
= ff_ass_split_dialog(s
->ass_ctx
, sub
->rects
[i
]->ass
, 0, &num
);
265 for (; dialog
&& num
--; dialog
++) {
266 if (avctx
->codec
->id
== AV_CODEC_ID_SRT
) {
267 int sh
, sm
, ss
, sc
= 10 * dialog
->start
;
268 int eh
, em
, es
, ec
= 10 * dialog
->end
;
269 sh
= sc
/3600000; sc
-= 3600000*sh
;
270 sm
= sc
/ 60000; sc
-= 60000*sm
;
271 ss
= sc
/ 1000; sc
-= 1000*ss
;
272 eh
= ec
/3600000; ec
-= 3600000*eh
;
273 em
= ec
/ 60000; ec
-= 60000*em
;
274 es
= ec
/ 1000; ec
-= 1000*es
;
275 srt_print(s
,"%d\r\n%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d\r\n",
276 ++s
->count
, sh
, sm
, ss
, sc
, eh
, em
, es
, ec
);
277 s
->timestamp_end
= s
->buffer
.len
- 2;
279 s
->alignment_applied
= 0;
280 srt_style_apply(s
, dialog
->style
);
281 ff_ass_split_override_codes(&srt_callbacks
, s
, dialog
->text
);
285 if (!av_bprint_is_complete(&s
->buffer
))
286 return AVERROR(ENOMEM
);
290 if (s
->buffer
.len
> bufsize
) {
291 av_log(avctx
, AV_LOG_ERROR
, "Buffer too small for ASS event.\n");
294 memcpy(buf
, s
->buffer
.str
, s
->buffer
.len
);
296 return s
->buffer
.len
;
299 static int srt_encode_close(AVCodecContext
*avctx
)
301 SRTContext
*s
= avctx
->priv_data
;
302 ff_ass_split_free(s
->ass_ctx
);
303 av_bprint_finalize(&s
->buffer
, NULL
);
307 #if CONFIG_SRT_ENCODER
308 /* deprecated encoder */
309 AVCodec ff_srt_encoder
= {
311 .long_name
= NULL_IF_CONFIG_SMALL("SubRip subtitle with embedded timing"),
312 .type
= AVMEDIA_TYPE_SUBTITLE
,
313 .id
= AV_CODEC_ID_SRT
,
314 .priv_data_size
= sizeof(SRTContext
),
315 .init
= srt_encode_init
,
316 .encode_sub
= srt_encode_frame
,
317 .close
= srt_encode_close
,
321 #if CONFIG_SUBRIP_ENCODER
322 AVCodec ff_subrip_encoder
= {
324 .long_name
= NULL_IF_CONFIG_SMALL("SubRip subtitle"),
325 .type
= AVMEDIA_TYPE_SUBTITLE
,
326 .id
= AV_CODEC_ID_SUBRIP
,
327 .priv_data_size
= sizeof(SRTContext
),
328 .init
= srt_encode_init
,
329 .encode_sub
= srt_encode_frame
,
330 .close
= srt_encode_close
,