2 * SubRip subtitle decoder
3 * Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.org>
5 * This file is part of Libav.
7 * Libav 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 * Libav 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 Libav; 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/parseutils.h"
28 static int html_color_parse(AVCodecContext
*avctx
, const char *str
)
31 if (av_parse_color(rgba
, str
, strcspn(str
, "\" >"), avctx
) < 0)
33 return rgba
[0] | rgba
[1] << 8 | rgba
[2] << 16;
44 typedef struct SrtStack
{
46 char param
[PARAM_NUMBER
][128];
49 static const char *srt_to_ass(AVCodecContext
*avctx
, char *out
, char *out_end
,
50 const char *in
, int x1
, int y1
, int x2
, int y2
)
52 char c
, *param
, buffer
[128], tmp
[128];
53 int len
, tag_close
, sptr
= 1, line_start
= 1, an
= 0, end
= 0;
57 strcpy(stack
[0].param
[PARAM_SIZE
], "{\\fs}");
58 strcpy(stack
[0].param
[PARAM_COLOR
], "{\\c}");
59 strcpy(stack
[0].param
[PARAM_FACE
], "{\\fn}");
61 if (x1
>= 0 && y1
>= 0) {
62 if (x2
>= 0 && y2
>= 0 && (x2
!= x1
|| y2
!= y1
))
63 out
+= snprintf(out
, out_end
-out
,
64 "{\\an1}{\\move(%d,%d,%d,%d)}", x1
, y1
, x2
, y2
);
66 out
+= snprintf(out
, out_end
-out
, "{\\an1}{\\pos(%d,%d)}", x1
, y1
);
69 for (; out
< out_end
&& !end
&& *in
; in
++) {
78 while (out
[-1] == ' ')
80 out
+= snprintf(out
, out_end
-out
, "\\N");
87 case '{': /* skip all {\xxx} substrings except for {\an%d}
88 and all microdvd like styles such as {Y:xxx} */
89 an
+= sscanf(in
, "{\\an%*1u}%c", &c
) == 1;
90 if ((an
!= 1 && sscanf(in
, "{\\%*[^}]}%n%c", &len
, &c
) > 0) ||
91 sscanf(in
, "{%*1[CcFfoPSsYy]:%*[^}]}%n%c", &len
, &c
) > 0) {
97 tag_close
= in
[1] == '/';
98 if (sscanf(in
+tag_close
+1, "%127[^>]>%n%c", buffer
, &len
,&c
) >= 2) {
99 if ((param
= strchr(buffer
, ' ')))
101 if ((!tag_close
&& sptr
< FF_ARRAY_ELEMS(stack
)) ||
102 ( tag_close
&& sptr
> 0 && !strcmp(stack
[sptr
-1].tag
, buffer
))) {
103 int i
, j
, unknown
= 0;
104 in
+= len
+ tag_close
;
106 memset(stack
+sptr
, 0, sizeof(*stack
));
107 if (!strcmp(buffer
, "font")) {
109 for (i
=PARAM_NUMBER
-1; i
>=0; i
--)
110 if (stack
[sptr
-1].param
[i
][0])
111 for (j
=sptr
-2; j
>=0; j
--)
112 if (stack
[j
].param
[i
][0]) {
113 out
+= snprintf(out
, out_end
-out
,
114 "%s", stack
[j
].param
[i
]);
119 if (!strncmp(param
, "size=", 5)) {
121 param
+= 5 + (param
[5] == '"');
122 if (sscanf(param
, "%u", &font_size
) == 1) {
123 snprintf(stack
[sptr
].param
[PARAM_SIZE
],
124 sizeof(stack
[0].param
[PARAM_SIZE
]),
125 "{\\fs%u}", font_size
);
127 } else if (!strncmp(param
, "color=", 6)) {
128 param
+= 6 + (param
[6] == '"');
129 snprintf(stack
[sptr
].param
[PARAM_COLOR
],
130 sizeof(stack
[0].param
[PARAM_COLOR
]),
132 html_color_parse(avctx
, param
));
133 } else if (!strncmp(param
, "face=", 5)) {
134 param
+= 5 + (param
[5] == '"');
136 param
[-1] == '"' ? "\"" :" ");
137 av_strlcpy(tmp
, param
,
138 FFMIN(sizeof(tmp
), len
+1));
140 snprintf(stack
[sptr
].param
[PARAM_FACE
],
141 sizeof(stack
[0].param
[PARAM_FACE
]),
144 if ((param
= strchr(param
, ' ')))
147 for (i
=0; i
<PARAM_NUMBER
; i
++)
148 if (stack
[sptr
].param
[i
][0])
149 out
+= snprintf(out
, out_end
-out
,
150 "%s", stack
[sptr
].param
[i
]);
152 } else if (!buffer
[1] && strspn(buffer
, "bisu") == 1) {
153 out
+= snprintf(out
, out_end
-out
,
154 "{\\%c%d}", buffer
[0], !tag_close
);
157 snprintf(tmp
, sizeof(tmp
), "</%s>", buffer
);
161 } else if (unknown
&& !strstr(in
, tmp
)) {
162 in
-= len
+ tag_close
;
165 av_strlcpy(stack
[sptr
++].tag
, buffer
,
166 sizeof(stack
[0].tag
));
174 if (*in
!= ' ' && *in
!= '\r' && *in
!= '\n')
178 out
= FFMIN(out
, out_end
-3);
179 while (!strncmp(out
-2, "\\N", 2))
181 while (out
[-1] == ' ')
183 out
+= snprintf(out
, out_end
-out
, "\r\n");
187 static const char *read_ts(const char *buf
, int *ts_start
, int *ts_end
,
188 int *x1
, int *y1
, int *x2
, int *y2
)
190 int i
, hs
, ms
, ss
, he
, me
, se
;
192 for (i
=0; i
<2; i
++) {
193 /* try to read timestamps in either the first or second line */
194 int c
= sscanf(buf
, "%d:%2d:%2d%*1[,.]%3d --> %d:%2d:%2d%*1[,.]%3d"
195 "%*[ ]X1:%d X2:%d Y1:%d Y2:%d",
196 &hs
, &ms
, &ss
, ts_start
, &he
, &me
, &se
, ts_end
,
198 buf
+= strcspn(buf
, "\n") + 1;
200 *ts_start
= 100*(ss
+ 60*(ms
+ 60*hs
)) + *ts_start
/10;
201 *ts_end
= 100*(se
+ 60*(me
+ 60*he
)) + *ts_end
/10;
208 static int srt_decode_frame(AVCodecContext
*avctx
,
209 void *data
, int *got_sub_ptr
, AVPacket
*avpkt
)
211 AVSubtitle
*sub
= data
;
212 int ts_start
, ts_end
, x1
= -1, y1
= -1, x2
= -1, y2
= -1;
214 const char *ptr
= avpkt
->data
;
215 const char *end
= avpkt
->data
+ avpkt
->size
;
217 if (avpkt
->size
<= 0)
222 while (ptr
< end
&& *ptr
) {
223 ptr
= read_ts(ptr
, &ts_start
, &ts_end
, &x1
, &y1
, &x2
, &y2
);
226 ptr
= srt_to_ass(avctx
, buffer
, buffer
+sizeof(buffer
), ptr
,
228 ff_ass_add_rect(sub
, buffer
, ts_start
, ts_end
, 0);
231 *got_sub_ptr
= sub
->num_rects
> 0;
235 AVCodec ff_srt_decoder
= {
237 .long_name
= NULL_IF_CONFIG_SMALL("SubRip subtitle"),
238 .type
= AVMEDIA_TYPE_SUBTITLE
,
239 .id
= AV_CODEC_ID_SRT
,
240 .init
= ff_ass_subtitle_header_default
,
241 .decode
= srt_decode_frame
,