1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * TED Talks captions format decoder 3cabdff1aSopenharmony_ci * Copyright (c) 2012 Nicolas George 4cabdff1aSopenharmony_ci * 5cabdff1aSopenharmony_ci * This file is part of FFmpeg. 6cabdff1aSopenharmony_ci * 7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 11cabdff1aSopenharmony_ci * 12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15cabdff1aSopenharmony_ci * Lesser General Public License for more details. 16cabdff1aSopenharmony_ci * 17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20cabdff1aSopenharmony_ci */ 21cabdff1aSopenharmony_ci 22cabdff1aSopenharmony_ci#include "libavutil/bprint.h" 23cabdff1aSopenharmony_ci#include "libavutil/log.h" 24cabdff1aSopenharmony_ci#include "libavutil/opt.h" 25cabdff1aSopenharmony_ci#include "avformat.h" 26cabdff1aSopenharmony_ci#include "internal.h" 27cabdff1aSopenharmony_ci#include "subtitles.h" 28cabdff1aSopenharmony_ci 29cabdff1aSopenharmony_citypedef struct { 30cabdff1aSopenharmony_ci AVClass *class; 31cabdff1aSopenharmony_ci int64_t start_time; 32cabdff1aSopenharmony_ci FFDemuxSubtitlesQueue subs; 33cabdff1aSopenharmony_ci} TEDCaptionsDemuxer; 34cabdff1aSopenharmony_ci 35cabdff1aSopenharmony_cistatic const AVOption tedcaptions_options[] = { 36cabdff1aSopenharmony_ci { "start_time", "set the start time (offset) of the subtitles, in ms", 37cabdff1aSopenharmony_ci offsetof(TEDCaptionsDemuxer, start_time), AV_OPT_TYPE_INT64, 38cabdff1aSopenharmony_ci { .i64 = 15000 }, INT64_MIN, INT64_MAX, 39cabdff1aSopenharmony_ci AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM }, 40cabdff1aSopenharmony_ci { NULL }, 41cabdff1aSopenharmony_ci}; 42cabdff1aSopenharmony_ci 43cabdff1aSopenharmony_cistatic const AVClass tedcaptions_demuxer_class = { 44cabdff1aSopenharmony_ci .class_name = "tedcaptions_demuxer", 45cabdff1aSopenharmony_ci .item_name = av_default_item_name, 46cabdff1aSopenharmony_ci .option = tedcaptions_options, 47cabdff1aSopenharmony_ci .version = LIBAVUTIL_VERSION_INT, 48cabdff1aSopenharmony_ci}; 49cabdff1aSopenharmony_ci 50cabdff1aSopenharmony_ci#define BETWEEN(a, amin, amax) ((unsigned)((a) - (amin)) <= (amax) - (amin)) 51cabdff1aSopenharmony_ci 52cabdff1aSopenharmony_ci#define HEX_DIGIT_TEST(c) (BETWEEN(c, '0', '9') || BETWEEN((c) | 32, 'a', 'z')) 53cabdff1aSopenharmony_ci#define HEX_DIGIT_VAL(c) ((c) <= '9' ? (c) - '0' : ((c) | 32) - 'a' + 10) 54cabdff1aSopenharmony_ci#define ERR_CODE(c) ((c) < 0 ? (c) : AVERROR_INVALIDDATA) 55cabdff1aSopenharmony_ci 56cabdff1aSopenharmony_cistatic void av_bprint_utf8(AVBPrint *bp, unsigned c) 57cabdff1aSopenharmony_ci{ 58cabdff1aSopenharmony_ci int bytes, i; 59cabdff1aSopenharmony_ci 60cabdff1aSopenharmony_ci if (c <= 0x7F) { 61cabdff1aSopenharmony_ci av_bprint_chars(bp, c, 1); 62cabdff1aSopenharmony_ci return; 63cabdff1aSopenharmony_ci } 64cabdff1aSopenharmony_ci bytes = (av_log2(c) - 2) / 5; 65cabdff1aSopenharmony_ci av_bprint_chars(bp, (c >> (bytes * 6)) | ((0xFF80 >> bytes) & 0xFF), 1); 66cabdff1aSopenharmony_ci for (i = bytes - 1; i >= 0; i--) 67cabdff1aSopenharmony_ci av_bprint_chars(bp, ((c >> (i * 6)) & 0x3F) | 0x80, 1); 68cabdff1aSopenharmony_ci} 69cabdff1aSopenharmony_ci 70cabdff1aSopenharmony_cistatic void next_byte(AVIOContext *pb, int *cur_byte) 71cabdff1aSopenharmony_ci{ 72cabdff1aSopenharmony_ci uint8_t b; 73cabdff1aSopenharmony_ci int ret = avio_read(pb, &b, 1); 74cabdff1aSopenharmony_ci *cur_byte = ret > 0 ? b : ret == 0 ? AVERROR_EOF : ret; 75cabdff1aSopenharmony_ci} 76cabdff1aSopenharmony_ci 77cabdff1aSopenharmony_cistatic void skip_spaces(AVIOContext *pb, int *cur_byte) 78cabdff1aSopenharmony_ci{ 79cabdff1aSopenharmony_ci while (*cur_byte == ' ' || *cur_byte == '\t' || 80cabdff1aSopenharmony_ci *cur_byte == '\n' || *cur_byte == '\r') 81cabdff1aSopenharmony_ci next_byte(pb, cur_byte); 82cabdff1aSopenharmony_ci} 83cabdff1aSopenharmony_ci 84cabdff1aSopenharmony_cistatic int expect_byte(AVIOContext *pb, int *cur_byte, uint8_t c) 85cabdff1aSopenharmony_ci{ 86cabdff1aSopenharmony_ci skip_spaces(pb, cur_byte); 87cabdff1aSopenharmony_ci if (*cur_byte != c) 88cabdff1aSopenharmony_ci return ERR_CODE(*cur_byte); 89cabdff1aSopenharmony_ci next_byte(pb, cur_byte); 90cabdff1aSopenharmony_ci return 0; 91cabdff1aSopenharmony_ci} 92cabdff1aSopenharmony_ci 93cabdff1aSopenharmony_cistatic int parse_string(AVIOContext *pb, int *cur_byte, AVBPrint *bp, int full) 94cabdff1aSopenharmony_ci{ 95cabdff1aSopenharmony_ci int ret; 96cabdff1aSopenharmony_ci 97cabdff1aSopenharmony_ci ret = expect_byte(pb, cur_byte, '"'); 98cabdff1aSopenharmony_ci if (ret < 0) 99cabdff1aSopenharmony_ci return ret; 100cabdff1aSopenharmony_ci while (*cur_byte > 0 && *cur_byte != '"') { 101cabdff1aSopenharmony_ci if (*cur_byte == '\\') { 102cabdff1aSopenharmony_ci next_byte(pb, cur_byte); 103cabdff1aSopenharmony_ci if (*cur_byte < 0) 104cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 105cabdff1aSopenharmony_ci if ((*cur_byte | 32) == 'u') { 106cabdff1aSopenharmony_ci unsigned chr = 0, i; 107cabdff1aSopenharmony_ci for (i = 0; i < 4; i++) { 108cabdff1aSopenharmony_ci next_byte(pb, cur_byte); 109cabdff1aSopenharmony_ci if (!HEX_DIGIT_TEST(*cur_byte)) 110cabdff1aSopenharmony_ci return ERR_CODE(*cur_byte); 111cabdff1aSopenharmony_ci chr = chr * 16 + HEX_DIGIT_VAL(*cur_byte); 112cabdff1aSopenharmony_ci } 113cabdff1aSopenharmony_ci av_bprint_utf8(bp, chr); 114cabdff1aSopenharmony_ci } else { 115cabdff1aSopenharmony_ci av_bprint_chars(bp, *cur_byte, 1); 116cabdff1aSopenharmony_ci } 117cabdff1aSopenharmony_ci } else { 118cabdff1aSopenharmony_ci av_bprint_chars(bp, *cur_byte, 1); 119cabdff1aSopenharmony_ci } 120cabdff1aSopenharmony_ci next_byte(pb, cur_byte); 121cabdff1aSopenharmony_ci } 122cabdff1aSopenharmony_ci ret = expect_byte(pb, cur_byte, '"'); 123cabdff1aSopenharmony_ci if (ret < 0) 124cabdff1aSopenharmony_ci return ret; 125cabdff1aSopenharmony_ci if (full && !av_bprint_is_complete(bp)) 126cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 127cabdff1aSopenharmony_ci 128cabdff1aSopenharmony_ci return 0; 129cabdff1aSopenharmony_ci} 130cabdff1aSopenharmony_ci 131cabdff1aSopenharmony_cistatic int parse_label(AVIOContext *pb, int *cur_byte, AVBPrint *bp) 132cabdff1aSopenharmony_ci{ 133cabdff1aSopenharmony_ci int ret; 134cabdff1aSopenharmony_ci 135cabdff1aSopenharmony_ci av_bprint_init(bp, 0, AV_BPRINT_SIZE_AUTOMATIC); 136cabdff1aSopenharmony_ci ret = parse_string(pb, cur_byte, bp, 0); 137cabdff1aSopenharmony_ci if (ret < 0) 138cabdff1aSopenharmony_ci return ret; 139cabdff1aSopenharmony_ci ret = expect_byte(pb, cur_byte, ':'); 140cabdff1aSopenharmony_ci if (ret < 0) 141cabdff1aSopenharmony_ci return ret; 142cabdff1aSopenharmony_ci return 0; 143cabdff1aSopenharmony_ci} 144cabdff1aSopenharmony_ci 145cabdff1aSopenharmony_cistatic int parse_boolean(AVIOContext *pb, int *cur_byte, int *result) 146cabdff1aSopenharmony_ci{ 147cabdff1aSopenharmony_ci static const char * const text[] = { "false", "true" }; 148cabdff1aSopenharmony_ci const char *p; 149cabdff1aSopenharmony_ci int i; 150cabdff1aSopenharmony_ci 151cabdff1aSopenharmony_ci skip_spaces(pb, cur_byte); 152cabdff1aSopenharmony_ci for (i = 0; i < 2; i++) { 153cabdff1aSopenharmony_ci p = text[i]; 154cabdff1aSopenharmony_ci if (*cur_byte != *p) 155cabdff1aSopenharmony_ci continue; 156cabdff1aSopenharmony_ci for (; *p; p++, next_byte(pb, cur_byte)) 157cabdff1aSopenharmony_ci if (*cur_byte != *p) 158cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 159cabdff1aSopenharmony_ci if (BETWEEN(*cur_byte | 32, 'a', 'z')) 160cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 161cabdff1aSopenharmony_ci *result = i; 162cabdff1aSopenharmony_ci return 0; 163cabdff1aSopenharmony_ci } 164cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 165cabdff1aSopenharmony_ci} 166cabdff1aSopenharmony_ci 167cabdff1aSopenharmony_cistatic int parse_int(AVIOContext *pb, int *cur_byte, int64_t *result) 168cabdff1aSopenharmony_ci{ 169cabdff1aSopenharmony_ci int64_t val = 0; 170cabdff1aSopenharmony_ci 171cabdff1aSopenharmony_ci skip_spaces(pb, cur_byte); 172cabdff1aSopenharmony_ci if ((unsigned)*cur_byte - '0' > 9) 173cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 174cabdff1aSopenharmony_ci while (BETWEEN(*cur_byte, '0', '9')) { 175cabdff1aSopenharmony_ci if (val > INT_MAX/10 - (*cur_byte - '0')) 176cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 177cabdff1aSopenharmony_ci val = val * 10 + (*cur_byte - '0'); 178cabdff1aSopenharmony_ci next_byte(pb, cur_byte); 179cabdff1aSopenharmony_ci } 180cabdff1aSopenharmony_ci *result = val; 181cabdff1aSopenharmony_ci return 0; 182cabdff1aSopenharmony_ci} 183cabdff1aSopenharmony_ci 184cabdff1aSopenharmony_cistatic int parse_file(AVIOContext *pb, FFDemuxSubtitlesQueue *subs) 185cabdff1aSopenharmony_ci{ 186cabdff1aSopenharmony_ci int ret, cur_byte, start_of_par; 187cabdff1aSopenharmony_ci AVBPrint label, content; 188cabdff1aSopenharmony_ci int64_t pos, start, duration; 189cabdff1aSopenharmony_ci AVPacket *pkt; 190cabdff1aSopenharmony_ci 191cabdff1aSopenharmony_ci av_bprint_init(&content, 0, AV_BPRINT_SIZE_UNLIMITED); 192cabdff1aSopenharmony_ci 193cabdff1aSopenharmony_ci next_byte(pb, &cur_byte); 194cabdff1aSopenharmony_ci ret = expect_byte(pb, &cur_byte, '{'); 195cabdff1aSopenharmony_ci if (ret < 0) 196cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 197cabdff1aSopenharmony_ci ret = parse_label(pb, &cur_byte, &label); 198cabdff1aSopenharmony_ci if (ret < 0 || strcmp(label.str, "captions")) 199cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 200cabdff1aSopenharmony_ci ret = expect_byte(pb, &cur_byte, '['); 201cabdff1aSopenharmony_ci if (ret < 0) 202cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 203cabdff1aSopenharmony_ci while (1) { 204cabdff1aSopenharmony_ci start = duration = AV_NOPTS_VALUE; 205cabdff1aSopenharmony_ci ret = expect_byte(pb, &cur_byte, '{'); 206cabdff1aSopenharmony_ci if (ret < 0) 207cabdff1aSopenharmony_ci goto fail; 208cabdff1aSopenharmony_ci pos = avio_tell(pb) - 1; 209cabdff1aSopenharmony_ci while (1) { 210cabdff1aSopenharmony_ci ret = parse_label(pb, &cur_byte, &label); 211cabdff1aSopenharmony_ci if (ret < 0) 212cabdff1aSopenharmony_ci goto fail; 213cabdff1aSopenharmony_ci if (!strcmp(label.str, "startOfParagraph")) { 214cabdff1aSopenharmony_ci ret = parse_boolean(pb, &cur_byte, &start_of_par); 215cabdff1aSopenharmony_ci if (ret < 0) 216cabdff1aSopenharmony_ci goto fail; 217cabdff1aSopenharmony_ci } else if (!strcmp(label.str, "content")) { 218cabdff1aSopenharmony_ci ret = parse_string(pb, &cur_byte, &content, 1); 219cabdff1aSopenharmony_ci if (ret < 0) 220cabdff1aSopenharmony_ci goto fail; 221cabdff1aSopenharmony_ci } else if (!strcmp(label.str, "startTime")) { 222cabdff1aSopenharmony_ci ret = parse_int(pb, &cur_byte, &start); 223cabdff1aSopenharmony_ci if (ret < 0) 224cabdff1aSopenharmony_ci goto fail; 225cabdff1aSopenharmony_ci } else if (!strcmp(label.str, "duration")) { 226cabdff1aSopenharmony_ci ret = parse_int(pb, &cur_byte, &duration); 227cabdff1aSopenharmony_ci if (ret < 0) 228cabdff1aSopenharmony_ci goto fail; 229cabdff1aSopenharmony_ci } else { 230cabdff1aSopenharmony_ci ret = AVERROR_INVALIDDATA; 231cabdff1aSopenharmony_ci goto fail; 232cabdff1aSopenharmony_ci } 233cabdff1aSopenharmony_ci skip_spaces(pb, &cur_byte); 234cabdff1aSopenharmony_ci if (cur_byte != ',') 235cabdff1aSopenharmony_ci break; 236cabdff1aSopenharmony_ci next_byte(pb, &cur_byte); 237cabdff1aSopenharmony_ci } 238cabdff1aSopenharmony_ci ret = expect_byte(pb, &cur_byte, '}'); 239cabdff1aSopenharmony_ci if (ret < 0) 240cabdff1aSopenharmony_ci goto fail; 241cabdff1aSopenharmony_ci 242cabdff1aSopenharmony_ci if (!content.size || start == AV_NOPTS_VALUE || 243cabdff1aSopenharmony_ci duration == AV_NOPTS_VALUE) { 244cabdff1aSopenharmony_ci ret = AVERROR_INVALIDDATA; 245cabdff1aSopenharmony_ci goto fail; 246cabdff1aSopenharmony_ci } 247cabdff1aSopenharmony_ci pkt = ff_subtitles_queue_insert(subs, content.str, content.len, 0); 248cabdff1aSopenharmony_ci if (!pkt) { 249cabdff1aSopenharmony_ci ret = AVERROR(ENOMEM); 250cabdff1aSopenharmony_ci goto fail; 251cabdff1aSopenharmony_ci } 252cabdff1aSopenharmony_ci pkt->pos = pos; 253cabdff1aSopenharmony_ci pkt->pts = start; 254cabdff1aSopenharmony_ci pkt->duration = duration; 255cabdff1aSopenharmony_ci av_bprint_clear(&content); 256cabdff1aSopenharmony_ci 257cabdff1aSopenharmony_ci skip_spaces(pb, &cur_byte); 258cabdff1aSopenharmony_ci if (cur_byte != ',') 259cabdff1aSopenharmony_ci break; 260cabdff1aSopenharmony_ci next_byte(pb, &cur_byte); 261cabdff1aSopenharmony_ci } 262cabdff1aSopenharmony_ci ret = expect_byte(pb, &cur_byte, ']'); 263cabdff1aSopenharmony_ci if (ret < 0) 264cabdff1aSopenharmony_ci goto fail; 265cabdff1aSopenharmony_ci ret = expect_byte(pb, &cur_byte, '}'); 266cabdff1aSopenharmony_ci if (ret < 0) 267cabdff1aSopenharmony_ci goto fail; 268cabdff1aSopenharmony_ci skip_spaces(pb, &cur_byte); 269cabdff1aSopenharmony_ci if (cur_byte != AVERROR_EOF) 270cabdff1aSopenharmony_ci ret = ERR_CODE(cur_byte); 271cabdff1aSopenharmony_cifail: 272cabdff1aSopenharmony_ci av_bprint_finalize(&content, NULL); 273cabdff1aSopenharmony_ci return ret; 274cabdff1aSopenharmony_ci} 275cabdff1aSopenharmony_ci 276cabdff1aSopenharmony_cistatic av_cold int tedcaptions_read_header(AVFormatContext *avf) 277cabdff1aSopenharmony_ci{ 278cabdff1aSopenharmony_ci TEDCaptionsDemuxer *tc = avf->priv_data; 279cabdff1aSopenharmony_ci AVStream *st = avformat_new_stream(avf, NULL); 280cabdff1aSopenharmony_ci FFStream *sti; 281cabdff1aSopenharmony_ci int ret, i; 282cabdff1aSopenharmony_ci AVPacket *last; 283cabdff1aSopenharmony_ci 284cabdff1aSopenharmony_ci if (!st) 285cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 286cabdff1aSopenharmony_ci 287cabdff1aSopenharmony_ci sti = ffstream(st); 288cabdff1aSopenharmony_ci ret = parse_file(avf->pb, &tc->subs); 289cabdff1aSopenharmony_ci if (ret < 0) { 290cabdff1aSopenharmony_ci if (ret == AVERROR_INVALIDDATA) 291cabdff1aSopenharmony_ci av_log(avf, AV_LOG_ERROR, "Syntax error near offset %"PRId64".\n", 292cabdff1aSopenharmony_ci avio_tell(avf->pb)); 293cabdff1aSopenharmony_ci return ret; 294cabdff1aSopenharmony_ci } 295cabdff1aSopenharmony_ci ff_subtitles_queue_finalize(avf, &tc->subs); 296cabdff1aSopenharmony_ci for (i = 0; i < tc->subs.nb_subs; i++) 297cabdff1aSopenharmony_ci tc->subs.subs[i]->pts += tc->start_time; 298cabdff1aSopenharmony_ci 299cabdff1aSopenharmony_ci last = tc->subs.subs[tc->subs.nb_subs - 1]; 300cabdff1aSopenharmony_ci st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; 301cabdff1aSopenharmony_ci st->codecpar->codec_id = AV_CODEC_ID_TEXT; 302cabdff1aSopenharmony_ci avpriv_set_pts_info(st, 64, 1, 1000); 303cabdff1aSopenharmony_ci sti->probe_packets = 0; 304cabdff1aSopenharmony_ci st->start_time = 0; 305cabdff1aSopenharmony_ci st->duration = last->pts + last->duration; 306cabdff1aSopenharmony_ci sti->cur_dts = 0; 307cabdff1aSopenharmony_ci 308cabdff1aSopenharmony_ci return 0; 309cabdff1aSopenharmony_ci} 310cabdff1aSopenharmony_ci 311cabdff1aSopenharmony_cistatic int tedcaptions_read_packet(AVFormatContext *avf, AVPacket *packet) 312cabdff1aSopenharmony_ci{ 313cabdff1aSopenharmony_ci TEDCaptionsDemuxer *tc = avf->priv_data; 314cabdff1aSopenharmony_ci 315cabdff1aSopenharmony_ci return ff_subtitles_queue_read_packet(&tc->subs, packet); 316cabdff1aSopenharmony_ci} 317cabdff1aSopenharmony_ci 318cabdff1aSopenharmony_cistatic int tedcaptions_read_close(AVFormatContext *avf) 319cabdff1aSopenharmony_ci{ 320cabdff1aSopenharmony_ci TEDCaptionsDemuxer *tc = avf->priv_data; 321cabdff1aSopenharmony_ci 322cabdff1aSopenharmony_ci ff_subtitles_queue_clean(&tc->subs); 323cabdff1aSopenharmony_ci return 0; 324cabdff1aSopenharmony_ci} 325cabdff1aSopenharmony_ci 326cabdff1aSopenharmony_cistatic av_cold int tedcaptions_read_probe(const AVProbeData *p) 327cabdff1aSopenharmony_ci{ 328cabdff1aSopenharmony_ci static const char *const tags[] = { 329cabdff1aSopenharmony_ci "\"captions\"", "\"duration\"", "\"content\"", 330cabdff1aSopenharmony_ci "\"startOfParagraph\"", "\"startTime\"", 331cabdff1aSopenharmony_ci }; 332cabdff1aSopenharmony_ci unsigned i, count = 0; 333cabdff1aSopenharmony_ci const char *t; 334cabdff1aSopenharmony_ci 335cabdff1aSopenharmony_ci if (p->buf[strspn(p->buf, " \t\r\n")] != '{') 336cabdff1aSopenharmony_ci return 0; 337cabdff1aSopenharmony_ci for (i = 0; i < FF_ARRAY_ELEMS(tags); i++) { 338cabdff1aSopenharmony_ci if (!(t = strstr(p->buf, tags[i]))) 339cabdff1aSopenharmony_ci continue; 340cabdff1aSopenharmony_ci t += strlen(tags[i]); 341cabdff1aSopenharmony_ci t += strspn(t, " \t\r\n"); 342cabdff1aSopenharmony_ci if (*t == ':') 343cabdff1aSopenharmony_ci count++; 344cabdff1aSopenharmony_ci } 345cabdff1aSopenharmony_ci return count == FF_ARRAY_ELEMS(tags) ? AVPROBE_SCORE_MAX : 346cabdff1aSopenharmony_ci count ? AVPROBE_SCORE_EXTENSION : 0; 347cabdff1aSopenharmony_ci} 348cabdff1aSopenharmony_ci 349cabdff1aSopenharmony_cistatic int tedcaptions_read_seek(AVFormatContext *avf, int stream_index, 350cabdff1aSopenharmony_ci int64_t min_ts, int64_t ts, int64_t max_ts, 351cabdff1aSopenharmony_ci int flags) 352cabdff1aSopenharmony_ci{ 353cabdff1aSopenharmony_ci TEDCaptionsDemuxer *tc = avf->priv_data; 354cabdff1aSopenharmony_ci return ff_subtitles_queue_seek(&tc->subs, avf, stream_index, 355cabdff1aSopenharmony_ci min_ts, ts, max_ts, flags); 356cabdff1aSopenharmony_ci} 357cabdff1aSopenharmony_ci 358cabdff1aSopenharmony_ciconst AVInputFormat ff_tedcaptions_demuxer = { 359cabdff1aSopenharmony_ci .name = "tedcaptions", 360cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("TED Talks captions"), 361cabdff1aSopenharmony_ci .priv_data_size = sizeof(TEDCaptionsDemuxer), 362cabdff1aSopenharmony_ci .flags_internal = FF_FMT_INIT_CLEANUP, 363cabdff1aSopenharmony_ci .priv_class = &tedcaptions_demuxer_class, 364cabdff1aSopenharmony_ci .read_header = tedcaptions_read_header, 365cabdff1aSopenharmony_ci .read_packet = tedcaptions_read_packet, 366cabdff1aSopenharmony_ci .read_close = tedcaptions_read_close, 367cabdff1aSopenharmony_ci .read_probe = tedcaptions_read_probe, 368cabdff1aSopenharmony_ci .read_seek2 = tedcaptions_read_seek, 369cabdff1aSopenharmony_ci}; 370