static GF_Err gf_text_import_ttxt()

in src/media_tools/text_import.c [1785:2123]


static GF_Err gf_text_import_ttxt(GF_MediaImporter *import)
{
	GF_Err e;
	Bool last_sample_empty;
	u32 i, j, k, track, ID, nb_samples, nb_descs, nb_children;
	u64 last_sample_duration;
	GF_XMLAttribute *att;
	GF_DOMParser *parser;
	GF_XMLNode *root, *node, *ext;

	if (import->flags==GF_IMPORT_PROBE_ONLY) return GF_OK;

	parser = gf_xml_dom_new();
	e = gf_xml_dom_parse(parser, import->in_name, ttxt_import_progress, import);
	if (e) {
		gf_import_message(import, e, "Error parsing TTXT 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);

	e = GF_OK;
	if (strcmp(root->name, "TextStream")) {
		e = gf_import_message(import, GF_BAD_PARAM, "Invalid Timed Text file - expecting \"TextStream\" got %s", "TextStream", root->name);
		goto exit;
	}

	/*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, 1000);
	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 = 1000;
		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);
	}
	gf_text_import_set_language(import, track);

	gf_import_message(import, GF_OK, "Timed Text (GPAC TTXT) Import");

	last_sample_empty = GF_FALSE;
	last_sample_duration = 0;
	nb_descs = 0;
	nb_samples = 0;
	nb_children = gf_list_count(root->content);

	i=0;
	while ( (node = (GF_XMLNode*)gf_list_enum(root->content, &i))) {
		if (node->type) {
			nb_children--;
			continue;
		}

		if (!strcmp(node->name, "TextStreamHeader")) {
			GF_XMLNode *sdesc;
			s32 w, h, tx, ty, layer;
			u32 tref_id;
			w = TTXT_DEFAULT_WIDTH;
			h = TTXT_DEFAULT_HEIGHT;
			tx = ty = layer = 0;
			nb_children--;
			tref_id = 0;

			j=0;
			while ( (att=(GF_XMLAttribute *)gf_list_enum(node->attributes, &j))) {
				if (!strcmp(att->name, "width")) w = atoi(att->value);
				else if (!strcmp(att->name, "height")) h = atoi(att->value);
				else if (!strcmp(att->name, "layer")) layer = atoi(att->value);
				else if (!strcmp(att->name, "translation_x")) tx = atoi(att->value);
				else if (!strcmp(att->name, "translation_y")) ty = atoi(att->value);
				else if (!strcmp(att->name, "trefID")) tref_id = atoi(att->value);
			}

			if (tref_id)
				gf_isom_set_track_reference(import->dest, track, GF_ISOM_BOX_TYPE_CHAP, tref_id);

			gf_isom_set_track_layout_info(import->dest, track, w<<16, h<<16, tx<<16, ty<<16, (s16) layer);

			j=0;
			while ( (sdesc=(GF_XMLNode*)gf_list_enum(node->content, &j))) {
				if (sdesc->type) continue;

				if (!strcmp(sdesc->name, "TextSampleDescription")) {
					GF_TextSampleDescriptor td;
					u32 idx;
					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(sdesc->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, "backColor")) td.back_color = ttxt_get_color(import, att->value);
						else if (!strcmp(att->name, "verticalText") && !stricmp(att->value, "yes") ) td.displayFlags |= GF_TXT_VERTICAL;
						else if (!strcmp(att->name, "fillTextRegion") && !stricmp(att->value, "yes") ) td.displayFlags |= GF_TXT_FILL_REGION;
						else if (!strcmp(att->name, "continuousKaraoke") && !stricmp(att->value, "yes") ) td.displayFlags |= GF_TXT_KARAOKE;
						else if (!strcmp(att->name, "scroll")) {
							if (!stricmp(att->value, "inout")) td.displayFlags |= GF_TXT_SCROLL_IN | GF_TXT_SCROLL_OUT;
							else if (!stricmp(att->value, "in")) td.displayFlags |= GF_TXT_SCROLL_IN;
							else if (!stricmp(att->value, "out")) td.displayFlags |= GF_TXT_SCROLL_OUT;
						}
						else if (!strcmp(att->name, "scrollMode")) {
							u32 scroll_mode = GF_TXT_SCROLL_CREDITS;
							if (!stricmp(att->value, "Credits")) scroll_mode = GF_TXT_SCROLL_CREDITS;
							else if (!stricmp(att->value, "Marquee")) scroll_mode = GF_TXT_SCROLL_MARQUEE;
							else if (!stricmp(att->value, "Right")) scroll_mode = GF_TXT_SCROLL_RIGHT;
							else if (!stricmp(att->value, "Down")) scroll_mode = GF_TXT_SCROLL_DOWN;
							td.displayFlags |= ((scroll_mode<<7) & GF_TXT_SCROLL_DIRECTION);
						}
					}

					k=0;
					while ( (ext=(GF_XMLNode*)gf_list_enum(sdesc->content, &k))) {
						if (ext->type) continue;
						if (!strcmp(ext->name, "TextBox")) ttxt_parse_text_box(import, ext, &td.default_pos);
						else if (!strcmp(ext->name, "Style")) ttxt_parse_text_style(import, ext, &td.default_style);
						else if (!strcmp(ext->name, "FontTable")) {
							GF_XMLNode *ftable;
							u32 z=0;
							while ( (ftable=(GF_XMLNode*)gf_list_enum(ext->content, &z))) {
								u32 m;
								if (ftable->type || strcmp(ftable->name, "FontTableEntry")) continue;
								td.font_count += 1;
								td.fonts = (GF_FontRecord*)gf_realloc(td.fonts, sizeof(GF_FontRecord)*td.font_count);
								m=0;
								while ( (att=(GF_XMLAttribute *)gf_list_enum(ftable->attributes, &m))) {
									if (!stricmp(att->name, "fontID")) td.fonts[td.font_count-1].fontID = atoi(att->value);
									else if (!stricmp(att->name, "fontName")) td.fonts[td.font_count-1].fontName = gf_strdup(att->value);
								}
							}
						}
					}
					if (import->flags & GF_IMPORT_SKIP_TXT_BOX) {
						td.default_pos.top = td.default_pos.left = td.default_pos.right = td.default_pos.bottom = 0;
					} else {
						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_new_text_description(import->dest, track, &td, NULL, NULL, &idx);
					for (k=0; k<td.font_count; k++) gf_free(td.fonts[k].fontName);
					gf_free(td.fonts);
					nb_descs ++;
				}
			}
		}
		/*sample text*/
		else if (!strcmp(node->name, "TextSample")) {
			GF_ISOSample *s;
			GF_TextSample * samp;
			u32 ts, descIndex;
			Bool has_text = GF_FALSE;
			if (!nb_descs) {
				e = gf_import_message(import, GF_BAD_PARAM, "Invalid Timed Text file - text stream header not found or empty");
				goto exit;
			}
			samp = gf_isom_new_text_sample();
			ts = 0;
			descIndex = 1;
			last_sample_empty = GF_TRUE;

			j=0;
			while ( (att=(GF_XMLAttribute*)gf_list_enum(node->attributes, &j))) {
				if (!strcmp(att->name, "sampleTime")) {
					u32 h, m, s, ms;
					if (sscanf(att->value, "%u:%u:%u.%u", &h, &m, &s, &ms) == 4) {
						ts = (h*3600 + m*60 + s)*1000 + ms;
					} else {
						ts = (u32) (atof(att->value) * 1000);
					}
				}
				else if (!strcmp(att->name, "sampleDescriptionIndex")) descIndex = atoi(att->value);
				else if (!strcmp(att->name, "text")) {
					u32 len;
					char *str = ttxt_parse_string(import, att->value, GF_TRUE);
					len = (u32) strlen(str);
					gf_isom_text_add_text(samp, str, len);
					last_sample_empty = len ? GF_FALSE : GF_TRUE;
					has_text = GF_TRUE;
				}
				else if (!strcmp(att->name, "scrollDelay")) gf_isom_text_set_scroll_delay(samp, (u32) (1000*atoi(att->value)));
				else if (!strcmp(att->name, "highlightColor")) gf_isom_text_set_highlight_color_argb(samp, ttxt_get_color(import, att->value));
				else if (!strcmp(att->name, "wrap") && !strcmp(att->value, "Automatic")) gf_isom_text_set_wrap(samp, 0x01);
			}

			/*get all modifiers*/
			j=0;
			while ( (ext=(GF_XMLNode*)gf_list_enum(node->content, &j))) {
				if (!has_text && (ext->type==GF_XML_TEXT_TYPE)) {
					u32 len;
					char *str = ttxt_parse_string(import, ext->name, GF_FALSE);
					len = (u32) strlen(str);
					gf_isom_text_add_text(samp, str, len);
					last_sample_empty = len ? GF_FALSE : GF_TRUE;
					has_text = GF_TRUE;
				}
				if (ext->type) continue;

				if (!stricmp(ext->name, "Style")) {
					GF_StyleRecord r;
					ttxt_parse_text_style(import, ext, &r);
					gf_isom_text_add_style(samp, &r);
				}
				else if (!stricmp(ext->name, "TextBox")) {
					GF_BoxRecord r;
					ttxt_parse_text_box(import, ext, &r);
					gf_isom_text_set_box(samp, r.top, r.left, r.bottom, r.right);
				}
				else if (!stricmp(ext->name, "Highlight")) {
					u16 start, end;
					start = end = 0;
					k=0;
					while ( (att=(GF_XMLAttribute *)gf_list_enum(ext->attributes, &k))) {
						if (!strcmp(att->name, "fromChar")) start = atoi(att->value);
						else if (!strcmp(att->name, "toChar")) end = atoi(att->value);
					}
					gf_isom_text_add_highlight(samp, start, end);
				}
				else if (!stricmp(ext->name, "Blinking")) {
					u16 start, end;
					start = end = 0;
					k=0;
					while ( (att=(GF_XMLAttribute *)gf_list_enum(ext->attributes, &k))) {
						if (!strcmp(att->name, "fromChar")) start = atoi(att->value);
						else if (!strcmp(att->name, "toChar")) end = atoi(att->value);
					}
					gf_isom_text_add_blink(samp, start, end);
				}
				else if (!stricmp(ext->name, "HyperLink")) {
					u16 start, end;
					char *url, *url_tt;
					start = end = 0;
					url = url_tt = NULL;
					k=0;
					while ( (att=(GF_XMLAttribute *)gf_list_enum(ext->attributes, &k))) {
						if (!strcmp(att->name, "fromChar")) start = atoi(att->value);
						else if (!strcmp(att->name, "toChar")) end = atoi(att->value);
						else if (!strcmp(att->name, "URL")) url = gf_strdup(att->value);
						else if (!strcmp(att->name, "URLToolTip")) 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(ext->name, "Karaoke")) {
					u32 startTime;
					GF_XMLNode *krok;
					startTime = 0;
					k=0;
					while ( (att=(GF_XMLAttribute *)gf_list_enum(ext->attributes, &k))) {
						if (!strcmp(att->name, "startTime")) startTime = (u32) (1000*atof(att->value));
					}
					gf_isom_text_add_karaoke(samp, startTime);
					k=0;
					while ( (krok=(GF_XMLNode*)gf_list_enum(ext->content, &k))) {
						u16 start, end;
						u32 endTime, m;
						if (krok->type) continue;
						if (strcmp(krok->name, "KaraokeRange")) continue;
						start = end = 0;
						endTime = 0;
						m=0;
						while ( (att=(GF_XMLAttribute *)gf_list_enum(krok->attributes, &m))) {
							if (!strcmp(att->name, "fromChar")) start = atoi(att->value);
							else if (!strcmp(att->name, "toChar")) end = atoi(att->value);
							else if (!strcmp(att->name, "endTime")) endTime = (u32) (1000*atof(att->value));
						}
						gf_isom_text_set_karaoke_segment(samp, endTime, start, end);
					}
				}
			}

			/*in MP4 we must start at T=0, so add an empty sample*/
			if (ts && !nb_samples) {
				GF_TextSample * firstsamp = gf_isom_new_text_sample();
				s = gf_isom_text_to_sample(firstsamp);
				s->DTS = 0;
				gf_isom_add_sample(import->dest, track, 1, s);
				nb_samples++;
				gf_isom_delete_text_sample(firstsamp);
				gf_isom_sample_del(&s);
			}

			s = gf_isom_text_to_sample(samp);
			gf_isom_delete_text_sample(samp);
			s->DTS = ts;
			if (last_sample_empty) {
				last_sample_duration = s->DTS - last_sample_duration;
			} else {
				last_sample_duration = s->DTS;
			}

			e = gf_isom_add_sample(import->dest, track, descIndex, s);
			if (e) goto exit;
			gf_isom_sample_del(&s);
			nb_samples++;

			gf_set_progress("Importing TTXT", nb_samples, nb_children);
			if (import->duration && (ts>import->duration)) break;
		}
	}
	if (last_sample_empty) {
		gf_isom_remove_sample(import->dest, track, nb_samples);
		gf_isom_set_last_sample_duration(import->dest, track, (u32) last_sample_duration);
	}
	gf_set_progress("Importing TTXT", nb_samples, nb_samples);

exit:
	gf_xml_dom_del(parser);
	return e;
}