00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00026 #include "avcodec.h"
00027 #include "ass.h"
00028 #include "libavutil/bprint.h"
00029 #include "libavutil/opt.h"
00030
00031 typedef struct {
00032 AVClass *class;
00033 char *linebreaks;
00034 int keep_ass_markup;
00035 } TextContext;
00036
00037 #define OFFSET(x) offsetof(TextContext, x)
00038 #define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
00039 static const AVOption options[] = {
00040 { "linebreaks", "Extra line breaks characters", OFFSET(linebreaks), AV_OPT_TYPE_STRING, {.str=NULL}, .flags=SD },
00041 { "keep_ass_markup", "Set if ASS tags must be escaped", OFFSET(keep_ass_markup), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, .flags=SD },
00042 { NULL }
00043 };
00044
00045 static const AVClass text_decoder_class = {
00046 .class_name = "text decoder",
00047 .item_name = av_default_item_name,
00048 .option = options,
00049 .version = LIBAVUTIL_VERSION_INT,
00050 };
00051
00052 static int text_event_to_ass(const AVCodecContext *avctx, AVBPrint *buf,
00053 const char *p, const char *p_end)
00054 {
00055 const TextContext *text = avctx->priv_data;
00056
00057 for (; p < p_end && *p; p++) {
00058
00059
00060 if (text->linebreaks && strchr(text->linebreaks, *p)) {
00061 av_bprintf(buf, "\\N");
00062
00063
00064
00065 } else if (!text->keep_ass_markup && strchr("{}\\", *p)) {
00066 av_bprintf(buf, "\\%c", *p);
00067
00068
00069
00070
00071
00072
00073
00074 } else if (p[0] == '\n') {
00075
00076 if (p < p_end - 1)
00077 av_bprintf(buf, "\\N");
00078 } else if (p[0] == '\r' && p < p_end - 1 && p[1] == '\n') {
00079
00080
00081 continue;
00082
00083
00084 } else {
00085 av_bprint_chars(buf, *p, 1);
00086 }
00087 }
00088 av_bprintf(buf, "\r\n");
00089 return 0;
00090 }
00091
00092 static int text_decode_frame(AVCodecContext *avctx, void *data,
00093 int *got_sub_ptr, AVPacket *avpkt)
00094 {
00095 AVBPrint buf;
00096 AVSubtitle *sub = data;
00097 const char *ptr = avpkt->data;
00098 const int ts_start = av_rescale_q(avpkt->pts, avctx->time_base, (AVRational){1,100});
00099 const int ts_duration = avpkt->duration != -1 ?
00100 av_rescale_q(avpkt->duration, avctx->time_base, (AVRational){1,100}) : -1;
00101
00102 av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
00103 if (ptr && avpkt->size > 0 && *ptr &&
00104 !text_event_to_ass(avctx, &buf, ptr, ptr + avpkt->size)) {
00105 if (!av_bprint_is_complete(&buf)) {
00106 av_bprint_finalize(&buf, NULL);
00107 return AVERROR(ENOMEM);
00108 }
00109 ff_ass_add_rect(sub, buf.str, ts_start, ts_duration, 0);
00110 }
00111 *got_sub_ptr = sub->num_rects > 0;
00112 av_bprint_finalize(&buf, NULL);
00113 return avpkt->size;
00114 }
00115
00116 AVCodec ff_text_decoder = {
00117 .name = "text",
00118 .priv_data_size = sizeof(TextContext),
00119 .long_name = NULL_IF_CONFIG_SMALL("Raw text subtitle"),
00120 .type = AVMEDIA_TYPE_SUBTITLE,
00121 .id = AV_CODEC_ID_TEXT,
00122 .decode = text_decode_frame,
00123 .init = ff_ass_subtitle_header_default,
00124 .priv_class = &text_decoder_class,
00125 };