annotate ffmpeg/libavcodec/microdvddec.c @ 13:844d341cf643 tip

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