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 const char *srt_to_ass(AVCodecContext
*avctx
, char *out
, char *out_end
,
51 const char *in
, int x1
, int y1
, int x2
, int y2
)
53 char *param
, buffer
[128], tmp
[128];
54 int len
, tag_close
, sptr
= 1, line_start
= 1, an
= 0, end
= 0;
58 strcpy(stack
[0].param
[PARAM_SIZE
], "{\\fs}");
59 strcpy(stack
[0].param
[PARAM_COLOR
], "{\\c}");
60 strcpy(stack
[0].param
[PARAM_FACE
], "{\\fn}");
62 if (x1
>= 0 && y1
>= 0) {
63 if (x2
>= 0 && y2
>= 0 && (x2
!= x1
|| y2
!= y1
))
64 snprintf(out
, out_end
-out
,
65 "{\\an1}{\\move(%d,%d,%d,%d)}", x1
, y1
, x2
, y2
);
67 snprintf(out
, out_end
-out
, "{\\an1}{\\pos(%d,%d)}", x1
, y1
);
71 for (; out
< out_end
&& !end
&& *in
; in
++) {
80 while (out
[-1] == ' ')
82 snprintf(out
, out_end
-out
, "\\N");
83 if(out
<out_end
) out
+= strlen(out
);
90 case '{': /* skip all {\xxx} substrings except for {\an%d}
91 and all microdvd like styles such as {Y:xxx} */
93 an
+= sscanf(in
, "{\\an%*1u}%n", &len
) >= 0 && len
> 0;
94 if ((an
!= 1 && (len
= 0, sscanf(in
, "{\\%*[^}]}%n", &len
) >= 0 && len
> 0)) ||
95 (len
= 0, sscanf(in
, "{%*1[CcFfoPSsYy]:%*[^}]}%n", &len
) >= 0 && len
> 0)) {
101 tag_close
= in
[1] == '/';
103 if (sscanf(in
+tag_close
+1, "%127[^>]>%n", buffer
, &len
) >= 1 && len
> 0) {
104 if ((param
= strchr(buffer
, ' ')))
106 if ((!tag_close
&& sptr
< FF_ARRAY_ELEMS(stack
)) ||
107 ( tag_close
&& sptr
> 0 && !strcmp(stack
[sptr
-1].tag
, buffer
))) {
108 int i
, j
, unknown
= 0;
109 in
+= len
+ tag_close
;
111 memset(stack
+sptr
, 0, sizeof(*stack
));
112 if (!strcmp(buffer
, "font")) {
114 for (i
=PARAM_NUMBER
-1; i
>=0; i
--)
115 if (stack
[sptr
-1].param
[i
][0])
116 for (j
=sptr
-2; j
>=0; j
--)
117 if (stack
[j
].param
[i
][0]) {
118 snprintf(out
, out_end
-out
,
119 "%s", stack
[j
].param
[i
]);
120 if(out
<out_end
) out
+= strlen(out
);
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 snprintf(out
, out_end
-out
,
156 "%s", stack
[sptr
].param
[i
]);
157 if(out
<out_end
) out
+= strlen(out
);
160 } else if (!buffer
[1] && strspn(buffer
, "bisu") == 1) {
161 snprintf(out
, out_end
-out
,
162 "{\\%c%d}", buffer
[0], !tag_close
);
163 if(out
<out_end
) out
+= strlen(out
);
166 snprintf(tmp
, sizeof(tmp
), "</%s>", buffer
);
170 } else if (unknown
&& !strstr(in
, tmp
)) {
171 in
-= len
+ tag_close
;
174 av_strlcpy(stack
[sptr
++].tag
, buffer
,
175 sizeof(stack
[0].tag
));
183 if (*in
!= ' ' && *in
!= '\r' && *in
!= '\n')
187 out
= FFMIN(out
, out_end
-3);
188 while (!strncmp(out
-2, "\\N", 2))
190 while (out
[-1] == ' ')
192 snprintf(out
, out_end
-out
, "\r\n");
196 static const char *read_ts(const char *buf
, int *ts_start
, int *ts_end
,
197 int *x1
, int *y1
, int *x2
, int *y2
)
199 int i
, hs
, ms
, ss
, he
, me
, se
;
201 for (i
=0; i
<2; i
++) {
202 /* try to read timestamps in either the first or second line */
203 int c
= sscanf(buf
, "%d:%2d:%2d%*1[,.]%3d --> %d:%2d:%2d%*1[,.]%3d"
204 "%*[ ]X1:%u X2:%u Y1:%u Y2:%u",
205 &hs
, &ms
, &ss
, ts_start
, &he
, &me
, &se
, ts_end
,
207 buf
+= strcspn(buf
, "\n");
210 *ts_start
= 100*(ss
+ 60*(ms
+ 60*hs
)) + *ts_start
/10;
211 *ts_end
= 100*(se
+ 60*(me
+ 60*he
)) + *ts_end
/10;
218 static int srt_decode_frame(AVCodecContext
*avctx
,
219 void *data
, int *got_sub_ptr
, AVPacket
*avpkt
)
221 AVSubtitle
*sub
= data
;
222 int ts_start
, ts_end
, x1
= -1, y1
= -1, x2
= -1, y2
= -1;
224 const char *ptr
= avpkt
->data
;
225 const char *end
= avpkt
->data
+ avpkt
->size
;
227 const uint8_t *p
= av_packet_get_side_data(avpkt
, AV_PKT_DATA_SUBTITLE_POSITION
, &size
);
229 if (p
&& size
== 16) {
233 y2
= AV_RL32(p
+ 12);
236 if (avpkt
->size
<= 0)
239 while (ptr
< end
&& *ptr
) {
240 if (avctx
->codec
->id
== AV_CODEC_ID_SRT
) {
241 ptr
= read_ts(ptr
, &ts_start
, &ts_end
, &x1
, &y1
, &x2
, &y2
);
245 // Do final divide-by-10 outside rescale to force rounding down.
246 ts_start
= av_rescale_q(avpkt
->pts
,
248 (AVRational
){1,100});
249 ts_end
= av_rescale_q(avpkt
->pts
+ avpkt
->duration
,
251 (AVRational
){1,100});
253 ptr
= srt_to_ass(avctx
, buffer
, buffer
+sizeof(buffer
), ptr
,
255 ff_ass_add_rect(sub
, buffer
, ts_start
, ts_end
-ts_start
, 0);
258 *got_sub_ptr
= sub
->num_rects
> 0;
262 #if CONFIG_SRT_DECODER
263 /* deprecated decoder */
264 AVCodec ff_srt_decoder
= {
266 .long_name
= NULL_IF_CONFIG_SMALL("SubRip subtitle with embedded timing"),
267 .type
= AVMEDIA_TYPE_SUBTITLE
,
268 .id
= AV_CODEC_ID_SRT
,
269 .init
= ff_ass_subtitle_header_default
,
270 .decode
= srt_decode_frame
,
274 #if CONFIG_SUBRIP_DECODER
275 AVCodec ff_subrip_decoder
= {
277 .long_name
= NULL_IF_CONFIG_SMALL("SubRip subtitle"),
278 .type
= AVMEDIA_TYPE_SUBTITLE
,
279 .id
= AV_CODEC_ID_SUBRIP
,
280 .init
= ff_ass_subtitle_header_default
,
281 .decode
= srt_decode_frame
,