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