3 * Copyright (c) 2008 Michael Niedermayer
5 * This file is part of Libav.
7 * Libav 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.
12 * Libav 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.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with Libav; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "libavutil/mathematics.h"
26 #define MAX_LINESIZE 2000
28 typedef struct ASSContext
{
29 uint8_t *event_buffer
;
31 unsigned int event_count
;
32 unsigned int event_index
;
35 static int probe(AVProbeData
*p
)
37 const char *header
= "[Script Info]";
39 if( !memcmp(p
->buf
, header
, strlen(header
))
40 || !memcmp(p
->buf
+3, header
, strlen(header
)))
41 return AVPROBE_SCORE_MAX
;
46 static int read_close(AVFormatContext
*s
)
48 ASSContext
*ass
= s
->priv_data
;
50 av_freep(&ass
->event_buffer
);
51 av_freep(&ass
->event
);
56 static int64_t get_pts(const uint8_t *p
)
58 int hour
, min
, sec
, hsec
;
60 if(sscanf(p
, "%*[^,],%d:%d:%d%*c%d", &hour
, &min
, &sec
, &hsec
) != 4)
61 return AV_NOPTS_VALUE
;
63 av_dlog(NULL
, "%d %d %d %d [%s]\n", hour
, min
, sec
, hsec
, p
);
71 static int event_cmp(const void *_a
, const void *_b
)
73 const uint8_t *const *a
= _a
, *const *b
= _b
;
74 return get_pts(*a
) - get_pts(*b
);
77 static int read_header(AVFormatContext
*s
)
79 int i
, len
, header_remaining
;
80 ASSContext
*ass
= s
->priv_data
;
81 AVIOContext
*pb
= s
->pb
;
84 uint8_t *p
, **dst
[2]={0};
87 st
= avformat_new_stream(s
, NULL
);
90 avpriv_set_pts_info(st
, 64, 1, 100);
91 st
->codec
->codec_type
= AVMEDIA_TYPE_SUBTITLE
;
92 st
->codec
->codec_id
= AV_CODEC_ID_SSA
;
94 header_remaining
= INT_MAX
;
95 dst
[0] = &st
->codec
->extradata
;
96 dst
[1] = &ass
->event_buffer
;
97 while(!pb
->eof_reached
){
98 uint8_t line
[MAX_LINESIZE
];
100 len
= ff_get_line(pb
, line
, sizeof(line
));
102 if(!memcmp(line
, "[Events]", 8))
104 else if(line
[0]=='[')
105 header_remaining
= INT_MAX
;
107 i
= header_remaining
==0;
109 if(i
&& get_pts(line
) == AV_NOPTS_VALUE
)
112 p
= av_fast_realloc(*(dst
[i
]), &allocated
[i
], pos
[i
]+MAX_LINESIZE
);
116 memcpy(p
+ pos
[i
], line
, len
+1);
118 if(i
) ass
->event_count
++;
119 else header_remaining
--;
121 st
->codec
->extradata_size
= pos
[0];
123 if(ass
->event_count
>= UINT_MAX
/ sizeof(*ass
->event
))
126 ass
->event
= av_malloc(ass
->event_count
* sizeof(*ass
->event
));
127 p
= ass
->event_buffer
;
128 for(i
=0; i
<ass
->event_count
; i
++){
130 while(*p
&& *p
!= '\n')
135 qsort(ass
->event
, ass
->event_count
, sizeof(*ass
->event
), event_cmp
);
145 static int read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
147 ASSContext
*ass
= s
->priv_data
;
150 if(ass
->event_index
>= ass
->event_count
)
153 p
= ass
->event
[ ass
->event_index
];
155 end
= strchr(p
, '\n');
156 av_new_packet(pkt
, end
? end
-p
+1 : strlen(p
));
157 pkt
->flags
|= AV_PKT_FLAG_KEY
;
158 pkt
->pos
= p
- ass
->event_buffer
+ s
->streams
[0]->codec
->extradata_size
;
159 pkt
->pts
= pkt
->dts
= get_pts(p
);
160 memcpy(pkt
->data
, p
, pkt
->size
);
167 static int read_seek2(AVFormatContext
*s
, int stream_index
,
168 int64_t min_ts
, int64_t ts
, int64_t max_ts
, int flags
)
170 ASSContext
*ass
= s
->priv_data
;
172 if (flags
& AVSEEK_FLAG_BYTE
) {
173 return AVERROR(ENOSYS
);
174 } else if (flags
& AVSEEK_FLAG_FRAME
) {
175 if (ts
< 0 || ts
>= ass
->event_count
)
176 return AVERROR(ERANGE
);
177 ass
->event_index
= ts
;
180 int64_t min_ts_diff
= INT64_MAX
;
181 if (stream_index
== -1) {
182 AVRational time_base
= s
->streams
[0]->time_base
;
183 ts
= av_rescale_q(ts
, AV_TIME_BASE_Q
, time_base
);
184 min_ts
= av_rescale_rnd(min_ts
, time_base
.den
,
185 time_base
.num
* (int64_t)AV_TIME_BASE
,
187 max_ts
= av_rescale_rnd(max_ts
, time_base
.den
,
188 time_base
.num
* (int64_t)AV_TIME_BASE
,
191 /* TODO: ass->event[] is sorted by pts so we could do a binary search */
192 for (i
=0; i
<ass
->event_count
; i
++) {
193 int64_t pts
= get_pts(ass
->event
[i
]);
194 int64_t ts_diff
= FFABS(pts
- ts
);
195 if (pts
>= min_ts
&& pts
<= max_ts
&& ts_diff
< min_ts_diff
) {
196 min_ts_diff
= ts_diff
;
201 return AVERROR(ERANGE
);
202 ass
->event_index
= idx
;
207 AVInputFormat ff_ass_demuxer
= {
209 .long_name
= NULL_IF_CONFIG_SMALL("SSA (SubStation Alpha) subtitle"),
210 .priv_data_size
= sizeof(ASSContext
),
212 .read_header
= read_header
,
213 .read_packet
= read_packet
,
214 .read_close
= read_close
,
215 .read_seek2
= read_seek2
,