2 * SubRip subtitle decoder
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
22 #include "libavutil/avstring.h"
23 #include "libavutil/common.h"
24 #include "libavutil/intreadwrite.h"
25 #include "libavutil/parseutils.h"
29 static int html_color_parse(AVCodecContext
*avctx
, const char *str
)
32 if (av_parse_color(rgba
, str
, strcspn(str
, "\" >"), avctx
) < 0)
34 return rgba
[0] | rgba
[1] << 8 | rgba
[2] << 16;
47 char param
[PARAM_NUMBER
][128];
50 static void rstrip_spaces_buf(AVBPrint
*buf
)
52 while (buf
->len
> 0 && buf
->str
[buf
->len
- 1] == ' ')
53 buf
->str
[--buf
->len
] = 0;
56 static void srt_to_ass(AVCodecContext
*avctx
, AVBPrint
*dst
,
57 const char *in
, int x1
, int y1
, int x2
, int y2
)
59 char *param
, buffer
[128], tmp
[128];
60 int len
, tag_close
, sptr
= 1, line_start
= 1, an
= 0, end
= 0;
64 strcpy(stack
[0].param
[PARAM_SIZE
], "{\\fs}");
65 strcpy(stack
[0].param
[PARAM_COLOR
], "{\\c}");
66 strcpy(stack
[0].param
[PARAM_FACE
], "{\\fn}");
68 if (x1
>= 0 && y1
>= 0) {
69 if (x2
>= 0 && y2
>= 0 && (x2
!= x1
|| y2
!= y1
))
70 av_bprintf(dst
, "{\\an1}{\\move(%d,%d,%d,%d)}", x1
, y1
, x2
, y2
);
72 av_bprintf(dst
, "{\\an1}{\\pos(%d,%d)}", x1
, y1
);
75 for (; !end
&& *in
; in
++) {
84 rstrip_spaces_buf(dst
);
85 av_bprintf(dst
, "\\N");
90 av_bprint_chars(dst
, *in
, 1);
92 case '{': /* skip all {\xxx} substrings except for {\an%d}
93 and all microdvd like styles such as {Y:xxx} */
95 an
+= sscanf(in
, "{\\an%*1u}%n", &len
) >= 0 && len
> 0;
96 if ((an
!= 1 && (len
= 0, sscanf(in
, "{\\%*[^}]}%n", &len
) >= 0 && len
> 0)) ||
97 (len
= 0, sscanf(in
, "{%*1[CcFfoPSsYy]:%*[^}]}%n", &len
) >= 0 && len
> 0)) {
100 av_bprint_chars(dst
, *in
, 1);
103 tag_close
= in
[1] == '/';
105 if (sscanf(in
+tag_close
+1, "%127[^>]>%n", buffer
, &len
) >= 1 && len
> 0) {
106 if ((param
= strchr(buffer
, ' ')))
108 if ((!tag_close
&& sptr
< FF_ARRAY_ELEMS(stack
)) ||
109 ( tag_close
&& sptr
> 0 && !strcmp(stack
[sptr
-1].tag
, buffer
))) {
110 int i
, j
, unknown
= 0;
111 in
+= len
+ tag_close
;
113 memset(stack
+sptr
, 0, sizeof(*stack
));
114 if (!strcmp(buffer
, "font")) {
116 for (i
=PARAM_NUMBER
-1; i
>=0; i
--)
117 if (stack
[sptr
-1].param
[i
][0])
118 for (j
=sptr
-2; j
>=0; j
--)
119 if (stack
[j
].param
[i
][0]) {
120 av_bprintf(dst
, "%s", stack
[j
].param
[i
]);
125 if (!strncmp(param
, "size=", 5)) {
127 param
+= 5 + (param
[5] == '"');
128 if (sscanf(param
, "%u", &font_size
) == 1) {
129 snprintf(stack
[sptr
].param
[PARAM_SIZE
],
130 sizeof(stack
[0].param
[PARAM_SIZE
]),
131 "{\\fs%u}", font_size
);
133 } else if (!strncmp(param
, "color=", 6)) {
134 param
+= 6 + (param
[6] == '"');
135 snprintf(stack
[sptr
].param
[PARAM_COLOR
],
136 sizeof(stack
[0].param
[PARAM_COLOR
]),
138 html_color_parse(avctx
, param
));
139 } else if (!strncmp(param
, "face=", 5)) {
140 param
+= 5 + (param
[5] == '"');
142 param
[-1] == '"' ? "\"" :" ");
143 av_strlcpy(tmp
, param
,
144 FFMIN(sizeof(tmp
), len
+1));
146 snprintf(stack
[sptr
].param
[PARAM_FACE
],
147 sizeof(stack
[0].param
[PARAM_FACE
]),
150 if ((param
= strchr(param
, ' ')))
153 for (i
=0; i
<PARAM_NUMBER
; i
++)
154 if (stack
[sptr
].param
[i
][0])
155 av_bprintf(dst
, "%s", stack
[sptr
].param
[i
]);
157 } else if (!buffer
[1] && strspn(buffer
, "bisu") == 1) {
158 av_bprintf(dst
, "{\\%c%d}", buffer
[0], !tag_close
);
161 snprintf(tmp
, sizeof(tmp
), "</%s>", buffer
);
165 } else if (unknown
&& !strstr(in
, tmp
)) {
166 in
-= len
+ tag_close
;
167 av_bprint_chars(dst
, *in
, 1);
169 av_strlcpy(stack
[sptr
++].tag
, buffer
,
170 sizeof(stack
[0].tag
));
175 av_bprint_chars(dst
, *in
, 1);
178 if (*in
!= ' ' && *in
!= '\r' && *in
!= '\n')
182 while (dst
->len
>= 2 && !strncmp(&dst
->str
[dst
->len
- 2], "\\N", 2))
184 dst
->str
[dst
->len
] = 0;
185 rstrip_spaces_buf(dst
);
188 static int srt_decode_frame(AVCodecContext
*avctx
,
189 void *data
, int *got_sub_ptr
, AVPacket
*avpkt
)
191 AVSubtitle
*sub
= data
;
193 int ts_start
, ts_end
, x1
= -1, y1
= -1, x2
= -1, y2
= -1;
195 const uint8_t *p
= av_packet_get_side_data(avpkt
, AV_PKT_DATA_SUBTITLE_POSITION
, &size
);
197 if (p
&& size
== 16) {
201 y2
= AV_RL32(p
+ 12);
204 if (avpkt
->size
<= 0)
207 av_bprint_init(&buffer
, 0, AV_BPRINT_SIZE_UNLIMITED
);
210 // Do final divide-by-10 outside rescale to force rounding down.
211 ts_start
= av_rescale_q(avpkt
->pts
,
213 (AVRational
){1,100});
214 ts_end
= av_rescale_q(avpkt
->pts
+ avpkt
->duration
,
216 (AVRational
){1,100});
218 srt_to_ass(avctx
, &buffer
, avpkt
->data
, x1
, y1
, x2
, y2
);
219 ret
= ff_ass_add_rect_bprint(sub
, &buffer
, ts_start
, ts_end
-ts_start
);
220 av_bprint_finalize(&buffer
, NULL
);
224 *got_sub_ptr
= sub
->num_rects
> 0;
228 #if CONFIG_SRT_DECODER
229 /* deprecated decoder */
230 AVCodec ff_srt_decoder
= {
232 .long_name
= NULL_IF_CONFIG_SMALL("SubRip subtitle"),
233 .type
= AVMEDIA_TYPE_SUBTITLE
,
234 .id
= AV_CODEC_ID_SUBRIP
,
235 .init
= ff_ass_subtitle_header_default
,
236 .decode
= srt_decode_frame
,
240 #if CONFIG_SUBRIP_DECODER
241 AVCodec ff_subrip_decoder
= {
243 .long_name
= NULL_IF_CONFIG_SMALL("SubRip subtitle"),
244 .type
= AVMEDIA_TYPE_SUBTITLE
,
245 .id
= AV_CODEC_ID_SUBRIP
,
246 .init
= ff_ass_subtitle_header_default
,
247 .decode
= srt_decode_frame
,