📄 text_import.c
字号:
res |= (v&0xFF); return res;}void tx3g_parse_text_box(GF_MediaImporter *import, GF_XMLNode *n, GF_BoxRecord *box){ u32 i=0; GF_XMLAttribute *att; memset(box, 0, sizeof(GF_BoxRecord)); while ((att=(GF_XMLAttribute *)gf_list_enum(n->attributes, &i))) { if (!stricmp(att->name, "x")) box->left = atoi(att->value); else if (!stricmp(att->name, "y")) box->top = atoi(att->value); else if (!stricmp(att->name, "height")) box->bottom = atoi(att->value); else if (!stricmp(att->name, "width")) box->right = atoi(att->value); }}typedef struct{ u32 id; u32 pos;} Marker;#define GET_MARKER_POS(_val, __isend) \ { \ u32 i, __m = atoi(att->value); \ _val = 0; \ for (i=0; i<nb_marks; i++) { if (__m==marks[i].id) { _val = marks[i].pos; /*if (__isend) _val--; */break; } } \ } static void texml_import_progress(void *cbk, u32 cur_samp, u32 count){ gf_set_progress("TeXML Loading", cur_samp, count);}static GF_Err gf_text_import_texml(GF_MediaImporter *import){ GF_Err e; u32 track, ID, nb_samples, nb_children, nb_descs, timescale, w, h, i, j, k; u64 DTS; s32 tx, ty, layer; GF_StyleRecord styles[50]; Marker marks[50]; GF_XMLAttribute *att; GF_DOMParser *parser; GF_XMLNode *root, *node; if (import->flags==GF_IMPORT_PROBE_ONLY) return GF_OK; parser = gf_xml_dom_new(); e = gf_xml_dom_parse(parser, import->in_name, texml_import_progress, import); if (e) { gf_import_message(import, e, "Error parsing TeXML file: Line %d - %s", gf_xml_dom_get_line(parser), gf_xml_dom_get_error(parser)); gf_xml_dom_del(parser); return e; } root = gf_xml_dom_get_root(parser); if (strcmp(root->name, "text3GTrack")) { e = gf_import_message(import, GF_BAD_PARAM, "Invalid QT TeXML file - expecting root \"text3GTrack\" got \"%s\"", root->name); goto exit; } w = TTXT_DEFAULT_WIDTH; h = TTXT_DEFAULT_HEIGHT; tx = ty = 0; layer = 0; timescale = 1000; i=0; while ( (att=(GF_XMLAttribute *)gf_list_enum(root->attributes, &i))) { if (!strcmp(att->name, "trackWidth")) w = atoi(att->value); else if (!strcmp(att->name, "trackHeight")) h = atoi(att->value); else if (!strcmp(att->name, "layer")) layer = atoi(att->value); else if (!strcmp(att->name, "timescale")) timescale = atoi(att->value); else if (!strcmp(att->name, "transform")) { Float fx, fy; sscanf(att->value, "translate(%f,%f)", &fx, &fy); tx = (u32) fx; ty = (u32) fy; } } /*setup track in 3GP format directly (no ES desc)*/ ID = (import->esd) ? import->esd->ESID : 0; track = gf_isom_new_track(import->dest, ID, GF_ISOM_MEDIA_TEXT, timescale); if (!track) { e = gf_isom_last_error(import->dest); goto exit; } gf_isom_set_track_enabled(import->dest, track, 1); /*some MPEG-4 setup*/ if (import->esd) { if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); import->esd->slConfig->timestampResolution = timescale; import->esd->decoderConfig->streamType = GF_STREAM_TEXT; import->esd->decoderConfig->objectTypeIndication = 0x08; if (import->esd->OCRESID) gf_isom_set_track_reference(import->dest, track, GF_ISOM_REF_OCR, import->esd->OCRESID); } DTS = 0; gf_isom_set_track_layout_info(import->dest, track, w<<16, h<<16, tx<<16, ty<<16, (s16) layer); gf_text_import_set_language(import, track); e = GF_OK; gf_import_message(import, GF_OK, "Timed Text (QT TeXML) Import - Track Size %d x %d", w, h); nb_children = gf_list_count(root->content); nb_descs = 0; nb_samples = 0; i=0; while ( (node=(GF_XMLNode*)gf_list_enum(root->content, &i))) { GF_XMLNode *desc; GF_TextSampleDescriptor td; GF_TextSample * samp = NULL; GF_ISOSample *s; u32 duration, descIndex, nb_styles, nb_marks; Bool isRAP, same_style, same_box; if (node->type) continue; if (strcmp(node->name, "sample")) continue; isRAP = 0; duration = 1000; j=0; while ((att=(GF_XMLAttribute *)gf_list_enum(node->attributes, &j))) { if (!strcmp(att->name, "duration")) duration = atoi(att->value); else if (!strcmp(att->name, "keyframe")) isRAP = !stricmp(att->value, "true"); } nb_styles = 0; nb_marks = 0; same_style = same_box = 0; descIndex = 1; j=0; while ((desc=(GF_XMLNode*)gf_list_enum(node->content, &j))) { if (desc->type) continue; if (!strcmp(desc->name, "description")) { GF_XMLNode *sub; memset(&td, 0, sizeof(GF_TextSampleDescriptor)); td.tag = GF_ODF_TEXT_CFG_TAG; td.vert_justif = (s8) -1; td.default_style.fontID = 1; td.default_style.font_size = TTXT_DEFAULT_FONT_SIZE; k=0; while ((att=(GF_XMLAttribute *)gf_list_enum(desc->attributes, &k))) { if (!strcmp(att->name, "horizontalJustification")) { if (!stricmp(att->value, "center")) td.horiz_justif = 1; else if (!stricmp(att->value, "right")) td.horiz_justif = (s8) -1; else if (!stricmp(att->value, "left")) td.horiz_justif = 0; } else if (!strcmp(att->name, "verticalJustification")) { if (!stricmp(att->value, "center")) td.vert_justif = 1; else if (!stricmp(att->value, "bottom")) td.vert_justif = (s8) -1; else if (!stricmp(att->value, "top")) td.vert_justif = 0; } else if (!strcmp(att->name, "backgroundColor")) td.back_color = tx3g_get_color(import, att->value); else if (!strcmp(att->name, "displayFlags")) { Bool rev_scroll = 0; if (strstr(att->value, "scroll")) { u32 scroll_mode = 0; if (strstr(att->value, "scrollIn")) td.displayFlags |= GF_TXT_SCROLL_IN; if (strstr(att->value, "scrollOut")) td.displayFlags |= GF_TXT_SCROLL_OUT; if (strstr(att->value, "reverse")) rev_scroll = 1; if (strstr(att->value, "horizontal")) scroll_mode = rev_scroll ? GF_TXT_SCROLL_RIGHT : GF_TXT_SCROLL_MARQUEE; else scroll_mode = (rev_scroll ? GF_TXT_SCROLL_DOWN : GF_TXT_SCROLL_CREDITS); td.displayFlags |= (scroll_mode<<7) & GF_TXT_SCROLL_DIRECTION; } /*TODO FIXME: check in QT doc !!*/ if (strstr(att->value, "writeTextVertically")) td.displayFlags |= GF_TXT_VERTICAL; if (!strcmp(att->name, "continuousKaraoke")) td.displayFlags |= GF_TXT_KARAOKE; } } k=0; while ((sub=(GF_XMLNode*)gf_list_enum(desc->content, &k))) { if (sub->type) continue; if (!strcmp(sub->name, "defaultTextBox")) tx3g_parse_text_box(import, sub, &td.default_pos); else if (!strcmp(sub->name, "fontTable")) { GF_XMLNode *ftable; u32 m=0; while ((ftable=(GF_XMLNode*)gf_list_enum(sub->content, &m))) { if (ftable->type) continue; if (!strcmp(ftable->name, "font")) { u32 n=0; td.font_count += 1; td.fonts = (GF_FontRecord*)realloc(td.fonts, sizeof(GF_FontRecord)*td.font_count); while ((att=(GF_XMLAttribute *)gf_list_enum(ftable->attributes, &n))) { if (!stricmp(att->name, "id")) td.fonts[td.font_count-1].fontID = atoi(att->value); else if (!stricmp(att->name, "name")) td.fonts[td.font_count-1].fontName = strdup(att->value); } } } } else if (!strcmp(sub->name, "sharedStyles")) { u32 idx = 0; GF_XMLNode *style, *ftable; u32 m=0; while ((style=(GF_XMLNode*)gf_list_enum(sub->content, &m))) { if (style->type) continue; if (!strcmp(style->name, "style")) break; } if (style) { char *cur; s32 start=0; char css_style[1024], css_val[1024]; memset(&styles[nb_styles], 0, sizeof(GF_StyleRecord)); m=0; while ( (att=(GF_XMLAttribute *)gf_list_enum(style->attributes, &m))) { if (!strcmp(att->name, "id")) styles[nb_styles].startCharOffset = atoi(att->value); } m=0; while ( (ftable=(GF_XMLNode*)gf_list_enum(style->content, &m))) { if (ftable->type) break; } cur = ftable->name; while (cur) { start = gf_token_get_strip(cur, 0, "{:", " ", css_style, 1024); if (start <0) break; start = gf_token_get_strip(cur, start, ":}", " ", css_val, 1024); if (start <0) break; cur = strchr(cur+start, '{'); if (!strcmp(css_style, "font-table")) { u32 z; styles[nb_styles].fontID = atoi(css_val); for (z=0; z<td.font_count; z++) { if (td.fonts[z].fontID == styles[nb_styles].fontID) { idx = z; break; } } } else if (!strcmp(css_style, "font-size")) styles[nb_styles].font_size = atoi(css_val); else if (!strcmp(css_style, "font-style") && !strcmp(css_val, "italic")) styles[nb_styles].style_flags |= GF_TXT_STYLE_ITALIC; else if (!strcmp(css_style, "font-weight") && !strcmp(css_val, "bold")) styles[nb_styles].style_flags |= GF_TXT_STYLE_BOLD; else if (!strcmp(css_style, "text-decoration") && !strcmp(css_val, "underline")) styles[nb_styles].style_flags |= GF_TXT_STYLE_UNDERLINED; else if (!strcmp(css_style, "color")) styles[nb_styles].text_color = tx3g_get_color(import, css_val); } if (!nb_styles) td.default_style = styles[0]; nb_styles++; } } } if ((td.default_pos.bottom==td.default_pos.top) || (td.default_pos.right==td.default_pos.left)) { td.default_pos.top = td.default_pos.left = 0; td.default_pos.right = w; td.default_pos.bottom = h; } if (!td.fonts) { td.font_count = 1; td.fonts = (GF_FontRecord*)malloc(sizeof(GF_FontRecord)); td.fonts[0].fontID = 1; td.fonts[0].fontName = strdup("Serif"); } gf_isom_text_has_similar_description(import->dest, track, &td, &descIndex, &same_box, &same_style); if (!descIndex) { gf_isom_new_text_description(import->dest, track, &td, NULL, NULL, &descIndex); same_style = same_box = 1; } for (k=0; k<td.font_count; k++) free(td.fonts[k].fontName); free(td.fonts); nb_descs ++; } else if (!strcmp(desc->name, "sampleData")) { Bool is_utf16 = 0; GF_XMLNode *sub; u16 start, end; u32 styleID; u32 nb_chars, txt_len, m; txt_len = nb_chars = 0; samp = gf_isom_new_text_sample(); k=0; while ((att=(GF_XMLAttribute *)gf_list_enum(desc->attributes, &k))) { if (!strcmp(att->name, "targetEncoding") && !strcmp(att->value, "utf16")) is_utf16 = 1; else if (!strcmp(att->name, "scrollDelay")) gf_isom_text_set_scroll_delay(samp, atoi(att->value) ); else if (!strcmp(att->name, "highlightColor")) gf_isom_text_set_highlight_color_argb(samp, tx3g_get_color(import, att->value)); } start = end = 0; k=0; while ((sub=(GF_XMLNode*)gf_list_enum(desc->content, &k))) { if (sub->type) continue; if (!strcmp(sub->name, "text")) { GF_XMLNode *text; styleID = 0; m=0; while ((att=(GF_XMLAttribute *)gf_list_enum(sub->attributes, &m))) { if (!strcmp(att->name, "styleID")) styleID = atoi(att->value); } txt_len = 0; m=0; while ((text=(GF_XMLNode*)gf_list_enum(sub->content, &m))) { if (!text->type) { if (!strcmp(text->name, "marker")) { u32 z; memset(&marks[nb_marks], 0, sizeof(Marker)); marks[nb_marks].pos = nb_chars+txt_len; z = 0; while ( (att=(GF_XMLAttribute *)gf_list_enum(text->attributes, &z))) { if (!strcmp(att->name, "id")) marks[nb_marks].id = atoi(att->value); } nb_marks++; } } else if (text->type==GF_XML_TEXT_TYPE) { txt_len += strlen(text->name); gf_isom_text_add_text(samp, text->name, strlen(text->name)); } } if (styleID && (!same_style || (td.default_style.startCharOffset != styleID))) { GF_StyleRecord st = td.default_style; for (i=0; i<nb_styles; i++) { if (styles[i].startCharOffset==styleID) { st = styles[i]; break; } } st.startCharOffset = nb_chars; st.endCharOffset = nb_chars + txt_len; gf_isom_text_add_style(samp, &st); } nb_chars += txt_len; } else if (!stricmp(sub->name, "highlight")) { m=0; while ((att=(GF_XMLAttribute *)gf_list_enum(sub->attributes, &m))) { if (!strcmp(att->name, "startMarker")) GET_MARKER_POS(start, 0) else if (!strcmp(att->name, "endMarker")) GET_MARKER_POS(end, 1) } gf_isom_text_add_highlight(samp, start, end); } else if (!stricmp(sub->name, "blink")) { m=0; while ((att=(GF_XMLAttribute *)gf_list_enum(sub->attributes, &m))) { if (!strcmp(att->name, "startMarker")) GET_MARKER_POS(start, 0) else if (!strcmp(att->name, "endMarker")) GET_MARKER_POS(end, 1) } gf_isom_text_add_blink(samp, start, end); } else if (!stricmp(sub->name, "link")) { char *url, *url_tt; url = url_tt = NULL; m=0; while ((att=(GF_XMLAttribute *)gf_list_enum(sub->attributes, &m))) { if (!strcmp(att->name, "startMarker")) GET_MARKER_POS(start, 0) else if (!strcmp(att->name, "endMarker")) GET_MARKER_POS(end, 1) else if (!strcmp(att->name, "URL") || !strcmp(att->name, "href")) url = strdup(att->value); else if (!strcmp(att->name, "URLToolTip") || !strcmp(att->name, "altString")) url_tt = strdup(att->value); } gf_isom_text_add_hyperlink(samp, url, url_tt, start, end); if (url) free(url); if (url_tt) free(url_tt); } else if (!stricmp(sub->name, "karaoke")) { u32 time = 0; GF_XMLNode *krok; m=0; while ((att=(GF_XMLAttribute *)gf_list_enum(sub->attributes, &m))) { if (!strcmp(att->name, "startTime")) time = atoi(att->value); } gf_isom_text_add_karaoke(samp, time); m=0; while ((krok=(GF_XMLNode*)gf_list_enum(sub->content, &m))) { u32 u=0; if (krok->type) continue; if (strcmp(krok->name, "run")) continue; start = end = 0; while ((att=(GF_XMLAttribute *)gf_list_enum(krok->attributes, &u))) { if (!strcmp(att->name, "startMarker")) GET_MARKER_POS(start, 0) else if (!strcmp(att->name, "endMarker")) GET_MARKER_POS(end, 1) else if (!strcmp(att->name, "duration")) time += atoi(att->value); } gf_isom_text_set_karaoke_segment(samp, time, start, end); } } } } } /*OK, let's add the sample*/ if (samp) { if (!same_box) gf_isom_text_set_box(samp, td.default_pos.top, td.default_pos.left, td.default_pos.bottom, td.default_pos.right);// if (!same_style) gf_isom_text_add_style(samp, &td.default_style); s = gf_isom_text_to_sample(samp); gf_isom_delete_text_sample(samp); s->IsRAP = isRAP; s->DTS = DTS; gf_isom_add_sample(import->dest, track, descIndex, s); gf_isom_sample_del(&s); nb_samples++; DTS += duration; gf_set_progress("Importing TeXML", nb_samples, nb_children); if (import->duration && (DTS*1000> timescale*import->duration)) break; } } gf_isom_set_last_sample_duration(import->dest, track, 0); gf_set_progress("Importing TeXML", nb_samples, nb_samples);exit: gf_xml_dom_del(parser); return e;}GF_Err gf_import_timed_text(GF_MediaImporter *import){ GF_Err e; u32 fmt; e = gf_text_guess_format(import->in_name, &fmt); if (e) return e; if (!fmt) return GF_NOT_SUPPORTED; if (import->flags & GF_IMPORT_PROBE_ONLY) { if (fmt==GF_TEXT_IMPORT_SUB) import->flags |= GF_IMPORT_OVERRIDE_FPS; return GF_OK; } switch (fmt) { case GF_TEXT_IMPORT_SRT: return gf_text_import_srt(import); case GF_TEXT_IMPORT_SUB: return gf_text_import_sub(import); case GF_TEXT_IMPORT_TTXT: return gf_text_import_ttxt(import); case GF_TEXT_IMPORT_TEXML: return gf_text_import_texml(import); default: return GF_BAD_PARAM; }}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -