1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-12-23 12:43:46 +02:00

add SubRip decoder

Originally committed as revision 26119 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
Aurelien Jacobs 2010-12-28 23:52:53 +00:00
parent 312056c54e
commit 2c77c90684
11 changed files with 335 additions and 3 deletions

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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));

View File

@ -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().
* *

View File

@ -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, \

240
libavcodec/srtdec.c Normal file
View File

@ -0,0 +1,240 @@
/*
* 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,
};

View File

@ -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

1
tests/ref/fate/sub-srt Normal file
View File

@ -0,0 +1 @@
03b2a3f7e7e83624c8e4d1b5569df758