Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2018
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / Media terminal 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 : #include <gpac/internal/compositor_dev.h>
28 : #include <gpac/internal/scenegraph_dev.h>
29 : #include <gpac/term_info.h>
30 : #include <gpac/constants.h>
31 : #include <gpac/options.h>
32 : #include <gpac/network.h>
33 : #include <gpac/xml.h>
34 : #include <gpac/avparse.h>
35 : #include <gpac/nodes_svg.h>
36 :
37 : #include "../utils/module_wrap.h"
38 :
39 : /*textual command processing*/
40 : #include <gpac/terminal.h>
41 : #include <gpac/scene_manager.h>
42 :
43 : u32 gf_term_sample_clocks(GF_Terminal *term);
44 :
45 :
46 : struct _tag_terminal
47 : {
48 : GF_User *user;
49 : GF_Compositor *compositor;
50 : GF_FilterSession *fsess;
51 : Bool in_destroy;
52 : u32 reload_state;
53 : char *reload_url;
54 : };
55 :
56 : GF_Compositor *gf_sc_from_filter(GF_Filter *filter);
57 :
58 : u32 gf_sc_play_from_time(GF_Compositor *compositor, u64 from_time, u32 pause_at_first_frame);
59 :
60 :
61 : GF_EXPORT
62 4 : Bool gf_term_send_event(GF_Terminal *term, GF_Event *evt)
63 : {
64 4 : return gf_filter_send_gf_event(term->compositor->filter, evt);
65 : }
66 :
67 :
68 : #ifdef FILTER_FIXME
69 :
70 : static Bool gf_term_get_user_pass(void *usr_cbk, const char *site_url, char *usr_name, char *password)
71 : {
72 : GF_Event evt;
73 : GF_Terminal *term = (GF_Terminal *)usr_cbk;
74 : evt.type = GF_EVENT_AUTHORIZATION;
75 : evt.auth.site_url = site_url;
76 : evt.auth.user = usr_name;
77 : evt.auth.password = password;
78 : return gf_term_send_event(term, &evt);
79 : }
80 : #endif
81 :
82 2 : static GF_Err gf_sc_step_clocks_intern(GF_Compositor *compositor, u32 ms_diff, Bool force_resume_pause)
83 : {
84 : /*only play/pause if connected*/
85 2 : if (!compositor || !compositor->root_scene || !compositor->root_scene->root_od) return GF_BAD_PARAM;
86 :
87 2 : if (ms_diff) {
88 : u32 i, j;
89 : GF_SceneNamespace *ns;
90 : GF_Clock *ck;
91 :
92 2 : if (compositor->play_state == GF_STATE_PLAYING) return GF_BAD_PARAM;
93 :
94 2 : gf_sc_lock(compositor, 1);
95 2 : i = 0;
96 8 : while ((ns = (GF_SceneNamespace*)gf_list_enum(compositor->root_scene->namespaces, &i))) {
97 4 : j = 0;
98 10 : while (ns->clocks && (ck = (GF_Clock *)gf_list_enum(ns->clocks, &j))) {
99 2 : ck->init_timestamp += ms_diff;
100 2 : ck->media_time_at_init += ms_diff;
101 : //make sure we don't touch clock while doing resume/pause below
102 2 : if (force_resume_pause)
103 2 : ck->nb_paused++;
104 : }
105 : }
106 2 : compositor->step_mode = GF_TRUE;
107 2 : compositor->use_step_mode = GF_TRUE;
108 2 : gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
109 :
110 : //resume/pause to trigger codecs state change
111 2 : if (force_resume_pause) {
112 2 : mediacontrol_resume(compositor->root_scene->root_od, 0);
113 2 : mediacontrol_pause(compositor->root_scene->root_od);
114 :
115 : //release our safety
116 2 : i = 0;
117 8 : while ((ns = (GF_SceneNamespace*)gf_list_enum(compositor->root_scene->namespaces, &i))) {
118 4 : j = 0;
119 10 : while (ns->clocks && (ck = (GF_Clock *)gf_list_enum(ns->clocks, &j))) {
120 2 : ck->nb_paused--;
121 : }
122 : }
123 : }
124 :
125 2 : gf_sc_lock(compositor, 0);
126 :
127 : }
128 : return GF_OK;
129 : }
130 :
131 6 : static void gf_term_set_play_state(GF_Compositor *compositor, u32 PlayState, Bool reset_audio, Bool pause_clocks)
132 : {
133 : Bool resume_live = 0;
134 : u32 prev_state;
135 :
136 : /*only play/pause if connected*/
137 6 : if (!compositor || !compositor->root_scene) return;
138 :
139 6 : prev_state = compositor->play_state;
140 6 : compositor->use_step_mode = GF_FALSE;
141 :
142 6 : if (PlayState==GF_STATE_PLAY_LIVE) {
143 : PlayState = GF_STATE_PLAYING;
144 : resume_live = 1;
145 0 : if (compositor->play_state == GF_STATE_PLAYING) {
146 0 : compositor->play_state = GF_STATE_PAUSED;
147 0 : mediacontrol_pause(compositor->root_scene->root_od);
148 : }
149 : }
150 :
151 : /*and if not already paused/playing*/
152 6 : if ((compositor->play_state == GF_STATE_PLAYING) && (PlayState == GF_STATE_PLAYING)) return;
153 6 : if ((compositor->play_state != GF_STATE_PLAYING) && (PlayState == GF_STATE_PAUSED)) return;
154 :
155 : /*pause compositor*/
156 6 : if ((PlayState==GF_STATE_PLAYING) && reset_audio)
157 2 : gf_sc_set_option(compositor, GF_OPT_PLAY_STATE, 0xFF);
158 : else
159 4 : gf_sc_set_option(compositor, GF_OPT_PLAY_STATE, PlayState);
160 :
161 : /* step mode specific */
162 6 : if (PlayState==GF_STATE_STEP_PAUSE) {
163 4 : if (prev_state==GF_STATE_PLAYING) {
164 2 : mediacontrol_pause(compositor->root_scene->root_od);
165 2 : compositor->play_state = GF_STATE_PAUSED;
166 : } else {
167 : u32 diff=1;
168 2 : if (compositor->ms_until_next_frame>0) diff = compositor->ms_until_next_frame;
169 2 : gf_sc_step_clocks_intern(compositor, diff, GF_TRUE);
170 : }
171 : return;
172 : }
173 :
174 : /* nothing to change*/
175 2 : if (compositor->play_state == PlayState) return;
176 2 : compositor->play_state = PlayState;
177 :
178 2 : if (compositor->root_scene->first_frame_pause_type && (PlayState == GF_STATE_PLAYING))
179 0 : compositor->root_scene->first_frame_pause_type = 0;
180 :
181 2 : if (!pause_clocks) return;
182 :
183 2 : if (PlayState != GF_STATE_PLAYING) {
184 0 : mediacontrol_pause(compositor->root_scene->root_od);
185 : } else {
186 2 : mediacontrol_resume(compositor->root_scene->root_od, resume_live);
187 : }
188 :
189 : }
190 :
191 :
192 : GF_EXPORT
193 9 : void gf_sc_connect_from_time_ex(GF_Compositor *compositor, const char *URL, u64 startTime, u32 pause_at_first_frame, Bool secondary_scene, const char *parent_path)
194 : {
195 : GF_Scene *scene;
196 : GF_ObjectManager *odm;
197 9 : if (!URL || !strlen(URL)) return;
198 :
199 9 : if (compositor->root_scene) {
200 2 : if (compositor->root_scene->root_od && compositor->root_scene->root_od->scene_ns) {
201 2 : const char *main_url = compositor->root_scene->root_od->scene_ns->url;
202 2 : if (main_url && !strcmp(main_url, URL)) {
203 0 : gf_sc_play_from_time(compositor, 0, pause_at_first_frame);
204 0 : return;
205 : }
206 : }
207 : /*disconnect*/
208 2 : gf_sc_disconnect(compositor);
209 : }
210 9 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Connecting to %s\n", URL));
211 :
212 : assert(!compositor->root_scene);
213 :
214 : /*create a new scene*/
215 9 : scene = gf_scene_new(compositor, NULL);
216 9 : odm = gf_odm_new();
217 9 : scene->root_od = odm;
218 9 : odm->subscene = scene;
219 : //by default all scenes are dynamic, until we get a BIFS attached
220 9 : scene->is_dynamic_scene = GF_TRUE;
221 :
222 9 : odm->media_start_time = startTime;
223 :
224 : // we are not in compositor:process at this point of time since the terminal thread drives the compositor
225 9 : compositor->root_scene = scene;
226 :
227 : /*render first visual frame and pause*/
228 9 : if (pause_at_first_frame) {
229 0 : gf_term_set_play_state(compositor, GF_STATE_STEP_PAUSE, 0, 0);
230 0 : scene->first_frame_pause_type = pause_at_first_frame;
231 : }
232 :
233 9 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] root scene created\n", URL));
234 :
235 9 : if (!strnicmp(URL, "views://", 8)) {
236 0 : gf_scene_generate_views(compositor->root_scene, (char *) URL+8, (char*)parent_path);
237 0 : return;
238 : }
239 9 : else if (!strnicmp(URL, "mosaic://", 9)) {
240 0 : gf_scene_generate_mosaic(compositor->root_scene, (char *) URL+9, (char*)parent_path);
241 0 : return;
242 : }
243 :
244 9 : gf_scene_ns_connect_object(scene, odm, (char *) URL, (char*)parent_path);
245 : }
246 :
247 :
248 : GF_EXPORT
249 2 : Bool gf_term_is_type_supported(GF_Terminal *term, const char* mime)
250 : {
251 2 : return gf_fs_is_supported_mime(term->fsess, mime);
252 : }
253 :
254 : //todo: move this to compositor ?
255 7 : static void gf_term_refresh_cache()
256 : {
257 : u32 i, count;
258 7 : count = gf_opts_get_section_count();
259 70 : for (i=0; i<count; i++) {
260 : const char *opt;
261 : u32 sec, frac, exp;
262 : Bool force_delete;
263 : const char *file;
264 63 : const char *name = gf_opts_get_section_name(i);
265 126 : if (strncmp(name, "@cache=", 7)) continue;
266 :
267 0 : file = gf_opts_get_key(name, "cacheFile");
268 0 : opt = gf_opts_get_key(name, "expireAfterNTP");
269 0 : if (!opt) {
270 0 : if (file) gf_file_delete((char*) file);
271 0 : gf_opts_del_section(name);
272 0 : i--;
273 0 : count--;
274 0 : continue;
275 : }
276 :
277 : force_delete = 0;
278 0 : if (file) {
279 0 : FILE *t = gf_fopen(file, "r");
280 0 : if (!t) force_delete = 1;
281 0 : else gf_fclose(t);
282 : }
283 0 : sscanf(opt, "%u", &exp);
284 0 : gf_net_get_ntp(&sec, &frac);
285 0 : if (exp && (exp<sec)) force_delete=1;
286 :
287 0 : if (force_delete) {
288 0 : if (file) gf_file_delete((char*) opt);
289 :
290 0 : gf_opts_del_section(name);
291 0 : i--;
292 0 : count--;
293 0 : continue;
294 : }
295 : }
296 7 : }
297 :
298 : GF_EXPORT
299 7 : GF_Terminal *gf_term_new(GF_User *user)
300 : {
301 : GF_Terminal *tmp;
302 : GF_Filter *comp_filter;
303 : u32 def_w, def_h;
304 : GF_Err e;
305 : const char *opt;
306 : char szArgs[200];
307 :
308 7 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Creating terminal\n"));
309 :
310 7 : tmp = (GF_Terminal*)gf_malloc(sizeof(GF_Terminal));
311 7 : if (!tmp) {
312 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Terminal] Failed to allocate GF_Terminal : OUT OF MEMORY ?\n"));
313 : return NULL;
314 : }
315 : memset(tmp, 0, sizeof(GF_Terminal));
316 :
317 : /*just for safety in case not done before*/
318 7 : gf_sys_init(GF_MemTrackerNone, NULL);
319 :
320 7 : tmp->user = user;
321 :
322 : //for now we store the init_flags in the global config (used by compositor and AV output modules)
323 : //cleaning this would need futher API rework and getting rid of the GF_User strcuture
324 7 : sprintf(szArgs, "%d", user->init_flags);
325 7 : gf_opts_set_key("Temp", "InitFlags", szArgs);
326 :
327 7 : tmp->fsess = gf_fs_new_defaults(GF_FS_FLAG_NO_MAIN_THREAD);
328 7 : if (!tmp->fsess) {
329 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Terminal] Failed to create filter session.\n"));
330 0 : gf_free(tmp);
331 0 : return NULL;
332 : }
333 :
334 7 : gf_fs_set_ui_callback(tmp->fsess, user->EventProc, user->opaque);
335 :
336 7 : opt = gf_opts_get_key("Temp", "DefaultWidth");
337 7 : def_w = opt ? atoi(opt) : 0;
338 7 : opt = gf_opts_get_key("Temp", "DefaultHeight");
339 7 : def_h = opt ? atoi(opt) : 0;
340 :
341 7 : if (def_w && def_h) {
342 : sprintf(szArgs, "compositor:FID=compose:player=base:osize=%dx%d", def_w, def_h);
343 : } else {
344 : strcpy(szArgs, "compositor:FID=compose:player=base");
345 : }
346 :
347 7 : comp_filter = gf_fs_load_filter(tmp->fsess, szArgs, &e);
348 7 : tmp->compositor = comp_filter ? gf_sc_from_filter(comp_filter) : NULL;
349 7 : if (!tmp->compositor) {
350 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Terminal] Failed to load compositor filter: %s\n", gf_error_to_string(e) ));
351 0 : gf_fs_del(tmp->fsess);
352 0 : gf_free(tmp);
353 0 : return NULL;
354 : }
355 :
356 7 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] compositor loaded\n"));
357 :
358 : #ifdef FILTER_FIXME
359 : gf_dm_set_auth_callback(tmp->downloader, gf_term_get_user_pass, tmp);
360 :
361 : tmp->uri_relocators = gf_list_new();
362 : tmp->locales.relocate_uri = term_check_locales;
363 : tmp->locales.term = tmp;
364 : gf_list_add(tmp->uri_relocators, &tmp->locales);
365 : #endif
366 :
367 7 : gf_term_refresh_cache();
368 7 : gf_fs_run(tmp->fsess);
369 :
370 7 : return tmp;
371 : }
372 :
373 : GF_EXPORT
374 6 : GF_Err gf_term_del(GF_Terminal * term)
375 : {
376 6 : if (!term) return GF_BAD_PARAM;
377 :
378 6 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Destroying terminal\n"));
379 : /*close main service*/
380 6 : gf_term_disconnect(term);
381 6 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] main service disconnected\n"));
382 :
383 6 : term->in_destroy = GF_TRUE;
384 : /*stop the media manager */
385 6 : gf_fs_del(term->fsess);
386 :
387 6 : gf_sys_close();
388 6 : if (term->reload_url) gf_free(term->reload_url);
389 6 : gf_free(term);
390 6 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Terminal destroyed\n"));
391 : return GF_OK;
392 : }
393 :
394 : GF_EXPORT
395 4 : void gf_term_connect_from_time(GF_Terminal * term, const char *URL, u64 startTime, u32 pause_at_first_frame)
396 : {
397 4 : if (!term) return;
398 4 : gf_sc_connect_from_time_ex(term->compositor, URL, startTime, pause_at_first_frame, 0, NULL);
399 : }
400 :
401 : GF_EXPORT
402 3 : void gf_term_connect(GF_Terminal * term, const char *URL)
403 : {
404 3 : if (!term) return;
405 3 : gf_sc_connect_from_time_ex(term->compositor, URL, 0, 0, 0, NULL);
406 : }
407 :
408 :
409 : GF_EXPORT
410 2 : void gf_term_connect_with_path(GF_Terminal * term, const char *URL, const char *parent_path)
411 : {
412 2 : if (!term) return;
413 2 : gf_sc_connect_from_time_ex(term->compositor, URL, 0, 0, 0, parent_path);
414 : }
415 :
416 : GF_EXPORT
417 15 : void gf_sc_disconnect(GF_Compositor *compositor)
418 : {
419 : /*resume*/
420 15 : if (compositor->play_state != GF_STATE_PLAYING) gf_term_set_play_state(compositor, GF_STATE_PLAYING, 1, 1);
421 :
422 15 : if (compositor->root_scene && compositor->root_scene->root_od) {
423 : GF_ObjectManager *root = compositor->root_scene->root_od;
424 9 : gf_sc_lock(compositor, GF_TRUE);
425 9 : compositor->root_scene = NULL;
426 9 : gf_odm_disconnect(root, 2);
427 9 : gf_sc_lock(compositor, GF_FALSE);
428 : }
429 15 : }
430 :
431 : GF_EXPORT
432 13 : void gf_term_disconnect(GF_Terminal *term)
433 : {
434 13 : if (term) gf_sc_disconnect(term->compositor);
435 13 : }
436 :
437 : GF_EXPORT
438 2 : const char *gf_term_get_url(GF_Terminal *term)
439 : {
440 2 : GF_Compositor *compositor = term ? term->compositor : NULL;
441 2 : if (!compositor || !compositor->root_scene || !compositor->root_scene->root_od || !compositor->root_scene->root_od->scene_ns) return NULL;
442 2 : return compositor->root_scene->root_od->scene_ns->url;
443 : }
444 :
445 : /*set rendering option*/
446 : GF_EXPORT
447 29 : GF_Err gf_term_set_option(GF_Terminal * term, u32 type, u32 value)
448 : {
449 29 : if (!term) return GF_BAD_PARAM;
450 29 : switch (type) {
451 4 : case GF_OPT_PLAY_STATE:
452 4 : gf_term_set_play_state(term->compositor, value, 0, 1);
453 4 : return GF_OK;
454 : #ifdef FILTER_FIXME
455 : case GF_OPT_HTTP_MAX_RATE:
456 : gf_dm_set_data_rate(term->downloader, value);
457 : return GF_OK;
458 : #endif
459 25 : default:
460 25 : return gf_sc_set_option(term->compositor, type, value);
461 : }
462 : }
463 :
464 : GF_EXPORT
465 6 : Double gf_term_get_simulation_frame_rate(GF_Terminal *term, u32 *nb_frames_drawn)
466 : {
467 : Double fps;
468 6 : if (!term) return 0.0;
469 6 : if (nb_frames_drawn) *nb_frames_drawn = term->compositor->frame_number;
470 6 : fps = term->compositor->fps.num;
471 6 : fps /= term->compositor->fps.den;
472 6 : return fps;
473 : }
474 :
475 :
476 :
477 : /*get rendering option*/
478 : static
479 4 : u32 gf_sc_term_get_option(GF_Compositor *compositor, u32 type)
480 : {
481 4 : if (!compositor) return 0;
482 4 : switch (type) {
483 0 : case GF_OPT_HAS_JAVASCRIPT:
484 0 : return gf_sg_has_scripting();
485 0 : case GF_OPT_IS_FINISHED:
486 0 : return gf_sc_check_end_of_scene(compositor, 0);
487 0 : case GF_OPT_IS_OVER:
488 0 : return gf_sc_check_end_of_scene(compositor, 1);
489 0 : case GF_OPT_MAIN_ADDON:
490 0 : return compositor->root_scene ? compositor->root_scene->main_addon_selected : 0;
491 :
492 0 : case GF_OPT_PLAY_STATE:
493 0 : if (compositor->step_mode) return GF_STATE_STEP_PAUSE;
494 0 : if (compositor->root_scene) {
495 0 : GF_Clock *ck = compositor->root_scene->root_od->ck;
496 0 : if (!ck) return GF_STATE_PAUSED;
497 :
498 : // if (ck->Buffering) return GF_STATE_PLAYING;
499 : }
500 0 : if (compositor->play_state != GF_STATE_PLAYING) return GF_STATE_PAUSED;
501 0 : return GF_STATE_PLAYING;
502 0 : case GF_OPT_CAN_SELECT_STREAMS:
503 0 : return (compositor->root_scene && compositor->root_scene->is_dynamic_scene) ? 1 : 0;
504 : case GF_OPT_HTTP_MAX_RATE:
505 : #if FILTER_FIXME
506 : return gf_dm_get_data_rate(compositor->downloader);
507 : #else
508 : return 0;
509 : #endif
510 0 : case GF_OPT_VIDEO_BENCH:
511 0 : return compositor->bench_mode ? GF_TRUE : GF_FALSE;
512 0 : case GF_OPT_ORIENTATION_SENSORS_ACTIVE:
513 0 : return compositor->orientation_sensors_active;
514 4 : default:
515 4 : return gf_sc_get_option(compositor, type);
516 : }
517 : }
518 :
519 : /*get rendering option*/
520 : GF_EXPORT
521 4 : u32 gf_term_get_option(GF_Terminal * term, u32 type)
522 : {
523 4 : return term ? gf_sc_term_get_option(term->compositor, type) : 0;
524 : }
525 :
526 : GF_EXPORT
527 0 : GF_Err gf_term_set_size(GF_Terminal * term, u32 NewWidth, u32 NewHeight)
528 : {
529 0 : if (!term) return GF_BAD_PARAM;
530 0 : return gf_sc_set_size(term->compositor, NewWidth, NewHeight);
531 : }
532 :
533 : typedef struct
534 : {
535 : GF_ObjectManager *odm;
536 : char *service_url, *parent_url;
537 : } GF_TermConnectObject;
538 :
539 :
540 : #ifdef FILTER_FIXME
541 :
542 :
543 : /* Browses all registered relocators (ZIP-based, ISOFF-based or file-system-based to relocate a URI based on the locale */
544 : GF_EXPORT
545 : Bool gf_term_relocate_url(GF_Terminal *term, const char *service_url, const char *parent_url, char *out_relocated_url, char *out_localized_url)
546 : {
547 : u32 i, count;
548 :
549 : count = gf_list_count(term->uri_relocators);
550 : for (i=0; i<count; i++) {
551 : Bool result;
552 : GF_URIRelocator *uri_relocator = gf_list_get(term->uri_relocators, i);
553 : result = uri_relocator->relocate_uri(uri_relocator, parent_url, service_url, out_relocated_url, out_localized_url);
554 : if (result) return 1;
555 : }
556 : return 0;
557 : }
558 : #endif
559 :
560 : GF_EXPORT
561 2 : u32 gf_sc_play_from_time(GF_Compositor *compositor, u64 from_time, u32 pause_at_first_frame)
562 : {
563 2 : if (!compositor || !compositor->root_scene || !compositor->root_scene->root_od) return 0;
564 2 : if (compositor->root_scene->root_od->flags & GF_ODM_NO_TIME_CTRL) return 1;
565 :
566 0 : if (pause_at_first_frame==2) {
567 0 : if (gf_sc_term_get_option(compositor, GF_OPT_PLAY_STATE) != GF_STATE_PLAYING)
568 : pause_at_first_frame = 1;
569 : else
570 : pause_at_first_frame = 0;
571 : }
572 :
573 : /*for dynamic scene OD resources are static and all object use the same clock, so don't restart the root
574 : OD, just act as a mediaControl on all playing streams*/
575 0 : if (compositor->root_scene->is_dynamic_scene) {
576 :
577 : /*exit pause mode*/
578 0 : gf_term_set_play_state(compositor, GF_STATE_PLAYING, 1, 1);
579 :
580 0 : if (pause_at_first_frame)
581 0 : gf_term_set_play_state(compositor, GF_STATE_STEP_PAUSE, 0, 0);
582 :
583 0 : gf_sc_lock(compositor, 1);
584 0 : gf_scene_restart_dynamic(compositor->root_scene, from_time, 0, 0);
585 0 : gf_sc_lock(compositor, 0);
586 0 : return 2;
587 : }
588 :
589 : /*pause everything*/
590 0 : gf_term_set_play_state(compositor, GF_STATE_PAUSED, 0, 1);
591 : /*stop root*/
592 0 : gf_odm_stop(compositor->root_scene->root_od, 1);
593 0 : gf_scene_disconnect(compositor->root_scene, 0);
594 :
595 0 : compositor->root_scene->root_od->media_start_time = from_time;
596 :
597 0 : gf_odm_start(compositor->root_scene->root_od);
598 0 : gf_term_set_play_state(compositor, GF_STATE_PLAYING, 0, 1);
599 0 : if (pause_at_first_frame)
600 0 : gf_sc_set_option(compositor, GF_OPT_PLAY_STATE, GF_STATE_STEP_PAUSE);
601 : return 2;
602 : }
603 :
604 : GF_EXPORT
605 2 : u32 gf_term_play_from_time(GF_Terminal *term, u64 from_time, u32 pause_at_first_frame)
606 : {
607 2 : return term ? gf_sc_play_from_time(term->compositor, from_time, pause_at_first_frame) : 0;
608 : }
609 :
610 : GF_EXPORT
611 124 : Bool gf_term_user_event(GF_Terminal * term, GF_Event *evt)
612 : {
613 124 : if (term && !term->in_destroy) return gf_sc_user_event(term->compositor, evt);
614 : return GF_FALSE;
615 : }
616 :
617 :
618 : GF_EXPORT
619 2 : Double gf_term_get_framerate(GF_Terminal *term, Bool absoluteFPS)
620 : {
621 2 : if (!term || !term->compositor) return 0;
622 2 : return gf_sc_get_fps(term->compositor, absoluteFPS);
623 : }
624 :
625 : /*get main scene current time in sec*/
626 : GF_EXPORT
627 4 : u32 gf_term_get_time_in_ms(GF_Terminal *term)
628 : {
629 : GF_Clock *ck;
630 4 : GF_Compositor *compositor = term ? term->compositor : NULL;
631 4 : if (!term || !compositor->root_scene) return 0;
632 4 : ck = compositor->root_scene->root_od->ck;
633 4 : if (!ck) return 0;
634 2 : return gf_clock_media_time(ck);
635 : }
636 :
637 : GF_EXPORT
638 256 : u32 gf_term_get_elapsed_time_in_ms(GF_Terminal *term)
639 : {
640 : u32 i, count;
641 256 : GF_Compositor *compositor = term ? term->compositor : NULL;
642 256 : if (!term || !compositor->root_scene) return 0;
643 :
644 256 : count = gf_list_count(compositor->root_scene->namespaces);
645 462 : for (i=0; i<count; i++) {
646 462 : GF_SceneNamespace *sns = gf_list_get(compositor->root_scene->namespaces, i);
647 462 : GF_Clock *ck = gf_list_get(sns->clocks, 0);
648 462 : if (ck) return gf_clock_elapsed_time(ck);
649 : }
650 : return 0;
651 : }
652 :
653 : GF_EXPORT
654 4 : void gf_term_navigate_to(GF_Terminal *term, const char *toURL)
655 : {
656 4 : GF_Compositor *compositor = term ? term->compositor : NULL;
657 4 : if (!term) return;
658 4 : if (!toURL && !term->compositor->root_scene) return;
659 :
660 4 : if (term->reload_url) gf_free(term->reload_url);
661 4 : term->reload_url = NULL;
662 :
663 4 : if (toURL) {
664 4 : if (compositor->root_scene && compositor->root_scene->root_od && compositor->root_scene->root_od->scene_ns)
665 4 : term->reload_url = gf_url_concatenate(compositor->root_scene->root_od->scene_ns->url, toURL);
666 4 : if (!term->reload_url) term->reload_url = gf_strdup(toURL);
667 : }
668 4 : term->reload_state = 1;
669 : }
670 :
671 : GF_EXPORT
672 2 : GF_Err gf_term_get_viewpoint(GF_Terminal *term, u32 viewpoint_idx, const char **outName, Bool *is_bound)
673 : {
674 2 : return gf_sc_get_viewpoint(term->compositor, viewpoint_idx, outName, is_bound);
675 : }
676 :
677 : GF_EXPORT
678 2 : GF_Err gf_term_set_viewpoint(GF_Terminal *term, u32 viewpoint_idx, const char *viewpoint_name)
679 : {
680 2 : return gf_sc_set_viewpoint(term->compositor, viewpoint_idx, viewpoint_name);
681 : }
682 :
683 : GF_EXPORT
684 2 : GF_Err gf_term_add_object(GF_Terminal *term, const char *url, Bool auto_play)
685 : {
686 : GF_MediaObject *mo=NULL;
687 : //this needs refinement
688 : SFURL sfurl;
689 : MFURL mfurl;
690 2 : if (!url || !term || !term->compositor->root_scene || !term->compositor->root_scene->is_dynamic_scene) return GF_BAD_PARAM;
691 :
692 0 : sfurl.OD_ID = GF_MEDIA_EXTERNAL_ID;
693 0 : sfurl.url = (char *) url;
694 0 : mfurl.count = 1;
695 0 : mfurl.vals = &sfurl;
696 : /*only text tracks are supported for now...*/
697 0 : mo = gf_scene_get_media_object(term->compositor->root_scene, &mfurl, GF_MEDIA_OBJECT_TEXT, 1);
698 0 : if (mo) {
699 : /*check if we must deactivate it*/
700 0 : if (mo->odm) {
701 0 : if (mo->num_open && !auto_play) {
702 0 : gf_scene_select_object(term->compositor->root_scene, mo->odm);
703 : }
704 : } else {
705 0 : gf_list_del_item(term->compositor->root_scene->scene_objects, mo);
706 0 : gf_sg_vrml_mf_reset(&mo->URLs, GF_SG_VRML_MFURL);
707 0 : gf_free(mo);
708 : mo = NULL;
709 : }
710 : }
711 0 : return mo ? GF_OK : GF_NOT_SUPPORTED;
712 : }
713 :
714 :
715 : GF_EXPORT
716 2 : GF_Err gf_term_scene_update(GF_Terminal *term, char *type, char *com)
717 : {
718 : #ifndef GPAC_DISABLE_SMGR
719 2 : GF_Compositor *compositor = term ? term->compositor : NULL;
720 : GF_Err e;
721 : GF_StreamContext *sc;
722 : Bool is_xml = 0;
723 : Double time = 0;
724 : u32 i, tag;
725 : GF_SceneLoader load;
726 :
727 2 : if (!term || !com) return GF_BAD_PARAM;
728 :
729 2 : if (type && (!stricmp(type, "application/ecmascript") || !stricmp(type, "js")) ) {
730 0 : return gf_scene_execute_script(compositor->root_scene->graph, com);
731 : }
732 :
733 2 : if (!type && !strncmp(com, "gpac ", 5)) {
734 : #ifdef FILTER_FIXME
735 : com += 5;
736 : //new add-on
737 : if (compositor->root_scene && !strncmp(com, "add ", 4)) {
738 : GF_AssociatedContentLocation addon_info;
739 : memset(&addon_info, 0, sizeof(GF_AssociatedContentLocation));
740 : addon_info.external_URL = com + 4;
741 : addon_info.timeline_id = -100;
742 : gf_scene_register_associated_media(compositor->root_scene, &addon_info);
743 : return GF_OK;
744 : }
745 : //new splicing add-on
746 : if (term->root_scene && !strncmp(com, "splice ", 7)) {
747 : char *sep;
748 : Double start, end;
749 : Bool is_pts = GF_FALSE;
750 : GF_AssociatedContentLocation addon_info;
751 : memset(&addon_info, 0, sizeof(GF_AssociatedContentLocation));
752 : com += 7;
753 : if (!strnicmp(com, "pts ", 4)) {
754 : is_pts = GF_TRUE;
755 : com += 4;
756 : }
757 : sep = strchr(com, ':');
758 : start = 0;
759 : end = -1;
760 : if (sep) {
761 : sep[0]=0;
762 : if (sscanf(com, "%lf-%lf", &start, &end) != 2) {
763 : end = -1;
764 : sscanf(com, "%lf", &start);
765 : }
766 : sep[0]=':';
767 : addon_info.external_URL = sep+1;
768 : }
769 : //splice end, locate first splice with no end set and set it
770 : else if (sscanf(com, "%lf", &end)==1) {
771 : u32 count = gf_list_count(term->root_scene->declared_addons);
772 : for (i=0; i<count; i++) {
773 : GF_AddonMedia *addon = gf_list_get(term->root_scene->declared_addons, i);
774 : if (addon->is_splicing && (addon->splice_end<0) ) {
775 : addon->splice_end = end;
776 : break;
777 : }
778 : }
779 : return GF_OK;
780 : } else {
781 : //splice now, until end of spliced media
782 : addon_info.external_URL = com;
783 : }
784 : addon_info.is_splicing = GF_TRUE;
785 : addon_info.timeline_id = -100;
786 : addon_info.splice_start_time = start;
787 : addon_info.splice_end_time = end;
788 : addon_info.splice_time_pts = is_pts;
789 : gf_scene_register_associated_media(term->root_scene, &addon_info);
790 : return GF_OK;
791 : }
792 : //select object
793 : if (term->root_scene && !strncmp(com, "select ", 7)) {
794 : u32 idx = atoi(com+7);
795 : gf_term_select_object(term, gf_list_get(term->root_scene->resources, idx));
796 : return GF_OK;
797 : }
798 : #endif
799 : return GF_OK;
800 : }
801 :
802 : memset(&load, 0, sizeof(GF_SceneLoader));
803 2 : load.localPath = gf_opts_get_key("core", "cache");
804 2 : load.flags = GF_SM_LOAD_FOR_PLAYBACK | GF_SM_LOAD_CONTEXT_READY;
805 2 : load.type = GF_SM_LOAD_BT;
806 :
807 2 : if (!compositor->root_scene) {
808 :
809 : /*create a new scene*/
810 0 : compositor->root_scene = gf_scene_new(compositor, NULL);
811 0 : compositor->root_scene->root_od = gf_odm_new();
812 0 : compositor->root_scene->root_od->parentscene = NULL;
813 0 : compositor->root_scene->root_od->subscene = compositor->root_scene;
814 :
815 0 : load.ctx = gf_sm_new(compositor->root_scene->graph);
816 2 : } else if (compositor->root_scene->root_od) {
817 : u32 nb_ch = 0;
818 2 : load.ctx = gf_sm_new(compositor->root_scene->graph);
819 :
820 2 : if (compositor->root_scene->root_od->pid) nb_ch++;
821 2 : if (compositor->root_scene->root_od->extra_pids) nb_ch += gf_list_count(compositor->root_scene->root_od->extra_pids);
822 2 : for (i=0; i<nb_ch; i++) {
823 : u32 stream_type, es_id, oti;
824 : const GF_PropertyValue *p;
825 0 : GF_FilterPid *pid = compositor->root_scene->root_od->pid;
826 0 : if (i) {
827 0 : GF_ODMExtraPid *xpid = gf_list_get(compositor->root_scene->root_od->extra_pids, i-1);
828 0 : pid = xpid->pid;
829 : }
830 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
831 0 : if (!p) continue;
832 0 : stream_type = p->value.uint;
833 : switch (stream_type) {
834 0 : case GF_STREAM_OD:
835 : case GF_STREAM_SCENE:
836 : case GF_STREAM_PRIVATE_SCENE:
837 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ESID);
838 0 : if (!p)
839 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ID);
840 0 : es_id = p ? p->value.uint : 0;
841 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
842 0 : oti = p ? p->value.uint : 1;
843 :
844 0 : sc = gf_sm_stream_new(load.ctx, es_id, stream_type, oti);
845 0 : if (stream_type==GF_STREAM_PRIVATE_SCENE) sc->streamType = GF_STREAM_SCENE;
846 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
847 0 : sc->timeScale = p ? p->value.uint : 1000;
848 0 : break;
849 : }
850 : }
851 : } else {
852 : return GF_BAD_PARAM;
853 : }
854 2 : load.ctx->max_node_id = gf_sg_get_max_node_id(compositor->root_scene->graph);
855 :
856 : i=0;
857 2 : while ((com[i] == ' ') || (com[i] == '\r') || (com[i] == '\n') || (com[i] == '\t')) i++;
858 2 : if (com[i]=='<') is_xml = 1;
859 :
860 2 : load.type = is_xml ? GF_SM_LOAD_XMTA : GF_SM_LOAD_BT;
861 2 : time = gf_scene_get_time(compositor->root_scene);
862 :
863 :
864 2 : if (type && (!stricmp(type, "application/x-laser+xml") || !stricmp(type, "laser"))) {
865 0 : load.type = GF_SM_LOAD_XSR;
866 0 : time = gf_scene_get_time(compositor->root_scene);
867 : }
868 2 : else if (type && (!stricmp(type, "image/svg+xml") || !stricmp(type, "svg")) ) {
869 0 : load.type = GF_SM_LOAD_XSR;
870 0 : time = gf_scene_get_time(compositor->root_scene);
871 : }
872 2 : else if (type && (!stricmp(type, "model/x3d+xml") || !stricmp(type, "x3d")) ) load.type = GF_SM_LOAD_X3D;
873 2 : else if (type && (!stricmp(type, "model/x3d+vrml") || !stricmp(type, "x3dv")) ) load.type = GF_SM_LOAD_X3DV;
874 2 : else if (type && (!stricmp(type, "model/vrml") || !stricmp(type, "vrml")) ) load.type = GF_SM_LOAD_VRML;
875 2 : else if (type && (!stricmp(type, "application/x-xmt") || !stricmp(type, "xmt")) ) load.type = GF_SM_LOAD_XMTA;
876 2 : else if (type && (!stricmp(type, "application/x-bt") || !stricmp(type, "bt")) ) load.type = GF_SM_LOAD_BT;
877 2 : else if (gf_sg_get_root_node(compositor->root_scene->graph)) {
878 2 : tag = gf_node_get_tag(gf_sg_get_root_node(compositor->root_scene->graph));
879 2 : if (tag >= GF_NODE_RANGE_FIRST_SVG) {
880 0 : load.type = GF_SM_LOAD_XSR;
881 0 : time = gf_scene_get_time(compositor->root_scene);
882 2 : } else if (tag>=GF_NODE_RANGE_FIRST_X3D) {
883 0 : load.type = is_xml ? GF_SM_LOAD_X3D : GF_SM_LOAD_X3DV;
884 : } else {
885 2 : load.type = is_xml ? GF_SM_LOAD_XMTA : GF_SM_LOAD_BT;
886 2 : time = gf_scene_get_time(compositor->root_scene);
887 : }
888 : }
889 :
890 2 : e = gf_sm_load_init(&load);
891 2 : if (!e) e = gf_sm_load_string(&load, com, 1);
892 2 : gf_sm_load_done(&load);
893 2 : if (!e) {
894 : u32 j, au_count, st_count;
895 0 : st_count = gf_list_count(load.ctx->streams);
896 0 : for (i=0; i<st_count; i++) {
897 0 : sc = (GF_StreamContext*)gf_list_get(load.ctx->streams, i);
898 0 : au_count = gf_list_count(sc->AUs);
899 0 : for (j=0; j<au_count; j++) {
900 0 : GF_AUContext *au = (GF_AUContext *)gf_list_get(sc->AUs, j);
901 0 : e = gf_sg_command_apply_list(compositor->root_scene->graph, au->commands, time);
902 0 : if (e) break;
903 : }
904 : }
905 : }
906 2 : if (!compositor->root_scene->graph_attached) {
907 0 : if (!load.ctx->scene_width || !load.ctx->scene_height) {
908 : // load.ctx->scene_width = term->compositor->width;
909 : // load.ctx->scene_height = term->compositor->height;
910 : }
911 0 : gf_sg_set_scene_size_info(compositor->root_scene->graph, load.ctx->scene_width, load.ctx->scene_height, load.ctx->is_pixel_metrics);
912 0 : gf_scene_attach_to_compositor(compositor->root_scene);
913 : }
914 2 : gf_sm_del(load.ctx);
915 2 : return e;
916 : #else
917 : return GF_NOT_SUPPORTED;
918 : #endif
919 : }
920 :
921 : GF_EXPORT
922 2 : GF_Err gf_term_get_screen_buffer(GF_Terminal *term, GF_VideoSurface *framebuffer)
923 : {
924 2 : if (!term) return GF_BAD_PARAM;
925 2 : return gf_sc_get_screen_buffer(term->compositor, framebuffer, 0);
926 : }
927 :
928 : GF_EXPORT
929 2 : GF_Err gf_term_get_offscreen_buffer(GF_Terminal *term, GF_VideoSurface *framebuffer, u32 view_idx, GF_CompositorGrabMode depth_buffer_type)
930 : {
931 2 : if (!term) return GF_BAD_PARAM;
932 2 : return gf_sc_get_offscreen_buffer(term->compositor, framebuffer, view_idx, depth_buffer_type);
933 : }
934 :
935 : GF_EXPORT
936 2 : GF_Err gf_term_release_screen_buffer(GF_Terminal *term, GF_VideoSurface *framebuffer)
937 : {
938 2 : if (!term) return GF_BAD_PARAM;
939 2 : return gf_sc_release_screen_buffer(term->compositor, framebuffer);
940 : }
941 :
942 : GF_EXPORT
943 2 : const char *gf_term_get_text_selection(GF_Terminal *term, Bool probe_only)
944 : {
945 : Bool has_text;
946 2 : if (!term) return NULL;
947 2 : has_text = gf_sc_has_text_selection(term->compositor);
948 2 : if (!has_text) return NULL;
949 0 : if (probe_only) return "";
950 0 : return gf_sc_get_selected_text(term->compositor);
951 : }
952 :
953 :
954 : GF_EXPORT
955 2 : GF_Err gf_term_paste_text(GF_Terminal *term, const char *txt, Bool probe_only)
956 : {
957 2 : if (!term) return GF_BAD_PARAM;
958 2 : if (probe_only) return gf_sc_paste_text(term->compositor, NULL);
959 0 : return gf_sc_paste_text(term->compositor, txt);
960 : }
961 :
962 :
963 : enum
964 : {
965 : GF_ACTION_PLAY,
966 : GF_ACTION_STOP,
967 : GF_ACTION_STEP,
968 : GF_ACTION_EXIT,
969 : GF_ACTION_MUTE,
970 : GF_ACTION_VOLUP,
971 : GF_ACTION_VOLDOWN,
972 : GF_ACTION_JUMP_FORWARD,
973 : GF_ACTION_JUMP_BACKWARD,
974 : GF_ACTION_JUMP_START,
975 : GF_ACTION_JUMP_END,
976 : GF_ACTION_VERY_FAST_FORWARD,
977 : GF_ACTION_FAST_FORWARD,
978 : GF_ACTION_SLOW_FORWARD,
979 : GF_ACTION_VERY_FAST_REWIND,
980 : GF_ACTION_FAST_REWIND,
981 : GF_ACTION_SLOW_REWIND,
982 : GF_ACTION_NEXT,
983 : GF_ACTION_PREVIOUS,
984 : GF_ACTION_QUALITY_UP,
985 : GF_ACTION_QUALITY_DOWN,
986 : };
987 :
988 : #ifdef FILTER_FIXME //unused for now, need to patch compositor shortcuts
989 : static void set_clocks_speed(GF_Compositor *compositor, Fixed ratio)
990 : {
991 : u32 i, j;
992 : GF_SceneNamespace *ns;
993 :
994 : /*pause all clocks on all services*/
995 : i=0;
996 : while ( (ns = (GF_SceneNamespace*)gf_list_enum(compositor->root_scene->namespaces, &i)) ) {
997 : GF_Clock *ck;
998 : j=0;
999 : while ( (ck = (GF_Clock *)gf_list_enum(ns->Clocks, &j)) ) {
1000 : Fixed s = gf_mulfix(ck->speed, ratio);
1001 : gf_clock_set_speed(ck, s);
1002 : }
1003 : }
1004 : }
1005 : #endif
1006 :
1007 : GF_EXPORT
1008 2 : GF_Err gf_term_set_speed(GF_Terminal *term, Fixed speed)
1009 : {
1010 : GF_Fraction fps;
1011 : u32 i, j;
1012 : GF_SceneNamespace *ns;
1013 : Bool restart = 0;
1014 2 : u32 scene_time = gf_term_get_time_in_ms(term);
1015 :
1016 2 : if (!term || !speed) return GF_BAD_PARAM;
1017 :
1018 2 : if (speed<0) {
1019 0 : i=0;
1020 0 : while ( (ns = (GF_SceneNamespace*)gf_list_enum(term->compositor->root_scene->namespaces, &i)) ) {
1021 : #ifdef FILTER_FIXME
1022 : GF_NetworkCommand com;
1023 : GF_Err e;
1024 : memset(&com, 0, sizeof(GF_NetworkCommand));
1025 : com.base.command_type = GF_NET_SERVICE_CAN_REVERSE_PLAYBACK;
1026 : e = gf_term_service_command(ns, &com);
1027 : if (e != GF_OK) {
1028 : return e;
1029 : }
1030 : #endif
1031 : }
1032 : }
1033 :
1034 : /*adjust all clocks on all services, if possible*/
1035 2 : i=0;
1036 6 : while ( (ns = (GF_SceneNamespace*)gf_list_enum(term->compositor->root_scene->namespaces, &i)) ) {
1037 : GF_Clock *ck;
1038 2 : ns->set_speed = speed;
1039 2 : j=0;
1040 4 : while (ns->clocks && (ck = (GF_Clock *)gf_list_enum(ns->clocks, &j)) ) {
1041 : //we will have to reissue a PLAY command since playback direction changed
1042 0 : if ( gf_mulfix(ck->speed,speed) < 0)
1043 : restart = 1;
1044 0 : gf_clock_set_speed(ck, speed);
1045 :
1046 0 : if (ns->owner) {
1047 0 : gf_odm_set_speed(ns->owner, speed, GF_FALSE);
1048 0 : if (ns->owner->subscene) {
1049 0 : u32 k=0;
1050 : GF_ObjectManager *odm;
1051 0 : GF_Scene *scene = ns->owner->subscene;
1052 0 : while ( (odm = gf_list_enum(scene->resources, &k))) {
1053 0 : gf_odm_set_speed(odm, speed, GF_FALSE);
1054 : }
1055 : }
1056 : }
1057 : }
1058 : }
1059 :
1060 2 : if (restart) {
1061 0 : if (term->compositor->root_scene->is_dynamic_scene) {
1062 0 : gf_scene_restart_dynamic(term->compositor->root_scene, scene_time, 0, 0);
1063 : }
1064 : }
1065 :
1066 2 : if (speed<0)
1067 0 : speed = -speed;
1068 :
1069 2 : fps = term->compositor->fps;
1070 2 : if (fps.den<1000) {
1071 2 : fps.num = fps.num * (u32) (1000 * FIX2FLT(speed));
1072 2 : fps.den *= 1000;
1073 : } else {
1074 0 : fps.num = (u32) (fps.num * FIX2FLT(speed));
1075 : }
1076 2 : gf_media_get_reduced_frame_rate(&fps.num, &fps.den);
1077 2 : gf_sc_set_fps(term->compositor, fps);
1078 2 : return GF_OK;
1079 : }
1080 :
1081 : #ifdef FILTER_FIXME
1082 : GF_EXPORT
1083 : void gf_term_process_shortcut(GF_Terminal *term, GF_Event *ev)
1084 : {
1085 : GF_Event evt;
1086 : if (ev->type==GF_EVENT_KEYDOWN) {
1087 : u32 i;
1088 : u8 mod = 0;
1089 : if (ev->key.flags & GF_KEY_MOD_CTRL) mod |= GF_KEY_MOD_CTRL;
1090 : if (ev->key.flags & GF_KEY_MOD_ALT) mod |= GF_KEY_MOD_ALT;
1091 :
1092 : for (i=0; i<MAX_SHORTCUTS; i++) {
1093 : u32 val;
1094 : if (!term->shortcuts[i].code) break;
1095 : if (term->shortcuts[i].mods!=mod) continue;
1096 : if (term->shortcuts[i].code!=ev->key.key_code) continue;
1097 :
1098 : switch (term->shortcuts[i].action) {
1099 : case GF_ACTION_PLAY:
1100 : if (gf_term_get_option(term, GF_OPT_PLAY_STATE) == GF_STATE_PAUSED) {
1101 : gf_term_set_option(term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING);
1102 : } else if (term->speed_ratio != FIX_ONE) {
1103 : set_clocks_speed(term, gf_divfix(1, term->speed_ratio) );
1104 : term->speed_ratio = FIX_ONE;
1105 : } else {
1106 : gf_term_set_option(term, GF_OPT_PLAY_STATE, GF_STATE_PAUSED);
1107 : }
1108 : break;
1109 : case GF_ACTION_STOP:
1110 : gf_term_play_from_time(term, 0, 1);
1111 : break;
1112 : case GF_ACTION_NEXT:
1113 : evt.type = GF_EVENT_KEYDOWN;
1114 : evt.key.key_code = GF_KEY_MEDIANEXTTRACK;
1115 : gf_term_send_event(term, &evt);
1116 : break;
1117 : case GF_ACTION_PREVIOUS:
1118 : evt.type = GF_EVENT_KEYDOWN;
1119 : evt.key.key_code = GF_KEY_MEDIAPREVIOUSTRACK;
1120 : gf_term_send_event(term, &evt);
1121 : break;
1122 :
1123 : case GF_ACTION_STEP:
1124 : gf_term_set_option(term, GF_OPT_PLAY_STATE, GF_STATE_STEP_PAUSE);
1125 : break;
1126 : case GF_ACTION_EXIT:
1127 : memset(&evt, 0, sizeof(GF_Event));
1128 : evt.type = GF_EVENT_QUIT;
1129 : gf_term_send_event(term, &evt);
1130 : break;
1131 : case GF_ACTION_MUTE:
1132 : gf_term_set_option(term, GF_OPT_AUDIO_MUTE, gf_term_get_option(term, GF_OPT_AUDIO_MUTE) ? 0 : 1);
1133 : break;
1134 : case GF_ACTION_VOLUP:
1135 : val = gf_term_get_option(term, GF_OPT_AUDIO_VOLUME);
1136 : if (val<95) val += 5;
1137 : else val = 100;
1138 : gf_term_set_option(term, GF_OPT_AUDIO_VOLUME, val);
1139 : break;
1140 : case GF_ACTION_VOLDOWN:
1141 : val = gf_term_get_option(term, GF_OPT_AUDIO_VOLUME);
1142 : if (val>5) val -= 5;
1143 : else val = 0;
1144 : gf_term_set_option(term, GF_OPT_AUDIO_VOLUME, val);
1145 : break;
1146 : case GF_ACTION_JUMP_FORWARD:
1147 : case GF_ACTION_JUMP_BACKWARD:
1148 : case GF_ACTION_VERY_FAST_REWIND:
1149 : case GF_ACTION_FAST_REWIND:
1150 : case GF_ACTION_SLOW_REWIND:
1151 : if (0 && term->root_scene && !(term->root_scene->root_od->flags & GF_ODM_NO_TIME_CTRL) ) {
1152 : s32 res;
1153 : u32 dur = (u32) term->root_scene->duration ;
1154 : val = gf_term_get_time_in_ms(term);
1155 : res = val;
1156 : switch (term->shortcuts[i].action) {
1157 : case GF_ACTION_JUMP_BACKWARD:
1158 : case GF_ACTION_FAST_REWIND:
1159 : res -= (s32) (5*dur/100);
1160 : if (res<0) res = 0;
1161 : break;
1162 : case GF_ACTION_VERY_FAST_REWIND:
1163 : res -= (s32) (10*dur/100);
1164 : if (res<0) res = 0;
1165 : break;
1166 : case GF_ACTION_SLOW_REWIND:
1167 : res -= (s32) (dur/100);
1168 : if (res<0) res = 0;
1169 : break;
1170 : default:
1171 : res += (s32) (5*dur/100);
1172 : if (res > (s32)dur) res = dur;
1173 : break;
1174 : }
1175 : gf_term_play_from_time(term, res, 2);
1176 : }
1177 : break;
1178 : case GF_ACTION_JUMP_START:
1179 : if (term->root_scene && !(term->root_scene->root_od->flags & GF_ODM_NO_TIME_CTRL) ) {
1180 : gf_term_play_from_time(term, 0, 2);
1181 : }
1182 : break;
1183 : case GF_ACTION_JUMP_END:
1184 : if (term->root_scene && !(term->root_scene->root_od->flags & GF_ODM_NO_TIME_CTRL) ) {
1185 : gf_term_play_from_time(term, term->root_scene->duration, 2);
1186 : }
1187 : break;
1188 : case GF_ACTION_VERY_FAST_FORWARD:
1189 : case GF_ACTION_FAST_FORWARD:
1190 : case GF_ACTION_SLOW_FORWARD:
1191 : if (term->speed_ratio != FIX_ONE) {
1192 : set_clocks_speed(term, gf_divfix(1, term->speed_ratio) );
1193 : term->speed_ratio = FIX_ONE;
1194 : }
1195 : else {
1196 : switch (term->shortcuts[i].action) {
1197 : case GF_ACTION_VERY_FAST_FORWARD:
1198 : term->speed_ratio = INT2FIX(4);
1199 : break;
1200 : case GF_ACTION_FAST_FORWARD:
1201 : term->speed_ratio = INT2FIX(2);
1202 : break;
1203 : case GF_ACTION_SLOW_FORWARD:
1204 : term->speed_ratio = INT2FIX(1)/4;
1205 : break;
1206 : }
1207 : set_clocks_speed(term, term->speed_ratio);
1208 : }
1209 : break;
1210 : case GF_ACTION_QUALITY_UP:
1211 : gf_term_switch_quality(term, 1);
1212 : break;
1213 : case GF_ACTION_QUALITY_DOWN:
1214 : gf_term_switch_quality(term, 0);
1215 : break;
1216 : }
1217 : break;
1218 : }
1219 : }
1220 : }
1221 :
1222 : void gf_term_load_shortcuts(GF_Terminal *term)
1223 : {
1224 : char szVal[51];
1225 : u32 i, k, count;
1226 :
1227 : memset(term->shortcuts, 0, sizeof(GF_Shortcut)*MAX_SHORTCUTS);
1228 : count = gf_opts_get_key_count("Shortcuts");
1229 : k = 0;
1230 : for (i=0; i<count; i++) {
1231 : char *name = (char*)gf_opts_get_key_name("Shortcuts", i);
1232 : char *val = (char*)gf_opts_get_key("Shortcuts", name);
1233 : if (!name || !val) continue;
1234 :
1235 : strncpy(szVal, val, 50);
1236 : szVal[50] = 0;
1237 : strlwr(szVal);
1238 : val = szVal;
1239 :
1240 : while (strchr(val, '+')) {
1241 : if (!strnicmp(val, "ctrl+", 5)) {
1242 : val += 5;
1243 : term->shortcuts[k].mods |= GF_KEY_MOD_CTRL;
1244 : }
1245 : if (!strnicmp(val, "alt+", 4)) {
1246 : val += 4;
1247 : term->shortcuts[k].mods |= GF_KEY_MOD_ALT;
1248 : }
1249 : }
1250 : #ifndef GPAC_DISABLE_SVG
1251 : term->shortcuts[k].code = gf_dom_get_key_type((char *)val);
1252 : #endif
1253 : if (!term->shortcuts[k].code) continue;
1254 :
1255 : if (!stricmp(name, "Play") || !stricmp(name, "Pause")) term->shortcuts[k].action = GF_ACTION_PLAY;
1256 : else if (!stricmp(name, "Stop")) term->shortcuts[k].action = GF_ACTION_STOP;
1257 : else if (!stricmp(name, "Step")) term->shortcuts[k].action = GF_ACTION_STEP;
1258 : else if (!stricmp(name, "Exit")) term->shortcuts[k].action = GF_ACTION_EXIT;
1259 : else if (!stricmp(name, "Mute")) term->shortcuts[k].action = GF_ACTION_MUTE;
1260 : else if (!stricmp(name, "VolumeUp")) term->shortcuts[k].action = GF_ACTION_VOLUP;
1261 : else if (!stricmp(name, "VolumeDown")) term->shortcuts[k].action = GF_ACTION_VOLDOWN;
1262 : else if (!stricmp(name, "JumpForward")) term->shortcuts[k].action = GF_ACTION_JUMP_FORWARD;
1263 : else if (!stricmp(name, "JumpBackward")) term->shortcuts[k].action = GF_ACTION_JUMP_BACKWARD;
1264 : else if (!stricmp(name, "JumpStart")) term->shortcuts[k].action = GF_ACTION_JUMP_START;
1265 : else if (!stricmp(name, "JumpEnd")) term->shortcuts[k].action = GF_ACTION_JUMP_END;
1266 : else if (!stricmp(name, "VeryFastForward")) term->shortcuts[k].action = GF_ACTION_VERY_FAST_FORWARD;
1267 : else if (!stricmp(name, "FastForward")) term->shortcuts[k].action = GF_ACTION_FAST_FORWARD;
1268 : else if (!stricmp(name, "SlowForward")) term->shortcuts[k].action = GF_ACTION_SLOW_FORWARD;
1269 : else if (!stricmp(name, "VeryFastRewind")) term->shortcuts[k].action = GF_ACTION_VERY_FAST_REWIND;
1270 : else if (!stricmp(name, "FastRewind")) term->shortcuts[k].action = GF_ACTION_FAST_REWIND;
1271 : else if (!stricmp(name, "SlowRewind")) term->shortcuts[k].action = GF_ACTION_SLOW_REWIND;
1272 : else if (!stricmp(name, "Next")) term->shortcuts[k].action = GF_ACTION_NEXT;
1273 : else if (!stricmp(name, "Previous")) term->shortcuts[k].action = GF_ACTION_PREVIOUS;
1274 : else if (!stricmp(name, "QualityUp")) term->shortcuts[k].action = GF_ACTION_QUALITY_UP;
1275 : else if (!stricmp(name, "QualityDown")) term->shortcuts[k].action = GF_ACTION_QUALITY_DOWN;
1276 : else {
1277 : term->shortcuts[k].mods = 0;
1278 : term->shortcuts[k].code = 0;
1279 : continue;
1280 : }
1281 : k++;
1282 : if (k==MAX_SHORTCUTS) break;
1283 : }
1284 : }
1285 : #endif
1286 :
1287 :
1288 : GF_EXPORT
1289 2 : void gf_term_switch_quality(GF_Terminal *term, Bool up)
1290 : {
1291 2 : if (term)
1292 2 : gf_scene_switch_quality(term->compositor->root_scene, up);
1293 2 : }
1294 :
1295 : GF_EXPORT
1296 4 : GF_Err gf_term_get_visual_output_size(GF_Terminal *term, u32 *width, u32 *height)
1297 : {
1298 4 : if (!term) return GF_BAD_PARAM;
1299 4 : if (width) *width = term->compositor->display_width;
1300 4 : if (height) *height = term->compositor->display_height;
1301 : return GF_OK;
1302 : }
1303 :
1304 : GF_EXPORT
1305 404 : Bool gf_term_process_step(GF_Terminal *term)
1306 : {
1307 :
1308 404 : term->compositor->frame_was_produced = GF_FALSE;
1309 : /*need to reload*/
1310 404 : if (term->reload_state == 1) {
1311 0 : term->reload_state = 0;
1312 0 : gf_term_disconnect(term);
1313 0 : term->reload_state = 2;
1314 : }
1315 404 : if (term->reload_state == 2) {
1316 0 : if (!term->compositor->root_scene) {
1317 0 : term->reload_state = 0;
1318 0 : if (term->reload_url) {
1319 0 : gf_term_connect(term, term->reload_url);
1320 0 : gf_free(term->reload_url);
1321 : }
1322 0 : term->reload_url = NULL;
1323 : }
1324 : }
1325 :
1326 404 : gf_fs_run_step(term->fsess);
1327 404 : return term->compositor->frame_was_produced;
1328 : }
1329 :
1330 36 : static Bool check_in_scene(GF_Scene *scene, GF_ObjectManager *odm)
1331 : {
1332 : u32 i;
1333 : GF_ObjectManager *ptr, *root;
1334 36 : if (!scene) return 0;
1335 36 : root = scene->root_od;
1336 36 : if (odm == root) return 1;
1337 0 : scene = root->subscene;
1338 :
1339 0 : i=0;
1340 0 : while ((ptr = (GF_ObjectManager *)gf_list_enum(scene->resources, &i))) {
1341 0 : if (ptr == odm) return 1;
1342 0 : if (check_in_scene(ptr->subscene, odm)) return 1;
1343 : }
1344 : return 0;
1345 : }
1346 :
1347 : static Bool gf_term_check_odm(GF_Terminal *term, GF_ObjectManager *odm)
1348 : {
1349 36 : if (!term->compositor->root_scene) return 0;
1350 36 : return check_in_scene(term->compositor->root_scene, odm);
1351 : }
1352 :
1353 :
1354 : /*returns top-level OD of the presentation*/
1355 : GF_EXPORT
1356 18 : GF_ObjectManager *gf_term_get_root_object(GF_Terminal *term)
1357 : {
1358 18 : if (!term) return NULL;
1359 18 : if (!term->compositor->root_scene) return NULL;
1360 18 : return term->compositor->root_scene->root_od;
1361 : }
1362 :
1363 : /*returns number of sub-ODs in the current root. scene_od must be an inline OD*/
1364 : GF_EXPORT
1365 10 : u32 gf_term_get_object_count(GF_Terminal *term, GF_ObjectManager *scene_od)
1366 : {
1367 10 : if (!term || !scene_od) return 0;
1368 20 : if (!gf_term_check_odm(term, scene_od)) return 0;
1369 10 : if (!scene_od->subscene) return 0;
1370 10 : return gf_list_count(scene_od->subscene->resources);
1371 : }
1372 :
1373 : /*returns indexed (0-based) OD manager in the scene*/
1374 : GF_EXPORT
1375 2 : GF_ObjectManager *gf_term_get_object(GF_Terminal *term, GF_ObjectManager *scene_od, u32 index)
1376 : {
1377 2 : if (!term || !scene_od) return NULL;
1378 4 : if (!gf_term_check_odm(term, scene_od)) return NULL;
1379 2 : if (!scene_od->subscene) return NULL;
1380 2 : return (GF_ObjectManager *) gf_list_get(scene_od->subscene->resources, index);
1381 : }
1382 :
1383 : GF_EXPORT
1384 2 : u32 gf_term_object_subscene_type(GF_Terminal *term, GF_ObjectManager *odm)
1385 : {
1386 2 : if (!term || !odm) return 0;
1387 0 : if (!gf_term_check_odm(term, odm)) return 0;
1388 :
1389 0 : if (!odm->subscene) return 0;
1390 : #ifndef GPAC_DISABLE_VRML
1391 0 : if (odm->parentscene) {
1392 :
1393 0 : u32 i=0;
1394 : GF_ProtoLink *pl;
1395 : i=0;
1396 0 : while ((pl = (GF_ProtoLink*)gf_list_enum(odm->parentscene->extern_protos, &i))) {
1397 0 : if (pl->mo->odm == odm) return 3;
1398 : }
1399 : return 2;
1400 : }
1401 :
1402 : #endif
1403 : return 1;
1404 : }
1405 :
1406 : /*select given object when stream selection is available*/
1407 : GF_EXPORT
1408 2 : void gf_term_select_object(GF_Terminal *term, GF_ObjectManager *odm)
1409 : {
1410 2 : if (!term || !odm) return;
1411 0 : if (!gf_term_check_odm(term, odm)) return;
1412 :
1413 0 : gf_scene_select_object(term->compositor->root_scene, odm);
1414 : }
1415 :
1416 :
1417 : /*select given object when stream selection is available*/
1418 : GF_EXPORT
1419 2 : void gf_term_select_service(GF_Terminal *term, GF_ObjectManager *odm, u32 service_id)
1420 : {
1421 2 : if (!term || !odm || !odm->subscene) return;
1422 4 : if (!gf_term_check_odm(term, odm)) return;
1423 :
1424 : #ifndef GPAC_DISABLE_VRML
1425 2 : gf_scene_set_service_id(odm->subscene, service_id);
1426 : #endif
1427 : }
1428 :
1429 : GF_EXPORT
1430 2 : Bool gf_term_find_service(GF_Terminal *term, GF_ObjectManager *odm, u32 service_id)
1431 : {
1432 : u32 i;
1433 : GF_ObjectManager *anodm;
1434 2 : if (!term || !odm || !odm->subscene) return GF_FALSE;
1435 4 : if (!gf_term_check_odm(term, odm)) return GF_FALSE;
1436 :
1437 2 : i=0;
1438 4 : while ((anodm = gf_list_enum(odm->subscene->resources, &i))) {
1439 0 : if (anodm->ServiceID==service_id) return GF_TRUE;
1440 : }
1441 : return GF_FALSE;
1442 : }
1443 :
1444 : /*select given object when stream selection is available*/
1445 : GF_EXPORT
1446 2 : void gf_term_toggle_addons(GF_Terminal *term, Bool show_addons)
1447 : {
1448 2 : if (!term || !term->compositor->root_scene || !term->compositor->root_scene->is_dynamic_scene) return;
1449 : #ifndef GPAC_DISABLE_VRML
1450 0 : gf_scene_toggle_addons(term->compositor->root_scene, show_addons);
1451 : #endif
1452 : }
1453 :
1454 : GF_EXPORT
1455 4 : u32 gf_term_get_current_service_id(GF_Terminal *term)
1456 : {
1457 : SFURL *the_url;
1458 : GF_MediaObject *mo;
1459 4 : GF_Compositor *compositor = term ? term->compositor : NULL;
1460 4 : if (!term || !term->compositor->root_scene) return 0;
1461 4 : if (! term->compositor->root_scene->is_dynamic_scene) return term->compositor->root_scene->root_od->ServiceID;
1462 :
1463 0 : if (compositor->root_scene->visual_url.OD_ID || compositor->root_scene->visual_url.url)
1464 0 : the_url = &compositor->root_scene->visual_url;
1465 : else
1466 0 : the_url = &compositor->root_scene->audio_url;
1467 :
1468 0 : mo = gf_scene_find_object(compositor->root_scene, the_url->OD_ID, the_url->url);
1469 0 : if (mo && mo->odm) return mo->odm->ServiceID;
1470 : return 0;
1471 : }
1472 :
1473 :
1474 :
1475 :
1476 : GF_EXPORT
1477 12 : GF_Err gf_term_get_object_info(GF_Terminal *term, GF_ObjectManager *odm, GF_MediaInfo *info)
1478 : {
1479 12 : if (!term || !odm || !info) return GF_BAD_PARAM;
1480 24 : if (!gf_term_check_odm(term, odm)) return GF_BAD_PARAM;
1481 12 : return gf_odm_get_object_info(odm, info);
1482 : }
1483 :
1484 :
1485 : GF_EXPORT
1486 2 : Bool gf_term_get_download_info(GF_Terminal *term, GF_ObjectManager *odm, u32 *d_enum, const char **url, u32 *bytes_done, u32 *total_bytes, u32 *bytes_per_sec)
1487 : {
1488 : u32 nb_ch;
1489 : const GF_PropertyValue *p;
1490 2 : GF_PropertyEntry *pe=NULL;
1491 : GF_FilterPid *pid=NULL;
1492 4 : if (!term || !odm || !gf_term_check_odm(term, odm)) return GF_FALSE;
1493 2 : if (odm->scene_ns->owner != odm)
1494 : return GF_FALSE;
1495 :
1496 2 : nb_ch = odm->pid ? 1 : 0;
1497 2 : if (odm->extra_pids)
1498 0 : nb_ch += gf_list_count(odm->extra_pids);
1499 :
1500 2 : if (!nb_ch) {
1501 : GF_ObjectManager *anodm;
1502 2 : if (*d_enum || !odm->subscene) return GF_FALSE;
1503 2 : anodm = gf_list_get(odm->subscene->resources, 0);
1504 2 : if (anodm) pid = anodm->pid;
1505 : } else {
1506 :
1507 0 : if (*d_enum >= nb_ch) return GF_FALSE;
1508 0 : if (! *d_enum) pid = odm->pid;
1509 0 : else pid = gf_list_get(odm->extra_pids, *d_enum - 1);
1510 : }
1511 0 : if (!pid) return GF_FALSE;
1512 :
1513 0 : (*d_enum) ++;
1514 :
1515 0 : p = gf_filter_pid_get_info(pid, GF_PROP_PID_DOWN_RATE, &pe);
1516 0 : if (p && bytes_per_sec) {
1517 0 : *bytes_per_sec = p->value.uint;
1518 0 : *bytes_per_sec /= 8;
1519 : }
1520 :
1521 0 : p = gf_filter_pid_get_info(pid, GF_PROP_PID_DOWN_BYTES, &pe);
1522 0 : if (p && bytes_done) *bytes_done = (u32) p->value.longuint;
1523 :
1524 0 : p = gf_filter_pid_get_info(pid, GF_PROP_PID_DOWN_SIZE, &pe);
1525 0 : if (p && total_bytes) *total_bytes = (u32) p->value.longuint;
1526 :
1527 0 : p = gf_filter_pid_get_info(pid, GF_PROP_PID_URL, &pe);
1528 0 : if (p && url) *url = p->value.string;
1529 :
1530 0 : gf_filter_release_property(pe);
1531 :
1532 0 : return GF_TRUE;
1533 : }
1534 :
1535 : GF_EXPORT
1536 2 : Bool gf_term_get_channel_net_info(GF_Terminal *term, GF_ObjectManager *odm, u32 *d_enum, u32 *chid, GF_TermNetStats *net_stats, GF_Err *ret_code)
1537 : {
1538 : u32 nb_ch;
1539 : const GF_PropertyValue *p;
1540 2 : GF_PropertyEntry *pe=NULL;
1541 : GF_FilterPid *pid;
1542 4 : if (!term || !odm || !gf_term_check_odm(term, odm)) return GF_FALSE;
1543 :
1544 2 : nb_ch = odm->pid ? 1 : 0;
1545 2 : if (odm->extra_pids)
1546 0 : nb_ch += gf_list_count(odm->extra_pids);
1547 :
1548 2 : if (*d_enum >= nb_ch) return GF_FALSE;
1549 0 : if (! *d_enum) pid = odm->pid;
1550 0 : else pid = gf_list_get(odm->extra_pids, *d_enum - 1);
1551 0 : if (!pid) return GF_FALSE;
1552 0 : (*d_enum) ++;
1553 :
1554 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ID);
1555 0 : if (p) (*chid) = p->value.uint;
1556 :
1557 0 : (*ret_code) = GF_OK;
1558 :
1559 : memset(net_stats, 0, sizeof(GF_TermNetStats));
1560 0 : p = gf_filter_pid_get_info_str(pid, "nets:loss", &pe);
1561 0 : if (p) net_stats->pck_loss_percentage = p->value.fnumber;
1562 :
1563 0 : p = gf_filter_pid_get_info_str(pid, "nets:interleaved", &pe);
1564 0 : if (p) {
1565 0 : net_stats->multiplex_port = p->value.uint;
1566 0 : p = gf_filter_pid_get_info_str(pid, "nets:rtpid", &pe);
1567 0 : if (p) net_stats->port = p->value.uint;
1568 0 : p = gf_filter_pid_get_info_str(pid, "nets:rtcpid", &pe);
1569 0 : if (p) net_stats->ctrl_port = p->value.uint;
1570 : } else {
1571 0 : p = gf_filter_pid_get_info_str(pid, "nets:rtpp", &pe);
1572 0 : if (p) net_stats->port = p->value.uint;
1573 0 : p = gf_filter_pid_get_info_str(pid, "nets:rtcpp", &pe);
1574 0 : if (p) net_stats->ctrl_port = p->value.uint;
1575 : }
1576 :
1577 0 : p = gf_filter_pid_get_info_str(pid, "nets:bw_down", &pe);
1578 0 : if (p) net_stats->bw_down = p->value.uint;
1579 0 : p = gf_filter_pid_get_info_str(pid, "nets:bw_up", &pe);
1580 0 : if (p) net_stats->bw_up = p->value.uint;
1581 0 : p = gf_filter_pid_get_info_str(pid, "nets:ctrl_bw_down", &pe);
1582 0 : if (p) net_stats->ctrl_bw_down = p->value.uint;
1583 0 : p = gf_filter_pid_get_info_str(pid, "nets:ctrl_bw_up", &pe);
1584 0 : if (p) net_stats->ctrl_bw_up = p->value.uint;
1585 :
1586 0 : gf_filter_release_property(pe);
1587 0 : return GF_TRUE;
1588 : }
1589 :
1590 : GF_EXPORT
1591 4 : GF_Err gf_term_get_service_info(GF_Terminal *term, GF_ObjectManager *odm, GF_TermURLInfo *urli)
1592 : {
1593 : const GF_PropertyValue *p;
1594 4 : GF_PropertyEntry *pe=NULL;
1595 : GF_FilterPid *pid;
1596 4 : if (urli) memset(urli, 0, sizeof(GF_TermURLInfo));
1597 8 : if (!term || !odm || !urli || !gf_term_check_odm(term, odm)) return GF_BAD_PARAM;
1598 :
1599 4 : pid = odm->pid;
1600 4 : if (!pid && odm->subscene) {
1601 2 : GF_ObjectManager *anodm = gf_list_get(odm->subscene->resources, 0);
1602 2 : if (!anodm) return GF_OK;
1603 2 : pid = anodm->pid;
1604 : }
1605 4 : if (!pid) return GF_OK;
1606 :
1607 : memset(urli, 0, sizeof(GF_TermURLInfo));
1608 :
1609 4 : p = gf_filter_pid_get_info_str(pid, "info:name", &pe);
1610 4 : if (p) urli->name = p->value.string;
1611 :
1612 4 : p = gf_filter_pid_get_info_str(pid, "info:artist", &pe);
1613 4 : if (p) urli->artist = p->value.string;
1614 :
1615 4 : p = gf_filter_pid_get_info_str(pid, "info:album", &pe);
1616 4 : if (p) urli->album = p->value.string;
1617 :
1618 4 : p = gf_filter_pid_get_info_str(pid, "info:comment", &pe);
1619 4 : if (p) urli->comment = p->value.string;
1620 :
1621 4 : p = gf_filter_pid_get_info_str(pid, "info:composer", &pe);
1622 4 : if (p) urli->composer = p->value.string;
1623 :
1624 4 : p = gf_filter_pid_get_info_str(pid, "info:writer", &pe);
1625 4 : if (p) urli->writer = p->value.string;
1626 :
1627 4 : p = gf_filter_pid_get_info_str(pid, "info:track", &pe);
1628 4 : if (p) {
1629 0 : urli->track_num = p->value.frac.num;
1630 0 : urli->track_total = p->value.frac.den;
1631 : }
1632 4 : gf_filter_release_property(pe);
1633 4 : return GF_OK;
1634 : }
1635 :
1636 : GF_EXPORT
1637 2 : const char *gf_term_get_world_info(GF_Terminal *term, GF_ObjectManager *scene_od, GF_List *descriptions)
1638 : {
1639 : GF_Node *info;
1640 2 : if (!term) return NULL;
1641 : info = NULL;
1642 2 : if (!scene_od) {
1643 2 : if (!term->compositor->root_scene) return NULL;
1644 2 : info = (GF_Node*)term->compositor->root_scene->world_info;
1645 : } else {
1646 0 : if (!gf_term_check_odm(term, scene_od)) return NULL;
1647 0 : info = (GF_Node*) (scene_od->subscene ? scene_od->subscene->world_info : scene_od->parentscene->world_info);
1648 : }
1649 2 : if (!info) return NULL;
1650 :
1651 2 : if (gf_node_get_tag(info) == TAG_SVG_title) {
1652 : /*FIXME*/
1653 : //return ((SVG_titleElement *) info)->textContent;
1654 : return "TO FIX IN GPAC!!";
1655 : } else {
1656 : #ifndef GPAC_DISABLE_VRML
1657 : M_WorldInfo *wi = (M_WorldInfo *) info;
1658 2 : if (descriptions) {
1659 : u32 i;
1660 22 : for (i=0; i<wi->info.count; i++) {
1661 22 : gf_list_add(descriptions, wi->info.vals[i]);
1662 : }
1663 : }
1664 2 : return wi->title.buffer;
1665 : #endif
1666 : }
1667 : return "GPAC";
1668 : }
1669 :
1670 : GF_EXPORT
1671 2 : GF_Err gf_term_dump_scene(GF_Terminal *term, char *rad_name, char **filename, Bool xml_dump, Bool skip_protos, GF_ObjectManager *scene_od)
1672 : {
1673 : #ifndef GPAC_DISABLE_SCENE_DUMP
1674 : GF_SceneGraph *sg;
1675 : GF_ObjectManager *odm;
1676 : GF_SceneDumper *dumper;
1677 : GF_List *extra_graphs;
1678 : u32 mode;
1679 : u32 i;
1680 : char *ext;
1681 : GF_Err e;
1682 :
1683 2 : if (!term || !term->compositor->root_scene) return GF_BAD_PARAM;
1684 2 : if (!scene_od) {
1685 : if (!term->compositor->root_scene) return GF_BAD_PARAM;
1686 2 : odm = term->compositor->root_scene->root_od;
1687 : } else {
1688 : odm = scene_od;
1689 0 : if (!gf_term_check_odm(term, scene_od))
1690 0 : odm = term->compositor->root_scene->root_od;
1691 : }
1692 :
1693 2 : if (odm->subscene) {
1694 2 : if (!odm->subscene->graph) return GF_IO_ERR;
1695 : sg = odm->subscene->graph;
1696 2 : extra_graphs = odm->subscene->extra_scenes;
1697 : } else {
1698 0 : if (!odm->parentscene->graph) return GF_IO_ERR;
1699 : sg = odm->parentscene->graph;
1700 0 : extra_graphs = odm->parentscene->extra_scenes;
1701 : }
1702 :
1703 2 : mode = xml_dump ? GF_SM_DUMP_AUTO_XML : GF_SM_DUMP_AUTO_TXT;
1704 : /*figure out best dump format based on extension*/
1705 2 : ext = odm->scene_ns ? gf_file_ext_start(odm->scene_ns->url) : NULL;
1706 2 : if (ext) {
1707 : char szExt[20];
1708 : strcpy(szExt, ext);
1709 2 : strlwr(szExt);
1710 2 : if (!strcmp(szExt, ".wrl")) mode = xml_dump ? GF_SM_DUMP_X3D_XML : GF_SM_DUMP_VRML;
1711 2 : else if(!strncmp(szExt, ".x3d", 4) || !strncmp(szExt, ".x3dv", 5) ) mode = xml_dump ? GF_SM_DUMP_X3D_XML : GF_SM_DUMP_X3D_VRML;
1712 2 : else if(!strncmp(szExt, ".bt", 3) || !strncmp(szExt, ".xmt", 4) || !strncmp(szExt, ".mp4", 4) ) mode = xml_dump ? GF_SM_DUMP_XMTA : GF_SM_DUMP_BT;
1713 : }
1714 :
1715 2 : dumper = gf_sm_dumper_new(sg, rad_name, GF_FALSE, ' ', mode);
1716 :
1717 2 : if (!dumper) return GF_IO_ERR;
1718 2 : e = gf_sm_dump_graph(dumper, skip_protos, 0);
1719 2 : for (i = 0; i < gf_list_count(extra_graphs); i++) {
1720 0 : GF_SceneGraph *extra = (GF_SceneGraph *)gf_list_get(extra_graphs, i);
1721 0 : gf_sm_dumper_set_extra_graph(dumper, extra);
1722 0 : e = gf_sm_dump_graph(dumper, skip_protos, 0);
1723 : }
1724 : #ifdef GPAC_ENABLE_COVERAGE
1725 2 : if (gf_sys_is_cov_mode()) {
1726 0 : gf_sm_dumper_set_extra_graph(dumper, NULL);
1727 0 : gf_sm_dump_get_name(dumper);
1728 : }
1729 : #endif
1730 :
1731 2 : if (filename) *filename = gf_strdup(gf_sm_dump_get_name(dumper));
1732 2 : gf_sm_dumper_del(dumper);
1733 2 : return e;
1734 : #else
1735 : return GF_NOT_SUPPORTED;
1736 : #endif
1737 : }
1738 :
1739 : GF_EXPORT
1740 2 : void gf_term_print_stats(GF_Terminal *term)
1741 : {
1742 2 : if (term->fsess)
1743 2 : gf_fs_print_stats(term->fsess);
1744 2 : }
1745 :
1746 : GF_EXPORT
1747 2 : void gf_term_print_graph(GF_Terminal *term)
1748 : {
1749 2 : if (term->fsess)
1750 2 : gf_fs_print_connections(term->fsess);
1751 2 : }
|