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 };