00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00029 #include "libavutil/avstring.h"
00030 #include "libavutil/parseutils.h"
00031 #include "libavutil/bprint.h"
00032 #include "avcodec.h"
00033 #include "ass.h"
00034
00035 static int indexof(const char *s, int c)
00036 {
00037 char *f = strchr(s, c);
00038 return f ? (f - s) : -1;
00039 }
00040
00041 struct microdvd_tag {
00042 char key;
00043 int persistent;
00044 uint32_t data1;
00045 uint32_t data2;
00046 char *data_string;
00047 int data_string_len;
00048 };
00049
00050 #define MICRODVD_PERSISTENT_OFF 0
00051 #define MICRODVD_PERSISTENT_ON 1
00052 #define MICRODVD_PERSISTENT_OPENED 2
00053
00054
00055 #define MICRODVD_TAGS "cfshyYpo"
00056
00057 static void microdvd_set_tag(struct microdvd_tag *tags, struct microdvd_tag tag)
00058 {
00059 int tag_index = indexof(MICRODVD_TAGS, tag.key);
00060
00061 if (tag_index < 0)
00062 return;
00063 memcpy(&tags[tag_index], &tag, sizeof(tag));
00064 }
00065
00066
00067 #define MICRODVD_STYLES "ibus"
00068
00069 static char *microdvd_load_tags(struct microdvd_tag *tags, char *s)
00070 {
00071 while (*s == '{') {
00072 char *start = s;
00073 char tag_char = *(s + 1);
00074 struct microdvd_tag tag = {0};
00075
00076 if (!tag_char || *(s + 2) != ':')
00077 break;
00078 s += 3;
00079
00080 switch (tag_char) {
00081
00082
00083 case 'Y':
00084 tag.persistent = MICRODVD_PERSISTENT_ON;
00085 case 'y':
00086 while (*s && *s != '}') {
00087 int style_index = indexof(MICRODVD_STYLES, *s);
00088
00089 if (style_index >= 0)
00090 tag.data1 |= (1 << style_index);
00091 s++;
00092 }
00093 if (*s != '}')
00094 break;
00095
00096
00097 tag.key = tag_char;
00098 break;
00099
00100
00101 case 'C':
00102 tag.persistent = MICRODVD_PERSISTENT_ON;
00103 case 'c':
00104 if (*s == '$')
00105 s++;
00106 tag.data1 = strtol(s, &s, 16) & 0x00ffffff;
00107 if (*s != '}')
00108 break;
00109 tag.key = 'c';
00110 break;
00111
00112
00113 case 'F':
00114 tag.persistent = MICRODVD_PERSISTENT_ON;
00115 case 'f': {
00116 int len = indexof(s, '}');
00117 if (len < 0)
00118 break;
00119 tag.data_string = s;
00120 tag.data_string_len = len;
00121 s += len;
00122 tag.key = 'f';
00123 break;
00124 }
00125
00126
00127 case 'S':
00128 tag.persistent = MICRODVD_PERSISTENT_ON;
00129 case 's':
00130 tag.data1 = strtol(s, &s, 10);
00131 if (*s != '}')
00132 break;
00133 tag.key = 's';
00134 break;
00135
00136
00137 case 'H': {
00138
00139 int len = indexof(s, '}');
00140 if (len < 0)
00141 break;
00142 tag.data_string = s;
00143 tag.data_string_len = len;
00144 s += len;
00145 tag.key = 'h';
00146 break;
00147 }
00148
00149
00150 case 'P':
00151 tag.persistent = MICRODVD_PERSISTENT_ON;
00152 tag.data1 = (*s++ == '1');
00153 if (*s != '}')
00154 break;
00155 tag.key = 'p';
00156 break;
00157
00158
00159 case 'o':
00160 tag.persistent = MICRODVD_PERSISTENT_ON;
00161 tag.data1 = strtol(s, &s, 10);
00162 if (*s != ',')
00163 break;
00164 s++;
00165 tag.data2 = strtol(s, &s, 10);
00166 if (*s != '}')
00167 break;
00168 tag.key = 'o';
00169 break;
00170
00171 default:
00172 break;
00173 }
00174
00175 if (tag.key == 0)
00176 return start;
00177
00178 microdvd_set_tag(tags, tag);
00179 s++;
00180 }
00181 return s;
00182 }
00183
00184 static void microdvd_open_tags(AVBPrint *new_line, struct microdvd_tag *tags)
00185 {
00186 int i, sidx;
00187 for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
00188 if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED)
00189 continue;
00190 switch (tags[i].key) {
00191 case 'Y':
00192 case 'y':
00193 for (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++)
00194 if (tags[i].data1 & (1 << sidx))
00195 av_bprintf(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]);
00196 break;
00197
00198 case 'c':
00199 av_bprintf(new_line, "{\\c&H%06X&}", tags[i].data1);
00200 break;
00201
00202 case 'f':
00203 av_bprintf(new_line, "{\\fn%.*s}",
00204 tags[i].data_string_len, tags[i].data_string);
00205 break;
00206
00207 case 's':
00208 av_bprintf(new_line, "{\\fs%d}", tags[i].data1);
00209 break;
00210
00211 case 'p':
00212 if (tags[i].data1 == 0)
00213 av_bprintf(new_line, "{\\an8}");
00214 break;
00215
00216 case 'o':
00217 av_bprintf(new_line, "{\\pos(%d,%d)}",
00218 tags[i].data1, tags[i].data2);
00219 break;
00220 }
00221 if (tags[i].persistent == MICRODVD_PERSISTENT_ON)
00222 tags[i].persistent = MICRODVD_PERSISTENT_OPENED;
00223 }
00224 }
00225
00226 static void microdvd_close_no_persistent_tags(AVBPrint *new_line,
00227 struct microdvd_tag *tags)
00228 {
00229 int i, sidx;
00230
00231 for (i = sizeof(MICRODVD_TAGS) - 2; i >= 0; i--) {
00232 if (tags[i].persistent != MICRODVD_PERSISTENT_OFF)
00233 continue;
00234 switch (tags[i].key) {
00235
00236 case 'y':
00237 for (sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--)
00238 if (tags[i].data1 & (1 << sidx))
00239 av_bprintf(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]);
00240 break;
00241
00242 case 'c':
00243 av_bprintf(new_line, "{\\c}");
00244 break;
00245
00246 case 'f':
00247 av_bprintf(new_line, "{\\fn}");
00248 break;
00249
00250 case 's':
00251 av_bprintf(new_line, "{\\fs}");
00252 break;
00253 }
00254 tags[i].key = 0;
00255 }
00256 }
00257
00258 static int microdvd_decode_frame(AVCodecContext *avctx,
00259 void *data, int *got_sub_ptr, AVPacket *avpkt)
00260 {
00261 AVSubtitle *sub = data;
00262 AVBPrint new_line;
00263 char *decoded_sub;
00264 char *line = avpkt->data;
00265 char *end = avpkt->data + avpkt->size;
00266 struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
00267
00268 if (avpkt->size <= 0)
00269 return avpkt->size;
00270
00271 av_bprint_init(&new_line, 0, 2048);
00272
00273
00274 line = strchr(line, '}'); if (!line) goto end; line++;
00275 line = strchr(line, '}'); if (!line) goto end; line++;
00276
00277
00278 while (line < end && *line) {
00279
00280
00281 line = microdvd_load_tags(tags, line);
00282 microdvd_open_tags(&new_line, tags);
00283
00284
00285 while (line < end && *line && *line != '|') {
00286 av_bprint_chars(&new_line, *line, 1);
00287 line++;
00288 }
00289
00290
00291 if (line < end && *line == '|') {
00292 microdvd_close_no_persistent_tags(&new_line, tags);
00293 av_bprintf(&new_line, "\\N");
00294 line++;
00295 }
00296 }
00297
00298 end:
00299 av_bprint_finalize(&new_line, &decoded_sub);
00300 if (*decoded_sub) {
00301 int64_t start = avpkt->pts;
00302 int64_t duration = avpkt->duration;
00303 int ts_start = av_rescale_q(start, avctx->time_base, (AVRational){1,100});
00304 int ts_duration = duration != -1 ?
00305 av_rescale_q(duration, avctx->time_base, (AVRational){1,100}) : -1;
00306 ff_ass_add_rect(sub, decoded_sub, ts_start, ts_duration, 0);
00307 }
00308 av_free(decoded_sub);
00309
00310 *got_sub_ptr = sub->num_rects > 0;
00311 return avpkt->size;
00312 }
00313
00314 static int microdvd_init(AVCodecContext *avctx)
00315 {
00316 int i, sidx;
00317 AVBPrint font_buf;
00318 int font_size = ASS_DEFAULT_FONT_SIZE;
00319 int color = ASS_DEFAULT_COLOR;
00320 int bold = ASS_DEFAULT_BOLD;
00321 int italic = ASS_DEFAULT_ITALIC;
00322 int underline = ASS_DEFAULT_UNDERLINE;
00323 int alignment = ASS_DEFAULT_ALIGNMENT;
00324 struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
00325
00326 av_bprint_init(&font_buf, 0, AV_BPRINT_SIZE_AUTOMATIC);
00327 av_bprintf(&font_buf, "%s", ASS_DEFAULT_FONT);
00328
00329 if (avctx->extradata) {
00330 microdvd_load_tags(tags, avctx->extradata);
00331 for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
00332 switch (av_tolower(tags[i].key)) {
00333 case 'y':
00334 for (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++) {
00335 if (tags[i].data1 & (1 << sidx)) {
00336 switch (MICRODVD_STYLES[sidx]) {
00337 case 'i': italic = 1; break;
00338 case 'b': bold = 1; break;
00339 case 'u': underline = 1; break;
00340 }
00341 }
00342 }
00343 break;
00344
00345 case 'c': color = tags[i].data1; break;
00346 case 's': font_size = tags[i].data1; break;
00347 case 'p': alignment = 8; break;
00348
00349 case 'f':
00350 av_bprint_clear(&font_buf);
00351 av_bprintf(&font_buf, "%.*s",
00352 tags[i].data_string_len, tags[i].data_string);
00353 break;
00354 }
00355 }
00356 }
00357 return ff_ass_subtitle_header(avctx, font_buf.str, font_size, color,
00358 ASS_DEFAULT_BACK_COLOR, bold, italic,
00359 underline, alignment);
00360 }
00361
00362 AVCodec ff_microdvd_decoder = {
00363 .name = "microdvd",
00364 .long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle"),
00365 .type = AVMEDIA_TYPE_SUBTITLE,
00366 .id = AV_CODEC_ID_MICRODVD,
00367 .init = microdvd_init,
00368 .decode = microdvd_decode_frame,
00369 };