tedcaptionsdec.c
Go to the documentation of this file.
1 /*
2  * TED Talks captions format decoder
3  * Copyright (c) 2012 Nicolas George
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 "libavutil/bprint.h"
23 #include "libavutil/log.h"
24 #include "libavutil/opt.h"
25 #include "avformat.h"
26 #include "internal.h"
27 #include "subtitles.h"
28 
29 typedef struct {
30  AVClass *class;
31  int64_t start_time;
34 
35 static const AVOption tedcaptions_options[] = {
36  { "start_time", "set the start time (offset) of the subtitles, in ms",
38  { .i64 = 15000 }, INT64_MIN, INT64_MAX,
40  { NULL },
41 };
42 
44  .class_name = "tedcaptions_demuxer",
45  .item_name = av_default_item_name,
46  .option = tedcaptions_options,
47  .version = LIBAVUTIL_VERSION_INT,
48 };
49 
50 #define BETWEEN(a, amin, amax) ((unsigned)((a) - (amin)) <= (amax) - (amin))
51 
52 #define HEX_DIGIT_TEST(c) (BETWEEN(c, '0', '9') || BETWEEN((c) | 32, 'a', 'z'))
53 #define HEX_DIGIT_VAL(c) ((c) <= '9' ? (c) - '0' : ((c) | 32) - 'a' + 10)
54 #define ERR_CODE(c) (c < 0 ? c : AVERROR_INVALIDDATA)
55 
56 static void av_bprint_utf8(AVBPrint *bp, unsigned c)
57 {
58  int bytes, i;
59 
60  if (c <= 0x7F) {
61  av_bprint_chars(bp, c, 1);
62  return;
63  }
64  bytes = (av_log2(c) - 2) / 5;
65  av_bprint_chars(bp, (c >> (bytes * 6)) | ((0xFF80 >> bytes) & 0xFF), 1);
66  for (i = bytes - 1; i >= 0; i--)
67  av_bprint_chars(bp, ((c >> (i * 6)) & 0x3F) | 0x80, 1);
68 }
69 
70 static void next_byte(AVIOContext *pb, int *cur_byte)
71 {
72  uint8_t b;
73  int ret = avio_read(pb, &b, 1);
74  *cur_byte = ret > 0 ? b : ret == 0 ? AVERROR_EOF : ret;
75 }
76 
77 static void skip_spaces(AVIOContext *pb, int *cur_byte)
78 {
79  while (*cur_byte == ' ' || *cur_byte == '\t' ||
80  *cur_byte == '\n' || *cur_byte == '\r')
81  next_byte(pb, cur_byte);
82 }
83 
84 static int expect_byte(AVIOContext *pb, int *cur_byte, uint8_t c)
85 {
86  skip_spaces(pb, cur_byte);
87  if (*cur_byte != c)
88  return ERR_CODE(*cur_byte);
89  next_byte(pb, cur_byte);
90  return 0;
91 }
92 
93 static int parse_string(AVIOContext *pb, int *cur_byte, AVBPrint *bp, int full)
94 {
95  int ret;
96 
97  av_bprint_init(bp, 0, full ? -1 : 1);
98  ret = expect_byte(pb, cur_byte, '"');
99  if (ret < 0)
100  goto fail;
101  while (*cur_byte > 0 && *cur_byte != '"') {
102  if (*cur_byte == '\\') {
103  next_byte(pb, cur_byte);
104  if (*cur_byte < 0) {
105  ret = AVERROR_INVALIDDATA;
106  goto fail;
107  }
108  if ((*cur_byte | 32) == 'u') {
109  unsigned chr = 0, i;
110  for (i = 0; i < 4; i++) {
111  next_byte(pb, cur_byte);
112  if (!HEX_DIGIT_TEST(*cur_byte)) {
113  ret = ERR_CODE(*cur_byte);
114  goto fail;
115  }
116  chr = chr * 16 + HEX_DIGIT_VAL(*cur_byte);
117  }
118  av_bprint_utf8(bp, chr);
119  } else {
120  av_bprint_chars(bp, *cur_byte, 1);
121  }
122  } else {
123  av_bprint_chars(bp, *cur_byte, 1);
124  }
125  next_byte(pb, cur_byte);
126  }
127  ret = expect_byte(pb, cur_byte, '"');
128  if (ret < 0)
129  goto fail;
130  if (full && !av_bprint_is_complete(bp)) {
131  ret = AVERROR(ENOMEM);
132  goto fail;
133  }
134  return 0;
135 
136 fail:
138  return ret;
139 }
140 
141 static int parse_label(AVIOContext *pb, int *cur_byte, AVBPrint *bp)
142 {
143  int ret;
144 
145  ret = parse_string(pb, cur_byte, bp, 0);
146  if (ret < 0)
147  return ret;
148  ret = expect_byte(pb, cur_byte, ':');
149  if (ret < 0)
150  return ret;
151  return 0;
152 }
153 
154 static int parse_boolean(AVIOContext *pb, int *cur_byte, int *result)
155 {
156  const char *text[] = { "false", "true" }, *p;
157  int i;
158 
159  skip_spaces(pb, cur_byte);
160  for (i = 0; i < 2; i++) {
161  p = text[i];
162  if (*cur_byte != *p)
163  continue;
164  for (; *p; p++, next_byte(pb, cur_byte))
165  if (*cur_byte != *p)
166  return AVERROR_INVALIDDATA;
167  if (BETWEEN(*cur_byte | 32, 'a', 'z'))
168  return AVERROR_INVALIDDATA;
169  *result = i;
170  return 0;
171  }
172  return AVERROR_INVALIDDATA;
173 }
174 
175 static int parse_int(AVIOContext *pb, int *cur_byte, int64_t *result)
176 {
177  int64_t val = 0;
178 
179  skip_spaces(pb, cur_byte);
180  if ((unsigned)*cur_byte - '0' > 9)
181  return AVERROR_INVALIDDATA;
182  while (BETWEEN(*cur_byte, '0', '9')) {
183  val = val * 10 + (*cur_byte - '0');
184  next_byte(pb, cur_byte);
185  }
186  *result = val;
187  return 0;
188 }
189 
191 {
192  int ret, cur_byte, start_of_par;
193  AVBPrint label, content;
194  int64_t pos, start, duration;
195  AVPacket *pkt;
196 
197  next_byte(pb, &cur_byte);
198  ret = expect_byte(pb, &cur_byte, '{');
199  if (ret < 0)
200  return AVERROR_INVALIDDATA;
201  ret = parse_label(pb, &cur_byte, &label);
202  if (ret < 0 || strcmp(label.str, "captions"))
203  return AVERROR_INVALIDDATA;
204  ret = expect_byte(pb, &cur_byte, '[');
205  if (ret < 0)
206  return AVERROR_INVALIDDATA;
207  while (1) {
208  content.size = 0;
209  start = duration = AV_NOPTS_VALUE;
210  ret = expect_byte(pb, &cur_byte, '{');
211  if (ret < 0)
212  return ret;
213  pos = avio_tell(pb) - 1;
214  while (1) {
215  ret = parse_label(pb, &cur_byte, &label);
216  if (ret < 0)
217  return ret;
218  if (!strcmp(label.str, "startOfParagraph")) {
219  ret = parse_boolean(pb, &cur_byte, &start_of_par);
220  if (ret < 0)
221  return ret;
222  } else if (!strcmp(label.str, "content")) {
223  ret = parse_string(pb, &cur_byte, &content, 1);
224  if (ret < 0)
225  return ret;
226  } else if (!strcmp(label.str, "startTime")) {
227  ret = parse_int(pb, &cur_byte, &start);
228  if (ret < 0)
229  return ret;
230  } else if (!strcmp(label.str, "duration")) {
231  ret = parse_int(pb, &cur_byte, &duration);
232  if (ret < 0)
233  return ret;
234  } else {
235  return AVERROR_INVALIDDATA;
236  }
237  skip_spaces(pb, &cur_byte);
238  if (cur_byte != ',')
239  break;
240  next_byte(pb, &cur_byte);
241  }
242  ret = expect_byte(pb, &cur_byte, '}');
243  if (ret < 0)
244  return ret;
245 
246  if (!content.size || start == AV_NOPTS_VALUE ||
247  duration == AV_NOPTS_VALUE)
248  return AVERROR_INVALIDDATA;
249  pkt = ff_subtitles_queue_insert(subs, content.str, content.len, 0);
250  if (!pkt)
251  return AVERROR(ENOMEM);
252  pkt->pos = pos;
253  pkt->pts = start;
254  pkt->duration = duration;
255  av_bprint_finalize(&content, NULL);
256 
257  skip_spaces(pb, &cur_byte);
258  if (cur_byte != ',')
259  break;
260  next_byte(pb, &cur_byte);
261  }
262  ret = expect_byte(pb, &cur_byte, ']');
263  if (ret < 0)
264  return ret;
265  ret = expect_byte(pb, &cur_byte, '}');
266  if (ret < 0)
267  return ret;
268  skip_spaces(pb, &cur_byte);
269  if (cur_byte != AVERROR_EOF)
270  return ERR_CODE(cur_byte);
271  return 0;
272 }
273 
275 {
277  AVStream *st;
278  int ret, i;
279  AVPacket *last;
280 
281  ret = parse_file(avf->pb, &tc->subs);
282  if (ret < 0) {
283  if (ret == AVERROR_INVALIDDATA)
284  av_log(avf, AV_LOG_ERROR, "Syntax error near offset %"PRId64".\n",
285  avio_tell(avf->pb));
287  return ret;
288  }
290  for (i = 0; i < tc->subs.nb_subs; i++)
291  tc->subs.subs[i].pts += tc->start_time;
292 
293  last = &tc->subs.subs[tc->subs.nb_subs - 1];
294  st = avformat_new_stream(avf, NULL);
295  if (!st)
296  return AVERROR(ENOMEM);
299  avpriv_set_pts_info(st, 64, 1, 1000);
300  st->probe_packets = 0;
301  st->start_time = 0;
302  st->duration = last->pts + last->duration;
303  st->cur_dts = 0;
304 
305  return 0;
306 }
307 
309 {
311 
312  return ff_subtitles_queue_read_packet(&tc->subs, packet);
313 }
314 
316 {
318 
320  return 0;
321 }
322 
324 {
325  static const char *const tags[] = {
326  "\"captions\"", "\"duration\"", "\"content\"",
327  "\"startOfParagraph\"", "\"startTime\"",
328  };
329  unsigned i, count = 0;
330  const char *t;
331 
332  if (p->buf[strspn(p->buf, " \t\r\n")] != '{')
333  return 0;
334  for (i = 0; i < FF_ARRAY_ELEMS(tags); i++) {
335  if (!(t = strstr(p->buf, tags[i])))
336  continue;
337  t += strlen(tags[i]);
338  t += strspn(t, " \t\r\n");
339  if (*t == ':')
340  count++;
341  }
342  return count == FF_ARRAY_ELEMS(tags) ? AVPROBE_SCORE_MAX :
343  count ? AVPROBE_SCORE_MAX / 2 : 0;
344 }
345 
346 static int tedcaptions_read_seek(AVFormatContext *avf, int stream_index,
347  int64_t min_ts, int64_t ts, int64_t max_ts,
348  int flags)
349 {
351  return ff_subtitles_queue_seek(&tc->subs, avf, stream_index,
352  min_ts, ts, max_ts, flags);
353 }
354 
356  .name = "tedcaptions",
357  .long_name = NULL_IF_CONFIG_SMALL("TED Talks captions"),
358  .priv_data_size = sizeof(TEDCaptionsDemuxer),
359  .priv_class = &tedcaptions_demuxer_class,
364  .read_seek2 = tedcaptions_read_seek,
365 };
Bytestream IO Context.
Definition: avio.h:68
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
static int tedcaptions_read_seek(AVFormatContext *avf, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
AVOption.
Definition: opt.h:251
#define AV_OPT_FLAG_SUBTITLE_PARAM
Definition: opt.h:286
av_default_item_name
int64_t pos
byte position in stream, -1 if unknown
void avpriv_set_pts_info(AVStream *s, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
int probe_packets
Definition: avformat.h:793
void ff_subtitles_queue_clean(FFDemuxSubtitlesQueue *q)
Remove and destroy all the subtitles packets.
Definition: subtitles.c:134
#define tc
Definition: regdef.h:69
#define FF_ARRAY_ELEMS(a)
AVPacket * ff_subtitles_queue_insert(FFDemuxSubtitlesQueue *q, const uint8_t *event, int len, int merge)
Insert a new subtitle event.
Definition: subtitles.c:26
text(-8, 1,'a)')
static void av_bprint_utf8(AVBPrint *bp, unsigned c)
AVInputFormat ff_tedcaptions_demuxer
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:193
#define BETWEEN(a, amin, amax)
Format I/O context.
Definition: avformat.h:944
int64_t cur_dts
Definition: avformat.h:785
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:55
static const AVClass tedcaptions_demuxer_class
static int64_t start_time
Definition: ffplay.c:293
uint8_t
#define av_cold
Definition: attributes.h:78
FFDemuxSubtitlesQueue subs
AVOptions.
int ff_subtitles_queue_read_packet(FFDemuxSubtitlesQueue *q, AVPacket *pkt)
Generic read_packet() callback for subtitles demuxers using this queue system.
Definition: subtitles.c:82
static AVPacket pkt
Definition: demuxing.c:56
#define b
Definition: input.c:42
static int tedcaptions_read_packet(AVFormatContext *avf, AVPacket *packet)
AVStream * avformat_new_stream(AVFormatContext *s, const AVCodec *c)
Add a new stream to a media file.
static void skip_spaces(AVIOContext *pb, int *cur_byte)
#define AVERROR_EOF
End of file.
Definition: error.h:55
static av_cold int read_close(AVFormatContext *ctx)
Definition: libcdio.c:145
static av_always_inline int64_t avio_tell(AVIOContext *s)
ftell() equivalent for AVIOContext.
Definition: avio.h:248
static int64_t duration
Definition: ffplay.c:294
int duration
Duration of this packet in AVStream->time_base units, 0 if unknown.
int avio_read(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition: aviobuf.c:478
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Init a print buffer.
Definition: bprint.c:68
static int av_bprint_is_complete(AVBPrint *buf)
Test if the print buffer is complete (not truncated).
Definition: bprint.h:166
#define ERR_CODE(c)
void av_log(void *avcl, int level, const char *fmt,...)
Definition: log.c:246
static int parse_label(AVIOContext *pb, int *cur_byte, AVBPrint *bp)
AVCodecContext * codec
Codec context associated with this stream.
Definition: avformat.h:662
static int tedcaptions_read_close(AVFormatContext *avf)
unsigned char * buf
Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero.
Definition: avformat.h:336
Buffer to print data progressively.
Definition: bprint.h:75
static int read_probe(AVProbeData *pd)
ret
Definition: avfilter.c:821
static int parse_file(AVIOContext *pb, FFDemuxSubtitlesQueue *subs)
t
Definition: genspecsines3.m:6
void ff_subtitles_queue_finalize(FFDemuxSubtitlesQueue *q)
Set missing durations and sort subtitles by PTS, and then byte position.
Definition: subtitles.c:72
static void next_byte(AVIOContext *pb, int *cur_byte)
LIBAVUTIL_VERSION_INT
Definition: eval.c:55
static av_cold int tedcaptions_read_header(AVFormatContext *avf)
static int read_header(FFV1Context *f)
Definition: ffv1dec.c:517
Stream structure.
Definition: avformat.h:643
#define HEX_DIGIT_TEST(c)
NULL
Definition: eval.c:55
int ff_subtitles_queue_seek(FFDemuxSubtitlesQueue *q, AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
Update current_sub_idx to emulate a seek.
Definition: subtitles.c:95
enum AVMediaType codec_type
enum AVCodecID codec_id
AVIOContext * pb
I/O context.
Definition: avformat.h:977
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:148
static int read_packet(AVFormatContext *ctx, AVPacket *pkt)
Definition: libcdio.c:114
Describe the class of an AVClass context structure.
Definition: log.h:50
synthesis window for stochastic i
#define AV_OPT_FLAG_DECODING_PARAM
a generic parameter which can be set by the user for demuxing or decoding
Definition: opt.h:282
This structure contains the data a format has to probe a file.
Definition: avformat.h:334
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFilterBuffer structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later.That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another.Buffer references ownership and permissions
static int flags
Definition: cpu.c:23
int64_t duration
Decoding: duration of the stream, in stream time base.
Definition: avformat.h:696
#define AVPROBE_SCORE_MAX
maximum score, half of that is used for file-extension-based detection
Definition: avformat.h:340
#define HEX_DIGIT_VAL(c)
Main libavformat public API header.
static int expect_byte(AVIOContext *pb, int *cur_byte, uint8_t c)
raw UTF-8 text
int64_t start_time
Decoding: pts of the first frame of the stream in presentation order, in stream time base...
Definition: avformat.h:689
static double c[64]
static const AVOption tedcaptions_options[]
AVPacket * subs
array of subtitles packets
Definition: subtitles.h:29
#define av_log2
Definition: intmath.h:89
void * priv_data
Format private data.
Definition: avformat.h:964
static int parse_string(AVIOContext *pb, int *cur_byte, AVBPrint *bp, int full)
void INT64 INT64 count
Definition: avisynth_c.h:594
void INT64 start
Definition: avisynth_c.h:594
const char * name
A comma separated list of short names for the format.
Definition: avformat.h:461
static int parse_boolean(AVIOContext *pb, int *cur_byte, int *result)
This structure stores compressed data.
int nb_subs
number of subtitles packets
Definition: subtitles.h:30
static av_cold int tedcaptions_read_probe(AVProbeData *p)
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
#define AV_NOPTS_VALUE
Undefined timestamp value.
Definition: avutil.h:190
void av_bprint_chars(AVBPrint *buf, char c, unsigned n)
Append char c n times to a print buffer.
Definition: bprint.c:116
static int parse_int(AVIOContext *pb, int *cur_byte, int64_t *result)