static GF_Err gf_text_import_srt()

in src/media_tools/text_import.c [272:714]


static GF_Err gf_text_import_srt(GF_MediaImporter *import)
{
	FILE *srt_in;
	u32 track, timescale, i, count;
	GF_TextConfig*cfg;
	GF_Err e;
	GF_StyleRecord rec;
	GF_TextSample * samp;
	GF_ISOSample *s;
	u32 sh, sm, ss, sms, eh, em, es, ems, txt_line, char_len, char_line, nb_samp, j, duration, rem_styles;
	Bool set_start_char, set_end_char, first_samp, rem_color;
	u64 start, end, prev_end, file_size;
	u32 state, curLine, line, len, ID, OCR_ES_ID, default_color;
	s32 unicode_type;
	char szLine[2048], szText[2048], *ptr;
	unsigned short uniLine[5000], uniText[5000], *sptr;

	srt_in = gf_fopen(import->in_name, "rt");
	gf_fseek(srt_in, 0, SEEK_END);
	file_size = gf_ftell(srt_in);
	gf_fseek(srt_in, 0, SEEK_SET);

	unicode_type = gf_text_get_utf_type(srt_in);
	if (unicode_type<0) {
		gf_fclose(srt_in);
		return gf_import_message(import, GF_NOT_SUPPORTED, "Unsupported SRT UTF encoding");
	}

	cfg = NULL;
	if (import->esd) {
		if (!import->esd->slConfig) {
			import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG);
			import->esd->slConfig->predefined = 2;
			import->esd->slConfig->timestampResolution = 1000;
		}
		timescale = import->esd->slConfig->timestampResolution;
		if (!timescale) timescale = 1000;

		/*explicit text config*/
		if (import->esd->decoderConfig && import->esd->decoderConfig->decoderSpecificInfo->tag == GF_ODF_TEXT_CFG_TAG) {
			cfg = (GF_TextConfig *) import->esd->decoderConfig->decoderSpecificInfo;
			import->esd->decoderConfig->decoderSpecificInfo = NULL;
		}
		ID = import->esd->ESID;
		OCR_ES_ID = import->esd->OCRESID;
	} else {
		timescale = 1000;
		OCR_ES_ID = ID = 0;
	}

	if (cfg && cfg->timescale) timescale = cfg->timescale;
	track = gf_isom_new_track(import->dest, ID, GF_ISOM_MEDIA_TEXT, timescale);
	if (!track) {
		gf_fclose(srt_in);
		return gf_import_message(import, gf_isom_last_error(import->dest), "Error creating text track");
	}
	gf_isom_set_track_enabled(import->dest, track, 1);
	import->final_trackID = gf_isom_get_track_id(import->dest, track);
	if (import->esd && !import->esd->ESID) import->esd->ESID = import->final_trackID;

	if (OCR_ES_ID) gf_isom_set_track_reference(import->dest, track, GF_ISOM_REF_OCR, OCR_ES_ID);

	/*setup track*/
	if (cfg) {
		char *firstFont = NULL;
		/*set track info*/
		gf_isom_set_track_layout_info(import->dest, track, cfg->text_width<<16, cfg->text_height<<16, 0, 0, cfg->layer);

		/*and set sample descriptions*/
		count = gf_list_count(cfg->sample_descriptions);
		for (i=0; i<count; i++) {
			GF_TextSampleDescriptor *sd= (GF_TextSampleDescriptor *)gf_list_get(cfg->sample_descriptions, i);
			if (!sd->font_count) {
				sd->fonts = (GF_FontRecord*)gf_malloc(sizeof(GF_FontRecord));
				sd->font_count = 1;
				sd->fonts[0].fontID = 1;
				sd->fonts[0].fontName = gf_strdup("Serif");
			}
			if (!sd->default_style.fontID) sd->default_style.fontID = sd->fonts[0].fontID;
			if (!sd->default_style.font_size) sd->default_style.font_size = 16;
			if (!sd->default_style.text_color) sd->default_style.text_color = 0xFF000000;
			/*store attribs*/
			if (!i) rec = sd->default_style;

			gf_isom_new_text_description(import->dest, track, sd, NULL, NULL, &state);
			if (!firstFont) firstFont = sd->fonts[0].fontName;
		}
		gf_import_message(import, GF_OK, "Timed Text (SRT) import - text track %d x %d, font %s (size %d)", cfg->text_width, cfg->text_height, firstFont, rec.font_size);

		gf_odf_desc_del((GF_Descriptor *)cfg);
	} else {
		u32 w, h;
		GF_TextSampleDescriptor *sd;
		gf_text_get_video_size(import, &w, &h);

		/*have to work with default - use max size (if only one video, this means the text region is the
		entire display, and with bottom alignment things should be fine...*/
		gf_isom_set_track_layout_info(import->dest, track, w<<16, h<<16, 0, 0, 0);
		sd = (GF_TextSampleDescriptor*)gf_odf_desc_new(GF_ODF_TX3G_TAG);
		sd->fonts = (GF_FontRecord*)gf_malloc(sizeof(GF_FontRecord));
		sd->font_count = 1;
		sd->fonts[0].fontID = 1;
		sd->fonts[0].fontName = gf_strdup(import->fontName ? import->fontName : "Serif");
		sd->back_color = 0x00000000;	/*transparent*/
		sd->default_style.fontID = 1;
		sd->default_style.font_size = import->fontSize ? import->fontSize : TTXT_DEFAULT_FONT_SIZE;
		sd->default_style.text_color = 0xFFFFFFFF;	/*white*/
		sd->default_style.style_flags = 0;
		sd->horiz_justif = 1; /*center of scene*/
		sd->vert_justif = (s8) -1;	/*bottom of scene*/

		if (import->flags & GF_IMPORT_SKIP_TXT_BOX) {
			sd->default_pos.top = sd->default_pos.left = sd->default_pos.right = sd->default_pos.bottom = 0;
		} else {
			if ((sd->default_pos.bottom==sd->default_pos.top) || (sd->default_pos.right==sd->default_pos.left)) {
				sd->default_pos.left = import->text_x;
				sd->default_pos.top = import->text_y;
				sd->default_pos.right = (import->text_width ? import->text_width : w) + sd->default_pos.left;
				sd->default_pos.bottom = (import->text_height ? import->text_height : h) + sd->default_pos.top;
			}
		}

		/*store attribs*/
		rec = sd->default_style;
		gf_isom_new_text_description(import->dest, track, sd, NULL, NULL, &state);

		gf_import_message(import, GF_OK, "Timed Text (SRT) import - text track %d x %d, font %s (size %d)", w, h, sd->fonts[0].fontName, rec.font_size);
		gf_odf_desc_del((GF_Descriptor *)sd);
	}
	gf_text_import_set_language(import, track);
	duration = (u32) (((Double) import->duration)*timescale/1000.0);

	default_color = rec.text_color;

	e = GF_OK;
	state = 0;
	end = prev_end = 0;
	curLine = 0;
	txt_line = 0;
	set_start_char = set_end_char = GF_FALSE;
	char_len = 0;
	start = 0;
	nb_samp = 0;
	samp = gf_isom_new_text_sample();

	first_samp = GF_TRUE;
	while (1) {
		char *sOK = gf_text_get_utf8_line(szLine, 2048, srt_in, unicode_type);

		if (sOK) REM_TRAIL_MARKS(szLine, "\r\n\t ")
			if (!sOK || !strlen(szLine)) {
				rec.style_flags = 0;
				rec.startCharOffset = rec.endCharOffset = 0;
				if (txt_line) {
					if (prev_end && (start != prev_end)) {
						GF_TextSample * empty_samp = gf_isom_new_text_sample();
						s = gf_isom_text_to_sample(empty_samp);
						gf_isom_delete_text_sample(empty_samp);
						if (state<=2) {
							s->DTS = (u64) ((timescale*prev_end)/1000);
							s->IsRAP = RAP;
							gf_isom_add_sample(import->dest, track, 1, s);
							nb_samp++;
						}
						gf_isom_sample_del(&s);
					}

					s = gf_isom_text_to_sample(samp);
					if (state<=2) {
						s->DTS = (u64) ((timescale*start)/1000);
						s->IsRAP = RAP;
						gf_isom_add_sample(import->dest, track, 1, s);
						gf_isom_sample_del(&s);
						nb_samp++;
						prev_end = end;
					}
					txt_line = 0;
					char_len = 0;
					set_start_char = set_end_char = GF_FALSE;
					rec.startCharOffset = rec.endCharOffset = 0;
					gf_isom_text_reset(samp);

					//gf_import_progress(import, nb_samp, nb_samp+1);
					gf_set_progress("Importing SRT", gf_ftell(srt_in), file_size);
					if (duration && (end >= duration)) break;
				}
				state = 0;
				if (!sOK) break;
				continue;
			}

		switch (state) {
		case 0:
			if (sscanf(szLine, "%u", &line) != 1) {
				e = gf_import_message(import, GF_CORRUPTED_DATA, "Bad SRT formatting - expecting number got \"%s\"", szLine);
				goto exit;
			}
			if (line != curLine + 1) gf_import_message(import, GF_OK, "WARNING: corrupted SRT frame %d after frame %d", line, curLine);
			curLine = line;
			state = 1;
			break;
		case 1:
			if (sscanf(szLine, "%u:%u:%u,%u --> %u:%u:%u,%u", &sh, &sm, &ss, &sms, &eh, &em, &es, &ems) != 8) {
				sh = eh = 0;
				if (sscanf(szLine, "%u:%u,%u --> %u:%u,%u", &sm, &ss, &sms, &em, &es, &ems) != 6) {
					e = gf_import_message(import, GF_CORRUPTED_DATA, "Error scanning SRT frame %d timing", curLine);
					goto exit;
				}
			}
			start = (3600*sh + 60*sm + ss)*1000 + sms;
			if (start<end) {
				gf_import_message(import, GF_OK, "WARNING: overlapping SRT frame %d - starts "LLD" ms is before end of previous one "LLD" ms - adjusting time stamps", curLine, start, end);
				start = end;
			}

			end = (3600*eh + 60*em + es)*1000 + ems;
			/*make stream start at 0 by inserting a fake AU*/
			if (first_samp && (start>0)) {
				s = gf_isom_text_to_sample(samp);
				s->DTS = 0;
				gf_isom_add_sample(import->dest, track, 1, s);
				gf_isom_sample_del(&s);
				nb_samp++;
			}
			rec.style_flags = 0;
			state = 2;
			if (end<=prev_end) {
				gf_import_message(import, GF_OK, "WARNING: overlapping SRT frame %d end "LLD" is at or before previous end "LLD" - removing", curLine, end, prev_end);
				start = end;
				state = 3;
			}
			break;

		default:
			/*reset only when text is present*/
			first_samp = GF_FALSE;

			/*go to line*/
			if (txt_line) {
				gf_isom_text_add_text(samp, "\n", 1);
				char_len += 1;
			}

			ptr = (char *) szLine;
			{
				size_t _len = gf_utf8_mbstowcs(uniLine, 5000, (const char **) &ptr);
				if (_len == (size_t) -1) {
					e = gf_import_message(import, GF_CORRUPTED_DATA, "Invalid UTF data (line %d)", curLine);
					goto exit;
				}
				len = (u32) _len;
			}
			char_line = 0;
			i=j=0;
			rem_styles = 0;
			rem_color = 0;
			while (i<len) {
				u32 font_style = 0;
				u32 style_nb_chars = 0;
				u32 style_def_type = 0;

				if ( (uniLine[i]=='<') && (uniLine[i+2]=='>')) {
					style_nb_chars = 3;
					style_def_type = 1;
				}
				else if ( (uniLine[i]=='<') && (uniLine[i+1]=='/') && (uniLine[i+3]=='>')) {
					style_def_type = 2;
					style_nb_chars = 4;
				}
				else if (uniLine[i]=='<')  {
					const unsigned short* src = uniLine + i;
					size_t alen = gf_utf8_wcstombs(szLine, 2048, (const unsigned short**) & src);
					szLine[alen] = 0;
					strlwr(szLine);
					if (!strncmp(szLine, "<font ", 6) ) {
						char *a_sep = strstr(szLine, "color");
						if (a_sep) a_sep = strchr(a_sep, '"');
						if (a_sep) {
							char *e_sep = strchr(a_sep+1, '"');
							if (e_sep) {
								e_sep[0] = 0;
								font_style = gf_color_parse(a_sep+1);
								e_sep[0] = '"';
								e_sep = strchr(e_sep+1, '>');
								if (e_sep) {
									style_nb_chars = (u32) (1 + e_sep - szLine);
									style_def_type = 1;
								}
							}

						}
					}
					else if (!strncmp(szLine, "</font>", 7) ) {
						style_nb_chars = 7;
						style_def_type = 2;
						font_style = 0xFFFFFFFF;
					}
					//skip unknown
					else {
						char *a_sep = strstr(szLine, ">");
						if (a_sep) {
							style_nb_chars = (u32) (a_sep - szLine);
							i += style_nb_chars;
							continue;
						}
					}

				}

				/*start of new style*/
				if (style_def_type==1)  {
					/*store prev style*/
					if (set_end_char) {
						assert(set_start_char);
						gf_isom_text_add_style(samp, &rec);
						set_end_char = set_start_char = GF_FALSE;
						rec.style_flags &= ~rem_styles;
						rem_styles = 0;
						if (rem_color) {
							rec.text_color = default_color;
							rem_color = 0;
						}
					}
					if (set_start_char && (rec.startCharOffset != j)) {
						rec.endCharOffset = char_len + j;
						if (rec.style_flags) gf_isom_text_add_style(samp, &rec);
					}
					switch (uniLine[i+1]) {
					case 'b':
					case 'B':
						rec.style_flags |= GF_TXT_STYLE_BOLD;
						set_start_char = GF_TRUE;
						rec.startCharOffset = char_len + j;
						break;
					case 'i':
					case 'I':
						rec.style_flags |= GF_TXT_STYLE_ITALIC;
						set_start_char = GF_TRUE;
						rec.startCharOffset = char_len + j;
						break;
					case 'u':
					case 'U':
						rec.style_flags |= GF_TXT_STYLE_UNDERLINED;
						set_start_char = GF_TRUE;
						rec.startCharOffset = char_len + j;
						break;
					case 'f':
					case 'F':
						if (font_style) {
							rec.text_color = font_style;
							set_start_char = GF_TRUE;
							rec.startCharOffset = char_len + j;
						}
						break;
					}
					i += style_nb_chars;
					continue;
				}

				/*end of prev style*/
				if (style_def_type==2)  {
					switch (uniLine[i+2]) {
					case 'b':
					case 'B':
						rem_styles |= GF_TXT_STYLE_BOLD;
						set_end_char = GF_TRUE;
						rec.endCharOffset = char_len + j;
						break;
					case 'i':
					case 'I':
						rem_styles |= GF_TXT_STYLE_ITALIC;
						set_end_char = GF_TRUE;
						rec.endCharOffset = char_len + j;
						break;
					case 'u':
					case 'U':
						rem_styles |= GF_TXT_STYLE_UNDERLINED;
						set_end_char = GF_TRUE;
						rec.endCharOffset = char_len + j;
						break;
					case 'f':
					case 'F':
						if (font_style) {
							rem_color = 1;
							set_end_char = GF_TRUE;
							rec.endCharOffset = char_len + j;
						}
					}
					i+=style_nb_chars;
					continue;
				}
				/*store style*/
				if (set_end_char) {
					gf_isom_text_add_style(samp, &rec);
					set_end_char = GF_FALSE;
					set_start_char = GF_TRUE;
					rec.startCharOffset = char_len + j;
					rec.style_flags &= ~rem_styles;
					rem_styles = 0;
					rec.text_color = default_color;
					rem_color = 0;
				}

				uniText[j] = uniLine[i];
				j++;
				i++;
			}
			/*store last style*/
			if (set_end_char) {
				gf_isom_text_add_style(samp, &rec);
				set_end_char = GF_FALSE;
				set_start_char = GF_TRUE;
				rec.startCharOffset = char_len + j;
				rec.style_flags &= ~rem_styles;
				rem_styles = 0;
			}

			char_line = j;
			uniText[j] = 0;

			sptr = (u16 *) uniText;
			len = (u32) gf_utf8_wcstombs(szText, 5000, (const u16 **) &sptr);

			gf_isom_text_add_text(samp, szText, len);
			char_len += char_line;
			txt_line ++;
			break;
		}
		if (duration && (start >= duration)) break;
	}

	gf_isom_delete_text_sample(samp);
	/*do not add any empty sample at the end since it modifies track duration and is not needed - it is the player job
	to figure out when to stop displaying the last text sample
	However update the last sample duration*/
	gf_isom_set_last_sample_duration(import->dest, track, (u32) (end-start) );
	gf_set_progress("Importing SRT", nb_samp, nb_samp);

exit:
	if (e) gf_isom_remove_track(import->dest, track);
	gf_fclose(srt_in);
	return e;
}