Permalink
Browse files

WIP

  • Loading branch information...
1 parent 807fa71 commit 13bf4d6fab9b8c8d4d8080027c9e8bd9c2daec22 @ubitux committed Sep 9, 2012
Showing with 715 additions and 24 deletions.
  1. +63 −3 libavcodec/assenc.c
  2. +8 −0 libavcodec/avcodec.h
  3. +241 −20 libavcodec/srtdec.c
  4. +28 −1 libavformat/assenc.c
  5. +2 −0 libavutil/Makefile
  6. +255 −0 libavutil/subtitles.c
  7. +118 −0 libavutil/subtitles.h
View
66 libavcodec/assenc.c
@@ -23,8 +23,10 @@
#include "avcodec.h"
#include "libavutil/avstring.h"
+#include "libavutil/bprint.h"
#include "libavutil/internal.h"
#include "libavutil/mem.h"
+#include "libavutil/subtitles.h"
static av_cold int ass_encode_init(AVCodecContext *avctx)
{
@@ -36,18 +38,76 @@ static av_cold int ass_encode_init(AVCodecContext *avctx)
return 0;
}
+static void encode_ast_to_ass(AVBPrint *buf, const AVSubtitle *sub)
+{
+ int i;
+ const AVSubtitleAST *ast = sub->rects[0]->ast;
+
+ if (ast->g_settings && ast->g_settings->name) av_bprintf(buf, "%s,", ast->g_settings->name);
+ else av_bprintf(buf, "Default,");
+
+ for (i = 0; i < ast->nb_chunks; i++) {
+ const AVSubtitleASTChunk *c = &ast->chunks[i];
+ int *p = c->p;
+
+ if (c->reset) {
+ switch (c->type) {
+ case AVSUBTITLE_AST_CHUNK_FONTNAME: av_bprintf(buf, "{\\fn}"); break;
+ case AVSUBTITLE_AST_CHUNK_FONTSIZE: av_bprintf(buf, "{\\fs}"); break;
+ case AVSUBTITLE_AST_CHUNK_COLOR: av_bprintf(buf, "{\\c}"); break;
+ case AVSUBTITLE_AST_CHUNK_COLOR_2: av_bprintf(buf, "{\\2c}"); break;
+ case AVSUBTITLE_AST_CHUNK_COLOR_OUTLINE: av_bprintf(buf, "{\\3c}"); break;
+ case AVSUBTITLE_AST_CHUNK_COLOR_BACK: av_bprintf(buf, "{\\4c}"); break;
+ case AVSUBTITLE_AST_CHUNK_BOLD: av_bprintf(buf, "{\\b}"); break;
+ case AVSUBTITLE_AST_CHUNK_ITALIC: av_bprintf(buf, "{\\i}"); break;
+ case AVSUBTITLE_AST_CHUNK_STRIKEOUT: av_bprintf(buf, "{\\s}"); break;
+ case AVSUBTITLE_AST_CHUNK_UNDERLINE: av_bprintf(buf, "{\\u}"); break;
+ case AVSUBTITLE_AST_CHUNK_ALIGNMENT: av_bprintf(buf, "{\\an}"); break;
+ }
+ } else {
+ switch (c->type) {
+ case AVSUBTITLE_AST_CHUNK_RAW_TEXT: av_bprintf(buf, "%s", c->s); break;
+ case AVSUBTITLE_AST_CHUNK_COMMENT: av_bprintf(buf, "{%s}", c->s); break;
+ case AVSUBTITLE_AST_CHUNK_TIMING: av_bprintf(buf, "{\\k%d}", c->i); break;
+ case AVSUBTITLE_AST_CHUNK_FONTNAME: av_bprintf(buf, "{\\fn%s}", c->s); break;
+ case AVSUBTITLE_AST_CHUNK_FONTSIZE: av_bprintf(buf, "{\\fs%d}", c->i); break;
+ case AVSUBTITLE_AST_CHUNK_COLOR: av_bprintf(buf, "{\\c&H%06X&}", c->u32 & 0xffffff); break;
+ case AVSUBTITLE_AST_CHUNK_COLOR_2: av_bprintf(buf, "{\\2c&H%06X&}", c->u32 & 0xffffff); break;
+ case AVSUBTITLE_AST_CHUNK_COLOR_OUTLINE: av_bprintf(buf, "{\\3c&H%06X&}", c->u32 & 0xffffff); break;
+ case AVSUBTITLE_AST_CHUNK_COLOR_BACK: av_bprintf(buf, "{\\4c&H%06X&}", c->u32 & 0xffffff); break;
+ case AVSUBTITLE_AST_CHUNK_BOLD: av_bprintf(buf, "{\\b%d}", c->i); break;
+ case AVSUBTITLE_AST_CHUNK_ITALIC: av_bprintf(buf, "{\\i%d}", c->i); break;
+ case AVSUBTITLE_AST_CHUNK_STRIKEOUT: av_bprintf(buf, "{\\s%d}", c->i); break;
+ case AVSUBTITLE_AST_CHUNK_UNDERLINE: av_bprintf(buf, "{\\u%d}", c->i); break;
+ case AVSUBTITLE_AST_CHUNK_ALIGNMENT: av_bprintf(buf, "{\\an%d}", c->i); break;
+ case AVSUBTITLE_AST_CHUNK_POSITION: av_bprintf(buf, "{\\pos(%d,%d)", p[0], p[1]); break;
+ case AVSUBTITLE_AST_CHUNK_MOVE: av_bprintf(buf, "{\\move(%d,%d,%d,%d)", p[0], p[1], p[2], p[3]); break;
+ case AVSUBTITLE_AST_CHUNK_LINEBREAK: av_bprintf(buf, "\\N"); break;
+ }
+ }
+ }
+}
+
static int ass_encode_frame(AVCodecContext *avctx,
unsigned char *buf, int bufsize,
const AVSubtitle *sub)
{
int i, len, total_len = 0;
for (i=0; i<sub->num_rects; i++) {
- if (sub->rects[i]->type != SUBTITLE_ASS) {
+
+ if (sub->rects[i]->type == SUBTITLE_AST) {
+ AVBPrint assbuf;
+
+ av_log(0,0,"assenc: encode AST\n");
+ av_bprint_init(&assbuf, 0, AV_BPRINT_SIZE_UNLIMITED);
+ encode_ast_to_ass(&assbuf, sub);
+ len = av_strlcpy(buf+total_len, assbuf.str, bufsize-total_len);
+ av_bprint_finalize(&assbuf, NULL);
+ } else if (sub->rects[i]->type != SUBTITLE_ASS) {
av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
return -1;
- }
-
+ } else
len = av_strlcpy(buf+total_len, sub->rects[i]->ass, bufsize-total_len);
if (len > bufsize-total_len-1) {
View
8 libavcodec/avcodec.h
@@ -35,6 +35,7 @@
#include "libavutil/log.h"
#include "libavutil/pixfmt.h"
#include "libavutil/rational.h"
+#include "libavutil/subtitles.h"
#include "libavcodec/version.h"
/**
@@ -3393,6 +3394,11 @@ enum AVSubtitleType {
* authoritative. pict and text fields may contain approximations.
*/
SUBTITLE_ASS,
+
+ /**
+ * Abstract textual representation.
+ */
+ SUBTITLE_AST = 0x1000,
};
#define AV_SUBTITLE_FLAG_FORCED 0x00000001
@@ -3421,6 +3427,8 @@ typedef struct AVSubtitleRect {
char *ass;
int flags;
+
+ AVSubtitleAST *ast;
} AVSubtitleRect;
typedef struct AVSubtitle {
View
261 libavcodec/srtdec.c
@@ -19,10 +19,13 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
+#include "libavutil/bprint.h"
#include "libavutil/common.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/parseutils.h"
+#include "libavutil/subtitles.h"
#include "avcodec.h"
#include "ass.h"
@@ -34,6 +37,190 @@ static int html_color_parse(AVCodecContext *avctx, const char *str)
return rgba[0] | rgba[1] << 8 | rgba[2] << 16;
}
+static void flush_text_chunk(AVSubtitleAST *sub, AVBPrint *buf)
+{
+ AVSubtitleASTChunk chunk = {.type = AVSUBTITLE_AST_CHUNK_RAW_TEXT};
+
+ if (!buf->len || !av_bprint_is_complete(buf))
+ return;
+ chunk.s = av_strdup(buf->str);
+ if (chunk.s)
+ av_sub_ast_add_chunk(sub, chunk);
+ av_bprint_clear(buf);
+}
+
+enum {
+ FONT_HAS_SIZE = 1<<0,
+ FONT_HAS_COLOR = 1<<1,
+ FONT_HAS_FACE = 1<<2,
+};
+
+static AVSubtitleAST *subrip_decode_sub(AVCodecContext *avctx, const char *in,
+ int x1, int y1, int x2, int y2)
+{
+ char c, *param, buffer[128];
+ AVBPrint textbuf;
+ AVSubtitleAST *sub;
+ int len, tag_close, sptr = 0, an = 0;
+ uint8_t stack[64] = {0};
+ AVSubtitleASTChunk lb_chunk = {
+ .i = 1,
+ .type = AVSUBTITLE_AST_CHUNK_LINEBREAK,
+ };
+
+ sub = av_sub_ast_alloc();
+ if (!sub)
+ return NULL;
+
+ av_bprint_init(&textbuf, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+ if (x1 >= 0 && y1 >= 0) {
+ int32_t *pos = av_malloc(4 * sizeof(*pos)); // only 2 used with CHUNK_POS
+
+ if (pos) {
+ AVSubtitleASTChunk pos_chunk = {.p = pos};
+ AVSubtitleASTChunk align_chunk = {
+ .type = AVSUBTITLE_AST_CHUNK_ALIGNMENT,
+ .i = AVSUBTITLE_AST_PROP_ALIGN_BOTTOM_LEFT,
+ };
+
+ pos[0] = x1, pos[1] = y1, pos[2] = x2, pos[3] = y2;
+ if (x2 >= 0 && y2 >= 0 && (x2 != x1 || y2 != y1))
+ pos_chunk.type = AVSUBTITLE_AST_CHUNK_MOVE;
+ else
+ pos_chunk.type = AVSUBTITLE_AST_CHUNK_POSITION;
+ av_sub_ast_add_chunk(sub, align_chunk);
+ av_sub_ast_add_chunk(sub, pos_chunk);
+ }
+ }
+
+ for (; *in; in++) {
+ switch (*in) {
+ case '\r':
+ break;
+ case '\n':
+ flush_text_chunk(sub, &textbuf);
+ av_sub_ast_add_chunk(sub, lb_chunk);
+ 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
+ av_bprint_chars(&textbuf, *in, 1);
+ break;
+ case '<':
+ flush_text_chunk(sub, &textbuf);
+ tag_close = in[1] == '/';
+ if (sscanf(in+tag_close+1, "%127[^>]>%n%c", buffer, &len,&c) >= 2) {
+ if ((param = strchr(buffer, ' ')))
+ *param++ = 0;
+ in += len + tag_close;
+
+ if (!strcmp(buffer, "font")) {
+
+ if (tag_close) {
+#define CLOSE_TAG(attr, t) do { \
+ if (stack[sptr] & attr) { \
+ AVSubtitleASTChunk close_chunk = { \
+ .reset = 1, .type = t \
+ }; \
+ av_sub_ast_add_chunk(sub, close_chunk); \
+ } \
+} while (0)
+ sptr -= sptr != 0;
+ CLOSE_TAG(FONT_HAS_SIZE, AVSUBTITLE_AST_CHUNK_FONTSIZE);
+ CLOSE_TAG(FONT_HAS_COLOR, AVSUBTITLE_AST_CHUNK_COLOR);
+ CLOSE_TAG(FONT_HAS_FACE, AVSUBTITLE_AST_CHUNK_FONTSIZE);
+ stack[sptr] = 0;
+ } else if (sptr < FF_ARRAY_ELEMS(stack)) {
+ stack[sptr] = 0;
+ while (param) {
+ if (!strncmp(param, "size=", 5)) {
+ unsigned font_size;
+ AVSubtitleASTChunk size_chunk = {
+ .type = AVSUBTITLE_AST_CHUNK_FONTSIZE,
+ };
+
+ param += 5 + (param[5] == '"');
+ if (sscanf(param, "%u", &font_size) == 1) {
+ stack[sptr] |= FONT_HAS_SIZE;
+ size_chunk.i = font_size;
+ av_sub_ast_add_chunk(sub, size_chunk);
+ }
+ } else if (!strncmp(param, "color=", 6)) {
+ uint8_t rgba[4];
+ AVSubtitleASTChunk color_chunk = {
+ .type = AVSUBTITLE_AST_CHUNK_COLOR,
+ };
+
+ param += 6 + (param[6] == '"');
+ if (av_parse_color(rgba, param, strcspn(param, "\" >"), avctx) >= 0) {
+ stack[sptr] |= FONT_HAS_COLOR;
+ color_chunk.u32 = rgba[0]<<16 | rgba[1] << 8 | rgba[2];
+ av_sub_ast_add_chunk(sub, color_chunk);
+ }
+ } else if (!strncmp(param, "face=", 5)) {
+ char font_face[128];
+ AVSubtitleASTChunk face_chunk = {
+ .type = AVSUBTITLE_AST_CHUNK_FONTNAME,
+ };
+
+ param += 5 + (param[5] == '"');
+ len = strcspn(param,
+ param[-1] == '"' ? "\"" :" ");
+ av_strlcpy(font_face, param,
+ FFMIN(sizeof(font_face), len+1));
+ param += len;
+ if (font_face[0]) {
+ stack[sptr] |= FONT_HAS_FACE;
+ face_chunk.s = av_strdup(font_face);
+ av_sub_ast_add_chunk(sub, face_chunk);
+ }
+ }
+ if ((param = strchr(param, ' ')))
+ param++;
+ }
+ sptr++;
+ }
+ } else if (!buffer[1] && strchr("bisu", av_tolower(buffer[0]))) {
+ AVSubtitleASTChunk tag = {.reset = tag_close, .i = !tag_close};
+
+ switch (av_tolower(buffer[0])) {
+ case 'b': tag.type = AVSUBTITLE_AST_CHUNK_BOLD; break;
+ case 'i': tag.type = AVSUBTITLE_AST_CHUNK_ITALIC; break;
+ case 's': tag.type = AVSUBTITLE_AST_CHUNK_STRIKEOUT; break;
+ case 'u': tag.type = AVSUBTITLE_AST_CHUNK_UNDERLINE; break;
+ }
+ av_sub_ast_add_chunk(sub, tag);
+ }
+ }
+ break;
+ default:
+ av_bprint_chars(&textbuf, *in, 1);
+ break;
+ }
+ }
+ flush_text_chunk(sub, &textbuf);
+
+ av_sub_ast_dump(sub);
+
+ av_log(0,0,"nested to flat...\n");
+ av_sub_ast_nested_to_flat(sub);
+
+ av_sub_ast_dump(sub);
+
+ av_log(0,0,"cleanup...\n");
+ av_sub_ast_cleanup(sub);
+
+ av_sub_ast_dump(sub);
+
+
+ return sub;
+}
+
enum {
PARAM_UNKNOWN = -1,
PARAM_SIZE,
@@ -220,33 +407,14 @@ static int srt_decode_frame(AVCodecContext *avctx,
char buffer[2048];
const char *ptr = avpkt->data;
const char *end = avpkt->data + avpkt->size;
- int size;
- const uint8_t *p = av_packet_get_side_data(avpkt, AV_PKT_DATA_SUBTITLE_POSITION, &size);
-
- if (p && size == 16) {
- x1 = AV_RL32(p );
- y1 = AV_RL32(p + 4);
- x2 = AV_RL32(p + 8);
- y2 = AV_RL32(p + 12);
- }
if (avpkt->size <= 0)
return avpkt->size;
while (ptr < end && *ptr) {
- if (avctx->codec->id == CODEC_ID_SRT) {
ptr = read_ts(ptr, &ts_start, &ts_end, &x1, &y1, &x2, &y2);
if (!ptr)
break;
- } else {
- // Do final divide-by-10 outside rescale to force rounding down.
- ts_start = av_rescale_q(avpkt->pts,
- avctx->time_base,
- (AVRational){1,100});
- ts_end = av_rescale_q(avpkt->pts + avpkt->duration,
- avctx->time_base,
- (AVRational){1,100});
- }
ptr = srt_to_ass(avctx, buffer, buffer+sizeof(buffer), ptr,
x1, y1, x2, y2);
ff_ass_add_rect(sub, buffer, ts_start, ts_end-ts_start, 0);
@@ -256,6 +424,59 @@ static int srt_decode_frame(AVCodecContext *avctx,
return avpkt->size;
}
+static int set_sub_ast(AVSubtitle *sub, AVSubtitleAST *sub_ast, int duration)
+{
+ if (!sub)
+ return AVERROR(ENOMEM);
+ sub->rects = av_malloc(sizeof(*sub->rects));
+ if (!sub->rects)
+ return AVERROR(ENOMEM);
+ sub->rects[0] = av_mallocz(sizeof(*sub->rects[0]));
+ if (!sub->rects[0]) {
+ av_freep(&sub->rects);
+ return AVERROR(ENOMEM);
+ }
+ sub->rects[0]->type = SUBTITLE_AST;
+ sub->rects[0]->ast = sub_ast;
+ sub->end_display_time = duration;
+ sub->num_rects = 1;
+ return 0;
+}
+
+static int subrip_decode(AVCodecContext *avctx, void *data,
+ int *got_sub_ptr, AVPacket *avpkt)
+{
+ AVSubtitle *sub = data;
+ AVSubtitleAST *ast_sub;
+ int ret, size, duration;
+ int x1 = -1, y1 = -1, x2 = -1, y2 = -1;
+ const uint8_t *p;
+
+ p = av_packet_get_side_data(avpkt, AV_PKT_DATA_SUBTITLE_POSITION, &size);
+ if (p && size == 16) {
+ x1 = AV_RL32(p );
+ y1 = AV_RL32(p + 4);
+ x2 = AV_RL32(p + 8);
+ y2 = AV_RL32(p + 12);
+ }
+
+ if (avpkt->size <= 0)
+ return avpkt->size;
+
+ //av_log(0,0,"decode sub of size %d: %s\n", avpkt->size, avpkt->data);
+
+ //av_assert0(avpkt->data[avpkt->size] == 0);
+
+ duration = av_rescale_q(avpkt->duration, avctx->time_base, (AVRational){1,1000});
+ ast_sub = subrip_decode_sub(avctx, avpkt->data, x1, y1, x2, y2);
+ ret = set_sub_ast(sub, ast_sub, duration);
+ if (ret < 0)
+ return ret;
+
+ *got_sub_ptr = sub->num_rects > 0;
+ return avpkt->size;
+}
+
#if CONFIG_SRT_DECODER
/* deprecated decoder */
AVCodec ff_srt_decoder = {
@@ -275,6 +496,6 @@ AVCodec ff_subrip_decoder = {
.type = AVMEDIA_TYPE_SUBTITLE,
.id = AV_CODEC_ID_SUBRIP,
.init = ff_ass_subtitle_header_default,
- .decode = srt_decode_frame,
+ .decode = subrip_decode,
};
#endif
View
29 libavformat/assenc.c
@@ -55,9 +55,36 @@ static int write_header(AVFormatContext *s)
return 0;
}
+static int ts_to_string(char *str, int strlen, int ts)
+{
+ int h, m, s;
+ h = ts/360000; ts -= 360000*h;
+ m = ts/ 6000; ts -= 6000*m;
+ s = ts/ 100; ts -= 100*s;
+ return snprintf(str, strlen, "%d:%02d:%02d.%02d", h, m, s, ts);
+}
+
static int write_packet(AVFormatContext *s, AVPacket *pkt)
{
- avio_write(s->pb, pkt->data, pkt->size);
+ /* old muxing method has packets with timing infornation embedded in
+ * payload, thus the following condition */
+ if (!strncmp(pkt->data, "Dialogue:", 9)) {
+ avio_write(s->pb, pkt->data, pkt->size);
+ } else {
+ char s_start[16], s_end[16], buf[64] = {0};
+ int len;
+
+ ts_to_string(s_start, sizeof(s_start), pkt->pts);
+ if (pkt->duration == -1)
+ snprintf(s_end, sizeof(s_end), "9:59:59.99");
+ else
+ ts_to_string(s_end, sizeof(s_end), pkt->pts + pkt->duration);
+ len = snprintf(buf, sizeof(buf), "Dialogue: 0,%s,%s,",
+ s_start, s_end);
+ avio_write(s->pb, buf, len);
+ avio_write(s->pb, pkt->data, pkt->size);
+ avio_write(s->pb, "\r\n", 2);
+ }
avio_flush(s->pb);
View
2 libavutil/Makefile
@@ -41,6 +41,7 @@ HEADERS = adler32.h \
rational.h \
samplefmt.h \
sha.h \
+ subtitles.h \
time.h \
timecode.h \
timestamp.h \
@@ -91,6 +92,7 @@ OBJS = adler32.o \
rc4.o \
samplefmt.o \
sha.o \
+ subtitles.o \
time.o \
timecode.o \
tree.o \
View
255 libavutil/subtitles.c
@@ -0,0 +1,255 @@
+/*
+ * 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 "common.h"
+#include "subtitles.h"
+
+static const int alloc_based_chunks[] = {
+ AVSUBTITLE_AST_CHUNK_RAW_TEXT,
+ AVSUBTITLE_AST_CHUNK_COMMENT,
+ AVSUBTITLE_AST_CHUNK_FONTNAME,
+ AVSUBTITLE_AST_CHUNK_POSITION,
+ AVSUBTITLE_AST_CHUNK_MOVE,
+};
+
+AVSubtitleAST *av_sub_ast_alloc(void)
+{
+ AVSubtitleAST *sub = av_mallocz(sizeof(*sub));
+ return sub;
+}
+
+AVSubtitleASTSettings *av_sub_ast_settings_alloc(const char *name)
+{
+ AVSubtitleASTSettings *s = av_mallocz(sizeof(*s));
+ if (!s)
+ return NULL;
+ s->name = av_strdup(name);
+ return s;
+}
+
+static int is_allocated_type(int type)
+{
+ int i;
+
+ for (i = 0; i < FF_ARRAY_ELEMS(alloc_based_chunks); i++)
+ if (type == alloc_based_chunks[i])
+ return 1;
+ return 0;
+}
+
+static void free_chunk(AVSubtitleASTChunk *chunk)
+{
+ if (is_allocated_type(chunk->type))
+ av_free(chunk->p); // p and s point to the same location
+}
+
+static void free_chunks(AVSubtitleASTChunk *chunks, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ free_chunk(&chunks[i]);
+ av_free(chunks);
+}
+
+void av_sub_ast_free(AVSubtitleAST *sub)
+{
+ free_chunks(sub->chunks, sub->nb_chunks);
+ av_free(sub);
+}
+
+void av_sub_ast_settings_free(AVSubtitleASTSettings *settings)
+{
+ free_chunks(settings->v, settings->nb);
+ av_free(settings->name);
+ av_free(settings);
+}
+
+void av_sub_ast_cleanup(AVSubtitleAST *sub)
+{
+ int i;
+
+ if (!sub->nb_chunks)
+ return;
+
+ /* trim non-text ending line (styles, line breaks) */
+ for (i = sub->nb_chunks - 1; i >= 0; i--) {
+ if (sub->chunks[i].type == AVSUBTITLE_AST_CHUNK_RAW_TEXT ||
+ sub->chunks[i].type == AVSUBTITLE_AST_CHUNK_COMMENT)
+ break;
+ free_chunk(&sub->chunks[i]);
+ sub->nb_chunks--;
+ }
+
+ /* skip initial text spacing if the text chunk is not composed of spaces
+ * only */
+ if (sub->chunks[0].type == AVSUBTITLE_AST_CHUNK_RAW_TEXT) {
+ char *s = sub->chunks[0].s;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (*s && s != sub->chunks[0].s) {
+ s = av_strdup(s);
+ if (s) {
+ av_free(sub->chunks[0].s);
+ sub->chunks[0].s = s;
+ }
+ }
+ }
+}
+
+/* limitation: only work if nested tags are not of allocated type */
+int av_sub_ast_nested_to_flat(AVSubtitleAST *sub)
+{
+ int i;
+ AVSubtitleASTChunk *chunks = sub->chunks;
+
+ for (i = sub->nb_chunks - 1; i >= 0; i--) {
+ if (chunks[i].reset && !is_allocated_type(chunks[i].type)) {
+ int j, skip = 2;
+
+ for (j = i - 1; j >= 0; j--) {
+ if (chunks[j].type == chunks[i].type) {
+ skip += chunks[j].reset ? 1 : -1;
+ if (skip == 0) {
+ chunks[i].i64 = chunks[j].i64;
+ chunks[i].reset = 0;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* based on av_dynarray_add() */
+static void *sub_dynarray_alloc(void **tab_ptr, int *nb_ptr, size_t elem_size)
+{
+ int nb = *nb_ptr;
+ uint8_t *tab = *tab_ptr;
+
+ if ((nb & (nb - 1)) == 0) {
+ int nb_alloc = nb == 0 ? 1 : nb * 2;
+ tab = av_realloc_f(tab, nb_alloc, elem_size);
+ if (!tab)
+ return NULL;
+ *tab_ptr = tab;
+ }
+ *nb_ptr = nb + 1;
+ return tab + nb*elem_size;
+}
+
+int av_sub_ast_add_chunk(AVSubtitleAST *sub, AVSubtitleASTChunk chunk)
+{
+ AVSubtitleASTChunk *p;
+
+ p = sub_dynarray_alloc((void**)&sub->chunks, &sub->nb_chunks, sizeof(*sub->chunks));
+ if (!p)
+ return AVERROR(ENOMEM);
+ *p = chunk;
+ return 0;
+}
+
+int av_sub_ast_add_setting(AVSubtitleASTSettings *settings, AVSubtitleASTChunk chunk)
+{
+ AVSubtitleASTChunk *p;
+
+ p = sub_dynarray_alloc((void**)&settings->v, &settings->nb, sizeof(*settings->v));
+ if (!p)
+ return AVERROR(ENOMEM);
+ *p = chunk;
+ return 0;
+}
+
+enum {TYPE_STR, TYPE_DBL, TYPE_INT, TYPE_I64, TYPE_U32, TYPE_SPE};
+
+static const struct {
+ int chunk;
+ int type;
+} type_mapping[] = {
+ {AVSUBTITLE_AST_CHUNK_RAW_TEXT, TYPE_STR},
+ {AVSUBTITLE_AST_CHUNK_COMMENT, TYPE_STR},
+ {AVSUBTITLE_AST_CHUNK_TIMING, TYPE_I64},
+ {AVSUBTITLE_AST_CHUNK_KARAOKE, TYPE_INT},
+
+ {AVSUBTITLE_AST_CHUNK_FONTNAME, TYPE_STR},
+ {AVSUBTITLE_AST_CHUNK_FONTSIZE, TYPE_INT},
+
+ {AVSUBTITLE_AST_CHUNK_COLOR, TYPE_U32},
+ {AVSUBTITLE_AST_CHUNK_COLOR_2, TYPE_U32},
+ {AVSUBTITLE_AST_CHUNK_COLOR_OUTLINE, TYPE_U32},
+ {AVSUBTITLE_AST_CHUNK_COLOR_BACK, TYPE_U32},
+
+ {AVSUBTITLE_AST_CHUNK_BOLD, TYPE_INT},
+ {AVSUBTITLE_AST_CHUNK_ITALIC, TYPE_INT},
+ {AVSUBTITLE_AST_CHUNK_STRIKEOUT, TYPE_INT},
+ {AVSUBTITLE_AST_CHUNK_UNDERLINE, TYPE_INT},
+ {AVSUBTITLE_AST_CHUNK_BORDER_STYLE, TYPE_INT},
+ {AVSUBTITLE_AST_CHUNK_OUTLINE, TYPE_INT},
+ {AVSUBTITLE_AST_CHUNK_SHADOW, TYPE_INT},
+ {AVSUBTITLE_AST_CHUNK_ALIGNMENT, TYPE_INT},
+ {AVSUBTITLE_AST_CHUNK_MARGIN_L, TYPE_INT},
+ {AVSUBTITLE_AST_CHUNK_MARGIN_R, TYPE_INT},
+ {AVSUBTITLE_AST_CHUNK_MARGIN_V, TYPE_INT},
+ {AVSUBTITLE_AST_CHUNK_ALPHA_LEVEL, TYPE_INT},
+ {AVSUBTITLE_AST_CHUNK_ENCODING, TYPE_INT},
+
+ {AVSUBTITLE_AST_CHUNK_POSITION, TYPE_SPE},
+ {AVSUBTITLE_AST_CHUNK_MOVE, TYPE_SPE},
+
+ {AVSUBTITLE_AST_CHUNK_LINEBREAK, TYPE_INT},
+};
+
+static int get_type(const int chunk)
+{
+ int i;
+
+ for (i = 0; i < FF_ARRAY_ELEMS(type_mapping); i++)
+ if (type_mapping[i].chunk == chunk)
+ return type_mapping[i].type;
+ return -1;
+}
+
+void av_sub_ast_dump(const AVSubtitleAST *sub)
+{
+ int i;
+
+ av_log(NULL, AV_LOG_INFO, "AST subtitle dump %p:\n", sub);
+ for (i = 0; i < sub->nb_chunks; i++) {
+ const AVSubtitleASTChunk *c = &sub->chunks[i];
+
+ av_log(NULL, AV_LOG_INFO, " [%c%c%c%c] ",
+ c->type >> 24,
+ c->type >> 16 & 0xff,
+ c->type >> 8 & 0xff,
+ c->type & 0xff);
+ if (c->reset) {
+ av_log(NULL, AV_LOG_INFO, "(RESET/CLOSE)");
+ } else {
+ switch (get_type(c->type)) {
+ case TYPE_STR: av_log(NULL, AV_LOG_INFO, "'%s'", c->s); break;
+ case TYPE_DBL: av_log(NULL, AV_LOG_INFO, "%f", c->d); break;
+ case TYPE_INT: av_log(NULL, AV_LOG_INFO, "%d", c->i); break;
+ case TYPE_I64: av_log(NULL, AV_LOG_INFO, "%"PRId64, c->i64); break;
+ case TYPE_U32: av_log(NULL, AV_LOG_INFO, "%08X", c->u32); break;
+ case TYPE_SPE: av_log(NULL, AV_LOG_INFO, "?"); break;
+ }
+ }
+ av_log(NULL, AV_LOG_INFO, "\n");
+ }
+}
View
118 libavutil/subtitles.h
@@ -0,0 +1,118 @@
+/*
+ * 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
+ */
+
+#ifndef AVUTIL_SUBTITLES_H
+#define AVUTIL_SUBTITLES_H
+
+enum {
+ AVSUBTITLE_AST_CHUNK_RAW_TEXT = MKBETAG('t','e','x','t'), // s
+ AVSUBTITLE_AST_CHUNK_COMMENT = MKBETAG('c','o','m',' '), // s
+ AVSUBTITLE_AST_CHUNK_TIMING = MKBETAG('t','i','m','e'), // i64
+ AVSUBTITLE_AST_CHUNK_KARAOKE = MKBETAG('k','a','r','a'), // i
+
+ AVSUBTITLE_AST_CHUNK_FONTNAME = MKBETAG('f','o','n','t'), // s
+ AVSUBTITLE_AST_CHUNK_FONTSIZE = MKBETAG('f','s','i','z'), // i
+
+ AVSUBTITLE_AST_CHUNK_COLOR = MKBETAG('c','l','r','1'), // u32
+ AVSUBTITLE_AST_CHUNK_COLOR_2 = MKBETAG('c','l','r','2'), // u32
+ AVSUBTITLE_AST_CHUNK_COLOR_OUTLINE = MKBETAG('c','l','r','O'), // u32
+ AVSUBTITLE_AST_CHUNK_COLOR_BACK = MKBETAG('c','l','r','B'), // u32
+
+ AVSUBTITLE_AST_CHUNK_BOLD = MKBETAG('b','o','l','d'), // i
+ AVSUBTITLE_AST_CHUNK_ITALIC = MKBETAG('i','t','a','l'), // i
+ AVSUBTITLE_AST_CHUNK_STRIKEOUT = MKBETAG('s','t','r','k'), // i
+ AVSUBTITLE_AST_CHUNK_UNDERLINE = MKBETAG('u','n','l','n'), // i
+ AVSUBTITLE_AST_CHUNK_BORDER_STYLE = MKBETAG('b','d','e','r'), // i
+ AVSUBTITLE_AST_CHUNK_OUTLINE = MKBETAG('o','u','t','l'), // i
+ AVSUBTITLE_AST_CHUNK_SHADOW = MKBETAG('s','h','a','d'), // i
+ AVSUBTITLE_AST_CHUNK_ALIGNMENT = MKBETAG('a','l','g','n'), // i
+ AVSUBTITLE_AST_CHUNK_MARGIN_L = MKBETAG('m','a','r','L'), // i
+ AVSUBTITLE_AST_CHUNK_MARGIN_R = MKBETAG('m','a','r','R'), // i
+ AVSUBTITLE_AST_CHUNK_MARGIN_V = MKBETAG('m','a','r','V'), // i
+ AVSUBTITLE_AST_CHUNK_ALPHA_LEVEL = MKBETAG('a','l','p','h'), // i
+ AVSUBTITLE_AST_CHUNK_ENCODING = MKBETAG('e','n','c',' '), // i
+
+ AVSUBTITLE_AST_CHUNK_POSITION = MKBETAG('p','o','s',' '), // p (2 x i32: x, y)
+ AVSUBTITLE_AST_CHUNK_MOVE = MKBETAG('m','o','v','e'), // p (4 x i32: x1, y1, x2, y2)
+
+ AVSUBTITLE_AST_CHUNK_LINEBREAK = MKBETAG('l','b','r','k'), // i
+};
+
+enum {
+ AVSUBTITLE_AST_PROP_ALIGN_BOTTOM_LEFT = 1,
+ AVSUBTITLE_AST_PROP_ALIGN_BOTTOM_CENTER,
+ AVSUBTITLE_AST_PROP_ALIGN_BOTTOM_RIGHT,
+ AVSUBTITLE_AST_PROP_ALIGN_MIDDLE_LEFT,
+ AVSUBTITLE_AST_PROP_ALIGN_MIDDLE_CENTER,
+ AVSUBTITLE_AST_PROP_ALIGN_MIDDLE_RIGHT,
+ AVSUBTITLE_AST_PROP_ALIGN_TOP_LEFT,
+ AVSUBTITLE_AST_PROP_ALIGN_TOP_CENTER,
+ AVSUBTITLE_AST_PROP_ALIGN_TOP_RIGHT,
+};
+
+typedef struct AVSubtitleASTChunk {
+ int type; ///< one of the AVSUBTITLE_AST_SETTING_TYPE_*
+ int reset; /**< this chunk restores the setting to the default
+ value (or disable the previous one in nested
+ mode) */
+ union {
+ char *s; ///< must be a av_malloc'ed string if string type
+ double d;
+ int i;
+ int64_t i64;
+ uint32_t u32;
+ void *p; ///< pointer to allocated data of an arbitrary size (chunk type dependent)
+ };
+ int p_nb; ///< number of entries in p, can be used for variable sized data
+} AVSubtitleASTChunk;
+
+/*
+ * examples:
+ * "default", [color=red, bold=1, timing=1234, font="Arial"]
+ * [text="hello"]
+ * [fontname=NULL, italic=0]
+ */
+typedef struct AVSubtitleASTSettings {
+ char *name; ///< optional settings name reference
+ int nb; ///< number of allocated chunks
+ AVSubtitleASTChunk *v; ///< array of nb chunks
+} AVSubtitleASTSettings;
+
+typedef struct AVSubtitleAST {
+ const AVSubtitleASTSettings *g_settings; ///< pointer to one of the global settings for the subtitle event
+ AVSubtitleASTChunk *chunks; ///< styles and text chunks
+ int nb_chunks; ///< number of chunks
+} AVSubtitleAST;
+
+
+/* TODO: doxy */
+
+AVSubtitleAST *av_sub_ast_alloc(void);
+int av_sub_ast_add_chunk(AVSubtitleAST *sub, AVSubtitleASTChunk chunk);
+void av_sub_ast_free(AVSubtitleAST *sub);
+
+AVSubtitleASTSettings *av_sub_ast_settings_alloc(const char *name);
+int av_sub_ast_add_setting(AVSubtitleASTSettings *settings, AVSubtitleASTChunk chunk);
+void av_sub_ast_settings_free(AVSubtitleASTSettings *settings);
+
+/* internals only? */
+int av_sub_ast_nested_to_flat(AVSubtitleAST *sub);
+void av_sub_ast_cleanup(AVSubtitleAST *sub); // assume flat
+void av_sub_ast_dump(const AVSubtitleAST *sub);
+
+#endif /* AVUTIL_SUBTITLES_H */

0 comments on commit 13bf4d6

Please sign in to comment.