Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2019
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 :
32 24 : GF_Err gf_isom_get_text_description(GF_ISOFile *movie, u32 trackNumber, u32 descriptionIndex, GF_TextSampleDescriptor **out_desc)
33 : {
34 : GF_TrackBox *trak;
35 : u32 i;
36 : Bool is_qt_text = GF_FALSE;
37 : GF_Tx3gSampleEntryBox *txt;
38 :
39 24 : if (!descriptionIndex || !out_desc) return GF_BAD_PARAM;
40 :
41 24 : trak = gf_isom_get_track_from_file(movie, trackNumber);
42 24 : if (!trak || !trak->Media) return GF_BAD_PARAM;
43 :
44 24 : switch (trak->Media->handler->handlerType) {
45 : case GF_ISOM_MEDIA_TEXT:
46 : case GF_ISOM_MEDIA_SUBT:
47 : break;
48 : default:
49 : return GF_BAD_PARAM;
50 : }
51 :
52 24 : txt = (GF_Tx3gSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, descriptionIndex - 1);
53 24 : if (!txt) return GF_BAD_PARAM;
54 24 : switch (txt->type) {
55 : case GF_ISOM_BOX_TYPE_TX3G:
56 : break;
57 0 : case GF_ISOM_BOX_TYPE_TEXT:
58 : is_qt_text = GF_TRUE;
59 0 : break;
60 : default:
61 : return GF_BAD_PARAM;
62 : }
63 :
64 24 : (*out_desc) = (GF_TextSampleDescriptor *) gf_odf_desc_new(GF_ODF_TX3G_TAG);
65 24 : if (! (*out_desc) ) return GF_OUT_OF_MEM;
66 24 : (*out_desc)->back_color = txt->back_color;
67 24 : (*out_desc)->default_pos = txt->default_box;
68 24 : (*out_desc)->default_style = txt->default_style;
69 24 : (*out_desc)->displayFlags = txt->displayFlags;
70 24 : (*out_desc)->vert_justif = txt->vertical_justification;
71 24 : (*out_desc)->horiz_justif = txt->horizontal_justification;
72 24 : if (is_qt_text) {
73 : GF_TextSampleEntryBox *qt_txt = (GF_TextSampleEntryBox *) txt;
74 0 : if (qt_txt->textName) {
75 0 : (*out_desc)->font_count = 1;
76 0 : (*out_desc)->fonts = (GF_FontRecord *) gf_malloc(sizeof(GF_FontRecord));
77 0 : (*out_desc)->fonts[0].fontName = gf_strdup(qt_txt->textName);
78 : }
79 : } else {
80 24 : (*out_desc)->font_count = txt->font_table->entry_count;
81 24 : (*out_desc)->fonts = (GF_FontRecord *) gf_malloc(sizeof(GF_FontRecord) * txt->font_table->entry_count);
82 48 : for (i=0; i<txt->font_table->entry_count; i++) {
83 24 : (*out_desc)->fonts[i].fontID = txt->font_table->fonts[i].fontID;
84 24 : if (txt->font_table->fonts[i].fontName)
85 24 : (*out_desc)->fonts[i].fontName = gf_strdup(txt->font_table->fonts[i].fontName);
86 : }
87 : }
88 : return GF_OK;
89 : }
90 :
91 : #ifndef GPAC_DISABLE_ISOM_WRITE
92 :
93 : #if 0 //unused
94 : /*! updates text sample description
95 : \param isom_file the target ISO file
96 : \param trackNumber the target track
97 : \param sampleDescriptionIndex the target sample description index
98 : \param desc the text sample descriptor to use
99 : \return error if any
100 : */
101 : GF_Err gf_isom_update_text_description(GF_ISOFile *movie, u32 trackNumber, u32 descriptionIndex, GF_TextSampleDescriptor *desc)
102 : {
103 : GF_TrackBox *trak;
104 : GF_Err e;
105 : Bool is_qt_text = GF_FALSE;
106 : u32 i;
107 : GF_Tx3gSampleEntryBox *txt;
108 :
109 : if (!descriptionIndex || !desc) return GF_BAD_PARAM;
110 : e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
111 : if (e) return e;
112 :
113 : trak = gf_isom_get_track_from_file(movie, trackNumber);
114 : if (!trak || !trak->Media || !desc->font_count) return GF_BAD_PARAM;
115 :
116 : switch (trak->Media->handler->handlerType) {
117 : case GF_ISOM_MEDIA_TEXT:
118 : case GF_ISOM_MEDIA_SUBT:
119 : break;
120 : default:
121 : return GF_BAD_PARAM;
122 : }
123 :
124 : txt = (GF_Tx3gSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, descriptionIndex - 1);
125 : if (!txt) return GF_BAD_PARAM;
126 : switch (txt->type) {
127 : case GF_ISOM_BOX_TYPE_TX3G:
128 : break;
129 : case GF_ISOM_BOX_TYPE_TEXT:
130 : is_qt_text = GF_TRUE;
131 : break;
132 : default:
133 : return GF_BAD_PARAM;
134 : }
135 :
136 : if (!movie->keep_utc)
137 : trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
138 :
139 : txt->back_color = desc->back_color;
140 : txt->default_box = desc->default_pos;
141 : txt->default_style = desc->default_style;
142 : txt->displayFlags = desc->displayFlags;
143 : txt->vertical_justification = desc->vert_justif;
144 : txt->horizontal_justification = desc->horiz_justif;
145 : if (is_qt_text) {
146 : GF_TextSampleEntryBox *qtt = (GF_TextSampleEntryBox *) txt;
147 : if (qtt->textName) gf_free(qtt->textName);
148 : qtt->textName = NULL;
149 : if (desc->font_count) {
150 : qtt->textName = gf_strdup(desc->fonts[0].fontName);
151 : }
152 : } else {
153 : if (txt->font_table) gf_isom_box_del_parent(&txt->child_boxes, (GF_Box*)txt->font_table);
154 :
155 : txt->font_table = (GF_FontTableBox *)gf_isom_box_new_parent(&txt->child_boxes, GF_ISOM_BOX_TYPE_FTAB);
156 : txt->font_table->entry_count = desc->font_count;
157 : txt->font_table->fonts = (GF_FontRecord *) gf_malloc(sizeof(GF_FontRecord) * desc->font_count);
158 : for (i=0; i<desc->font_count; i++) {
159 : txt->font_table->fonts[i].fontID = desc->fonts[i].fontID;
160 : if (desc->fonts[i].fontName) txt->font_table->fonts[i].fontName = gf_strdup(desc->fonts[i].fontName);
161 : }
162 : }
163 : return e;
164 : }
165 : #endif //unused
166 :
167 : GF_EXPORT
168 45 : GF_Err gf_isom_new_text_description(GF_ISOFile *movie, u32 trackNumber, GF_TextSampleDescriptor *desc, const char *URLname, const char *URNname, u32 *outDescriptionIndex)
169 : {
170 : GF_TrackBox *trak;
171 : GF_Err e;
172 : u32 dataRefIndex, i;
173 : GF_Tx3gSampleEntryBox *txt;
174 :
175 45 : e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
176 45 : if (e) return e;
177 :
178 45 : trak = gf_isom_get_track_from_file(movie, trackNumber);
179 45 : if (!trak || !trak->Media || !desc || !desc->font_count) return GF_BAD_PARAM;
180 :
181 45 : switch (trak->Media->handler->handlerType) {
182 : case GF_ISOM_MEDIA_TEXT:
183 : case GF_ISOM_MEDIA_SUBT:
184 : break;
185 : default:
186 : return GF_BAD_PARAM;
187 : }
188 :
189 : //get or create the data ref
190 45 : e = Media_FindDataRef(trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex);
191 45 : if (e) return e;
192 45 : if (!dataRefIndex) {
193 44 : e = Media_CreateDataRef(movie, trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex);
194 44 : if (e) return e;
195 : }
196 45 : if (!movie->keep_utc)
197 45 : trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
198 :
199 45 : txt = (GF_Tx3gSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TX3G);
200 45 : if (!txt) return GF_OUT_OF_MEM;
201 45 : txt->dataReferenceIndex = dataRefIndex;
202 45 : gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, txt);
203 45 : if (outDescriptionIndex) *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes);
204 :
205 45 : txt->back_color = desc->back_color;
206 45 : txt->default_box = desc->default_pos;
207 45 : txt->default_style = desc->default_style;
208 45 : txt->displayFlags = desc->displayFlags;
209 45 : txt->vertical_justification = desc->vert_justif;
210 45 : txt->horizontal_justification = desc->horiz_justif;
211 45 : txt->font_table = (GF_FontTableBox *)gf_isom_box_new_parent(&txt->child_boxes, GF_ISOM_BOX_TYPE_FTAB);
212 45 : if (!txt->font_table) return GF_OUT_OF_MEM;
213 45 : txt->font_table->entry_count = desc->font_count;
214 :
215 45 : txt->font_table->fonts = (GF_FontRecord *) gf_malloc(sizeof(GF_FontRecord) * desc->font_count);
216 45 : if (!txt->font_table->fonts) return GF_OUT_OF_MEM;
217 45 : for (i=0; i<desc->font_count; i++) {
218 45 : txt->font_table->fonts[i].fontID = desc->fonts[i].fontID;
219 45 : if (desc->fonts[i].fontName) txt->font_table->fonts[i].fontName = gf_strdup(desc->fonts[i].fontName);
220 : }
221 : return e;
222 : }
223 :
224 :
225 : /*blindly adds text - note we don't rely on terminaison characters to handle utf8 and utf16 data
226 : in the same way. It is the user responsability to signal UTF16*/
227 : GF_EXPORT
228 4184 : GF_Err gf_isom_text_add_text(GF_TextSample *samp, char *text_data, u32 text_len)
229 : {
230 4184 : if (!samp) return GF_BAD_PARAM;
231 4184 : if (!text_len) return GF_OK;
232 4184 : samp->text = (char*)gf_realloc(samp->text, sizeof(char) * (samp->len + text_len) );
233 4184 : memcpy(samp->text + samp->len, text_data, sizeof(char) * text_len);
234 4184 : samp->len += text_len;
235 4184 : return GF_OK;
236 : }
237 :
238 : #if 0 //unused
239 : /*! sets UTF16 marker for text data. This MUST be called on an empty sample. If text data added later
240 : on (cf below) is not formatted as UTF16 data(2 bytes char) the resulting text sample won't be compliant,
241 : but this library won't warn
242 : \param tx_samp the target text sample
243 : \return error if any
244 : */
245 : GF_Err gf_isom_text_set_utf16_marker(GF_TextSample *samp)
246 : {
247 : /*we MUST have an empty sample*/
248 : if (!samp || samp->text) return GF_BAD_PARAM;
249 : samp->text = (char*)gf_malloc(sizeof(char) * 2);
250 : if (!samp->text) return GF_OUT_OF_MEM;
251 : samp->text[0] = (char) 0xFE;
252 : samp->text[1] = (char) 0xFF;
253 : samp->len = 2;
254 : return GF_OK;
255 : }
256 : #endif //unused
257 :
258 : GF_EXPORT
259 335 : GF_Err gf_isom_text_add_style(GF_TextSample *samp, GF_StyleRecord *rec)
260 : {
261 335 : if (!samp || !rec) return GF_BAD_PARAM;
262 :
263 335 : if (!samp->styles) {
264 169 : samp->styles = (GF_TextStyleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STYL);
265 169 : if (!samp->styles) return GF_OUT_OF_MEM;
266 : }
267 335 : samp->styles->styles = (GF_StyleRecord*)gf_realloc(samp->styles->styles, sizeof(GF_StyleRecord)*(samp->styles->entry_count+1));
268 335 : if (!samp->styles->styles) return GF_OUT_OF_MEM;
269 335 : samp->styles->styles[samp->styles->entry_count] = *rec;
270 335 : samp->styles->entry_count++;
271 335 : return GF_OK;
272 : }
273 :
274 : GF_EXPORT
275 1 : GF_Err gf_isom_text_add_highlight(GF_TextSample *samp, u16 start_char, u16 end_char)
276 : {
277 : GF_TextHighlightBox *a;
278 1 : if (!samp) return GF_BAD_PARAM;
279 1 : if (start_char == end_char) return GF_BAD_PARAM;
280 :
281 1 : a = (GF_TextHighlightBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HLIT);
282 1 : if (!a) return GF_OUT_OF_MEM;
283 1 : a->startcharoffset = start_char;
284 1 : a->endcharoffset = end_char;
285 1 : return gf_list_add(samp->others, a);
286 : }
287 :
288 :
289 : GF_EXPORT
290 2 : GF_Err gf_isom_text_set_highlight_color(GF_TextSample *samp, u32 argb)
291 : {
292 2 : if (!samp) return GF_BAD_PARAM;
293 :
294 2 : if (!samp->highlight_color) {
295 2 : samp->highlight_color = (GF_TextHighlightColorBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HCLR);
296 2 : if (!samp->highlight_color) return GF_OUT_OF_MEM;
297 : }
298 2 : samp->highlight_color->hil_color = argb;
299 2 : return GF_OK;
300 : }
301 :
302 : /*3GPP spec is quite obscur here*/
303 : GF_EXPORT
304 1 : GF_Err gf_isom_text_add_karaoke(GF_TextSample *samp, u32 start_time)
305 : {
306 1 : if (!samp) return GF_BAD_PARAM;
307 1 : samp->cur_karaoke = (GF_TextKaraokeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_KROK);
308 1 : if (!samp->cur_karaoke) return GF_OUT_OF_MEM;
309 1 : samp->cur_karaoke->highlight_starttime = start_time;
310 1 : return gf_list_add(samp->others, samp->cur_karaoke);
311 : }
312 :
313 : GF_EXPORT
314 2 : GF_Err gf_isom_text_set_karaoke_segment(GF_TextSample *samp, u32 end_time, u16 start_char, u16 end_char)
315 : {
316 2 : if (!samp || !samp->cur_karaoke) return GF_BAD_PARAM;
317 2 : samp->cur_karaoke->records = (KaraokeRecord*)gf_realloc(samp->cur_karaoke->records, sizeof(KaraokeRecord)*(samp->cur_karaoke->nb_entries+1));
318 2 : if (!samp->cur_karaoke->records) return GF_OUT_OF_MEM;
319 2 : samp->cur_karaoke->records[samp->cur_karaoke->nb_entries].end_charoffset = end_char;
320 2 : samp->cur_karaoke->records[samp->cur_karaoke->nb_entries].start_charoffset = start_char;
321 2 : samp->cur_karaoke->records[samp->cur_karaoke->nb_entries].highlight_endtime = end_time;
322 2 : samp->cur_karaoke->nb_entries++;
323 2 : return GF_OK;
324 : }
325 :
326 : GF_EXPORT
327 2 : GF_Err gf_isom_text_set_scroll_delay(GF_TextSample *samp, u32 scroll_delay)
328 : {
329 2 : if (!samp) return GF_BAD_PARAM;
330 2 : if (!samp->scroll_delay) {
331 2 : samp->scroll_delay = (GF_TextScrollDelayBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_DLAY);
332 2 : if (!samp->scroll_delay) return GF_OUT_OF_MEM;
333 : }
334 2 : samp->scroll_delay->scroll_delay = scroll_delay;
335 2 : return GF_OK;
336 : }
337 :
338 : GF_EXPORT
339 1 : GF_Err gf_isom_text_add_hyperlink(GF_TextSample *samp, char *URL, char *altString, u16 start_char, u16 end_char)
340 : {
341 : GF_TextHyperTextBox*a;
342 1 : if (!samp) return GF_BAD_PARAM;
343 1 : a = (GF_TextHyperTextBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_HREF);
344 1 : if (!a) return GF_OUT_OF_MEM;
345 1 : a->startcharoffset = start_char;
346 1 : a->endcharoffset = end_char;
347 1 : a->URL = URL ? gf_strdup(URL) : NULL;
348 1 : a->URL_hint = altString ? gf_strdup(altString) : NULL;
349 1 : return gf_list_add(samp->others, a);
350 : }
351 :
352 : GF_EXPORT
353 2 : GF_Err gf_isom_text_set_box(GF_TextSample *samp, s16 top, s16 left, s16 bottom, s16 right)
354 : {
355 2 : if (!samp) return GF_BAD_PARAM;
356 2 : if (!samp->box) {
357 2 : samp->box = (GF_TextBoxBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_TBOX);
358 2 : if (!samp->box) return GF_OUT_OF_MEM;
359 : }
360 2 : samp->box->box.top = top;
361 2 : samp->box->box.left = left;
362 2 : samp->box->box.bottom = bottom;
363 2 : samp->box->box.right = right;
364 2 : return GF_OK;
365 : }
366 :
367 : GF_EXPORT
368 2 : GF_Err gf_isom_text_add_blink(GF_TextSample *samp, u16 start_char, u16 end_char)
369 : {
370 : GF_TextBlinkBox *a;
371 2 : if (!samp) return GF_BAD_PARAM;
372 2 : a = (GF_TextBlinkBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_BLNK);
373 2 : if (!a) return GF_OUT_OF_MEM;
374 2 : a->startcharoffset = start_char;
375 2 : a->endcharoffset = end_char;
376 2 : return gf_list_add(samp->others, a);
377 : }
378 :
379 : GF_EXPORT
380 0 : GF_Err gf_isom_text_set_wrap(GF_TextSample *samp, u8 wrap_flags)
381 : {
382 0 : if (!samp) return GF_BAD_PARAM;
383 0 : if (!samp->wrap) {
384 0 : samp->wrap = (GF_TextWrapBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_TWRP);
385 0 : if (!samp->wrap) return GF_OUT_OF_MEM;
386 : }
387 0 : samp->wrap->wrap_flag = wrap_flags;
388 0 : return GF_OK;
389 : }
390 :
391 23660 : static GFINLINE GF_Err gpp_write_modifier(GF_BitStream *bs, GF_Box *a)
392 : {
393 : GF_Err e;
394 23660 : if (!a) return GF_OK;
395 180 : e = gf_isom_box_size(a);
396 180 : if (!e) e = gf_isom_box_write(a, bs);
397 : return e;
398 : }
399 :
400 : GF_EXPORT
401 4731 : GF_Err gf_isom_text_sample_write_bs(const GF_TextSample *samp, GF_BitStream *bs)
402 : {
403 : GF_Err e;
404 : u32 i;
405 4731 : if (!samp) return GF_BAD_PARAM;
406 :
407 4731 : gf_bs_write_u16(bs, samp->len);
408 4731 : if (samp->len) gf_bs_write_data(bs, samp->text, samp->len);
409 :
410 4731 : e = gpp_write_modifier(bs, (GF_Box *)samp->styles);
411 4731 : if (!e) e = gpp_write_modifier(bs, (GF_Box *)samp->highlight_color);
412 4731 : if (!e) e = gpp_write_modifier(bs, (GF_Box *)samp->scroll_delay);
413 4731 : if (!e) e = gpp_write_modifier(bs, (GF_Box *)samp->box);
414 4731 : if (!e) e = gpp_write_modifier(bs, (GF_Box *)samp->wrap);
415 :
416 4731 : if (!e) {
417 : GF_Box *a;
418 4731 : i=0;
419 9467 : while ((a = (GF_Box*)gf_list_enum(samp->others, &i))) {
420 5 : e = gpp_write_modifier(bs, a);
421 5 : if (e) break;
422 : }
423 : }
424 : return e;
425 : }
426 :
427 : GF_EXPORT
428 0 : GF_ISOSample *gf_isom_text_to_sample(const GF_TextSample *samp)
429 : {
430 : GF_Err e;
431 : GF_ISOSample *res;
432 : GF_BitStream *bs;
433 0 : if (!samp) return NULL;
434 :
435 0 : bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
436 :
437 0 : e = gf_isom_text_sample_write_bs(samp, bs);
438 :
439 0 : if (e) {
440 0 : gf_bs_del(bs);
441 0 : return NULL;
442 : }
443 0 : res = gf_isom_sample_new();
444 0 : if (!res) {
445 0 : gf_bs_del(bs);
446 0 : return NULL;
447 : }
448 0 : gf_bs_get_content(bs, &res->data, &res->dataLength);
449 0 : gf_bs_del(bs);
450 0 : res->IsRAP = RAP;
451 0 : return res;
452 : }
453 :
454 4731 : u32 gf_isom_text_sample_size(GF_TextSample *samp)
455 : {
456 : GF_Box *a;
457 : u32 i, size;
458 4731 : if (!samp) return 0;
459 :
460 4731 : size = 2 + samp->len;
461 4731 : if (samp->styles) {
462 169 : gf_isom_box_size((GF_Box *)samp->styles);
463 169 : size += (u32) samp->styles->size;
464 : }
465 4731 : if (samp->highlight_color) {
466 2 : gf_isom_box_size((GF_Box *)samp->highlight_color);
467 2 : size += (u32) samp->highlight_color->size;
468 : }
469 4731 : if (samp->scroll_delay) {
470 2 : gf_isom_box_size((GF_Box *)samp->scroll_delay);
471 2 : size += (u32) samp->scroll_delay->size;
472 : }
473 4731 : if (samp->box) {
474 2 : gf_isom_box_size((GF_Box *)samp->box);
475 2 : size += (u32) samp->box->size;
476 : }
477 4731 : if (samp->wrap) {
478 0 : gf_isom_box_size((GF_Box *)samp->wrap);
479 0 : size += (u32) samp->wrap->size;
480 : }
481 4731 : i=0;
482 9467 : while ((a = (GF_Box*)gf_list_enum(samp->others, &i))) {
483 5 : gf_isom_box_size((GF_Box *)a);
484 5 : size += (u32) a->size;
485 : }
486 : return size;
487 : }
488 :
489 : #if 0 //unused
490 : /*! checks if this text description is already inserted
491 : \param isom_file the target ISO file
492 : \param trackNumber the target track
493 : \param desc the 3GPP text sample description to check
494 : \param outDescIdx set to 0 if not found, or index of the matching sample description
495 : \param same_styles indicates if default styles matches
496 : \param same_box indicates if default box matches
497 : */
498 : GF_Err gf_isom_text_has_similar_description(GF_ISOFile *movie, u32 trackNumber, GF_TextSampleDescriptor *desc, u32 *outDescIdx, Bool *same_box, Bool *same_styles)
499 : {
500 : GF_TrackBox *trak;
501 : GF_Err e;
502 : u32 i, j, count;
503 : GF_Tx3gSampleEntryBox *txt;
504 :
505 : *same_box = *same_styles = 0;
506 : *outDescIdx = 0;
507 :
508 : if (!desc) return GF_BAD_PARAM;
509 : e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
510 : if (e) return GF_BAD_PARAM;
511 :
512 : trak = gf_isom_get_track_from_file(movie, trackNumber);
513 : if (!trak || !trak->Media || !desc->font_count) return GF_BAD_PARAM;
514 :
515 : switch (trak->Media->handler->handlerType) {
516 : case GF_ISOM_MEDIA_TEXT:
517 : case GF_ISOM_MEDIA_SUBT:
518 : break;
519 : default:
520 : return GF_BAD_PARAM;
521 : }
522 :
523 : count = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes);
524 : for (i=0; i<count; i++) {
525 : Bool same_fonts;
526 : txt = (GF_Tx3gSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, i);
527 : if (!txt) continue;
528 : if ((txt->type != GF_ISOM_BOX_TYPE_TX3G) && (txt->type != GF_ISOM_BOX_TYPE_TEXT)) continue;
529 : if (txt->back_color != desc->back_color) continue;
530 : if (txt->displayFlags != desc->displayFlags) continue;
531 : if (txt->vertical_justification != desc->vert_justif) continue;
532 : if (txt->horizontal_justification != desc->horiz_justif) continue;
533 : if (txt->font_table->entry_count != desc->font_count) continue;
534 :
535 : same_fonts = 1;
536 : for (j=0; j<desc->font_count; j++) {
537 : if (txt->font_table->fonts[j].fontID != desc->fonts[j].fontID) same_fonts = 0;
538 : else if (strcmp(desc->fonts[j].fontName, txt->font_table->fonts[j].fontName)) same_fonts = 0;
539 : }
540 : if (same_fonts) {
541 : *outDescIdx = i+1;
542 : if (!memcmp(&txt->default_box, &desc->default_pos, sizeof(GF_BoxRecord))) *same_box = 1;
543 : if (!memcmp(&txt->default_style, &desc->default_style, sizeof(GF_StyleRecord))) *same_styles = 1;
544 : return GF_OK;
545 : }
546 : }
547 : return GF_OK;
548 : }
549 : #endif
550 :
551 : #endif /*GPAC_DISABLE_ISOM_WRITE*/
552 :
553 : GF_EXPORT
554 2204 : GF_TextSample *gf_isom_new_text_sample()
555 : {
556 : GF_TextSample *res;
557 2204 : GF_SAFEALLOC(res, GF_TextSample);
558 2204 : if (!res) return NULL;
559 2204 : res->others = gf_list_new();
560 2204 : return res;
561 : }
562 :
563 : GF_EXPORT
564 4892 : GF_Err gf_isom_text_reset_styles(GF_TextSample *samp)
565 : {
566 4892 : if (!samp) return GF_BAD_PARAM;
567 4892 : if (samp->box) gf_isom_box_del((GF_Box *)samp->box);
568 4892 : samp->box = NULL;
569 4892 : if (samp->highlight_color) gf_isom_box_del((GF_Box *)samp->highlight_color);
570 4892 : samp->highlight_color = NULL;
571 4892 : if (samp->scroll_delay) gf_isom_box_del((GF_Box *)samp->scroll_delay);
572 4892 : samp->scroll_delay = NULL;
573 4892 : if (samp->wrap) gf_isom_box_del((GF_Box *)samp->wrap);
574 4892 : samp->wrap = NULL;
575 4892 : if (samp->styles) gf_isom_box_del((GF_Box *)samp->styles);
576 4892 : samp->styles = NULL;
577 4892 : samp->cur_karaoke = NULL;
578 9790 : while (gf_list_count(samp->others)) {
579 6 : GF_Box *a = (GF_Box*)gf_list_get(samp->others, 0);
580 6 : gf_list_rem(samp->others, 0);
581 6 : gf_isom_box_del(a);
582 : }
583 : return GF_OK;
584 : }
585 :
586 : GF_EXPORT
587 4892 : GF_Err gf_isom_text_reset(GF_TextSample *samp)
588 : {
589 4892 : if (!samp) return GF_BAD_PARAM;
590 4892 : if (samp->text) gf_free(samp->text);
591 4892 : samp->text = NULL;
592 4892 : samp->len = 0;
593 4892 : return gf_isom_text_reset_styles(samp);
594 : }
595 :
596 : GF_EXPORT
597 2204 : void gf_isom_delete_text_sample(GF_TextSample * tx_samp)
598 : {
599 2204 : gf_isom_text_reset(tx_samp);
600 2204 : gf_list_del(tx_samp->others);
601 2204 : gf_free(tx_samp);
602 2204 : }
603 :
604 : GF_EXPORT
605 122 : GF_TextSample *gf_isom_parse_text_sample(GF_BitStream *bs)
606 : {
607 122 : GF_TextSample *s = gf_isom_new_text_sample();
608 :
609 : /*empty sample*/
610 122 : if (!bs || !gf_bs_available(bs)) return s;
611 :
612 122 : s->len = gf_bs_read_u16(bs);
613 122 : if (s->len) {
614 : /*2 extra bytes for UTF-16 term char just in case (we don't know if a BOM marker is present or
615 : not since this may be a sample carried over RTP*/
616 68 : s->text = (char *) gf_malloc(sizeof(char)*(s->len+2) );
617 68 : if (!s->text) return NULL;
618 68 : s->text[s->len] = 0;
619 68 : s->text[s->len+1] = 0;
620 68 : gf_bs_read_data(bs, s->text, s->len);
621 : }
622 :
623 162 : while (gf_bs_available(bs)) {
624 : GF_Box *a;
625 40 : GF_Err e = gf_isom_box_parse(&a, bs);
626 40 : if (e) break;
627 :
628 40 : switch (a->type) {
629 39 : case GF_ISOM_BOX_TYPE_STYL:
630 39 : if (s->styles) {
631 : GF_TextStyleBox *st2 = (GF_TextStyleBox *)a;
632 0 : if (!s->styles->entry_count) {
633 0 : gf_isom_box_del((GF_Box*)s->styles);
634 0 : s->styles = st2;
635 : } else {
636 0 : s->styles->styles = (GF_StyleRecord*)gf_realloc(s->styles->styles, sizeof(GF_StyleRecord) * (s->styles->entry_count + st2->entry_count));
637 0 : memcpy(&s->styles->styles[s->styles->entry_count], st2->styles, sizeof(GF_StyleRecord) * st2->entry_count);
638 0 : s->styles->entry_count += st2->entry_count;
639 0 : gf_isom_box_del(a);
640 : }
641 : } else {
642 39 : s->styles = (GF_TextStyleBox*)a;
643 : }
644 : break;
645 0 : case GF_ISOM_BOX_TYPE_KROK:
646 0 : s->cur_karaoke = (GF_TextKaraokeBox*)a;
647 1 : case GF_ISOM_BOX_TYPE_HLIT:
648 : case GF_ISOM_BOX_TYPE_HREF:
649 : case GF_ISOM_BOX_TYPE_BLNK:
650 1 : gf_list_add(s->others, a);
651 1 : break;
652 0 : case GF_ISOM_BOX_TYPE_HCLR:
653 0 : if (s->highlight_color) gf_isom_box_del(a);
654 0 : else s->highlight_color = (GF_TextHighlightColorBox *) a;
655 : break;
656 0 : case GF_ISOM_BOX_TYPE_DLAY:
657 0 : if (s->scroll_delay) gf_isom_box_del(a);
658 0 : else s->scroll_delay= (GF_TextScrollDelayBox*) a;
659 : break;
660 0 : case GF_ISOM_BOX_TYPE_TBOX:
661 0 : if (s->box) gf_isom_box_del(a);
662 0 : else s->box= (GF_TextBoxBox *) a;
663 : break;
664 0 : case GF_ISOM_BOX_TYPE_TWRP:
665 0 : if (s->wrap) gf_isom_box_del(a);
666 0 : else s->wrap= (GF_TextWrapBox*) a;
667 : break;
668 0 : default:
669 0 : gf_isom_box_del(a);
670 0 : break;
671 : }
672 : }
673 : return s;
674 : }
675 :
676 : #if 0 //unused
677 : GF_TextSample *gf_isom_parse_text_sample_from_data(u8 *data, u32 dataLength)
678 : {
679 : GF_TextSample *s;
680 : GF_BitStream *bs;
681 : /*empty text sample*/
682 : if (!data || !dataLength) {
683 : return gf_isom_new_text_sample();
684 : }
685 :
686 : bs = gf_bs_new(data, dataLength, GF_BITSTREAM_READ);
687 : s = gf_isom_parse_text_sample(bs);
688 : gf_bs_del(bs);
689 : return s;
690 : }
691 : #endif
692 :
693 :
694 : /*out-of-band sample desc (128 and 255 reserved in RFC)*/
695 : #define SAMPLE_INDEX_OFFSET 129
696 :
697 :
698 34 : static void gf_isom_write_tx3g(GF_Tx3gSampleEntryBox *a, GF_BitStream *bs, u32 sidx, u32 sidx_offset)
699 : {
700 : u32 size, j, fount_count;
701 34 : Bool is_qt_text = (a->type==GF_ISOM_BOX_TYPE_TEXT) ? GF_TRUE : GF_FALSE;
702 : const char *qt_fontname = NULL;
703 : void gpp_write_rgba(GF_BitStream *bs, u32 col);
704 : void gpp_write_box(GF_BitStream *bs, GF_BoxRecord *rec);
705 : void gpp_write_style(GF_BitStream *bs, GF_StyleRecord *rec);
706 :
707 :
708 34 : if (sidx_offset) gf_bs_write_u8(bs, sidx + sidx_offset);
709 :
710 : /*SINCE WINCE HAS A READONLY VERSION OF MP4 WE MUST DO IT BY HAND*/
711 : size = 8 + 18 + 8 + 12;
712 : size += 8 + 2;
713 : fount_count = 0;
714 34 : if (is_qt_text) {
715 : GF_TextSampleEntryBox *qt = (GF_TextSampleEntryBox *)a;
716 0 : if (qt->textName) {
717 : qt_fontname = qt->textName;
718 : fount_count = 1;
719 : }
720 : } else {
721 34 : if (a->font_table) {
722 34 : fount_count = a->font_table->entry_count;
723 68 : for (j=0; j<fount_count; j++) {
724 34 : size += 3;
725 34 : if (a->font_table->fonts[j].fontName) size += (u32) strlen(a->font_table->fonts[j].fontName);
726 : }
727 : }
728 : }
729 : /*write TextSampleEntry box*/
730 34 : gf_bs_write_u32(bs, size);
731 34 : gf_bs_write_u32(bs, a->type);
732 34 : gf_bs_write_data(bs, a->reserved, 6);
733 34 : gf_bs_write_u16(bs, a->dataReferenceIndex);
734 34 : gf_bs_write_u32(bs, a->displayFlags);
735 34 : gf_bs_write_u8(bs, a->horizontal_justification);
736 34 : gf_bs_write_u8(bs, a->vertical_justification);
737 34 : gpp_write_rgba(bs, a->back_color);
738 34 : gpp_write_box(bs, &a->default_box);
739 34 : gpp_write_style(bs, &a->default_style);
740 : /*write font table box*/
741 34 : size -= (8 + 18 + 8 + 12);
742 34 : gf_bs_write_u32(bs, size);
743 34 : gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_FTAB);
744 :
745 34 : gf_bs_write_u16(bs, fount_count);
746 68 : for (j=0; j<fount_count; j++) {
747 34 : if (is_qt_text) {
748 0 : gf_bs_write_u16(bs, 0);
749 0 : if (qt_fontname) {
750 0 : u32 len = (u32) strlen(qt_fontname);
751 0 : gf_bs_write_u8(bs, len);
752 0 : gf_bs_write_data(bs, qt_fontname, len);
753 : } else {
754 0 : gf_bs_write_u8(bs, 0);
755 : }
756 : } else {
757 34 : gf_bs_write_u16(bs, a->font_table->fonts[j].fontID);
758 34 : if (a->font_table->fonts[j].fontName) {
759 34 : u32 len = (u32) strlen(a->font_table->fonts[j].fontName);
760 34 : gf_bs_write_u8(bs, len);
761 34 : gf_bs_write_data(bs, a->font_table->fonts[j].fontName, len);
762 : } else {
763 0 : gf_bs_write_u8(bs, 0);
764 : }
765 : }
766 : }
767 34 : }
768 :
769 3 : GF_Err gf_isom_get_ttxt_esd(GF_MediaBox *mdia, GF_ESD **out_esd)
770 : {
771 : GF_BitStream *bs;
772 : u32 count, i;
773 : Bool has_v_info;
774 : GF_List *sampleDesc;
775 : GF_ESD *esd;
776 : GF_TrackBox *tk;
777 :
778 3 : *out_esd = NULL;
779 3 : sampleDesc = mdia->information->sampleTable->SampleDescription->child_boxes;
780 3 : count = gf_list_count(sampleDesc);
781 3 : if (!count) return GF_ISOM_INVALID_MEDIA;
782 :
783 3 : esd = gf_odf_desc_esd_new(2);
784 3 : esd->decoderConfig->streamType = GF_STREAM_TEXT;
785 3 : esd->decoderConfig->objectTypeIndication = GF_CODECID_TEXT_MPEG4;
786 :
787 3 : bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
788 :
789 :
790 : /*Base3GPPFormat*/
791 3 : gf_bs_write_u8(bs, 0x10);
792 : /*MPEGExtendedFormat*/
793 3 : gf_bs_write_u8(bs, 0x10);
794 : /*profileLevel*/
795 3 : gf_bs_write_u8(bs, 0x10);
796 3 : gf_bs_write_u24(bs, mdia->mediaHeader->timeScale);
797 3 : gf_bs_write_int(bs, 0, 1); /*no alt formats*/
798 3 : gf_bs_write_int(bs, 2, 2); /*only out-of-band-band sample desc*/
799 3 : gf_bs_write_int(bs, 1, 1); /*we will write sample desc*/
800 :
801 : /*write v info if any visual track in this movie*/
802 : has_v_info = 0;
803 3 : i=0;
804 18 : while ((tk = (GF_TrackBox*)gf_list_enum(mdia->mediaTrack->moov->trackList, &i))) {
805 12 : if (tk->Media->handler && (tk->Media->handler->handlerType == GF_ISOM_MEDIA_VISUAL)) {
806 : has_v_info = 1;
807 : }
808 : }
809 3 : gf_bs_write_int(bs, has_v_info, 1);
810 :
811 3 : gf_bs_write_int(bs, 0, 3); /*reserved, spec doesn't say the values*/
812 3 : gf_bs_write_u8(bs, mdia->mediaTrack->Header->layer);
813 3 : gf_bs_write_u16(bs, mdia->mediaTrack->Header->width>>16);
814 3 : gf_bs_write_u16(bs, mdia->mediaTrack->Header->height>>16);
815 :
816 : /*write desc*/
817 3 : gf_bs_write_u8(bs, count);
818 6 : for (i=0; i<count; i++) {
819 : GF_Tx3gSampleEntryBox *a;
820 3 : a = (GF_Tx3gSampleEntryBox *) gf_list_get(sampleDesc, i);
821 3 : if ((a->type != GF_ISOM_BOX_TYPE_TX3G) && (a->type != GF_ISOM_BOX_TYPE_TEXT) ) continue;
822 3 : gf_isom_write_tx3g(a, bs, i+1, SAMPLE_INDEX_OFFSET);
823 : }
824 3 : if (has_v_info) {
825 : u32 trans;
826 : /*which video shall we pick for MPEG-4, and how is the associations indicated in 3GP ???*/
827 3 : gf_bs_write_u16(bs, 0);
828 3 : gf_bs_write_u16(bs, 0);
829 3 : trans = mdia->mediaTrack->Header->matrix[6];
830 3 : trans >>= 16;
831 3 : gf_bs_write_u16(bs, trans);
832 3 : trans = mdia->mediaTrack->Header->matrix[7];
833 3 : trans >>= 16;
834 3 : gf_bs_write_u16(bs, trans);
835 : }
836 :
837 3 : gf_bs_get_content(bs, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength);
838 3 : gf_bs_del(bs);
839 3 : *out_esd = esd;
840 3 : return GF_OK;
841 : }
842 :
843 11 : GF_Err gf_isom_rewrite_text_sample(GF_ISOSample *samp, u32 sampleDescriptionIndex, u32 sample_dur)
844 : {
845 : GF_BitStream *bs;
846 : u32 pay_start, txt_size;
847 : Bool is_utf_16 = 0;
848 11 : if (!samp || !samp->data || !samp->dataLength) return GF_OK;
849 :
850 11 : bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_READ);
851 11 : txt_size = gf_bs_read_u16(bs);
852 11 : gf_bs_del(bs);
853 :
854 : /*remove BOM*/
855 : pay_start = 2;
856 11 : if (txt_size>2) {
857 : /*seems 3GP only accepts BE UTF-16 (no LE, no UTF32)*/
858 5 : if (((u8) samp->data[2]==(u8) 0xFE) && ((u8)samp->data[3]==(u8) 0xFF)) {
859 : is_utf_16 = 1;
860 : pay_start = 4;
861 0 : txt_size -= 2;
862 : }
863 : }
864 :
865 : /*rewrite as TTU(1)*/
866 11 : bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
867 11 : gf_bs_write_int(bs, is_utf_16, 1);
868 11 : gf_bs_write_int(bs, 0, 4);
869 11 : gf_bs_write_int(bs, 1, 3);
870 11 : gf_bs_write_u16(bs, 8 + samp->dataLength - pay_start);
871 11 : gf_bs_write_u8(bs, sampleDescriptionIndex + SAMPLE_INDEX_OFFSET);
872 11 : gf_bs_write_u24(bs, sample_dur);
873 : /*write text size*/
874 11 : gf_bs_write_u16(bs, txt_size);
875 11 : if (txt_size) gf_bs_write_data(bs, samp->data + pay_start, samp->dataLength - pay_start);
876 :
877 11 : gf_free(samp->data);
878 11 : samp->data = NULL;
879 11 : gf_bs_get_content(bs, &samp->data, &samp->dataLength);
880 11 : gf_bs_del(bs);
881 11 : return GF_OK;
882 : }
883 :
884 :
885 31 : GF_Err gf_isom_text_get_encoded_tx3g(GF_ISOFile *file, u32 track, u32 sidx, u32 sidx_offset, u8 **tx3g, u32 *tx3g_size)
886 : {
887 : GF_BitStream *bs;
888 : GF_TrackBox *trak;
889 : GF_Tx3gSampleEntryBox *a;
890 :
891 31 : trak = gf_isom_get_track_from_file(file, track);
892 31 : if (!trak) return GF_BAD_PARAM;
893 :
894 31 : a = (GF_Tx3gSampleEntryBox *) gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sidx-1);
895 31 : if (!a) return GF_BAD_PARAM;
896 31 : if ((a->type != GF_ISOM_BOX_TYPE_TX3G) && (a->type != GF_ISOM_BOX_TYPE_TEXT)) return GF_BAD_PARAM;
897 :
898 31 : bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
899 31 : gf_isom_write_tx3g(a, bs, sidx, sidx_offset);
900 31 : *tx3g = NULL;
901 31 : *tx3g_size = 0;
902 31 : gf_bs_get_content(bs, tx3g, tx3g_size);
903 31 : gf_bs_del(bs);
904 31 : return GF_OK;
905 : }
906 :
907 : #endif /*GPAC_DISABLE_ISOM*/
|