Mercurial > mplayer.hg
annotate sub/subassconvert.c @ 35553:29f2de5e63d6
Don't unconditionally reset playlist to first item after playback.
Only do so if the current item isn't the first one in the list.
This will continue displaying the file's media information.
author | ib |
---|---|
date | Mon, 10 Dec 2012 02:08:43 +0000 |
parents | 482418e9e922 |
children | 389d43c448b3 |
rev | line source |
---|---|
32462 | 1 /* |
2 * Subtitles converter to SSA/ASS in order to allow special formatting | |
3 * | |
4 * This file is part of MPlayer. | |
5 * | |
6 * MPlayer is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; either version 2 of the License, or | |
9 * (at your option) any later version. | |
10 * | |
11 * MPlayer is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 * GNU General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License along | |
17 * with MPlayer; if not, write to the Free Software Foundation, Inc., | |
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
19 */ | |
20 | |
21 #include <string.h> | |
22 #include <stdint.h> | |
23 #include <stdlib.h> | |
24 #include <stdio.h> | |
25 #include <stdarg.h> | |
26 | |
27 #include "mp_msg.h" | |
28 #include "help_mp.h" | |
29 #include "bstr.h" | |
32464
22888a8cb312
Do not use a path for including files in the same directory.
reimar
parents:
32462
diff
changeset
|
30 #include "subassconvert.h" |
32462 | 31 #include "libavutil/common.h" |
32 | |
33 struct line { | |
34 char *buf; | |
35 size_t bufsize; | |
36 size_t len; | |
37 }; | |
38 | |
39 #ifdef __GNUC__ | |
40 static void append_text(struct line *dst, char *fmt, ...) __attribute__ ((format(printf, 2, 3))); | |
41 #endif | |
42 | |
43 static void append_text(struct line *dst, char *fmt, ...) | |
44 { | |
45 va_list va; | |
46 int ret; | |
47 | |
48 va_start(va, fmt); | |
49 ret = vsnprintf(dst->buf + dst->len, dst->bufsize - dst->len, fmt, va); | |
50 if (ret >= 0) { | |
51 dst->len += ret; | |
52 if (dst->len > dst->bufsize) | |
53 dst->len = dst->bufsize; | |
54 } | |
55 va_end(va); | |
56 } | |
57 | |
58 static int indexof(const char *s, int c) | |
59 { | |
60 char *f = strchr(s, c); | |
61 return f ? (f - s) : -1; | |
62 } | |
63 | |
64 | |
65 | |
66 /* | |
67 * SubRip | |
68 * | |
69 * Support basic tags (italic, bold, underline, strike-through) | |
70 * and font tag with size, color and face attributes. | |
71 * | |
72 */ | |
73 | |
74 struct font_tag { | |
75 struct bstr face; | |
76 int size; | |
77 uint32_t color; | |
78 }; | |
79 | |
80 static const struct tag_conv { | |
81 const char *from; | |
82 const char *to; | |
83 } subrip_basic_tags[] = { | |
84 {"<i>", "{\\i1}"}, {"</i>", "{\\i0}"}, | |
85 {"<b>", "{\\b1}"}, {"</b>", "{\\b0}"}, | |
86 {"<u>", "{\\u1}"}, {"</u>", "{\\u0}"}, | |
87 {"<s>", "{\\s1}"}, {"</s>", "{\\s0}"}, | |
88 {"{", "\\{"}, {"}", "\\}"}, | |
34757 | 89 {"\r\n", "\\N"}, {"\n", "\\N"}, {"\r", "\\N"}, |
32462 | 90 }; |
91 | |
92 static const struct { | |
93 const char *s; | |
94 uint32_t v; | |
95 } subrip_web_colors[] = { | |
34912
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
96 /* Named CSS3 colors in BGR format; a subset of those |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
97 at http://www.w3.org/TR/css3-color/#svg-color */ |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
98 {"aqua", 0xffff00}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
99 {"black", 0x000000}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
100 {"blue", 0xff0000}, |
34913 | 101 {"cyan", 0xffff00}, |
34912
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
102 {"fuchsia", 0xff00ff}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
103 {"gray", 0x808080}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
104 {"green", 0x008000}, |
34913 | 105 {"grey", 0x808080}, |
34912
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
106 {"lime", 0x00ff00}, |
34913 | 107 {"magenta", 0xff00ff}, |
34912
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
108 {"maroon", 0x000080}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
109 {"navy", 0x800000}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
110 {"olive", 0x008080}, |
34913 | 111 {"orange", 0x00a5ff}, |
112 {"pink", 0xcbc0ff}, | |
34912
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
113 {"purple", 0x800080}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
114 {"red", 0x0000ff}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
115 {"silver", 0xc0c0c0}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
116 {"teal", 0x808000}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
117 {"white", 0xffffff}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
118 {"yellow", 0x00ffff}, |
32462 | 119 }; |
120 | |
121 #define SUBRIP_MAX_STACKED_FONT_TAGS 16 | |
122 #define SUBRIP_FLAG_COLOR 0x01000000 | |
123 | |
124 /** | |
125 * \brief Convert SubRip lines into ASS markup | |
126 * \param orig original SubRip lines. The content will remain untouched. | |
127 * \param dest ASS markup destination buffer. | |
128 * \param dest_buffer_size maximum size for the destination buffer. | |
129 */ | |
130 void subassconvert_subrip(const char *orig, char *dest, size_t dest_buffer_size) | |
131 { | |
132 /* line is not const to avoid warnings with strtol, etc. | |
133 * orig content won't be changed */ | |
134 char *line = (char *)orig; | |
135 struct line new_line = { | |
136 .buf = dest, | |
137 .bufsize = dest_buffer_size, | |
138 }; | |
139 struct font_tag font_stack[SUBRIP_MAX_STACKED_FONT_TAGS]; | |
140 int sp = 0; | |
141 | |
142 font_stack[0] = (struct font_tag){}; // type with all defaults | |
143 while (*line && new_line.len < new_line.bufsize - 1) { | |
144 char *orig_line = line; | |
145 int i; | |
146 | |
147 for (i = 0; i < FF_ARRAY_ELEMS(subrip_basic_tags); i++) { | |
148 const struct tag_conv *tag = &subrip_basic_tags[i]; | |
149 int from_len = strlen(tag->from); | |
150 if (strncmp(line, tag->from, from_len) == 0) { | |
151 append_text(&new_line, "%s", tag->to); | |
152 line += from_len; | |
153 } | |
154 } | |
155 | |
156 if (strncmp(line, "</font>", 7) == 0) { | |
157 /* Closing font tag */ | |
158 line += 7; | |
159 | |
160 if (sp > 0) { | |
161 struct font_tag *tag = &font_stack[sp]; | |
162 struct font_tag *last_tag = &tag[-1]; | |
163 sp--; | |
164 | |
165 if (tag->size) { | |
166 if (!last_tag->size) | |
167 append_text(&new_line, "{\\fs}"); | |
168 else if (last_tag->size != tag->size) | |
169 append_text(&new_line, "{\\fs%d}", last_tag->size); | |
170 } | |
171 | |
172 if (tag->color & SUBRIP_FLAG_COLOR) { | |
173 if (!(last_tag->color & SUBRIP_FLAG_COLOR)) | |
174 append_text(&new_line, "{\\c}"); | |
175 else if (last_tag->color != tag->color) | |
176 append_text(&new_line, "{\\c&H%06X&}", | |
177 last_tag->color & 0xffffff); | |
178 } | |
179 | |
180 if (tag->face.len) { | |
181 if (!last_tag->face.len) | |
182 append_text(&new_line, "{\\fn}"); | |
183 else if (bstrcmp(last_tag->face, tag->face) != 0) | |
184 append_text(&new_line, "{\\fn%.*s}", | |
185 BSTR_P(last_tag->face)); | |
186 } | |
187 } | |
188 } else if (strncmp(line, "<font ", 6) == 0 | |
189 && sp + 1 < FF_ARRAY_ELEMS(font_stack)) { | |
190 /* Opening font tag */ | |
191 char *potential_font_tag_start = line; | |
192 int len_backup = new_line.len; | |
193 struct font_tag *tag = &font_stack[sp + 1]; | |
194 int has_valid_attr = 0; | |
195 | |
196 *tag = tag[-1]; // keep values from previous tag | |
197 line += 6; | |
198 | |
199 while (*line && *line != '>') { | |
34976
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
200 if (strncmp(line, "size=", 5) == 0) { |
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
201 line += 5; |
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
202 if (*line == '"') line++; |
32462 | 203 tag->size = strtol(line, &line, 10); |
34976
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
204 if (!tag->size) |
32462 | 205 break; |
206 append_text(&new_line, "{\\fs%d}", tag->size); | |
207 has_valid_attr = 1; | |
34976
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
208 } else if (strncmp(line, "color=", 6) == 0) { |
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
209 line += 6; |
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
210 if (*line == '"') line++; |
32462 | 211 if (*line == '#') { |
212 // #RRGGBB format | |
213 line++; | |
214 tag->color = strtol(line, &line, 16) & 0x00ffffff; | |
215 tag->color = ((tag->color & 0xff) << 16) | | |
216 (tag->color & 0xff00) | | |
217 ((tag->color & 0xff0000) >> 16) | | |
218 SUBRIP_FLAG_COLOR; | |
219 } else { | |
220 // Standard web colors | |
34976
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
221 int i; |
32462 | 222 for (i = 0; i < FF_ARRAY_ELEMS(subrip_web_colors); i++) { |
223 const char *color = subrip_web_colors[i].s; | |
34976
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
224 const int len = strlen(color); |
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
225 if (strncasecmp(line, color, len) == 0) { |
32462 | 226 tag->color = SUBRIP_FLAG_COLOR | subrip_web_colors[i].v; |
34976
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
227 line += len; |
32462 | 228 break; |
229 } | |
230 } | |
231 | |
232 if (i == FF_ARRAY_ELEMS(subrip_web_colors)) { | |
233 /* We didn't find any matching color */ | |
34976
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
234 line += strcspn(line, "\" >"); |
32462 | 235 mp_msg(MSGT_SUBREADER, MSGL_WARN, |
236 MSGTR_SUBTITLES_SubRip_UnknownFontColor, orig); | |
237 append_text(&new_line, "{\\c}"); | |
238 continue; | |
239 } | |
240 } | |
241 append_text(&new_line, "{\\c&H%06X&}", tag->color & 0xffffff); | |
242 has_valid_attr = 1; | |
243 } else if (strncmp(line, "face=\"", 6) == 0) { | |
244 /* Font face attribute */ | |
245 int len; | |
246 line += 6; | |
247 len = indexof(line, '"'); | |
248 if (len <= 0) | |
249 break; | |
250 tag->face.start = line; | |
251 tag->face.len = len; | |
252 line += len; | |
253 append_text(&new_line, "{\\fn%.*s}", BSTR_P(tag->face)); | |
254 has_valid_attr = 1; | |
255 } | |
34976
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
256 if (*line != '>') |
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
257 line++; |
32462 | 258 } |
259 | |
260 if (!has_valid_attr || *line != '>') { /* Not valid font tag */ | |
261 line = potential_font_tag_start; | |
262 new_line.len = len_backup; | |
263 } else { | |
264 sp++; | |
265 line++; | |
266 } | |
267 } | |
268 | |
269 /* Tag conversion code didn't match */ | |
270 if (line == orig_line) | |
271 new_line.buf[new_line.len++] = *line++; | |
272 } | |
273 new_line.buf[new_line.len] = 0; | |
274 } | |
275 | |
276 | |
277 /* | |
278 * MicroDVD | |
279 * | |
280 * Based on the specifications found here: | |
281 * https://trac.videolan.org/vlc/ticket/1825#comment:6 | |
282 */ | |
283 | |
284 struct microdvd_tag { | |
285 char key; | |
286 int persistent; | |
287 uint32_t data1; | |
288 uint32_t data2; | |
289 struct bstr data_string; | |
290 }; | |
291 | |
292 #define MICRODVD_PERSISTENT_OFF 0 | |
293 #define MICRODVD_PERSISTENT_ON 1 | |
294 #define MICRODVD_PERSISTENT_OPENED 2 | |
295 | |
296 // Color, Font, Size, cHarset, stYle, Position, cOordinate | |
297 #define MICRODVD_TAGS "cfshyYpo" | |
298 | |
299 static void microdvd_set_tag(struct microdvd_tag *tags, struct microdvd_tag tag) | |
300 { | |
301 int tag_index = indexof(MICRODVD_TAGS, tag.key); | |
302 | |
303 if (tag_index < 0) | |
304 return; | |
305 memcpy(&tags[tag_index], &tag, sizeof(tag)); | |
306 } | |
307 | |
308 // italic, bold, underline, strike-through | |
309 #define MICRODVD_STYLES "ibus" | |
310 | |
311 static char *microdvd_load_tags(struct microdvd_tag *tags, char *s) | |
312 { | |
313 while (*s == '{') { | |
314 char *start = s; | |
315 char tag_char = *(s + 1); | |
34758 | 316 struct microdvd_tag tag = {0}; |
32462 | 317 |
318 if (!tag_char || *(s + 2) != ':') | |
319 break; | |
320 s += 3; | |
321 | |
322 switch (tag_char) { | |
323 | |
324 /* Style */ | |
325 case 'Y': | |
326 tag.persistent = MICRODVD_PERSISTENT_ON; | |
327 case 'y': | |
328 while (*s && *s != '}') { | |
329 int style_index = indexof(MICRODVD_STYLES, *s); | |
330 | |
331 if (style_index >= 0) | |
332 tag.data1 |= (1 << style_index); | |
333 s++; | |
334 } | |
335 if (*s != '}') | |
336 break; | |
337 /* We must distinguish persistent and non-persistent styles | |
338 * to handle this kind of style tags: {y:ib}{Y:us} */ | |
339 tag.key = tag_char; | |
340 break; | |
341 | |
342 /* Color */ | |
343 case 'C': | |
344 tag.persistent = MICRODVD_PERSISTENT_ON; | |
345 case 'c': | |
346 tag.data1 = strtol(s, &s, 16) & 0x00ffffff; | |
347 if (*s != '}') | |
348 break; | |
349 tag.key = 'c'; | |
350 break; | |
351 | |
352 /* Font name */ | |
353 case 'F': | |
354 tag.persistent = MICRODVD_PERSISTENT_ON; | |
355 case 'f': | |
356 { | |
357 int len = indexof(s, '}'); | |
358 if (len < 0) | |
359 break; | |
360 tag.data_string.start = s; | |
361 tag.data_string.len = len; | |
362 s += len; | |
363 tag.key = 'f'; | |
364 break; | |
365 } | |
366 | |
367 /* Font size */ | |
368 case 'S': | |
369 tag.persistent = MICRODVD_PERSISTENT_ON; | |
370 case 's': | |
371 tag.data1 = strtol(s, &s, 10); | |
372 if (*s != '}') | |
373 break; | |
374 tag.key = 's'; | |
375 break; | |
376 | |
377 /* Charset */ | |
378 case 'H': | |
379 { | |
380 //TODO: not yet handled, just parsed. | |
381 int len = indexof(s, '}'); | |
382 if (len < 0) | |
383 break; | |
384 tag.data_string.start = s; | |
385 tag.data_string.len = len; | |
386 s += len; | |
387 tag.key = 'h'; | |
388 break; | |
389 } | |
390 | |
391 /* Position */ | |
392 case 'P': | |
393 tag.persistent = MICRODVD_PERSISTENT_ON; | |
394 tag.data1 = (*s++ == '1'); | |
395 if (*s != '}') | |
396 break; | |
397 tag.key = 'p'; | |
398 break; | |
399 | |
400 /* Coordinates */ | |
401 case 'o': | |
402 tag.persistent = MICRODVD_PERSISTENT_ON; | |
403 tag.data1 = strtol(s, &s, 10); | |
404 if (*s != ',') | |
405 break; | |
406 s++; | |
407 tag.data2 = strtol(s, &s, 10); | |
408 if (*s != '}') | |
409 break; | |
410 tag.key = 'o'; | |
411 break; | |
412 | |
413 default: /* Unknown tag, we consider it's text */ | |
414 break; | |
415 } | |
416 | |
417 if (tag.key == 0) | |
418 return start; | |
419 | |
420 microdvd_set_tag(tags, tag); | |
421 s++; | |
422 } | |
423 return s; | |
424 } | |
425 | |
426 static void microdvd_open_tags(struct line *new_line, struct microdvd_tag *tags) | |
427 { | |
428 int i, sidx; | |
429 for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) { | |
430 if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED) | |
431 continue; | |
432 switch (tags[i].key) { | |
433 case 'Y': | |
434 case 'y': | |
435 for (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++) | |
436 if (tags[i].data1 & (1 << sidx)) | |
437 append_text(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]); | |
438 break; | |
439 | |
440 case 'c': | |
441 append_text(new_line, "{\\c&H%06X&}", tags[i].data1); | |
442 break; | |
443 | |
444 case 'f': | |
445 append_text(new_line, "{\\fn%.*s}", BSTR_P(tags[i].data_string)); | |
446 break; | |
447 | |
448 case 's': | |
449 append_text(new_line, "{\\fs%d}", tags[i].data1); | |
450 break; | |
451 | |
452 case 'p': | |
453 if (tags[i].data1 == 0) | |
454 append_text(new_line, "{\\an8}"); | |
455 break; | |
456 | |
457 case 'o': | |
458 append_text(new_line, "{\\pos(%d,%d)}", | |
459 tags[i].data1, tags[i].data2); | |
460 break; | |
461 } | |
462 if (tags[i].persistent == MICRODVD_PERSISTENT_ON) | |
463 tags[i].persistent = MICRODVD_PERSISTENT_OPENED; | |
464 } | |
465 } | |
466 | |
467 static void microdvd_close_no_persistent_tags(struct line *new_line, | |
468 struct microdvd_tag *tags) | |
469 { | |
470 int i, sidx; | |
471 | |
472 for (i = sizeof(MICRODVD_TAGS) - 2; i; i--) { | |
473 if (tags[i].persistent != MICRODVD_PERSISTENT_OFF) | |
474 continue; | |
475 switch (tags[i].key) { | |
476 | |
477 case 'y': | |
478 for (sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--) | |
479 if (tags[i].data1 & (1 << sidx)) | |
480 append_text(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]); | |
481 break; | |
482 | |
483 case 'c': | |
484 append_text(new_line, "{\\c}"); | |
485 break; | |
486 | |
487 case 'f': | |
488 append_text(new_line, "{\\fn}"); | |
489 break; | |
490 | |
491 case 's': | |
492 append_text(new_line, "{\\fs}"); | |
493 break; | |
494 } | |
495 tags[i].key = 0; | |
496 } | |
497 } | |
498 | |
499 /** | |
500 * \brief Convert MicroDVD lines into ASS markup | |
501 * \param orig original MicroDVD line. The content will remain untouched. | |
502 * \param dest ASS markup destination buffer. | |
503 * \param dest_buffer_size maximum size for the destination buffer. | |
504 */ | |
505 void subassconvert_microdvd(const char *orig, char *dest, size_t dest_buffer_size) | |
506 { | |
507 /* line is not const to avoid warnings with strtol, etc. | |
508 * orig content won't be changed */ | |
509 char *line = (char *)orig; | |
510 struct line new_line = { | |
511 .buf = dest, | |
512 .bufsize = dest_buffer_size, | |
513 }; | |
34758 | 514 struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}}; |
32462 | 515 |
516 while (*line) { | |
517 line = microdvd_load_tags(tags, line); | |
518 microdvd_open_tags(&new_line, tags); | |
519 | |
520 while (*line && *line != '|') | |
521 new_line.buf[new_line.len++] = *line++; | |
522 | |
523 if (*line == '|') { | |
524 microdvd_close_no_persistent_tags(&new_line, tags); | |
525 append_text(&new_line, "\\N"); | |
526 line++; | |
527 } | |
528 } | |
529 new_line.buf[new_line.len] = 0; | |
530 } |