Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / Scene Context loader filter
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/compositor_dev.h>
27 : #include <gpac/scene_manager.h>
28 : #include <gpac/constants.h>
29 : #include <gpac/network.h>
30 : #include <gpac/nodes_mpeg4.h>
31 :
32 : #if !defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_SCENEGRAPH)
33 :
34 : typedef struct
35 : {
36 : //opts
37 : Bool progressive;
38 : u32 sax_dur;
39 :
40 : //internal
41 : GF_FilterPid *in_pid, *out_pid;
42 :
43 : GF_Scene *scene;
44 : const char *file_name;
45 :
46 :
47 : GF_SceneManager *ctx;
48 : GF_SceneLoader load;
49 : u64 file_size;
50 : u32 load_flags;
51 : u32 nb_streams;
52 : u32 base_stream_id;
53 : u32 last_check_time;
54 : u64 last_check_size;
55 : /*mp3 import from flash*/
56 : GF_List *files_to_delete;
57 : /*progressive loading support for XMT X3D*/
58 : FILE *src;
59 : u32 file_pos;
60 :
61 : u64 pck_time;
62 : const char *service_url;
63 : Bool is_playing;
64 : } CTXLoadPriv;
65 :
66 :
67 : void ODS_SetupOD(GF_Scene *scene, GF_ObjectDescriptor *od);
68 :
69 :
70 275 : static void CTXLoad_ExecuteConditional(M_Conditional *c, GF_Scene *scene)
71 : {
72 275 : GF_List *clist = c->buffer.commandList;
73 275 : c->buffer.commandList = NULL;
74 :
75 275 : gf_sg_command_apply_list(gf_node_get_graph((GF_Node*)c), clist, gf_scene_get_time(scene));
76 :
77 275 : if (c->buffer.commandList != NULL) {
78 0 : while (gf_list_count(clist)) {
79 0 : GF_Command *sub_com = (GF_Command *)gf_list_get(clist, 0);
80 0 : gf_sg_command_del(sub_com);
81 0 : gf_list_rem(clist, 0);
82 : }
83 0 : gf_list_del(clist);
84 : } else {
85 275 : c->buffer.commandList = clist;
86 : }
87 275 : }
88 :
89 319 : static void CTXLoad_OnActivate(GF_Node *node, GF_Route *route)
90 : {
91 319 : GF_Scene *scene = (GF_Scene *) gf_node_get_private(node);
92 : M_Conditional*c = (M_Conditional*)node;
93 : /*always apply in parent graph to handle protos correctly*/
94 319 : if (c->activate) CTXLoad_ExecuteConditional(c, scene);
95 319 : }
96 :
97 208 : static void CTXLoad_OnReverseActivate(GF_Node *node, GF_Route *route)
98 : {
99 208 : GF_Scene *scene = (GF_Scene *) gf_node_get_private(node);
100 : M_Conditional*c = (M_Conditional*)node;
101 : /*always apply in parent graph to handle protos correctly*/
102 208 : if (!c->reverseActivate)
103 87 : CTXLoad_ExecuteConditional(c, scene);
104 208 : }
105 :
106 127949 : void CTXLoad_NodeCallback(void *cbk, GF_SGNodeCbkType type, GF_Node *node, void *param)
107 : {
108 128469 : if ((type==GF_SG_CALLBACK_INIT) && (gf_node_get_tag(node) == TAG_MPEG4_Conditional) ) {
109 : M_Conditional*c = (M_Conditional*)node;
110 520 : c->on_activate = CTXLoad_OnActivate;
111 520 : c->on_reverseActivate = CTXLoad_OnReverseActivate;
112 520 : gf_node_set_private(node, cbk);
113 : } else {
114 127429 : gf_scene_node_callback(cbk, type, node, param);
115 : }
116 127949 : }
117 :
118 412 : static GF_Err CTXLoad_Setup(GF_Filter *filter, CTXLoadPriv *priv)
119 : {
120 : const GF_PropertyValue *prop;
121 412 : if (!priv->file_name) return GF_BAD_PARAM;
122 :
123 412 : priv->ctx = gf_sm_new(priv->scene->graph);
124 412 : memset(&priv->load, 0, sizeof(GF_SceneLoader));
125 412 : priv->load.ctx = priv->ctx;
126 412 : priv->load.is = priv->scene;
127 412 : priv->load.scene_graph = priv->scene->graph;
128 412 : priv->load.fileName = priv->file_name;
129 412 : priv->load.src_url = priv->service_url;
130 412 : priv->load.flags = GF_SM_LOAD_FOR_PLAYBACK;
131 412 : priv->load.localPath = gf_get_default_cache_directory();
132 :
133 412 : priv->load.swf_import_flags = GF_SM_SWF_STATIC_DICT | GF_SM_SWF_QUAD_CURVE | GF_SM_SWF_SCALABLE_LINE | GF_SM_SWF_SPLIT_TIMELINE;
134 :
135 412 : if (!priv->files_to_delete)
136 412 : priv->files_to_delete = gf_list_new();
137 :
138 412 : prop = gf_filter_pid_get_property(priv->in_pid, GF_PROP_PID_MIME);
139 412 : if (prop && prop->value.string) {
140 409 : if (!strcmp(prop->value.string, "application/x-bt")) priv->load.type = GF_SM_LOAD_BT;
141 29 : else if (!strcmp(prop->value.string, "application/x-xmt")) priv->load.type = GF_SM_LOAD_XMTA;
142 28 : else if (!strcmp(prop->value.string, "model/vrml")) priv->load.type = GF_SM_LOAD_VRML;
143 18 : else if (!strcmp(prop->value.string, "x-model/x-vrml")) priv->load.type = GF_SM_LOAD_VRML;
144 18 : else if (!strcmp(prop->value.string, "model/x3d+vrml")) priv->load.type = GF_SM_LOAD_X3DV;
145 0 : else if (!strcmp(prop->value.string, "model/x3d+xml")) priv->load.type = GF_SM_LOAD_X3D;
146 0 : else if (!strcmp(prop->value.string, "application/x-shockwave-flash")) priv->load.type = GF_SM_LOAD_SWF;
147 0 : else if (!strcmp(prop->value.string, "application/x-LASeR+xml")) priv->load.type = GF_SM_LOAD_XSR;
148 : }
149 :
150 : return GF_OK;
151 : }
152 :
153 432 : GF_Err ctxload_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
154 : {
155 432 : CTXLoadPriv *priv = gf_filter_get_udta(filter);
156 : const GF_PropertyValue *prop;
157 :
158 432 : if (is_remove) {
159 20 : priv->in_pid = NULL;
160 20 : if (priv->out_pid) {
161 20 : gf_filter_pid_remove(priv->out_pid);
162 20 : priv->out_pid = NULL;
163 : }
164 : return GF_OK;
165 : }
166 :
167 412 : if (! gf_filter_pid_check_caps(pid))
168 : return GF_NOT_SUPPORTED;
169 :
170 : //we must have a file path
171 412 : prop = gf_filter_pid_get_property(pid, GF_PROP_PID_FILEPATH);
172 412 : if (!prop || ! prop->value.string) {
173 : return GF_NOT_SUPPORTED;
174 : }
175 :
176 412 : if (!priv->in_pid) {
177 : GF_FilterEvent fevt;
178 412 : priv->in_pid = pid;
179 :
180 : //we work with full file only, send a play event on source to indicate that
181 412 : GF_FEVT_INIT(fevt, GF_FEVT_PLAY, pid);
182 : fevt.play.start_range = 0;
183 412 : fevt.base.on_pid = priv->in_pid;
184 412 : fevt.play.full_file_only = GF_TRUE;
185 412 : gf_filter_pid_send_event(priv->in_pid, &fevt);
186 :
187 : } else {
188 0 : if (pid != priv->in_pid) {
189 : return GF_REQUIRES_NEW_INSTANCE;
190 : }
191 : //update of PID filename
192 0 : if (!prop->value.string || !priv->file_name || strcmp(prop->value.string, priv->file_name))
193 : return GF_NOT_SUPPORTED;
194 0 : return GF_OK;
195 : }
196 :
197 : #ifdef FILTER_FIXME
198 : /*animation stream like*/
199 : if (priv->ctx) {
200 : GF_StreamContext *sc;
201 : u32 i = 0;
202 : while ((sc = (GF_StreamContext *)gf_list_enum(priv->ctx->streams, &i))) {
203 : if (esd->ESID == sc->ESID) {
204 : priv->nb_streams++;
205 : return GF_OK;
206 : }
207 : }
208 : return GF_NON_COMPLIANT_BITSTREAM;
209 : }
210 : #endif
211 :
212 412 : priv->file_name = prop->value.string;
213 412 : priv->nb_streams = 1;
214 :
215 : //declare a new output PID of type scene, codecid RAW
216 412 : priv->out_pid = gf_filter_pid_new(filter);
217 :
218 412 : gf_filter_pid_copy_properties(priv->out_pid, pid);
219 412 : gf_filter_pid_set_property(priv->out_pid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_SCENE) );
220 412 : gf_filter_pid_set_property(priv->out_pid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_RAW) );
221 412 : gf_filter_pid_set_property(priv->out_pid, GF_PROP_PID_IN_IOD, &PROP_BOOL(GF_TRUE) );
222 :
223 412 : gf_filter_pid_set_udta(pid, priv->out_pid);
224 :
225 :
226 412 : priv->file_size = 0;
227 412 : priv->load_flags = 0;
228 412 : prop = gf_filter_pid_get_property(pid, GF_PROP_PID_ESID);
229 412 : priv->base_stream_id = prop ? prop->value.uint : -1;
230 :
231 412 : priv->pck_time = -1;
232 :
233 :
234 412 : switch (priv->load.type) {
235 0 : case GF_SM_LOAD_BT:
236 0 : gf_filter_set_name(filter, "Load:BT");
237 0 : break;
238 0 : case GF_SM_LOAD_VRML:
239 0 : gf_filter_set_name(filter, "Load:VRML97");
240 0 : break;
241 0 : case GF_SM_LOAD_X3DV:
242 0 : gf_filter_set_name(filter, "Load:X3D+vrml");
243 0 : break;
244 0 : case GF_SM_LOAD_XMTA:
245 0 : gf_filter_set_name(filter, "Load:XMTA");
246 0 : break;
247 0 : case GF_SM_LOAD_X3D:
248 0 : gf_filter_set_name(filter, "Load:X3D+XML Syntax");
249 0 : break;
250 0 : case GF_SM_LOAD_SWF:
251 0 : gf_filter_set_name(filter, "Load:SWF");
252 0 : break;
253 0 : case GF_SM_LOAD_XSR:
254 0 : gf_filter_set_name(filter, "Load:LASeRML");
255 0 : break;
256 0 : case GF_SM_LOAD_MP4:
257 0 : gf_filter_set_name(filter, "Load:MP4BIFSMemory");
258 0 : break;
259 : default:
260 : break;
261 : }
262 :
263 : return GF_OK;
264 : }
265 :
266 2847 : static Bool ctxload_process_event(GF_Filter *filter, const GF_FilterEvent *com)
267 : {
268 : u32 count, i;
269 2847 : CTXLoadPriv *priv = gf_filter_get_udta(filter);
270 : //check for scene attach
271 2847 : switch (com->base.type) {
272 412 : case GF_FEVT_PLAY:
273 : //cancel play event, we work with full file
274 : //TODO: animation stream in BT
275 412 : priv->is_playing = GF_TRUE;
276 412 : return GF_TRUE;
277 : case GF_FEVT_ATTACH_SCENE:
278 : break;
279 408 : case GF_FEVT_RESET_SCENE:
280 408 : gf_sm_load_done(&priv->load);
281 408 : if (priv->ctx) gf_sm_del(priv->ctx);
282 408 : priv->ctx = NULL;
283 408 : priv->load_flags = 3;
284 408 : return GF_FALSE;
285 : default:
286 : return GF_FALSE;
287 : }
288 412 : if (!com->attach_scene.on_pid) return GF_TRUE;
289 :
290 412 : count = gf_filter_get_ipid_count(filter);
291 412 : for (i=0; i<count; i++) {
292 412 : GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
293 412 : GF_FilterPid *opid = gf_filter_pid_get_udta(ipid);
294 : //we found our pid, set it up
295 412 : if (opid == com->attach_scene.on_pid) {
296 412 : if (!priv->scene) {
297 412 : GF_ObjectManager *odm = com->attach_scene.object_manager;
298 412 : priv->scene = odm->subscene ? odm->subscene : odm->parentscene;
299 412 : gf_sg_set_node_callback(priv->scene->graph, CTXLoad_NodeCallback);
300 :
301 412 : priv->service_url = odm->scene_ns->url;
302 :
303 412 : if (!priv->ctx) CTXLoad_Setup(filter, priv);
304 :
305 : }
306 : return GF_TRUE;
307 : }
308 : }
309 :
310 : return GF_FALSE;
311 : }
312 :
313 1177 : static Bool CTXLoad_StreamInRootOD(GF_ObjectDescriptor *od, u32 ESID)
314 : {
315 : u32 i, count;
316 : /*no root, only one stream possible*/
317 1177 : if (!od) return GF_TRUE;
318 1113 : count = gf_list_count(od->ESDescriptors);
319 : /*idem*/
320 1113 : if (!count) return GF_TRUE;
321 563 : for (i=0; i<count; i++) {
322 1517 : GF_ESD *esd = (GF_ESD *)gf_list_get(od->ESDescriptors, i);
323 1517 : if (esd->ESID==ESID) return GF_TRUE;
324 : }
325 : return GF_FALSE;
326 : }
327 :
328 :
329 188 : Double CTXLoad_GetVRMLTime(void *cbk)
330 : {
331 : u32 secs, msecs;
332 : Double res;
333 188 : gf_utc_time_since_1970(&secs, &msecs);
334 188 : res = msecs;
335 188 : res /= 1000;
336 188 : res += secs;
337 188 : return res;
338 : }
339 :
340 824 : static void CTXLoad_CheckStreams(CTXLoadPriv *priv )
341 : {
342 : u32 i, j, max_dur;
343 : GF_StreamContext *sc;
344 : u32 nb_aus=0;
345 : max_dur = 0;
346 824 : i=0;
347 2686 : while ((sc = (GF_StreamContext *)gf_list_enum(priv->ctx->streams, &i))) {
348 : GF_AUContext *au;
349 : /*all streams in root OD are handled with ESID 0 to differentiate with any animation streams*/
350 1038 : if (CTXLoad_StreamInRootOD(priv->ctx->root_od, sc->ESID)) sc->in_root_od = GF_TRUE;
351 1038 : if (!sc->timeScale) sc->timeScale = 1000;
352 :
353 1038 : j=0;
354 3159 : while ((au = (GF_AUContext *)gf_list_enum(sc->AUs, &j))) {
355 1083 : if (!au->timing) au->timing = (u64) (sc->timeScale*au->timing_sec);
356 1083 : if (gf_list_count(au->commands))
357 1083 : nb_aus++;
358 : }
359 : if (au && sc->in_root_od && (au->timing>max_dur)) max_dur = (u32) (au->timing * 1000 / sc->timeScale);
360 : }
361 : if (max_dur) {
362 : priv->scene->root_od->duration = max_dur;
363 : gf_scene_set_duration(priv->scene);
364 : }
365 824 : if ((priv->load_flags==1) && priv->ctx->root_od && priv->ctx->root_od->URLString) {
366 1 : gf_filter_pid_set_property(priv->out_pid, GF_PROP_PID_REMOTE_URL, &PROP_STRING(priv->ctx->root_od->URLString) );
367 : }
368 824 : if ((priv->load_flags==2) && !nb_aus) {
369 28 : gf_filter_pid_set_eos(priv->out_pid);
370 : }
371 824 : }
372 :
373 3234 : static GF_Err ctxload_process(GF_Filter *filter)
374 : {
375 : GF_Err e = GF_OK;
376 : GF_FilterPacket *pck;
377 : u32 i, j, k, updates_pending, last_rap=0;
378 : GF_AUContext *au;
379 : Bool can_delete_com;
380 : GF_StreamContext *sc;
381 : Bool is_seek = GF_FALSE;
382 : Bool is_start, is_end;
383 : u32 min_next_time_ms = 0;
384 3234 : CTXLoadPriv *priv = gf_filter_get_udta(filter);
385 :
386 : //not yet ready
387 3234 : if (!priv->scene) {
388 0 : if (priv->is_playing) {
389 0 : gf_filter_pid_set_eos(priv->out_pid);
390 0 : return GF_EOS;
391 : }
392 : return GF_OK;
393 : }
394 :
395 : /*something failed*/
396 3234 : if (priv->load_flags==3) return GF_EOS;
397 :
398 : /*this signals main scene deconnection, destroy the context if needed*/
399 3234 : if (!priv->ctx) {
400 0 : e = CTXLoad_Setup(filter, priv);
401 0 : if (e) return e;
402 : }
403 :
404 3234 : is_start = is_end = GF_FALSE;
405 3234 : pck = gf_filter_pid_get_packet(priv->in_pid);
406 3234 : if (pck) {
407 : //source is FILE, untimed - consider we init from 0
408 : u64 cts = 0;
409 598 : if ((s64)priv->pck_time<0) priv->pck_time = cts;
410 186 : else if (priv->pck_time!=cts) {
411 0 : priv->pck_time = cts;
412 : is_seek = GF_TRUE;
413 : }
414 : //init clocks
415 598 : gf_odm_check_buffering(priv->scene->root_od, priv->in_pid);
416 598 : gf_filter_pck_get_framing(pck, &is_start, &is_end);
417 598 : gf_filter_pid_drop_packet(priv->in_pid);
418 : }
419 :
420 598 : if (is_seek) {
421 : /*seek on root stream: destroy the context manager and reload it. We cannot seek on the main stream
422 : because commands may have changed node attributes/children and we don't track the initial value*/
423 0 : if (priv->load_flags) {
424 0 : if (priv->src) gf_fclose(priv->src);
425 0 : priv->src = NULL;
426 0 : gf_sm_load_done(&priv->load);
427 0 : priv->file_pos = 0;
428 :
429 0 : return CTXLoad_Setup(filter, priv);
430 : }
431 0 : i=0;
432 0 : while ((sc = (GF_StreamContext *)gf_list_enum(priv->ctx->streams, &i))) {
433 : #ifdef FILTER_FIXME
434 : /*not our stream*/
435 : if (!sc->in_root_od && (sc->ESID != ES_ID)) continue;
436 : /*not the base stream*/
437 : if (sc->in_root_od && (priv->base_stream_id != ES_ID)) continue;
438 : #endif
439 : /*handle SWF media extraction*/
440 0 : if ((sc->streamType == GF_STREAM_OD) && (priv->load_flags==1)) continue;
441 0 : sc->last_au_time = 0;
442 : }
443 : return GF_OK;
444 : }
445 :
446 3234 : if (priv->load_flags != 2) {
447 :
448 1073 : if (priv->progressive) {
449 : u32 entry_time;
450 : char file_buf[4096+1];
451 0 : if (!priv->src) {
452 0 : priv->src = gf_fopen(priv->file_name, "rb");
453 0 : if (!priv->src) return GF_URL_ERROR;
454 0 : priv->file_pos = 0;
455 : }
456 0 : priv->load.type = GF_SM_LOAD_XMTA;
457 : e = GF_OK;
458 0 : entry_time = gf_sys_clock();
459 0 : gf_fseek(priv->src, priv->file_pos, SEEK_SET);
460 : while (1) {
461 : u32 diff;
462 0 : s32 nb_read = (s32) gf_fread(file_buf, 4096, priv->src);
463 0 : if (nb_read<0) {
464 : return GF_IO_ERR;
465 : }
466 0 : file_buf[nb_read] = 0;
467 0 : if (!nb_read) {
468 0 : if (priv->file_pos==priv->file_size) {
469 0 : gf_fclose(priv->src);
470 0 : priv->src = NULL;
471 0 : priv->load_flags = 2;
472 0 : gf_sm_load_done(&priv->load);
473 0 : break;
474 : }
475 : break;
476 : }
477 :
478 0 : e = gf_sm_load_string(&priv->load, file_buf, GF_FALSE);
479 0 : priv->file_pos += nb_read;
480 0 : if (e) break;
481 0 : diff = gf_sys_clock() - entry_time;
482 0 : if (diff > priv->sax_dur) break;
483 : }
484 0 : if (!priv->scene->graph_attached) {
485 0 : gf_sg_set_scene_size_info(priv->scene->graph, priv->ctx->scene_width, priv->ctx->scene_height, priv->ctx->is_pixel_metrics);
486 0 : gf_scene_attach_to_compositor(priv->scene);
487 :
488 0 : CTXLoad_CheckStreams(priv);
489 : }
490 : }
491 : /*load first frame only*/
492 1073 : else if (!priv->load_flags) {
493 : /*we need the whole file - this could be further optimized by loading from memory the entire buffer*/
494 661 : if (!is_end) return GF_OK;
495 :
496 412 : priv->load_flags = 1;
497 412 : e = gf_sm_load_init(&priv->load);
498 412 : if (!e) {
499 412 : CTXLoad_CheckStreams(priv);
500 412 : gf_sg_set_scene_size_info(priv->scene->graph, priv->ctx->scene_width, priv->ctx->scene_height, priv->ctx->is_pixel_metrics);
501 : /*VRML, override base clock*/
502 412 : if ((priv->load.type==GF_SM_LOAD_VRML) || (priv->load.type==GF_SM_LOAD_X3DV) || (priv->load.type==GF_SM_LOAD_X3D)) {
503 : /*override clock callback*/
504 28 : gf_sg_set_scene_time_callback(priv->scene->graph, CTXLoad_GetVRMLTime);
505 : }
506 : }
507 : }
508 : /*load the rest*/
509 : else {
510 412 : priv->load_flags = 2;
511 412 : e = gf_sm_load_run(&priv->load);
512 412 : gf_sm_load_done(&priv->load);
513 : /*in case this was not set in the first pass (XMT)*/
514 412 : gf_sg_set_scene_size_info(priv->scene->graph, priv->ctx->scene_width, priv->ctx->scene_height, priv->ctx->is_pixel_metrics);
515 : }
516 :
517 824 : if (e<0) {
518 0 : gf_sm_load_done(&priv->load);
519 0 : gf_sm_del(priv->ctx);
520 0 : priv->ctx = NULL;
521 0 : priv->load_flags = 3;
522 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[CtxLoad] Failed to load context for file %s: %s\n", priv->file_name, gf_error_to_string(e) ));
523 0 : if (priv->out_pid)
524 0 : gf_filter_pid_set_eos(priv->out_pid);
525 : return e;
526 : }
527 :
528 : /*and figure out duration of root scene, and take care of XMT timing*/
529 824 : if (priv->load_flags==2) {
530 412 : CTXLoad_CheckStreams(priv);
531 412 : if (!gf_list_count(priv->ctx->streams)) {
532 28 : gf_scene_attach_to_compositor(priv->scene);
533 : }
534 : }
535 : }
536 :
537 : updates_pending = 0;
538 :
539 2985 : i=0;
540 11279 : while ((sc = (GF_StreamContext *)gf_list_enum(priv->ctx->streams, &i))) {
541 5309 : u32 stream_time = gf_clock_time(priv->scene->root_od->ck);
542 :
543 : //compositor is in end of stream mode, flush all commands
544 5309 : if (priv->scene->compositor->check_eos_state==2)
545 : stream_time=0xFFFFFFFF;
546 :
547 : #ifdef FILTER_FIXME
548 : /*not our stream*/
549 : if (!sc->in_root_od && (sc->ESID != ES_ID)) continue;
550 : /*not the base stream*/
551 : if (sc->in_root_od && (priv->base_stream_id != ES_ID)) continue;
552 : #endif
553 : /*handle SWF media extraction*/
554 5309 : if ((sc->streamType == GF_STREAM_OD) && (priv->load_flags==1)) continue;
555 :
556 : /*check for seek*/
557 5186 : if (sc->last_au_time > 1 + stream_time) {
558 8 : sc->last_au_time = 0;
559 : }
560 :
561 : can_delete_com = GF_FALSE;
562 5186 : if (sc->in_root_od && (priv->load_flags==2)) can_delete_com = GF_TRUE;
563 :
564 : /*we're in the right stream, apply update*/
565 5186 : j=0;
566 :
567 : /*seek*/
568 5186 : if (!sc->last_au_time) {
569 1504 : while ((au = (GF_AUContext *)gf_list_enum(sc->AUs, &j))) {
570 557 : u32 au_time = (u32) (au->timing*1000/sc->timeScale);
571 :
572 557 : if (au_time > stream_time)
573 : break;
574 549 : if (au->flags & GF_SM_AU_RAP) last_rap = j-1;
575 : }
576 955 : j = last_rap;
577 : }
578 :
579 6377 : while ((au = (GF_AUContext *)gf_list_enum(sc->AUs, &j))) {
580 3450 : u32 au_time = (u32) (au->timing*1000/sc->timeScale);
581 :
582 3450 : if (au_time + 1 <= sc->last_au_time) {
583 : /*remove first replace command*/
584 597 : if (can_delete_com && (sc->streamType==GF_STREAM_SCENE)) {
585 2040 : while (gf_list_count(au->commands)) {
586 1656 : GF_Command *com = (GF_Command *)gf_list_get(au->commands, 0);
587 1656 : gf_list_rem(au->commands, 0);
588 1656 : gf_sg_command_del(com);
589 : }
590 384 : j--;
591 384 : gf_list_rem(sc->AUs, j);
592 384 : gf_list_del(au->commands);
593 384 : gf_free(au);
594 : }
595 597 : continue;
596 : }
597 :
598 2853 : if (au_time > stream_time) {
599 : Double ts_offset;
600 2259 : u32 t = au_time - stream_time;
601 2259 : if (!min_next_time_ms || (min_next_time_ms>t))
602 : min_next_time_ms = t;
603 :
604 2259 : updates_pending++;
605 :
606 2259 : ts_offset = (Double) au->timing;
607 2259 : ts_offset /= sc->timeScale;
608 2259 : gf_sc_sys_frame_pending(priv->scene->compositor, ts_offset, stream_time, filter);
609 2259 : break;
610 : }
611 594 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[CtxLoad] %s applying AU time %d\n", priv->file_name, au_time ));
612 :
613 594 : if (sc->streamType == GF_STREAM_SCENE) {
614 : GF_Command *com;
615 : /*apply the commands*/
616 465 : k=0;
617 2833 : while ((com = (GF_Command *)gf_list_enum(au->commands, &k))) {
618 1903 : e = gf_sg_command_apply(priv->scene->graph, com, 0);
619 1903 : if (e) break;
620 : /*remove commands on base layer*/
621 1903 : if (can_delete_com) {
622 225 : k--;
623 225 : gf_list_rem(au->commands, k);
624 225 : gf_sg_command_del(com);
625 : }
626 : }
627 : }
628 129 : else if (sc->streamType == GF_STREAM_OD) {
629 : /*apply the commands*/
630 258 : while (gf_list_count(au->commands)) {
631 : Bool keep_com = GF_FALSE;
632 129 : GF_ODCom *com = (GF_ODCom *)gf_list_get(au->commands, 0);
633 129 : gf_list_rem(au->commands, 0);
634 129 : switch (com->tag) {
635 : case GF_ODF_OD_UPDATE_TAG:
636 : {
637 : GF_ODUpdate *odU = (GF_ODUpdate *)com;
638 266 : while (gf_list_count(odU->objectDescriptors)) {
639 : GF_ESD *esd;
640 : char *remote;
641 : GF_MuxInfo *mux = NULL;
642 146 : GF_ObjectDescriptor *od = (GF_ObjectDescriptor *)gf_list_get(odU->objectDescriptors, 0);
643 146 : gf_list_rem(odU->objectDescriptors, 0);
644 : /*we can only work with single-stream ods*/
645 146 : esd = (GF_ESD*)gf_list_get(od->ESDescriptors, 0);
646 146 : if (!esd) {
647 7 : if (od->URLString) {
648 7 : ODS_SetupOD(priv->scene, od);
649 : }
650 7 : gf_odf_desc_del((GF_Descriptor *) od);
651 7 : continue;
652 : }
653 : /*fix OCR dependencies*/
654 139 : if (CTXLoad_StreamInRootOD(priv->ctx->root_od, esd->OCRESID)) esd->OCRESID = priv->base_stream_id;
655 :
656 : /*forbidden if ESD*/
657 139 : if (od->URLString) {
658 0 : gf_odf_desc_del((GF_Descriptor *) od);
659 0 : continue;
660 : }
661 : /*look for MUX info*/
662 139 : k=0;
663 281 : while ((mux = (GF_MuxInfo*)gf_list_enum(esd->extensionDescriptors, &k))) {
664 133 : if (mux->tag == GF_ODF_MUXINFO_TAG) break;
665 : mux = NULL;
666 : }
667 : /*we need a mux if not animation stream*/
668 139 : if (!mux || !mux->file_name) {
669 : /*only animation streams are handled*/
670 10 : if (!esd->decoderConfig) {
671 10 : } else if (esd->decoderConfig->streamType==GF_STREAM_SCENE) {
672 : /*set ST to private scene to get sure the stream will be redirected to us*/
673 0 : esd->decoderConfig->streamType = GF_STREAM_PRIVATE_SCENE;
674 0 : esd->dependsOnESID = priv->base_stream_id;
675 0 : ODS_SetupOD(priv->scene, od);
676 10 : } else if (esd->decoderConfig->streamType==GF_STREAM_INTERACT) {
677 9 : GF_UIConfig *cfg = (GF_UIConfig *) esd->decoderConfig->decoderSpecificInfo;
678 9 : gf_odf_encode_ui_config(cfg, &esd->decoderConfig->decoderSpecificInfo);
679 9 : gf_odf_desc_del((GF_Descriptor *) cfg);
680 9 : ODS_SetupOD(priv->scene, od);
681 1 : } else if (esd->decoderConfig->streamType==GF_STREAM_OCR) {
682 1 : ODS_SetupOD(priv->scene, od);
683 : }
684 10 : gf_odf_desc_del((GF_Descriptor *) od);
685 10 : continue;
686 : }
687 : //solve url before import
688 129 : if (mux->src_url) {
689 129 : char *res_url = gf_url_concatenate(mux->src_url, mux->file_name);
690 129 : if (res_url) {
691 129 : gf_free(mux->file_name);
692 129 : mux->file_name = res_url;
693 : }
694 129 : gf_free(mux->src_url);
695 129 : mux->src_url = NULL;
696 : }
697 : /*text import*/
698 129 : if (mux->textNode) {
699 : #ifdef GPAC_DISABLE_MEDIA_IMPORT
700 : gf_odf_desc_del((GF_Descriptor *) od);
701 : continue;
702 : #else
703 2 : e = gf_sm_import_bifs_subtitle(priv->ctx, esd, mux);
704 2 : if (e) {
705 : e = GF_OK;
706 0 : gf_odf_desc_del((GF_Descriptor *) od);
707 0 : continue;
708 : }
709 : /*set ST to private scene and dependency to base to get sure the stream will be redirected to us*/
710 2 : esd->decoderConfig->streamType = GF_STREAM_PRIVATE_SCENE;
711 2 : esd->dependsOnESID = priv->base_stream_id;
712 2 : ODS_SetupOD(priv->scene, od);
713 2 : gf_odf_desc_del((GF_Descriptor *) od);
714 2 : continue;
715 : #endif
716 : }
717 :
718 : /*soundstreams are a bit of a pain, they may be declared before any data gets written*/
719 127 : if (mux->delete_file) {
720 0 : FILE *t = gf_fopen(mux->file_name, "rb");
721 0 : if (!t) {
722 : keep_com = GF_TRUE;
723 0 : gf_list_insert(odU->objectDescriptors, od, 0);
724 : break;
725 : }
726 0 : gf_fclose(t);
727 : }
728 : /*remap to remote URL - warning, the URL has already been resolved according to the parent path*/
729 127 : remote = (char*)gf_malloc(sizeof(char) * (strlen("gpac://")+strlen(mux->file_name)+1) );
730 : strcpy(remote, "gpac://");
731 127 : strcat(remote, mux->file_name);
732 127 : k = od->objectDescriptorID;
733 : /*if files were created we'll have to clean up (swf import)*/
734 127 : if (mux->delete_file) gf_list_add(priv->files_to_delete, gf_strdup(remote));
735 :
736 127 : gf_odf_desc_del((GF_Descriptor *) od);
737 127 : od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG);
738 127 : od->URLString = remote;
739 127 : od->fake_remote = GF_TRUE;
740 127 : od->objectDescriptorID = k;
741 127 : ODS_SetupOD(priv->scene, od);
742 127 : gf_odf_desc_del((GF_Descriptor*)od);
743 : }
744 : if (keep_com) break;
745 : }
746 : break;
747 4 : case GF_ODF_OD_REMOVE_TAG:
748 : {
749 : GF_ODRemove *odR = (GF_ODRemove*)com;
750 8 : for (k=0; k<odR->NbODs; k++) {
751 4 : GF_ObjectManager *odm = gf_scene_find_odm(priv->scene, odR->OD_ID[k]);
752 4 : if (odm) gf_odm_disconnect(odm, 1);
753 : }
754 : }
755 : break;
756 : default:
757 : break;
758 : }
759 : if (keep_com) {
760 0 : gf_list_insert(au->commands, com, 0);
761 0 : break;
762 : } else {
763 129 : gf_odf_com_del(&com);
764 : }
765 129 : if (e) break;
766 : }
767 :
768 : }
769 594 : sc->last_au_time = au_time + 1;
770 : /*attach graph to renderer*/
771 594 : if (!priv->scene->graph_attached)
772 355 : gf_scene_attach_to_compositor(priv->scene);
773 594 : if (e) return e;
774 :
775 : /*for root streams remove completed AUs (no longer needed)*/
776 594 : if (sc->in_root_od && !gf_list_count(au->commands) ) {
777 200 : j--;
778 200 : gf_list_rem(sc->AUs, j);
779 200 : gf_list_del(au->commands);
780 200 : gf_free(au);
781 : }
782 : }
783 : }
784 2985 : if (e) return e;
785 :
786 2985 : if ((priv->load_flags==2) && !updates_pending) {
787 419 : gf_filter_pid_set_eos(priv->out_pid);
788 419 : return GF_EOS;
789 : }
790 2566 : if (!min_next_time_ms) min_next_time_ms = 1;
791 2566 : min_next_time_ms *= 1000;
792 2566 : if (min_next_time_ms>2000)
793 : min_next_time_ms = 2000;
794 :
795 2566 : gf_filter_ask_rt_reschedule(filter, min_next_time_ms);
796 2566 : return GF_OK;
797 : }
798 :
799 :
800 411 : static void ctxload_finalize(GF_Filter *filter)
801 : {
802 411 : CTXLoadPriv *priv = gf_filter_get_udta(filter);
803 :
804 411 : if (priv->ctx) gf_sm_del(priv->ctx);
805 411 : if (priv->files_to_delete) gf_list_del(priv->files_to_delete);
806 411 : }
807 :
808 : #include <gpac/utf.h>
809 3074 : static const char *ctxload_probe_data(const u8 *probe_data, u32 size, GF_FilterProbeScore *score)
810 : {
811 : const char *mime_type = NULL;
812 3074 : char *dst = NULL;
813 : u8 *res;
814 :
815 : /* check gzip magic header */
816 3074 : if ((size>2) && (probe_data[0] == 0x1f) && (probe_data[1] == 0x8b)) {
817 0 : *score = GF_FPROBE_EXT_MATCH;
818 0 : return "btz|bt.gz|xmt.gz|xmtz|wrl.gz|x3dv.gz|x3dvz|x3d.gz|x3dz";
819 : }
820 :
821 3074 : res = gf_utf_get_utf8_string_from_bom((char *)probe_data, size, &dst);
822 3074 : if (res) probe_data = res;
823 :
824 : //strip all spaces and \r\n
825 3080 : while (probe_data[0] && strchr("\n\r\t ", (char) probe_data[0]))
826 6 : probe_data ++;
827 :
828 : //for XML, strip doctype, <?xml and comments
829 : while (1) {
830 3381 : if (!strncmp(probe_data, "<!DOCTYPE", 9)) {
831 29 : probe_data = strchr(probe_data, '>');
832 29 : if (!probe_data) goto exit;
833 29 : probe_data++;
834 87 : while (probe_data[0] && strchr("\n\r\t ", (char) probe_data[0]))
835 29 : probe_data ++;
836 : }
837 : //for XML, strip xml header
838 3352 : else if (!strncmp(probe_data, "<?xml", 5)) {
839 214 : probe_data = strstr(probe_data, "?>");
840 214 : if (!probe_data) goto exit;
841 :
842 214 : probe_data += 2;
843 656 : while (probe_data[0] && strchr("\n\r\t ", (char) probe_data[0]))
844 228 : probe_data ++;
845 : }
846 3138 : else if (!strncmp(probe_data, "<!--", 4)) {
847 64 : probe_data = strstr(probe_data, "-->");
848 64 : if (!probe_data) goto exit;
849 64 : probe_data += 3;
850 208 : while (probe_data[0] && strchr("\n\r\t ", (char) probe_data[0]))
851 80 : probe_data ++;
852 : } else {
853 : break;
854 : }
855 : }
856 : //probe_data is now the first element of the document, if XML
857 : //we should refin by getting the xmlns attribute value rather than searching for its value...
858 :
859 3074 : if (!strncmp(probe_data, "<XMT-A", strlen("<XMT-A"))
860 3074 : || strstr(probe_data, "urn:mpeg:mpeg4:xmta:schema:2002")
861 : ) {
862 : mime_type = "application/x-xmt";
863 3073 : } else if (strstr(probe_data, "<X3D")
864 3073 : || strstr(probe_data, "http://www.web3d.org/specifications/x3d-3.0.xsd")
865 : ) {
866 : mime_type = "model/x3d+xml";
867 3073 : } else if (strstr(probe_data, "<saf")
868 3073 : || strstr(probe_data, "urn:mpeg:mpeg4:SAF:2005")
869 3073 : || strstr(probe_data, "urn:mpeg:mpeg4:LASeR:2005")
870 : ) {
871 : mime_type = "application/x-LASeR+xml";
872 3073 : } else if (!strncmp(probe_data, "<DIMSStream", strlen("<DIMSStream") ) ) {
873 : mime_type = "application/dims";
874 3071 : } else if (!strncmp(probe_data, "<svg", 4) || strstr(probe_data, "http://www.w3.org/2000/svg") ) {
875 : mime_type = "image/svg+xml";
876 3002 : } else if (!strncmp(probe_data, "<widget", strlen("<widget") ) ) {
877 : mime_type = "application/widget";
878 3002 : } else if (!strncmp(probe_data, "<NHNTStream", strlen("<NHNTStream") ) ) {
879 : mime_type = "application/x-nhml";
880 2976 : } else if (!strncmp(probe_data, "<TextStream", strlen("<TextStream") ) ) {
881 : mime_type = "text/ttxt";
882 2972 : } else if (!strncmp(probe_data, "<text3GTrack", strlen("<text3GTrack") ) ) {
883 : mime_type = "quicktime/text";
884 : }
885 : //BT/VRML with no doc header
886 : else {
887 : //get first keyword
888 : while (1) {
889 : //strip all spaces and \r\n
890 5592 : while (probe_data[0] && strchr("\n\r\t ", (char) probe_data[0]))
891 1339 : probe_data ++;
892 :
893 : //VRML / XRDV files
894 4253 : if (!strncmp(probe_data, "#VRML V2.0", strlen("#VRML V2.0"))) {
895 : mime_type = "model/vrml";
896 : goto exit;
897 : }
898 4243 : if (!strncmp(probe_data, "#X3D V3.0", strlen("#X3D V3.0"))) {
899 : mime_type = "model/x3d+vrml";
900 : goto exit;
901 : }
902 :
903 : //skip comment lines and some specific X3D keyword (we want to fetch a group
904 4225 : if ((probe_data[0] != '#')
905 2945 : && strncmp(probe_data, "PROFILE", strlen("PROFILE"))
906 2944 : && strncmp(probe_data, "COMPONENT", strlen("COMPONENT"))
907 2944 : && strncmp(probe_data, "META", strlen("META"))
908 2944 : && strncmp(probe_data, "IMPORT", strlen("IMPORT"))
909 2944 : && strncmp(probe_data, "EXPORT", strlen("EXPORT"))
910 : ) {
911 : break;
912 : }
913 : //skip line and go one
914 1281 : probe_data = strchr(probe_data, '\n');
915 1281 : if (!probe_data) goto exit;
916 : }
917 :
918 2944 : if (!strncmp(probe_data, "InitialObjectDescriptor", strlen("InitialObjectDescriptor"))
919 2592 : || !strncmp(probe_data, "EXTERNPROTO", strlen("EXTERNPROTO"))
920 2589 : || !strncmp(probe_data, "PROTO", strlen("PROTO"))
921 2589 : || !strncmp(probe_data, "Group", strlen("Group"))
922 2589 : || !strncmp(probe_data, "OrderedGroup", strlen("OrderedGroup"))
923 2560 : || !strncmp(probe_data, "Layer2D", strlen("Layer2D"))
924 2560 : || !strncmp(probe_data, "Layer3D", strlen("Layer3D"))
925 : ) {
926 384 : if (strstr(probe_data, "children"))
927 : mime_type = "application/x-bt";
928 : }
929 : }
930 :
931 :
932 2561 : exit:
933 :
934 3074 : if (dst) gf_free(dst);
935 3074 : if (mime_type) {
936 513 : *score = GF_FPROBE_MAYBE_SUPPORTED;
937 513 : return mime_type;
938 : }
939 :
940 2561 : *score = GF_FPROBE_NOT_SUPPORTED;
941 2561 : return NULL;
942 : }
943 :
944 : static const GF_FilterCapability CTXLoadCaps[] =
945 : {
946 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
947 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "bt|btz|bt.gz|xmt|xmt.gz|xmtz|wrl|wrl.gz|x3dv|x3dv.gz|x3dvz|x3d|x3d.gz|x3dz|swf|xsr"),
948 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "application/x-bt|application/x-xmt|model/vrml|x-model/x-vrml|model/x3d+vrml|model/x3d+xml|application/x-shockwave-flash|application/x-LASeR+xml"),
949 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_SCENE),
950 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
951 : };
952 :
953 :
954 : #define OFFS(_n) #_n, offsetof(CTXLoadPriv, _n)
955 :
956 : static const GF_FilterArgs CTXLoadArgs[] =
957 : {
958 : { OFFS(progressive), "enable progressive loading", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
959 : { OFFS(sax_dur), "loading duration for SAX parsing (XMT), 0 disables SAX parsing", GF_PROP_UINT, "1000", NULL, GF_FS_ARG_HINT_ADVANCED},
960 : {0}
961 : };
962 :
963 : GF_FilterRegister CTXLoadRegister = {
964 : .name = "btplay",
965 : GF_FS_SET_DESCRIPTION("BT/XMT/X3D loader")
966 : GF_FS_SET_HELP("This filter parses MPEG-4 BIFS (BT and XMT), VRML97 and X3D (wrl and XML) files directly into the scene graph of the compositor.")
967 : .private_size = sizeof(CTXLoadPriv),
968 : .flags = GF_FS_REG_MAIN_THREAD,
969 : .args = CTXLoadArgs,
970 : SETCAPS(CTXLoadCaps),
971 : .finalize = ctxload_finalize,
972 : .process = ctxload_process,
973 : .configure_pid = ctxload_configure_pid,
974 : .process_event = ctxload_process_event,
975 : .probe_data = ctxload_probe_data,
976 : };
977 :
978 : #endif //defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_SCENEGRAPH)
979 :
980 :
981 2877 : const GF_FilterRegister *ctxload_register(GF_FilterSession *session)
982 : {
983 : #if !defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_SCENEGRAPH)
984 2877 : return &CTXLoadRegister;
985 : #else
986 : return NULL;
987 : #endif
988 : }
989 :
990 :
|