LCOV - code coverage report
Current view: top level - media_tools - webvtt.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 659 748 88.1 %
Date: 2021-04-29 23:48:07 Functions: 60 61 98.4 %

          Line data    Source code
       1             : /*
       2             :  *          GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *          Authors: Cyril Concolato
       5             :  *          Copyright (c) Telecom ParisTech 2000-2021
       6             :  *                  All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / ISO Media File Format sub-project
       9             :  *
      10             :  *  GPAC is free software; you can redistribute it and/or modify
      11             :  *  it under the terms of the GNU Lesser General Public License as published by
      12             :  *  the Free Software Foundation; either version 2, or (at your option)
      13             :  *  any later version.
      14             :  *
      15             :  *  GPAC is distributed in the hope that it will be useful,
      16             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :  *  GNU Lesser General Public License for more details.
      19             :  *
      20             :  *  You should have received a copy of the GNU Lesser General Public
      21             :  *  License along with this library; see the file COPYING.  If not, write to
      22             :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      23             :  *
      24             :  */
      25             : 
      26             : #include <gpac/list.h>
      27             : #include <gpac/internal/isomedia_dev.h>
      28             : #include <gpac/internal/media_dev.h>
      29             : #include <gpac/webvtt.h>
      30             : #include <gpac/constants.h>
      31             : 
      32             : #ifndef GPAC_DISABLE_VTT
      33             : 
      34             : struct _webvtt_sample
      35             : {
      36             :         u64 start;
      37             :         u64 end;
      38             :         GF_List *cues;
      39             : };
      40             : 
      41             : 
      42             : #ifndef GPAC_DISABLE_ISOM
      43             : 
      44             : typedef struct
      45             : {
      46             :         GF_ISOM_BOX
      47             :         GF_StringBox *id;
      48             :         GF_StringBox *time;
      49             :         GF_StringBox *settings;
      50             :         GF_StringBox *payload;
      51             : } GF_VTTCueBox;
      52             : 
      53       25973 : GF_Box *boxstring_box_new() {
      54             :         //type is assigned by caller
      55       51946 :         ISOM_DECL_BOX_ALLOC(GF_StringBox, 0);
      56       25973 :         return (GF_Box *)tmp;
      57             : }
      58             : 
      59       25876 : GF_Box *boxstring_new_with_data(u32 type, const char *string, GF_List **parent)
      60             : {
      61             :         GF_Box *a = NULL;
      62             : 
      63       25876 :         switch (type) {
      64       25876 :         case GF_ISOM_BOX_TYPE_VTTC_CONFIG:
      65             :         case GF_ISOM_BOX_TYPE_CTIM:
      66             :         case GF_ISOM_BOX_TYPE_IDEN:
      67             :         case GF_ISOM_BOX_TYPE_STTG:
      68             :         case GF_ISOM_BOX_TYPE_PAYL:
      69             :         case GF_ISOM_BOX_TYPE_VTTA:
      70       25876 :                 if (string) {
      71             :                         /* remove trailing spaces; spec. \r, \n; skip if empty */
      72       25876 :                         size_t len = strlen(string);
      73       25876 :                         char const* last = string + len-1;
      74       80525 :                         while (len && isspace(*last--))
      75       28773 :                                 --len;
      76             : 
      77       25876 :                         if (!len) break;
      78       25761 :                         if (parent) {
      79       25761 :                                 a = gf_isom_box_new_parent(parent, type);
      80             :                         } else {
      81           0 :                                 a = gf_isom_box_new(type);
      82             :                         }
      83       25761 :                         if (a) {
      84       25761 :                                 char* str = ((GF_StringBox *)a)->string = gf_malloc(len + 1);
      85             :                                 memcpy(str, string, len);
      86       25761 :                                 str[len] = '\0';
      87             :                         }
      88             :                 }
      89             :                 break;
      90           0 :         default:
      91           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Box type %s is not a boxstring, cannot initialize with data\n", gf_4cc_to_str(type) ));
      92             : 
      93             :                 break;
      94             :         }
      95       25991 :         return a;
      96             : }
      97             : 
      98       14539 : GF_Box *vtcu_box_new()
      99             : {
     100       29078 :         ISOM_DECL_BOX_ALLOC(GF_VTTCueBox, GF_ISOM_BOX_TYPE_VTCC_CUE);
     101       14539 :         return (GF_Box *)tmp;
     102             : }
     103             : 
     104         241 : GF_Box *vtte_box_new() {
     105         482 :         ISOM_DECL_BOX_ALLOC(GF_Box, GF_ISOM_BOX_TYPE_VTTE);
     106         241 :         return (GF_Box *)tmp;
     107             : }
     108             : 
     109       25973 : void boxstring_box_del(GF_Box *s)
     110             : {
     111             :         GF_StringBox *box = (GF_StringBox *)s;
     112       25973 :         if (box->string) gf_free(box->string);
     113       25973 :         gf_free(box);
     114       25973 : }
     115             : 
     116       14539 : void vtcu_box_del(GF_Box *s)
     117             : {
     118       14539 :         gf_free(s);
     119       14539 : }
     120             : 
     121         241 : void vtte_box_del(GF_Box *s)
     122             : {
     123         241 :         gf_free(s);
     124         241 : }
     125             : 
     126          57 : GF_Box *wvtt_box_new()
     127             : {
     128         114 :         ISOM_DECL_BOX_ALLOC(GF_WebVTTSampleEntryBox, GF_ISOM_BOX_TYPE_WVTT);
     129          57 :         gf_isom_sample_entry_init((GF_SampleEntryBox *)tmp);
     130          57 :         return (GF_Box *)tmp;
     131             : }
     132             : 
     133          57 : void wvtt_box_del(GF_Box *s)
     134             : {
     135          57 :         gf_isom_sample_entry_predestroy((GF_SampleEntryBox *)s);
     136          57 :         gf_free(s);
     137          57 : }
     138             : 
     139         200 : GF_Err boxstring_box_read(GF_Box *s, GF_BitStream *bs)
     140             : {
     141             :         GF_StringBox *box = (GF_StringBox *)s;
     142         200 :         box->string = (char *)gf_malloc((u32)(s->size+1));
     143         200 :         gf_bs_read_data(bs, box->string, (u32)(s->size));
     144         200 :         box->string[(u32)(s->size)] = 0;
     145         200 :         return GF_OK;
     146             : }
     147             : 
     148             : 
     149         182 : GF_Err vtcu_on_child_box(GF_Box *s, GF_Box *a, Bool is_rem)
     150             : {
     151             :         GF_VTTCueBox *ptr = (GF_VTTCueBox *)s;
     152         182 :         switch (a->type) {
     153           0 :         case GF_ISOM_BOX_TYPE_CTIM:
     154           0 :                 BOX_FIELD_ASSIGN(time, GF_StringBox);
     155           0 :                 break;
     156          47 :         case GF_ISOM_BOX_TYPE_IDEN:
     157          47 :                 BOX_FIELD_ASSIGN(id, GF_StringBox);
     158          47 :                 break;
     159          27 :         case GF_ISOM_BOX_TYPE_STTG:
     160          27 :                 BOX_FIELD_ASSIGN(settings, GF_StringBox);
     161          27 :                 break;
     162         108 :         case GF_ISOM_BOX_TYPE_PAYL:
     163         108 :                 BOX_FIELD_ASSIGN(payload, GF_StringBox);
     164         108 :                 break;
     165             :         }
     166             :         return GF_OK;
     167             : }
     168             : 
     169         109 : GF_Err vtcu_box_read(GF_Box *s, GF_BitStream *bs)
     170             : {
     171         109 :         return gf_isom_box_array_read(s, bs);
     172             : }
     173             : 
     174          29 : GF_Err vtte_box_read(GF_Box *s, GF_BitStream *bs)
     175             : {
     176          29 :         return gf_isom_box_array_read(s, bs);
     177             : }
     178             : 
     179          12 : GF_Err wvtt_on_child_box(GF_Box *s, GF_Box *a, Bool is_rem)
     180             : {
     181             :         GF_WebVTTSampleEntryBox *ptr = (GF_WebVTTSampleEntryBox *)s;
     182          12 :         switch (a->type) {
     183          12 :         case GF_ISOM_BOX_TYPE_VTTC_CONFIG:
     184          12 :                 BOX_FIELD_ASSIGN(config, GF_StringBox);
     185          12 :                 break;
     186             :         }
     187             :         return GF_OK;
     188             : }
     189             : 
     190          13 : GF_Err wvtt_box_read(GF_Box *s, GF_BitStream *bs)
     191             : {
     192             :         GF_Err e;
     193             :         GF_WebVTTSampleEntryBox *wvtt = (GF_WebVTTSampleEntryBox *)s;
     194          13 :         e = gf_isom_base_sample_entry_read((GF_SampleEntryBox *)wvtt, bs);
     195          13 :         if (e) return e;
     196             : 
     197          13 :         wvtt->size -= 8;
     198          13 :         return gf_isom_box_array_read(s, bs);
     199             : }
     200             : 
     201             : #ifndef GPAC_DISABLE_ISOM_WRITE
     202       25772 : GF_Err boxstring_box_write(GF_Box *s, GF_BitStream *bs)
     203             : {
     204             :         GF_Err e;
     205             :         GF_StringBox *box = (GF_StringBox *)s;
     206       25772 :         e = gf_isom_box_write_header(s, bs);
     207       25772 :         if (e) return e;
     208       25772 :         if (box->string) {
     209       25766 :                 gf_bs_write_data(bs, box->string, (u32)(box->size-8));
     210             :         }
     211             :         return GF_OK;
     212             : }
     213             : 
     214       14429 : GF_Err vtcu_box_write(GF_Box *s, GF_BitStream *bs)
     215             : {
     216       14429 :         return gf_isom_box_write_header(s, bs);
     217             : }
     218             : 
     219         211 : GF_Err vtte_box_write(GF_Box *s, GF_BitStream *bs)
     220             : {
     221             :         GF_Err e;
     222         211 :         e = gf_isom_box_write_header(s, bs);
     223         211 :         return e;
     224             : }
     225             : 
     226          48 : GF_Err wvtt_box_write(GF_Box *s, GF_BitStream *bs)
     227             : {
     228             :         GF_Err e;
     229             :         GF_WebVTTSampleEntryBox *wvtt = (GF_WebVTTSampleEntryBox *)s;
     230          48 :         e = gf_isom_box_write_header(s, bs);
     231          48 :         gf_bs_write_data(bs, wvtt->reserved, 6);
     232          48 :         gf_bs_write_u16(bs, wvtt->dataReferenceIndex);
     233          48 :         return e;
     234             : }
     235             : 
     236       25856 : GF_Err boxstring_box_size(GF_Box *s)
     237             : {
     238             :         GF_StringBox *box = (GF_StringBox *)s;
     239       25856 :         if (box->string)
     240       25850 :                 box->size += strlen(box->string);
     241       25856 :         return GF_OK;
     242             : }
     243             : 
     244       14429 : GF_Err vtcu_box_size(GF_Box *s)
     245             : {
     246       14429 :         u32 pos=0;
     247             :         GF_VTTCueBox *cuebox = (GF_VTTCueBox *)s;
     248       14429 :         gf_isom_check_position(s, (GF_Box*)cuebox->id, &pos);
     249       14429 :         gf_isom_check_position(s, (GF_Box*)cuebox->time, &pos);
     250       14429 :         gf_isom_check_position(s, (GF_Box*)cuebox->settings, &pos);
     251       14429 :         gf_isom_check_position(s, (GF_Box*)cuebox->payload, &pos);
     252       14429 :         return GF_OK;
     253             : }
     254             : 
     255         211 : GF_Err vtte_box_size(GF_Box *s)
     256             : {
     257         211 :         return GF_OK;
     258             : }
     259             : 
     260         132 : GF_Err wvtt_box_size(GF_Box *s)
     261             : {
     262         132 :         u32 pos=0;
     263             :         GF_WebVTTSampleEntryBox *wvtt = (GF_WebVTTSampleEntryBox *)s;
     264         132 :         s->size += 8; // reserved and dataReferenceIndex
     265         132 :         gf_isom_check_position(s, (GF_Box *)wvtt->config, &pos);
     266         132 :         return GF_OK;
     267             : }
     268             : 
     269       14428 : static GF_Err wvtt_write_cue(GF_BitStream *bs, GF_WebVTTCue *cue)
     270             : {
     271             :         GF_Err e;
     272             :         GF_VTTCueBox *cuebox;
     273       14428 :         if (!cue) return GF_OK;
     274             : 
     275       14428 :         cuebox = (GF_VTTCueBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_VTCC_CUE);
     276             : 
     277       14428 :         if (cue->id) {
     278       11299 :                 cuebox->id = (GF_StringBox *)boxstring_new_with_data(GF_ISOM_BOX_TYPE_IDEN, cue->id, &cuebox->child_boxes);
     279             :         }
     280       14428 :         if (cue->settings) {
     281         107 :                 cuebox->settings = (GF_StringBox *)boxstring_new_with_data(GF_ISOM_BOX_TYPE_STTG, cue->settings, &cuebox->child_boxes);
     282             :         }
     283       14428 :         if (cue->text) {
     284       14428 :                 cuebox->payload = (GF_StringBox *)boxstring_new_with_data(GF_ISOM_BOX_TYPE_PAYL, cue->text, &cuebox->child_boxes);
     285             :         }
     286             :         /* TODO: check if a time box should be written */
     287       14428 :         e = gf_isom_box_size((GF_Box *)cuebox);
     288       14428 :         if (!e) e = gf_isom_box_write((GF_Box *)cuebox, bs);
     289             : 
     290       14428 :         gf_isom_box_del((GF_Box *)cuebox);
     291       14428 :         return e;
     292             : }
     293             : 
     294       14570 : GF_ISOSample *gf_isom_webvtt_to_sample(void *s)
     295             : {
     296             :         GF_Err e = GF_OK;
     297             :         GF_ISOSample *res;
     298             :         GF_BitStream *bs;
     299             :         u32 i;
     300             :         GF_WebVTTSample *samp = (GF_WebVTTSample *)s;
     301       14570 :         if (!samp) return NULL;
     302             : 
     303       14570 :         bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
     304             : 
     305       14570 :         if (gf_list_count(samp->cues)) {
     306             :                 GF_WebVTTCue *cue;
     307       14360 :                 i=0;
     308       43148 :                 while ((cue = (GF_WebVTTCue *)gf_list_enum(samp->cues, &i))) {
     309       14428 :                         e = wvtt_write_cue(bs, cue);
     310       14428 :                         if (e) break;
     311             :                 }
     312       14360 :                 if (e) {
     313           0 :                         gf_bs_del(bs);
     314           0 :                         return NULL;
     315             :                 }
     316             :         } else {
     317         210 :                 GF_Box *cuebox = (GF_Box *)gf_isom_box_new(GF_ISOM_BOX_TYPE_VTTE);
     318         210 :                 e = gf_isom_box_size((GF_Box *)cuebox);
     319         210 :                 if (!e) e = gf_isom_box_write((GF_Box *)cuebox, bs);
     320         210 :                 gf_isom_box_del((GF_Box *)cuebox);
     321         210 :                 if (e) {
     322           0 :                         gf_bs_del(bs);
     323           0 :                         return NULL;
     324             :                 }
     325             :         }
     326       14570 :         res = gf_isom_sample_new();
     327       14570 :         if (!res) {
     328           0 :                 gf_bs_del(bs);
     329           0 :                 return NULL;
     330             :         }
     331       14570 :         gf_bs_get_content(bs, &res->data, &res->dataLength);
     332       14570 :         gf_bs_del(bs);
     333       14570 :         res->IsRAP = RAP;
     334       14570 :         return res;
     335             : }
     336             : #endif /*GPAC_DISABLE_ISOM_WRITE*/
     337             : 
     338             : #ifndef GPAC_DISABLE_ISOM_DUMP
     339             : 
     340          38 : GF_Err boxstring_box_dump(GF_Box *a, FILE * trace)
     341             : {
     342             :         char *szName;
     343             :         GF_StringBox *sbox = (GF_StringBox *)a;
     344          38 :         switch (a->type) {
     345             :         case GF_ISOM_BOX_TYPE_VTTC_CONFIG:
     346             :                 szName = "WebVTTConfigurationBox";
     347             :                 break;
     348           0 :         case GF_ISOM_BOX_TYPE_CTIM:
     349             :                 szName = "CueTimeBox";
     350           0 :                 break;
     351           1 :         case GF_ISOM_BOX_TYPE_IDEN:
     352             :                 szName = "CueIDBox";
     353           1 :                 break;
     354           9 :         case GF_ISOM_BOX_TYPE_STTG:
     355             :                 szName = "CueSettingsBox";
     356           9 :                 break;
     357          18 :         case GF_ISOM_BOX_TYPE_PAYL:
     358             :                 szName = "CuePayloadBox";
     359          18 :                 break;
     360           0 :         case GF_ISOM_BOX_TYPE_VTTA:
     361             :                 szName = "VTTAdditionalCueBox";
     362           0 :                 break;
     363           6 :         default:
     364             :                 szName = "StringBox";
     365           6 :                 break;
     366             :         }
     367          38 :         gf_isom_box_dump_start(a, szName, trace);
     368          38 :         gf_fprintf(trace, ">");
     369          38 :         if (sbox->string && strlen(sbox->string))
     370          32 :                 gf_fprintf(trace, "<![CDATA[\n%s\n]]>", sbox->string);
     371          38 :         gf_isom_box_dump_done(szName, a, trace);
     372          38 :         return GF_OK;
     373             : }
     374             : 
     375          19 : GF_Err vtcu_box_dump(GF_Box *a, FILE * trace)
     376             : {
     377          19 :         gf_isom_box_dump_start(a, "WebVTTCueBox", trace);
     378          19 :         gf_fprintf(trace, ">\n");
     379          19 :         gf_isom_box_dump_done("WebVTTCueBox", a, trace);
     380          19 :         return GF_OK;
     381             : }
     382             : 
     383           3 : GF_Err vtte_box_dump(GF_Box *a, FILE * trace)
     384             : {
     385           3 :         gf_isom_box_dump_start(a, "WebVTTEmptyCueBox", trace);
     386           3 :         gf_fprintf(trace, ">\n");
     387           3 :         gf_isom_box_dump_done("WebVTTEmptyCueBox", a, trace);
     388           3 :         return GF_OK;
     389             : }
     390             : 
     391           5 : GF_Err wvtt_box_dump(GF_Box *a, FILE * trace)
     392             : {
     393           5 :         gf_isom_box_dump_start(a, "WebVTTSampleEntryBox", trace);
     394           5 :         gf_fprintf(trace, ">\n");
     395           5 :         gf_isom_box_dump_done("WebVTTSampleEntryBox", a, trace);
     396           5 :         return GF_OK;
     397             : }
     398             : #endif /* GPAC_DISABLE_ISOM_DUMP */
     399             : 
     400             : #endif /*GPAC_DISABLE_ISOM*/
     401             : 
     402             : typedef enum {
     403             :         WEBVTT_PARSER_STATE_WAITING_SIGNATURE,
     404             :         WEBVTT_PARSER_STATE_WAITING_HEADER,
     405             :         WEBVTT_PARSER_STATE_WAITING_CUE,
     406             :         WEBVTT_PARSER_STATE_WAITING_CUE_TIMESTAMP,
     407             :         WEBVTT_PARSER_STATE_WAITING_CUE_PAYLOAD
     408             : } GF_WebVTTParserState;
     409             : 
     410             : struct _webvtt_parser {
     411             :         GF_WebVTTParserState state;
     412             :         Bool is_srt, suspend, is_eof;
     413             : 
     414             :         /* List of non-overlapping GF_WebVTTSample */
     415             :         GF_List *samples;
     416             : 
     417             :         FILE *vtt_in;
     418             :         s32 unicode_type;
     419             : 
     420             :         u64  last_duration;
     421             :         void *user;
     422             :         GF_Err (*report_message)(void *, GF_Err, char *, const char *);
     423             :         void (*on_header_parsed)(void *, const char *);
     424             :         void (*on_sample_parsed)(void *, GF_WebVTTSample *);
     425             :         void (*on_cue_read)(void *, GF_WebVTTCue *);
     426             : };
     427             : 
     428             : 
     429             : static Bool gf_webvtt_timestamp_is_zero(GF_WebVTTTimestamp *ts)
     430             : {
     431          20 :         return (ts->hour == 0 && ts->min == 0 && ts->sec == 0 && ts->ms == 0) ? GF_TRUE : GF_FALSE;
     432             : }
     433             : 
     434             : #ifndef GPAC_DISABLE_MEDIA_IMPORT
     435             : 
     436             : static Bool gf_webvtt_timestamp_greater(GF_WebVTTTimestamp *ts1, GF_WebVTTTimestamp *ts2)
     437             : {
     438       14346 :         u64 t_ts1 = (60 * 60 * ts1->hour + 60 * ts1->min + ts1->sec) * 1000 + ts1->ms;
     439       14346 :         u64 t_ts2 = (60 * 60 * ts2->hour + 60 * ts2->min + ts2->sec) * 1000 + ts2->ms;
     440             :         return (t_ts1 >= t_ts2) ? GF_TRUE : GF_FALSE;
     441             : }
     442             : 
     443             : 
     444             : /* mark the overlapped cue in the previous sample as split */
     445             : /* duplicate the cue, mark it as split and adjust its timing */
     446             : /* adjust the end of the overlapped cue in the previous sample */
     447          83 : static GF_WebVTTCue *gf_webvtt_cue_split_at(GF_WebVTTCue *cue, GF_WebVTTTimestamp *time)
     448             : {
     449             :         GF_WebVTTCue *dup_cue;
     450             : 
     451          83 :         cue->split = GF_TRUE;
     452          83 :         cue->orig_start = cue->start;
     453          83 :         cue->orig_end = cue->end;
     454             : 
     455          83 :         GF_SAFEALLOC(dup_cue, GF_WebVTTCue);
     456          83 :         if (!dup_cue) return NULL;
     457          83 :         dup_cue->split = GF_TRUE;
     458          83 :         if (time) dup_cue->start = *time;
     459          83 :         dup_cue->end = cue->end;
     460          83 :         dup_cue->orig_start = cue->orig_start;
     461          83 :         dup_cue->orig_end = cue->orig_end;
     462          83 :         dup_cue->id = gf_strdup((cue->id ? cue->id : ""));
     463          83 :         dup_cue->settings = gf_strdup((cue->settings ? cue->settings : ""));
     464          83 :         dup_cue->text = gf_strdup((cue->text ? cue->text : ""));
     465             : 
     466          83 :         if (time) cue->end = *time;
     467             :         return dup_cue;
     468             : }
     469             : 
     470       25751 : static GF_Err gf_webvtt_cue_add_property(GF_WebVTTCue *cue, GF_WebVTTCuePropertyType type, char *text_data, u32 text_len)
     471             : {
     472             :         char **prop = NULL;
     473             :         u32 len;
     474       25751 :         if (!cue) return GF_BAD_PARAM;
     475       25751 :         if (!text_len) return GF_OK;
     476       25751 :         switch(type)
     477             :         {
     478       11262 :         case WEBVTT_ID:
     479       11262 :                 prop = &cue->id;
     480       11262 :                 break;
     481          42 :         case WEBVTT_SETTINGS:
     482          42 :                 prop = &cue->settings;
     483          42 :                 break;
     484       14447 :         case WEBVTT_PAYLOAD:
     485       14447 :                 prop = &cue->text;
     486       14447 :                 break;
     487           0 :         case WEBVTT_POSTCUE_TEXT:
     488           0 :                 prop = &cue->post_text;
     489           0 :                 break;
     490           0 :         case WEBVTT_PRECUE_TEXT:
     491           0 :                 prop = &cue->pre_text;
     492           0 :                 break;
     493             :         }
     494       25751 :         if (*prop) {
     495          12 :                 len = (u32) strlen(*prop);
     496          12 :                 *prop = (char*)gf_realloc(*prop, sizeof(char) * (len + text_len + 1) );
     497          12 :                 strcpy(*prop + len, text_data);
     498             :         } else {
     499       25739 :                 *prop = gf_strdup(text_data);
     500             :         }
     501             :         return GF_OK;
     502             : }
     503             : 
     504       14439 : static GF_WebVTTCue *gf_webvtt_cue_new()
     505             : {
     506             :         GF_WebVTTCue *cue;
     507       14439 :         GF_SAFEALLOC(cue, GF_WebVTTCue);
     508       14439 :         return cue;
     509             : }
     510             : 
     511             : GF_EXPORT
     512       14522 : void gf_webvtt_cue_del(GF_WebVTTCue * cue)
     513             : {
     514       14522 :         if (!cue) return;
     515       14522 :         if (cue->id) gf_free(cue->id);
     516       14522 :         if (cue->settings) gf_free(cue->settings);
     517       14522 :         if (cue->text) gf_free(cue->text);
     518       14522 :         if (cue->pre_text) gf_free(cue->pre_text);
     519       14522 :         if (cue->post_text) gf_free(cue->post_text);
     520       14522 :         gf_free(cue);
     521             : }
     522             : 
     523       14598 : static GF_WebVTTSample *gf_webvtt_sample_new()
     524             : {
     525             :         GF_WebVTTSample *samp;
     526       14598 :         GF_SAFEALLOC(samp, GF_WebVTTSample);
     527       14598 :         if (!samp) return NULL;
     528       14598 :         samp->cues = gf_list_new();
     529       14598 :         return samp;
     530             : }
     531             : 
     532       14570 : u64 gf_webvtt_sample_get_start(GF_WebVTTSample * samp)
     533             : {
     534       14570 :         return samp->start;
     535             : }
     536       14570 : u64 gf_webvtt_sample_get_end(GF_WebVTTSample * samp)
     537             : {
     538       14570 :         return samp->end;
     539             : }
     540             : 
     541       14598 : void gf_webvtt_sample_del(GF_WebVTTSample * samp)
     542             : {
     543       43624 :         while (gf_list_count(samp->cues)) {
     544       14428 :                 GF_WebVTTCue *cue = (GF_WebVTTCue *)gf_list_get(samp->cues, 0);
     545       14428 :                 gf_list_rem(samp->cues, 0);
     546       14428 :                 gf_webvtt_cue_del(cue);
     547             :         }
     548       14598 :         gf_list_del(samp->cues);
     549       14598 :         gf_free(samp);
     550       14598 : }
     551             : 
     552          49 : GF_WebVTTParser *gf_webvtt_parser_new()
     553             : {
     554             :         GF_WebVTTParser *parser;
     555          49 :         GF_SAFEALLOC(parser, GF_WebVTTParser);
     556          49 :         if (!parser) return NULL;
     557          49 :         parser->samples = gf_list_new();
     558          49 :         return parser;
     559             : }
     560             : 
     561             : extern s32 gf_text_get_utf_type(FILE *in_src);
     562             : 
     563          46 : GF_Err gf_webvtt_parser_init(GF_WebVTTParser *parser, FILE *vtt_file, s32 unicode_type, Bool is_srt,
     564             :                              void *user, GF_Err (*report_message)(void *, GF_Err, char *, const char *),
     565             :                              void (*on_sample_parsed)(void *, GF_WebVTTSample *),
     566             :                              void (*on_header_parsed)(void *, const char *))
     567             : {
     568          46 :         if (!parser) return GF_BAD_PARAM;
     569          46 :         parser->state = WEBVTT_PARSER_STATE_WAITING_SIGNATURE;
     570             : 
     571             : #ifdef GPAC_ENABLE_COVERAGE
     572          46 :         if (gf_sys_is_cov_mode()) {
     573          46 :                 gf_webvtt_parser_restart(parser);
     574             :         }
     575             : #endif
     576             : 
     577          46 :         parser->is_srt = is_srt;
     578          46 :         if (is_srt)
     579           2 :                 parser->state = WEBVTT_PARSER_STATE_WAITING_CUE;
     580             : 
     581          46 :         parser->vtt_in = vtt_file;
     582          46 :         parser->unicode_type = unicode_type;
     583             : 
     584          46 :         parser->user = user;
     585          46 :         parser->report_message = report_message;
     586          46 :         parser->on_sample_parsed = on_sample_parsed;
     587          46 :         parser->on_header_parsed = on_header_parsed;
     588          46 :         return GF_OK;
     589             : }
     590             : 
     591       14615 : void gf_webvtt_parser_suspend(GF_WebVTTParser *vttparser)
     592             : {
     593       14615 :         vttparser->suspend = GF_TRUE;
     594       14615 : }
     595             : 
     596          46 : void gf_webvtt_parser_restart(GF_WebVTTParser *parser)
     597             : {
     598          46 :         if (!parser->vtt_in) return;
     599             : 
     600           0 :         gf_fseek(parser->vtt_in, 0, SEEK_SET);
     601           0 :         parser->last_duration = 0;
     602           0 :         while (gf_list_count(parser->samples)) {
     603           0 :                 gf_webvtt_sample_del((GF_WebVTTSample *)gf_list_get(parser->samples, 0));
     604           0 :                 gf_list_rem(parser->samples, 0);
     605             :         }
     606           0 :         parser->state = WEBVTT_PARSER_STATE_WAITING_SIGNATURE;
     607             : }
     608             : 
     609          49 : void gf_webvtt_parser_reset(GF_WebVTTParser *parser)
     610             : {
     611          49 :         if (!parser) return;
     612          49 :         while (gf_list_count(parser->samples)) {
     613           0 :                 gf_webvtt_sample_del((GF_WebVTTSample *)gf_list_get(parser->samples, 0));
     614           0 :                 gf_list_rem(parser->samples, 0);
     615             :         }
     616          49 :         parser->last_duration = 0;
     617          49 :         parser->on_header_parsed = NULL;
     618          49 :         parser->on_sample_parsed = NULL;
     619          49 :         parser->report_message = NULL;
     620          49 :         parser->state = WEBVTT_PARSER_STATE_WAITING_SIGNATURE;
     621          49 :         parser->unicode_type = 0;
     622          49 :         parser->user = NULL;
     623          49 :         parser->vtt_in = NULL;
     624             : }
     625             : 
     626          49 : void gf_webvtt_parser_del(GF_WebVTTParser *parser)
     627             : {
     628          49 :         if (parser) {
     629          49 :                 gf_webvtt_parser_reset(parser);
     630          49 :                 gf_list_del(parser->samples);
     631          49 :                 gf_free(parser);
     632             :         }
     633          49 : }
     634             : 
     635             : #if 0
     636             : u64 gf_webvtt_parser_last_duration(GF_WebVTTParser *parser)
     637             : {
     638             :         return parser ? parser->last_duration : 0;
     639             : }
     640             : #endif
     641             : 
     642             : 
     643       14345 : static GF_Err gf_webvtt_add_cue_to_samples(GF_WebVTTParser *parser, GF_List *samples, GF_WebVTTCue *cue)
     644             : {
     645             :         s32 i;
     646             :         u64 cue_start;
     647             :         u64 cue_end;
     648             :         u64 sample_end;
     649             : 
     650             :         sample_end = 0;
     651       14345 :         cue_start = gf_webvtt_timestamp_get(&cue->start);
     652       14345 :         cue_end   = gf_webvtt_timestamp_get(&cue->end);
     653             :         /* samples in the samples list are contiguous: sample(n)->start == sample(n-1)->end */
     654       14339 :         for (i = 0; i < (s32)gf_list_count(samples); i++) {
     655             :                 GF_WebVTTSample *sample;
     656       14367 :                 sample = (GF_WebVTTSample *)gf_list_get(samples, i);
     657             :                 /* save the sample end in case there are no more samples to test */
     658       14367 :                 sample_end = sample->end;
     659       14367 :                 if (cue_start < sample->start) {
     660             :                         /* cues must be ordered according to their start time, so drop the cue */
     661             :                         /* TODO delete the cue */
     662             :                         return GF_BAD_PARAM;
     663       14367 :                 } else if (cue_start == sample->start && cue_end == sample->end) {
     664             :                         /* if the timing of the new cue matches the sample, no need to split, add the cue to the sample */
     665          17 :                         gf_list_add(sample->cues, cue);
     666             :                         /* the cue does not need to processed further */
     667             :                         return GF_OK;
     668       14350 :                 } else if (cue_start >= sample->end) {
     669             :                         /* flush the current sample */
     670       14277 :                         gf_list_del_item(samples, sample);
     671       14277 :                         parser->on_sample_parsed(parser->user, sample);
     672             :                         sample = NULL;
     673       14277 :                         i--;
     674             :                         /* process the cue with next sample (if any) or create a new sample */
     675       14277 :                         continue;
     676             :                 } else {
     677             :                         u32 j;
     678          73 :                         if (cue_start > sample->start) {
     679             :                                 /* create a new sample, insert it after the current one */
     680          32 :                                 GF_WebVTTSample *new_sample = gf_webvtt_sample_new();
     681          32 :                                 new_sample->start = cue_start;
     682          32 :                                 new_sample->end = sample->end;
     683          32 :                                 gf_list_insert(samples, new_sample, i+1);
     684             :                                 /* split the cues from the old sample into the new one */
     685          41 :                                 for (j = 0; j < gf_list_count(sample->cues); j++) {
     686          41 :                                         GF_WebVTTCue *old_cue = (GF_WebVTTCue *)gf_list_get(sample->cues, j);
     687          41 :                                         GF_WebVTTCue *new_cue = gf_webvtt_cue_split_at(old_cue, &cue->start);
     688          41 :                                         gf_list_add(new_sample->cues, new_cue);
     689             :                                 }
     690             :                                 /* adjust the end of the old sample and flush it */
     691          32 :                                 sample->end = cue_start;
     692          32 :                                 gf_list_del_item(samples, sample);
     693          32 :                                 parser->on_sample_parsed(parser->user, sample);
     694             :                                 sample = NULL;
     695          32 :                                 i--;
     696             :                                 /* process the cue again with this new sample */
     697          32 :                                 continue;
     698             :                         }
     699          41 :                         if (cue_end > sample->end) {
     700             :                                 /* the cue is longer than the sample, we split the cue, add one part to the current sample
     701             :                                 and reevaluate with the last part of the cue */
     702          30 :                                 GF_WebVTTCue *old_cue = (GF_WebVTTCue *)gf_list_get(sample->cues, 0);
     703          30 :                                 GF_WebVTTCue *new_cue = gf_webvtt_cue_split_at(cue, &old_cue->end);
     704          30 :                                 gf_list_add(sample->cues, cue);
     705             :                                 cue = new_cue;
     706          30 :                                 cue_start = sample->end;
     707             :                                 /* cue_end unchanged */
     708             :                                 /* process the remaining part of the cue (i.e. the new cue) with the other samples */
     709          30 :                                 continue;
     710             :                         } else { /* cue_end < sample->end */
     711          11 :                                 GF_WebVTTSample *new_sample = gf_webvtt_sample_new();
     712          11 :                                 new_sample->start = cue_end;
     713          11 :                                 new_sample->end   = sample->end;
     714          11 :                                 gf_list_insert(samples, new_sample, i+1);
     715          12 :                                 for (j = 0; j < gf_list_count(sample->cues); j++) {
     716          12 :                                         GF_WebVTTCue *old_cue = (GF_WebVTTCue *)gf_list_get(sample->cues, j);
     717          12 :                                         GF_WebVTTCue *new_cue = gf_webvtt_cue_split_at(old_cue, &cue->end);
     718          12 :                                         gf_list_add(new_sample->cues, new_cue);
     719             :                                 }
     720          11 :                                 gf_list_add(sample->cues, cue);
     721          11 :                                 sample->end = new_sample->start;
     722             :                                 /* done with this cue */
     723             :                                 return GF_OK;
     724             :                         }
     725             :                 }
     726             :         }
     727             :         /* (a part of) the cue remains (was not overlapping) */
     728       14317 :         if (cue_start > sample_end) {
     729             :                 /* if the new cue start is greater than the last sample end,
     730             :                     create an empty sample to fill the gap, flush it */
     731         210 :                 GF_WebVTTSample *esample = gf_webvtt_sample_new();
     732         210 :                 esample->start = sample_end;
     733         210 :                 esample->end   = cue_start;
     734         210 :                 parser->on_sample_parsed(parser->user, esample);
     735             :         }
     736             :         /* if the cue has not been added to a sample, create a new sample for it */
     737             :         {
     738             :                 GF_WebVTTSample *sample;
     739       14317 :                 sample = gf_webvtt_sample_new();
     740       14317 :                 gf_list_add(samples, sample);
     741       14317 :                 sample->start = cue_start;
     742       14317 :                 sample->end = cue_end;
     743       14317 :                 gf_list_add(sample->cues, cue);
     744             :         }
     745             :         return GF_OK;
     746             : }
     747             : 
     748             : #define REM_TRAIL_MARKS(__str, __sep) while (1) {       \
     749             :                 u32 _len = (u32) strlen(__str);         \
     750             :                 if (!_len) break;       \
     751             :                 _len--;                         \
     752             :                 if (strchr(__sep, __str[_len])) { \
     753             :                         had_marks = GF_TRUE; \
     754             :                         __str[_len] = 0;        \
     755             :                 } else break;   \
     756             :         }
     757             : 
     758             : extern char *gf_text_get_utf8_line(char *szLine, u32 lineSize, FILE *txt_in, s32 unicode_type);
     759             : 
     760       28698 : GF_Err gf_webvtt_parse_timestamp(GF_WebVTTParser *parser, GF_WebVTTTimestamp *ts, const char *line)
     761             : {
     762             :         u32 len;
     763             :         u32 pos;
     764             :         u32 pos2;
     765             :         u32 value1;
     766             :         u32 value2;
     767             :         u32 value3;
     768             :         u32 value4;
     769             :         Bool is_hour = GF_FALSE;
     770       28698 :         if (!ts || !line) return GF_BAD_PARAM;
     771       28698 :         len = (u32) strlen(line);
     772       28698 :         if (!len) return GF_BAD_PARAM;
     773             :         pos = 0;
     774       28698 :         if (!(line[pos] >= '0' && line[pos] <= '9')) return GF_BAD_PARAM;
     775             :         value1 = 0;
     776       86099 :         while (pos < len && line[pos] >= '0' && line[pos] <= '9') {
     777       57401 :                 value1 = value1*10 + (line[pos]-'0');
     778       57401 :                 pos++;
     779             :         }
     780       28698 :         if (pos>2 || value1>59) {
     781             :                 is_hour = GF_TRUE;
     782             :         }
     783       28698 :         if (pos == len || line[pos] != ':') {
     784             :                 return GF_BAD_PARAM;
     785             :         } else {
     786       28698 :                 pos++;
     787             :         }
     788             :         value2 = 0;
     789             :         pos2 = 0;
     790      114792 :         while (pos < len && line[pos] >= '0' && line[pos] <= '9') {
     791       57397 :                 value2 = value2*10 + (line[pos]-'0');
     792       57397 :                 pos++;
     793       57397 :                 pos2++;
     794       57397 :                 if (pos2 > 2) return GF_BAD_PARAM;
     795             :         }
     796       28697 :         if (is_hour || (pos < len && line[pos] == ':')) {
     797       22427 :                 if (pos == len || line[pos] != ':') {
     798             :                         return GF_BAD_PARAM;
     799             :                 } else {
     800       22426 :                         pos++;
     801             :                         pos2 = 0;
     802             :                         value3 = 0;
     803       89704 :                         while (pos < len && line[pos] >= '0' && line[pos] <= '9') {
     804       44853 :                                 value3 = value3*10 + (line[pos]-'0');
     805       44853 :                                 pos++;
     806       44853 :                                 pos2++;
     807       44853 :                                 if (pos2 > 2) return GF_BAD_PARAM;
     808             :                         }
     809             :                 }
     810             :         } else {
     811             :                 value3 = value2;
     812             :                 value2 = value1;
     813             :                 value1 = 0;
     814             :         }
     815             :         /* checking SRT syntax for timestamp with , */
     816       28695 :         if (pos == len || (!parser->is_srt && line[pos] != '.') || (parser->is_srt && line[pos] != ',')) {
     817             :                 return GF_BAD_PARAM;
     818             :         } else {
     819       28695 :                 pos++;
     820             :         }
     821             :         pos2 = 0;
     822             :         value4 = 0;
     823      143476 :         while (pos < len && line[pos] >= '0' && line[pos] <= '9') {
     824       86086 :                 value4 = value4*10 + (line[pos]-'0');
     825       86086 :                 pos++;
     826       86086 :                 pos2++;
     827       86086 :                 if (pos2 > 4) return GF_BAD_PARAM;
     828             :         }
     829       28695 :         if (value2>59 || value3 > 59) return GF_BAD_PARAM;
     830       28695 :         ts->hour = value1;
     831       28695 :         ts->min = value2;
     832       28695 :         ts->sec = value3;
     833       28695 :         ts->ms = value4;
     834       28695 :         return GF_OK;
     835             : }
     836             : 
     837             : #define SKIP_WHITESPACE \
     838             :     while (pos < len && (line[pos] == ' ' || line[pos] == '\t' || \
     839             :            line[pos] == '\r' || line[pos] == '\f' || line[pos] == '\n')) pos++;
     840             : 
     841       14349 : GF_Err gf_webvtt_parser_parse_timings_settings(GF_WebVTTParser *parser, GF_WebVTTCue *cue, char *line, u32 len)
     842             : {
     843             :         GF_Err e;
     844             :         char *timestamp_string;
     845             :         u32 pos;
     846             : 
     847             :         pos = 0;
     848       14349 :         if (!cue || !line || !len) return GF_BAD_PARAM;
     849           3 :         SKIP_WHITESPACE
     850       14349 :         timestamp_string = line + pos;
     851       14349 :         while (pos < len && line[pos] != ' ' && line[pos] != '\t') pos++;
     852       14349 :         if (pos == len) {
     853             :                 e = GF_CORRUPTED_DATA;
     854           0 :                 parser->report_message(parser->user, e, "Error scanning WebVTT cue timing in %s", line);
     855           0 :                 return e;
     856             :         }
     857       14349 :         line[pos] = 0;
     858       14349 :         e = gf_webvtt_parse_timestamp(parser, &cue->start, timestamp_string);
     859       14349 :         if (e) {
     860           0 :                 parser->report_message(parser->user, e, "Bad VTT timestamp formatting %s", timestamp_string);
     861           0 :                 return e;
     862             :         }
     863       14349 :         line[pos] = ' ';
     864       14349 :         SKIP_WHITESPACE
     865       14349 :         if (pos == len) {
     866             :                 e = GF_CORRUPTED_DATA;
     867           0 :                 parser->report_message(parser->user, e, "Error scanning WebVTT cue timing in %s", line);
     868           0 :                 return e;
     869             :         }
     870       14349 :         if ( (pos+2)>= len || line[pos] != '-' || line[pos+1] != '-' || line[pos+2] != '>') {
     871             :                 e = GF_CORRUPTED_DATA;
     872           0 :                 parser->report_message(parser->user, e, "Error scanning WebVTT cue timing in %s", line);
     873           0 :                 return e;
     874             :         } else {
     875       14349 :                 pos += 3;
     876       14349 :                 SKIP_WHITESPACE
     877       14349 :                 if (pos == len) {
     878             :                         e = GF_CORRUPTED_DATA;
     879           0 :                         parser->report_message(parser->user, e, "Error scanning WebVTT cue timing in %s", line);
     880           0 :                         return e;
     881             :                 }
     882       14349 :                 timestamp_string = line + pos;
     883       14349 :                 while (pos < len && line[pos] != ' ' && line[pos] != '\t') pos++;
     884       14349 :                 if (pos < len) {
     885        3054 :                         line[pos] = 0;
     886             :                 }
     887       14349 :                 e = gf_webvtt_parse_timestamp(parser, &cue->end, timestamp_string);
     888       14349 :                 if (e) {
     889           3 :                         parser->report_message(parser->user, e, "Bad VTT timestamp formatting %s", timestamp_string);
     890           3 :                         return e;
     891             :                 }
     892       14346 :                 if (pos < len) {
     893        3054 :                         line[pos] = ' ';
     894             :                 }
     895        3054 :                 SKIP_WHITESPACE
     896       14346 :                 if (pos < len) {
     897          24 :                         char *settings = line + pos;
     898          24 :                         e = gf_webvtt_cue_add_property(cue, WEBVTT_SETTINGS, settings, (u32) strlen(settings));
     899             :                 }
     900             : 
     901       14346 :                 if (!gf_webvtt_timestamp_greater(&cue->end, &cue->start)) {
     902           1 :                         parser->report_message(parser->user, e, "Bad VTT timestamps, end smaller than start", timestamp_string);
     903           1 :                         cue->end = cue->start;
     904           1 :                         cue->end.ms += 1;
     905           1 :                         return GF_NON_COMPLIANT_BITSTREAM;
     906             : 
     907             :                 }
     908             :         }
     909             :         return e;
     910             : }
     911             : 
     912       14393 : GF_Err gf_webvtt_parser_parse(GF_WebVTTParser *parser)
     913             : {
     914             :         char szLine[2048];
     915             :         char *sOK;
     916             :         u32 len;
     917             :         GF_Err e;
     918             :         GF_WebVTTCue *cue = NULL;
     919             :         char *prevLine = NULL;
     920             :         char *header = NULL;
     921             :         u32 header_len = 0;
     922             :         Bool had_marks = GF_FALSE;
     923             : 
     924       14393 :         if (!parser) return GF_BAD_PARAM;
     925       14393 :         parser->suspend = GF_FALSE;
     926             : 
     927       14393 :         if (parser->is_srt) {
     928          14 :                 parser->on_header_parsed(parser->user, "WEBVTT\n");
     929             :         }
     930             : 
     931       68835 :         while (!parser->is_eof) {
     932       68757 :                 if (!cue && parser->suspend)
     933             :                         break;
     934       54442 :                 sOK = gf_text_get_utf8_line(szLine, 2048, parser->vtt_in, parser->unicode_type);
     935      108810 :                 REM_TRAIL_MARKS(szLine, "\r\n")
     936             :                 len = (u32) strlen(szLine);
     937       54442 :                 switch (parser->state) {
     938          44 :                 case WEBVTT_PARSER_STATE_WAITING_SIGNATURE:
     939          44 :                         if (!sOK || len < 6 || strnicmp(szLine, "WEBVTT", 6) || (len > 6 && szLine[6] != ' ' && szLine[6] != '\t')) {
     940             :                                 e = GF_CORRUPTED_DATA;
     941           0 :                                 parser->report_message(parser->user, e, "Bad WEBVTT file signature %s", szLine);
     942           0 :                                 goto exit;
     943             :                         } else {
     944          44 :                                 if (had_marks) {
     945          43 :                                         szLine[len] = '\n';
     946          43 :                                         len++;
     947             :                                 }
     948          44 :                                 header = gf_strdup(szLine);
     949             :                                 header_len = len;
     950          44 :                                 parser->state = WEBVTT_PARSER_STATE_WAITING_HEADER;
     951             :                         }
     952          44 :                         break; /* proceed to next line */
     953          74 :                 case WEBVTT_PARSER_STATE_WAITING_HEADER:
     954          74 :                         if (prevLine) {
     955          30 :                                 u32 prev_len = (u32) strlen(prevLine);
     956          30 :                                 header = (char *)gf_realloc(header, header_len + prev_len + 1);
     957          30 :                                 strcpy(header+header_len,prevLine);
     958             :                                 header_len += prev_len;
     959          30 :                                 gf_free(prevLine);
     960             :                                 prevLine = NULL;
     961             :                         }
     962          74 :                         if (sOK && len) {
     963          32 :                                 if (strstr(szLine, "-->")) {
     964           2 :                                         parser->on_header_parsed(parser->user, header);
     965             :                                         /* continue to the next state without breaking */
     966           2 :                                         parser->state = WEBVTT_PARSER_STATE_WAITING_CUE_TIMESTAMP;
     967             :                                         /* no break, continue to the next state*/
     968             :                                 } else {
     969          30 :                                         if (had_marks) {
     970          30 :                                                 szLine[len] = '\n';
     971             :                                                 len++;
     972             :                                         }
     973          30 :                                         prevLine = gf_strdup(szLine);
     974          30 :                                         break; /* proceed to next line */
     975             :                                 }
     976             :                         } else {
     977          42 :                                 parser->on_header_parsed(parser->user, header);
     978          42 :                                 if (header) gf_free(header);
     979             :                                 header = NULL;
     980          42 :                                 if (!sOK) {
     981             :                                         /* end of file, parsing is done */
     982           3 :                                         parser->is_eof = GF_TRUE;
     983           3 :                                         break;
     984             :                                 } else {
     985             :                                         /* empty line means end of header */
     986          39 :                                         parser->state = WEBVTT_PARSER_STATE_WAITING_CUE;
     987             :                                         /* no break, continue to the next state*/
     988             :                                 }
     989             :                         }
     990             :                 case WEBVTT_PARSER_STATE_WAITING_CUE:
     991       25663 :                         if (sOK && len) {
     992       25593 :                                 if (strstr(szLine, "-->")) {
     993       14349 :                                         parser->state = WEBVTT_PARSER_STATE_WAITING_CUE_TIMESTAMP;
     994             :                                         /* continue to the next state without breaking */
     995             :                                 } else {
     996             :                                         /* discard the previous line */
     997             :                                         /* should we do something with it ? callback ?*/
     998       11244 :                                         if (prevLine) {
     999          12 :                                                 gf_free(prevLine);
    1000             :                                                 prevLine = NULL;
    1001             :                                         }
    1002             :                                         /* save this new line */
    1003       11244 :                                         if (had_marks) {
    1004       11243 :                                                 szLine[len] = '\n';
    1005             :                                                 len++;
    1006             :                                         }
    1007       11244 :                                         prevLine = gf_strdup(szLine);
    1008             :                                         /* stay in the same state */
    1009       11244 :                                         break;
    1010             :                                 }
    1011             :                         } else {
    1012             :                                 /* discard the previous line */
    1013             :                                 /* should we do something with it ? callback ?*/
    1014          70 :                                 if (prevLine) {
    1015          16 :                                         gf_free(prevLine);
    1016             :                                         prevLine = NULL;
    1017             :                                 }
    1018          70 :                                 if (!sOK) {
    1019          13 :                                         parser->is_eof = GF_TRUE;
    1020          13 :                                         break;
    1021             :                                 } else {
    1022             :                                         /* remove empty lines and stay in the same state */
    1023             :                                         break;
    1024             :                                 }
    1025             :                         }
    1026             :                 case WEBVTT_PARSER_STATE_WAITING_CUE_TIMESTAMP:
    1027       14349 :                         if (sOK && len) {
    1028       14349 :                                 if (cue == NULL) {
    1029       14349 :                                         cue   = gf_webvtt_cue_new();
    1030             :                                 }
    1031       14349 :                                 if (prevLine) {
    1032       11216 :                                         gf_webvtt_cue_add_property(cue, WEBVTT_ID, prevLine, (u32) strlen(prevLine));
    1033       11216 :                                         gf_free(prevLine);
    1034             :                                         prevLine = NULL;
    1035             :                                 }
    1036       14349 :                                 e = gf_webvtt_parser_parse_timings_settings(parser, cue, szLine, len);
    1037       14349 :                                 if (e) {
    1038           4 :                                         if (cue) gf_webvtt_cue_del(cue);
    1039             :                                         cue = NULL;
    1040           4 :                                         parser->state = WEBVTT_PARSER_STATE_WAITING_CUE;
    1041             :                                 } else {
    1042             : //                                      start = (u32)gf_webvtt_timestamp_get(&cue->start);
    1043             : //                                      end   = (u32)gf_webvtt_timestamp_get(&cue->end);
    1044       14345 :                                         parser->state = WEBVTT_PARSER_STATE_WAITING_CUE_PAYLOAD;
    1045             :                                 }
    1046             :                         } else {
    1047             :                                 /* not possible */
    1048             :                                 assert(0);
    1049             :                         }
    1050             :                         break;
    1051       28702 :                 case WEBVTT_PARSER_STATE_WAITING_CUE_PAYLOAD:
    1052       28702 :                         if (sOK && len) {
    1053       14357 :                                 if (had_marks) {
    1054       14357 :                                         szLine[len] = '\n';
    1055       14357 :                                         len++;
    1056             :                                 }
    1057       14357 :                                 gf_webvtt_cue_add_property(cue, WEBVTT_PAYLOAD, szLine, len);
    1058             :                                 /* remain in the same state as a cue payload can have multiple lines */
    1059       14357 :                                 break;
    1060             :                         } else {
    1061             :                                 /* end of the current cue */
    1062       14345 :                                 gf_webvtt_add_cue_to_samples(parser, parser->samples, cue);
    1063             :                                 cue = NULL;
    1064             : 
    1065       14345 :                                 if (!sOK) {
    1066          30 :                                         parser->is_eof = GF_TRUE;
    1067          30 :                                         break;
    1068             :                                 } else {
    1069             :                                         /* empty line, move to next cue */
    1070       14315 :                                         parser->state = WEBVTT_PARSER_STATE_WAITING_CUE;
    1071       14315 :                                         break;
    1072             :                                 }
    1073             :                         }
    1074             :                 }
    1075             :         }
    1076       14393 :         if (header) gf_free(header);
    1077             :         header = NULL;
    1078             : 
    1079       14393 :         if (parser->suspend)
    1080             :                 return GF_OK;
    1081             : 
    1082             :         /* no more cues to come, flush everything */
    1083          46 :         if (cue) {
    1084           0 :                 gf_webvtt_add_cue_to_samples(parser, parser->samples, cue);
    1085             :                 cue = NULL;
    1086             :         }
    1087          97 :         while (gf_list_count(parser->samples) > 0) {
    1088          51 :                 GF_WebVTTSample *sample = (GF_WebVTTSample *)gf_list_get(parser->samples, 0);
    1089          51 :                 parser->last_duration = (sample->end > sample->start) ? sample->end - sample->start : 0;
    1090          51 :                 gf_list_rem(parser->samples, 0);
    1091          51 :                 parser->on_sample_parsed(parser->user, sample);
    1092             :         }
    1093             :         e = GF_EOS;
    1094          46 : exit:
    1095          46 :         if (cue) gf_webvtt_cue_del(cue);
    1096          46 :         if (prevLine) gf_free(prevLine);
    1097          46 :         if (header) gf_free(header);
    1098             :         return e;
    1099             : }
    1100             : 
    1101             : GF_EXPORT
    1102          17 : GF_List *gf_webvtt_parse_iso_cues(GF_ISOSample *iso_sample, u64 start, u64 end)
    1103             : {
    1104          17 :         return gf_webvtt_parse_cues_from_data(iso_sample->data, iso_sample->dataLength, start, end);
    1105             : }
    1106             : 
    1107             : GF_EXPORT
    1108         101 : GF_List *gf_webvtt_parse_cues_from_data(const u8 *data, u32 dataLength, u64 start, u64 end)
    1109             : {
    1110             :         GF_List *cues;
    1111             :         GF_WebVTTCue *cue;
    1112             :         GF_VTTCueBox *cuebox;
    1113             :         GF_BitStream *bs;
    1114             :         char *pre_text;
    1115             :         cue = NULL;
    1116             :         pre_text = NULL;
    1117         101 :         cues = gf_list_new();
    1118         101 :         bs = gf_bs_new((u8 *)data, dataLength, GF_BITSTREAM_READ);
    1119         318 :         while(gf_bs_available(bs))
    1120             :         {
    1121             :                 GF_Err  e;
    1122             :                 GF_Box  *box;
    1123         116 :                 e = gf_isom_box_parse(&box, bs);
    1124         116 :                 if (e) return NULL;
    1125         116 :                 if (box->type == GF_ISOM_BOX_TYPE_VTCC_CUE) {
    1126             :                         cuebox = (GF_VTTCueBox *)box;
    1127          90 :                         cue   = gf_webvtt_cue_new();
    1128          90 :                         if (pre_text) {
    1129           0 :                                 gf_webvtt_cue_add_property(cue, WEBVTT_PRECUE_TEXT, pre_text, (u32) strlen(pre_text));
    1130           0 :                                 gf_free(pre_text);
    1131             :                                 pre_text = NULL;
    1132             :                         }
    1133          90 :                         gf_list_add(cues, cue);
    1134          90 :                         gf_webvtt_timestamp_set(&cue->start, start);
    1135          90 :                         gf_webvtt_timestamp_set(&cue->end, end);
    1136          90 :                         if (cuebox->id) {
    1137          46 :                                 gf_webvtt_cue_add_property(cue, WEBVTT_ID, cuebox->id->string, (u32) strlen(cuebox->id->string));
    1138             :                         }
    1139          90 :                         if (cuebox->settings) {
    1140          18 :                                 gf_webvtt_cue_add_property(cue, WEBVTT_SETTINGS, cuebox->settings->string, (u32) strlen(cuebox->settings->string));
    1141             :                         }
    1142          90 :                         if (cuebox->payload) {
    1143          90 :                                 gf_webvtt_cue_add_property(cue, WEBVTT_PAYLOAD, cuebox->payload->string, (u32) strlen(cuebox->payload->string));
    1144             :                         }
    1145          26 :                 } else if (box->type == GF_ISOM_BOX_TYPE_VTTA) {
    1146             :                         GF_StringBox *sbox = (GF_StringBox *)box;
    1147           0 :                         if (cue) {
    1148           0 :                                 gf_webvtt_cue_add_property(cue, WEBVTT_POSTCUE_TEXT, sbox->string, (u32) strlen(sbox->string));
    1149             :                         } else {
    1150           0 :                                 pre_text = gf_strdup(sbox->string);
    1151             :                         }
    1152             :                 }
    1153         116 :                 gf_isom_box_del(box);
    1154             :         }
    1155         101 :         gf_bs_del(bs);
    1156         101 :         return cues;
    1157             : }
    1158             : 
    1159          28 : GF_Err gf_webvtt_merge_cues(GF_WebVTTParser *parser, u64 start, GF_List *cues)
    1160             : {
    1161             :         GF_WebVTTSample *wsample;
    1162             :         GF_WebVTTSample *prev_wsample;
    1163             :         Bool            has_continuation_cue = GF_FALSE;
    1164             : 
    1165             :         assert(gf_list_count(parser->samples) <= 1);
    1166             : 
    1167          28 :         wsample = gf_webvtt_sample_new();
    1168          28 :         wsample->start = start;
    1169             : 
    1170          28 :         prev_wsample = (GF_WebVTTSample *)gf_list_last(parser->samples);
    1171          94 :         while (gf_list_count(cues)) {
    1172          38 :                 GF_WebVTTCue *cue = (GF_WebVTTCue *)gf_list_get(cues, 0);
    1173          38 :                 gf_list_rem(cues, 0);
    1174             :                 /* add the cue to the current sample */
    1175          38 :                 gf_list_add(wsample->cues, cue);
    1176             :                 /* update with the previous sample */
    1177          38 :                 if (prev_wsample) {
    1178             :                         Bool do_del = GF_TRUE;
    1179             :                         Bool  found = GF_FALSE;
    1180          70 :                         while (!found && gf_list_count(prev_wsample->cues)) {
    1181          35 :                                 GF_WebVTTCue *old_cue = (GF_WebVTTCue *)gf_list_get(prev_wsample->cues, 0);
    1182          35 :                                 gf_list_rem(prev_wsample->cues, 0);
    1183          35 :                                 if (
    1184          78 :                                     ((!cue->id && !old_cue->id) || (old_cue->id && cue->id && !strcmp(old_cue->id, cue->id))) &&
    1185          71 :                                     ((!cue->settings && !old_cue->settings) || (old_cue->settings && cue->settings && !strcmp(old_cue->settings, cue->settings))) &&
    1186          23 :                                     ((!cue->text && !old_cue->text) || (old_cue->text && cue->text && !strcmp(old_cue->text, cue->text)))
    1187             :                                 ) {
    1188             :                                         /* if it is the same cue, update its start with the initial start */
    1189          19 :                                         cue->start = old_cue->start;
    1190             :                                         has_continuation_cue = GF_TRUE;
    1191             :                                         found = GF_TRUE;
    1192          19 :                                         if (old_cue->pre_text) {
    1193           0 :                                                 cue->pre_text = old_cue->pre_text;
    1194           0 :                                                 old_cue->pre_text = NULL;
    1195             :                                         }
    1196          19 :                                         if (old_cue->post_text) {
    1197           0 :                                                 cue->post_text = old_cue->post_text;
    1198           0 :                                                 old_cue->post_text = NULL;
    1199             :                                         }
    1200             :                                 } else {
    1201             :                                         /* finalize the end cue time */
    1202          16 :                                         if (gf_webvtt_timestamp_is_zero(&old_cue->end)) {
    1203           0 :                                                 gf_webvtt_timestamp_set(&old_cue->end, wsample->start);
    1204             :                                         }
    1205             :                                         /* transfer the cue */
    1206          16 :                                         if (!has_continuation_cue) {
    1207             :                                                 /* the cue can be safely serialized while keeping the order */
    1208          16 :                                                 parser->on_cue_read(parser->user, old_cue);
    1209             :                                         } else {
    1210             :                                                 /* keep the cue in the current sample to respect cue start ordering */
    1211           0 :                                                 gf_list_add(wsample->cues, old_cue);
    1212             :                                                 do_del = GF_FALSE;
    1213             :                                         }
    1214             :                                 }
    1215             :                                 /* delete the old cue */
    1216          35 :                                 if (do_del)
    1217          35 :                                         gf_webvtt_cue_del(old_cue);
    1218             :                         }
    1219             :                 }
    1220             :         }
    1221             :         /* No cue in the current sample */
    1222          28 :         if (prev_wsample) {
    1223          26 :                 while (gf_list_count(prev_wsample->cues)) {
    1224             :                         Bool do_del = GF_TRUE;
    1225           2 :                         GF_WebVTTCue *cue = (GF_WebVTTCue *)gf_list_get(prev_wsample->cues, 0);
    1226           2 :                         gf_list_rem(prev_wsample->cues, 0);
    1227             :                         /* finalize the end cue time */
    1228           2 :                         if (gf_webvtt_timestamp_is_zero(&cue->end)) {
    1229           0 :                                 gf_webvtt_timestamp_set(&cue->end, wsample->start);
    1230             :                         }
    1231             :                         /* transfer the cue */
    1232           2 :                         if (!has_continuation_cue) {
    1233             :                                 /* the cue can be safely serialized while keeping the order */
    1234           1 :                                 parser->on_cue_read(parser->user, cue);
    1235             :                         } else {
    1236             :                                 /* keep the cue in the current sample to respect cue start ordering */
    1237           1 :                                 gf_list_add(wsample->cues, cue);
    1238             :                                 do_del = GF_FALSE;
    1239             :                         }
    1240             :                         if (do_del)
    1241           1 :                                 gf_webvtt_cue_del(cue);
    1242             :                 }
    1243          24 :                 gf_webvtt_sample_del(prev_wsample);
    1244          24 :                 gf_list_rem_last(parser->samples);
    1245             :                 prev_wsample = NULL;
    1246             :         } else {
    1247             :                 /* nothing to do */
    1248             :         }
    1249          28 :         if (gf_list_count(wsample->cues)) {
    1250          26 :                 gf_list_add(parser->samples, wsample);
    1251             :         } else {
    1252           2 :                 gf_webvtt_sample_del(wsample);
    1253             :         }
    1254          28 :         return GF_OK;
    1255             : }
    1256             : 
    1257          34 : static GF_Err gf_webvtt_parse_iso_sample(GF_WebVTTParser *parser, u32 timescale, GF_ISOSample *iso_sample, u32 duration, Bool merge, Bool box_mode)
    1258             : {
    1259          34 :         if (merge) {
    1260             :                 u64             start;
    1261             :                 u64             end;
    1262             :                 GF_List         *cues;
    1263          17 :                 start = (iso_sample->DTS * 1000) / timescale;
    1264          17 :                 end = (iso_sample->DTS + duration) * 1000 / timescale;
    1265          17 :                 cues = gf_webvtt_parse_iso_cues(iso_sample, start, end);
    1266          17 :                 gf_webvtt_merge_cues(parser, start, cues);
    1267          17 :                 gf_list_del(cues);
    1268             :         } else {
    1269             :                 GF_Err gf_webvtt_dump_iso_sample(FILE *dump, u32 timescale, GF_ISOSample *iso_sample, Bool box_mode);
    1270             : 
    1271          17 :                 gf_webvtt_dump_iso_sample((FILE *)parser->user, timescale, iso_sample, box_mode);
    1272             :         }
    1273             : 
    1274          34 :         return GF_OK;
    1275             : }
    1276             : #endif //GPAC_DISABLE_MEDIA_IMPORT
    1277             : 
    1278             : 
    1279          34 : void gf_webvtt_timestamp_set(GF_WebVTTTimestamp *ts, u64 value)
    1280             : {
    1281             :         u64 tmp;
    1282         214 :         if (!ts) return;
    1283             :         tmp = value;
    1284         214 :         ts->hour = (u32)(tmp/(3600*1000));
    1285         214 :         tmp -= ts->hour*3600*1000;
    1286         214 :         ts->min  = (u32)(tmp/(60*1000));
    1287         214 :         tmp -= ts->min*60*1000;
    1288         214 :         ts->sec  = (u32)(tmp/1000);
    1289         214 :         tmp -= ts->sec*1000;
    1290         214 :         ts->ms   = (u32)tmp;
    1291             : }
    1292             : 
    1293           0 : u64 gf_webvtt_timestamp_get(GF_WebVTTTimestamp *ts)
    1294             : {
    1295       28690 :         if (!ts) return 0;
    1296       28690 :         return (3600*ts->hour + 60*ts->min + ts->sec)*1000 + ts->ms;
    1297             : }
    1298             : 
    1299          60 : void gf_webvtt_timestamp_dump(GF_WebVTTTimestamp *ts, FILE *dump, Bool dump_hour)
    1300             : {
    1301          60 :         if (dump_hour || ts->hour != 0) {
    1302          34 :                 gf_fprintf(dump, "%02u:", ts->hour);
    1303             :         }
    1304             : 
    1305          60 :         gf_fprintf(dump, "%02u:%02u.%03u", ts->min, ts->sec, ts->ms);
    1306          60 : }
    1307           2 : GF_Err gf_webvtt_dump_header_boxed(FILE *dump, const u8 *data, u32 dataLength, u32 *dumpedLength)
    1308             : {
    1309             : #ifdef GPAC_DISABLE_ISOM
    1310             :         return GF_NOT_SUPPORTED;
    1311             : #else
    1312             :         GF_Err e;
    1313             :         GF_Box *box;
    1314             :         GF_StringBox *config;
    1315             :         GF_BitStream *bs;
    1316           2 :         *dumpedLength = 0;
    1317           2 :         bs = gf_bs_new((u8 *)data, dataLength, GF_BITSTREAM_READ);
    1318           2 :         e = gf_isom_box_parse(&box, bs);
    1319           2 :         if (!box || (box->type != GF_ISOM_BOX_TYPE_VTTC_CONFIG)) {
    1320           2 :                 gf_bs_del(bs);
    1321           2 :                 if (box) gf_isom_box_del(box);
    1322             :                 return GF_BAD_PARAM;
    1323             :         }
    1324             :         config = (GF_StringBox *)box;
    1325           0 :         if (config->string) {
    1326           0 :                 gf_fprintf(dump, "%s", config->string);
    1327           0 :                 *dumpedLength = (u32)strlen(config->string)+1;
    1328             :         }
    1329           0 :         gf_bs_del(bs);
    1330           0 :         gf_isom_box_del(box);
    1331           0 :         return e;
    1332             : #endif
    1333             : }
    1334             : 
    1335             : #ifndef GPAC_DISABLE_ISOM
    1336           2 : GF_Err gf_webvtt_dump_header(FILE *dump, GF_ISOFile *file, u32 track, Bool box_mode, u32 index)
    1337             : {
    1338             :         GF_WebVTTSampleEntryBox *wvtt;
    1339           2 :         wvtt = gf_webvtt_isom_get_description(file, track, index);
    1340           2 :         if (!wvtt) return GF_BAD_PARAM;
    1341           2 :         if (box_mode) {
    1342           2 :                 gf_isom_box_dump(wvtt, dump);
    1343             :         } else {
    1344           0 :                 gf_fprintf(dump, "%s\n\n", wvtt->config->string);
    1345             :         }
    1346             :         return GF_OK;
    1347             : }
    1348             : 
    1349          17 : GF_Err gf_webvtt_dump_iso_sample(FILE *dump, u32 timescale, GF_ISOSample *iso_sample, Bool box_mode)
    1350             : {
    1351             :         GF_Err e;
    1352             :         GF_BitStream *bs;
    1353             : 
    1354          17 :         if (box_mode) {
    1355          17 :                 gf_fprintf(dump, "<WebVTTSample decodingTimeStamp=\""LLU"\" compositionTimeStamp=\""LLD"\" RAP=\"%d\" dataLength=\"%d\" >\n", iso_sample->DTS, (s64)iso_sample->DTS + iso_sample->CTS_Offset, iso_sample->IsRAP, iso_sample->dataLength);
    1356             :         }
    1357          17 :         bs = gf_bs_new(iso_sample->data, iso_sample->dataLength, GF_BITSTREAM_READ);
    1358          54 :         while(gf_bs_available(bs))
    1359             :         {
    1360             :                 GF_Box *box;
    1361             :                 GF_WebVTTTimestamp ts;
    1362          20 :                 e = gf_isom_box_parse(&box, bs);
    1363          20 :                 if (e) return e;
    1364             : 
    1365          20 :                 if (box_mode) {
    1366          20 :                         gf_isom_box_dump(box, dump);
    1367           0 :                 } else if (box->type == GF_ISOM_BOX_TYPE_VTCC_CUE) {
    1368             :                         GF_VTTCueBox *cuebox = (GF_VTTCueBox *)box;
    1369           0 :                         if (cuebox->id) gf_fprintf(dump, "%s", cuebox->id->string);
    1370           0 :                         gf_webvtt_timestamp_set(&ts, (iso_sample->DTS * 1000) / timescale);
    1371           0 :                         gf_webvtt_timestamp_dump(&ts, dump, GF_FALSE);
    1372           0 :                         gf_fprintf(dump, " --> NEXT");
    1373           0 :                         if (cuebox->settings) gf_fprintf(dump, " %s", cuebox->settings->string);
    1374           0 :                         gf_fprintf(dump, "\n");
    1375           0 :                         if (cuebox->payload) gf_fprintf(dump, "%s", cuebox->payload->string);
    1376           0 :                         gf_fprintf(dump, "\n");
    1377           0 :                 } else if (box->type == GF_ISOM_BOX_TYPE_VTTE) {
    1378           0 :                         gf_webvtt_timestamp_set(&ts, (iso_sample->DTS * 1000) / timescale);
    1379           0 :                         gf_webvtt_timestamp_dump(&ts, dump, GF_FALSE);
    1380           0 :                         gf_fprintf(dump, " --> NEXT\n\n");
    1381           0 :                 } else if (box->type == GF_ISOM_BOX_TYPE_VTTA) {
    1382           0 :                         gf_fprintf(dump, "%s\n\n", ((GF_StringBox *)box)->string);
    1383             :                 }
    1384          20 :                 gf_isom_box_del(box);
    1385             :         }
    1386          17 :         gf_bs_del(bs);
    1387          17 :         if (box_mode) {
    1388          17 :                 gf_fprintf(dump, "</WebVTTSample>\n");
    1389             :         }
    1390             :         return GF_OK;
    1391             : }
    1392             : #endif
    1393             : 
    1394             : #ifndef GPAC_DISABLE_ISOM_DUMP
    1395           3 : GF_Err gf_webvtt_parser_finalize(GF_WebVTTParser *parser, u64 duration)
    1396             : {
    1397             :         GF_WebVTTSample *sample;
    1398             :         assert(gf_list_count(parser->samples) <= 1);
    1399           3 :         sample = (GF_WebVTTSample *)gf_list_get(parser->samples, 0);
    1400           3 :         if (sample) {
    1401           4 :                 while (gf_list_count(sample->cues)) {
    1402           2 :                         GF_WebVTTCue *cue = (GF_WebVTTCue *)gf_list_get(sample->cues, 0);
    1403           2 :                         gf_list_rem(sample->cues, 0);
    1404           2 :                         if (gf_webvtt_timestamp_is_zero(&cue->end)) {
    1405             :                                 gf_webvtt_timestamp_set(&cue->end, duration);
    1406             :                         }
    1407           2 :                         parser->on_cue_read(parser->user, cue);
    1408           2 :                         gf_webvtt_cue_del(cue);
    1409             :                 }
    1410           2 :                 gf_webvtt_sample_del(sample);
    1411           2 :                 gf_list_rem(parser->samples, 0);
    1412             :         }
    1413           3 :         return GF_OK;
    1414             : }
    1415             : 
    1416             : #ifndef GPAC_DISABLE_MEDIA_IMPORT
    1417          13 : static void gf_webvtt_dump_cue(void *user, GF_WebVTTCue *cue)
    1418             : {
    1419             :         FILE *dump = (FILE *)user;
    1420          13 :         if (!cue || !dump) return;
    1421          13 :         if (cue->pre_text) {
    1422           0 :                 gf_fprintf(dump, "%s", cue->pre_text);
    1423           0 :                 gf_fprintf(dump, "\n");
    1424           0 :                 gf_fprintf(dump, "\n");
    1425             :         }
    1426          13 :         if (cue->id) gf_fprintf(dump, "%s\n", cue->id);
    1427          13 :         if (cue->start.hour || cue->end.hour) {
    1428           0 :                 gf_webvtt_timestamp_dump(&cue->start, dump, GF_TRUE);
    1429           0 :                 gf_fprintf(dump, " --> ");
    1430           0 :                 gf_webvtt_timestamp_dump(&cue->end, dump, GF_TRUE);
    1431             :         } else {
    1432          13 :                 gf_webvtt_timestamp_dump(&cue->start, dump, GF_FALSE);
    1433          13 :                 gf_fprintf(dump, " --> ");
    1434          13 :                 gf_webvtt_timestamp_dump(&cue->end, dump, GF_FALSE);
    1435             :         }
    1436          13 :         if (cue->settings) {
    1437           4 :                 gf_fprintf(dump, " %s", cue->settings);
    1438             :         }
    1439          13 :         gf_fprintf(dump, "\n");
    1440          13 :         if (cue->text) gf_fprintf(dump, "%s", cue->text);
    1441          13 :         gf_fprintf(dump, "\n");
    1442          13 :         gf_fprintf(dump, "\n");
    1443          13 :         if (cue->post_text) {
    1444           0 :                 gf_fprintf(dump, "%s", cue->post_text);
    1445           0 :                 gf_fprintf(dump, "\n");
    1446           0 :                 gf_fprintf(dump, "\n");
    1447             :         }
    1448             : }
    1449             : #endif
    1450             : 
    1451             : //unused
    1452             : #if 0
    1453             : static GF_Err gf_webvtt_dump_cues(FILE *dump, GF_List *cues)
    1454             : {
    1455             :         u32 i;
    1456             :         for (i = 0; i < gf_list_count(cues); i++) {
    1457             :                 GF_WebVTTCue *cue = (GF_WebVTTCue *)gf_list_get(cues, i);
    1458             :                 gf_webvtt_dump_cue(dump, cue);
    1459             :         }
    1460             :         return GF_OK;
    1461             : }
    1462             : 
    1463             : GF_Err gf_webvtt_dump_sample(FILE *dump, GF_WebVTTSample *samp)
    1464             : {
    1465             :         gf_fprintf(stdout, "NOTE New WebVTT Sample ("LLD"-"LLD")\n\n", samp->start, samp->end);
    1466             :         return gf_webvtt_dump_cues(dump, samp->cues);
    1467             : }
    1468             : #endif
    1469             : 
    1470             : 
    1471           1 : void gf_webvtt_parser_cue_callback(GF_WebVTTParser *parser, void (*on_cue_read)(void *, GF_WebVTTCue *), void *udta)
    1472             : {
    1473           1 :         parser->on_cue_read = on_cue_read;
    1474           1 :         parser->user = udta;
    1475           1 : }
    1476             : 
    1477             : #ifndef GPAC_DISABLE_MEDIA_EXPORT
    1478             : 
    1479             : GF_EXPORT
    1480           2 : GF_Err gf_webvtt_dump_iso_track(GF_MediaExporter *dumper, u32 track, Bool merge, Bool box_dump)
    1481             : {
    1482             : #ifdef GPAC_DISABLE_MEDIA_IMPORT
    1483             :         return GF_NOT_SUPPORTED;
    1484             : #else
    1485             :         GF_Err  e;
    1486             :         u32     i;
    1487             :         u32     count;
    1488             :         u32     timescale;
    1489             :         FILE    *out;
    1490             :         u32     di;
    1491             :         u64     duration;
    1492             :         GF_WebVTTParser *parser;
    1493             : 
    1494           2 :         out = (dumper->dump_file ? dumper->dump_file : stdout);
    1495           2 :         if (!out) return GF_IO_ERR;// gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szName);
    1496             : 
    1497           2 :         parser = gf_webvtt_parser_new();
    1498           2 :         parser->user = out;
    1499           2 :         parser->on_cue_read = gf_webvtt_dump_cue;
    1500             : 
    1501           2 :         if (box_dump)
    1502           2 :                 gf_fprintf(out, "<WebVTTTrack trackID=\"%d\">\n", gf_isom_get_track_id(dumper->file, track) );
    1503             : 
    1504           2 :         e = gf_webvtt_dump_header(out, dumper->file, track, box_dump, 1);
    1505           2 :         if (e) goto exit;
    1506             : 
    1507           2 :         timescale = gf_isom_get_media_timescale(dumper->file, track);
    1508             : 
    1509           2 :         count = gf_isom_get_sample_count(dumper->file, track);
    1510           2 :         for (i=0; i<count; i++) {
    1511             :                 u32 sdur;
    1512          34 :                 GF_ISOSample *samp = gf_isom_get_sample(dumper->file, track, i+1, &di);
    1513          34 :                 if (!samp) {
    1514           0 :                         e = gf_isom_last_error(dumper->file);
    1515           0 :                         goto exit;
    1516             :                 }
    1517          34 :                 sdur = gf_isom_get_sample_duration(dumper->file, track, i+1);
    1518          34 :                 e = gf_webvtt_parse_iso_sample(parser, timescale, samp, sdur, merge, box_dump);
    1519          34 :                 if (e) {
    1520             :                         goto exit;
    1521             :                 }
    1522          34 :                 gf_isom_sample_del(&samp);
    1523             :         }
    1524           2 :         duration = gf_isom_get_media_duration(dumper->file, track);
    1525           2 :         gf_webvtt_parser_finalize(parser, duration);
    1526             : 
    1527           2 :         if (box_dump)
    1528           2 :                 gf_fprintf(out, "</WebVTTTrack>\n");
    1529             : 
    1530           2 : exit:
    1531           2 :         gf_webvtt_parser_del(parser);
    1532           2 :         return e;
    1533             : #endif
    1534             : }
    1535             : 
    1536             : #endif /*GPAC_DISABLE_MEDIA_EXPORT*/
    1537             : 
    1538             : #endif /*GPAC_DISABLE_ISOM_DUMP*/
    1539             : 
    1540             : #endif /*GPAC_DISABLE_VTT*/

Generated by: LCOV version 1.13