LCOV - code coverage report
Current view: top level - libavformat - subtitles.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 208 254 81.9 %
Date: 2017-12-18 20:14:19 Functions: 22 22 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2012-2013 Clément Bœsch <u pkh me>
       3             :  *
       4             :  * This file is part of FFmpeg.
       5             :  *
       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.
      10             :  *
      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.
      15             :  *
      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
      19             :  */
      20             : 
      21             : #include "avformat.h"
      22             : #include "subtitles.h"
      23             : #include "avio_internal.h"
      24             : #include "libavutil/avassert.h"
      25             : #include "libavutil/avstring.h"
      26             : 
      27       30664 : void ff_text_init_avio(void *s, FFTextReader *r, AVIOContext *pb)
      28             : {
      29             :     int i;
      30       30664 :     r->pb = pb;
      31       30664 :     r->buf_pos = r->buf_len = 0;
      32       30664 :     r->type = FF_UTF_8;
      33       91992 :     for (i = 0; i < 2; i++)
      34       61328 :         r->buf[r->buf_len++] = avio_r8(r->pb);
      35       30664 :     if (strncmp("\xFF\xFE", r->buf, 2) == 0) {
      36           0 :         r->type = FF_UTF16LE;
      37           0 :         r->buf_pos += 2;
      38       30664 :     } else if (strncmp("\xFE\xFF", r->buf, 2) == 0) {
      39           0 :         r->type = FF_UTF16BE;
      40           0 :         r->buf_pos += 2;
      41             :     } else {
      42       30664 :         r->buf[r->buf_len++] = avio_r8(r->pb);
      43       30664 :         if (strncmp("\xEF\xBB\xBF", r->buf, 3) == 0) {
      44             :             // UTF8
      45          45 :             r->buf_pos += 3;
      46             :         }
      47             :     }
      48       30664 :     if (s && (r->type == FF_UTF16LE || r->type == FF_UTF16BE))
      49           0 :         av_log(s, AV_LOG_INFO,
      50             :                "UTF16 is automatically converted to UTF8, do not specify a character encoding\n");
      51       30664 : }
      52             : 
      53       30650 : void ff_text_init_buf(FFTextReader *r, void *buf, size_t size)
      54             : {
      55       30650 :     memset(&r->buf_pb, 0, sizeof(r->buf_pb));
      56       30650 :     ffio_init_context(&r->buf_pb, buf, size, 0, NULL, NULL, NULL, NULL);
      57       30650 :     ff_text_init_avio(NULL, r, &r->buf_pb);
      58       30650 : }
      59             : 
      60        4334 : int64_t ff_text_pos(FFTextReader *r)
      61             : {
      62        4334 :     return avio_tell(r->pb) - r->buf_len + r->buf_pos;
      63             : }
      64             : 
      65      467610 : int ff_text_r8(FFTextReader *r)
      66             : {
      67             :     uint32_t val;
      68             :     uint8_t tmp;
      69      467610 :     if (r->buf_pos < r->buf_len)
      70       94667 :         return r->buf[r->buf_pos++];
      71      372943 :     if (r->type == FF_UTF16LE) {
      72           0 :         GET_UTF16(val, avio_rl16(r->pb), return 0;)
      73      372943 :     } else if (r->type == FF_UTF16BE) {
      74           0 :         GET_UTF16(val, avio_rb16(r->pb), return 0;)
      75             :     } else {
      76      372943 :         return avio_r8(r->pb);
      77             :     }
      78           0 :     if (!val)
      79           0 :         return 0;
      80           0 :     r->buf_pos = 0;
      81           0 :     r->buf_len = 0;
      82           0 :     PUT_UTF8(val, tmp, r->buf[r->buf_len++] = tmp;)
      83           0 :     return r->buf[r->buf_pos++]; // buf_len is at least 1
      84             : }
      85             : 
      86       24520 : void ff_text_read(FFTextReader *r, char *buf, size_t size)
      87             : {
      88      294240 :     for ( ; size > 0; size--)
      89      269720 :         *buf++ = ff_text_r8(r);
      90       24520 : }
      91             : 
      92        9231 : int ff_text_eof(FFTextReader *r)
      93             : {
      94        9231 :     return r->buf_pos >= r->buf_len && avio_feof(r->pb);
      95             : }
      96             : 
      97       45648 : int ff_text_peek_r8(FFTextReader *r)
      98             : {
      99             :     int c;
     100       45648 :     if (r->buf_pos < r->buf_len)
     101       39072 :         return r->buf[r->buf_pos];
     102        6576 :     c = ff_text_r8(r);
     103        6576 :     if (!avio_feof(r->pb)) {
     104        6576 :         r->buf_pos = 0;
     105        6576 :         r->buf_len = 1;
     106        6576 :         r->buf[0] = c;
     107             :     }
     108        6576 :     return c;
     109             : }
     110             : 
     111        2113 : AVPacket *ff_subtitles_queue_insert(FFDemuxSubtitlesQueue *q,
     112             :                                     const uint8_t *event, size_t len, int merge)
     113             : {
     114             :     AVPacket *subs, *sub;
     115             : 
     116        3047 :     if (merge && q->nb_subs > 0) {
     117             :         /* merge with previous event */
     118             : 
     119             :         int old_len;
     120         934 :         sub = &q->subs[q->nb_subs - 1];
     121         934 :         old_len = sub->size;
     122         934 :         if (av_grow_packet(sub, len) < 0)
     123           0 :             return NULL;
     124         934 :         memcpy(sub->data + old_len, event, len);
     125             :     } else {
     126             :         /* new event */
     127             : 
     128        1179 :         if (q->nb_subs >= INT_MAX/sizeof(*q->subs) - 1)
     129           0 :             return NULL;
     130        1179 :         subs = av_fast_realloc(q->subs, &q->allocated_size,
     131        1179 :                                (q->nb_subs + 1) * sizeof(*q->subs));
     132        1179 :         if (!subs)
     133           0 :             return NULL;
     134        1179 :         q->subs = subs;
     135        1179 :         sub = &subs[q->nb_subs++];
     136        1179 :         if (av_new_packet(sub, len) < 0)
     137           0 :             return NULL;
     138        1179 :         sub->flags |= AV_PKT_FLAG_KEY;
     139        1179 :         sub->pts = sub->dts = 0;
     140        1179 :         memcpy(sub->data, event, len);
     141             :     }
     142        2113 :     return sub;
     143             : }
     144             : 
     145        3836 : static int cmp_pkt_sub_ts_pos(const void *a, const void *b)
     146             : {
     147        3836 :     const AVPacket *s1 = a;
     148        3836 :     const AVPacket *s2 = b;
     149        3836 :     if (s1->pts == s2->pts)
     150         133 :         return FFDIFFSIGN(s1->pos, s2->pos);
     151        3703 :     return FFDIFFSIGN(s1->pts , s2->pts);
     152             : }
     153             : 
     154         123 : static int cmp_pkt_sub_pos_ts(const void *a, const void *b)
     155             : {
     156         123 :     const AVPacket *s1 = a;
     157         123 :     const AVPacket *s2 = b;
     158         123 :     if (s1->pos == s2->pos) {
     159           0 :         if (s1->pts == s2->pts)
     160           0 :             return 0;
     161           0 :         return s1->pts > s2->pts ? 1 : -1;
     162             :     }
     163         123 :     return s1->pos > s2->pos ? 1 : -1;
     164             : }
     165             : 
     166          26 : static void drop_dups(void *log_ctx, FFDemuxSubtitlesQueue *q)
     167             : {
     168          26 :     int i, drop = 0;
     169             : 
     170        1081 :     for (i = 1; i < q->nb_subs; i++) {
     171        1055 :         const int last_id = i - 1 - drop;
     172        1055 :         const AVPacket *last = &q->subs[last_id];
     173             : 
     174        1158 :         if (q->subs[i].pts        == last->pts &&
     175         125 :             q->subs[i].duration   == last->duration &&
     176          44 :             q->subs[i].stream_index == last->stream_index &&
     177          22 :             !strcmp(q->subs[i].data, last->data)) {
     178             : 
     179           0 :             av_packet_unref(&q->subs[i]);
     180           0 :             drop++;
     181        1055 :         } else if (drop) {
     182           0 :             q->subs[last_id + 1] = q->subs[i];
     183           0 :             memset(&q->subs[i], 0, sizeof(q->subs[i])); // for safety
     184             :         }
     185             :     }
     186             : 
     187          26 :     if (drop) {
     188           0 :         q->nb_subs -= drop;
     189           0 :         av_log(log_ctx, AV_LOG_WARNING, "Dropping %d duplicated subtitle events\n", drop);
     190             :     }
     191          26 : }
     192             : 
     193          30 : void ff_subtitles_queue_finalize(void *log_ctx, FFDemuxSubtitlesQueue *q)
     194             : {
     195             :     int i;
     196             : 
     197          30 :     qsort(q->subs, q->nb_subs, sizeof(*q->subs),
     198          30 :           q->sort == SUB_SORT_TS_POS ? cmp_pkt_sub_ts_pos
     199             :                                      : cmp_pkt_sub_pos_ts);
     200        1209 :     for (i = 0; i < q->nb_subs; i++)
     201        1179 :         if (q->subs[i].duration < 0 && i < q->nb_subs - 1)
     202         180 :             q->subs[i].duration = q->subs[i + 1].pts - q->subs[i].pts;
     203             : 
     204          30 :     if (!q->keep_duplicates)
     205          26 :         drop_dups(log_ctx, q);
     206          30 : }
     207             : 
     208        1192 : int ff_subtitles_queue_read_packet(FFDemuxSubtitlesQueue *q, AVPacket *pkt)
     209             : {
     210        1192 :     AVPacket *sub = q->subs + q->current_sub_idx;
     211             : 
     212        1192 :     if (q->current_sub_idx == q->nb_subs)
     213          29 :         return AVERROR_EOF;
     214        1163 :     if (av_packet_ref(pkt, sub) < 0) {
     215           0 :         return AVERROR(ENOMEM);
     216             :     }
     217             : 
     218        1163 :     pkt->dts = pkt->pts;
     219        1163 :     q->current_sub_idx++;
     220        1163 :     return 0;
     221             : }
     222             : 
     223           2 : static int search_sub_ts(const FFDemuxSubtitlesQueue *q, int64_t ts)
     224             : {
     225           2 :     int s1 = 0, s2 = q->nb_subs - 1;
     226             : 
     227           2 :     if (s2 < s1)
     228           0 :         return AVERROR(ERANGE);
     229             : 
     230          12 :     for (;;) {
     231             :         int mid;
     232             : 
     233          14 :         if (s1 == s2)
     234           0 :             return s1;
     235          14 :         if (s1 == s2 - 1)
     236           2 :             return q->subs[s1].pts <= q->subs[s2].pts ? s1 : s2;
     237          12 :         mid = (s1 + s2) / 2;
     238          12 :         if (q->subs[mid].pts <= ts)
     239           3 :             s1 = mid;
     240             :         else
     241           9 :             s2 = mid;
     242             :     }
     243             : }
     244             : 
     245           2 : int ff_subtitles_queue_seek(FFDemuxSubtitlesQueue *q, AVFormatContext *s, int stream_index,
     246             :                             int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
     247             : {
     248           2 :     if (flags & AVSEEK_FLAG_BYTE) {
     249           0 :         return AVERROR(ENOSYS);
     250           2 :     } else if (flags & AVSEEK_FLAG_FRAME) {
     251           0 :         if (ts < 0 || ts >= q->nb_subs)
     252           0 :             return AVERROR(ERANGE);
     253           0 :         q->current_sub_idx = ts;
     254             :     } else {
     255           2 :         int i, idx = search_sub_ts(q, ts);
     256             :         int64_t ts_selected;
     257             : 
     258           2 :         if (idx < 0)
     259           0 :             return idx;
     260           2 :         for (i = idx; i < q->nb_subs && q->subs[i].pts < min_ts; i++)
     261           0 :             if (stream_index == -1 || q->subs[i].stream_index == stream_index)
     262           0 :                 idx = i;
     263           2 :         for (i = idx; i > 0 && q->subs[i].pts > max_ts; i--)
     264           0 :             if (stream_index == -1 || q->subs[i].stream_index == stream_index)
     265           0 :                 idx = i;
     266             : 
     267           2 :         ts_selected = q->subs[idx].pts;
     268           2 :         if (ts_selected < min_ts || ts_selected > max_ts)
     269           1 :             return AVERROR(ERANGE);
     270             : 
     271             :         /* look back in the latest subtitles for overlapping subtitles */
     272           1 :         for (i = idx - 1; i >= 0; i--) {
     273           1 :             int64_t pts = q->subs[i].pts;
     274           1 :             if (q->subs[i].duration <= 0 ||
     275           1 :                 (stream_index != -1 && q->subs[i].stream_index != stream_index))
     276           0 :                 continue;
     277           1 :             if (pts >= min_ts && pts > ts_selected - q->subs[i].duration)
     278           0 :                 idx = i;
     279             :             else
     280             :                 break;
     281             :         }
     282             : 
     283             :         /* If the queue is used to store multiple subtitles streams (like with
     284             :          * VobSub) and the stream index is not specified, we need to make sure
     285             :          * to focus on the smallest file position offset for a same timestamp;
     286             :          * queue is ordered by pts and then filepos, so we can take the first
     287             :          * entry for a given timestamp. */
     288           1 :         if (stream_index == -1)
     289           0 :             while (idx > 0 && q->subs[idx - 1].pts == q->subs[idx].pts)
     290           0 :                 idx--;
     291             : 
     292           1 :         q->current_sub_idx = idx;
     293             :     }
     294           1 :     return 0;
     295             : }
     296             : 
     297          30 : void ff_subtitles_queue_clean(FFDemuxSubtitlesQueue *q)
     298             : {
     299             :     int i;
     300             : 
     301        1209 :     for (i = 0; i < q->nb_subs; i++)
     302        1179 :         av_packet_unref(&q->subs[i]);
     303          30 :     av_freep(&q->subs);
     304          30 :     q->nb_subs = q->allocated_size = q->current_sub_idx = 0;
     305          30 : }
     306             : 
     307        1111 : int ff_smil_extract_next_text_chunk(FFTextReader *tr, AVBPrint *buf, char *c)
     308             : {
     309        1111 :     int i = 0;
     310             :     char end_chr;
     311             : 
     312        1111 :     if (!*c) // cached char?
     313         683 :         *c = ff_text_r8(tr);
     314        1111 :     if (!*c)
     315           0 :         return 0;
     316             : 
     317        1111 :     end_chr = *c == '<' ? '>' : '<';
     318             :     do {
     319       14499 :         av_bprint_chars(buf, *c, 1);
     320       14499 :         *c = ff_text_r8(tr);
     321       14499 :         i++;
     322       14499 :     } while (*c != end_chr && *c);
     323        1111 :     if (end_chr == '>') {
     324         682 :         av_bprint_chars(buf, '>', 1);
     325         682 :         *c = 0;
     326             :     }
     327        1111 :     return i;
     328             : }
     329             : 
     330         176 : const char *ff_smil_get_attr_ptr(const char *s, const char *attr)
     331             : {
     332         176 :     int in_quotes = 0;
     333         176 :     const size_t len = strlen(attr);
     334             : 
     335         361 :     while (*s) {
     336        1327 :         while (*s) {
     337        1142 :             if (!in_quotes && av_isspace(*s))
     338         179 :                 break;
     339         963 :             in_quotes ^= *s == '"'; // XXX: support escaping?
     340         963 :             s++;
     341             :         }
     342         543 :         while (av_isspace(*s))
     343         179 :             s++;
     344         182 :         if (!av_strncasecmp(s, attr, len) && s[len] == '=')
     345         173 :             return s + len + 1 + (s[len + 1] == '"');
     346             :     }
     347           3 :     return NULL;
     348             : }
     349             : 
     350        3935 : static inline int is_eol(char c)
     351             : {
     352        3935 :     return c == '\r' || c == '\n';
     353             : }
     354             : 
     355          56 : void ff_subtitles_read_text_chunk(FFTextReader *tr, AVBPrint *buf)
     356             : {
     357          56 :     char eol_buf[5], last_was_cr = 0;
     358          56 :     int n = 0, i = 0, nb_eol = 0;
     359             : 
     360          56 :     av_bprint_clear(buf);
     361             : 
     362        3831 :     for (;;) {
     363        3887 :         char c = ff_text_r8(tr);
     364             : 
     365        3887 :         if (!c)
     366           6 :             break;
     367             : 
     368             :         /* ignore all initial line breaks */
     369        3881 :         if (n == 0 && is_eol(c))
     370           0 :             continue;
     371             : 
     372             :         /* line break buffering: we don't want to add the trailing \r\n */
     373        3881 :         if (is_eol(c)) {
     374         176 :             nb_eol += c == '\n' || last_was_cr;
     375         176 :             if (nb_eol == 2)
     376          50 :                 break;
     377         126 :             eol_buf[i++] = c;
     378         126 :             if (i == sizeof(eol_buf) - 1)
     379           0 :                 break;
     380         126 :             last_was_cr = c == '\r';
     381         126 :             continue;
     382             :         }
     383             : 
     384             :         /* only one line break followed by data: we flush the line breaks
     385             :          * buffer */
     386        3705 :         if (i) {
     387          67 :             eol_buf[i] = 0;
     388          67 :             av_bprintf(buf, "%s", eol_buf);
     389          67 :             i = nb_eol = 0;
     390             :         }
     391             : 
     392        3705 :         av_bprint_chars(buf, c, 1);
     393        3705 :         n++;
     394             :     }
     395          56 : }
     396             : 
     397          56 : void ff_subtitles_read_chunk(AVIOContext *pb, AVBPrint *buf)
     398             : {
     399             :     FFTextReader tr;
     400          56 :     tr.buf_pos = tr.buf_len = 0;
     401          56 :     tr.type = 0;
     402          56 :     tr.pb = pb;
     403          56 :     ff_subtitles_read_text_chunk(&tr, buf);
     404          56 : }
     405             : 
     406        9273 : ptrdiff_t ff_subtitles_read_line(FFTextReader *tr, char *buf, size_t size)
     407             : {
     408        9273 :     size_t cur = 0;
     409        9273 :     if (!size)
     410           0 :         return 0;
     411      159812 :     while (cur + 1 < size) {
     412      150398 :         unsigned char c = ff_text_r8(tr);
     413      150398 :         if (!c)
     414        4854 :             return ff_text_eof(tr) ? cur : AVERROR_INVALIDDATA;
     415      145544 :         if (c == '\r' || c == '\n')
     416             :             break;
     417      141266 :         buf[cur++] = c;
     418      141266 :         buf[cur] = '\0';
     419             :     }
     420        4419 :     if (ff_text_peek_r8(tr) == '\r')
     421        2118 :         ff_text_r8(tr);
     422        4419 :     if (ff_text_peek_r8(tr) == '\n')
     423        3143 :         ff_text_r8(tr);
     424        4419 :     return cur;
     425             : }

Generated by: LCOV version 1.13