Commit 2c77c906 authored by Aurelien Jacobs's avatar Aurelien Jacobs

add SubRip decoder

Originally committed as revision 26119 to svn://svn.ffmpeg.org/ffmpeg/trunk
parent 312056c5
...@@ -69,6 +69,7 @@ version <next>: ...@@ -69,6 +69,7 @@ version <next>:
- replace the ocv_smooth filter with a more generic ocv filter - replace the ocv_smooth filter with a more generic ocv filter
- Windows Televison (WTV) demuxer - Windows Televison (WTV) demuxer
- FFmpeg metadata format muxer and demuxer - FFmpeg metadata format muxer and demuxer
- SubRip (srt) subtitle decoder
version 0.6: version 0.6:
......
...@@ -198,6 +198,7 @@ Codecs: ...@@ -198,6 +198,7 @@ Codecs:
smc.c Mike Melanson smc.c Mike Melanson
snow.c Michael Niedermayer, Loren Merritt snow.c Michael Niedermayer, Loren Merritt
sonic.c Alex Beregszaszi sonic.c Alex Beregszaszi
srtdec.c Aurelien Jacobs
sunrast.c Ivo van Poorten sunrast.c Ivo van Poorten
svq3.c Michael Niedermayer svq3.c Michael Niedermayer
targa.c Kostya Shishkov targa.c Kostya Shishkov
......
...@@ -694,7 +694,7 @@ performance on systems without hardware floating point support). ...@@ -694,7 +694,7 @@ performance on systems without hardware floating point support).
@item DVB @tab X @tab X @tab X @tab X @item DVB @tab X @tab X @tab X @tab X
@item DVD @tab X @tab X @tab X @tab X @item DVD @tab X @tab X @tab X @tab X
@item PGS @tab @tab @tab @tab X @item PGS @tab @tab @tab @tab X
@item SubRip (SRT) @tab X @tab X @item SubRip (SRT) @tab X @tab X @tab @tab X
@item XSUB @tab @tab @tab X @tab X @item XSUB @tab @tab @tab X @tab X
@end multitable @end multitable
......
...@@ -337,6 +337,7 @@ OBJS-$(CONFIG_SONIC_DECODER) += sonic.o ...@@ -337,6 +337,7 @@ OBJS-$(CONFIG_SONIC_DECODER) += sonic.o
OBJS-$(CONFIG_SONIC_ENCODER) += sonic.o OBJS-$(CONFIG_SONIC_ENCODER) += sonic.o
OBJS-$(CONFIG_SONIC_LS_ENCODER) += sonic.o OBJS-$(CONFIG_SONIC_LS_ENCODER) += sonic.o
OBJS-$(CONFIG_SP5X_DECODER) += sp5xdec.o mjpegdec.o mjpeg.o OBJS-$(CONFIG_SP5X_DECODER) += sp5xdec.o mjpegdec.o mjpeg.o
OBJS-$(CONFIG_SRT_DECODER) += srtdec.o ass.o
OBJS-$(CONFIG_SUNRAST_DECODER) += sunrast.o OBJS-$(CONFIG_SUNRAST_DECODER) += sunrast.o
OBJS-$(CONFIG_SVQ1_DECODER) += svq1dec.o svq1.o h263.o \ OBJS-$(CONFIG_SVQ1_DECODER) += svq1dec.o svq1.o h263.o \
mpegvideo.o error_resilience.o mpegvideo.o error_resilience.o
......
...@@ -347,6 +347,7 @@ void avcodec_register_all(void) ...@@ -347,6 +347,7 @@ void avcodec_register_all(void)
REGISTER_ENCDEC (DVBSUB, dvbsub); REGISTER_ENCDEC (DVBSUB, dvbsub);
REGISTER_ENCDEC (DVDSUB, dvdsub); REGISTER_ENCDEC (DVDSUB, dvdsub);
REGISTER_DECODER (PGSSUB, pgssub); REGISTER_DECODER (PGSSUB, pgssub);
REGISTER_DECODER (SRT, srt);
REGISTER_ENCDEC (XSUB, xsub); REGISTER_ENCDEC (XSUB, xsub);
/* external libraries */ /* external libraries */
......
...@@ -22,6 +22,46 @@ ...@@ -22,6 +22,46 @@
#include "avcodec.h" #include "avcodec.h"
#include "ass.h" #include "ass.h"
int ff_ass_subtitle_header(AVCodecContext *avctx,
const char *font, int font_size,
int color, int back_color,
int bold, int italic, int underline,
int alignment)
{
char header[512];
snprintf(header, sizeof(header),
"[Script Info]\r\n"
"ScriptType: v4.00+\r\n"
"\r\n"
"[V4+ Styles]\r\n"
"Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding\r\n"
"Style: Default,%s,%d,&H%x,&H%x,&H%x,&H%x,%d,%d,%d,1,1,0,%d,10,10,10,0,0\r\n"
"\r\n"
"[Events]\r\n"
"Format: Layer, Start, End, Text\r\n",
font, font_size, color, color, back_color, back_color,
-bold, -italic, -underline, alignment);
avctx->subtitle_header = av_strdup(header);
if (!avctx->subtitle_header)
return AVERROR(ENOMEM);
avctx->subtitle_header_size = strlen(avctx->subtitle_header);
return 0;
}
int ff_ass_subtitle_header_default(AVCodecContext *avctx)
{
return ff_ass_subtitle_header(avctx, ASS_DEFAULT_FONT,
ASS_DEFAULT_FONT_SIZE,
ASS_DEFAULT_COLOR,
ASS_DEFAULT_BACK_COLOR,
ASS_DEFAULT_BOLD,
ASS_DEFAULT_ITALIC,
ASS_DEFAULT_UNDERLINE,
ASS_DEFAULT_ALIGNMENT);
}
void ff_ass_init(AVSubtitle *sub) void ff_ass_init(AVSubtitle *sub)
{ {
memset(sub, 0, sizeof(*sub)); memset(sub, 0, sizeof(*sub));
......
...@@ -24,6 +24,51 @@ ...@@ -24,6 +24,51 @@
#include "avcodec.h" #include "avcodec.h"
/**
* Default values for ASS style.
* @defgroup ass_default
* @{
*/
#define ASS_DEFAULT_FONT "Arial"
#define ASS_DEFAULT_FONT_SIZE 16
#define ASS_DEFAULT_COLOR 0xffffff
#define ASS_DEFAULT_BACK_COLOR 0
#define ASS_DEFAULT_BOLD 0
#define ASS_DEFAULT_ITALIC 0
#define ASS_DEFAULT_UNDERLINE 0
#define ASS_DEFAULT_ALIGNMENT 2
/** @} */
/**
* Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
*
* @param avctx pointer to the AVCodecContext
* @param font name of the default font face to use
* @param font_size default font size to use
* @param color default text color to use (ABGR)
* @param back_color default background color to use (ABGR)
* @param bold 1 for bold text, 0 for normal text
* @param italic 1 for italic text, 0 for normal text
* @param underline 1 for underline text, 0 for normal text
* @param alignment position of the text (left, center, top...), defined after
* the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top)
* @return >= 0 on success otherwise an error code <0
*/
int ff_ass_subtitle_header(AVCodecContext *avctx,
const char *font, int font_size,
int color, int back_color,
int bold, int italic, int underline,
int alignment);
/**
* Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS
* with default style.
*
* @param avctx pointer to the AVCodecContext
* @return >= 0 on success otherwise an error code <0
*/
int ff_ass_subtitle_header_default(AVCodecContext *avctx);
/** /**
* Initialize an AVSubtitle structure for use with ff_ass_add_rect(). * Initialize an AVSubtitle structure for use with ff_ass_add_rect().
* *
......
...@@ -32,8 +32,8 @@ ...@@ -32,8 +32,8 @@
#include "libavutil/cpu.h" #include "libavutil/cpu.h"
#define LIBAVCODEC_VERSION_MAJOR 52 #define LIBAVCODEC_VERSION_MAJOR 52
#define LIBAVCODEC_VERSION_MINOR 100 #define LIBAVCODEC_VERSION_MINOR 101
#define LIBAVCODEC_VERSION_MICRO 1 #define LIBAVCODEC_VERSION_MICRO 0
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
LIBAVCODEC_VERSION_MINOR, \ LIBAVCODEC_VERSION_MINOR, \
......
/*
* SubRip subtitle decoder
* Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.org>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "libavutil/avstring.h"
#include "libavcore/parseutils.h"
#include "avcodec.h"
#include "ass.h"
static int html_color_parse(AVCodecContext *avctx, const char *str)
{
uint8_t rgba[4];
if (av_parse_color(rgba, str, strcspn(str, "\" >"), avctx) < 0)
return -1;
return rgba[0] | rgba[1] << 8 | rgba[2] << 16;
}
enum {
PARAM_UNKNOWN = -1,
PARAM_SIZE,
PARAM_COLOR,
PARAM_FACE,
PARAM_NUMBER
};
typedef struct {
char tag[128];
char param[PARAM_NUMBER][128];
} SrtStack;
static const char *srt_to_ass(AVCodecContext *avctx, char *out, char *out_end,
const char *in, int x1, int y1, int x2, int y2)
{
char c, *param, buffer[128], tmp[128];
int len, tag_close, sptr = 1, line_start = 1, an = 0, end = 0;
SrtStack stack[16];
stack[0].tag[0] = 0;
strcpy(stack[0].param[PARAM_SIZE], "{\\fs}");
strcpy(stack[0].param[PARAM_COLOR], "{\\c}");
strcpy(stack[0].param[PARAM_FACE], "{\\fn}");
if (x1 >= 0 && y1 >= 0) {
if (x2 >= 0 && y2 >= 0 && (x2 != x1 || y2 != y1))
out += snprintf(out, out_end-out,
"{\\an1}{\\move(%d,%d,%d,%d)}", x1, y1, x2, y2);
else
out += snprintf(out, out_end-out, "{\\an1}{\\pos(%d,%d)}", x1, y1);
}
for (; *in && out < out_end && !end; in++) {
switch (*in) {
case '\r':
break;
case '\n':
if (line_start) {
end = 1;
break;
}
while (out[-1] == ' ')
out--;
out += snprintf(out, out_end-out, "\\N");
line_start = 1;
break;
case ' ':
if (!line_start)
*out++ = *in;
break;
case '{': /* skip all {\xxx} substrings except for {\an%d}
and all microdvd like styles such as {Y:xxx} */
an += sscanf(in, "{\\an%*1u}%c", &c) == 1;
if ((an != 1 && sscanf(in, "{\\%*[^}]}%n%c", &len, &c) > 0) ||
sscanf(in, "{%*1[CcFfoPSsYy]:%*[^}]}%n%c", &len, &c) > 0) {
in += len - 1;
} else
*out++ = *in;
break;
case '<':
tag_close = in[1] == '/';
if (sscanf(in+tag_close+1, "%128[^>]>%n%c", buffer, &len,&c) >= 2) {
if ((param = strchr(buffer, ' ')))
*param++ = 0;
if ((!tag_close && sptr < FF_ARRAY_ELEMS(stack)) ||
( tag_close && sptr > 0 && !strcmp(stack[sptr-1].tag, buffer))) {
int i, j, unknown = 0;
in += len + tag_close;
if (!tag_close)
memset(stack+sptr, 0, sizeof(*stack));
if (!strcmp(buffer, "font")) {
if (tag_close) {
for (i=PARAM_NUMBER-1; i>=0; i--)
if (stack[sptr-1].param[i][0])
for (j=sptr-2; j>=0; j--)
if (stack[j].param[i][0]) {
out += snprintf(out, out_end-out,
stack[j].param[i]);
break;
}
} else {
while (param) {
if (!strncmp(param, "size=", 5)) {
unsigned font_size;
param += 5 + (param[5] == '"');
if (sscanf(param, "%u", &font_size) == 1) {
snprintf(stack[sptr].param[PARAM_SIZE],
sizeof(stack[0].param[PARAM_SIZE]),
"{\\fs%u}", font_size);
}
} else if (!strncmp(param, "color=", 6)) {
param += 6 + (param[6] == '"');
snprintf(stack[sptr].param[PARAM_COLOR],
sizeof(stack[0].param[PARAM_COLOR]),
"{\\c&H%X&}",
html_color_parse(avctx, param));
} else if (!strncmp(param, "face=", 5)) {
param += 5 + (param[5] == '"');
len = strcspn(param,
param[-1] == '"' ? "\"" :" ");
av_strlcpy(tmp, param,
FFMIN(sizeof(tmp), len+1));
param += len;
snprintf(stack[sptr].param[PARAM_FACE],
sizeof(stack[0].param[PARAM_FACE]),
"{\\fn%s}", tmp);
}
if ((param = strchr(param, ' ')))
param++;
}
for (i=0; i<PARAM_NUMBER; i++)
if (stack[sptr].param[i][0])
out += snprintf(out, out_end-out,
stack[sptr].param[i]);
}
} else if (!buffer[1] && strspn(buffer, "bisu") == 1) {
out += snprintf(out, out_end-out,
"{\\%c%d}", buffer[0], !tag_close);
} else {
unknown = 1;
snprintf(tmp, sizeof(tmp), "</%s>", buffer);
}
if (tag_close) {
sptr--;
} else if (unknown && !strstr(in, tmp)) {
in -= len + tag_close;
*out++ = *in;
} else
av_strlcpy(stack[sptr++].tag, buffer,
sizeof(stack[0].tag));
break;
}
}
default:
*out++ = *in;
break;
}
if (*in != ' ' && *in != '\r' && *in != '\n')
line_start = 0;
}
out = FFMIN(out, out_end-3);
while (!strncmp(out-2, "\\N", 2))
out -= 2;
while (out[-1] == ' ')
out--;
out += snprintf(out, out_end-out, "\r\n");
return in;
}
static const char *read_ts(const char *buf, int *ts_start, int *ts_end,
int *x1, int *y1, int *x2, int *y2)
{
int i, hs, ms, ss, he, me, se;
for (i=0; i<2; i++) {
/* try to read timestamps in either the first or second line */
int c = sscanf(buf, "%d:%2d:%2d%*1[,.]%3d --> %d:%2d:%2d%*1[,.]%3d"
"%*[ ]X1:%u X2:%u Y1:%u Y2:%u",
&hs, &ms, &ss, ts_start, &he, &me, &se, ts_end,
x1, x2, y1, y2);
buf += strcspn(buf, "\n") + 1;
if (c >= 8) {
*ts_start = 100*(ss + 60*(ms + 60*hs)) + *ts_start/10;
*ts_end = 100*(se + 60*(me + 60*he)) + *ts_end /10;
return buf;
}
}
return NULL;
}
static int srt_decode_frame(AVCodecContext *avctx,
void *data, int *got_sub_ptr, AVPacket *avpkt)
{
AVSubtitle *sub = data;
int ts_start, ts_end, x1 = -1, y1 = -1, x2 = -1, y2 = -1;
char buffer[2048];
const char *ptr = avpkt->data;
if (avpkt->size <= 0)
return avpkt->size;
ff_ass_init(sub);
while (*ptr) {
ptr = read_ts(ptr, &ts_start, &ts_end, &x1, &y1, &x2, &y2);
if (!ptr)
break;
ptr = srt_to_ass(avctx, buffer, buffer+sizeof(buffer), ptr,
x1, y1, x2, y2);
ff_ass_add_rect(sub, buffer, ts_start, ts_end, 0);
}
*got_sub_ptr = sub->num_rects > 0;
return avpkt->size;
}
AVCodec srt_decoder = {
.name = "srt",
.long_name = NULL_IF_CONFIG_SMALL("SubRip subtitle"),
.type = AVMEDIA_TYPE_SUBTITLE,
.id = CODEC_ID_SRT,
.init = ff_ass_subtitle_header_default,
.decode = srt_decode_frame,
};
...@@ -282,6 +282,8 @@ FATE_TESTS += fate-smc ...@@ -282,6 +282,8 @@ FATE_TESTS += fate-smc
fate-smc: CMD = framecrc -i $(SAMPLES)/smc/cass_schi.qt -vsync 0 -pix_fmt rgb24 fate-smc: CMD = framecrc -i $(SAMPLES)/smc/cass_schi.qt -vsync 0 -pix_fmt rgb24
FATE_TESTS += fate-sp5x FATE_TESTS += fate-sp5x
fate-sp5x: CMD = framecrc -idct simple -i $(SAMPLES)/sp5x/sp5x_problem.avi fate-sp5x: CMD = framecrc -idct simple -i $(SAMPLES)/sp5x/sp5x_problem.avi
FATE_TESTS += fate-sub-srt
fate-sub-srt: CMD = md5 -i $(SAMPLES)/sub/SubRip_capability_tester.srt -f ass
FATE_TESTS += fate-sunraster-1bit-raw FATE_TESTS += fate-sunraster-1bit-raw
fate-sunraster-1bit-raw: CMD = framecrc -i $(SAMPLES)/sunraster/lena-1bit-raw.sun fate-sunraster-1bit-raw: CMD = framecrc -i $(SAMPLES)/sunraster/lena-1bit-raw.sun
FATE_TESTS += fate-sunraster-1bit-rle FATE_TESTS += fate-sunraster-1bit-rle
......
03b2a3f7e7e83624c8e4d1b5569df758
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment