ffmpeg / libavformat / assdec.c @ 66e5b1df
History | View | Annotate | Download (5.82 KB)
1 |
/*
|
---|---|
2 |
* SSA/ASS demuxer
|
3 |
* Copyright (c) 2008 Michael Niedermayer
|
4 |
*
|
5 |
* This file is part of FFmpeg.
|
6 |
*
|
7 |
* FFmpeg is free software; you can redistribute it and/or
|
8 |
* modify it under the terms of the GNU Lesser General Public
|
9 |
* License as published by the Free Software Foundation; either
|
10 |
* version 2.1 of the License, or (at your option) any later version.
|
11 |
*
|
12 |
* FFmpeg is distributed in the hope that it will be useful,
|
13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15 |
* Lesser General Public License for more details.
|
16 |
*
|
17 |
* You should have received a copy of the GNU Lesser General Public
|
18 |
* License along with FFmpeg; if not, write to the Free Software
|
19 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
20 |
*/
|
21 |
|
22 |
#include "avformat.h" |
23 |
#include "internal.h" |
24 |
|
25 |
#define MAX_LINESIZE 2000 |
26 |
|
27 |
typedef struct ASSContext{ |
28 |
uint8_t *event_buffer; |
29 |
uint8_t **event; |
30 |
unsigned int event_count; |
31 |
unsigned int event_index; |
32 |
}ASSContext; |
33 |
|
34 |
static int probe(AVProbeData *p) |
35 |
{ |
36 |
const char *header= "[Script Info]"; |
37 |
|
38 |
if( !memcmp(p->buf , header, strlen(header))
|
39 |
|| !memcmp(p->buf+3, header, strlen(header)))
|
40 |
return AVPROBE_SCORE_MAX;
|
41 |
|
42 |
return 0; |
43 |
} |
44 |
|
45 |
static int read_close(AVFormatContext *s) |
46 |
{ |
47 |
ASSContext *ass = s->priv_data; |
48 |
|
49 |
av_freep(&ass->event_buffer); |
50 |
av_freep(&ass->event); |
51 |
|
52 |
return 0; |
53 |
} |
54 |
|
55 |
static int64_t get_pts(const uint8_t *p) |
56 |
{ |
57 |
int hour, min, sec, hsec;
|
58 |
|
59 |
if(sscanf(p, "%*[^,],%d:%d:%d%*c%d", &hour, &min, &sec, &hsec) != 4) |
60 |
return AV_NOPTS_VALUE;
|
61 |
|
62 |
// av_log(NULL, AV_LOG_ERROR, "%d %d %d %d %d [%s]\n", i, hour, min, sec, hsec, p);
|
63 |
|
64 |
min+= 60*hour;
|
65 |
sec+= 60*min;
|
66 |
|
67 |
return sec*100+hsec; |
68 |
} |
69 |
|
70 |
static int event_cmp(uint8_t **a, uint8_t **b) |
71 |
{ |
72 |
return get_pts(*a) - get_pts(*b);
|
73 |
} |
74 |
|
75 |
static int read_header(AVFormatContext *s, AVFormatParameters *ap) |
76 |
{ |
77 |
int i, len, header_remaining;
|
78 |
ASSContext *ass = s->priv_data; |
79 |
AVIOContext *pb = s->pb; |
80 |
AVStream *st; |
81 |
int allocated[2]={0}; |
82 |
uint8_t *p, **dst[2]={0}; |
83 |
int pos[2]={0}; |
84 |
|
85 |
st = av_new_stream(s, 0);
|
86 |
if (!st)
|
87 |
return -1; |
88 |
av_set_pts_info(st, 64, 1, 100); |
89 |
st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; |
90 |
st->codec->codec_id= CODEC_ID_SSA; |
91 |
|
92 |
header_remaining= INT_MAX; |
93 |
dst[0] = &st->codec->extradata;
|
94 |
dst[1] = &ass->event_buffer;
|
95 |
while(!pb->eof_reached){
|
96 |
uint8_t line[MAX_LINESIZE]; |
97 |
|
98 |
len = ff_get_line(pb, line, sizeof(line));
|
99 |
|
100 |
if(!memcmp(line, "[Events]", 8)) |
101 |
header_remaining= 2;
|
102 |
else if(line[0]=='[') |
103 |
header_remaining= INT_MAX; |
104 |
|
105 |
i= header_remaining==0;
|
106 |
|
107 |
if(i && get_pts(line) == AV_NOPTS_VALUE)
|
108 |
continue;
|
109 |
|
110 |
p = av_fast_realloc(*(dst[i]), &allocated[i], pos[i]+MAX_LINESIZE); |
111 |
if(!p)
|
112 |
goto fail;
|
113 |
*(dst[i])= p; |
114 |
memcpy(p + pos[i], line, len+1);
|
115 |
pos[i] += len; |
116 |
if(i) ass->event_count++;
|
117 |
else header_remaining--;
|
118 |
} |
119 |
st->codec->extradata_size= pos[0];
|
120 |
|
121 |
if(ass->event_count >= UINT_MAX / sizeof(*ass->event)) |
122 |
goto fail;
|
123 |
|
124 |
ass->event= av_malloc(ass->event_count * sizeof(*ass->event));
|
125 |
p= ass->event_buffer; |
126 |
for(i=0; i<ass->event_count; i++){ |
127 |
ass->event[i]= p; |
128 |
while(*p && *p != '\n') |
129 |
p++; |
130 |
p++; |
131 |
} |
132 |
|
133 |
qsort(ass->event, ass->event_count, sizeof(*ass->event), (void*)event_cmp); |
134 |
|
135 |
return 0; |
136 |
|
137 |
fail:
|
138 |
read_close(s); |
139 |
|
140 |
return -1; |
141 |
} |
142 |
|
143 |
static int read_packet(AVFormatContext *s, AVPacket *pkt) |
144 |
{ |
145 |
ASSContext *ass = s->priv_data; |
146 |
uint8_t *p, *end; |
147 |
|
148 |
if(ass->event_index >= ass->event_count)
|
149 |
return AVERROR(EIO);
|
150 |
|
151 |
p= ass->event[ ass->event_index ]; |
152 |
|
153 |
end= strchr(p, '\n');
|
154 |
av_new_packet(pkt, end ? end-p+1 : strlen(p));
|
155 |
pkt->flags |= AV_PKT_FLAG_KEY; |
156 |
pkt->pos= p - ass->event_buffer + s->streams[0]->codec->extradata_size;
|
157 |
pkt->pts= pkt->dts= get_pts(p); |
158 |
memcpy(pkt->data, p, pkt->size); |
159 |
|
160 |
ass->event_index++; |
161 |
|
162 |
return 0; |
163 |
} |
164 |
|
165 |
static int read_seek2(AVFormatContext *s, int stream_index, |
166 |
int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
|
167 |
{ |
168 |
ASSContext *ass = s->priv_data; |
169 |
|
170 |
if (flags & AVSEEK_FLAG_BYTE) {
|
171 |
return AVERROR_NOTSUPP;
|
172 |
} else if (flags & AVSEEK_FLAG_FRAME) { |
173 |
if (ts < 0 || ts >= ass->event_count) |
174 |
return AVERROR(ERANGE);
|
175 |
ass->event_index = ts; |
176 |
} else {
|
177 |
int i, idx = -1; |
178 |
int64_t min_ts_diff = INT64_MAX; |
179 |
if (stream_index == -1) { |
180 |
AVRational time_base = s->streams[0]->time_base;
|
181 |
ts = av_rescale_q(ts, AV_TIME_BASE_Q, time_base); |
182 |
min_ts = av_rescale_rnd(min_ts, time_base.den, |
183 |
time_base.num * (int64_t)AV_TIME_BASE, |
184 |
AV_ROUND_UP); |
185 |
max_ts = av_rescale_rnd(max_ts, time_base.den, |
186 |
time_base.num * (int64_t)AV_TIME_BASE, |
187 |
AV_ROUND_DOWN); |
188 |
} |
189 |
/* TODO: ass->event[] is sorted by pts so we could do a binary search */
|
190 |
for (i=0; i<ass->event_count; i++) { |
191 |
int64_t pts = get_pts(ass->event[i]); |
192 |
int64_t ts_diff = FFABS(pts - ts); |
193 |
if (pts >= min_ts && pts <= max_ts && ts_diff < min_ts_diff) {
|
194 |
min_ts_diff = ts_diff; |
195 |
idx = i; |
196 |
} |
197 |
} |
198 |
if (idx < 0) |
199 |
return AVERROR(ERANGE);
|
200 |
ass->event_index = idx; |
201 |
} |
202 |
return 0; |
203 |
} |
204 |
|
205 |
AVInputFormat ff_ass_demuxer = { |
206 |
.name = "ass",
|
207 |
.long_name = NULL_IF_CONFIG_SMALL("Advanced SubStation Alpha subtitle format"),
|
208 |
.priv_data_size = sizeof(ASSContext),
|
209 |
.read_probe = probe, |
210 |
.read_header = read_header, |
211 |
.read_packet = read_packet, |
212 |
.read_close = read_close, |
213 |
.read_seek2 = read_seek2, |
214 |
}; |