in src/media_tools/text_import.c [2180:2548]
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 = GPAC_OTI_TEXT_MPEG4;
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 = GF_FALSE;
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") ? GF_TRUE : GF_FALSE);
}
nb_styles = 0;
nb_marks = 0;
same_style = same_box = GF_FALSE;
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 = GF_FALSE;
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 = GF_TRUE;
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*)gf_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 = gf_strdup(att->value);
}
}
}
}
else if (!strcmp(sub->name, "sharedStyles")) {
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)
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*)gf_malloc(sizeof(GF_FontRecord));
td.fonts[0].fontID = 1;
td.fonts[0].fontName = gf_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 = GF_TRUE;
}
for (k=0; k<td.font_count; k++) gf_free(td.fonts[k].fontName);
gf_free(td.fonts);
nb_descs ++;
}
else if (!strcmp(desc->name, "sampleData")) {
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 += (u32) strlen(text->name);
gf_isom_text_add_text(samp, text->name, (u32) strlen(text->name));
}
}
if (styleID && (!same_style || (td.default_style.startCharOffset != styleID))) {
GF_StyleRecord st = td.default_style;
for (m=0; m<nb_styles; m++) {
if (styles[m].startCharOffset==styleID) {
st = styles[m];
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 = gf_strdup(att->value);
else if (!strcmp(att->name, "URLToolTip") || !strcmp(att->name, "altString")) url_tt = gf_strdup(att->value);
}
gf_isom_text_add_hyperlink(samp, url, url_tt, start, end);
if (url) gf_free(url);
if (url_tt) gf_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 ? RAP : RAP_NO;
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;
}