/src/gpac/src/scene_manager/text_to_bifs.c
Line | Count | Source |
1 | | /* |
2 | | * GPAC - Multimedia Framework C SDK |
3 | | * |
4 | | * Authors: Jean Le Feuvre |
5 | | * Copyright (c) Telecom ParisTech 2000-2026 |
6 | | * All rights reserved |
7 | | * |
8 | | * This file is part of GPAC / Scene Management 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 | | |
27 | | |
28 | | #include <gpac/constants.h> |
29 | | #include <gpac/utf.h> |
30 | | #include <gpac/xml.h> |
31 | | #include <gpac/internal/media_dev.h> |
32 | | #include <gpac/scene_manager.h> |
33 | | |
34 | | enum |
35 | | { |
36 | | GF_TEXT_IMPORT_NONE = 0, |
37 | | GF_TEXT_IMPORT_SRT, |
38 | | GF_TEXT_IMPORT_SUB, |
39 | | GF_TEXT_IMPORT_TTXT, |
40 | | GF_TEXT_IMPORT_TEXML, |
41 | | }; |
42 | | |
43 | 0 | #define REM_TRAIL_MARKS(__str, __sep) while (1) { \ |
44 | 0 | u32 _len = (u32) strlen(__str); \ |
45 | 0 | if (!_len) break; \ |
46 | 0 | _len--; \ |
47 | 0 | if (strchr(__sep, __str[_len])) __str[_len] = 0; \ |
48 | 0 | else break; \ |
49 | 0 | } \ |
50 | | |
51 | | |
52 | | static GF_Err gf_text_guess_format(char *filename, u32 *fmt) |
53 | 0 | { |
54 | 0 | char szLine[2048], szTest[10]; |
55 | 0 | u32 val; |
56 | 0 | FILE *test = gf_fopen(filename, "rt"); |
57 | 0 | if (!test) return GF_URL_ERROR; |
58 | | |
59 | 0 | while (gf_fgets(szLine, 2048, test) != NULL) { |
60 | 0 | REM_TRAIL_MARKS(szLine, "\r\n\t ") |
61 | |
|
62 | 0 | if (strlen(szLine)) break; |
63 | 0 | } |
64 | 0 | *fmt = GF_TEXT_IMPORT_NONE; |
65 | 0 | if ((szLine[0]=='{') && strstr(szLine, "}{")) *fmt = GF_TEXT_IMPORT_SUB; |
66 | 0 | else if (sscanf(szLine, "%u", &val)==1) { |
67 | 0 | sprintf(szTest, "%u", val); |
68 | 0 | if (!strcmp(szTest, szLine)) *fmt = GF_TEXT_IMPORT_SRT; |
69 | 0 | } |
70 | 0 | else if (!strnicmp(szLine, "<?xml ", 6)) { |
71 | 0 | char *ext = gf_file_ext_start(filename); |
72 | 0 | if (!strnicmp(ext, ".ttxt", 5)) *fmt = GF_TEXT_IMPORT_TTXT; |
73 | 0 | ext = strstr(szLine, "?>"); |
74 | 0 | if (ext) ext += 2; |
75 | 0 | if (ext && !ext[0]) { |
76 | 0 | if (!gf_fgets(szLine, 2048, test)) |
77 | 0 | szLine[0] = '\0'; |
78 | 0 | } |
79 | 0 | if (strstr(szLine, "x-quicktime-tx3g")) *fmt = GF_TEXT_IMPORT_TEXML; |
80 | 0 | } |
81 | 0 | gf_fclose(test); |
82 | 0 | return GF_OK; |
83 | 0 | } |
84 | | |
85 | | #ifndef GPAC_DISABLE_VRML |
86 | | |
87 | | static GF_Err gf_text_import_srt_bifs(GF_SceneManager *ctx, GF_ESD *src, GF_MuxInfo *mux) |
88 | 0 | { |
89 | 0 | GF_Err e; |
90 | 0 | GF_Node *text, *font; |
91 | 0 | GF_StreamContext *srt; |
92 | 0 | FILE *srt_in; |
93 | 0 | GF_FieldInfo string, style; |
94 | 0 | u32 sh, sm, ss, sms, eh, em, es, ems, start, end; |
95 | 0 | GF_AUContext *au; |
96 | 0 | GF_Command *com; |
97 | 0 | SFString *sfstr; |
98 | 0 | GF_CommandField *inf; |
99 | 0 | Bool italic, underlined, bold; |
100 | 0 | u32 state, curLine, line, i, len; |
101 | 0 | char szLine[2048], szText[2048], *ptr; |
102 | 0 | GF_StreamContext *sc = NULL; |
103 | |
|
104 | 0 | if (!ctx->scene_graph) { |
105 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] base scene not assigned\n")); |
106 | 0 | return GF_BAD_PARAM; |
107 | 0 | } |
108 | 0 | i=0; |
109 | 0 | while ((sc = (GF_StreamContext*)gf_list_enum(ctx->streams, &i))) { |
110 | 0 | if (sc->streamType==GF_STREAM_SCENE) break; |
111 | 0 | sc = NULL; |
112 | 0 | } |
113 | |
|
114 | 0 | if (!sc) { |
115 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] cannot locate base scene\n")); |
116 | 0 | return GF_BAD_PARAM; |
117 | 0 | } |
118 | 0 | if (!mux->textNode) { |
119 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] Target text node unspecified\n")); |
120 | 0 | return GF_BAD_PARAM; |
121 | 0 | } |
122 | 0 | text = gf_sg_find_node_by_name(ctx->scene_graph, mux->textNode); |
123 | 0 | if (!text) { |
124 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] cannot find target text node %s\n", mux->textNode)); |
125 | 0 | return GF_BAD_PARAM; |
126 | 0 | } |
127 | 0 | if (gf_node_get_field_by_name(text, "string", &string) != GF_OK) { |
128 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] Target text node %s doesn't look like text\n", mux->textNode)); |
129 | 0 | return GF_BAD_PARAM; |
130 | 0 | } |
131 | | |
132 | 0 | font = NULL; |
133 | 0 | if (mux->fontNode) { |
134 | 0 | font = gf_sg_find_node_by_name(ctx->scene_graph, mux->fontNode); |
135 | 0 | if (!font) { |
136 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] cannot find target font node %s\n", mux->fontNode)); |
137 | 0 | return GF_BAD_PARAM; |
138 | 0 | } |
139 | 0 | if (gf_node_get_field_by_name(font, "style", &style) != GF_OK) { |
140 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] Target font node %s doesn't look like font\n", mux->fontNode)); |
141 | 0 | return GF_BAD_PARAM; |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | 0 | srt_in = gf_fopen(mux->file_name, "rt"); |
146 | 0 | if (!srt_in) { |
147 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] cannot open input file %s\n", mux->file_name)); |
148 | 0 | return GF_URL_ERROR; |
149 | 0 | } |
150 | | |
151 | 0 | srt = gf_sm_stream_new(ctx, src->ESID, GF_STREAM_SCENE, GF_CODECID_BIFS); |
152 | 0 | if (!srt) return GF_OUT_OF_MEM; |
153 | | |
154 | 0 | if (!src->slConfig) src->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); |
155 | 0 | src->slConfig->timestampResolution = 1000; |
156 | 0 | if (!src->decoderConfig) src->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); |
157 | 0 | src->decoderConfig->streamType = GF_STREAM_SCENE; |
158 | 0 | src->decoderConfig->objectTypeIndication = GF_CODECID_BIFS; |
159 | |
|
160 | 0 | e = GF_OK; |
161 | 0 | state = end = 0; |
162 | 0 | curLine = 0; |
163 | 0 | au = NULL; |
164 | 0 | com = NULL; |
165 | 0 | italic = underlined = bold = 0; |
166 | 0 | inf = NULL; |
167 | |
|
168 | 0 | while (1) { |
169 | 0 | char *sOK = gf_fgets(szLine, 2048, srt_in); |
170 | |
|
171 | 0 | if (sOK) REM_TRAIL_MARKS(szLine, "\r\n\t ") |
172 | |
|
173 | 0 | if (!sOK || !strlen(szLine)) { |
174 | 0 | state = 0; |
175 | 0 | if (au) { |
176 | | /*if italic or underscore do it*/ |
177 | 0 | if (font && (italic || underlined || bold)) { |
178 | 0 | com = gf_sg_command_new(ctx->scene_graph, GF_SG_FIELD_REPLACE); |
179 | 0 | com->node = font; |
180 | 0 | gf_node_register(font, NULL); |
181 | 0 | inf = gf_sg_command_field_new(com); |
182 | 0 | inf->fieldIndex = style.fieldIndex; |
183 | 0 | inf->fieldType = style.fieldType; |
184 | 0 | inf->field_ptr = gf_sg_vrml_field_pointer_new(style.fieldType); |
185 | 0 | sfstr = (SFString *)inf->field_ptr; |
186 | 0 | if (bold && italic && underlined) sfstr->buffer = gf_strdup("BOLDITALIC UNDERLINED"); |
187 | 0 | else if (italic && underlined) sfstr->buffer = gf_strdup("ITALIC UNDERLINED"); |
188 | 0 | else if (bold && underlined) sfstr->buffer = gf_strdup("BOLD UNDERLINED"); |
189 | 0 | else if (underlined) sfstr->buffer = gf_strdup("UNDERLINED"); |
190 | 0 | else if (bold && italic) sfstr->buffer = gf_strdup("BOLDITALIC"); |
191 | 0 | else if (bold) sfstr->buffer = gf_strdup("BOLD"); |
192 | 0 | else sfstr->buffer = gf_strdup("ITALIC"); |
193 | 0 | gf_list_add(au->commands, com); |
194 | 0 | } |
195 | |
|
196 | 0 | au = gf_sm_stream_au_new(srt, end, 0, 1); |
197 | 0 | com = gf_sg_command_new(ctx->scene_graph, GF_SG_FIELD_REPLACE); |
198 | 0 | com->node = text; |
199 | 0 | gf_node_register(text, NULL); |
200 | 0 | inf = gf_sg_command_field_new(com); |
201 | 0 | inf->fieldIndex = string.fieldIndex; |
202 | 0 | inf->fieldType = string.fieldType; |
203 | 0 | inf->field_ptr = gf_sg_vrml_field_pointer_new(string.fieldType); |
204 | 0 | gf_list_add(au->commands, com); |
205 | | /*reset font styles so that all AUs are true random access*/ |
206 | 0 | if (font) { |
207 | 0 | com = gf_sg_command_new(ctx->scene_graph, GF_SG_FIELD_REPLACE); |
208 | 0 | com->node = font; |
209 | 0 | gf_node_register(font, NULL); |
210 | 0 | inf = gf_sg_command_field_new(com); |
211 | 0 | inf->fieldIndex = style.fieldIndex; |
212 | 0 | inf->fieldType = style.fieldType; |
213 | 0 | inf->field_ptr = gf_sg_vrml_field_pointer_new(style.fieldType); |
214 | 0 | gf_list_add(au->commands, com); |
215 | 0 | } |
216 | 0 | au = NULL; |
217 | 0 | } |
218 | 0 | inf = NULL; |
219 | 0 | if (!sOK) break; |
220 | 0 | continue; |
221 | 0 | } |
222 | | |
223 | 0 | if (!gf_utf8_is_legal(sOK, (u32)strlen(szLine))) { |
224 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] Illegal UTF8 data\n")); |
225 | 0 | e = GF_CORRUPTED_DATA; |
226 | 0 | goto exit; |
227 | 0 | } |
228 | | |
229 | 0 | switch (state) { |
230 | 0 | case 0: |
231 | 0 | if (sscanf(szLine, "%u", &line) != 1) { |
232 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] bad frame format (src: %s)\n", szLine)); |
233 | 0 | e = GF_CORRUPTED_DATA; |
234 | 0 | goto exit; |
235 | 0 | } |
236 | 0 | if (line != curLine + 1) { |
237 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] bad frame: previous %d - current %d (src: %s)\n", curLine, line, szLine)); |
238 | 0 | e = GF_CORRUPTED_DATA; |
239 | 0 | goto exit; |
240 | 0 | } |
241 | 0 | curLine = line; |
242 | 0 | state = 1; |
243 | 0 | break; |
244 | 0 | case 1: |
245 | 0 | if (sscanf(szLine, "%u:%u:%u,%u --> %u:%u:%u,%u", &sh, &sm, &ss, &sms, &eh, &em, &es, &ems) != 8) { |
246 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] bad frame %u (src: %s)\n", curLine, szLine)); |
247 | 0 | e = GF_CORRUPTED_DATA; |
248 | 0 | goto exit; |
249 | 0 | } |
250 | 0 | start = (3600*sh + 60*sm + ss)*1000 + sms; |
251 | 0 | if (start<end) { |
252 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[srt->bifs] corrupted frame starts before end of previous one (SRT Frame %d) - adjusting time stamps\n", curLine)); |
253 | 0 | start = end; |
254 | 0 | } |
255 | 0 | end = (3600*eh + 60*em + es)*1000 + ems; |
256 | | /*make stream start at 0 by inserting a fake AU*/ |
257 | 0 | if ((curLine==1) && start>0) { |
258 | 0 | au = gf_sm_stream_au_new(srt, 0, 0, 1); |
259 | 0 | com = gf_sg_command_new(ctx->scene_graph, GF_SG_FIELD_REPLACE); |
260 | 0 | com->node = text; |
261 | 0 | gf_node_register(text, NULL); |
262 | 0 | inf = gf_sg_command_field_new(com); |
263 | 0 | inf->fieldIndex = string.fieldIndex; |
264 | 0 | inf->fieldType = string.fieldType; |
265 | 0 | inf->field_ptr = gf_sg_vrml_field_pointer_new(string.fieldType); |
266 | 0 | gf_list_add(au->commands, com); |
267 | 0 | } |
268 | |
|
269 | 0 | au = gf_sm_stream_au_new(srt, start, 0, 1); |
270 | 0 | com = NULL; |
271 | 0 | state = 2; |
272 | 0 | italic = underlined = bold = 0; |
273 | 0 | break; |
274 | | |
275 | 0 | default: |
276 | 0 | ptr = szLine; |
277 | | /*FIXME - other styles posssibles ??*/ |
278 | 0 | while (1) { |
279 | 0 | if (!strnicmp(ptr, "<i>", 3)) { |
280 | 0 | italic = 1; |
281 | 0 | ptr += 3; |
282 | 0 | } |
283 | 0 | else if (!strnicmp(ptr, "<u>", 3)) { |
284 | 0 | underlined = 1; |
285 | 0 | ptr += 3; |
286 | 0 | } |
287 | 0 | else if (!strnicmp(ptr, "<b>", 3)) { |
288 | 0 | bold = 1; |
289 | 0 | ptr += 3; |
290 | 0 | } |
291 | 0 | else |
292 | 0 | break; |
293 | 0 | } |
294 | | /*if style remove end markers*/ |
295 | 0 | while ((strlen(ptr)>4) && (ptr[strlen(ptr) - 4] == '<') && (ptr[strlen(ptr) - 1] == '>')) { |
296 | 0 | ptr[strlen(ptr) - 4] = 0; |
297 | 0 | } |
298 | |
|
299 | 0 | if (!com) { |
300 | 0 | com = gf_sg_command_new(ctx->scene_graph, GF_SG_FIELD_REPLACE); |
301 | 0 | com->node = text; |
302 | 0 | gf_node_register(text, NULL); |
303 | 0 | inf = gf_sg_command_field_new(com); |
304 | 0 | inf->fieldIndex = string.fieldIndex; |
305 | 0 | inf->fieldType = string.fieldType; |
306 | 0 | inf->field_ptr = gf_sg_vrml_field_pointer_new(string.fieldType); |
307 | 0 | gf_list_add(au->commands, com); |
308 | 0 | } |
309 | 0 | if (!inf) { |
310 | 0 | gf_assert(0); |
311 | 0 | e = GF_NON_COMPLIANT_BITSTREAM; |
312 | 0 | goto exit; |
313 | 0 | } |
314 | 0 | gf_sg_vrml_mf_append(inf->field_ptr, GF_SG_VRML_MFSTRING, (void **) &sfstr); |
315 | 0 | len = 0; |
316 | 0 | for (i=0; i<strlen(ptr); i++) { |
317 | | /*FIXME - UTF16 support !!*/ |
318 | 0 | if (len >= GF_ARRAY_LENGTH(szText)) break; |
319 | 0 | if (ptr[i] & 0x80) { |
320 | | /*non UTF8 (likely some win-CP)*/ |
321 | 0 | if ((ptr[i+1] & 0xc0) != 0x80) { |
322 | 0 | szText[len] = 0xc0 | ( (ptr[i] >> 6) & 0x3 ); |
323 | 0 | len++; |
324 | 0 | ptr[i] &= 0xbf; |
325 | 0 | } |
326 | | /*we only handle UTF8 chars on 2 bytes (eg first byte is 0b110xxxxx)*/ |
327 | 0 | else if ((ptr[i] & 0xe0) == 0xc0) { |
328 | 0 | szText[len] = ptr[i]; |
329 | 0 | len++; |
330 | 0 | i++; |
331 | 0 | } |
332 | 0 | } |
333 | 0 | if (len >= GF_ARRAY_LENGTH(szText)) break; |
334 | 0 | szText[len] = ptr[i]; |
335 | 0 | len++; |
336 | 0 | } |
337 | 0 | szText[MIN(len, GF_ARRAY_LENGTH(szText)-1)] = 0; |
338 | 0 | sfstr->buffer = gf_strdup(szText); |
339 | 0 | break; |
340 | 0 | } |
341 | 0 | } |
342 | | |
343 | 0 | exit: |
344 | 0 | gf_fclose(srt_in); |
345 | 0 | return e; |
346 | 0 | } |
347 | | #endif /*GPAC_DISABLE_VRML*/ |
348 | | |
349 | | |
350 | | #ifndef GPAC_DISABLE_VRML |
351 | | |
352 | | static GF_Err gf_text_import_sub_bifs(GF_SceneManager *ctx, GF_ESD *src, GF_MuxInfo *mux) |
353 | 0 | { |
354 | 0 | GF_Err e; |
355 | 0 | GF_Node *text, *font; |
356 | 0 | GF_StreamContext *srt; |
357 | 0 | FILE *sub_in; |
358 | 0 | GF_FieldInfo string, style; |
359 | 0 | u32 start, end, line, i, j, k, len; |
360 | 0 | GF_AUContext *au; |
361 | 0 | GF_Command *com; |
362 | 0 | SFString *sfstr; |
363 | 0 | GF_CommandField *inf; |
364 | 0 | Bool first_samp; |
365 | 0 | char szLine[2048], szTime[30], szText[2048]; |
366 | 0 | GF_StreamContext *sc = NULL; |
367 | |
|
368 | 0 | if (!ctx->scene_graph) { |
369 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] base scene not assigned\n")); |
370 | 0 | return GF_BAD_PARAM; |
371 | 0 | } |
372 | 0 | i=0; |
373 | 0 | while ((sc = (GF_StreamContext*)gf_list_enum(ctx->streams, &i))) { |
374 | 0 | if (sc->streamType==GF_STREAM_SCENE) break; |
375 | 0 | sc = NULL; |
376 | 0 | } |
377 | |
|
378 | 0 | if (!sc) { |
379 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] cannot locate base scene\n")); |
380 | 0 | return GF_BAD_PARAM; |
381 | 0 | } |
382 | 0 | if (!mux->textNode) { |
383 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] target text node unspecified\n")); |
384 | 0 | return GF_BAD_PARAM; |
385 | 0 | } |
386 | 0 | text = gf_sg_find_node_by_name(ctx->scene_graph, mux->textNode); |
387 | 0 | if (!text) { |
388 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] cannot find target text node %s\n", mux->textNode)); |
389 | 0 | return GF_BAD_PARAM; |
390 | 0 | } |
391 | 0 | if (gf_node_get_field_by_name(text, "string", &string) != GF_OK) { |
392 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] target text node %s doesn't look like text\n", mux->textNode)); |
393 | 0 | return GF_BAD_PARAM; |
394 | 0 | } |
395 | | |
396 | 0 | font = NULL; |
397 | 0 | if (mux->fontNode) { |
398 | 0 | font = gf_sg_find_node_by_name(ctx->scene_graph, mux->fontNode); |
399 | 0 | if (!font) { |
400 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] cannot find target font node %s\n", mux->fontNode)); |
401 | 0 | return GF_BAD_PARAM; |
402 | 0 | } |
403 | 0 | if (gf_node_get_field_by_name(font, "style", &style) != GF_OK) { |
404 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] target font node %s doesn't look like font\n", mux->fontNode)); |
405 | 0 | return GF_BAD_PARAM; |
406 | 0 | } |
407 | 0 | } |
408 | | |
409 | 0 | sub_in = gf_fopen(mux->file_name, "rt"); |
410 | 0 | if (!sub_in) { |
411 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] cannot open input file %s\n", mux->file_name)); |
412 | 0 | return GF_URL_ERROR; |
413 | 0 | } |
414 | | |
415 | 0 | srt = gf_sm_stream_new(ctx, src->ESID, GF_STREAM_SCENE, GF_CODECID_BIFS); |
416 | 0 | if (!srt) return GF_OUT_OF_MEM; |
417 | | |
418 | 0 | if (!src->slConfig) src->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); |
419 | 0 | src->slConfig->timestampResolution = 1000; |
420 | 0 | if (!src->decoderConfig) src->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); |
421 | 0 | src->decoderConfig->streamType = GF_STREAM_SCENE; |
422 | 0 | src->decoderConfig->objectTypeIndication = GF_CODECID_BIFS; |
423 | |
|
424 | 0 | e = GF_OK; |
425 | 0 | end = 0; |
426 | 0 | au = NULL; |
427 | 0 | com = NULL; |
428 | 0 | inf = NULL; |
429 | |
|
430 | 0 | line = 0; |
431 | 0 | first_samp = 1; |
432 | 0 | while (1) { |
433 | 0 | char *sOK = gf_fgets(szLine, 2048, sub_in); |
434 | 0 | if (!sOK) break; |
435 | 0 | REM_TRAIL_MARKS(szLine, "\r\n\t ") |
436 | |
|
437 | 0 | line++; |
438 | 0 | len = (u32) strlen(szLine); |
439 | 0 | if (!len) continue; |
440 | | |
441 | 0 | i=0; |
442 | 0 | if (szLine[i] != '{') { |
443 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] Bad frame (line %d): expecting \"{\" got \"%c\"\n", line, szLine[i])); |
444 | 0 | e = GF_NON_COMPLIANT_BITSTREAM; |
445 | 0 | break; |
446 | 0 | } |
447 | 0 | while (szLine[i+1] && szLine[i+1]!='}') { |
448 | 0 | if (i>=GF_ARRAY_LENGTH(szTime)) { |
449 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] Bad frame (line %d): expected \"}\" before %d chars after \"{\"\n", line, GF_ARRAY_LENGTH(szTime))); |
450 | 0 | e = GF_NON_COMPLIANT_BITSTREAM; |
451 | 0 | szTime[0] = 0; |
452 | 0 | goto exit; |
453 | 0 | } |
454 | 0 | szTime[i] = szLine[i+1]; |
455 | 0 | i++; |
456 | 0 | } |
457 | 0 | szTime[MIN(i, GF_ARRAY_LENGTH(szTime)-1)] = 0; |
458 | 0 | start = atoi(szTime); |
459 | 0 | if (start<end) { |
460 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[sub->bifs] corrupted SUB frame (line %d) - starts (at %d ms) before end of previous one (%d ms) - adjusting time stamps\n", line, start, end)); |
461 | 0 | start = end; |
462 | 0 | } |
463 | 0 | j=i+2; |
464 | 0 | i=0; |
465 | 0 | if (szLine[i+j] != '{') { |
466 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] Bad frame - expecting \"{\" got \"%c\"\n", szLine[i])); |
467 | 0 | e = GF_NON_COMPLIANT_BITSTREAM; |
468 | 0 | break; |
469 | 0 | } |
470 | 0 | while (szLine[i+1+j] && szLine[i+1+j]!='}') { |
471 | 0 | if (i>=GF_ARRAY_LENGTH(szTime)) { |
472 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] Bad frame (line %d): expected \"}\" before %d chars after \"{\"\n", line, GF_ARRAY_LENGTH(szTime))); |
473 | 0 | e = GF_NON_COMPLIANT_BITSTREAM; |
474 | 0 | szTime[0] = 0; |
475 | 0 | goto exit; |
476 | 0 | } |
477 | 0 | szTime[i] = szLine[i+1+j]; |
478 | 0 | i++; |
479 | 0 | } |
480 | 0 | szTime[MIN(i, GF_ARRAY_LENGTH(szTime)-1)] = 0; |
481 | 0 | end = atoi(szTime); |
482 | 0 | j+=i+2; |
483 | |
|
484 | 0 | if (start>end) { |
485 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[sub->bifs] corrupted frame (line %d) - ends (at %d ms) before start of current frame (%d ms) - skipping\n", line, end, start)); |
486 | 0 | continue; |
487 | 0 | } |
488 | | |
489 | 0 | if (start && first_samp) { |
490 | 0 | au = gf_sm_stream_au_new(srt, 0, 0, 1); |
491 | 0 | com = gf_sg_command_new(ctx->scene_graph, GF_SG_FIELD_REPLACE); |
492 | 0 | com->node = text; |
493 | 0 | gf_node_register(text, NULL); |
494 | 0 | inf = gf_sg_command_field_new(com); |
495 | 0 | inf->fieldIndex = string.fieldIndex; |
496 | 0 | inf->fieldType = string.fieldType; |
497 | 0 | inf->field_ptr = gf_sg_vrml_field_pointer_new(string.fieldType); |
498 | 0 | gf_list_add(au->commands, com); |
499 | 0 | } |
500 | |
|
501 | 0 | k=0; |
502 | 0 | for (i=j; i<len; i++) { |
503 | 0 | if (szLine[i]=='|') { |
504 | 0 | szText[k] = '\n'; |
505 | 0 | } else { |
506 | 0 | if (szLine[i] & 0x80) { |
507 | | /*non UTF8 (likely some win-CP)*/ |
508 | 0 | if ( (szLine[i+1] & 0xc0) != 0x80) { |
509 | 0 | szText[k] = 0xc0 | ( (szLine[i] >> 6) & 0x3 ); |
510 | 0 | k++; |
511 | 0 | szLine[i] &= 0xbf; |
512 | 0 | } |
513 | | /*we only handle UTF8 chars on 2 bytes (eg first byte is 0b110xxxxx)*/ |
514 | 0 | else if ( (szLine[i] & 0xe0) == 0xc0) { |
515 | 0 | szText[k] = szLine[i]; |
516 | 0 | i++; |
517 | 0 | k++; |
518 | 0 | } |
519 | 0 | } |
520 | 0 | szText[k] = szLine[i]; |
521 | 0 | } |
522 | 0 | k++; |
523 | 0 | } |
524 | 0 | szText[i-j] = 0; |
525 | |
|
526 | 0 | if (au) { |
527 | 0 | com = gf_sg_command_new(ctx->scene_graph, GF_SG_FIELD_REPLACE); |
528 | 0 | com->node = text; |
529 | 0 | gf_node_register(text, NULL); |
530 | 0 | inf = gf_sg_command_field_new(com); |
531 | 0 | inf->fieldIndex = string.fieldIndex; |
532 | 0 | inf->fieldType = string.fieldType; |
533 | 0 | inf->field_ptr = gf_sg_vrml_field_pointer_new(string.fieldType); |
534 | 0 | gf_list_add(au->commands, com); |
535 | |
|
536 | 0 | gf_sg_vrml_mf_append(inf->field_ptr, GF_SG_VRML_MFSTRING, (void **) &sfstr); |
537 | 0 | sfstr->buffer = gf_strdup(szText); |
538 | 0 | } |
539 | 0 | } |
540 | 0 | exit: |
541 | 0 | gf_fclose(sub_in); |
542 | 0 | return e; |
543 | 0 | } |
544 | | #endif |
545 | | |
546 | | |
547 | | GF_EXPORT |
548 | | GF_Err gf_sm_import_bifs_subtitle(GF_SceneManager *ctx, GF_ESD *src, GF_MuxInfo *mux) |
549 | 0 | { |
550 | 0 | #ifndef GPAC_DISABLE_VRML |
551 | 0 | GF_Err e; |
552 | 0 | u32 fmt; |
553 | 0 | e = gf_text_guess_format(mux->file_name, &fmt); |
554 | 0 | if (e) return e; |
555 | 0 | if (!fmt || (fmt>=3)) return GF_NOT_SUPPORTED; |
556 | | |
557 | 0 | if (fmt==1) return gf_text_import_srt_bifs(ctx, src, mux); |
558 | 0 | else return gf_text_import_sub_bifs(ctx, src, mux); |
559 | | #else |
560 | | return GF_NOT_SUPPORTED; |
561 | | #endif |
562 | 0 | } |