static GF_Err VTT_ProcessData(GF_SceneDecoder *plug, const char *inBuffer, u32 inBufferLength, u16 ES_ID, u32 stream_time, u32 mmlevel) { GF_Err e = GF_OK; VTTDec *vttdec = (VTTDec *)plug->privateStack; if (!vttdec->has_rendering_script) return GF_BAD_PARAM; if (stream_time==(u32)-1) { /* TODO */ return GF_OK; } switch (vttdec->oti) { case GPAC_OTI_PRIVATE_SCENE_VTT: /*full parsing*/ if (vttdec->file_size) { /*init step*/ if (!vttdec->use_progressive) { /*not done yet*/ if (!vtt_check_download(vttdec)) return GF_OK; /* TODO: parse */ } else { /* TODO: parse what you can */ } } break; case GPAC_OTI_SCENE_VTT: /* TODO: try to parse a cue from the given string */ break; case GPAC_OTI_SCENE_VTT_MP4: { #ifndef GPAC_DISABLE_MEDIA_IMPORT char start[100], end[100]; GF_List *cues; cues = gf_webvtt_parse_cues_from_data(inBuffer, inBufferLength, 0); gf_webvtt_js_removeCues(vttdec->sg->RootNode); if (gf_list_count(cues)) { while (gf_list_count(cues)) { GF_WebVTTCue *cue = (GF_WebVTTCue *)gf_list_get(cues, 0); gf_list_rem(cues, 0); sprintf(start, "%02d:%02d:%02d.%03d", cue->start.hour, cue->start.min, cue->start.sec, cue->start.ms); sprintf(end, "%02d:%02d:%02d.%03d", cue->end.hour, cue->end.min, cue->end.sec, cue->end.ms); gf_webvtt_js_addCue(vttdec->sg->RootNode, cue->id, start, end, cue->settings, cue->text); gf_webvtt_cue_del(cue); } } gf_list_del(cues); #endif } break; default: return GF_BAD_PARAM; } return e; }
void gf_webvtt_sample_del(GF_WebVTTSample * samp) { while (gf_list_count(samp->cues)) { GF_WebVTTCue *cue = (GF_WebVTTCue *)gf_list_get(samp->cues, 0); gf_list_rem(samp->cues, 0); gf_webvtt_cue_del(cue); } gf_list_del(samp->cues); gf_free(samp); }
static GF_Err gf_webvtt_parser_dump_finalize(GF_WebVTTParser *parser, u64 duration) { GF_WebVTTSample *sample; assert(gf_list_count(parser->samples) <= 1); sample = (GF_WebVTTSample *)gf_list_get(parser->samples, 0); if (sample) { while (gf_list_count(sample->cues)) { GF_WebVTTCue *cue = (GF_WebVTTCue *)gf_list_get(sample->cues, 0); gf_list_rem(sample->cues, 0); if (gf_webvtt_timestamp_is_zero(&cue->end)) { gf_webvtt_timestamp_set(&cue->end, duration); } parser->on_cue_read(parser->user, cue); gf_webvtt_cue_del(cue); } gf_webvtt_sample_del(sample); gf_list_rem(parser->samples, 0); } return GF_OK; }
GF_Err gf_webvtt_parser_parse(GF_WebVTTParser *parser, u32 duration) { char szLine[2048]; char *sOK; u32 len; GF_Err e; Bool do_parse = GF_TRUE; GF_WebVTTCue *cue = NULL; u32 start = 0; u32 end = 0; char *prevLine = NULL; char *header = NULL; u32 header_len = 0; Bool had_marks = GF_FALSE; if (!parser) return GF_BAD_PARAM; if (parser->is_srt) { parser->on_header_parsed(parser->user, gf_strdup("WEBVTT\n")); } while (do_parse) { sOK = gf_text_get_utf8_line(szLine, 2048, parser->vtt_in, parser->unicode_type); REM_TRAIL_MARKS(szLine, "\r\n") len = (u32) strlen(szLine); switch (parser->state) { case WEBVTT_PARSER_STATE_WAITING_SIGNATURE: if (!sOK || len < 6 || strnicmp(szLine, "WEBVTT", 6) || (len > 6 && szLine[6] != ' ' && szLine[6] != '\t')) { e = GF_CORRUPTED_DATA; parser->report_message(parser->user, e, "Bad WEBVTT file signature %s", szLine); goto exit; } else { if (had_marks) { szLine[len] = '\n'; len++; } header = gf_strdup(szLine); header_len = len; parser->state = WEBVTT_PARSER_STATE_WAITING_HEADER; } break; /* proceed to next line */ case WEBVTT_PARSER_STATE_WAITING_HEADER: if (prevLine) { u32 prev_len = (u32) strlen(prevLine); header = (char *)gf_realloc(header, header_len + prev_len + 1); strcpy(header+header_len,prevLine); header_len += prev_len; gf_free(prevLine); prevLine = NULL; } if (sOK && len) { if (strstr(szLine, "-->")) { parser->on_header_parsed(parser->user, header); /* continue to the next state without breaking */ parser->state = WEBVTT_PARSER_STATE_WAITING_CUE_TIMESTAMP; /* no break, continue to the next state*/ } else { if (had_marks) { szLine[len] = '\n'; len++; } prevLine = gf_strdup(szLine); break; /* proceed to next line */ } } else { parser->on_header_parsed(parser->user, header); if (!sOK) { /* end of file, parsing is done */ do_parse = GF_FALSE; break; } else { /* empty line means end of header */ parser->state = WEBVTT_PARSER_STATE_WAITING_CUE; /* no break, continue to the next state*/ } } case WEBVTT_PARSER_STATE_WAITING_CUE: if (sOK && len) { if (strstr(szLine, "-->")) { parser->state = WEBVTT_PARSER_STATE_WAITING_CUE_TIMESTAMP; /* continue to the next state without breaking */ } else { /* discard the previous line */ /* should we do something with it ? callback ?*/ if (prevLine) { gf_free(prevLine); prevLine = NULL; } /* save this new line */ if (had_marks) { szLine[len] = '\n'; len++; } prevLine = gf_strdup(szLine); /* stay in the same state */ break; } } else { /* discard the previous line */ /* should we do something with it ? callback ?*/ if (prevLine) { gf_free(prevLine); prevLine = NULL; } if (!sOK) { do_parse = GF_FALSE; break; } else { /* remove empty lines and stay in the same state */ break; } } case WEBVTT_PARSER_STATE_WAITING_CUE_TIMESTAMP: if (sOK && len) { if (cue == NULL) { cue = gf_webvtt_cue_new(); } if (prevLine) { gf_webvtt_cue_add_property(cue, WEBVTT_ID, prevLine, (u32) strlen(prevLine)); gf_free(prevLine); prevLine = NULL; } e = gf_webvtt_parser_parse_timings_settings(parser, cue, szLine, len); if (e) { if (cue) gf_webvtt_cue_del(cue); cue = NULL; parser->state = WEBVTT_PARSER_STATE_WAITING_CUE; } else { start = (u32)gf_webvtt_timestamp_get(&cue->start); end = (u32)gf_webvtt_timestamp_get(&cue->end); parser->state = WEBVTT_PARSER_STATE_WAITING_CUE_PAYLOAD; } } else { /* not possible */ assert(0); } break; case WEBVTT_PARSER_STATE_WAITING_CUE_PAYLOAD: if (sOK && len) { if (had_marks) { szLine[len] = '\n'; len++; } gf_webvtt_cue_add_property(cue, WEBVTT_PAYLOAD, szLine, len); /* remain in the same state as a cue payload can have multiple lines */ break; } else { /* end of the current cue */ gf_webvtt_add_cue_to_samples(parser, parser->samples, cue); cue = NULL; gf_set_progress("Importing WebVTT", gf_ftell(parser->vtt_in), parser->file_size); if ((duration && (end >= duration)) || !sOK) { do_parse = GF_FALSE; break; } else { /* empty line, move to next cue */ parser->state = WEBVTT_PARSER_STATE_WAITING_CUE; break; } } } if (duration && (start >= duration)) { do_parse = GF_FALSE; break; } } /* no more cues to come, flush everything */ if (cue) { gf_webvtt_add_cue_to_samples(parser, parser->samples, cue); cue = NULL; } while (gf_list_count(parser->samples) > 0) { GF_WebVTTSample *sample = (GF_WebVTTSample *)gf_list_get(parser->samples, 0); parser->last_duration = sample->end - sample->start; gf_list_rem(parser->samples, 0); parser->on_sample_parsed(parser->user, sample); } gf_set_progress("Importing WebVTT", parser->file_size, parser->file_size); e = GF_OK; exit: if (cue) gf_webvtt_cue_del(cue); if (prevLine) gf_free(prevLine); if (header) gf_free(header); return e; }
GF_Err gf_webvtt_merge_cues(GF_WebVTTParser *parser, u64 start, GF_List *cues) { GF_WebVTTSample *wsample; GF_WebVTTSample *prev_wsample; Bool has_continuation_cue = GF_FALSE; assert(gf_list_count(parser->samples) <= 1); wsample = gf_webvtt_sample_new(); wsample->start = start; prev_wsample = (GF_WebVTTSample *)gf_list_last(parser->samples); while (gf_list_count(cues)) { GF_WebVTTCue *cue = (GF_WebVTTCue *)gf_list_get(cues, 0); gf_list_rem(cues, 0); /* add the cue to the current sample */ gf_list_add(wsample->cues, cue); /* update with the previous sample */ if (prev_wsample) { Bool found = GF_FALSE; while (!found && gf_list_count(prev_wsample->cues)) { GF_WebVTTCue *old_cue = (GF_WebVTTCue *)gf_list_get(prev_wsample->cues, 0); gf_list_rem(prev_wsample->cues, 0); if ( ((!cue->id && !old_cue->id) || (old_cue->id && cue->id && !strcmp(old_cue->id, cue->id))) && ((!cue->settings && !old_cue->settings) || (old_cue->settings && cue->settings && !strcmp(old_cue->settings, cue->settings))) && ((!cue->text && !old_cue->text) || (old_cue->text && cue->text && !strcmp(old_cue->text, cue->text))) ) { /* if it is the same cue, update its start with the initial start */ cue->start = old_cue->start; has_continuation_cue = GF_TRUE; found = GF_TRUE; if (old_cue->pre_text) { cue->pre_text = old_cue->pre_text; old_cue->pre_text = NULL; } if (old_cue->post_text) { cue->post_text = old_cue->post_text; old_cue->post_text = NULL; } /* delete the old cue */ gf_webvtt_cue_del(old_cue); } else { /* finalize the end cue time */ if (gf_webvtt_timestamp_is_zero(&old_cue->end)) { gf_webvtt_timestamp_set(&old_cue->end, wsample->start); } /* transfer the cue */ if (!has_continuation_cue) { /* the cue can be safely serialized while keeping the order */ parser->on_cue_read(parser->user, old_cue); } else { /* keep the cue in the current sample to respect cue start ordering */ gf_list_add(wsample->cues, old_cue); } } } } } /* No cue in the current sample */ if (prev_wsample) { while (gf_list_count(prev_wsample->cues)) { GF_WebVTTCue *cue = (GF_WebVTTCue *)gf_list_get(prev_wsample->cues, 0); gf_list_rem(prev_wsample->cues, 0); /* finalize the end cue time */ if (gf_webvtt_timestamp_is_zero(&cue->end)) { gf_webvtt_timestamp_set(&cue->end, wsample->start); } /* transfer the cue */ if (!has_continuation_cue) { /* the cue can be safely serialized while keeping the order */ parser->on_cue_read(parser->user, cue); } else { /* keep the cue in the current sample to respect cue start ordering */ gf_list_add(wsample->cues, cue); } } gf_webvtt_sample_del(prev_wsample); gf_list_rem_last(parser->samples); prev_wsample = NULL; } else { /* nothing to do */ } if (gf_list_count(wsample->cues)) { gf_list_add(parser->samples, wsample); } else { gf_webvtt_sample_del(wsample); } return GF_OK; }