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 tag.data1 = strtol(s, &s, 16) & 0x00ffffff;
00105 if (*s != '}')
00106 break;
00107 tag.key = 'c';
00108 break;
00109
00110
00111 case 'F':
00112 tag.persistent = MICRODVD_PERSISTENT_ON;
00113 case 'f': {
00114 int len = indexof(s, '}');
00115 if (len < 0)
00116 break;
00117 tag.data_string = s;
00118 tag.data_string_len = len;
00119 s += len;
00120 tag.key = 'f';
00121 break;
00122 }
00123
00124
00125 case 'S':
00126 tag.persistent = MICRODVD_PERSISTENT_ON;
00127 case 's':
00128 tag.data1 = strtol(s, &s, 10);
00129 if (*s != '}')
00130 break;
00131 tag.key = 's';
00132 break;
00133
00134
00135 case 'H': {
00136
00137 int len = indexof(s, '}');
00138 if (len < 0)
00139 break;
00140 tag.data_string = s;
00141 tag.data_string_len = len;
00142 s += len;
00143 tag.key = 'h';
00144 break;
00145 }
00146
00147
00148 case 'P':
00149 tag.persistent = MICRODVD_PERSISTENT_ON;
00150 tag.data1 = (*s++ == '1');
00151 if (*s != '}')
00152 break;
00153 tag.key = 'p';
00154 break;
00155
00156
00157 case 'o':
00158 tag.persistent = MICRODVD_PERSISTENT_ON;
00159 tag.data1 = strtol(s, &s, 10);
00160 if (*s != ',')
00161 break;
00162 s++;
00163 tag.data2 = strtol(s, &s, 10);
00164 if (*s != '}')
00165 break;
00166 tag.key = 'o';
00167 break;
00168
00169 default:
00170 break;
00171 }
00172
00173 if (tag.key == 0)
00174 return start;
00175
00176 microdvd_set_tag(tags, tag);
00177 s++;
00178 }
00179 return s;
00180 }
00181
00182 static void microdvd_open_tags(AVBPrint *new_line, struct microdvd_tag *tags)
00183 {
00184 int i, sidx;
00185 for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
00186 if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED)
00187 continue;
00188 switch (tags[i].key) {
00189 case 'Y':
00190 case 'y':
00191 for (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++)
00192 if (tags[i].data1 & (1 << sidx))
00193 av_bprintf(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]);
00194 break;
00195
00196 case 'c':
00197 av_bprintf(new_line, "{\\c&H%06X&}", tags[i].data1);
00198 break;
00199
00200 case 'f':
00201 av_bprintf(new_line, "{\\fn%.*s}",
00202 tags[i].data_string_len, tags[i].data_string);
00203 break;
00204
00205 case 's':
00206 av_bprintf(new_line, "{\\fs%d}", tags[i].data1);
00207 break;
00208
00209 case 'p':
00210 if (tags[i].data1 == 0)
00211 av_bprintf(new_line, "{\\an8}");
00212 break;
00213
00214 case 'o':
00215 av_bprintf(new_line, "{\\pos(%d,%d)}",
00216 tags[i].data1, tags[i].data2);
00217 break;
00218 }
00219 if (tags[i].persistent == MICRODVD_PERSISTENT_ON)
00220 tags[i].persistent = MICRODVD_PERSISTENT_OPENED;
00221 }
00222 }
00223
00224 static void microdvd_close_no_persistent_tags(AVBPrint *new_line,
00225 struct microdvd_tag *tags)
00226 {
00227 int i, sidx;
00228
00229 for (i = sizeof(MICRODVD_TAGS) - 2; i; i--) {
00230 if (tags[i].persistent != MICRODVD_PERSISTENT_OFF)
00231 continue;
00232 switch (tags[i].key) {
00233
00234 case 'y':
00235 for (sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--)
00236 if (tags[i].data1 & (1 << sidx))
00237 av_bprintf(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]);
00238 break;
00239
00240 case 'c':
00241 av_bprintf(new_line, "{\\c}");
00242 break;
00243
00244 case 'f':
00245 av_bprintf(new_line, "{\\fn}");
00246 break;
00247
00248 case 's':
00249 av_bprintf(new_line, "{\\fs}");
00250 break;
00251 }
00252 tags[i].key = 0;
00253 }
00254 }
00255
00256 static int microdvd_decode_frame(AVCodecContext *avctx,
00257 void *data, int *got_sub_ptr, AVPacket *avpkt)
00258 {
00259 AVSubtitle *sub = data;
00260 AVBPrint new_line;
00261 char *decoded_sub;
00262 char *line = avpkt->data;
00263 char *end = avpkt->data + avpkt->size;
00264 int64_t frame_start = avpkt->pts;
00265 int64_t frame_end = avpkt->pts + avpkt->duration;
00266 int ts_start = av_rescale_q(frame_start, avctx->time_base, (AVRational){1,100});
00267 int ts_end = av_rescale_q(frame_end, avctx->time_base, (AVRational){1,100});
00268 struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
00269
00270 if (avpkt->size <= 0)
00271 return avpkt->size;
00272
00273 av_bprint_init(&new_line, 0, 2048);
00274
00275
00276 line = strchr(line, '}'); if (!line) goto end; line++;
00277 line = strchr(line, '}'); if (!line) goto end; line++;
00278
00279
00280 while (line < end && *line) {
00281
00282
00283 line = microdvd_load_tags(tags, line);
00284 microdvd_open_tags(&new_line, tags);
00285
00286
00287 while (line < end && *line && *line != '|') {
00288 av_bprint_chars(&new_line, *line, 1);
00289 line++;
00290 }
00291
00292
00293 if (line < end && *line == '|') {
00294 microdvd_close_no_persistent_tags(&new_line, tags);
00295 av_bprintf(&new_line, "\\N");
00296 line++;
00297 }
00298 }
00299
00300 end:
00301 av_bprint_finalize(&new_line, &decoded_sub);
00302 if (*decoded_sub)
00303 ff_ass_add_rect(sub, decoded_sub, ts_start, ts_end, 0);
00304 av_free(decoded_sub);
00305
00306 *got_sub_ptr = sub->num_rects > 0;
00307 return avpkt->size;
00308 }
00309
00310 AVCodec ff_microdvd_decoder = {
00311 .name = "microdvd",
00312 .long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle"),
00313 .type = AVMEDIA_TYPE_SUBTITLE,
00314 .id = CODEC_ID_MICRODVD,
00315 .init = ff_ass_subtitle_header_default,
00316 .decode = microdvd_decode_frame,
00317 };