yading@10: /* yading@10: * SubRip subtitle decoder yading@10: * Copyright (c) 2010 Aurelien Jacobs yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: #include "libavutil/avstring.h" yading@10: #include "libavutil/common.h" yading@10: #include "libavutil/intreadwrite.h" yading@10: #include "libavutil/parseutils.h" yading@10: #include "avcodec.h" yading@10: #include "ass.h" yading@10: yading@10: static int html_color_parse(AVCodecContext *avctx, const char *str) yading@10: { yading@10: uint8_t rgba[4]; yading@10: if (av_parse_color(rgba, str, strcspn(str, "\" >"), avctx) < 0) yading@10: return -1; yading@10: return rgba[0] | rgba[1] << 8 | rgba[2] << 16; yading@10: } yading@10: yading@10: enum { yading@10: PARAM_UNKNOWN = -1, yading@10: PARAM_SIZE, yading@10: PARAM_COLOR, yading@10: PARAM_FACE, yading@10: PARAM_NUMBER yading@10: }; yading@10: yading@10: typedef struct { yading@10: char tag[128]; yading@10: char param[PARAM_NUMBER][128]; yading@10: } SrtStack; yading@10: yading@10: static const char *srt_to_ass(AVCodecContext *avctx, char *out, char *out_end, yading@10: const char *in, int x1, int y1, int x2, int y2) yading@10: { yading@10: char *param, buffer[128], tmp[128]; yading@10: int len, tag_close, sptr = 1, line_start = 1, an = 0, end = 0; yading@10: SrtStack stack[16]; yading@10: yading@10: stack[0].tag[0] = 0; yading@10: strcpy(stack[0].param[PARAM_SIZE], "{\\fs}"); yading@10: strcpy(stack[0].param[PARAM_COLOR], "{\\c}"); yading@10: strcpy(stack[0].param[PARAM_FACE], "{\\fn}"); yading@10: yading@10: if (x1 >= 0 && y1 >= 0) { yading@10: if (x2 >= 0 && y2 >= 0 && (x2 != x1 || y2 != y1)) yading@10: snprintf(out, out_end-out, yading@10: "{\\an1}{\\move(%d,%d,%d,%d)}", x1, y1, x2, y2); yading@10: else yading@10: snprintf(out, out_end-out, "{\\an1}{\\pos(%d,%d)}", x1, y1); yading@10: out += strlen(out); yading@10: } yading@10: yading@10: for (; out < out_end && !end && *in; in++) { yading@10: switch (*in) { yading@10: case '\r': yading@10: break; yading@10: case '\n': yading@10: if (line_start) { yading@10: end = 1; yading@10: break; yading@10: } yading@10: while (out[-1] == ' ') yading@10: out--; yading@10: snprintf(out, out_end-out, "\\N"); yading@10: if(out= 0 && len > 0; yading@10: if ((an != 1 && (len = 0, sscanf(in, "{\\%*[^}]}%n", &len) >= 0 && len > 0)) || yading@10: (len = 0, sscanf(in, "{%*1[CcFfoPSsYy]:%*[^}]}%n", &len) >= 0 && len > 0)) { yading@10: in += len - 1; yading@10: } else yading@10: *out++ = *in; yading@10: break; yading@10: case '<': yading@10: tag_close = in[1] == '/'; yading@10: len = 0; yading@10: if (sscanf(in+tag_close+1, "%127[^>]>%n", buffer, &len) >= 1 && len > 0) { yading@10: if ((param = strchr(buffer, ' '))) yading@10: *param++ = 0; yading@10: if ((!tag_close && sptr < FF_ARRAY_ELEMS(stack)) || yading@10: ( tag_close && sptr > 0 && !strcmp(stack[sptr-1].tag, buffer))) { yading@10: int i, j, unknown = 0; yading@10: in += len + tag_close; yading@10: if (!tag_close) yading@10: memset(stack+sptr, 0, sizeof(*stack)); yading@10: if (!strcmp(buffer, "font")) { yading@10: if (tag_close) { yading@10: for (i=PARAM_NUMBER-1; i>=0; i--) yading@10: if (stack[sptr-1].param[i][0]) yading@10: for (j=sptr-2; j>=0; j--) yading@10: if (stack[j].param[i][0]) { yading@10: snprintf(out, out_end-out, yading@10: "%s", stack[j].param[i]); yading@10: if(out", buffer); yading@10: } yading@10: if (tag_close) { yading@10: sptr--; yading@10: } else if (unknown && !strstr(in, tmp)) { yading@10: in -= len + tag_close; yading@10: *out++ = *in; yading@10: } else yading@10: av_strlcpy(stack[sptr++].tag, buffer, yading@10: sizeof(stack[0].tag)); yading@10: break; yading@10: } yading@10: } yading@10: default: yading@10: *out++ = *in; yading@10: break; yading@10: } yading@10: if (*in != ' ' && *in != '\r' && *in != '\n') yading@10: line_start = 0; yading@10: } yading@10: yading@10: out = FFMIN(out, out_end-3); yading@10: while (!strncmp(out-2, "\\N", 2)) yading@10: out -= 2; yading@10: while (out[-1] == ' ') yading@10: out--; yading@10: snprintf(out, out_end-out, "\r\n"); yading@10: return in; yading@10: } yading@10: yading@10: static const char *read_ts(const char *buf, int *ts_start, int *ts_end, yading@10: int *x1, int *y1, int *x2, int *y2) yading@10: { yading@10: int i, hs, ms, ss, he, me, se; yading@10: yading@10: for (i=0; i<2; i++) { yading@10: /* try to read timestamps in either the first or second line */ yading@10: int c = sscanf(buf, "%d:%2d:%2d%*1[,.]%3d --> %d:%2d:%2d%*1[,.]%3d" yading@10: "%*[ ]X1:%u X2:%u Y1:%u Y2:%u", yading@10: &hs, &ms, &ss, ts_start, &he, &me, &se, ts_end, yading@10: x1, x2, y1, y2); yading@10: buf += strcspn(buf, "\n") + 1; yading@10: if (c >= 8) { yading@10: *ts_start = 100*(ss + 60*(ms + 60*hs)) + *ts_start/10; yading@10: *ts_end = 100*(se + 60*(me + 60*he)) + *ts_end /10; yading@10: return buf; yading@10: } yading@10: } yading@10: return NULL; yading@10: } yading@10: yading@10: static int srt_decode_frame(AVCodecContext *avctx, yading@10: void *data, int *got_sub_ptr, AVPacket *avpkt) yading@10: { yading@10: AVSubtitle *sub = data; yading@10: int ts_start, ts_end, x1 = -1, y1 = -1, x2 = -1, y2 = -1; yading@10: char buffer[2048]; yading@10: const char *ptr = avpkt->data; yading@10: const char *end = avpkt->data + avpkt->size; yading@10: int size; yading@10: const uint8_t *p = av_packet_get_side_data(avpkt, AV_PKT_DATA_SUBTITLE_POSITION, &size); yading@10: yading@10: if (p && size == 16) { yading@10: x1 = AV_RL32(p ); yading@10: y1 = AV_RL32(p + 4); yading@10: x2 = AV_RL32(p + 8); yading@10: y2 = AV_RL32(p + 12); yading@10: } yading@10: yading@10: if (avpkt->size <= 0) yading@10: return avpkt->size; yading@10: yading@10: while (ptr < end && *ptr) { yading@10: if (avctx->codec->id == AV_CODEC_ID_SRT) { yading@10: ptr = read_ts(ptr, &ts_start, &ts_end, &x1, &y1, &x2, &y2); yading@10: if (!ptr) yading@10: break; yading@10: } else { yading@10: // Do final divide-by-10 outside rescale to force rounding down. yading@10: ts_start = av_rescale_q(avpkt->pts, yading@10: avctx->time_base, yading@10: (AVRational){1,100}); yading@10: ts_end = av_rescale_q(avpkt->pts + avpkt->duration, yading@10: avctx->time_base, yading@10: (AVRational){1,100}); yading@10: } yading@10: ptr = srt_to_ass(avctx, buffer, buffer+sizeof(buffer), ptr, yading@10: x1, y1, x2, y2); yading@10: ff_ass_add_rect(sub, buffer, ts_start, ts_end-ts_start, 0); yading@10: } yading@10: yading@10: *got_sub_ptr = sub->num_rects > 0; yading@10: return avpkt->size; yading@10: } yading@10: yading@10: #if CONFIG_SRT_DECODER yading@10: /* deprecated decoder */ yading@10: AVCodec ff_srt_decoder = { yading@10: .name = "srt", yading@10: .long_name = NULL_IF_CONFIG_SMALL("SubRip subtitle with embedded timing"), yading@10: .type = AVMEDIA_TYPE_SUBTITLE, yading@10: .id = AV_CODEC_ID_SRT, yading@10: .init = ff_ass_subtitle_header_default, yading@10: .decode = srt_decode_frame, yading@10: }; yading@10: #endif yading@10: yading@10: #if CONFIG_SUBRIP_DECODER yading@10: AVCodec ff_subrip_decoder = { yading@10: .name = "subrip", yading@10: .long_name = NULL_IF_CONFIG_SMALL("SubRip subtitle"), yading@10: .type = AVMEDIA_TYPE_SUBTITLE, yading@10: .id = AV_CODEC_ID_SUBRIP, yading@10: .init = ff_ass_subtitle_header_default, yading@10: .decode = srt_decode_frame, yading@10: }; yading@10: #endif