1 /*
2 *			GPAC - Multimedia Framework C SDK
3 *
4 *			Authors: Jean Le Feuvre
5 *			Copyright (c) Telecom ParisTech 2000-2012
6 *					All rights reserved
7 *
8 *  This file is part of GPAC / ISO Media File Format sub-project
9 *
10 *  GPAC is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU Lesser General Public License as published by
12 *  the Free Software Foundation; either version 2, or (at your option)
13 *  any later version.
14 *
15 *  GPAC is distributed in the hope that it will be useful,
16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 *  GNU Lesser General Public License for more details.
19 *
20 *  You should have received a copy of the GNU Lesser General Public
21 *  License along with this library; see the file COPYING.  If not, write to
22 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25 
26 #include <gpac/internal/isomedia_dev.h>
27 #include <gpac/constants.h>
28 
29 #ifndef GPAC_DISABLE_ISOM
30 
31 #ifndef GPAC_DISABLE_ISOM_WRITE
32 
gf_isom_update_text_description(GF_ISOFile * movie,u32 trackNumber,u32 descriptionIndex,GF_TextSampleDescriptor * desc)33 GF_Err gf_isom_update_text_description(GF_ISOFile *movie, u32 trackNumber, u32 descriptionIndex, GF_TextSampleDescriptor *desc)
34 {
35 	GF_TrackBox *trak;
36 	GF_Err e;
37 	u32 i;
38 	GF_Tx3gSampleEntryBox *txt;
39 
40 	if (!descriptionIndex || !desc) return GF_BAD_PARAM;
41 	e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
42 	if (e) return e;
43 
44 	trak = gf_isom_get_track_from_file(movie, trackNumber);
45 	if (!trak || !trak->Media || !desc->font_count) return GF_BAD_PARAM;
46 
47 	switch (trak->Media->handler->handlerType) {
48 	case GF_ISOM_MEDIA_TEXT:
49 	case GF_ISOM_MEDIA_SUBT:
50 		break;
51 	default:
52 		return GF_BAD_PARAM;
53 	}
54 
55 	txt = (GF_Tx3gSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, descriptionIndex - 1);
56 	if (!txt) return GF_BAD_PARAM;
57 	switch (txt->type) {
58 	case GF_ISOM_BOX_TYPE_TX3G:
59 	case GF_ISOM_BOX_TYPE_TEXT:
60 		break;
61 	default:
62 		return GF_BAD_PARAM;
63 	}
64 
65 	if (!movie->keep_utc)
66 		trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
67 
68 	txt->back_color = desc->back_color;
69 	txt->default_box = desc->default_pos;
70 	txt->default_style = desc->default_style;
71 	txt->displayFlags = desc->displayFlags;
72 	txt->vertical_justification = desc->vert_justif;
73 	txt->horizontal_justification = desc->horiz_justif;
74 	if (txt->font_table) gf_isom_box_del((GF_Box*)txt->font_table);
75 
76 	txt->font_table = (GF_FontTableBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_FTAB);
77 	txt->font_table->entry_count = desc->font_count;
78 	txt->font_table->fonts = (GF_FontRecord *)gf_malloc(sizeof(GF_FontRecord) * desc->font_count);
79 	for (i = 0; i<desc->font_count; i++) {
80 		txt->font_table->fonts[i].fontID = desc->fonts[i].fontID;
81 		if (desc->fonts[i].fontName) txt->font_table->fonts[i].fontName = gf_strdup(desc->fonts[i].fontName);
82 	}
83 	return e;
84 }
85 
gf_isom_new_text_description(GF_ISOFile * movie,u32 trackNumber,GF_TextSampleDescriptor * desc,char * URLname,char * URNname,u32 * outDescriptionIndex)86 GF_Err gf_isom_new_text_description(GF_ISOFile *movie, u32 trackNumber, GF_TextSampleDescriptor *desc, char *URLname, char *URNname, u32 *outDescriptionIndex)
87 {
88 	GF_TrackBox *trak;
89 	GF_Err e;
90 	u32 dataRefIndex, i;
91 	GF_Tx3gSampleEntryBox *txt;
92 
93 	e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
94 	if (e) return e;
95 
96 	trak = gf_isom_get_track_from_file(movie, trackNumber);
97 	if (!trak || !trak->Media || !desc || !desc->font_count) return GF_BAD_PARAM;
98 
99 	switch (trak->Media->handler->handlerType) {
100 	case GF_ISOM_MEDIA_TEXT:
101 	case GF_ISOM_MEDIA_SUBT:
102 		break;
103 	default:
104 		return GF_BAD_PARAM;
105 	}
106 
107 	//get or create the data ref
108 	e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex);
109 	if (e) return e;
110 	if (!dataRefIndex) {
111 		e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex);
112 		if (e) return e;
113 	}
114 	if (!movie->keep_utc)
115 		trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
116 
117 	txt = (GF_Tx3gSampleEntryBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TX3G);
118 	txt->dataReferenceIndex = dataRefIndex;
119 	gf_list_add(trak->Media->information->sampleTable->SampleDescription->other_boxes, txt);
120 	if (outDescriptionIndex) *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->other_boxes);
121 
122 	txt->back_color = desc->back_color;
123 	txt->default_box = desc->default_pos;
124 	txt->default_style = desc->default_style;
125 	txt->displayFlags = desc->displayFlags;
126 	txt->vertical_justification = desc->vert_justif;
127 	txt->horizontal_justification = desc->horiz_justif;
128 	txt->font_table = (GF_FontTableBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_FTAB);
129 	txt->font_table->entry_count = desc->font_count;
130 
131 	txt->font_table->fonts = (GF_FontRecord *)gf_malloc(sizeof(GF_FontRecord) * desc->font_count);
132 	for (i = 0; i<desc->font_count; i++) {
133 		txt->font_table->fonts[i].fontID = desc->fonts[i].fontID;
134 		if (desc->fonts[i].fontName) txt->font_table->fonts[i].fontName = gf_strdup(desc->fonts[i].fontName);
135 	}
136 	return e;
137 }
138 
139 
140 /*blindly adds text - note we don't rely on terminaison characters to handle utf8 and utf16 data
141 in the same way. It is the user responsability to signal UTF16*/
gf_isom_text_add_text(GF_TextSample * samp,char * text_data,u32 text_len)142 GF_Err gf_isom_text_add_text(GF_TextSample *samp, char *text_data, u32 text_len)
143 {
144 	if (!samp) return GF_BAD_PARAM;
145 	if (!text_len) return GF_OK;
146 	samp->text = (char*)gf_realloc(samp->text, sizeof(char) * (samp->len + text_len));
147 	memcpy(samp->text + samp->len, text_data, sizeof(char) * text_len);
148 	samp->len += text_len;
149 	return GF_OK;
150 }
151 
gf_isom_text_set_utf16_marker(GF_TextSample * samp)152 GF_Err gf_isom_text_set_utf16_marker(GF_TextSample *samp)
153 {
154 	/*we MUST have an empty sample*/
155 	if (!samp || samp->text) return GF_BAD_PARAM;
156 	samp->text = (char*)gf_malloc(sizeof(char) * 2);
157 	samp->text[0] = (char)0xFE;
158 	samp->text[1] = (char)0xFF;
159 	samp->len = 2;
160 	return GF_OK;
161 }
162 
gf_isom_text_add_style(GF_TextSample * samp,GF_StyleRecord * rec)163 GF_Err gf_isom_text_add_style(GF_TextSample *samp, GF_StyleRecord *rec)
164 {
165 	if (!samp || !rec) return GF_BAD_PARAM;
166 
167 	if (!samp->styles) {
168 		samp->styles = (GF_TextStyleBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STYL);
169 		if (!samp->styles) return GF_OUT_OF_MEM;
170 	}
171 	samp->styles->styles = (GF_StyleRecord*)gf_realloc(samp->styles->styles, sizeof(GF_StyleRecord)*(samp->styles->entry_count + 1));
172 	if (!samp->styles->styles) return GF_OUT_OF_MEM;
173 	samp->styles->styles[samp->styles->entry_count] = *rec;
174 	samp->styles->entry_count++;
175 	return GF_OK;
176 }
177 
gf_isom_text_add_highlight(GF_TextSample * samp,u16 start_char,u16 end_char)178 GF_Err gf_isom_text_add_highlight(GF_TextSample *samp, u16 start_char, u16 end_char)
179 {
180 	GF_TextHighlightBox *a;
181 	if (!samp) return GF_BAD_PARAM;
182 	if (start_char == end_char) return GF_BAD_PARAM;
183 
184 	a = (GF_TextHighlightBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_HLIT);
185 	if (!a) return GF_OUT_OF_MEM;
186 	a->startcharoffset = start_char;
187 	a->endcharoffset = end_char;
188 	return gf_list_add(samp->others, a);
189 }
190 
gf_isom_text_set_highlight_color(GF_TextSample * samp,u8 r,u8 g,u8 b,u8 a)191 GF_Err gf_isom_text_set_highlight_color(GF_TextSample *samp, u8 r, u8 g, u8 b, u8 a)
192 {
193 	if (!samp) return GF_BAD_PARAM;
194 
195 	if (!samp->highlight_color) {
196 		samp->highlight_color = (GF_TextHighlightColorBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_HCLR);
197 		if (!samp->highlight_color) return GF_OUT_OF_MEM;
198 	}
199 	samp->highlight_color->hil_color = a;
200 	samp->highlight_color->hil_color <<= 8;
201 	samp->highlight_color->hil_color = r;
202 	samp->highlight_color->hil_color <<= 8;
203 	samp->highlight_color->hil_color = g;
204 	samp->highlight_color->hil_color <<= 8;
205 	samp->highlight_color->hil_color = b;
206 	return GF_OK;
207 }
208 
gf_isom_text_set_highlight_color_argb(GF_TextSample * samp,u32 argb)209 GF_Err gf_isom_text_set_highlight_color_argb(GF_TextSample *samp, u32 argb)
210 {
211 	if (!samp) return GF_BAD_PARAM;
212 
213 	if (!samp->highlight_color) {
214 		samp->highlight_color = (GF_TextHighlightColorBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_HCLR);
215 		if (!samp->highlight_color) return GF_OUT_OF_MEM;
216 	}
217 	samp->highlight_color->hil_color = argb;
218 	return GF_OK;
219 }
220 
221 /*3GPP spec is quite obscur here*/
gf_isom_text_add_karaoke(GF_TextSample * samp,u32 start_time)222 GF_Err gf_isom_text_add_karaoke(GF_TextSample *samp, u32 start_time)
223 {
224 	if (!samp) return GF_BAD_PARAM;
225 	samp->cur_karaoke = (GF_TextKaraokeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_KROK);
226 	if (!samp->cur_karaoke) return GF_OUT_OF_MEM;
227 	samp->cur_karaoke->highlight_starttime = start_time;
228 	return gf_list_add(samp->others, samp->cur_karaoke);
229 }
230 
gf_isom_text_set_karaoke_segment(GF_TextSample * samp,u32 end_time,u16 start_char,u16 end_char)231 GF_Err gf_isom_text_set_karaoke_segment(GF_TextSample *samp, u32 end_time, u16 start_char, u16 end_char)
232 {
233 	if (!samp || !samp->cur_karaoke) return GF_BAD_PARAM;
234 	samp->cur_karaoke->records = (KaraokeRecord*)gf_realloc(samp->cur_karaoke->records, sizeof(KaraokeRecord)*(samp->cur_karaoke->nb_entries + 1));
235 	if (!samp->cur_karaoke->records) return GF_OUT_OF_MEM;
236 	samp->cur_karaoke->records[samp->cur_karaoke->nb_entries].end_charoffset = end_char;
237 	samp->cur_karaoke->records[samp->cur_karaoke->nb_entries].start_charoffset = start_char;
238 	samp->cur_karaoke->records[samp->cur_karaoke->nb_entries].highlight_endtime = end_time;
239 	samp->cur_karaoke->nb_entries++;
240 	return GF_OK;
241 }
242 
243 
gf_isom_text_set_scroll_delay(GF_TextSample * samp,u32 scroll_delay)244 GF_Err gf_isom_text_set_scroll_delay(GF_TextSample *samp, u32 scroll_delay)
245 {
246 	if (!samp) return GF_BAD_PARAM;
247 	if (!samp->scroll_delay) {
248 		samp->scroll_delay = (GF_TextScrollDelayBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_DLAY);
249 		if (!samp->scroll_delay) return GF_OUT_OF_MEM;
250 	}
251 	samp->scroll_delay->scroll_delay = scroll_delay;
252 	return GF_OK;
253 }
254 
gf_isom_text_add_hyperlink(GF_TextSample * samp,char * URL,char * altString,u16 start_char,u16 end_char)255 GF_Err gf_isom_text_add_hyperlink(GF_TextSample *samp, char *URL, char *altString, u16 start_char, u16 end_char)
256 {
257 	GF_TextHyperTextBox*a;
258 	if (!samp) return GF_BAD_PARAM;
259 	a = (GF_TextHyperTextBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_HREF);
260 	if (!a) return GF_OUT_OF_MEM;
261 	a->startcharoffset = start_char;
262 	a->endcharoffset = end_char;
263 	a->URL = URL ? gf_strdup(URL) : NULL;
264 	a->URL_hint = altString ? gf_strdup(altString) : NULL;
265 	return gf_list_add(samp->others, a);
266 }
267 
gf_isom_text_set_box(GF_TextSample * samp,s16 top,s16 left,s16 bottom,s16 right)268 GF_Err gf_isom_text_set_box(GF_TextSample *samp, s16 top, s16 left, s16 bottom, s16 right)
269 {
270 	if (!samp) return GF_BAD_PARAM;
271 	if (!samp->box) {
272 		samp->box = (GF_TextBoxBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_TBOX);
273 		if (!samp->box) return GF_OUT_OF_MEM;
274 	}
275 	samp->box->box.top = top;
276 	samp->box->box.left = left;
277 	samp->box->box.bottom = bottom;
278 	samp->box->box.right = right;
279 	return GF_OK;
280 }
281 
gf_isom_text_add_blink(GF_TextSample * samp,u16 start_char,u16 end_char)282 GF_Err gf_isom_text_add_blink(GF_TextSample *samp, u16 start_char, u16 end_char)
283 {
284 	GF_TextBlinkBox *a;
285 	if (!samp) return GF_BAD_PARAM;
286 	a = (GF_TextBlinkBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_BLNK);
287 	if (!a) return GF_OUT_OF_MEM;
288 	a->startcharoffset = start_char;
289 	a->endcharoffset = end_char;
290 	return gf_list_add(samp->others, a);
291 }
292 
gf_isom_text_set_wrap(GF_TextSample * samp,u8 wrap_flags)293 GF_Err gf_isom_text_set_wrap(GF_TextSample *samp, u8 wrap_flags)
294 {
295 	if (!samp) return GF_BAD_PARAM;
296 	if (!samp->wrap) {
297 		samp->wrap = (GF_TextWrapBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_TWRP);
298 		if (!samp->wrap) return GF_OUT_OF_MEM;
299 	}
300 	samp->wrap->wrap_flag = wrap_flags;
301 	return GF_OK;
302 }
303 
gpp_write_modifier(GF_BitStream * bs,GF_Box * a)304 static GFINLINE GF_Err gpp_write_modifier(GF_BitStream *bs, GF_Box *a)
305 {
306 	GF_Err e;
307 	if (!a) return GF_OK;
308 	e = gf_isom_box_size(a);
309 	if (!e) e = gf_isom_box_write(a, bs);
310 	return e;
311 }
312 
gf_isom_text_to_sample(GF_TextSample * samp)313 GF_ISOSample *gf_isom_text_to_sample(GF_TextSample *samp)
314 {
315 	GF_Err e;
316 	GF_ISOSample *res;
317 	GF_BitStream *bs;
318 	u32 i;
319 	if (!samp) return NULL;
320 
321 	bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
322 	gf_bs_write_u16(bs, samp->len);
323 	if (samp->len) gf_bs_write_data(bs, samp->text, samp->len);
324 
325 	e = gpp_write_modifier(bs, (GF_Box *)samp->styles);
326 	if (!e) e = gpp_write_modifier(bs, (GF_Box *)samp->highlight_color);
327 	if (!e) e = gpp_write_modifier(bs, (GF_Box *)samp->scroll_delay);
328 	if (!e) e = gpp_write_modifier(bs, (GF_Box *)samp->box);
329 	if (!e) e = gpp_write_modifier(bs, (GF_Box *)samp->wrap);
330 
331 	if (!e) {
332 		GF_Box *a;
333 		i = 0;
334 		while ((a = (GF_Box*)gf_list_enum(samp->others, &i))) {
335 			e = gpp_write_modifier(bs, a);
336 			if (e) break;
337 		}
338 	}
339 	if (e) {
340 		gf_bs_del(bs);
341 		return NULL;
342 	}
343 	res = gf_isom_sample_new();
344 	if (!res) {
345 		gf_bs_del(bs);
346 		return NULL;
347 	}
348 	gf_bs_get_content(bs, &res->data, &res->dataLength);
349 	gf_bs_del(bs);
350 	res->IsRAP = RAP;
351 	return res;
352 }
353 
gf_isom_text_has_similar_description(GF_ISOFile * movie,u32 trackNumber,GF_TextSampleDescriptor * desc,u32 * outDescIdx,Bool * same_box,Bool * same_styles)354 GF_Err gf_isom_text_has_similar_description(GF_ISOFile *movie, u32 trackNumber, GF_TextSampleDescriptor *desc, u32 *outDescIdx, Bool *same_box, Bool *same_styles)
355 {
356 	GF_TrackBox *trak;
357 	GF_Err e;
358 	u32 i, j, count;
359 	GF_Tx3gSampleEntryBox *txt;
360 
361 	*same_box = *same_styles = 0;
362 	*outDescIdx = 0;
363 
364 	if (!desc) return GF_BAD_PARAM;
365 	e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
366 	if (e) return GF_BAD_PARAM;
367 
368 	trak = gf_isom_get_track_from_file(movie, trackNumber);
369 	if (!trak || !trak->Media || !desc->font_count) return GF_BAD_PARAM;
370 
371 	switch (trak->Media->handler->handlerType) {
372 	case GF_ISOM_MEDIA_TEXT:
373 	case GF_ISOM_MEDIA_SUBT:
374 		break;
375 	default:
376 		return GF_BAD_PARAM;
377 	}
378 
379 	count = gf_list_count(trak->Media->information->sampleTable->SampleDescription->other_boxes);
380 	for (i = 0; i<count; i++) {
381 		Bool same_fonts;
382 		txt = (GF_Tx3gSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, i);
383 		if (!txt) continue;
384 		if ((txt->type != GF_ISOM_BOX_TYPE_TX3G) && (txt->type != GF_ISOM_BOX_TYPE_TEXT)) continue;
385 		if (txt->back_color != desc->back_color) continue;
386 		if (txt->displayFlags != desc->displayFlags) continue;
387 		if (txt->vertical_justification != desc->vert_justif) continue;
388 		if (txt->horizontal_justification != desc->horiz_justif) continue;
389 		if (txt->font_table->entry_count != desc->font_count) continue;
390 
391 		same_fonts = 1;
392 		for (j = 0; j<desc->font_count; j++) {
393 			if (txt->font_table->fonts[j].fontID != desc->fonts[j].fontID) same_fonts = 0;
394 			else if (strcmp(desc->fonts[j].fontName, txt->font_table->fonts[j].fontName)) same_fonts = 0;
395 		}
396 		if (same_fonts) {
397 			*outDescIdx = i + 1;
398 			if (!memcmp(&txt->default_box, &desc->default_pos, sizeof(GF_BoxRecord))) *same_box = 1;
399 			if (!memcmp(&txt->default_style, &desc->default_style, sizeof(GF_StyleRecord))) *same_styles = 1;
400 			return GF_OK;
401 		}
402 	}
403 	return GF_OK;
404 }
405 
406 #endif /*GPAC_DISABLE_ISOM_WRITE*/
407 
gf_isom_new_text_sample()408 GF_TextSample *gf_isom_new_text_sample()
409 {
410 	GF_TextSample *res;
411 	GF_SAFEALLOC(res, GF_TextSample);
412 	if (!res) return NULL;
413 	res->others = gf_list_new();
414 	return res;
415 }
416 
gf_isom_text_reset_styles(GF_TextSample * samp)417 GF_Err gf_isom_text_reset_styles(GF_TextSample *samp)
418 {
419 	if (!samp) return GF_BAD_PARAM;
420 	if (samp->box) gf_isom_box_del((GF_Box *)samp->box);
421 	samp->box = NULL;
422 	if (samp->highlight_color) gf_isom_box_del((GF_Box *)samp->highlight_color);
423 	samp->highlight_color = NULL;
424 	if (samp->scroll_delay) gf_isom_box_del((GF_Box *)samp->scroll_delay);
425 	samp->scroll_delay = NULL;
426 	if (samp->wrap) gf_isom_box_del((GF_Box *)samp->wrap);
427 	samp->wrap = NULL;
428 	if (samp->styles) gf_isom_box_del((GF_Box *)samp->styles);
429 	samp->styles = NULL;
430 	samp->cur_karaoke = NULL;
431 	while (gf_list_count(samp->others)) {
432 		GF_Box *a = (GF_Box*)gf_list_get(samp->others, 0);
433 		gf_list_rem(samp->others, 0);
434 		gf_isom_box_del(a);
435 	}
436 	return GF_OK;
437 }
438 
gf_isom_text_reset(GF_TextSample * samp)439 GF_Err gf_isom_text_reset(GF_TextSample *samp)
440 {
441 	if (!samp) return GF_BAD_PARAM;
442 	if (samp->text) gf_free(samp->text);
443 	samp->text = NULL;
444 	samp->len = 0;
445 	return gf_isom_text_reset_styles(samp);
446 }
447 
448 GF_EXPORT
gf_isom_delete_text_sample(GF_TextSample * tx_samp)449 void gf_isom_delete_text_sample(GF_TextSample * tx_samp)
450 {
451 	gf_isom_text_reset(tx_samp);
452 	gf_list_del(tx_samp->others);
453 	gf_free(tx_samp);
454 }
455 
456 GF_EXPORT
gf_isom_parse_texte_sample(GF_BitStream * bs)457 GF_TextSample *gf_isom_parse_texte_sample(GF_BitStream *bs)
458 {
459 	GF_TextSample *s = gf_isom_new_text_sample();
460 
461 	/*empty sample*/
462 	if (!bs || !gf_bs_available(bs)) return s;
463 
464 	s->len = gf_bs_read_u16(bs);
465 	if (s->len) {
466 		/*2 extra bytes for UTF-16 term char just in case (we don't know if a BOM marker is present or
467 		not since this may be a sample carried over RTP*/
468 		s->text = (char *)gf_malloc(sizeof(char)*(s->len + 2));
469 		s->text[s->len] = 0;
470 		s->text[s->len + 1] = 0;
471 		gf_bs_read_data(bs, s->text, s->len);
472 	}
473 
474 	while (gf_bs_available(bs)) {
475 		GF_Box *a;
476 		GF_Err e = gf_isom_parse_box(&a, bs);
477 		if (!e) {
478 			switch (a->type) {
479 			case GF_ISOM_BOX_TYPE_STYL:
480 				if (s->styles) {
481 					GF_TextStyleBox *st2 = (GF_TextStyleBox *)a;
482 					if (!s->styles->entry_count) {
483 						gf_isom_box_del((GF_Box*)s->styles);
484 						s->styles = st2;
485 					}
486 					else {
487 						s->styles->styles = (GF_StyleRecord*)gf_realloc(s->styles->styles, sizeof(GF_StyleRecord) * (s->styles->entry_count + st2->entry_count));
488 						memcpy(&s->styles->styles[s->styles->entry_count], st2->styles, sizeof(GF_StyleRecord) * st2->entry_count);
489 						s->styles->entry_count += st2->entry_count;
490 						gf_isom_box_del(a);
491 					}
492 				}
493 				else {
494 					s->styles = (GF_TextStyleBox*)a;
495 				}
496 				break;
497 			case GF_ISOM_BOX_TYPE_KROK:
498 				s->cur_karaoke = (GF_TextKaraokeBox*)a;
499 			case GF_ISOM_BOX_TYPE_HLIT:
500 			case GF_ISOM_BOX_TYPE_HREF:
501 			case GF_ISOM_BOX_TYPE_BLNK:
502 				gf_list_add(s->others, a);
503 				break;
504 			case GF_ISOM_BOX_TYPE_HCLR:
505 				if (s->highlight_color) gf_isom_box_del(a);
506 				else s->highlight_color = (GF_TextHighlightColorBox *)a;
507 				break;
508 			case GF_ISOM_BOX_TYPE_DLAY:
509 				if (s->scroll_delay) gf_isom_box_del(a);
510 				else s->scroll_delay = (GF_TextScrollDelayBox*)a;
511 				break;
512 			case GF_ISOM_BOX_TYPE_TBOX:
513 				if (s->box) gf_isom_box_del(a);
514 				else s->box = (GF_TextBoxBox *)a;
515 				break;
516 			case GF_ISOM_BOX_TYPE_TWRP:
517 				if (s->wrap) gf_isom_box_del(a);
518 				else s->wrap = (GF_TextWrapBox*)a;
519 				break;
520 			default:
521 				gf_isom_box_del(a);
522 				break;
523 			}
524 		}
525 	}
526 	return s;
527 }
528 
gf_isom_parse_texte_sample_from_data(char * data,u32 dataLength)529 GF_TextSample *gf_isom_parse_texte_sample_from_data(char *data, u32 dataLength)
530 {
531 	GF_TextSample *s;
532 	GF_BitStream *bs;
533 	/*empty text sample*/
534 	if (!data || !dataLength) {
535 		return gf_isom_new_text_sample();
536 	}
537 
538 	bs = gf_bs_new(data, dataLength, GF_BITSTREAM_READ);
539 	s = gf_isom_parse_texte_sample(bs);
540 	gf_bs_del(bs);
541 	return s;
542 }
543 
544 
545 /*out-of-band sample desc (128 and 255 reserved in RFC)*/
546 #define SAMPLE_INDEX_OFFSET		129
547 
548 
gf_isom_write_tx3g(GF_Tx3gSampleEntryBox * a,GF_BitStream * bs,u32 sidx,u32 sidx_offset)549 static void gf_isom_write_tx3g(GF_Tx3gSampleEntryBox *a, GF_BitStream *bs, u32 sidx, u32 sidx_offset)
550 {
551 	u32 size, j, fount_count;
552 	void gpp_write_rgba(GF_BitStream *bs, u32 col);
553 	void gpp_write_box(GF_BitStream *bs, GF_BoxRecord *rec);
554 	void gpp_write_style(GF_BitStream *bs, GF_StyleRecord *rec);
555 
556 
557 	if (sidx_offset) gf_bs_write_u8(bs, sidx + sidx_offset);
558 
559 	/*SINCE WINCE HAS A READONLY VERSION OF MP4 WE MUST DO IT BY HAND*/
560 	size = 8 + 18 + 8 + 12;
561 	size += 8 + 2;
562 	fount_count = 0;
563 	if (a->font_table) {
564 		fount_count = a->font_table->entry_count;
565 		for (j = 0; j<fount_count; j++) {
566 			size += 3;
567 			if (a->font_table->fonts[j].fontName) size += (u32)strlen(a->font_table->fonts[j].fontName);
568 		}
569 	}
570 	/*write TextSampleEntry box*/
571 	gf_bs_write_u32(bs, size);
572 	gf_bs_write_u32(bs, a->type);
573 	gf_bs_write_data(bs, a->reserved, 6);
574 	gf_bs_write_u16(bs, a->dataReferenceIndex);
575 	gf_bs_write_u32(bs, a->displayFlags);
576 	gf_bs_write_u8(bs, a->horizontal_justification);
577 	gf_bs_write_u8(bs, a->vertical_justification);
578 	gpp_write_rgba(bs, a->back_color);
579 	gpp_write_box(bs, &a->default_box);
580 	gpp_write_style(bs, &a->default_style);
581 	/*write font table box*/
582 	size -= (8 + 18 + 8 + 12);
583 	gf_bs_write_u32(bs, size);
584 	gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_FTAB);
585 
586 	gf_bs_write_u16(bs, fount_count);
587 	for (j = 0; j<fount_count; j++) {
588 		gf_bs_write_u16(bs, a->font_table->fonts[j].fontID);
589 		if (a->font_table->fonts[j].fontName) {
590 			u32 len = (u32)strlen(a->font_table->fonts[j].fontName);
591 			gf_bs_write_u8(bs, len);
592 			gf_bs_write_data(bs, a->font_table->fonts[j].fontName, len);
593 		}
594 		else {
595 			gf_bs_write_u8(bs, 0);
596 		}
597 	}
598 }
599 
gf_isom_get_ttxt_esd(GF_MediaBox * mdia,GF_ESD ** out_esd)600 GF_Err gf_isom_get_ttxt_esd(GF_MediaBox *mdia, GF_ESD **out_esd)
601 {
602 	GF_BitStream *bs;
603 	u32 count, i;
604 	Bool has_v_info;
605 	GF_List *sampleDesc;
606 	GF_ESD *esd;
607 	GF_TrackBox *tk;
608 
609 	*out_esd = NULL;
610 	sampleDesc = mdia->information->sampleTable->SampleDescription->other_boxes;
611 	count = gf_list_count(sampleDesc);
612 	if (!count) return GF_ISOM_INVALID_MEDIA;
613 
614 	esd = gf_odf_desc_esd_new(2);
615 	esd->decoderConfig->streamType = GF_STREAM_TEXT;
616 	esd->decoderConfig->objectTypeIndication = GPAC_OTI_TEXT_MPEG4;
617 
618 	bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
619 
620 
621 	/*Base3GPPFormat*/
622 	gf_bs_write_u8(bs, 0x10);
623 	/*MPEGExtendedFormat*/
624 	gf_bs_write_u8(bs, 0x10);
625 	/*profileLevel*/
626 	gf_bs_write_u8(bs, 0x10);
627 	gf_bs_write_u24(bs, mdia->mediaHeader->timeScale);
628 	gf_bs_write_int(bs, 0, 1);	/*no alt formats*/
629 	gf_bs_write_int(bs, 2, 2);	/*only out-of-band-band sample desc*/
630 	gf_bs_write_int(bs, 1, 1);	/*we will write sample desc*/
631 
632 								/*write v info if any visual track in this movie*/
633 	has_v_info = 0;
634 	i = 0;
635 	while ((tk = (GF_TrackBox*)gf_list_enum(mdia->mediaTrack->moov->trackList, &i))) {
636 		if (tk->Media->handler && (tk->Media->handler->handlerType == GF_ISOM_MEDIA_VISUAL)) {
637 			has_v_info = 1;
638 		}
639 	}
640 	gf_bs_write_int(bs, has_v_info, 1);
641 
642 	gf_bs_write_int(bs, 0, 3);	/*reserved, spec doesn't say the values*/
643 	gf_bs_write_u8(bs, mdia->mediaTrack->Header->layer);
644 	gf_bs_write_u16(bs, mdia->mediaTrack->Header->width >> 16);
645 	gf_bs_write_u16(bs, mdia->mediaTrack->Header->height >> 16);
646 
647 	/*write desc*/
648 	gf_bs_write_u8(bs, count);
649 	for (i = 0; i<count; i++) {
650 		GF_Tx3gSampleEntryBox *a;
651 		a = (GF_Tx3gSampleEntryBox *)gf_list_get(sampleDesc, i);
652 		if ((a->type != GF_ISOM_BOX_TYPE_TX3G) && (a->type != GF_ISOM_BOX_TYPE_TEXT)) continue;
653 		gf_isom_write_tx3g(a, bs, i + 1, SAMPLE_INDEX_OFFSET);
654 	}
655 	if (has_v_info) {
656 		u32 trans;
657 		/*which video shall we pick for MPEG-4, and how is the associations indicated in 3GP ???*/
658 		gf_bs_write_u16(bs, 0);
659 		gf_bs_write_u16(bs, 0);
660 		trans = mdia->mediaTrack->Header->matrix[6];
661 		trans >>= 16;
662 		gf_bs_write_u16(bs, trans);
663 		trans = mdia->mediaTrack->Header->matrix[7];
664 		trans >>= 16;
665 		gf_bs_write_u16(bs, trans);
666 	}
667 
668 	gf_bs_get_content(bs, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength);
669 	gf_bs_del(bs);
670 	*out_esd = esd;
671 	return GF_OK;
672 }
673 
gf_isom_rewrite_text_sample(GF_ISOSample * samp,u32 sampleDescriptionIndex,u32 sample_dur)674 GF_Err gf_isom_rewrite_text_sample(GF_ISOSample *samp, u32 sampleDescriptionIndex, u32 sample_dur)
675 {
676 	GF_BitStream *bs;
677 	u32 pay_start, txt_size;
678 	Bool is_utf_16 = 0;
679 	if (!samp || !samp->data || !samp->dataLength) return GF_OK;
680 
681 	bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_READ);
682 	txt_size = gf_bs_read_u16(bs);
683 	gf_bs_del(bs);
684 
685 	/*remove BOM*/
686 	pay_start = 2;
687 	if (txt_size>2) {
688 		/*seems 3GP only accepts BE UTF-16 (no LE, no UTF32)*/
689 		if (((u8)samp->data[2] == (u8)0xFE) && ((u8)samp->data[3] == (u8)0xFF)) {
690 			is_utf_16 = 1;
691 			pay_start = 4;
692 			txt_size -= 2;
693 		}
694 	}
695 
696 	/*rewrite as TTU(1)*/
697 	bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
698 	gf_bs_write_int(bs, is_utf_16, 1);
699 	gf_bs_write_int(bs, 0, 4);
700 	gf_bs_write_int(bs, 1, 3);
701 	gf_bs_write_u16(bs, 8 + samp->dataLength - pay_start);
702 	gf_bs_write_u8(bs, sampleDescriptionIndex + SAMPLE_INDEX_OFFSET);
703 	gf_bs_write_u24(bs, sample_dur);
704 	/*write text size*/
705 	gf_bs_write_u16(bs, txt_size);
706 	if (txt_size) gf_bs_write_data(bs, samp->data + pay_start, samp->dataLength - pay_start);
707 
708 	gf_free(samp->data);
709 	samp->data = NULL;
710 	gf_bs_get_content(bs, &samp->data, &samp->dataLength);
711 	gf_bs_del(bs);
712 	return GF_OK;
713 }
714 
715 
gf_isom_text_get_encoded_tx3g(GF_ISOFile * file,u32 track,u32 sidx,u32 sidx_offset,char ** tx3g,u32 * tx3g_size)716 GF_Err gf_isom_text_get_encoded_tx3g(GF_ISOFile *file, u32 track, u32 sidx, u32 sidx_offset, char **tx3g, u32 *tx3g_size)
717 {
718 	GF_BitStream *bs;
719 	GF_TrackBox *trak;
720 	GF_Tx3gSampleEntryBox *a;
721 
722 	trak = gf_isom_get_track_from_file(file, track);
723 	if (!trak) return GF_BAD_PARAM;
724 
725 	a = (GF_Tx3gSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, sidx - 1);
726 	if (!a) return GF_BAD_PARAM;
727 	if ((a->type != GF_ISOM_BOX_TYPE_TX3G) && (a->type != GF_ISOM_BOX_TYPE_TEXT)) return GF_BAD_PARAM;
728 
729 	bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
730 	gf_isom_write_tx3g(a, bs, sidx, sidx_offset);
731 	*tx3g = NULL;
732 	*tx3g_size = 0;
733 	gf_bs_get_content(bs, tx3g, tx3g_size);
734 	gf_bs_del(bs);
735 	return GF_OK;
736 }
737 
738 #endif /*GPAC_DISABLE_ISOM*/
739