00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "avformat.h"
00023 #include "internal.h"
00024 #include "subtitles.h"
00025 #include "libavutil/bprint.h"
00026
00027 typedef struct ASSContext{
00028 FFDemuxSubtitlesQueue q;
00029 }ASSContext;
00030
00031 static int ass_probe(AVProbeData *p)
00032 {
00033 const char *header= "[Script Info]";
00034
00035 if( !memcmp(p->buf , header, strlen(header))
00036 || !memcmp(p->buf+3, header, strlen(header)))
00037 return AVPROBE_SCORE_MAX;
00038
00039 return 0;
00040 }
00041
00042 static int ass_read_close(AVFormatContext *s)
00043 {
00044 ASSContext *ass = s->priv_data;
00045 ff_subtitles_queue_clean(&ass->q);
00046 return 0;
00047 }
00048
00049 static int read_ts(const uint8_t *p, int64_t *start, int *duration)
00050 {
00051 int64_t end;
00052 int hh1, mm1, ss1, ms1;
00053 int hh2, mm2, ss2, ms2;
00054
00055 if (sscanf(p, "%*[^,],%d:%d:%d%*c%d,%d:%d:%d%*c%d",
00056 &hh1, &mm1, &ss1, &ms1,
00057 &hh2, &mm2, &ss2, &ms2) == 8) {
00058 end = (hh2*3600LL + mm2*60LL + ss2) * 100LL + ms2;
00059 *start = (hh1*3600LL + mm1*60LL + ss1) * 100LL + ms1;
00060 *duration = end - *start;
00061 return 0;
00062 }
00063 return -1;
00064 }
00065
00066 static int64_t get_line(AVBPrint *buf, AVIOContext *pb)
00067 {
00068 int64_t pos = avio_tell(pb);
00069
00070 av_bprint_clear(buf);
00071 for (;;) {
00072 char c = avio_r8(pb);
00073 if (!c)
00074 break;
00075 av_bprint_chars(buf, c, 1);
00076 if (c == '\n')
00077 break;
00078 }
00079 return pos;
00080 }
00081
00082 static int ass_read_header(AVFormatContext *s)
00083 {
00084 ASSContext *ass = s->priv_data;
00085 AVBPrint header, line;
00086 int header_remaining, res = 0;
00087 AVStream *st;
00088
00089 st = avformat_new_stream(s, NULL);
00090 if (!st)
00091 return AVERROR(ENOMEM);
00092 avpriv_set_pts_info(st, 64, 1, 100);
00093 st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
00094 st->codec->codec_id= AV_CODEC_ID_SSA;
00095
00096 header_remaining= INT_MAX;
00097
00098 av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED);
00099 av_bprint_init(&line, 0, AV_BPRINT_SIZE_UNLIMITED);
00100
00101 for (;;) {
00102 int64_t pos = get_line(&line, s->pb);
00103
00104 if (!line.str[0])
00105 break;
00106
00107 if (!memcmp(line.str, "[Events]", 8))
00108 header_remaining= 2;
00109 else if (line.str[0]=='[')
00110 header_remaining= INT_MAX;
00111
00112 if (header_remaining) {
00113 av_bprintf(&header, "%s", line.str);
00114 header_remaining--;
00115 } else {
00116 int64_t ts_start = AV_NOPTS_VALUE;
00117 int duration = -1;
00118 AVPacket *sub;
00119
00120 if (read_ts(line.str, &ts_start, &duration) < 0)
00121 continue;
00122 sub = ff_subtitles_queue_insert(&ass->q, line.str, line.len, 0);
00123 if (!sub) {
00124 res = AVERROR(ENOMEM);
00125 goto end;
00126 }
00127 sub->pos = pos;
00128 sub->pts = ts_start;
00129 sub->duration = duration;
00130 }
00131 }
00132
00133 av_bprint_finalize(&line, NULL);
00134
00135 av_bprint_finalize(&header, (char **)&st->codec->extradata);
00136 if (!st->codec->extradata) {
00137 res = AVERROR(ENOMEM);
00138 goto end;
00139 }
00140 st->codec->extradata_size = header.len + 1;
00141
00142 ff_subtitles_queue_finalize(&ass->q);
00143
00144 end:
00145 return res;
00146 }
00147
00148 static int ass_read_packet(AVFormatContext *s, AVPacket *pkt)
00149 {
00150 ASSContext *ass = s->priv_data;
00151 return ff_subtitles_queue_read_packet(&ass->q, pkt);
00152 }
00153
00154 static int ass_read_seek(AVFormatContext *s, int stream_index,
00155 int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
00156 {
00157 ASSContext *ass = s->priv_data;
00158 return ff_subtitles_queue_seek(&ass->q, s, stream_index,
00159 min_ts, ts, max_ts, flags);
00160 }
00161
00162 AVInputFormat ff_ass_demuxer = {
00163 .name = "ass",
00164 .long_name = NULL_IF_CONFIG_SMALL("SSA (SubStation Alpha) subtitle"),
00165 .priv_data_size = sizeof(ASSContext),
00166 .read_probe = ass_probe,
00167 .read_header = ass_read_header,
00168 .read_packet = ass_read_packet,
00169 .read_close = ass_read_close,
00170 .read_seek2 = ass_read_seek,
00171 };