2 * Copyright (c) 2012 Clément Bœsch
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 * WebVTT subtitle demuxer
24 * @see http://dev.w3.org/html5/webvtt/
29 #include "subtitles.h"
30 #include "libavutil/bprint.h"
31 #include "libavutil/intreadwrite.h"
32 #include "libavutil/opt.h"
36 FFDemuxSubtitlesQueue q
;
40 static int webvtt_probe(AVProbeData
*p
)
42 const uint8_t *ptr
= p
->buf
;
44 if (AV_RB24(ptr
) == 0xEFBBBF)
45 ptr
+= 3; /* skip UTF-8 BOM */
46 if (!strncmp(ptr
, "WEBVTT", 6) &&
47 (!ptr
[6] || strchr("\n\r\t ", ptr
[6])))
48 return AVPROBE_SCORE_MAX
;
52 static int64_t read_ts(const char *s
)
55 if (sscanf(s
, "%u:%u:%u.%u", &hh
, &mm
, &ss
, &ms
) == 4) return (hh
*3600LL + mm
*60LL + ss
) * 1000LL + ms
;
56 if (sscanf(s
, "%u:%u.%u", &mm
, &ss
, &ms
) == 3) return ( mm
*60LL + ss
) * 1000LL + ms
;
57 return AV_NOPTS_VALUE
;
60 static int webvtt_read_header(AVFormatContext
*s
)
62 WebVTTContext
*webvtt
= s
->priv_data
;
65 AVStream
*st
= avformat_new_stream(s
, NULL
);
68 return AVERROR(ENOMEM
);
69 avpriv_set_pts_info(st
, 64, 1, 1000);
70 st
->codec
->codec_type
= AVMEDIA_TYPE_SUBTITLE
;
71 st
->codec
->codec_id
= AV_CODEC_ID_WEBVTT
;
72 st
->disposition
|= webvtt
->kind
;
74 av_bprint_init(&header
, 0, AV_BPRINT_SIZE_UNLIMITED
);
75 av_bprint_init(&cue
, 0, AV_BPRINT_SIZE_UNLIMITED
);
81 const char *p
, *identifier
, *settings
;
82 int identifier_len
, settings_len
;
83 int64_t ts_start
, ts_end
;
85 ff_subtitles_read_chunk(s
->pb
, &cue
);
90 p
= identifier
= cue
.str
;
91 pos
= avio_tell(s
->pb
);
93 /* ignore header chunk */
94 if (!strncmp(p
, "\xEF\xBB\xBFWEBVTT", 9) ||
95 !strncmp(p
, "WEBVTT", 6))
98 /* optional cue identifier (can be a number like in SRT or some kind of
100 for (i
= 0; p
[i
] && p
[i
] != '\n' && p
[i
] != '\r'; i
++) {
101 if (!strncmp(p
+ i
, "-->", 3)) {
109 identifier_len
= strcspn(p
, "\r\n");
118 if ((ts_start
= read_ts(p
)) == AV_NOPTS_VALUE
)
120 if (!(p
= strstr(p
, "-->")))
123 do p
++; while (*p
== ' ' || *p
== '\t');
124 if ((ts_end
= read_ts(p
)) == AV_NOPTS_VALUE
)
127 /* optional cue settings */
128 p
+= strcspn(p
, "\n\t ");
129 while (*p
== '\t' || *p
== ' ')
132 settings_len
= strcspn(p
, "\r\n");
140 sub
= ff_subtitles_queue_insert(&webvtt
->q
, p
, strlen(p
), 0);
142 res
= AVERROR(ENOMEM
);
147 sub
->duration
= ts_end
- ts_start
;
149 #define SET_SIDE_DATA(name, type) do { \
151 uint8_t *buf = av_packet_new_side_data(sub, type, name##_len); \
153 res = AVERROR(ENOMEM); \
156 memcpy(buf, name, name##_len); \
160 SET_SIDE_DATA(identifier
, AV_PKT_DATA_WEBVTT_IDENTIFIER
);
161 SET_SIDE_DATA(settings
, AV_PKT_DATA_WEBVTT_SETTINGS
);
164 ff_subtitles_queue_finalize(&webvtt
->q
);
167 av_bprint_finalize(&cue
, NULL
);
168 av_bprint_finalize(&header
, NULL
);
172 static int webvtt_read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
174 WebVTTContext
*webvtt
= s
->priv_data
;
175 return ff_subtitles_queue_read_packet(&webvtt
->q
, pkt
);
178 static int webvtt_read_seek(AVFormatContext
*s
, int stream_index
,
179 int64_t min_ts
, int64_t ts
, int64_t max_ts
, int flags
)
181 WebVTTContext
*webvtt
= s
->priv_data
;
182 return ff_subtitles_queue_seek(&webvtt
->q
, s
, stream_index
,
183 min_ts
, ts
, max_ts
, flags
);
186 static int webvtt_read_close(AVFormatContext
*s
)
188 WebVTTContext
*webvtt
= s
->priv_data
;
189 ff_subtitles_queue_clean(&webvtt
->q
);
193 #define OFFSET(x) offsetof(WebVTTContext, x)
194 #define KIND_FLAGS AV_OPT_FLAG_SUBTITLE_PARAM
196 static const AVOption options
[] = {
197 { "kind", "Set kind of WebVTT track", OFFSET(kind
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, INT_MAX
, KIND_FLAGS
, "webvtt_kind" },
198 { "subtitles", "WebVTT subtitles kind", 0, AV_OPT_TYPE_CONST
, { .i64
= 0 }, INT_MIN
, INT_MAX
, 0, "webvtt_kind" },
199 { "captions", "WebVTT captions kind", 0, AV_OPT_TYPE_CONST
, { .i64
= AV_DISPOSITION_CAPTIONS
}, INT_MIN
, INT_MAX
, 0, "webvtt_kind" },
200 { "descriptions", "WebVTT descriptions kind", 0, AV_OPT_TYPE_CONST
, { .i64
= AV_DISPOSITION_DESCRIPTIONS
}, INT_MIN
, INT_MAX
, 0, "webvtt_kind" },
201 { "metadata", "WebVTT metadata kind", 0, AV_OPT_TYPE_CONST
, { .i64
= AV_DISPOSITION_METADATA
}, INT_MIN
, INT_MAX
, 0, "webvtt_kind" },
205 static const AVClass webvtt_demuxer_class
= {
206 .class_name
= "WebVTT demuxer",
207 .item_name
= av_default_item_name
,
209 .version
= LIBAVUTIL_VERSION_INT
,
212 AVInputFormat ff_webvtt_demuxer
= {
214 .long_name
= NULL_IF_CONFIG_SMALL("WebVTT subtitle"),
215 .priv_data_size
= sizeof(WebVTTContext
),
216 .read_probe
= webvtt_probe
,
217 .read_header
= webvtt_read_header
,
218 .read_packet
= webvtt_read_packet
,
219 .read_seek2
= webvtt_read_seek
,
220 .read_close
= webvtt_read_close
,
222 .priv_class
= &webvtt_demuxer_class
,