Mercurial > mplayer.hg
annotate libass/ass.c @ 35044:99ac85950277
Support decoding teletext in the format that WTV uses.
author | reimar |
---|---|
date | Sat, 01 Sep 2012 14:53:05 +0000 |
parents | 6e7f60f6f9d4 |
children | 49fc594fda43 |
rev | line source |
---|---|
20008
fa122b7c71c6
Add copyright notice and vim/emacs comments to libass and vf_ass.c.
eugeni
parents:
19905
diff
changeset
|
1 /* |
26723 | 2 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> |
3 * | |
26738
588ce97b44f2
Speak of libass instead of MPlayer in the libass license headers.
diego
parents:
26723
diff
changeset
|
4 * This file is part of libass. |
26723 | 5 * |
34011 | 6 * Permission to use, copy, modify, and distribute this software for any |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
26723 | 9 * |
34011 | 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
26723 | 17 */ |
20008
fa122b7c71c6
Add copyright notice and vim/emacs comments to libass and vf_ass.c.
eugeni
parents:
19905
diff
changeset
|
18 |
18937 | 19 #include "config.h" |
20 | |
21 #include <stdio.h> | |
22 #include <stdlib.h> | |
23 #include <string.h> | |
31875 | 24 #include <strings.h> |
18937 | 25 #include <assert.h> |
26 #include <errno.h> | |
27 #include <sys/types.h> | |
28 #include <sys/stat.h> | |
29 #include <unistd.h> | |
19374 | 30 #include <inttypes.h> |
34295 | 31 #include <ctype.h> |
18937 | 32 |
27393 | 33 #ifdef CONFIG_ICONV |
18937 | 34 #include <iconv.h> |
35 #endif | |
36 | |
37 #include "ass.h" | |
38 #include "ass_utils.h" | |
20477 | 39 #include "ass_library.h" |
19652
2c016957360a
Add -ass-styles option. It allows to load styles from a file and use them
eugeni
parents:
19639
diff
changeset
|
40 |
31853 | 41 #define ass_atof(STR) (ass_strtod((STR),NULL)) |
42 | |
30200 | 43 typedef enum { |
44 PST_UNKNOWN = 0, | |
45 PST_INFO, | |
46 PST_STYLES, | |
47 PST_EVENTS, | |
48 PST_FONTS | |
49 } ParserState; | |
50 | |
51 struct parser_priv { | |
52 ParserState state; | |
53 char *fontname; | |
54 char *fontdata; | |
55 int fontdata_size; | |
56 int fontdata_used; | |
19492 | 57 }; |
58 | |
18937 | 59 #define ASS_STYLES_ALLOC 20 |
60 #define ASS_EVENTS_ALLOC 200 | |
61 | |
30200 | 62 void ass_free_track(ASS_Track *track) |
63 { | |
64 int i; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
65 |
30200 | 66 if (track->parser_priv) { |
31875 | 67 free(track->parser_priv->fontname); |
68 free(track->parser_priv->fontdata); | |
30200 | 69 free(track->parser_priv); |
70 } | |
31875 | 71 free(track->style_format); |
72 free(track->event_format); | |
34295 | 73 free(track->Language); |
30200 | 74 if (track->styles) { |
75 for (i = 0; i < track->n_styles; ++i) | |
76 ass_free_style(track, i); | |
77 } | |
31875 | 78 free(track->styles); |
30200 | 79 if (track->events) { |
80 for (i = 0; i < track->n_events; ++i) | |
81 ass_free_event(track, i); | |
82 } | |
31875 | 83 free(track->events); |
30200 | 84 free(track->name); |
85 free(track); | |
18937 | 86 } |
87 | |
88 /// \brief Allocate a new style struct | |
89 /// \param track track | |
90 /// \return style id | |
30200 | 91 int ass_alloc_style(ASS_Track *track) |
92 { | |
93 int sid; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
94 |
30200 | 95 assert(track->n_styles <= track->max_styles); |
18937 | 96 |
30200 | 97 if (track->n_styles == track->max_styles) { |
98 track->max_styles += ASS_STYLES_ALLOC; | |
99 track->styles = | |
100 (ASS_Style *) realloc(track->styles, | |
101 sizeof(ASS_Style) * | |
102 track->max_styles); | |
103 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
104 |
30200 | 105 sid = track->n_styles++; |
106 memset(track->styles + sid, 0, sizeof(ASS_Style)); | |
107 return sid; | |
18937 | 108 } |
109 | |
110 /// \brief Allocate a new event struct | |
111 /// \param track track | |
112 /// \return event id | |
30200 | 113 int ass_alloc_event(ASS_Track *track) |
114 { | |
115 int eid; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
116 |
30200 | 117 assert(track->n_events <= track->max_events); |
18937 | 118 |
30200 | 119 if (track->n_events == track->max_events) { |
120 track->max_events += ASS_EVENTS_ALLOC; | |
121 track->events = | |
122 (ASS_Event *) realloc(track->events, | |
123 sizeof(ASS_Event) * | |
124 track->max_events); | |
125 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
126 |
30200 | 127 eid = track->n_events++; |
128 memset(track->events + eid, 0, sizeof(ASS_Event)); | |
129 return eid; | |
18937 | 130 } |
131 | |
30200 | 132 void ass_free_event(ASS_Track *track, int eid) |
133 { | |
134 ASS_Event *event = track->events + eid; | |
31875 | 135 |
136 free(event->Name); | |
137 free(event->Effect); | |
138 free(event->Text); | |
139 free(event->render_priv); | |
19474
07209f48e527
Add public functions for removal of styles and events.
eugeni
parents:
19405
diff
changeset
|
140 } |
07209f48e527
Add public functions for removal of styles and events.
eugeni
parents:
19405
diff
changeset
|
141 |
30200 | 142 void ass_free_style(ASS_Track *track, int sid) |
143 { | |
144 ASS_Style *style = track->styles + sid; | |
31875 | 145 |
146 free(style->Name); | |
147 free(style->FontName); | |
18937 | 148 } |
149 | |
150 // ============================================================================================== | |
151 | |
30200 | 152 static void skip_spaces(char **str) |
153 { | |
154 char *p = *str; | |
155 while ((*p == ' ') || (*p == '\t')) | |
156 ++p; | |
157 *str = p; | |
18937 | 158 } |
159 | |
30200 | 160 static void rskip_spaces(char **str, char *limit) |
161 { | |
162 char *p = *str; | |
163 while ((p >= limit) && ((*p == ' ') || (*p == '\t'))) | |
164 --p; | |
165 *str = p; | |
18937 | 166 } |
167 | |
168 /** | |
34011 | 169 * \brief Set up default style |
170 * \param style style to edit to defaults | |
171 * The parameters are mostly taken directly from VSFilter source for | |
172 * best compatibility. | |
173 */ | |
174 static void set_default_style(ASS_Style *style) | |
175 { | |
176 style->Name = strdup("Default"); | |
177 style->FontName = strdup("Arial"); | |
178 style->FontSize = 18; | |
179 style->PrimaryColour = 0xffffff00; | |
180 style->SecondaryColour = 0x00ffff00; | |
181 style->OutlineColour = 0x00000000; | |
182 style->BackColour = 0x00000080; | |
183 style->Bold = 200; | |
184 style->ScaleX = 1.0; | |
185 style->ScaleY = 1.0; | |
186 style->Spacing = 0; | |
187 style->BorderStyle = 1; | |
188 style->Outline = 2; | |
189 style->Shadow = 3; | |
190 style->Alignment = 2; | |
191 style->MarginL = style->MarginR = style->MarginV = 20; | |
192 } | |
193 | |
194 /** | |
18937 | 195 * \brief find style by name |
196 * \param track track | |
197 * \param name style name | |
198 * \return index in track->styles | |
199 * Returnes 0 if no styles found => expects at least 1 style. | |
200 * Parsing code always adds "Default" style in the end. | |
201 */ | |
30200 | 202 static int lookup_style(ASS_Track *track, char *name) |
203 { | |
204 int i; | |
205 if (*name == '*') | |
206 ++name; // FIXME: what does '*' really mean ? | |
207 for (i = track->n_styles - 1; i >= 0; --i) { | |
208 if (strcmp(track->styles[i].Name, name) == 0) | |
209 return i; | |
210 } | |
211 i = track->default_style; | |
212 ass_msg(track->library, MSGL_WARN, | |
213 "[%p]: Warning: no style named '%s' found, using '%s'", | |
214 track, name, track->styles[i].Name); | |
215 return i; // use the first style | |
18937 | 216 } |
217 | |
30200 | 218 static uint32_t string2color(ASS_Library *library, char *p) |
219 { | |
220 uint32_t tmp; | |
221 (void) strtocolor(library, &p, &tmp, 0); | |
222 return tmp; | |
18937 | 223 } |
224 | |
30200 | 225 static long long string2timecode(ASS_Library *library, char *p) |
226 { | |
227 unsigned h, m, s, ms; | |
228 long long tm; | |
229 int res = sscanf(p, "%1d:%2d:%2d.%2d", &h, &m, &s, &ms); | |
230 if (res < 4) { | |
231 ass_msg(library, MSGL_WARN, "Bad timestamp"); | |
232 return 0; | |
233 } | |
234 tm = ((h * 60 + m) * 60 + s) * 1000 + ms * 10; | |
235 return tm; | |
18937 | 236 } |
237 | |
238 /** | |
239 * \brief converts numpad-style align to align. | |
240 */ | |
30200 | 241 static int numpad2align(int val) |
242 { | |
243 int res, v; | |
244 v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment | |
245 if (v != 0) | |
246 v = 3 - v; | |
247 res = ((val - 1) % 3) + 1; // horizontal alignment | |
248 res += v * 4; | |
249 return res; | |
18937 | 250 } |
251 | |
252 #define NEXT(str,token) \ | |
253 token = next_token(&str); \ | |
254 if (!token) break; | |
255 | |
256 #define ANYVAL(name,func) \ | |
257 } else if (strcasecmp(tname, #name) == 0) { \ | |
258 target->name = func(token); \ | |
30200 | 259 ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token); |
19495 | 260 |
261 #define STRVAL(name) \ | |
262 } else if (strcasecmp(tname, #name) == 0) { \ | |
263 if (target->name != NULL) free(target->name); \ | |
264 target->name = strdup(token); \ | |
30200 | 265 ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token); |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
266 |
30200 | 267 #define COLORVAL(name) \ |
268 } else if (strcasecmp(tname, #name) == 0) { \ | |
269 target->name = string2color(track->library, token); \ | |
270 ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token); | |
271 | |
18937 | 272 #define INTVAL(name) ANYVAL(name,atoi) |
31853 | 273 #define FPVAL(name) ANYVAL(name,ass_atof) |
30200 | 274 #define TIMEVAL(name) \ |
275 } else if (strcasecmp(tname, #name) == 0) { \ | |
276 target->name = string2timecode(track->library, token); \ | |
277 ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token); | |
278 | |
18937 | 279 #define STYLEVAL(name) \ |
280 } else if (strcasecmp(tname, #name) == 0) { \ | |
281 target->name = lookup_style(track, token); \ | |
30200 | 282 ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token); |
18937 | 283 |
284 #define ALIAS(alias,name) \ | |
285 if (strcasecmp(tname, #alias) == 0) {tname = #name;} | |
286 | |
30200 | 287 static char *next_token(char **str) |
288 { | |
289 char *p = *str; | |
290 char *start; | |
291 skip_spaces(&p); | |
292 if (*p == '\0') { | |
293 *str = p; | |
294 return 0; | |
295 } | |
296 start = p; // start of the token | |
297 for (; (*p != '\0') && (*p != ','); ++p) { | |
298 } | |
299 if (*p == '\0') { | |
300 *str = p; // eos found, str will point to '\0' at exit | |
301 } else { | |
302 *p = '\0'; | |
303 *str = p + 1; // ',' found, str will point to the next char (beginning of the next token) | |
304 } | |
305 --p; // end of current token | |
306 rskip_spaces(&p, start); | |
307 if (p < start) | |
308 p = start; // empty token | |
309 else | |
310 ++p; // the first space character, or '\0' | |
311 *p = '\0'; | |
312 return start; | |
18937 | 313 } |
30200 | 314 |
18937 | 315 /** |
316 * \brief Parse the tail of Dialogue line | |
317 * \param track track | |
318 * \param event parsed data goes here | |
319 * \param str string to parse, zero-terminated | |
320 * \param n_ignored number of format options to skip at the beginning | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
321 */ |
30200 | 322 static int process_event_tail(ASS_Track *track, ASS_Event *event, |
323 char *str, int n_ignored) | |
18937 | 324 { |
30200 | 325 char *token; |
326 char *tname; | |
327 char *p = str; | |
328 int i; | |
329 ASS_Event *target = event; | |
18937 | 330 |
30200 | 331 char *format = strdup(track->event_format); |
332 char *q = format; // format scanning pointer | |
29472 | 333 |
30200 | 334 if (track->n_styles == 0) { |
335 // add "Default" style to the end | |
336 // will be used if track does not contain a default style (or even does not contain styles at all) | |
337 int sid = ass_alloc_style(track); | |
34011 | 338 set_default_style(&track->styles[sid]); |
339 track->default_style = sid; | |
30200 | 340 } |
18937 | 341 |
30200 | 342 for (i = 0; i < n_ignored; ++i) { |
343 NEXT(q, tname); | |
344 } | |
18937 | 345 |
30200 | 346 while (1) { |
347 NEXT(q, tname); | |
348 if (strcasecmp(tname, "Text") == 0) { | |
349 char *last; | |
350 event->Text = strdup(p); | |
351 if (*event->Text != 0) { | |
352 last = event->Text + strlen(event->Text) - 1; | |
353 if (last >= event->Text && *last == '\r') | |
354 *last = 0; | |
355 } | |
356 ass_msg(track->library, MSGL_DBG2, "Text = %s", event->Text); | |
357 event->Duration -= event->Start; | |
358 free(format); | |
359 return 0; // "Text" is always the last | |
360 } | |
361 NEXT(p, token); | |
18937 | 362 |
30200 | 363 ALIAS(End, Duration) // temporarily store end timecode in event->Duration |
364 if (0) { // cool ;) | |
365 INTVAL(Layer) | |
366 STYLEVAL(Style) | |
367 STRVAL(Name) | |
368 STRVAL(Effect) | |
369 INTVAL(MarginL) | |
370 INTVAL(MarginR) | |
371 INTVAL(MarginV) | |
372 TIMEVAL(Start) | |
373 TIMEVAL(Duration) | |
374 } | |
375 } | |
376 free(format); | |
377 return 1; | |
18937 | 378 } |
379 | |
380 /** | |
19495 | 381 * \brief Parse command line style overrides (--ass-force-style option) |
382 * \param track track to apply overrides to | |
383 * The format for overrides is [StyleName.]Field=Value | |
384 */ | |
30200 | 385 void ass_process_force_style(ASS_Track *track) |
386 { | |
387 char **fs, *eq, *dt, *style, *tname, *token; | |
388 ASS_Style *target; | |
389 int sid; | |
390 char **list = track->library->style_overrides; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
391 |
30200 | 392 if (!list) |
393 return; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
394 |
30200 | 395 for (fs = list; *fs; ++fs) { |
396 eq = strrchr(*fs, '='); | |
397 if (!eq) | |
398 continue; | |
399 *eq = '\0'; | |
400 token = eq + 1; | |
19495 | 401 |
30200 | 402 if (!strcasecmp(*fs, "PlayResX")) |
403 track->PlayResX = atoi(token); | |
404 else if (!strcasecmp(*fs, "PlayResY")) | |
405 track->PlayResY = atoi(token); | |
406 else if (!strcasecmp(*fs, "Timer")) | |
31853 | 407 track->Timer = ass_atof(token); |
30200 | 408 else if (!strcasecmp(*fs, "WrapStyle")) |
409 track->WrapStyle = atoi(token); | |
410 else if (!strcasecmp(*fs, "ScaledBorderAndShadow")) | |
411 track->ScaledBorderAndShadow = parse_bool(token); | |
412 else if (!strcasecmp(*fs, "Kerning")) | |
413 track->Kerning = parse_bool(token); | |
25582
6b1d7568ae3d
Allow overriding [Script Info] parameters with -ass-force-style option.
eugeni
parents:
25515
diff
changeset
|
414 |
30200 | 415 dt = strrchr(*fs, '.'); |
416 if (dt) { | |
417 *dt = '\0'; | |
418 style = *fs; | |
419 tname = dt + 1; | |
420 } else { | |
421 style = NULL; | |
422 tname = *fs; | |
423 } | |
424 for (sid = 0; sid < track->n_styles; ++sid) { | |
425 if (style == NULL | |
426 || strcasecmp(track->styles[sid].Name, style) == 0) { | |
427 target = track->styles + sid; | |
428 if (0) { | |
429 STRVAL(FontName) | |
430 COLORVAL(PrimaryColour) | |
431 COLORVAL(SecondaryColour) | |
432 COLORVAL(OutlineColour) | |
433 COLORVAL(BackColour) | |
434 FPVAL(FontSize) | |
435 INTVAL(Bold) | |
436 INTVAL(Italic) | |
437 INTVAL(Underline) | |
438 INTVAL(StrikeOut) | |
439 FPVAL(Spacing) | |
440 INTVAL(Angle) | |
441 INTVAL(BorderStyle) | |
442 INTVAL(Alignment) | |
443 INTVAL(MarginL) | |
444 INTVAL(MarginR) | |
445 INTVAL(MarginV) | |
446 INTVAL(Encoding) | |
447 FPVAL(ScaleX) | |
448 FPVAL(ScaleY) | |
449 FPVAL(Outline) | |
450 FPVAL(Shadow) | |
451 } | |
452 } | |
453 } | |
454 *eq = '='; | |
455 if (dt) | |
456 *dt = '.'; | |
457 } | |
19495 | 458 } |
459 | |
460 /** | |
18937 | 461 * \brief Parse the Style line |
462 * \param track track | |
463 * \param str string to parse, zero-terminated | |
464 * Allocates a new style struct. | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
465 */ |
30200 | 466 static int process_style(ASS_Track *track, char *str) |
18937 | 467 { |
468 | |
30200 | 469 char *token; |
470 char *tname; | |
471 char *p = str; | |
472 char *format; | |
473 char *q; // format scanning pointer | |
474 int sid; | |
475 ASS_Style *style; | |
476 ASS_Style *target; | |
18937 | 477 |
30200 | 478 if (!track->style_format) { |
479 // no style format header | |
480 // probably an ancient script version | |
481 if (track->track_type == TRACK_TYPE_SSA) | |
482 track->style_format = | |
483 strdup | |
484 ("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour," | |
485 "TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline," | |
486 "Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding"); | |
487 else | |
488 track->style_format = | |
489 strdup | |
490 ("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour," | |
491 "OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut," | |
492 "ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow," | |
493 "Alignment, MarginL, MarginR, MarginV, Encoding"); | |
494 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
495 |
30200 | 496 q = format = strdup(track->style_format); |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
497 |
34011 | 498 // Add default style first |
499 if (track->n_styles == 0) { | |
500 // will be used if track does not contain a default style (or even does not contain styles at all) | |
501 int sid = ass_alloc_style(track); | |
502 set_default_style(&track->styles[sid]); | |
503 track->default_style = sid; | |
504 } | |
505 | |
30200 | 506 ass_msg(track->library, MSGL_V, "[%p] Style: %s", track, str); |
507 | |
508 sid = ass_alloc_style(track); | |
18937 | 509 |
30200 | 510 style = track->styles + sid; |
511 target = style; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
512 |
30200 | 513 // fill style with some default values |
514 style->ScaleX = 100.; | |
515 style->ScaleY = 100.; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
516 |
30200 | 517 while (1) { |
518 NEXT(q, tname); | |
519 NEXT(p, token); | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
520 |
30200 | 521 if (0) { // cool ;) |
522 STRVAL(Name) | |
523 if ((strcmp(target->Name, "Default") == 0) | |
524 || (strcmp(target->Name, "*Default") == 0)) | |
525 track->default_style = sid; | |
526 STRVAL(FontName) | |
527 COLORVAL(PrimaryColour) | |
528 COLORVAL(SecondaryColour) | |
529 COLORVAL(OutlineColour) // TertiaryColor | |
530 COLORVAL(BackColour) | |
531 // SSA uses BackColour for both outline and shadow | |
532 // this will destroy SSA's TertiaryColour, but i'm not going to use it anyway | |
533 if (track->track_type == TRACK_TYPE_SSA) | |
534 target->OutlineColour = target->BackColour; | |
535 FPVAL(FontSize) | |
536 INTVAL(Bold) | |
537 INTVAL(Italic) | |
538 INTVAL(Underline) | |
539 INTVAL(StrikeOut) | |
540 FPVAL(Spacing) | |
541 INTVAL(Angle) | |
542 INTVAL(BorderStyle) | |
543 INTVAL(Alignment) | |
544 if (track->track_type == TRACK_TYPE_ASS) | |
545 target->Alignment = numpad2align(target->Alignment); | |
546 INTVAL(MarginL) | |
547 INTVAL(MarginR) | |
548 INTVAL(MarginV) | |
549 INTVAL(Encoding) | |
550 FPVAL(ScaleX) | |
551 FPVAL(ScaleY) | |
552 FPVAL(Outline) | |
553 FPVAL(Shadow) | |
554 } | |
555 } | |
556 style->ScaleX /= 100.; | |
557 style->ScaleY /= 100.; | |
558 style->Bold = !!style->Bold; | |
559 style->Italic = !!style->Italic; | |
560 style->Underline = !!style->Underline; | |
561 if (!style->Name) | |
562 style->Name = strdup("Default"); | |
563 if (!style->FontName) | |
564 style->FontName = strdup("Arial"); | |
565 free(format); | |
566 return 0; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
567 |
18937 | 568 } |
569 | |
30200 | 570 static int process_styles_line(ASS_Track *track, char *str) |
19492 | 571 { |
30200 | 572 if (!strncmp(str, "Format:", 7)) { |
573 char *p = str + 7; | |
574 skip_spaces(&p); | |
575 track->style_format = strdup(p); | |
576 ass_msg(track->library, MSGL_DBG2, "Style format: %s", | |
577 track->style_format); | |
578 } else if (!strncmp(str, "Style:", 6)) { | |
579 char *p = str + 6; | |
580 skip_spaces(&p); | |
581 process_style(track, p); | |
582 } | |
583 return 0; | |
584 } | |
585 | |
586 static int process_info_line(ASS_Track *track, char *str) | |
587 { | |
588 if (!strncmp(str, "PlayResX:", 9)) { | |
589 track->PlayResX = atoi(str + 9); | |
590 } else if (!strncmp(str, "PlayResY:", 9)) { | |
591 track->PlayResY = atoi(str + 9); | |
592 } else if (!strncmp(str, "Timer:", 6)) { | |
31853 | 593 track->Timer = ass_atof(str + 6); |
30200 | 594 } else if (!strncmp(str, "WrapStyle:", 10)) { |
595 track->WrapStyle = atoi(str + 10); | |
596 } else if (!strncmp(str, "ScaledBorderAndShadow:", 22)) { | |
597 track->ScaledBorderAndShadow = parse_bool(str + 22); | |
598 } else if (!strncmp(str, "Kerning:", 8)) { | |
599 track->Kerning = parse_bool(str + 8); | |
34295 | 600 } else if (!strncmp(str, "Language:", 9)) { |
601 char *p = str + 9; | |
602 while (*p && isspace(*p)) p++; | |
603 track->Language = malloc(3); | |
604 strncpy(track->Language, p, 2); | |
605 track->Language[2] = 0; | |
30200 | 606 } |
607 return 0; | |
19492 | 608 } |
609 | |
30200 | 610 static void event_format_fallback(ASS_Track *track) |
19492 | 611 { |
30200 | 612 track->parser_priv->state = PST_EVENTS; |
613 if (track->track_type == TRACK_TYPE_SSA) | |
614 track->event_format = strdup("Format: Marked, Start, End, Style, " | |
615 "Name, MarginL, MarginR, MarginV, Effect, Text"); | |
616 else | |
617 track->event_format = strdup("Format: Layer, Start, End, Style, " | |
618 "Actor, MarginL, MarginR, MarginV, Effect, Text"); | |
619 ass_msg(track->library, MSGL_V, | |
620 "No event format found, using fallback"); | |
19492 | 621 } |
622 | |
30200 | 623 static int process_events_line(ASS_Track *track, char *str) |
19492 | 624 { |
30200 | 625 if (!strncmp(str, "Format:", 7)) { |
626 char *p = str + 7; | |
627 skip_spaces(&p); | |
31875 | 628 free(track->event_format); |
30200 | 629 track->event_format = strdup(p); |
630 ass_msg(track->library, MSGL_DBG2, "Event format: %s", track->event_format); | |
631 } else if (!strncmp(str, "Dialogue:", 9)) { | |
632 // This should never be reached for embedded subtitles. | |
633 // They have slightly different format and are parsed in ass_process_chunk, | |
634 // called directly from demuxer | |
635 int eid; | |
636 ASS_Event *event; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
637 |
30200 | 638 str += 9; |
639 skip_spaces(&str); | |
19492 | 640 |
30200 | 641 eid = ass_alloc_event(track); |
642 event = track->events + eid; | |
19492 | 643 |
30200 | 644 // We can't parse events with event_format |
645 if (!track->event_format) | |
646 event_format_fallback(track); | |
647 | |
648 process_event_tail(track, event, str, 0); | |
649 } else { | |
31853 | 650 ass_msg(track->library, MSGL_V, "Not understood: '%.30s'", str); |
30200 | 651 } |
652 return 0; | |
19492 | 653 } |
654 | |
655 // Copied from mkvtoolnix | |
30200 | 656 static unsigned char *decode_chars(unsigned char c1, unsigned char c2, |
657 unsigned char c3, unsigned char c4, | |
658 unsigned char *dst, int cnt) | |
19492 | 659 { |
30200 | 660 uint32_t value; |
661 unsigned char bytes[3]; | |
662 int i; | |
19492 | 663 |
30200 | 664 value = |
665 ((c1 - 33) << 18) + ((c2 - 33) << 12) + ((c3 - 33) << 6) + (c4 - | |
666 33); | |
667 bytes[2] = value & 0xff; | |
668 bytes[1] = (value & 0xff00) >> 8; | |
669 bytes[0] = (value & 0xff0000) >> 16; | |
19492 | 670 |
30200 | 671 for (i = 0; i < cnt; ++i) |
672 *dst++ = bytes[i]; | |
673 return dst; | |
19492 | 674 } |
675 | |
30200 | 676 static int decode_font(ASS_Track *track) |
19492 | 677 { |
30200 | 678 unsigned char *p; |
679 unsigned char *q; | |
680 int i; | |
681 int size; // original size | |
682 int dsize; // decoded size | |
683 unsigned char *buf = 0; | |
19492 | 684 |
30200 | 685 ass_msg(track->library, MSGL_V, "Font: %d bytes encoded data", |
686 track->parser_priv->fontdata_used); | |
687 size = track->parser_priv->fontdata_used; | |
688 if (size % 4 == 1) { | |
689 ass_msg(track->library, MSGL_ERR, "Bad encoded data size"); | |
690 goto error_decode_font; | |
691 } | |
692 buf = malloc(size / 4 * 3 + 2); | |
693 q = buf; | |
694 for (i = 0, p = (unsigned char *) track->parser_priv->fontdata; | |
695 i < size / 4; i++, p += 4) { | |
696 q = decode_chars(p[0], p[1], p[2], p[3], q, 3); | |
697 } | |
698 if (size % 4 == 2) { | |
699 q = decode_chars(p[0], p[1], 0, 0, q, 1); | |
700 } else if (size % 4 == 3) { | |
701 q = decode_chars(p[0], p[1], p[2], 0, q, 2); | |
702 } | |
703 dsize = q - buf; | |
704 assert(dsize <= size / 4 * 3 + 2); | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
705 |
30200 | 706 if (track->library->extract_fonts) { |
707 ass_add_font(track->library, track->parser_priv->fontname, | |
708 (char *) buf, dsize); | |
709 } | |
19492 | 710 |
31875 | 711 error_decode_font: |
712 free(buf); | |
30200 | 713 free(track->parser_priv->fontname); |
714 free(track->parser_priv->fontdata); | |
715 track->parser_priv->fontname = 0; | |
716 track->parser_priv->fontdata = 0; | |
717 track->parser_priv->fontdata_size = 0; | |
718 track->parser_priv->fontdata_used = 0; | |
719 return 0; | |
19492 | 720 } |
721 | |
30200 | 722 static int process_fonts_line(ASS_Track *track, char *str) |
19492 | 723 { |
30200 | 724 int len; |
19492 | 725 |
30200 | 726 if (!strncmp(str, "fontname:", 9)) { |
727 char *p = str + 9; | |
728 skip_spaces(&p); | |
729 if (track->parser_priv->fontname) { | |
730 decode_font(track); | |
731 } | |
732 track->parser_priv->fontname = strdup(p); | |
733 ass_msg(track->library, MSGL_V, "Fontname: %s", | |
734 track->parser_priv->fontname); | |
735 return 0; | |
736 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
737 |
30200 | 738 if (!track->parser_priv->fontname) { |
739 ass_msg(track->library, MSGL_V, "Not understood: '%s'", str); | |
740 return 0; | |
741 } | |
19492 | 742 |
30200 | 743 len = strlen(str); |
744 if (len > 80) { | |
745 ass_msg(track->library, MSGL_WARN, "Font line too long: %d, %s", | |
746 len, str); | |
747 return 0; | |
748 } | |
749 if (track->parser_priv->fontdata_used + len > | |
750 track->parser_priv->fontdata_size) { | |
751 track->parser_priv->fontdata_size += 100 * 1024; | |
752 track->parser_priv->fontdata = | |
753 realloc(track->parser_priv->fontdata, | |
754 track->parser_priv->fontdata_size); | |
755 } | |
756 memcpy(track->parser_priv->fontdata + track->parser_priv->fontdata_used, | |
757 str, len); | |
758 track->parser_priv->fontdata_used += len; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
759 |
30200 | 760 return 0; |
19492 | 761 } |
762 | |
18937 | 763 /** |
764 * \brief Parse a header line | |
765 * \param track track | |
766 * \param str string to parse, zero-terminated | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
767 */ |
30200 | 768 static int process_line(ASS_Track *track, char *str) |
18937 | 769 { |
30200 | 770 if (!strncasecmp(str, "[Script Info]", 13)) { |
771 track->parser_priv->state = PST_INFO; | |
772 } else if (!strncasecmp(str, "[V4 Styles]", 11)) { | |
773 track->parser_priv->state = PST_STYLES; | |
774 track->track_type = TRACK_TYPE_SSA; | |
775 } else if (!strncasecmp(str, "[V4+ Styles]", 12)) { | |
776 track->parser_priv->state = PST_STYLES; | |
777 track->track_type = TRACK_TYPE_ASS; | |
778 } else if (!strncasecmp(str, "[Events]", 8)) { | |
779 track->parser_priv->state = PST_EVENTS; | |
780 } else if (!strncasecmp(str, "[Fonts]", 7)) { | |
781 track->parser_priv->state = PST_FONTS; | |
782 } else { | |
783 switch (track->parser_priv->state) { | |
784 case PST_INFO: | |
785 process_info_line(track, str); | |
786 break; | |
787 case PST_STYLES: | |
788 process_styles_line(track, str); | |
789 break; | |
790 case PST_EVENTS: | |
791 process_events_line(track, str); | |
792 break; | |
793 case PST_FONTS: | |
794 process_fonts_line(track, str); | |
795 break; | |
796 default: | |
797 break; | |
798 } | |
799 } | |
19492 | 800 |
30200 | 801 // there is no explicit end-of-font marker in ssa/ass |
802 if ((track->parser_priv->state != PST_FONTS) | |
803 && (track->parser_priv->fontname)) | |
804 decode_font(track); | |
19492 | 805 |
30200 | 806 return 0; |
19492 | 807 } |
808 | |
30200 | 809 static int process_text(ASS_Track *track, char *str) |
19492 | 810 { |
30200 | 811 char *p = str; |
812 while (1) { | |
813 char *q; | |
814 while (1) { | |
815 if ((*p == '\r') || (*p == '\n')) | |
816 ++p; | |
817 else if (p[0] == '\xef' && p[1] == '\xbb' && p[2] == '\xbf') | |
818 p += 3; // U+FFFE (BOM) | |
819 else | |
820 break; | |
821 } | |
822 for (q = p; ((*q != '\0') && (*q != '\r') && (*q != '\n')); ++q) { | |
823 }; | |
824 if (q == p) | |
825 break; | |
826 if (*q != '\0') | |
827 *(q++) = '\0'; | |
828 process_line(track, p); | |
829 if (*q == '\0') | |
830 break; | |
831 p = q; | |
832 } | |
833 return 0; | |
18937 | 834 } |
835 | |
836 /** | |
27498
d895515b366d
libass: add a new ass_process_data() to process demuxed subtitle packets
aurel
parents:
27393
diff
changeset
|
837 * \brief Process a chunk of subtitle stream data. |
18937 | 838 * \param track track |
839 * \param data string to parse | |
840 * \param size length of data | |
27498
d895515b366d
libass: add a new ass_process_data() to process demuxed subtitle packets
aurel
parents:
27393
diff
changeset
|
841 */ |
30200 | 842 void ass_process_data(ASS_Track *track, char *data, int size) |
18937 | 843 { |
30200 | 844 char *str = malloc(size + 1); |
18937 | 845 |
30200 | 846 memcpy(str, data, size); |
847 str[size] = '\0'; | |
18937 | 848 |
30200 | 849 ass_msg(track->library, MSGL_V, "Event: %s", str); |
850 process_text(track, str); | |
851 free(str); | |
27498
d895515b366d
libass: add a new ass_process_data() to process demuxed subtitle packets
aurel
parents:
27393
diff
changeset
|
852 } |
d895515b366d
libass: add a new ass_process_data() to process demuxed subtitle packets
aurel
parents:
27393
diff
changeset
|
853 |
d895515b366d
libass: add a new ass_process_data() to process demuxed subtitle packets
aurel
parents:
27393
diff
changeset
|
854 /** |
d895515b366d
libass: add a new ass_process_data() to process demuxed subtitle packets
aurel
parents:
27393
diff
changeset
|
855 * \brief Process CodecPrivate section of subtitle stream |
d895515b366d
libass: add a new ass_process_data() to process demuxed subtitle packets
aurel
parents:
27393
diff
changeset
|
856 * \param track track |
d895515b366d
libass: add a new ass_process_data() to process demuxed subtitle packets
aurel
parents:
27393
diff
changeset
|
857 * \param data string to parse |
d895515b366d
libass: add a new ass_process_data() to process demuxed subtitle packets
aurel
parents:
27393
diff
changeset
|
858 * \param size length of data |
d895515b366d
libass: add a new ass_process_data() to process demuxed subtitle packets
aurel
parents:
27393
diff
changeset
|
859 CodecPrivate section contains [Stream Info] and [V4+ Styles] ([V4 Styles] for SSA) sections |
d895515b366d
libass: add a new ass_process_data() to process demuxed subtitle packets
aurel
parents:
27393
diff
changeset
|
860 */ |
30200 | 861 void ass_process_codec_private(ASS_Track *track, char *data, int size) |
27498
d895515b366d
libass: add a new ass_process_data() to process demuxed subtitle packets
aurel
parents:
27393
diff
changeset
|
862 { |
30200 | 863 ass_process_data(track, data, size); |
18937 | 864 |
30200 | 865 // probably an mkv produced by ancient mkvtoolnix |
866 // such files don't have [Events] and Format: headers | |
867 if (!track->event_format) | |
868 event_format_fallback(track); | |
19495 | 869 |
30200 | 870 ass_process_force_style(track); |
18937 | 871 } |
872 | |
30200 | 873 static int check_duplicate_event(ASS_Track *track, int ReadOrder) |
18937 | 874 { |
30200 | 875 int i; |
876 for (i = 0; i < track->n_events - 1; ++i) // ignoring last event, it is the one we are comparing with | |
877 if (track->events[i].ReadOrder == ReadOrder) | |
878 return 1; | |
879 return 0; | |
18937 | 880 } |
881 | |
882 /** | |
25515 | 883 * \brief Process a chunk of subtitle stream data. In Matroska, this contains exactly 1 event (or a commentary). |
18937 | 884 * \param track track |
885 * \param data string to parse | |
886 * \param size length of data | |
887 * \param timecode starting time of the event (milliseconds) | |
888 * \param duration duration of the event (milliseconds) | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
889 */ |
30200 | 890 void ass_process_chunk(ASS_Track *track, char *data, int size, |
891 long long timecode, long long duration) | |
18937 | 892 { |
30200 | 893 char *str; |
894 int eid; | |
895 char *p; | |
896 char *token; | |
897 ASS_Event *event; | |
18937 | 898 |
30200 | 899 if (!track->event_format) { |
900 ass_msg(track->library, MSGL_WARN, "Event format header missing"); | |
901 return; | |
902 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
903 |
30200 | 904 str = malloc(size + 1); |
905 memcpy(str, data, size); | |
906 str[size] = '\0'; | |
907 ass_msg(track->library, MSGL_V, "Event at %" PRId64 ", +%" PRId64 ": %s", | |
908 (int64_t) timecode, (int64_t) duration, str); | |
18937 | 909 |
30200 | 910 eid = ass_alloc_event(track); |
911 event = track->events + eid; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
912 |
30200 | 913 p = str; |
18937 | 914 |
30200 | 915 do { |
916 NEXT(p, token); | |
917 event->ReadOrder = atoi(token); | |
918 if (check_duplicate_event(track, event->ReadOrder)) | |
919 break; | |
18937 | 920 |
30200 | 921 NEXT(p, token); |
922 event->Layer = atoi(token); | |
18937 | 923 |
30200 | 924 process_event_tail(track, event, p, 3); |
925 | |
926 event->Start = timecode; | |
927 event->Duration = duration; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
928 |
30200 | 929 free(str); |
930 return; | |
931 // dump_events(tid); | |
932 } while (0); | |
933 // some error | |
934 ass_free_event(track, eid); | |
935 track->n_events--; | |
936 free(str); | |
18937 | 937 } |
938 | |
31227
ee7beb1a3a6e
backport ass_flush_events() from upstream libass and make use of it
aurel
parents:
30200
diff
changeset
|
939 /** |
ee7beb1a3a6e
backport ass_flush_events() from upstream libass and make use of it
aurel
parents:
30200
diff
changeset
|
940 * \brief Flush buffered events. |
ee7beb1a3a6e
backport ass_flush_events() from upstream libass and make use of it
aurel
parents:
30200
diff
changeset
|
941 * \param track track |
ee7beb1a3a6e
backport ass_flush_events() from upstream libass and make use of it
aurel
parents:
30200
diff
changeset
|
942 */ |
ee7beb1a3a6e
backport ass_flush_events() from upstream libass and make use of it
aurel
parents:
30200
diff
changeset
|
943 void ass_flush_events(ASS_Track *track) |
ee7beb1a3a6e
backport ass_flush_events() from upstream libass and make use of it
aurel
parents:
30200
diff
changeset
|
944 { |
ee7beb1a3a6e
backport ass_flush_events() from upstream libass and make use of it
aurel
parents:
30200
diff
changeset
|
945 if (track->events) { |
ee7beb1a3a6e
backport ass_flush_events() from upstream libass and make use of it
aurel
parents:
30200
diff
changeset
|
946 int eid; |
ee7beb1a3a6e
backport ass_flush_events() from upstream libass and make use of it
aurel
parents:
30200
diff
changeset
|
947 for (eid = 0; eid < track->n_events; eid++) |
ee7beb1a3a6e
backport ass_flush_events() from upstream libass and make use of it
aurel
parents:
30200
diff
changeset
|
948 ass_free_event(track, eid); |
ee7beb1a3a6e
backport ass_flush_events() from upstream libass and make use of it
aurel
parents:
30200
diff
changeset
|
949 track->n_events = 0; |
ee7beb1a3a6e
backport ass_flush_events() from upstream libass and make use of it
aurel
parents:
30200
diff
changeset
|
950 } |
ee7beb1a3a6e
backport ass_flush_events() from upstream libass and make use of it
aurel
parents:
30200
diff
changeset
|
951 } |
ee7beb1a3a6e
backport ass_flush_events() from upstream libass and make use of it
aurel
parents:
30200
diff
changeset
|
952 |
27393 | 953 #ifdef CONFIG_ICONV |
18937 | 954 /** \brief recode buffer to utf-8 |
20477 | 955 * constraint: codepage != 0 |
18937 | 956 * \param data pointer to text buffer |
957 * \param size buffer size | |
958 * \return a pointer to recoded buffer, caller is responsible for freeing it | |
959 **/ | |
30200 | 960 static char *sub_recode(ASS_Library *library, char *data, size_t size, |
961 char *codepage) | |
18937 | 962 { |
30200 | 963 iconv_t icdsc; |
964 char *tocp = "UTF-8"; | |
965 char *outbuf; | |
966 assert(codepage); | |
18937 | 967 |
30200 | 968 { |
969 const char *cp_tmp = codepage; | |
27393 | 970 #ifdef CONFIG_ENCA |
30200 | 971 char enca_lang[3], enca_fallback[100]; |
972 if (sscanf(codepage, "enca:%2s:%99s", enca_lang, enca_fallback) == 2 | |
973 || sscanf(codepage, "ENCA:%2s:%99s", enca_lang, | |
974 enca_fallback) == 2) { | |
975 cp_tmp = | |
976 ass_guess_buffer_cp(library, (unsigned char *) data, size, | |
977 enca_lang, enca_fallback); | |
978 } | |
18937 | 979 #endif |
30200 | 980 if ((icdsc = iconv_open(tocp, cp_tmp)) != (iconv_t) (-1)) { |
981 ass_msg(library, MSGL_V, "Opened iconv descriptor"); | |
982 } else | |
983 ass_msg(library, MSGL_ERR, "Error opening iconv descriptor"); | |
984 } | |
18937 | 985 |
30200 | 986 { |
987 size_t osize = size; | |
988 size_t ileft = size; | |
989 size_t oleft = size - 1; | |
990 char *ip; | |
991 char *op; | |
992 size_t rc; | |
993 int clear = 0; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
994 |
30200 | 995 outbuf = malloc(osize); |
996 ip = data; | |
997 op = outbuf; | |
18937 | 998 |
30200 | 999 while (1) { |
1000 if (ileft) | |
1001 rc = iconv(icdsc, &ip, &ileft, &op, &oleft); | |
1002 else { // clear the conversion state and leave | |
1003 clear = 1; | |
1004 rc = iconv(icdsc, NULL, NULL, &op, &oleft); | |
1005 } | |
1006 if (rc == (size_t) (-1)) { | |
1007 if (errno == E2BIG) { | |
1008 size_t offset = op - outbuf; | |
1009 outbuf = (char *) realloc(outbuf, osize + size); | |
1010 op = outbuf + offset; | |
1011 osize += size; | |
1012 oleft += size; | |
1013 } else { | |
1014 ass_msg(library, MSGL_WARN, "Error recoding file"); | |
1015 return NULL; | |
1016 } | |
1017 } else if (clear) | |
1018 break; | |
1019 } | |
1020 outbuf[osize - oleft - 1] = 0; | |
1021 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
1022 |
30200 | 1023 if (icdsc != (iconv_t) (-1)) { |
1024 (void) iconv_close(icdsc); | |
1025 icdsc = (iconv_t) (-1); | |
1026 ass_msg(library, MSGL_V, "Closed iconv descriptor"); | |
1027 } | |
1028 | |
1029 return outbuf; | |
18937 | 1030 } |
30200 | 1031 #endif // ICONV |
18937 | 1032 |
1033 /** | |
20603 | 1034 * \brief read file contents into newly allocated buffer |
1035 * \param fname file name | |
1036 * \param bufsize out: file size | |
1037 * \return pointer to file contents. Caller is responsible for its deallocation. | |
19652
2c016957360a
Add -ass-styles option. It allows to load styles from a file and use them
eugeni
parents:
19639
diff
changeset
|
1038 */ |
30200 | 1039 static char *read_file(ASS_Library *library, char *fname, size_t *bufsize) |
18937 | 1040 { |
30200 | 1041 int res; |
1042 long sz; | |
1043 long bytes_read; | |
1044 char *buf; | |
19652
2c016957360a
Add -ass-styles option. It allows to load styles from a file and use them
eugeni
parents:
19639
diff
changeset
|
1045 |
30200 | 1046 FILE *fp = fopen(fname, "rb"); |
1047 if (!fp) { | |
1048 ass_msg(library, MSGL_WARN, | |
1049 "ass_read_file(%s): fopen failed", fname); | |
1050 return 0; | |
1051 } | |
1052 res = fseek(fp, 0, SEEK_END); | |
1053 if (res == -1) { | |
1054 ass_msg(library, MSGL_WARN, | |
1055 "ass_read_file(%s): fseek failed", fname); | |
1056 fclose(fp); | |
1057 return 0; | |
1058 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
1059 |
30200 | 1060 sz = ftell(fp); |
1061 rewind(fp); | |
18937 | 1062 |
30200 | 1063 ass_msg(library, MSGL_V, "File size: %ld", sz); |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
1064 |
30200 | 1065 buf = malloc(sz + 1); |
1066 assert(buf); | |
1067 bytes_read = 0; | |
1068 do { | |
1069 res = fread(buf + bytes_read, 1, sz - bytes_read, fp); | |
1070 if (res <= 0) { | |
1071 ass_msg(library, MSGL_INFO, "Read failed, %d: %s", errno, | |
1072 strerror(errno)); | |
1073 fclose(fp); | |
1074 free(buf); | |
1075 return 0; | |
1076 } | |
1077 bytes_read += res; | |
1078 } while (sz - bytes_read > 0); | |
1079 buf[sz] = '\0'; | |
1080 fclose(fp); | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
1081 |
30200 | 1082 if (bufsize) |
1083 *bufsize = sz; | |
1084 return buf; | |
19652
2c016957360a
Add -ass-styles option. It allows to load styles from a file and use them
eugeni
parents:
19639
diff
changeset
|
1085 } |
2c016957360a
Add -ass-styles option. It allows to load styles from a file and use them
eugeni
parents:
19639
diff
changeset
|
1086 |
20603 | 1087 /* |
1088 * \param buf pointer to subtitle text in utf-8 | |
1089 */ | |
30200 | 1090 static ASS_Track *parse_memory(ASS_Library *library, char *buf) |
19652
2c016957360a
Add -ass-styles option. It allows to load styles from a file and use them
eugeni
parents:
19639
diff
changeset
|
1091 { |
30200 | 1092 ASS_Track *track; |
1093 int i; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
1094 |
30200 | 1095 track = ass_new_track(library); |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
1096 |
30200 | 1097 // process header |
1098 process_text(track, buf); | |
19492 | 1099 |
30200 | 1100 // external SSA/ASS subs does not have ReadOrder field |
1101 for (i = 0; i < track->n_events; ++i) | |
1102 track->events[i].ReadOrder = i; | |
19905 | 1103 |
30200 | 1104 // there is no explicit end-of-font marker in ssa/ass |
1105 if (track->parser_priv->fontname) | |
1106 decode_font(track); | |
19492 | 1107 |
30200 | 1108 if (track->track_type == TRACK_TYPE_UNKNOWN) { |
1109 ass_free_track(track); | |
1110 return 0; | |
1111 } | |
18937 | 1112 |
30200 | 1113 ass_process_force_style(track); |
19495 | 1114 |
30200 | 1115 return track; |
20603 | 1116 } |
1117 | |
1118 /** | |
1119 * \brief Read subtitles from memory. | |
1120 * \param library libass library object | |
1121 * \param buf pointer to subtitles text | |
1122 * \param bufsize size of buffer | |
1123 * \param codepage recode buffer contents from given codepage | |
1124 * \return newly allocated track | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
1125 */ |
30200 | 1126 ASS_Track *ass_read_memory(ASS_Library *library, char *buf, |
1127 size_t bufsize, char *codepage) | |
20603 | 1128 { |
30200 | 1129 ASS_Track *track; |
1130 int need_free = 0; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
1131 |
30200 | 1132 if (!buf) |
1133 return 0; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
1134 |
27393 | 1135 #ifdef CONFIG_ICONV |
30200 | 1136 if (codepage) { |
1137 buf = sub_recode(library, buf, bufsize, codepage); | |
1138 if (!buf) | |
1139 return 0; | |
1140 else | |
1141 need_free = 1; | |
1142 } | |
20603 | 1143 #endif |
30200 | 1144 track = parse_memory(library, buf); |
1145 if (need_free) | |
1146 free(buf); | |
1147 if (!track) | |
1148 return 0; | |
20603 | 1149 |
30200 | 1150 ass_msg(library, MSGL_INFO, "Added subtitle file: " |
1151 "<memory> (%d styles, %d events)", | |
1152 track->n_styles, track->n_events); | |
1153 return track; | |
20603 | 1154 } |
1155 | |
30200 | 1156 static char *read_file_recode(ASS_Library *library, char *fname, |
1157 char *codepage, size_t *size) | |
20603 | 1158 { |
30200 | 1159 char *buf; |
1160 size_t bufsize; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
1161 |
30200 | 1162 buf = read_file(library, fname, &bufsize); |
1163 if (!buf) | |
1164 return 0; | |
27393 | 1165 #ifdef CONFIG_ICONV |
30200 | 1166 if (codepage) { |
1167 char *tmpbuf = sub_recode(library, buf, bufsize, codepage); | |
1168 free(buf); | |
1169 buf = tmpbuf; | |
1170 } | |
1171 if (!buf) | |
1172 return 0; | |
20603 | 1173 #endif |
30200 | 1174 *size = bufsize; |
1175 return buf; | |
23424
7286d245bf33
Move code for reading a file and recoding it to utf-8 to a separate function.
eugeni
parents:
23300
diff
changeset
|
1176 } |
7286d245bf33
Move code for reading a file and recoding it to utf-8 to a separate function.
eugeni
parents:
23300
diff
changeset
|
1177 |
7286d245bf33
Move code for reading a file and recoding it to utf-8 to a separate function.
eugeni
parents:
23300
diff
changeset
|
1178 /** |
7286d245bf33
Move code for reading a file and recoding it to utf-8 to a separate function.
eugeni
parents:
23300
diff
changeset
|
1179 * \brief Read subtitles from file. |
7286d245bf33
Move code for reading a file and recoding it to utf-8 to a separate function.
eugeni
parents:
23300
diff
changeset
|
1180 * \param library libass library object |
7286d245bf33
Move code for reading a file and recoding it to utf-8 to a separate function.
eugeni
parents:
23300
diff
changeset
|
1181 * \param fname file name |
7286d245bf33
Move code for reading a file and recoding it to utf-8 to a separate function.
eugeni
parents:
23300
diff
changeset
|
1182 * \param codepage recode buffer contents from given codepage |
7286d245bf33
Move code for reading a file and recoding it to utf-8 to a separate function.
eugeni
parents:
23300
diff
changeset
|
1183 * \return newly allocated track |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
1184 */ |
30200 | 1185 ASS_Track *ass_read_file(ASS_Library *library, char *fname, |
1186 char *codepage) | |
23424
7286d245bf33
Move code for reading a file and recoding it to utf-8 to a separate function.
eugeni
parents:
23300
diff
changeset
|
1187 { |
30200 | 1188 char *buf; |
1189 ASS_Track *track; | |
1190 size_t bufsize; | |
23424
7286d245bf33
Move code for reading a file and recoding it to utf-8 to a separate function.
eugeni
parents:
23300
diff
changeset
|
1191 |
30200 | 1192 buf = read_file_recode(library, fname, codepage, &bufsize); |
1193 if (!buf) | |
1194 return 0; | |
1195 track = parse_memory(library, buf); | |
1196 free(buf); | |
1197 if (!track) | |
1198 return 0; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
1199 |
30200 | 1200 track->name = strdup(fname); |
20603 | 1201 |
30200 | 1202 ass_msg(library, MSGL_INFO, |
1203 "Added subtitle file: '%s' (%d styles, %d events)", | |
1204 fname, track->n_styles, track->n_events); | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
1205 |
30200 | 1206 return track; |
18937 | 1207 } |
1208 | |
19652
2c016957360a
Add -ass-styles option. It allows to load styles from a file and use them
eugeni
parents:
19639
diff
changeset
|
1209 /** |
2c016957360a
Add -ass-styles option. It allows to load styles from a file and use them
eugeni
parents:
19639
diff
changeset
|
1210 * \brief read styles from file into already initialized track |
2c016957360a
Add -ass-styles option. It allows to load styles from a file and use them
eugeni
parents:
19639
diff
changeset
|
1211 */ |
30200 | 1212 int ass_read_styles(ASS_Track *track, char *fname, char *codepage) |
19652
2c016957360a
Add -ass-styles option. It allows to load styles from a file and use them
eugeni
parents:
19639
diff
changeset
|
1213 { |
30200 | 1214 char *buf; |
1215 ParserState old_state; | |
1216 size_t sz; | |
19652
2c016957360a
Add -ass-styles option. It allows to load styles from a file and use them
eugeni
parents:
19639
diff
changeset
|
1217 |
30200 | 1218 buf = read_file(track->library, fname, &sz); |
1219 if (!buf) | |
1220 return 1; | |
27393 | 1221 #ifdef CONFIG_ICONV |
30200 | 1222 if (codepage) { |
1223 char *tmpbuf; | |
1224 tmpbuf = sub_recode(track->library, buf, sz, codepage); | |
1225 free(buf); | |
1226 buf = tmpbuf; | |
1227 } | |
1228 if (!buf) | |
1229 return 0; | |
20603 | 1230 #endif |
19652
2c016957360a
Add -ass-styles option. It allows to load styles from a file and use them
eugeni
parents:
19639
diff
changeset
|
1231 |
30200 | 1232 old_state = track->parser_priv->state; |
1233 track->parser_priv->state = PST_STYLES; | |
1234 process_text(track, buf); | |
1235 track->parser_priv->state = old_state; | |
19652
2c016957360a
Add -ass-styles option. It allows to load styles from a file and use them
eugeni
parents:
19639
diff
changeset
|
1236 |
30200 | 1237 return 0; |
19652
2c016957360a
Add -ass-styles option. It allows to load styles from a file and use them
eugeni
parents:
19639
diff
changeset
|
1238 } |
2c016957360a
Add -ass-styles option. It allows to load styles from a file and use them
eugeni
parents:
19639
diff
changeset
|
1239 |
30200 | 1240 long long ass_step_sub(ASS_Track *track, long long now, int movement) |
1241 { | |
1242 int i; | |
18937 | 1243 |
30200 | 1244 if (movement == 0) |
1245 return 0; | |
1246 if (track->n_events == 0) | |
1247 return 0; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
1248 |
30200 | 1249 if (movement < 0) |
1250 for (i = 0; | |
1251 (i < track->n_events) | |
1252 && | |
1253 ((long long) (track->events[i].Start + | |
1254 track->events[i].Duration) <= now); ++i) { | |
1255 } else | |
1256 for (i = track->n_events - 1; | |
1257 (i >= 0) && ((long long) (track->events[i].Start) > now); | |
1258 --i) { | |
1259 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28900
diff
changeset
|
1260 |
30200 | 1261 // -1 and n_events are ok |
1262 assert(i >= -1); | |
1263 assert(i <= track->n_events); | |
1264 i += movement; | |
1265 if (i < 0) | |
1266 i = 0; | |
1267 if (i >= track->n_events) | |
1268 i = track->n_events - 1; | |
1269 return ((long long) track->events[i].Start) - now; | |
18937 | 1270 } |
1271 | |
30200 | 1272 ASS_Track *ass_new_track(ASS_Library *library) |
1273 { | |
1274 ASS_Track *track = calloc(1, sizeof(ASS_Track)); | |
1275 track->library = library; | |
1276 track->ScaledBorderAndShadow = 1; | |
1277 track->parser_priv = calloc(1, sizeof(ASS_ParserPriv)); | |
1278 return track; | |
18937 | 1279 } |
34295 | 1280 |
1281 /** | |
1282 * \brief Prepare track for rendering | |
1283 */ | |
1284 void ass_lazy_track_init(ASS_Library *lib, ASS_Track *track) | |
1285 { | |
1286 if (track->PlayResX && track->PlayResY) | |
1287 return; | |
1288 if (!track->PlayResX && !track->PlayResY) { | |
1289 ass_msg(lib, MSGL_WARN, | |
1290 "Neither PlayResX nor PlayResY defined. Assuming 384x288"); | |
1291 track->PlayResX = 384; | |
1292 track->PlayResY = 288; | |
1293 } else { | |
1294 if (!track->PlayResY && track->PlayResX == 1280) { | |
1295 track->PlayResY = 1024; | |
1296 ass_msg(lib, MSGL_WARN, | |
1297 "PlayResY undefined, setting to %d", track->PlayResY); | |
1298 } else if (!track->PlayResY) { | |
1299 track->PlayResY = track->PlayResX * 3 / 4; | |
1300 ass_msg(lib, MSGL_WARN, | |
1301 "PlayResY undefined, setting to %d", track->PlayResY); | |
1302 } else if (!track->PlayResX && track->PlayResY == 1024) { | |
1303 track->PlayResX = 1280; | |
1304 ass_msg(lib, MSGL_WARN, | |
1305 "PlayResX undefined, setting to %d", track->PlayResX); | |
1306 } else if (!track->PlayResX) { | |
1307 track->PlayResX = track->PlayResY * 4 / 3; | |
1308 ass_msg(lib, MSGL_WARN, | |
1309 "PlayResX undefined, setting to %d", track->PlayResX); | |
1310 } | |
1311 } | |
1312 } |