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 * Raw subtitles decoder
|
yading@10
|
24 */
|
yading@10
|
25
|
yading@10
|
26 #include "avcodec.h"
|
yading@10
|
27 #include "ass.h"
|
yading@10
|
28 #include "libavutil/bprint.h"
|
yading@10
|
29 #include "libavutil/opt.h"
|
yading@10
|
30
|
yading@10
|
31 typedef struct {
|
yading@10
|
32 AVClass *class;
|
yading@10
|
33 const char *linebreaks;
|
yading@10
|
34 int keep_ass_markup;
|
yading@10
|
35 } TextContext;
|
yading@10
|
36
|
yading@10
|
37 #define OFFSET(x) offsetof(TextContext, x)
|
yading@10
|
38 #define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
|
yading@10
|
39 static const AVOption options[] = {
|
yading@10
|
40 { "keep_ass_markup", "Set if ASS tags must be escaped", OFFSET(keep_ass_markup), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, .flags=SD },
|
yading@10
|
41 { NULL }
|
yading@10
|
42 };
|
yading@10
|
43
|
yading@10
|
44 static int text_event_to_ass(const AVCodecContext *avctx, AVBPrint *buf,
|
yading@10
|
45 const char *p, const char *p_end)
|
yading@10
|
46 {
|
yading@10
|
47 const TextContext *text = avctx->priv_data;
|
yading@10
|
48
|
yading@10
|
49 for (; p < p_end && *p; p++) {
|
yading@10
|
50
|
yading@10
|
51 /* forced custom line breaks, not accounted as "normal" EOL */
|
yading@10
|
52 if (text->linebreaks && strchr(text->linebreaks, *p)) {
|
yading@10
|
53 av_bprintf(buf, "\\N");
|
yading@10
|
54
|
yading@10
|
55 /* standard ASS escaping so random characters don't get mis-interpreted
|
yading@10
|
56 * as ASS */
|
yading@10
|
57 } else if (!text->keep_ass_markup && strchr("{}\\", *p)) {
|
yading@10
|
58 av_bprintf(buf, "\\%c", *p);
|
yading@10
|
59
|
yading@10
|
60 /* some packets might end abruptly (no \0 at the end, like for example
|
yading@10
|
61 * in some cases of demuxing from a classic video container), some
|
yading@10
|
62 * might be terminated with \n or \r\n which we have to remove (for
|
yading@10
|
63 * consistency with those who haven't), and we also have to deal with
|
yading@10
|
64 * evil cases such as \r at the end of the buffer (and no \0 terminated
|
yading@10
|
65 * character) */
|
yading@10
|
66 } else if (p[0] == '\n') {
|
yading@10
|
67 /* some stuff left so we can insert a line break */
|
yading@10
|
68 if (p < p_end - 1)
|
yading@10
|
69 av_bprintf(buf, "\\N");
|
yading@10
|
70 } else if (p[0] == '\r' && p < p_end - 1 && p[1] == '\n') {
|
yading@10
|
71 /* \r followed by a \n, we can skip it. We don't insert the \N yet
|
yading@10
|
72 * because we don't know if it is followed by more text */
|
yading@10
|
73 continue;
|
yading@10
|
74
|
yading@10
|
75 /* finally, a sane character */
|
yading@10
|
76 } else {
|
yading@10
|
77 av_bprint_chars(buf, *p, 1);
|
yading@10
|
78 }
|
yading@10
|
79 }
|
yading@10
|
80 av_bprintf(buf, "\r\n");
|
yading@10
|
81 return 0;
|
yading@10
|
82 }
|
yading@10
|
83
|
yading@10
|
84 static int text_decode_frame(AVCodecContext *avctx, void *data,
|
yading@10
|
85 int *got_sub_ptr, AVPacket *avpkt)
|
yading@10
|
86 {
|
yading@10
|
87 AVBPrint buf;
|
yading@10
|
88 AVSubtitle *sub = data;
|
yading@10
|
89 const char *ptr = avpkt->data;
|
yading@10
|
90 const int ts_start = av_rescale_q(avpkt->pts, avctx->time_base, (AVRational){1,100});
|
yading@10
|
91 const int ts_duration = avpkt->duration != -1 ?
|
yading@10
|
92 av_rescale_q(avpkt->duration, avctx->time_base, (AVRational){1,100}) : -1;
|
yading@10
|
93
|
yading@10
|
94 av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
|
yading@10
|
95 if (ptr && avpkt->size > 0 && *ptr &&
|
yading@10
|
96 !text_event_to_ass(avctx, &buf, ptr, ptr + avpkt->size)) {
|
yading@10
|
97 if (!av_bprint_is_complete(&buf)) {
|
yading@10
|
98 av_bprint_finalize(&buf, NULL);
|
yading@10
|
99 return AVERROR(ENOMEM);
|
yading@10
|
100 }
|
yading@10
|
101 ff_ass_add_rect(sub, buf.str, ts_start, ts_duration, 0);
|
yading@10
|
102 }
|
yading@10
|
103 *got_sub_ptr = sub->num_rects > 0;
|
yading@10
|
104 av_bprint_finalize(&buf, NULL);
|
yading@10
|
105 return avpkt->size;
|
yading@10
|
106 }
|
yading@10
|
107
|
yading@10
|
108 #define DECLARE_CLASS(decname) static const AVClass decname ## _decoder_class = { \
|
yading@10
|
109 .class_name = #decname " decoder", \
|
yading@10
|
110 .item_name = av_default_item_name, \
|
yading@10
|
111 .option = decname ## _options, \
|
yading@10
|
112 .version = LIBAVUTIL_VERSION_INT, \
|
yading@10
|
113 }
|
yading@10
|
114
|
yading@10
|
115 #if CONFIG_TEXT_DECODER
|
yading@10
|
116 #define text_options options
|
yading@10
|
117 DECLARE_CLASS(text);
|
yading@10
|
118
|
yading@10
|
119 AVCodec ff_text_decoder = {
|
yading@10
|
120 .name = "text",
|
yading@10
|
121 .priv_data_size = sizeof(TextContext),
|
yading@10
|
122 .long_name = NULL_IF_CONFIG_SMALL("Raw text subtitle"),
|
yading@10
|
123 .type = AVMEDIA_TYPE_SUBTITLE,
|
yading@10
|
124 .id = AV_CODEC_ID_TEXT,
|
yading@10
|
125 .decode = text_decode_frame,
|
yading@10
|
126 .init = ff_ass_subtitle_header_default,
|
yading@10
|
127 .priv_class = &text_decoder_class,
|
yading@10
|
128 };
|
yading@10
|
129 #endif
|
yading@10
|
130
|
yading@10
|
131 #if CONFIG_VPLAYER_DECODER || CONFIG_PJS_DECODER || CONFIG_SUBVIEWER1_DECODER
|
yading@10
|
132
|
yading@10
|
133 static int linebreak_init(AVCodecContext *avctx)
|
yading@10
|
134 {
|
yading@10
|
135 TextContext *text = avctx->priv_data;
|
yading@10
|
136 text->linebreaks = "|";
|
yading@10
|
137 return ff_ass_subtitle_header_default(avctx);
|
yading@10
|
138 }
|
yading@10
|
139
|
yading@10
|
140 #if CONFIG_VPLAYER_DECODER
|
yading@10
|
141 #define vplayer_options options
|
yading@10
|
142 DECLARE_CLASS(vplayer);
|
yading@10
|
143
|
yading@10
|
144 AVCodec ff_vplayer_decoder = {
|
yading@10
|
145 .name = "vplayer",
|
yading@10
|
146 .priv_data_size = sizeof(TextContext),
|
yading@10
|
147 .long_name = NULL_IF_CONFIG_SMALL("VPlayer subtitle"),
|
yading@10
|
148 .type = AVMEDIA_TYPE_SUBTITLE,
|
yading@10
|
149 .id = AV_CODEC_ID_VPLAYER,
|
yading@10
|
150 .decode = text_decode_frame,
|
yading@10
|
151 .init = linebreak_init,
|
yading@10
|
152 .priv_class = &vplayer_decoder_class,
|
yading@10
|
153 };
|
yading@10
|
154 #endif
|
yading@10
|
155
|
yading@10
|
156 #if CONFIG_PJS_DECODER
|
yading@10
|
157 #define pjs_options options
|
yading@10
|
158 DECLARE_CLASS(pjs);
|
yading@10
|
159
|
yading@10
|
160 AVCodec ff_pjs_decoder = {
|
yading@10
|
161 .name = "pjs",
|
yading@10
|
162 .priv_data_size = sizeof(TextContext),
|
yading@10
|
163 .long_name = NULL_IF_CONFIG_SMALL("PJS subtitle"),
|
yading@10
|
164 .type = AVMEDIA_TYPE_SUBTITLE,
|
yading@10
|
165 .id = AV_CODEC_ID_PJS,
|
yading@10
|
166 .decode = text_decode_frame,
|
yading@10
|
167 .init = linebreak_init,
|
yading@10
|
168 .priv_class = &pjs_decoder_class,
|
yading@10
|
169 };
|
yading@10
|
170 #endif
|
yading@10
|
171
|
yading@10
|
172 #if CONFIG_SUBVIEWER1_DECODER
|
yading@10
|
173 #define subviewer1_options options
|
yading@10
|
174 DECLARE_CLASS(subviewer1);
|
yading@10
|
175
|
yading@10
|
176 AVCodec ff_subviewer1_decoder = {
|
yading@10
|
177 .name = "subviewer1",
|
yading@10
|
178 .priv_data_size = sizeof(TextContext),
|
yading@10
|
179 .long_name = NULL_IF_CONFIG_SMALL("SubViewer1 subtitle"),
|
yading@10
|
180 .type = AVMEDIA_TYPE_SUBTITLE,
|
yading@10
|
181 .id = AV_CODEC_ID_SUBVIEWER1,
|
yading@10
|
182 .decode = text_decode_frame,
|
yading@10
|
183 .init = linebreak_init,
|
yading@10
|
184 .priv_class = &subviewer1_decoder_class,
|
yading@10
|
185 };
|
yading@10
|
186 #endif
|
yading@10
|
187
|
yading@10
|
188 #endif /* text subtitles with '|' line break */
|