yading@11: /* yading@11: * Copyright (c) 2012 Clément Bœsch yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: #include "avformat.h" yading@11: #include "subtitles.h" yading@11: #include "libavutil/avassert.h" yading@11: #include "libavutil/avstring.h" yading@11: yading@11: AVPacket *ff_subtitles_queue_insert(FFDemuxSubtitlesQueue *q, yading@11: const uint8_t *event, int len, int merge) yading@11: { yading@11: AVPacket *subs, *sub; yading@11: yading@11: if (merge && q->nb_subs > 0) { yading@11: /* merge with previous event */ yading@11: yading@11: int old_len; yading@11: sub = &q->subs[q->nb_subs - 1]; yading@11: old_len = sub->size; yading@11: if (av_grow_packet(sub, len) < 0) yading@11: return NULL; yading@11: memcpy(sub->data + old_len, event, len); yading@11: } else { yading@11: /* new event */ yading@11: yading@11: if (q->nb_subs >= INT_MAX/sizeof(*q->subs) - 1) yading@11: return NULL; yading@11: subs = av_fast_realloc(q->subs, &q->allocated_size, yading@11: (q->nb_subs + 1) * sizeof(*q->subs)); yading@11: if (!subs) yading@11: return NULL; yading@11: q->subs = subs; yading@11: sub = &subs[q->nb_subs++]; yading@11: if (av_new_packet(sub, len) < 0) yading@11: return NULL; yading@11: sub->flags |= AV_PKT_FLAG_KEY; yading@11: sub->pts = sub->dts = 0; yading@11: memcpy(sub->data, event, len); yading@11: } yading@11: return sub; yading@11: } yading@11: yading@11: static int cmp_pkt_sub(const void *a, const void *b) yading@11: { yading@11: const AVPacket *s1 = a; yading@11: const AVPacket *s2 = b; yading@11: if (s1->pts == s2->pts) { yading@11: if (s1->pos == s2->pos) yading@11: return 0; yading@11: return s1->pos > s2->pos ? 1 : -1; yading@11: } yading@11: return s1->pts > s2->pts ? 1 : -1; yading@11: } yading@11: yading@11: void ff_subtitles_queue_finalize(FFDemuxSubtitlesQueue *q) yading@11: { yading@11: int i; yading@11: yading@11: qsort(q->subs, q->nb_subs, sizeof(*q->subs), cmp_pkt_sub); yading@11: for (i = 0; i < q->nb_subs; i++) yading@11: if (q->subs[i].duration == -1 && i < q->nb_subs - 1) yading@11: q->subs[i].duration = q->subs[i + 1].pts - q->subs[i].pts; yading@11: } yading@11: yading@11: int ff_subtitles_queue_read_packet(FFDemuxSubtitlesQueue *q, AVPacket *pkt) yading@11: { yading@11: AVPacket *sub = q->subs + q->current_sub_idx; yading@11: yading@11: if (q->current_sub_idx == q->nb_subs) yading@11: return AVERROR_EOF; yading@11: av_copy_packet(pkt, sub); yading@11: yading@11: pkt->dts = pkt->pts; yading@11: q->current_sub_idx++; yading@11: return 0; yading@11: } yading@11: yading@11: int ff_subtitles_queue_seek(FFDemuxSubtitlesQueue *q, AVFormatContext *s, int stream_index, yading@11: int64_t min_ts, int64_t ts, int64_t max_ts, int flags) yading@11: { yading@11: if (flags & AVSEEK_FLAG_BYTE) { yading@11: return AVERROR(ENOSYS); yading@11: } else if (flags & AVSEEK_FLAG_FRAME) { yading@11: if (ts < 0 || ts >= q->nb_subs) yading@11: return AVERROR(ERANGE); yading@11: q->current_sub_idx = ts; yading@11: } else { yading@11: int i, idx = -1; yading@11: int64_t min_ts_diff = INT64_MAX; yading@11: int64_t ts_selected; yading@11: /* TODO: q->subs[] is sorted by pts so we could do a binary search */ yading@11: for (i = 0; i < q->nb_subs; i++) { yading@11: int64_t pts = q->subs[i].pts; yading@11: uint64_t ts_diff = FFABS(pts - ts); yading@11: if (pts >= min_ts && pts <= max_ts && ts_diff < min_ts_diff) { yading@11: min_ts_diff = ts_diff; yading@11: idx = i; yading@11: } yading@11: } yading@11: if (idx < 0) yading@11: return AVERROR(ERANGE); yading@11: /* look back in the latest subtitles for overlapping subtitles */ yading@11: ts_selected = q->subs[idx].pts; yading@11: for (i = idx - 1; i >= 0; i--) { yading@11: if (q->subs[i].duration <= 0) yading@11: continue; yading@11: if (q->subs[i].pts > ts_selected - q->subs[i].duration) yading@11: idx = i; yading@11: else yading@11: break; yading@11: } yading@11: q->current_sub_idx = idx; yading@11: } yading@11: return 0; yading@11: } yading@11: yading@11: void ff_subtitles_queue_clean(FFDemuxSubtitlesQueue *q) yading@11: { yading@11: int i; yading@11: yading@11: for (i = 0; i < q->nb_subs; i++) yading@11: av_free_packet(&q->subs[i]); yading@11: av_freep(&q->subs); yading@11: q->nb_subs = q->allocated_size = q->current_sub_idx = 0; yading@11: } yading@11: yading@11: int ff_smil_extract_next_chunk(AVIOContext *pb, AVBPrint *buf, char *c) yading@11: { yading@11: int i = 0; yading@11: char end_chr; yading@11: yading@11: if (!*c) // cached char? yading@11: *c = avio_r8(pb); yading@11: if (!*c) yading@11: return 0; yading@11: yading@11: end_chr = *c == '<' ? '>' : '<'; yading@11: do { yading@11: av_bprint_chars(buf, *c, 1); yading@11: *c = avio_r8(pb); yading@11: i++; yading@11: } while (*c != end_chr && *c); yading@11: if (end_chr == '>') { yading@11: av_bprint_chars(buf, '>', 1); yading@11: *c = 0; yading@11: } yading@11: return i; yading@11: } yading@11: yading@11: const char *ff_smil_get_attr_ptr(const char *s, const char *attr) yading@11: { yading@11: int in_quotes = 0; yading@11: const int len = strlen(attr); yading@11: yading@11: while (*s) { yading@11: while (*s) { yading@11: if (!in_quotes && av_isspace(*s)) yading@11: break; yading@11: in_quotes ^= *s == '"'; // XXX: support escaping? yading@11: s++; yading@11: } yading@11: while (av_isspace(*s)) yading@11: s++; yading@11: if (!av_strncasecmp(s, attr, len) && s[len] == '=') yading@11: return s + len + 1 + (s[len + 1] == '"'); yading@11: } yading@11: return NULL; yading@11: } yading@11: yading@11: static inline int is_eol(char c) yading@11: { yading@11: return c == '\r' || c == '\n'; yading@11: } yading@11: yading@11: void ff_subtitles_read_chunk(AVIOContext *pb, AVBPrint *buf) yading@11: { yading@11: char eol_buf[5]; yading@11: int n = 0, i = 0, nb_eol = 0; yading@11: yading@11: av_bprint_clear(buf); yading@11: yading@11: for (;;) { yading@11: char c = avio_r8(pb); yading@11: yading@11: if (!c) yading@11: break; yading@11: yading@11: /* ignore all initial line breaks */ yading@11: if (n == 0 && is_eol(c)) yading@11: continue; yading@11: yading@11: /* line break buffering: we don't want to add the trailing \r\n */ yading@11: if (is_eol(c)) { yading@11: nb_eol += c == '\n'; yading@11: if (nb_eol == 2) yading@11: break; yading@11: eol_buf[i++] = c; yading@11: if (i == sizeof(eol_buf) - 1) yading@11: break; yading@11: continue; yading@11: } yading@11: yading@11: /* only one line break followed by data: we flush the line breaks yading@11: * buffer */ yading@11: if (i) { yading@11: eol_buf[i] = 0; yading@11: av_bprintf(buf, "%s", eol_buf); yading@11: i = nb_eol = 0; yading@11: } yading@11: yading@11: av_bprint_chars(buf, c, 1); yading@11: n++; yading@11: } yading@11: }