1/* 2 * Copyright (c) 2012 Clément Bœsch 3 * 4 * This file is part of FFmpeg. 5 * 6 * FFmpeg is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * FFmpeg is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with FFmpeg; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21/** 22 * @file 23 * Raw subtitles decoder 24 */ 25 26#include "config_components.h" 27 28#include "avcodec.h" 29#include "ass.h" 30#include "codec_internal.h" 31#include "libavutil/bprint.h" 32#include "libavutil/opt.h" 33 34typedef struct { 35 AVClass *class; 36 const char *linebreaks; 37 int keep_ass_markup; 38 int readorder; 39} TextContext; 40 41#define OFFSET(x) offsetof(TextContext, x) 42#define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM 43static const AVOption options[] = { 44 { "keep_ass_markup", "Set if ASS tags must be escaped", OFFSET(keep_ass_markup), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, .flags=SD }, 45 { NULL } 46}; 47 48static int text_decode_frame(AVCodecContext *avctx, AVSubtitle *sub, 49 int *got_sub_ptr, const AVPacket *avpkt) 50{ 51 int ret = 0; 52 AVBPrint buf; 53 const char *ptr = avpkt->data; 54 TextContext *text = avctx->priv_data; 55 56 av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); 57 if (ptr && avpkt->size > 0 && *ptr) { 58 ff_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup); 59 ret = ff_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL); 60 } 61 av_bprint_finalize(&buf, NULL); 62 if (ret < 0) 63 return ret; 64 *got_sub_ptr = sub->num_rects > 0; 65 return avpkt->size; 66} 67 68static void text_flush(AVCodecContext *avctx) 69{ 70 TextContext *text = avctx->priv_data; 71 if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP)) 72 text->readorder = 0; 73} 74 75static const AVClass textsub_decoder_class = { 76 .class_name = "text/vplayer/stl/pjs/subviewer1 decoder", 77 .item_name = av_default_item_name, 78 .option = options, 79 .version = LIBAVUTIL_VERSION_INT, 80}; 81 82#if CONFIG_TEXT_DECODER 83const FFCodec ff_text_decoder = { 84 .p.name = "text", 85 .p.long_name = NULL_IF_CONFIG_SMALL("Raw text subtitle"), 86 .priv_data_size = sizeof(TextContext), 87 .p.type = AVMEDIA_TYPE_SUBTITLE, 88 .p.id = AV_CODEC_ID_TEXT, 89 FF_CODEC_DECODE_SUB_CB(text_decode_frame), 90 .init = ff_ass_subtitle_header_default, 91 .p.priv_class = &textsub_decoder_class, 92 .flush = text_flush, 93 .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 94}; 95#endif 96 97#if CONFIG_VPLAYER_DECODER || CONFIG_PJS_DECODER || CONFIG_SUBVIEWER1_DECODER || CONFIG_STL_DECODER 98 99static int linebreak_init(AVCodecContext *avctx) 100{ 101 TextContext *text = avctx->priv_data; 102 text->linebreaks = "|"; 103 return ff_ass_subtitle_header_default(avctx); 104} 105 106#if CONFIG_VPLAYER_DECODER 107const FFCodec ff_vplayer_decoder = { 108 .p.name = "vplayer", 109 .p.long_name = NULL_IF_CONFIG_SMALL("VPlayer subtitle"), 110 .priv_data_size = sizeof(TextContext), 111 .p.type = AVMEDIA_TYPE_SUBTITLE, 112 .p.id = AV_CODEC_ID_VPLAYER, 113 FF_CODEC_DECODE_SUB_CB(text_decode_frame), 114 .init = linebreak_init, 115 .p.priv_class = &textsub_decoder_class, 116 .flush = text_flush, 117 .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 118}; 119#endif 120 121#if CONFIG_STL_DECODER 122const FFCodec ff_stl_decoder = { 123 .p.name = "stl", 124 .p.long_name = NULL_IF_CONFIG_SMALL("Spruce subtitle format"), 125 .priv_data_size = sizeof(TextContext), 126 .p.type = AVMEDIA_TYPE_SUBTITLE, 127 .p.id = AV_CODEC_ID_STL, 128 FF_CODEC_DECODE_SUB_CB(text_decode_frame), 129 .init = linebreak_init, 130 .p.priv_class = &textsub_decoder_class, 131 .flush = text_flush, 132 .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 133}; 134#endif 135 136#if CONFIG_PJS_DECODER 137const FFCodec ff_pjs_decoder = { 138 .p.name = "pjs", 139 .p.long_name = NULL_IF_CONFIG_SMALL("PJS subtitle"), 140 .priv_data_size = sizeof(TextContext), 141 .p.type = AVMEDIA_TYPE_SUBTITLE, 142 .p.id = AV_CODEC_ID_PJS, 143 FF_CODEC_DECODE_SUB_CB(text_decode_frame), 144 .init = linebreak_init, 145 .p.priv_class = &textsub_decoder_class, 146 .flush = text_flush, 147 .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 148}; 149#endif 150 151#if CONFIG_SUBVIEWER1_DECODER 152const FFCodec ff_subviewer1_decoder = { 153 .p.name = "subviewer1", 154 .p.long_name = NULL_IF_CONFIG_SMALL("SubViewer1 subtitle"), 155 .priv_data_size = sizeof(TextContext), 156 .p.type = AVMEDIA_TYPE_SUBTITLE, 157 .p.id = AV_CODEC_ID_SUBVIEWER1, 158 FF_CODEC_DECODE_SUB_CB(text_decode_frame), 159 .init = linebreak_init, 160 .p.priv_class = &textsub_decoder_class, 161 .flush = text_flush, 162 .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 163}; 164#endif 165 166#endif /* text subtitles with '|' line break */ 167