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