FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
microdvddec.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012 Clément Bœsch
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /**
22  * @file
23  * MicroDVD subtitle decoder
24  *
25  * Based on the specifications found here:
26  * https://trac.videolan.org/vlc/ticket/1825#comment:6
27  */
28 
29 #include "libavutil/avstring.h"
30 #include "libavutil/parseutils.h"
31 #include "libavutil/bprint.h"
32 #include "avcodec.h"
33 #include "ass.h"
34 
35 static int indexof(const char *s, int c)
36 {
37  char *f = strchr(s, c);
38  return f ? (f - s) : -1;
39 }
40 
41 struct microdvd_tag {
42  char key;
44  uint32_t data1;
45  uint32_t data2;
46  char *data_string;
48 };
49 
50 #define MICRODVD_PERSISTENT_OFF 0
51 #define MICRODVD_PERSISTENT_ON 1
52 #define MICRODVD_PERSISTENT_OPENED 2
53 
54 // Color, Font, Size, cHarset, stYle, Position, cOordinate
55 #define MICRODVD_TAGS "cfshyYpo"
56 
57 static void microdvd_set_tag(struct microdvd_tag *tags, struct microdvd_tag tag)
58 {
59  int tag_index = indexof(MICRODVD_TAGS, tag.key);
60 
61  if (tag_index < 0)
62  return;
63  memcpy(&tags[tag_index], &tag, sizeof(tag));
64 }
65 
66 // italic, bold, underline, strike-through
67 #define MICRODVD_STYLES "ibus"
68 
69 static char *microdvd_load_tags(struct microdvd_tag *tags, char *s)
70 {
71  while (*s == '{') {
72  char *start = s;
73  char tag_char = *(s + 1);
74  struct microdvd_tag tag = {0};
75 
76  if (!tag_char || *(s + 2) != ':')
77  break;
78  s += 3;
79 
80  switch (tag_char) {
81 
82  /* Style */
83  case 'Y':
85  case 'y':
86  while (*s && *s != '}') {
87  int style_index = indexof(MICRODVD_STYLES, *s);
88 
89  if (style_index >= 0)
90  tag.data1 |= (1 << style_index);
91  s++;
92  }
93  if (*s != '}')
94  break;
95  /* We must distinguish persistent and non-persistent styles
96  * to handle this kind of style tags: {y:ib}{Y:us} */
97  tag.key = tag_char;
98  break;
99 
100  /* Color */
101  case 'C':
103  case 'c':
104  if (*s == '$')
105  s++;
106  tag.data1 = strtol(s, &s, 16) & 0x00ffffff;
107  if (*s != '}')
108  break;
109  tag.key = 'c';
110  break;
111 
112  /* Font name */
113  case 'F':
115  case 'f': {
116  int len = indexof(s, '}');
117  if (len < 0)
118  break;
119  tag.data_string = s;
120  tag.data_string_len = len;
121  s += len;
122  tag.key = 'f';
123  break;
124  }
125 
126  /* Font size */
127  case 'S':
129  case 's':
130  tag.data1 = strtol(s, &s, 10);
131  if (*s != '}')
132  break;
133  tag.key = 's';
134  break;
135 
136  /* Charset */
137  case 'H': {
138  //TODO: not yet handled, just parsed.
139  int len = indexof(s, '}');
140  if (len < 0)
141  break;
142  tag.data_string = s;
143  tag.data_string_len = len;
144  s += len;
145  tag.key = 'h';
146  break;
147  }
148 
149  /* Position */
150  case 'P':
152  tag.data1 = (*s++ == '1');
153  if (*s != '}')
154  break;
155  tag.key = 'p';
156  break;
157 
158  /* Coordinates */
159  case 'o':
161  tag.data1 = strtol(s, &s, 10);
162  if (*s != ',')
163  break;
164  s++;
165  tag.data2 = strtol(s, &s, 10);
166  if (*s != '}')
167  break;
168  tag.key = 'o';
169  break;
170 
171  default: /* Unknown tag, we consider it's text */
172  break;
173  }
174 
175  if (tag.key == 0)
176  return start;
177 
178  microdvd_set_tag(tags, tag);
179  s++;
180  }
181  return s;
182 }
183 
184 static void microdvd_open_tags(AVBPrint *new_line, struct microdvd_tag *tags)
185 {
186  int i, sidx;
187  for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
188  if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED)
189  continue;
190  switch (tags[i].key) {
191  case 'Y':
192  case 'y':
193  for (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++)
194  if (tags[i].data1 & (1 << sidx))
195  av_bprintf(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]);
196  break;
197 
198  case 'c':
199  av_bprintf(new_line, "{\\c&H%06X&}", tags[i].data1);
200  break;
201 
202  case 'f':
203  av_bprintf(new_line, "{\\fn%.*s}",
204  tags[i].data_string_len, tags[i].data_string);
205  break;
206 
207  case 's':
208  av_bprintf(new_line, "{\\fs%d}", tags[i].data1);
209  break;
210 
211  case 'p':
212  if (tags[i].data1 == 0)
213  av_bprintf(new_line, "{\\an8}");
214  break;
215 
216  case 'o':
217  av_bprintf(new_line, "{\\pos(%d,%d)}",
218  tags[i].data1, tags[i].data2);
219  break;
220  }
221  if (tags[i].persistent == MICRODVD_PERSISTENT_ON)
223  }
224 }
225 
227  struct microdvd_tag *tags)
228 {
229  int i, sidx;
230 
231  for (i = sizeof(MICRODVD_TAGS) - 2; i >= 0; i--) {
232  if (tags[i].persistent != MICRODVD_PERSISTENT_OFF)
233  continue;
234  switch (tags[i].key) {
235 
236  case 'y':
237  for (sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--)
238  if (tags[i].data1 & (1 << sidx))
239  av_bprintf(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]);
240  break;
241 
242  case 'c':
243  av_bprintf(new_line, "{\\c}");
244  break;
245 
246  case 'f':
247  av_bprintf(new_line, "{\\fn}");
248  break;
249 
250  case 's':
251  av_bprintf(new_line, "{\\fs}");
252  break;
253  }
254  tags[i].key = 0;
255  }
256 }
257 
259  void *data, int *got_sub_ptr, AVPacket *avpkt)
260 {
261  AVSubtitle *sub = data;
262  AVBPrint new_line;
263  char *line = avpkt->data;
264  char *end = avpkt->data + avpkt->size;
265  struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
266 
267  if (avpkt->size <= 0)
268  return avpkt->size;
269 
270  av_bprint_init(&new_line, 0, 2048);
271 
272  // subtitle content
273  while (line < end && *line) {
274 
275  // parse MicroDVD tags, and open them in ASS
276  line = microdvd_load_tags(tags, line);
277  microdvd_open_tags(&new_line, tags);
278 
279  // simple copy until EOL or forced carriage return
280  while (line < end && *line && *line != '|') {
281  av_bprint_chars(&new_line, *line, 1);
282  line++;
283  }
284 
285  // line split
286  if (line < end && *line == '|') {
287  microdvd_close_no_persistent_tags(&new_line, tags);
288  av_bprintf(&new_line, "\\N");
289  line++;
290  }
291  }
292  if (new_line.len) {
293  int ret;
294  int64_t start = avpkt->pts;
295  int64_t duration = avpkt->duration;
296  int ts_start = av_rescale_q(start, avctx->time_base, (AVRational){1,100});
297  int ts_duration = duration != -1 ?
298  av_rescale_q(duration, avctx->time_base, (AVRational){1,100}) : -1;
299 
300  ret = ff_ass_add_rect_bprint(sub, &new_line, ts_start, ts_duration);
301  av_bprint_finalize(&new_line, NULL);
302  if (ret < 0)
303  return ret;
304  }
305 
306  *got_sub_ptr = sub->num_rects > 0;
307  return avpkt->size;
308 }
309 
310 static int microdvd_init(AVCodecContext *avctx)
311 {
312  int i, sidx;
313  AVBPrint font_buf;
314  int font_size = ASS_DEFAULT_FONT_SIZE;
315  int color = ASS_DEFAULT_COLOR;
316  int bold = ASS_DEFAULT_BOLD;
317  int italic = ASS_DEFAULT_ITALIC;
318  int underline = ASS_DEFAULT_UNDERLINE;
319  int alignment = ASS_DEFAULT_ALIGNMENT;
320  struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
321 
323  av_bprintf(&font_buf, "%s", ASS_DEFAULT_FONT);
324 
325  if (avctx->extradata) {
326  microdvd_load_tags(tags, avctx->extradata);
327  for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
328  switch (av_tolower(tags[i].key)) {
329  case 'y':
330  for (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++) {
331  if (tags[i].data1 & (1 << sidx)) {
332  switch (MICRODVD_STYLES[sidx]) {
333  case 'i': italic = 1; break;
334  case 'b': bold = 1; break;
335  case 'u': underline = 1; break;
336  }
337  }
338  }
339  break;
340 
341  case 'c': color = tags[i].data1; break;
342  case 's': font_size = tags[i].data1; break;
343  case 'p': alignment = 8; break;
344 
345  case 'f':
346  av_bprint_clear(&font_buf);
347  av_bprintf(&font_buf, "%.*s",
348  tags[i].data_string_len, tags[i].data_string);
349  break;
350  }
351  }
352  }
353  return ff_ass_subtitle_header(avctx, font_buf.str, font_size, color,
354  ASS_DEFAULT_BACK_COLOR, bold, italic,
355  underline, alignment);
356 }
357 
359  .name = "microdvd",
360  .long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle"),
361  .type = AVMEDIA_TYPE_SUBTITLE,
362  .id = AV_CODEC_ID_MICRODVD,
363  .init = microdvd_init,
364  .decode = microdvd_decode_frame,
365 };