00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00027 #include "ass.h"
00028 #include "libavutil/avstring.h"
00029 #include "libavutil/bprint.h"
00030
00031 typedef struct {
00032 AVBPrint source;
00033 AVBPrint content;
00034 AVBPrint full;
00035 } SAMIContext;
00036
00037 static int sami_paragraph_to_ass(AVCodecContext *avctx, const char *src)
00038 {
00039 SAMIContext *sami = avctx->priv_data;
00040 int ret = 0;
00041 char *tag = NULL;
00042 char *dupsrc = av_strdup(src);
00043 char *p = dupsrc;
00044
00045 av_bprint_clear(&sami->content);
00046 for (;;) {
00047 char *saveptr = NULL;
00048 int prev_chr_is_space = 0;
00049 AVBPrint *dst = &sami->content;
00050
00051
00052 p = av_stristr(p, "<P");
00053 if (!p)
00054 break;
00055 if (p[2] != '>' && !isspace(p[2])) {
00056 p++;
00057 continue;
00058 }
00059 if (dst->len)
00060 av_bprintf(dst, "\\N");
00061 tag = av_strtok(p, ">", &saveptr);
00062 if (!tag || !saveptr)
00063 break;
00064 p = saveptr;
00065
00066
00067 if (av_stristr(tag, "ID=Source") || av_stristr(tag, "ID=\"Source\"")) {
00068 dst = &sami->source;
00069 av_bprint_clear(dst);
00070 }
00071
00072
00073 while (isspace(*p))
00074 p++;
00075 if (!strncmp(p, " ", 6)) {
00076 ret = -1;
00077 goto end;
00078 }
00079
00080
00081 while (*p) {
00082 if (*p == '<') {
00083 if (!av_strncasecmp(p, "<P", 2) && (p[2] == '>' || isspace(p[2])))
00084 break;
00085 if (!av_strncasecmp(p, "<BR", 3))
00086 av_bprintf(dst, "\\N");
00087 p++;
00088 while (*p && *p != '>')
00089 p++;
00090 if (!*p)
00091 break;
00092 if (*p == '>')
00093 p++;
00094 }
00095 if (!isspace(*p))
00096 av_bprint_chars(dst, *p, 1);
00097 else if (!prev_chr_is_space)
00098 av_bprint_chars(dst, ' ', 1);
00099 prev_chr_is_space = isspace(*p);
00100 p++;
00101 }
00102 }
00103
00104 av_bprint_clear(&sami->full);
00105 if (sami->source.len)
00106 av_bprintf(&sami->full, "{\\i1}%s{\\i0}\\N", sami->source.str);
00107 av_bprintf(&sami->full, "%s\r\n", sami->content.str);
00108
00109 end:
00110 av_free(dupsrc);
00111 return ret;
00112 }
00113
00114 static int sami_decode_frame(AVCodecContext *avctx,
00115 void *data, int *got_sub_ptr, AVPacket *avpkt)
00116 {
00117 AVSubtitle *sub = data;
00118 const char *ptr = avpkt->data;
00119 SAMIContext *sami = avctx->priv_data;
00120
00121 if (ptr && avpkt->size > 0 && !sami_paragraph_to_ass(avctx, ptr)) {
00122 int ts_start = av_rescale_q(avpkt->pts, avctx->time_base, (AVRational){1,100});
00123 int ts_duration = avpkt->duration != -1 ?
00124 av_rescale_q(avpkt->duration, avctx->time_base, (AVRational){1,100}) : -1;
00125 ff_ass_add_rect(sub, sami->full.str, ts_start, ts_duration, 0);
00126 }
00127 *got_sub_ptr = sub->num_rects > 0;
00128 return avpkt->size;
00129 }
00130
00131 static av_cold int sami_init(AVCodecContext *avctx)
00132 {
00133 SAMIContext *sami = avctx->priv_data;
00134 av_bprint_init(&sami->source, 0, 2048);
00135 av_bprint_init(&sami->content, 0, 2048);
00136 av_bprint_init(&sami->full, 0, 2048);
00137 return ff_ass_subtitle_header_default(avctx);
00138 }
00139
00140 static av_cold int sami_close(AVCodecContext *avctx)
00141 {
00142 SAMIContext *sami = avctx->priv_data;
00143 av_bprint_finalize(&sami->source, NULL);
00144 av_bprint_finalize(&sami->content, NULL);
00145 av_bprint_finalize(&sami->full, NULL);
00146 return 0;
00147 }
00148
00149 AVCodec ff_sami_decoder = {
00150 .name = "sami",
00151 .long_name = NULL_IF_CONFIG_SMALL("SAMI subtitle"),
00152 .type = AVMEDIA_TYPE_SUBTITLE,
00153 .id = AV_CODEC_ID_SAMI,
00154 .priv_data_size = sizeof(SAMIContext),
00155 .init = sami_init,
00156 .close = sami_close,
00157 .decode = sami_decode_frame,
00158 };