LCOV - code coverage report
Current view: top level - terminal - terminal.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 410 659 62.2 %
Date: 2021-04-29 23:48:07 Functions: 56 57 98.2 %

          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 : }

Generated by: LCOV version 1.13