LCOV - code coverage report
Current view: top level - jsmods - scene_js.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 555 888 62.5 %
Date: 2021-04-29 23:48:07 Functions: 36 36 100.0 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2007-2020
       6             :  *                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / JavaScript Compositor extensions
       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/setup.h>
      28             : 
      29             : #if defined(GPAC_HAS_QJS) && !defined(GPAC_DISABLE_PLAYER)
      30             : 
      31             : /*base SVG type*/
      32             : #include <gpac/nodes_svg.h>
      33             : #include <gpac/nodes_mpeg4.h>
      34             : #include <gpac/nodes_x3d.h>
      35             : /*dom events*/
      36             : #include <gpac/events.h>
      37             : 
      38             : #include <gpac/download.h>
      39             : #include <gpac/network.h>
      40             : #include <gpac/options.h>
      41             : #include <gpac/xml.h>
      42             : 
      43             : 
      44             : #include <gpac/internal/scenegraph_dev.h>
      45             : 
      46             : #include "../scenegraph/qjs_common.h"
      47             : 
      48             : #include <gpac/internal/compositor_dev.h>
      49             : #include <gpac/term_info.h>
      50             : 
      51             : typedef struct
      52             : {
      53             :         GF_Compositor *compositor;
      54             :         JSValue evt_fun;
      55             :         GF_FSEventListener evt_filter;
      56             :         GF_Event *evt;
      57             : 
      58             :         JSContext *c;
      59             :         JSValue evt_filter_obj, evt_obj, scene_obj;
      60             : 
      61             :         GF_SystemRTInfo rti;
      62             : 
      63             :         //list of config files for storage
      64             :         GF_List *storages;
      65             : 
      66             :         GF_List *event_queue;
      67             :         GF_Mutex *event_mx;
      68             : 
      69             : } GF_SCENEJSExt;
      70             : 
      71             : enum {
      72             :         GJS_OM_PROP_ID=1,
      73             :         GJS_OM_PROP_NB_RES,
      74             :         GJS_OM_PROP_URL,
      75             :         GJS_OM_PROP_DUR,
      76             :         GJS_OM_PROP_CLOCK,
      77             :         GJS_OM_PROP_DRIFT,
      78             :         GJS_OM_PROP_STATUS,
      79             :         GJS_OM_PROP_BUFFER,
      80             :         GJS_OM_PROP_DB_COUNT,
      81             :         GJS_OM_PROP_CB_COUNT,
      82             :         GJS_OM_PROP_CB_CAP,
      83             :         GJS_OM_PROP_TYPE,
      84             :         GJS_OM_PROP_SAMPLERATE,
      85             :         GJS_OM_PROP_CHANNELS,
      86             :         GJS_OM_PROP_LANG,
      87             :         GJS_OM_PROP_WIDTH,
      88             :         GJS_OM_PROP_HEIGHT,
      89             :         GJS_OM_PROP_PIXELFORMAT,
      90             :         GJS_OM_PROP_PAR,
      91             :         GJS_OM_PROP_DEC_FRAMES,
      92             :         GJS_OM_PROP_DROP_FRAMES,
      93             :         GJS_OM_PROP_DEC_TIME_MAX,
      94             :         GJS_OM_PROP_DEC_TIME_TOTAL,
      95             :         GJS_OM_PROP_AVG_RATE,
      96             :         GJS_OM_PROP_MAX_RATE,
      97             :         GJS_OM_PROP_SERVICE_HANDLER,
      98             :         GJS_OM_PROP_CODEC,
      99             :         GJS_OM_PROP_NB_QUALITIES,
     100             :         GJS_OM_PROP_MAX_BUFFER,
     101             :         GJS_OM_PROP_MIN_BUFFER,
     102             :         GJS_OM_PROP_FRAME_DUR,
     103             :         GJS_OM_PROP_NB_IRAP,
     104             :         GJS_OM_PROP_IRAP_DEC_TIME,
     105             :         GJS_OM_PROP_IRAP_MAX_TIME,
     106             :         GJS_OM_PROP_SERVICE_ID,
     107             :         GJS_OM_PROP_SELECTED_SERVICE,
     108             :         GJS_OM_PROP_BANDWIDTH_DOWN,
     109             :         GJS_OM_PROP_NB_HTTP,
     110             :         GJS_OM_PROP_TIMESHIFT_DEPTH,
     111             :         GJS_OM_PROP_TIMESHIFT_TIME,
     112             :         GJS_OM_PROP_IS_ADDON,
     113             :         GJS_OM_PROP_MAIN_ADDON_ON,
     114             :         GJS_OM_PROP_IS_OVER,
     115             :         GJS_OM_PROP_IS_VR_SCENE,
     116             :         GJS_OM_PROP_DYNAMIC_SCENE,
     117             :         GJS_OM_PROP_SERVICE_NAME,
     118             :         GJS_OM_PROP_NTP_DIFF,
     119             :         GJS_OM_PROP_MAIN_ADDON_URL,
     120             :         GJS_OM_PROP_REVERSE_PLAYBACK,
     121             :         GJS_OM_PROP_SCALABLE_ENHANCEMENT,
     122             :         GJS_OM_PROP_MAIN_ADDON_MEDIATIME,
     123             :         GJS_OM_PROP_DEPENDENT_GROUPS,
     124             :         GJS_OM_PROP_DISABLED,
     125             :         GJS_OM_PROP_NTP_SENDER_DIFF,
     126             :         GJS_OM_PROP_BUFFERING,
     127             : };
     128             : 
     129             : enum {
     130             :         GJS_SCENE_PROP_FULLSCREEN=1,
     131             :         GJS_SCENE_PROP_CURRENT_PATH,
     132             :         GJS_SCENE_PROP_VOLUME,
     133             :         GJS_SCENE_PROP_NAVIGATION,
     134             :         GJS_SCENE_PROP_NAVIGATION_TYPE,
     135             :         GJS_SCENE_PROP_HARDWARE_YUV,
     136             :         GJS_SCENE_PROP_HARDWARE_RGB,
     137             :         GJS_SCENE_PROP_HARDWARE_RGBA,
     138             :         GJS_SCENE_PROP_HARDWARE_STRETCH,
     139             :         GJS_SCENE_PROP_SCREEN_WIDTH,
     140             :         GJS_SCENE_PROP_SCREEN_HEIGHT,
     141             :         GJS_SCENE_PROP_FPS,
     142             :         GJS_SCENE_PROP_CAPTION,
     143             :         GJS_SCENE_PROP_FOCUS_HIGHLIGHT,
     144             :         GJS_SCENE_PROP_DPI_X,
     145             :         GJS_SCENE_PROP_DPI_Y,
     146             :         GJS_SCENE_PROP_SENSORS_ACTIVE,
     147             :         GJS_SCENE_PROP_SIM_FPS,
     148             :         GJS_SCENE_PROP_HAS_OPENGL,
     149             :         GJS_SCENE_PROP_ZOOM,
     150             :         GJS_SCENE_PROP_TEXT_SEL,
     151             : 
     152             : };
     153             : 
     154             : enum {
     155             :         GJS_EVT_PROP_KEYCODE = 1,
     156             :         GJS_EVT_PROP_MOUSE_X,
     157             :         GJS_EVT_PROP_MOUSE_Y,
     158             :         GJS_EVT_PROP_PICKED,
     159             :         GJS_EVT_PROP_WHEEL,
     160             :         GJS_EVT_PROP_BUTTON,
     161             :         GJS_EVT_PROP_TYPE,
     162             :         GJS_EVT_PROP_NAME,
     163             :         GJS_EVT_PROP_HWKEY,
     164             :         GJS_EVT_PROP_TARGET_URL,
     165             :         GJS_EVT_PROP_FILES,
     166             : };
     167             : 
     168             : static JSClassID scene_class_id = 0;
     169             : static JSClassID odm_class_id = 0;
     170             : static JSClassID gpacevt_class_id = 0;
     171             : static JSClassID any_class_id = 0;
     172             : 
     173             : static void scenejs_finalize(JSRuntime *rt, JSValue obj);
     174             : 
     175          40 : static void scenejs_gc_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
     176             : {
     177          40 :         GF_SCENEJSExt *ext = JS_GetOpaque(val, scene_class_id);
     178          40 :     if (ext) {
     179          40 :                 JS_MarkValue(rt, ext->evt_fun, mark_func);
     180             :     }
     181          40 : }
     182             : 
     183             : JSClassDef sceneClass = {
     184             :     "JSSCENE",
     185             :     .finalizer = scenejs_finalize,
     186             :     .gc_mark = scenejs_gc_mark
     187             : };
     188             : JSClassDef gpacEvtClass = {
     189             :     "GPACEVT"
     190             : };
     191             : JSClassDef odmClass = {
     192             :     "MediaObject"
     193             : };
     194             : JSClassDef anyClass = {
     195             :         "GPACOBJECT"
     196             : };
     197             : 
     198             : 
     199             : static GF_Compositor *scenejs_get_compositor(JSContext *c, JSValue obj)
     200             : {
     201          68 :         GF_SCENEJSExt *ext = JS_GetOpaque(obj, scene_class_id);
     202          68 :         return ext ? ext->compositor : NULL;
     203             : }
     204             : 
     205         176 : static JSValue scenejs_getProperty(JSContext *ctx, JSValueConst this_val, int prop_id)
     206             : {
     207             :         Bool bval;
     208             :         JSValue ret;
     209             :         char *str;
     210         176 :         GF_SCENEJSExt *ext = JS_GetOpaque(this_val, scene_class_id);
     211         176 :         GF_Compositor *compositor = ext ? ext->compositor : NULL;
     212         176 :         if (!ext || !compositor) return JS_EXCEPTION;
     213             : 
     214         176 :         switch (prop_id) {
     215             : 
     216         122 :         case GJS_SCENE_PROP_FULLSCREEN:
     217         122 :                 return JS_NewBool(ctx, compositor->fullscreen);
     218             : 
     219           3 :         case GJS_SCENE_PROP_CURRENT_PATH:
     220           3 :                 str = gf_url_concatenate(compositor->root_scene->root_od->scene_ns->url, "");
     221           3 :                 if (!str) str = gf_strdup("");
     222           3 :                 ret = JS_NewString(ctx, str);
     223           3 :                 gf_free(str);
     224           3 :                 return ret;
     225             : 
     226          34 :         case GJS_SCENE_PROP_VOLUME:
     227          34 :                 return JS_NewInt32(ctx, gf_sc_get_option(compositor, GF_OPT_AUDIO_VOLUME));
     228             : 
     229           0 :         case GJS_SCENE_PROP_NAVIGATION:
     230           0 :                 return JS_NewInt32(ctx, gf_sc_get_option(compositor, GF_OPT_NAVIGATION));
     231             : 
     232           0 :         case GJS_SCENE_PROP_NAVIGATION_TYPE:
     233           0 :                 return JS_NewInt32(ctx, gf_sc_get_option(compositor, GF_OPT_NAVIGATION_TYPE) );
     234             : 
     235           0 :         case GJS_SCENE_PROP_HARDWARE_YUV:
     236           0 :                 return JS_NewBool(ctx, (compositor->video_out->hw_caps & GF_VIDEO_HW_HAS_YUV) ? 1 : 0 );
     237             : 
     238           0 :         case GJS_SCENE_PROP_HARDWARE_RGB:
     239           0 :                 return JS_NewBool(ctx, (compositor->video_out->hw_caps & GF_VIDEO_HW_HAS_RGB) ? 1 : 0 );
     240             : 
     241           0 :         case GJS_SCENE_PROP_HARDWARE_RGBA:
     242           0 :                 bval = (compositor->video_out->hw_caps & GF_VIDEO_HW_HAS_RGBA) ? 1 : 0;
     243             : #ifndef GPAC_DISABLE_3D
     244           0 :                 if (compositor->hybrid_opengl || compositor->is_opengl) bval = 1;
     245             : #endif
     246             :                 return JS_NewBool(ctx, bval);
     247             : 
     248           0 :         case GJS_SCENE_PROP_HARDWARE_STRETCH:
     249           0 :                 return JS_NewBool(ctx, (compositor->video_out->hw_caps & GF_VIDEO_HW_HAS_STRETCH) ? 1 : 0 );
     250             : 
     251           3 :         case GJS_SCENE_PROP_SCREEN_WIDTH:
     252           3 :                 if (compositor->osize.x)
     253             :                         return JS_NewInt32(ctx, compositor->osize.x);
     254           3 :                 return JS_NewInt32(ctx, compositor->video_out->max_screen_width);
     255             : 
     256           3 :         case GJS_SCENE_PROP_SCREEN_HEIGHT:
     257           3 :                 if (compositor->osize.y)
     258             :                         return JS_NewInt32(ctx, compositor->osize.y);
     259           3 :                 return JS_NewInt32(ctx, compositor->video_out->max_screen_height);
     260             : 
     261             : 
     262           4 :         case GJS_SCENE_PROP_FPS:
     263           4 :                 return JS_NewFloat64(ctx, gf_sc_get_fps(compositor, 0) );
     264             : 
     265           2 :         case GJS_SCENE_PROP_SIM_FPS:
     266           2 :                 return JS_NewFloat64(ctx, ((Double)compositor->fps.den)/compositor->fps.num);
     267             : 
     268           3 :         case GJS_SCENE_PROP_HAS_OPENGL:
     269           3 :                 return JS_NewBool(ctx, (compositor->ogl != GF_SC_GLMODE_OFF) ? 1 : 0);
     270             : 
     271           0 :         case GJS_SCENE_PROP_DPI_X:
     272           0 :                 return JS_NewInt32(ctx, compositor->video_out->dpi_x);
     273             : 
     274           0 :         case GJS_SCENE_PROP_DPI_Y:
     275           0 :                 return JS_NewInt32(ctx, compositor->video_out->dpi_y);
     276             : 
     277           0 :         case GJS_SCENE_PROP_SENSORS_ACTIVE:
     278           0 :                 return JS_NewBool(ctx, compositor->orientation_sensors_active);
     279             : 
     280           1 :         case GJS_SCENE_PROP_ZOOM:
     281           1 :                 if (compositor->root_scene && compositor->root_scene->graph->script_action) {
     282             :                         GF_JSAPIParam jspar;
     283             :                         memset(&jspar, 0, sizeof(GF_JSAPIParam));
     284           1 :                         compositor->root_scene->graph->script_action(compositor->root_scene->graph->script_action_cbck, GF_JSAPI_OP_GET_SCALE, NULL, &jspar);
     285             : #ifdef GPAC_ENABLE_COVERAGE
     286           1 :                         if (gf_sys_is_cov_mode()) {
     287           1 :                                 compositor->root_scene->graph->script_action(compositor->root_scene->graph->script_action_cbck, GF_JSAPI_OP_GET_VIEWPORT, NULL, &jspar);
     288             :                         }
     289             : #endif
     290           1 :                         return JS_NewFloat64(ctx, FIX2FLT(jspar.val) );
     291             :                 } else {
     292             :                         return JS_NewFloat64(ctx, 1.0 );
     293             :                 }
     294             :                 break;
     295           1 :         case GJS_SCENE_PROP_TEXT_SEL:
     296           1 :                 str = (char *) gf_sc_get_selected_text(compositor);
     297           1 :                 if (!str) str = "";
     298           1 :                 return JS_NewString(ctx, str);
     299             :         }
     300           0 :         return JS_UNDEFINED;
     301             : }
     302             : 
     303             : 
     304           7 : static JSValue scenejs_setProperty(JSContext *ctx, JSValueConst this_val, JSValueConst value, int magic)
     305             : {
     306             : #ifndef GPAC_CONFIG_IOS
     307             :         Bool bval;
     308             : #endif
     309             :         s32 ival;
     310             :         Double dval;
     311             :         const char *prop_val;
     312           7 :         GF_SCENEJSExt *ext = JS_GetOpaque(this_val, scene_class_id);
     313           7 :         GF_Compositor *compositor = ext ? ext->compositor : NULL;
     314           7 :         if (!ext || !compositor) return JS_EXCEPTION;
     315             : 
     316           7 :         switch (magic) {
     317           4 :         case GJS_SCENE_PROP_CAPTION:
     318             :         {
     319             :                 GF_Event evt;
     320           4 :                 if (!JS_IsString(value)) return JS_EXCEPTION;
     321             :                 prop_val = JS_ToCString(ctx, value);
     322           4 :                 evt.type = GF_EVENT_SET_CAPTION;
     323           4 :                 if (prop_val && !strnicmp(prop_val, "gpac://", 7)) {
     324           0 :                         evt.caption.caption = prop_val + 7;
     325             :                 } else {
     326           4 :                         evt.caption.caption = prop_val;
     327             :                 }
     328           4 :                 gf_sc_user_event(compositor, &evt);
     329           4 :                 JS_FreeCString(ctx, prop_val);
     330             :         }
     331           4 :                 break;
     332           0 :         case GJS_SCENE_PROP_FULLSCREEN:
     333             :                 /*no fullscreen for iOS (always on)*/
     334             : #ifndef GPAC_CONFIG_IOS
     335           0 :                 bval = JS_ToBool(ctx, value);
     336           0 :                 if (compositor->fullscreen != bval) {
     337           0 :                         gf_sc_set_option(compositor, GF_OPT_FULLSCREEN, bval);
     338             :                 }
     339             : #endif
     340             :                 break;
     341           0 :         case GJS_SCENE_PROP_VOLUME:
     342           0 :                 if (!JS_ToFloat64(ctx, &dval, value)) {
     343           0 :                         gf_sc_set_option(compositor, GF_OPT_AUDIO_VOLUME, (u32) dval);
     344           0 :                 } else if (!JS_ToInt32(ctx, &ival, value)) {
     345           0 :                         gf_sc_set_option(compositor, GF_OPT_AUDIO_VOLUME, (u32) ival);
     346             :                 }
     347             :                 break;
     348             : 
     349           0 :         case GJS_SCENE_PROP_NAVIGATION:
     350           0 :                 if (JS_ToInt32(ctx, &ival, value)) return JS_EXCEPTION;
     351           0 :                 gf_sc_set_option(compositor, GF_OPT_NAVIGATION, (u32) ival);
     352           0 :                 break;
     353           0 :         case GJS_SCENE_PROP_NAVIGATION_TYPE:
     354           0 :                 gf_sc_set_option(compositor, GF_OPT_NAVIGATION_TYPE, 0);
     355           0 :                 break;
     356           3 :         case GJS_SCENE_PROP_FOCUS_HIGHLIGHT:
     357           3 :                 compositor->disable_focus_highlight = JS_ToBool(ctx, value);
     358           3 :                 break;
     359           0 :         case GJS_SCENE_PROP_SENSORS_ACTIVE:
     360             :         {
     361             :                 GF_Event evt;
     362           0 :                 compositor->orientation_sensors_active = JS_ToBool(ctx, value);
     363             :                 //send activation for sensors
     364             :                 memset(&evt, 0, sizeof(GF_Event));
     365           0 :                 evt.type = GF_EVENT_SENSOR_REQUEST;
     366           0 :                 evt.activate_sensor.activate = compositor->orientation_sensors_active;
     367           0 :                 evt.activate_sensor.sensor_type = GF_EVENT_SENSOR_ORIENTATION;
     368           0 :                 if (gf_sc_send_event(compositor, &evt) == GF_FALSE) {
     369           0 :                         compositor->orientation_sensors_active = GF_FALSE;
     370             :                 }
     371             :         }
     372           0 :                 break;
     373             :         }
     374           7 :         return JS_UNDEFINED;
     375             : }
     376             : 
     377          15 : static JSValue scenejs_get_option(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
     378             : {
     379             :         const char *opt = NULL;
     380             :         const char *sec_name, *key_name;
     381             :         char arg_val[GF_PROP_DUMP_ARG_SIZE];
     382          15 :         s32 idx = -1;
     383             :         GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val);
     384          15 :         if (!compositor) return JS_EXCEPTION;
     385          15 :         if (argc < 2) return JS_EXCEPTION;
     386          30 :         if (!JS_IsString(argv[0])) return JS_EXCEPTION;
     387          30 :         if (!JS_IsString(argv[1]) && !JS_IsNumber(argv[1])) return JS_EXCEPTION;
     388             : 
     389             :         sec_name = JS_ToCString(ctx, argv[0]);
     390             :         key_name = NULL;
     391          30 :         if (JS_IsNumber(argv[1])) {
     392           0 :                 JS_ToInt32(ctx, &idx, argv[1]);
     393          15 :         } else if (JS_IsString(argv[1]) ) {
     394             :                 key_name = JS_ToCString(ctx, argv[1]);
     395             :         }
     396             : 
     397          15 :         if (sec_name && !stricmp(sec_name, "General") && key_name && !strcmp(key_name, "Version")) {
     398           0 :                 opt = gf_gpac_version();
     399          15 :         } else if (sec_name && key_name) {
     400          15 :                 if (!strcmp(sec_name, "Compositor")) {
     401           0 :                         opt = gf_filter_get_arg_str(compositor->filter, key_name, arg_val);
     402             :                 } else {
     403          15 :                         opt = gf_opts_get_key(sec_name, key_name);
     404             :                 }
     405           0 :         } else if (sec_name && (idx>=0)) {
     406           0 :                 opt = gf_opts_get_key_name(sec_name, idx);
     407             :         } else {
     408             :                 opt = NULL;
     409             :         }
     410             : 
     411          15 :         JS_FreeCString(ctx, key_name);
     412          15 :         JS_FreeCString(ctx, sec_name);
     413             : 
     414          15 :         if (opt) {
     415           3 :                 return JS_NewString(ctx, opt);
     416             :         }
     417          12 :         return JS_NULL;
     418             : }
     419             : 
     420           8 : static JSValue scenejs_set_option(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
     421             : {
     422             :         const char *sec_name, *key_name, *key_val;
     423             :         GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val);
     424           8 :         if (!compositor) return JS_EXCEPTION;
     425           8 :         if (argc < 3) return JS_EXCEPTION;
     426          16 :         if (!JS_IsString(argv[0])) return JS_EXCEPTION;
     427          16 :         if (!JS_IsString(argv[1])) return JS_EXCEPTION;
     428             : 
     429             :         sec_name = JS_ToCString(ctx, argv[0]);
     430             :         key_name = JS_ToCString(ctx, argv[1]);
     431             :         key_val = NULL;
     432          16 :         if (JS_IsString(argv[2]))
     433             :                 key_val = JS_ToCString(ctx, argv[2]);
     434             : 
     435           8 :         if (!strcmp(sec_name, "Compositor")) {
     436           0 :                 gf_filter_send_update(compositor->filter, NULL, key_name, key_val, 0);
     437             :         } else {
     438           8 :                 gf_opts_set_key(sec_name, key_name, key_val);
     439             :         }
     440           8 :         JS_FreeCString(ctx, sec_name);
     441           8 :         JS_FreeCString(ctx, key_name);
     442           8 :         if (key_val) {
     443           8 :                 JS_FreeCString(ctx, key_val);
     444             :         }
     445           8 :         return JS_UNDEFINED;
     446             : }
     447             : 
     448             : 
     449           4 : static JSValue scenejs_set_back_color(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
     450             : {
     451             :         u32 r, g, b, a, i;
     452             :         GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val);
     453           4 :         if (!compositor) return JS_EXCEPTION;
     454           4 :         if (argc < 3) return JS_EXCEPTION;
     455             :         r = g = b = 0;
     456             :         a = 255;
     457          16 :         for (i=0; i<(u32) argc; i++) {
     458             :                 Double d;
     459             :                 u32 v;
     460          16 :                 if (! JS_ToFloat64(ctx, &d, argv[i])) {
     461             :                 } else {
     462           0 :                         return JS_EXCEPTION;
     463             :                 }
     464          16 :                 d*=255;
     465             :                 v = 0;
     466          16 :                 if (d>0) {
     467          13 :                         v = (u32) d;
     468          13 :                         if (v>255)  v = 255;
     469             :                 }
     470          16 :                 if (i==0) r = v;
     471          12 :                 else if (i==1) g = v;
     472           8 :                 else if (i==2) b = v;
     473           4 :                 else if (i==3) a = v;
     474             :         }
     475           4 :         compositor->back_color = compositor->bc = GF_COL_ARGB(a, r, g, b);
     476           4 :         gf_sc_invalidate(compositor, NULL);
     477           4 :         return JS_UNDEFINED;
     478             : }
     479             : 
     480             : 
     481           1 : static JSValue scenejs_switch_quality(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
     482             : {
     483             :         GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val);
     484           1 :         if (!compositor) return JS_EXCEPTION;
     485           1 :         if (argc < 1) return JS_EXCEPTION;
     486             : 
     487           2 :         if (!JS_IsBool(argv[0]))
     488           0 :                 return JS_EXCEPTION;
     489           1 :         Bool up = JS_ToBool(ctx, argv[0]) ? GF_TRUE : GF_FALSE;
     490           1 :         gf_scene_switch_quality(compositor->root_scene, up);
     491           1 :         return JS_UNDEFINED;
     492             : }
     493             : 
     494             : 
     495             : #if 0 //unused
     496             : static JSValue scenejs_reload(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
     497             : {
     498             :         GF_Event evt;
     499             :         GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val);
     500             :         if (!compositor) return JS_EXCEPTION;
     501             : 
     502             :         memset(&evt, 0, sizeof(GF_Event));
     503             :         evt.type = GF_EVENT_RELOAD;
     504             :         gf_filter_ui_event(compositor->filter, &evt);
     505             :         return JS_UNDEFINED;
     506             : }
     507             : #endif
     508             : 
     509           1 : static JSValue scenejs_navigation_supported(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
     510             : {
     511             :         u32 type;
     512             :         GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val);
     513           1 :         if (!compositor) return JS_EXCEPTION;
     514           1 :         if (argc < 1) return JS_EXCEPTION;
     515             : 
     516           2 :         if (! JS_IsInteger(argv[0]) ) {
     517             :                 return JS_NewBool(ctx, 0);
     518             :         }
     519           1 :         if (JS_ToInt32(ctx, &type, argv[0]))
     520           0 :                 return JS_EXCEPTION;
     521           1 :         return JS_NewBool(ctx, gf_sc_navigation_supported(compositor, type) ? 1 : 0 );
     522             : }
     523             : 
     524          13 : static JSValue scenejs_set_size(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
     525             : {
     526             :         Bool override_size_info = 0;
     527             :         u32 w, h;
     528             :         GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val);
     529          13 :         if (!compositor) return JS_EXCEPTION;
     530          13 :         if (argc<2) return JS_EXCEPTION;
     531             : 
     532          13 :         w = h = 0;
     533          13 :         if (JS_ToInt32(ctx, &w, argv[0]))
     534           0 :                 return JS_EXCEPTION;
     535          13 :         if (JS_ToInt32(ctx, &h, argv[1]))
     536           0 :                 return JS_EXCEPTION;
     537             : 
     538          13 :         if (argc > 2)
     539           2 :                 override_size_info = JS_ToBool(ctx, argv[2]);
     540             : 
     541          24 :         if (w && h) {
     542             :                 GF_Event evt;
     543          13 :                 if (override_size_info) {
     544           2 :                         compositor->scene_width = w;
     545           2 :                         compositor->scene_height = h;
     546           2 :                         compositor->has_size_info = 1;
     547           2 :                         compositor->recompute_ar = 1;
     548           2 :                         return JS_UNDEFINED;
     549             :                 }
     550          11 :                 if (compositor->os_wnd) {
     551           0 :                         evt.type = GF_EVENT_SCENE_SIZE;
     552           0 :                         evt.size.width = w;
     553           0 :                         evt.size.height = h;
     554           0 :                         gf_sc_send_event(compositor, &evt);
     555             :                 } else {
     556          11 :                         gf_sc_set_size(compositor, w, h);
     557             :                 }
     558           0 :         } else if (override_size_info) {
     559           0 :                 compositor->has_size_info = 0;
     560           0 :                 compositor->recompute_ar = 1;
     561             :         }
     562             : 
     563          11 :         return JS_UNDEFINED;
     564             : }
     565             : 
     566             : 
     567           1 : static JSValue scenejs_exit(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
     568             : {
     569             :         GF_Event evt;
     570             :         GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val);
     571             :         memset(&evt, 0, sizeof(GF_Event));
     572           1 :         evt.type = GF_EVENT_QUIT;
     573           1 :         if (argc)
     574           1 :                 JS_ToInt32(ctx, (s32 *) &evt.message.error, argv[0]);
     575             :                 
     576           1 :         gf_sc_send_event(compositor, &evt);
     577           1 :         return JS_UNDEFINED;
     578             : }
     579             : 
     580           1 : static JSValue scenejs_set_3d(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
     581             : {
     582             :         Bool type_3d;
     583             :         GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val);
     584           1 :         if (!argc) return JS_EXCEPTION;
     585           1 :         type_3d = JS_ToBool(ctx, argv[0]);
     586             : 
     587           1 :         if (compositor->inherit_type_3d != type_3d) {
     588           0 :                 compositor->inherit_type_3d = type_3d;
     589           0 :                 compositor->root_visual_setup = 0;
     590           0 :                 gf_sc_reset_graphics(compositor);
     591             :         }
     592           1 :         return JS_UNDEFINED;
     593             : }
     594             : 
     595           1 : static JSValue scenejs_move_window(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
     596             : {
     597             :         GF_Event evt;
     598             :         GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val);
     599           1 :         if (argc < 2) return JS_EXCEPTION;
     600             : 
     601           1 :         evt.type = GF_EVENT_MOVE;
     602           1 :         evt.move.relative = 1;
     603           1 :         if (JS_ToInt32(ctx, &evt.move.x, argv[0]))
     604           0 :                 return JS_EXCEPTION;
     605           1 :         if (JS_ToInt32(ctx, &evt.move.y, argv[1]))
     606           0 :                 return JS_EXCEPTION;
     607             : 
     608           1 :         if (argc ==3) {
     609           1 :                 evt.move.relative = JS_ToBool(ctx, argv[2]);
     610             :         }
     611           1 :         compositor->video_out->ProcessEvent(compositor->video_out, &evt);
     612           1 :         return JS_UNDEFINED;
     613             : }
     614             : 
     615             : 
     616             : #if 0 //unused
     617             : static JSValue scenejs_get_scene_time(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
     618             : {
     619             :         GF_SceneGraph *sg = NULL;
     620             :         GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val);
     621             :         if (!compositor) return JS_EXCEPTION;
     622             : 
     623             :         if (!argc || !JS_IsObject(argv[0]) ) {
     624             :                 sg = compositor->root_scene->graph;
     625             :         } else {
     626             :                 GF_Node *n = gf_sg_js_get_node(c, JSVAL_TO_OBJECT(argv[0]));
     627             :                 sg = n ? n->sgprivate->scenegraph : compositor->root_scene->graph;
     628             :         }
     629             :         return JS_NewFloat64(ctx, sg->GetSceneTime(sg->userpriv) );
     630             : }
     631             : #endif
     632             : 
     633           2 : static JSValue scenejs_trigger_gc(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
     634             : {
     635             :         GF_SceneGraph *sg;
     636             :         GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val);
     637           2 :         sg = compositor->root_scene->graph;
     638           2 :         sg->trigger_gc = GF_TRUE;
     639           2 :         return JS_UNDEFINED;
     640             : }
     641             : 
     642           2 : static GF_FilterPid *gjs_enum_pids(void *udta, u32 *idx)
     643             : {
     644             :         GF_Scene *scene = (GF_Scene *)udta;
     645           2 :         GF_ObjectManager *odm = gf_list_get(scene->resources, *idx);
     646           2 :         if (odm) return odm->pid;
     647             :         return NULL;
     648             : }
     649             : 
     650         154 : static JSValue odm_getProperty(JSContext *ctx, JSValueConst this_val, int magic)
     651             : {
     652         154 :         GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id);
     653             :         GF_MediaInfo odi;
     654             :         char *str;
     655             :         GF_Scene *scene;
     656             :         Double dval;
     657             :         u32 i, count;
     658             : 
     659         154 :         switch (magic) {
     660           0 :         case GJS_OM_PROP_ID:
     661           0 :                 return JS_NewInt32(ctx, odm->ID);
     662          15 :         case GJS_OM_PROP_NB_RES:
     663          15 :                 return JS_NewInt32(ctx, odm->subscene ? gf_list_count(odm->subscene->resources) : 0);
     664           4 :         case GJS_OM_PROP_URL:
     665           4 :                 if (odm->scene_ns)
     666           4 :                         return JS_NewString(ctx, odm->scene_ns->url);
     667           0 :                 return JS_NULL;
     668           0 :         case GJS_OM_PROP_DUR:
     669           0 :                 return JS_NewFloat64(ctx, odm->duration / 1000.0);
     670           0 :         case GJS_OM_PROP_CLOCK:
     671           0 :                 gf_odm_get_object_info(odm, &odi);
     672           0 :                 return JS_NewFloat64(ctx, odi.current_time);
     673           0 :         case GJS_OM_PROP_DRIFT:
     674           0 :                 gf_odm_get_object_info(odm, &odi);
     675           0 :                 return JS_NewInt32(ctx, odi.clock_drift);
     676           4 :         case GJS_OM_PROP_STATUS:
     677           4 :                 gf_odm_get_object_info(odm, &odi);
     678           4 :                 if (odi.status==0) str = "Stopped";
     679           4 :                 else if (odi.status==1) str = "Playing";
     680           1 :                 else if (odi.status==2) str = "Paused";
     681           0 :                 else if (odi.status==3) str = "Not Setup";
     682             :                 else str = "Setup Failed";
     683           4 :                 return JS_NewString(ctx, str);
     684           8 :         case GJS_OM_PROP_BUFFER:
     685           8 :                 gf_odm_get_object_info(odm, &odi);
     686           8 :                 return JS_NewInt32(ctx, odi.buffer);
     687           0 :         case GJS_OM_PROP_DB_COUNT:
     688           0 :                 gf_odm_get_object_info(odm, &odi);
     689           0 :                 return JS_NewInt32(ctx, odi.db_unit_count);
     690           0 :         case GJS_OM_PROP_CB_COUNT:
     691           0 :                 gf_odm_get_object_info(odm, &odi);
     692           0 :                 return JS_NewInt32(ctx, odi.cb_unit_count);
     693           0 :         case GJS_OM_PROP_CB_CAP:
     694           0 :                 gf_odm_get_object_info(odm, &odi);
     695           0 :                 return JS_NewInt32(ctx, odi.cb_max_count);
     696           9 :         case GJS_OM_PROP_TYPE:
     697           9 :                 if (odm->type==GF_STREAM_SCENE) str = "Scene";
     698           9 :                 else if (odm->type==GF_STREAM_OD) str = "OD";
     699           9 :                 else if (odm->type==GF_STREAM_VISUAL) str = "Video";
     700           0 :                 else if (odm->type==GF_STREAM_AUDIO) str = "Audio";
     701           0 :                 else if (odm->type==GF_STREAM_TEXT) str = "Text";
     702           0 :                 else if (odm->subscene) str = "Subscene";
     703           0 :                 else str = (char*) gf_stream_type_name(odm->type);
     704           9 :                 return JS_NewString(ctx, str);
     705           0 :         case GJS_OM_PROP_SAMPLERATE:
     706           0 :                 return JS_NewInt32(ctx, odm->mo ? odm->mo->sample_rate : 0);
     707           0 :         case GJS_OM_PROP_CHANNELS:
     708           0 :                 return JS_NewInt32(ctx, odm->mo ? odm->mo->num_channels : 0);
     709           0 :         case GJS_OM_PROP_LANG:
     710           0 :                 gf_odm_get_object_info(odm, &odi);
     711           0 :                 return JS_NewString(ctx, odi.lang_code ? odi.lang_code : gf_4cc_to_str(odi.lang) );
     712          10 :         case GJS_OM_PROP_WIDTH:
     713          10 :                 return JS_NewInt32(ctx, odm->mo ? odm->mo->width : 0);
     714           5 :         case GJS_OM_PROP_HEIGHT:
     715           5 :                 return JS_NewInt32(ctx, odm->mo ? odm->mo->height : 0);
     716           0 :         case GJS_OM_PROP_PIXELFORMAT:
     717           0 :                 return JS_NewString(ctx, odm->mo ? gf_4cc_to_str(odm->mo->pixelformat) : "none");
     718           0 :         case GJS_OM_PROP_PAR:
     719           0 :                 if (odm->mo && odm->mo->pixel_ar) {
     720             :                         char szPar[50];
     721           0 :                         sprintf(szPar, "%d:%d", (odm->mo->pixel_ar>>16)&0xFF, (odm->mo->pixel_ar)&0xFF );
     722           0 :                         return JS_NewString(ctx, szPar);
     723           0 :                 } else if (odm->mo && odm->mo->width) {
     724           0 :                         return JS_NewString(ctx, "1:1");
     725             :                 }
     726           0 :                 return JS_NULL;
     727           0 :         case GJS_OM_PROP_DEC_FRAMES:
     728           0 :                 gf_odm_get_object_info(odm, &odi);
     729           0 :                 return JS_NewInt32(ctx, odi.nb_dec_frames);
     730           0 :         case GJS_OM_PROP_DROP_FRAMES:
     731           0 :                 gf_odm_get_object_info(odm, &odi);
     732           0 :                 return JS_NewInt32(ctx, odi.nb_dropped);
     733           0 :         case GJS_OM_PROP_DEC_TIME_MAX:
     734           0 :                 gf_odm_get_object_info(odm, &odi);
     735           0 :                 return JS_NewInt32(ctx, odi.max_dec_time);
     736           0 :         case GJS_OM_PROP_DEC_TIME_TOTAL:
     737           0 :                 gf_odm_get_object_info(odm, &odi);
     738           0 :                 return JS_NewInt64(ctx, odi.total_dec_time);
     739           4 :         case GJS_OM_PROP_AVG_RATE:
     740           4 :                 gf_odm_get_object_info(odm, &odi);
     741           4 :                 return JS_NewInt32(ctx, odi.avg_bitrate);
     742           0 :         case GJS_OM_PROP_MAX_RATE:
     743           0 :                 gf_odm_get_object_info(odm, &odi);
     744           0 :                 return JS_NewInt32(ctx, odi.max_bitrate);
     745           0 :         case GJS_OM_PROP_SERVICE_HANDLER:
     746           0 :                 gf_odm_get_object_info(odm, &odi);
     747           0 :             return JS_NewString(ctx, odi.service_handler ? odi.service_handler : "unloaded");
     748           0 :         case GJS_OM_PROP_CODEC:
     749           0 :                 gf_odm_get_object_info(odm, &odi);
     750           0 :             return JS_NewString(ctx, odi.codec_name ? odi.codec_name : "unloaded");
     751          20 :         case GJS_OM_PROP_NB_QUALITIES:
     752             :                 //use HAS qualities
     753          20 :                 if (odm->pid) {
     754             :                         u32 nb_qualities = 0;
     755          20 :                         GF_PropertyEntry *pe=NULL;
     756          20 :                         const GF_PropertyValue *prop = gf_filter_pid_get_info_str(odm->pid, "has:qualities", &pe);
     757          20 :                         if (prop) nb_qualities = prop->value.string_list.nb_items;
     758          20 :                         gf_filter_release_property(pe);
     759             : 
     760          20 :                         if (nb_qualities)
     761          40 :                                 return JS_NewInt32(ctx, nb_qualities);
     762             :                 }
     763             :                 //use input channels
     764           0 :                 if (odm->extra_pids) {
     765           0 :                         return JS_NewInt32(ctx, 1 + gf_list_count(odm->extra_pids));
     766             :                 }
     767             :                 //use number of scalable addons
     768           0 :                 if (odm->upper_layer_odm) {
     769             :                         count = 0;
     770           0 :                         while (odm) {
     771           0 :                                 odm = odm->upper_layer_odm;
     772           0 :                                 count++;
     773             :                         }
     774           0 :                         return JS_NewInt32(ctx, count);
     775             :                 }
     776             :                 return JS_NewInt32(ctx, 1);
     777             : 
     778           9 :         case GJS_OM_PROP_MAX_BUFFER:
     779           9 :                 gf_odm_get_object_info(odm, &odi);
     780           9 :                 return JS_NewInt32(ctx, odi.max_buffer);
     781           0 :         case GJS_OM_PROP_MIN_BUFFER:
     782           0 :                 gf_odm_get_object_info(odm, &odi);
     783           0 :                 return JS_NewInt32(ctx, odi.min_buffer);
     784           0 :         case GJS_OM_PROP_FRAME_DUR:
     785           0 :                 gf_odm_get_object_info(odm, &odi);
     786           0 :                 return JS_NewInt32(ctx, odi.au_duration);
     787           0 :         case GJS_OM_PROP_NB_IRAP:
     788           0 :                 gf_odm_get_object_info(odm, &odi);
     789           0 :                 return JS_NewInt32(ctx, odi.nb_iraps);
     790           0 :         case GJS_OM_PROP_IRAP_DEC_TIME:
     791           0 :                 gf_odm_get_object_info(odm, &odi);
     792           0 :                 return JS_NewInt64(ctx, odi.irap_total_dec_time);
     793           0 :         case GJS_OM_PROP_IRAP_MAX_TIME:
     794           0 :                 gf_odm_get_object_info(odm, &odi);
     795           0 :                 return JS_NewInt32(ctx, odi.irap_max_dec_time);
     796           6 :         case GJS_OM_PROP_SERVICE_ID:
     797           6 :                 return JS_NewInt32(ctx, odm->ServiceID);
     798           4 :         case GJS_OM_PROP_SELECTED_SERVICE:
     799           4 :                 return JS_NewInt32(ctx,  (!odm->addon && odm->subscene) ? odm->subscene->selected_service_id : odm->parentscene->selected_service_id);
     800             :                 break;
     801           0 :         case GJS_OM_PROP_BANDWIDTH_DOWN:
     802           0 :         if (odm->scene_ns->source_filter) {
     803             :                         JSValue ret;
     804           0 :             GF_PropertyEntry *pe=NULL;
     805           0 :             const GF_PropertyValue *prop = gf_filter_get_info(odm->scene_ns->source_filter, GF_PROP_PID_DOWN_RATE, &pe);
     806           0 :             ret = JS_NewInt32(ctx, prop ? prop->value.uint/1000 : 0);
     807           0 :             gf_filter_release_property(pe);
     808           0 :             return ret;
     809             :         }
     810             :         return JS_NewInt32(ctx, 0);
     811             : 
     812           2 :         case GJS_OM_PROP_NB_HTTP:
     813           2 :                 if (odm->scene_ns->source_filter) {
     814           2 :                         return JS_NewInt32(ctx, gf_filter_count_source_by_protocol(odm->scene_ns->source_filter, "http", GF_TRUE, gjs_enum_pids, odm->subscene ? odm->subscene : odm->parentscene ) );
     815             :                 }
     816             :                 return JS_NewInt32(ctx, 0);
     817           0 :         case GJS_OM_PROP_TIMESHIFT_DEPTH:
     818           0 :                 if ((s32) odm->timeshift_depth > 0) {
     819           0 :                         return JS_NewFloat64(ctx, ((Double) odm->timeshift_depth) / 1000.0 );
     820             :                 }
     821             :                 return JS_NewFloat64(ctx, 0.0);
     822             : 
     823           0 :         case GJS_OM_PROP_TIMESHIFT_TIME:
     824             :                 dval = 0.0;
     825           0 :                 if (!odm->timeshift_depth) {
     826             :                         return JS_NewInt32(ctx, 0);
     827             :                 }
     828             : 
     829           0 :                 scene = odm->subscene ? odm->subscene : odm->parentscene;
     830             :                 //we may need to check the main addon for timeshifting
     831           0 :                 if (scene->main_addon_selected) {
     832           0 :                         count = gf_list_count(scene->resources);
     833           0 :                         for (i=0; i < count; i++) {
     834           0 :                                 GF_ObjectManager *an_odm = gf_list_get(scene->resources, i);
     835           0 :                                 if (an_odm && an_odm->addon && (an_odm->addon->addon_type==GF_ADDON_TYPE_MAIN)) {
     836             :                                         odm = an_odm;
     837             :                                 }
     838             :                         }
     839             :                 }
     840             : 
     841           0 :                 if (odm->timeshift_depth) {
     842           0 :                         GF_FilterPid *pid = odm->pid;
     843           0 :                         if (!pid && odm->subscene) {
     844           0 :                                 odm = gf_list_get(odm->subscene->resources, 0);
     845           0 :                                 pid = odm->pid;
     846             :                         }
     847           0 :                         if (pid) {
     848           0 :                                 GF_PropertyEntry *pe=NULL;
     849           0 :                                 const GF_PropertyValue *p = gf_filter_pid_get_info(pid, GF_PROP_PID_TIMESHIFT_TIME, &pe);
     850           0 :                                 if (p) dval = p->value.number;
     851           0 :                                 gf_filter_release_property(pe);
     852             :                         }
     853           0 :                 } else if (scene->main_addon_selected) {
     854           0 :                         GF_Clock *ck = scene->root_od->ck;
     855           0 :                         if (ck) {
     856           0 :                                 u32 now = gf_clock_time(ck) ;
     857           0 :                                 u32 live = scene->obj_clock_at_main_activation + gf_sys_clock() - scene->sys_clock_at_main_activation;
     858           0 :                                 dval = ((Double) live) / 1000.0;
     859           0 :                                 dval -= ((Double) now) / 1000.0;
     860             : 
     861           0 :                                 if (dval<0) {
     862             :                                         GF_Event evt;
     863             :                                         memset(&evt, 0, sizeof(evt));
     864           0 :                                         evt.type = GF_EVENT_TIMESHIFT_UNDERRUN;
     865           0 :                                         gf_sc_send_event(scene->compositor, &evt);
     866             :                                         dval=0;
     867           0 :                                 } else if (dval && dval*1000 > scene->timeshift_depth) {
     868             :                                         GF_Event evt;
     869             :                                         memset(&evt, 0, sizeof(evt));
     870           0 :                                         evt.type = GF_EVENT_TIMESHIFT_OVERFLOW;
     871           0 :                                         gf_sc_send_event(scene->compositor, &evt);
     872           0 :                                         dval=scene->timeshift_depth;
     873           0 :                                         dval/=1000;
     874             :                                 }
     875             :                         }
     876             :                 }
     877             :                 return JS_NewFloat64(ctx, dval);
     878             : 
     879           0 :         case GJS_OM_PROP_IS_ADDON:
     880           0 :                 return JS_NewBool(ctx,  (odm->addon || (!odm->subscene && odm->parentscene->root_od->addon)) );
     881             : 
     882          18 :         case GJS_OM_PROP_MAIN_ADDON_ON:
     883          18 :                 scene = odm->subscene ? odm->subscene : odm->parentscene;
     884          18 :                 return JS_NewBool(ctx,  scene->main_addon_selected );
     885           0 :         case GJS_OM_PROP_IS_OVER:
     886           0 :                 scene = odm->subscene ? odm->subscene : odm->parentscene;
     887           0 :                 return JS_NewBool(ctx,  gf_sc_is_over(scene->compositor, scene->graph));
     888             : 
     889           1 :         case GJS_OM_PROP_DYNAMIC_SCENE:
     890           1 :                 return JS_NewBool(ctx, odm->subscene && odm->subscene->is_dynamic_scene);
     891             : 
     892           0 :         case GJS_OM_PROP_SERVICE_NAME:
     893           0 :                 if (odm->pid) {
     894           0 :                         const GF_PropertyValue *p = gf_filter_pid_get_property(odm->pid, GF_PROP_PID_SERVICE_NAME);
     895           0 :                         if (p && p->value.string)
     896           0 :                                 return JS_NewString(ctx, p->value.string);
     897             :                 }
     898           0 :                 return JS_NULL;
     899             : 
     900           9 :         case GJS_OM_PROP_NTP_DIFF:
     901           9 :                 gf_odm_get_object_info(odm, &odi);
     902           9 :                 return JS_NewInt32(ctx, odi.ntp_diff);
     903             : 
     904           0 :         case GJS_OM_PROP_NTP_SENDER_DIFF:
     905           0 :                 if (odm->last_drawn_frame_ntp_sender && odm->last_drawn_frame_ntp_receive) {
     906           0 :                         s32 diff = gf_net_ntp_diff_ms(odm->last_drawn_frame_ntp_receive, odm->last_drawn_frame_ntp_sender);
     907             :                         return JS_NewInt32(ctx, diff);
     908             :                 }
     909           0 :                 return JS_NULL;
     910             : 
     911           0 :         case GJS_OM_PROP_MAIN_ADDON_URL:
     912           0 :                 scene = odm->subscene ? odm->subscene : odm->parentscene;
     913           0 :                 count = gf_list_count(scene->resources);
     914           0 :                 for (i=0; i < count; i++) {
     915           0 :                         GF_ObjectManager *an_odm = gf_list_get(scene->resources, i);
     916           0 :                         if (an_odm && an_odm->addon && (an_odm->addon->addon_type==GF_ADDON_TYPE_MAIN)) {
     917           0 :                                 if (!strstr(an_odm->addon->url, "://")) {
     918             :                                         char szURL[GF_MAX_PATH];
     919             :                                         sprintf(szURL, "gpac://%s", an_odm->addon->url);
     920           0 :                                         return JS_NewString(ctx, szURL);
     921             :                                 } else {
     922           0 :                                         return JS_NewString(ctx, an_odm->addon->url);
     923             :                                 }
     924             :                         }
     925             :                 }
     926           0 :                 return JS_NULL;
     927             : 
     928           1 :         case GJS_OM_PROP_REVERSE_PLAYBACK:
     929           1 :                 if (odm->pid) {
     930           0 :                         const GF_PropertyValue *p = gf_filter_pid_get_property(odm->pid, GF_PROP_PID_PLAYBACK_MODE);
     931           0 :                         if (p && p->value.uint==GF_PLAYBACK_MODE_REWIND)
     932             :                                 return JS_NewBool(ctx, GF_TRUE);
     933             :                 }
     934             :                 return JS_NewBool(ctx, GF_FALSE );
     935             : 
     936           5 :         case GJS_OM_PROP_SCALABLE_ENHANCEMENT:
     937           5 :                 return JS_NewBool(ctx, odm && (odm->lower_layer_odm || odm->scalable_addon));
     938             : 
     939           0 :         case GJS_OM_PROP_MAIN_ADDON_MEDIATIME:
     940           0 :                 scene = odm->subscene ? odm->subscene : odm->parentscene;
     941           0 :                 count = gf_list_count(scene->resources);
     942             : 
     943           0 :                 if (! scene->main_addon_selected) count = 0;
     944             : 
     945           0 :                 for (i=0; i < count; i++) {
     946           0 :                         GF_ObjectManager *an_odm = gf_list_get(scene->resources, i);
     947           0 :                         if (an_odm && an_odm->addon && (an_odm->addon->addon_type==GF_ADDON_TYPE_MAIN)) {
     948           0 :                                 if (an_odm->duration) {
     949           0 :                                         Double now = gf_clock_time(scene->root_od->ck) / 1000.0;
     950           0 :                                         now -= ((Double) an_odm->addon->media_pts) / 90000.0;
     951           0 :                                         now += ((Double) an_odm->addon->media_timestamp) / an_odm->addon->media_timescale;
     952             :                                         return JS_NewFloat64(ctx, now);
     953             :                                 }
     954             :                         }
     955             :                 }
     956             :                 return JS_NewFloat64(ctx, -1);
     957             : 
     958             :         case GJS_OM_PROP_DEPENDENT_GROUPS:
     959             :         {
     960             : #if FILTER_FIXME
     961             :                 GF_NetworkCommand com;
     962             :                 memset(&com, 0, sizeof(GF_NetworkCommand));
     963             :                 com.base.command_type = GF_NET_SERVICE_QUALITY_QUERY;
     964             :                 com.base.on_channel = gf_list_get(odm->channels, 0);
     965             : 
     966             :                 gf_term_service_command(odm->net_service, &com);
     967             :                 if (com.quality_query.index) {
     968             :                         return JS_NewInt32(ctx, com.quality_query.dependent_group_index);
     969             :                 }
     970             : #endif
     971             :                 return JS_NewInt32(ctx, 0);
     972             :         }
     973             : 
     974          18 :         case GJS_OM_PROP_IS_VR_SCENE:
     975          18 :                 return JS_NewBool(ctx, odm->subscene && odm->subscene->vr_type);
     976           1 :         case GJS_OM_PROP_DISABLED:
     977           1 :                 return JS_NewBool(ctx, odm->redirect_url ? 1 : 0);
     978           0 :         case GJS_OM_PROP_BUFFERING:
     979           0 :                 if (odm->parentscene)
     980           0 :                         return JS_NewBool(ctx, odm->parentscene->nb_buffering ? 1 : 0);
     981           0 :                 return JS_NewBool(ctx, odm->subscene->nb_buffering ? 1 : 0);
     982             :         }
     983           0 :         return JS_UNDEFINED;
     984             : }
     985             : 
     986          28 : static JSValue gjs_odm_get_quality(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
     987             : {
     988          28 :         GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id);
     989             :         const GF_PropertyValue *prop;
     990          28 :         GF_PropertyEntry *pe=NULL;
     991             :         char *qdesc;
     992             :         const char *id="", *mime="", *codec="";
     993          28 :         u32 sr=0, ch=0, w=0, h=0, bw=0, par_n=1, par_d=1, tile_adaptation_mode=0,dependent_group_index=0;
     994             :         Bool ilced=GF_FALSE, disabled=GF_FALSE, selected=GF_FALSE, automatic=GF_FALSE;
     995             :         Double fps=30.0;
     996             :         s32 idx;
     997             : #if 0 //FILTER_FIXME
     998             :         s32 dep_idx=0;
     999             : #endif
    1000             : 
    1001          28 :         if (!odm) return JS_EXCEPTION;
    1002          28 :         if (argc<1) return JS_EXCEPTION;
    1003          28 :         if (JS_ToInt32(ctx, &idx, argv[0]))
    1004           0 :                 return JS_EXCEPTION;
    1005             : 
    1006             : #if 0 //FILTER_FIXME
    1007             :         if (argc>=2) dep_idx = JSVAL_TO_INT(argv[1]);
    1008             : #endif
    1009             : 
    1010          28 :         if (!odm->pid) return JS_NULL;
    1011             : 
    1012          28 :         prop = gf_filter_pid_get_info_str(odm->pid, "has:qualities", &pe);
    1013          28 :         if (!prop || (prop->type!=GF_PROP_STRING_LIST)) {
    1014           0 :                 gf_filter_release_property(pe);
    1015           0 :                 return JS_NULL;
    1016             :         }
    1017             :         qdesc = NULL;
    1018          28 :         if (idx < (s32) prop->value.string_list.nb_items)
    1019          28 :                 qdesc = prop->value.string_list.vals[idx];
    1020          28 :         if (!qdesc) {
    1021           0 :                 gf_filter_release_property(pe);
    1022           0 :                 return JS_NULL;
    1023             :         }
    1024          28 :         JSValue a = JS_NewObject(ctx);
    1025          28 :         if (JS_IsException(a))
    1026           0 :                 return a;
    1027             : 
    1028         196 :         while (qdesc) {
    1029         196 :                 char *sep=strstr(qdesc, "::");
    1030         196 :                 if (sep) sep[0]=0;
    1031             : 
    1032         196 :                 if (!strncmp(qdesc, "id=", 3)) {
    1033             :                         id = NULL;
    1034          28 :                         JS_SetPropertyStr(ctx, a, "ID", JS_NewString(ctx, qdesc+3) );
    1035         168 :                 } else if (!strncmp(qdesc, "mime=", 5)) {
    1036             :                         mime = NULL;
    1037          28 :                         JS_SetPropertyStr(ctx, a, "mime", JS_NewString(ctx, qdesc+5));
    1038         140 :                 } else if (!strncmp(qdesc, "codec=", 6)) {
    1039             :                         codec = NULL;
    1040          28 :                         JS_SetPropertyStr(ctx, a, "codec", JS_NewString(ctx, qdesc+6));
    1041             :                 }
    1042         140 :                 else if (!strncmp(qdesc, "bw=", 3)) bw = atoi(qdesc+3);
    1043         112 :                 else if (!strncmp(qdesc, "w=", 2)) w = atoi(qdesc+2);
    1044          84 :                 else if (!strncmp(qdesc, "h=", 2)) h = atoi(qdesc+2);
    1045          28 :                 else if (!strncmp(qdesc, "sr=", 3)) sr = atoi(qdesc+3);
    1046          28 :                 else if (!strncmp(qdesc, "ch=", 3)) ch = atoi(qdesc+3);
    1047          28 :                 else if (!strcmp(qdesc, "interlaced")) ilced = GF_TRUE;
    1048          28 :                 else if (!strcmp(qdesc, "disabled")) disabled = GF_TRUE;
    1049          28 :                 else if (!strncmp(qdesc, "fps=", 4)) {
    1050          28 :                         u32 fd=25, fn=1;
    1051          28 :                         if (sscanf(qdesc, "fps=%d/%d", &fn, &fd) != 2) {
    1052           0 :                                 fd=1;
    1053           0 :                                 sscanf(qdesc, "fps=%d", &fn);
    1054             :                         }
    1055          28 :                         fps = ((Double)fn) / fd;
    1056             :                 }
    1057           0 :                 else if (!strncmp(qdesc, "sar=", 4)) {
    1058           0 :                         sscanf(qdesc, "sar=%d/%d", &par_n, &par_d);
    1059             :                 }
    1060         196 :                 if (!sep) break;
    1061         168 :                 sep[0]=':';
    1062         168 :                 qdesc = sep+2;
    1063             :         }
    1064          28 :         prop = gf_filter_pid_get_info_str(odm->pid, "has:selected", &pe);
    1065          28 :         if (prop && (prop->value.uint==idx))
    1066             :                 selected = GF_TRUE;
    1067          28 :         prop = gf_filter_pid_get_info_str(odm->pid, "has:auto", &pe);
    1068          28 :         if (prop && prop->value.boolean)
    1069             :                 automatic = GF_TRUE;
    1070          28 :         prop = gf_filter_pid_get_info_str(odm->pid, "has:tilemode", &pe);
    1071          28 :         if (prop) tile_adaptation_mode = prop->value.uint;
    1072             : 
    1073          28 :         gf_filter_release_property(pe);
    1074             : 
    1075          28 :         if (id)
    1076           0 :                 JS_SetPropertyStr(ctx, a, "ID", JS_NewString(ctx, id));
    1077          28 :         if (mime)
    1078           0 :                 JS_SetPropertyStr(ctx, a, "mime", JS_NewString(ctx, mime));
    1079          28 :         if (codec)
    1080           0 :                 JS_SetPropertyStr(ctx, a, "codec", JS_NewString(ctx, codec));
    1081             : 
    1082          56 :         JS_SetPropertyStr(ctx, a, "width", JS_NewInt32(ctx, w));
    1083          56 :         JS_SetPropertyStr(ctx, a, "height", JS_NewInt32(ctx, h));
    1084          56 :         JS_SetPropertyStr(ctx, a, "bandwidth", JS_NewInt32(ctx, bw));
    1085          28 :         JS_SetPropertyStr(ctx, a, "interlaced", JS_NewBool(ctx, ilced));
    1086          28 :         JS_SetPropertyStr(ctx, a, "fps", JS_NewFloat64(ctx, fps) );
    1087          56 :         JS_SetPropertyStr(ctx, a, "samplerate", JS_NewInt32(ctx, sr));
    1088          56 :         JS_SetPropertyStr(ctx, a, "channels", JS_NewInt32(ctx, ch));
    1089          56 :         JS_SetPropertyStr(ctx, a, "par_num", JS_NewInt32(ctx, par_n));
    1090          56 :         JS_SetPropertyStr(ctx, a, "par_den", JS_NewInt32(ctx, par_d));
    1091          28 :         JS_SetPropertyStr(ctx, a, "disabled", JS_NewBool(ctx, disabled));
    1092          28 :         JS_SetPropertyStr(ctx, a, "is_selected", JS_NewBool(ctx, selected));
    1093          28 :         JS_SetPropertyStr(ctx, a, "automatic", JS_NewBool(ctx, automatic));
    1094          56 :         JS_SetPropertyStr(ctx, a, "tile_mode", JS_NewInt32(ctx, tile_adaptation_mode));
    1095          28 :         JS_SetPropertyStr(ctx, a, "dependent_groups", JS_NewInt32(ctx, dependent_group_index));
    1096             : 
    1097          28 :         return a;
    1098             : }
    1099             : 
    1100           7 : static JSValue gjs_odm_get_srd(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    1101             : {
    1102           7 :         GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id);
    1103             :         s32 x, y, w, h;
    1104             : 
    1105           7 :         if (!odm) return JS_EXCEPTION;
    1106             : 
    1107             :         x = y = w = h = 0;
    1108           7 :         if (argc) {
    1109             : #if 0 //FILTER_FIXME
    1110             :                 s32 dep_idx;
    1111             :                 if (JS_ToInt32(ctx, &dep_idx, argv[0]) )
    1112             :                         return JS_EXCEPTION;
    1113             : 
    1114             :                 memset(&com, 0, sizeof(GF_NetworkCommand));
    1115             :                 com.base.command_type = GF_NET_CHAN_GET_SRD;
    1116             :                 com.base.on_channel = gf_list_get(odm->channels, 0);
    1117             :                 com.srd.dependent_group_index = dep_idx;
    1118             : 
    1119             :                 if (gf_term_service_command(odm->net_service, &com) == GF_OK) {
    1120             :                         x = com.srd.x;
    1121             :                         y = com.srd.y;
    1122             :                         w = com.srd.w;
    1123             :                         h = com.srd.h;
    1124             :                 }
    1125             : #endif
    1126           7 :         } else if (odm && odm->mo && odm->mo->srd_w && odm->mo->srd_h) {
    1127           0 :                 x = odm->mo->srd_x;
    1128           0 :                 y = odm->mo->srd_y;
    1129           0 :                 w = odm->mo->srd_w;
    1130           0 :                 h = odm->mo->srd_h;
    1131             :         }
    1132             : 
    1133           7 :         if (w && h) {
    1134           0 :                 JSValue a = JS_NewObject(ctx);
    1135           0 :                 if (JS_IsException(a)) return a;
    1136             : 
    1137           0 :                 JS_SetPropertyStr(ctx, a, "x", JS_NewInt32(ctx, x));
    1138           0 :                 JS_SetPropertyStr(ctx, a, "y", JS_NewInt32(ctx, y));
    1139           0 :                 JS_SetPropertyStr(ctx, a, "w", JS_NewInt32(ctx, w));
    1140           0 :                 JS_SetPropertyStr(ctx, a, "h", JS_NewInt32(ctx, h));
    1141           0 :                 return a;
    1142             :         }
    1143           7 :         return JS_NULL;
    1144             : }
    1145             : 
    1146             : GF_Filter *jsff_get_filter(JSContext *c, JSValue this_val);
    1147             : 
    1148          13 : static JSValue gjs_odm_in_parent_chain(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    1149             : {
    1150             :         Bool res;
    1151             :         GF_Filter *f;
    1152             :         GF_Scene *scene;
    1153          13 :         GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id);
    1154          13 :         if (!odm || !argc) return JS_EXCEPTION;
    1155             : 
    1156          13 :         f = jsff_get_filter(ctx, argv[0]);
    1157          13 :         if (!f) return JS_EXCEPTION;
    1158             : 
    1159          13 :         scene = odm->subscene ? odm->subscene : odm->parentscene;
    1160          13 :         if (!scene) return JS_EXCEPTION;
    1161          13 :         if (gf_filter_in_parent_chain(f, scene->compositor->filter))
    1162           2 :                 return JS_FALSE;
    1163             : 
    1164          11 :         if (odm->pid) {
    1165           0 :                 res = gf_filter_pid_is_filter_in_parents(odm->pid, f);
    1166             :         } else {
    1167          11 :                 res = gf_filter_in_parent_chain(f, odm->scene_ns->source_filter);
    1168             :         }
    1169          11 :         return res ? JS_TRUE : JS_FALSE;
    1170             : }
    1171             : 
    1172           2 : static JSValue gjs_odm_select_quality(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    1173             : {
    1174             :         s32 idx = -1;
    1175           2 :         s32 tile_mode = -1;
    1176           2 :         u32 dep_idx = 0;
    1177             :         GF_FilterEvent evt;
    1178           2 :         GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id);
    1179             : 
    1180           2 :         if (!odm || !odm->pid) return JS_EXCEPTION;
    1181             : 
    1182           4 :         if ((argc>=1) && JS_IsString(argv[0])) {
    1183             :                 const char *ID = JS_ToCString(ctx, argv[0]);
    1184           2 :                 if (!strcmp(ID, "auto")) {
    1185             :                         idx = -1;
    1186             :                 } else {
    1187             :                         idx = atoi(ID);
    1188             :                 }
    1189           2 :                 JS_FreeCString(ctx, ID);
    1190             : 
    1191           2 :                 if (argc>=2) {
    1192           2 :                         if (JS_ToInt32(ctx, &dep_idx, argv[1]))
    1193           0 :                                 return JS_EXCEPTION;
    1194             :                 }
    1195             :         }
    1196           0 :         else if ((argc==1) && JS_IsInteger(argv[0])) {
    1197           0 :                 if (JS_ToInt32(ctx, &tile_mode, argv[0]))
    1198           0 :                         return JS_EXCEPTION;
    1199           0 :                 if (tile_mode<0)
    1200           0 :                         return JS_UNDEFINED;
    1201             :         }
    1202             : 
    1203           2 :         GF_FEVT_INIT(evt, GF_FEVT_QUALITY_SWITCH, odm->pid);
    1204             : 
    1205           2 :         if (tile_mode>=0) {
    1206           0 :                 evt.quality_switch.set_tile_mode_plus_one = 1 + tile_mode;
    1207             :         } else {
    1208           2 :                 evt.quality_switch.dependent_group_index = dep_idx;
    1209           2 :                 evt.quality_switch.q_idx = idx;
    1210             :         }
    1211           2 :         gf_filter_pid_send_event(odm->pid, &evt);
    1212           2 :         return JS_UNDEFINED;
    1213             : }
    1214             : 
    1215           1 : static JSValue gjs_odm_disable_main_addon(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    1216             : {
    1217           1 :         GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id);
    1218           1 :         if (!odm) return JS_EXCEPTION;
    1219           1 :         if (!odm->subscene || !odm->subscene->main_addon_selected) return JS_UNDEFINED;
    1220             : 
    1221           0 :         gf_scene_resume_live(odm->subscene);
    1222           0 :         return JS_UNDEFINED;
    1223             : }
    1224             : 
    1225           1 : static JSValue gjs_odm_select_service(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    1226             : {
    1227             :         u32 sid;
    1228           1 :         GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id);
    1229           1 :         if (!odm) return JS_EXCEPTION;
    1230           1 :         if (argc<1) return JS_EXCEPTION;
    1231             : 
    1232           1 :         if (JS_ToInt32(ctx, &sid, argv[0]))
    1233           0 :                 return JS_EXCEPTION;
    1234             : 
    1235           1 :         gf_scene_set_service_id(odm->subscene ? odm->subscene : odm->parentscene, sid);
    1236           1 :         return JS_UNDEFINED;
    1237             : }
    1238             : 
    1239           1 : static JSValue gjs_odm_select(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    1240             : {
    1241           1 :         GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id);
    1242           1 :         if (!odm) return JS_EXCEPTION;
    1243             : 
    1244             : #ifndef GPAC_DISABLE_PLAYER
    1245           1 :         gf_scene_select_object(odm->parentscene, odm);
    1246             : #endif
    1247           1 :         return JS_UNDEFINED;
    1248             : }
    1249             : 
    1250           5 : static JSValue gjs_odm_get_resource(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    1251             : {
    1252             :         GF_ObjectManager *an_odm = NULL;
    1253             :         u32 idx;
    1254           5 :         GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id);
    1255             : 
    1256           5 :         if (!odm) return JS_EXCEPTION;
    1257           5 :         if (argc<1) return JS_EXCEPTION;
    1258           5 :         if (JS_ToInt32(ctx, &idx, argv[0])) return JS_EXCEPTION;
    1259             : 
    1260           5 :         if (odm->subscene) {
    1261           5 :                 an_odm = gf_list_get(odm->subscene->resources, idx);
    1262             :         }
    1263           5 :         if (an_odm && an_odm->scene_ns) {
    1264           5 :                 JSValue anobj = JS_NewObjectClass(ctx, odm_class_id);
    1265           5 :                 JS_SetOpaque(anobj, an_odm);
    1266           5 :                 return anobj;
    1267             :         }
    1268           0 :         return JS_NULL;
    1269             : }
    1270             : 
    1271           1 : static JSValue gjs_odm_addon_layout(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    1272             : {
    1273             :         u32 pos, size;
    1274           1 :         GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id);
    1275           1 :         if (!odm) return JS_EXCEPTION;
    1276           1 :         if (argc<2) return JS_EXCEPTION;
    1277           1 :         if (JS_ToInt32(ctx, &pos, argv[0])) return JS_EXCEPTION;
    1278           1 :         if (JS_ToInt32(ctx, &size, argv[1])) return JS_EXCEPTION;
    1279           1 :         if (odm->subscene)
    1280           0 :                 gf_scene_set_addon_layout_info(odm->subscene, pos, size);
    1281           1 :         return JS_UNDEFINED;
    1282             : }
    1283             : 
    1284             : static void do_enable_addon(GF_ObjectManager *odm, char *addon_url, Bool enable_if_defined, Bool disable_if_defined )
    1285             : {
    1286             :         if (addon_url) {
    1287             : #ifdef FILTER_FIXME
    1288             :                 GF_AssociatedContentLocation addon_info;
    1289             :                 memset(&addon_info, 0, sizeof(GF_AssociatedContentLocation));
    1290             :                 addon_info.external_URL = addon_url;
    1291             :                 addon_info.timeline_id = -100;
    1292             :                 addon_info.enable_if_defined = enable_if_defined;
    1293             :                 addon_info.disable_if_defined = disable_if_defined;
    1294             :                 gf_scene_register_associated_media(odm->subscene ? odm->subscene : odm->parentscene, &addon_info);
    1295             : #endif
    1296             :         }
    1297             : }
    1298             : 
    1299           1 : static JSValue gjs_odm_enable_addon(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    1300             : {
    1301             :         Bool do_disable = GF_FALSE;
    1302             :         const char *addon_url = NULL;
    1303           1 :         GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id);
    1304           1 :         if (!odm || !argc) return JS_EXCEPTION;
    1305             : 
    1306           2 :         if (! JS_IsString(argv[0]) ) {
    1307             : #ifdef GPAC_ENABLE_COVERAGE
    1308           0 :                 if (gf_sys_is_cov_mode()) {
    1309             :                         do_enable_addon(odm, NULL, GF_TRUE, GF_FALSE);
    1310             :                 }
    1311             : #endif
    1312           0 :                 return JS_UNDEFINED;
    1313             :         }
    1314           1 :         if (argc==2)
    1315           0 :                 do_disable = JS_ToBool(ctx, argv[1]);
    1316             :         addon_url = JS_ToCString(ctx, argv[0]);
    1317             :         if (addon_url) {
    1318             :                 do_enable_addon(odm, (char *) addon_url, GF_TRUE, do_disable);
    1319             :         }
    1320           1 :         JS_FreeCString(ctx, addon_url);
    1321           1 :         return JS_UNDEFINED;
    1322             : }
    1323             : 
    1324           1 : static JSValue gjs_odm_declare_addon(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    1325             : {
    1326             :         const char *addon_url = NULL;
    1327           1 :         GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id);
    1328           1 :         if (!odm || !argc) return JS_EXCEPTION;
    1329           2 :         if (! JS_IsString(argv[0]) ) return JS_EXCEPTION;
    1330             : 
    1331             :         addon_url = JS_ToCString(ctx, argv[0]);
    1332             :         if (addon_url && strcmp(addon_url, "gtest")) {
    1333             :                 do_enable_addon(odm, (char *)addon_url, GF_FALSE, GF_FALSE);
    1334             :         }
    1335           1 :         JS_FreeCString(ctx, addon_url);
    1336           1 :         return JS_UNDEFINED;
    1337             : }
    1338             : 
    1339           1 : static JSValue scenejs_get_object_manager(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    1340             : {
    1341             :         JSValue anobj;
    1342             :         u32 i, count;
    1343             :         GF_ObjectManager *odm = NULL;
    1344             :         const char *service_url = NULL;
    1345           1 :         GF_SCENEJSExt *sjs = JS_GetOpaque(this_val, scene_class_id);
    1346             :         GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val);
    1347           1 :         GF_Scene *scene = compositor->root_scene;
    1348           1 :         if (!sjs) return JS_EXCEPTION;
    1349             : 
    1350           2 :         if (JS_IsString(argv[0]) ) {
    1351             :                 const char *url;
    1352             :                 char *an_url;
    1353             :                 u32 url_len;
    1354             :                 url = service_url = JS_ToCString(ctx, argv[0]);
    1355           1 :                 if (!service_url) {
    1356           0 :                         return JS_NULL;
    1357             :                 }
    1358           1 :                 if (!strncmp(service_url, "gpac://", 7)) url = service_url + 7;
    1359           1 :                 if (!strncmp(service_url, "file://", 7)) url = service_url + 7;
    1360           1 :                 url_len = (u32) strlen(url);
    1361           1 :                 an_url = strchr(url, '#');
    1362           1 :                 if (an_url) url_len -= (u32) strlen(an_url);
    1363             : 
    1364           1 :                 count = gf_list_count(scene->resources);
    1365           5 :                 for (i=0; i<count; i++) {
    1366           5 :                         odm = gf_list_get(scene->resources, i);
    1367           5 :                         if (odm->scene_ns) {
    1368           5 :                                 an_url = odm->scene_ns->url;
    1369           5 :                                 if (!strncmp(an_url, "gpac://", 7)) an_url = an_url + 7;
    1370           5 :                                 if (!strncmp(an_url, "file://", 7)) an_url = an_url + 7;
    1371           5 :                                 if (!strncmp(an_url, url, url_len))
    1372             :                                         break;
    1373             :                         }
    1374             :                         odm = NULL;
    1375             :                 }
    1376             :         }
    1377             : 
    1378           1 :         JS_FreeCString(ctx, service_url);
    1379             : 
    1380           1 :         if (!odm) return JS_NULL;
    1381             : 
    1382           1 :         anobj = JS_NewObjectClass(ctx, odm_class_id);
    1383           1 :         if (JS_IsException(anobj)) return anobj;
    1384             : 
    1385           1 :         JS_SetOpaque(anobj, odm);
    1386           1 :         return anobj;
    1387             : }
    1388             : 
    1389             : 
    1390         242 : static JSValue gpacevt_getProperty(JSContext *ctx, JSValueConst this_val, int magic)
    1391             : {
    1392         242 :         GF_SCENEJSExt *sjs = JS_GetOpaque(this_val, gpacevt_class_id);
    1393         242 :         if (!sjs || !sjs->evt) return JS_EXCEPTION;
    1394             :         GF_Event *evt = sjs->evt;
    1395             : 
    1396         242 :         switch (magic) {
    1397           0 :         case GJS_EVT_PROP_KEYCODE:
    1398             : #ifndef GPAC_DISABLE_SVG
    1399           0 :                 return JS_NewString(ctx, gf_dom_get_key_name(evt->key.key_code) );
    1400             : #else
    1401             :                 return JS_NULL;
    1402             : #endif
    1403             : 
    1404           0 :         case GJS_EVT_PROP_MOUSE_X:
    1405           0 :                 return JS_NewInt32(ctx, evt->mouse.x);
    1406           0 :         case GJS_EVT_PROP_MOUSE_Y:
    1407           0 :                 return JS_NewInt32(ctx, evt->mouse.y);
    1408           0 :         case GJS_EVT_PROP_PICKED:
    1409           0 :                 if (sjs->compositor->hit_appear) return JS_NewBool(ctx, 1);
    1410           0 :                 else if (gf_list_count(sjs->compositor->previous_sensors) ) return JS_NewBool(ctx, 1);
    1411           0 :                 else if (sjs->compositor->text_selection) return JS_NewBool(ctx, 1);
    1412             :                 else return JS_NewBool(ctx, 0);
    1413             : 
    1414           0 :         case GJS_EVT_PROP_WHEEL:
    1415           0 :                 return JS_NewFloat64(ctx, FIX2FLT(evt->mouse.wheel_pos));
    1416           0 :         case GJS_EVT_PROP_BUTTON:
    1417           0 :                 return JS_NewInt32(ctx,  evt->mouse.button);
    1418         242 :         case GJS_EVT_PROP_TYPE:
    1419         242 :                 return JS_NewInt32(ctx, evt->type);
    1420           0 :         case GJS_EVT_PROP_NAME:
    1421             : #ifndef GPAC_DISABLE_SVG
    1422           0 :                 return JS_NewString(ctx, gf_dom_event_get_name(evt->type) );
    1423             : #else
    1424             :                 return JS_NULL;
    1425             : #endif
    1426             : 
    1427           0 :         case GJS_EVT_PROP_HWKEY:
    1428           0 :                 return JS_NewInt32(ctx, evt->key.hw_code);
    1429           0 :         case GJS_EVT_PROP_TARGET_URL:
    1430           0 :                 return JS_NewString(ctx, evt->navigate.to_url);
    1431           0 :         case GJS_EVT_PROP_FILES:
    1432             :         {
    1433             :                 u32 i, idx;
    1434           0 :                 JSValue files_array = JS_NewArray(ctx);
    1435             :                 idx=0;
    1436           0 :                 for (i=0; i<evt->open_file.nb_files; i++) {
    1437           0 :                         if (evt->open_file.files[i]) {
    1438           0 :                                 JS_SetPropertyUint32(ctx, files_array, idx, JS_NewString(ctx, evt->open_file.files[i]) );
    1439           0 :                                 idx++;
    1440             :                         }
    1441             :                 }
    1442           0 :                 return files_array;
    1443             :         }
    1444             :         }
    1445           0 :         return JS_UNDEFINED;
    1446             : }
    1447             : 
    1448          55 : static Bool gjs_event_filter_process(GF_SCENEJSExt *sjs, GF_Event *evt)
    1449             : {
    1450             :         s32 res;
    1451             :         JSValue rval;
    1452             : 
    1453          55 :         sjs->evt = evt;
    1454          55 :         JS_SetOpaque(sjs->evt_obj, sjs);
    1455          55 :         rval = JS_Call(sjs->c, sjs->evt_fun, sjs->evt_filter_obj, 1, &sjs->evt_obj);
    1456          55 :         JS_SetOpaque(sjs->evt_obj, NULL);
    1457          55 :         sjs->evt = NULL;
    1458             : 
    1459          55 :         res = 0;
    1460          55 :         if (JS_IsBool(rval))
    1461          55 :                 res = JS_ToBool(sjs->c, rval);
    1462           0 :         else if (JS_IsNumber(rval))
    1463           0 :                 JS_ToInt32(sjs->c, &res, rval);
    1464             : 
    1465          55 :         JS_FreeValue(sjs->c, rval);
    1466          55 :         return res ? GF_TRUE : GF_FALSE;
    1467             : }
    1468             : 
    1469          55 : static Bool gjs_event_filter(void *udta, GF_Event *evt, Bool consumed_by_compositor)
    1470             : {
    1471             :         u32 lock_fail;
    1472             :         Bool res;
    1473             :         GF_SCENEJSExt *sjs = (GF_SCENEJSExt *)udta;
    1474          55 :         if (consumed_by_compositor) return 0;
    1475             : 
    1476          55 :         if (sjs->evt != NULL) return 0;
    1477             : 
    1478             :         lock_fail=0;
    1479          55 :         res = gf_mx_try_lock(sjs->compositor->mx);
    1480          55 :         if (!res) {
    1481             :                 lock_fail=1;
    1482             :         } else {
    1483          55 :                 res = gf_js_try_lock(sjs->c);
    1484          55 :                 if (!res) lock_fail=2;
    1485             :         }
    1486             :         if (lock_fail) {
    1487             :                 GF_Event *evt_clone;
    1488           0 :                 gf_mx_p(sjs->event_mx);
    1489           0 :                 evt_clone = gf_malloc(sizeof(GF_Event));
    1490             :                 memcpy(evt_clone, evt, sizeof(GF_Event));
    1491           0 :                 gf_list_add(sjs->event_queue, evt_clone);
    1492           0 :                 GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[SCENEJS] Couldn't lock % mutex, queing event\n", (lock_fail==2) ? "JavaScript" : "Compositor"));
    1493           0 :                 gf_mx_v(sjs->event_mx);
    1494             : 
    1495           0 :                 if (lock_fail==2){
    1496           0 :                         gf_mx_v(sjs->compositor->mx);
    1497             :                 }
    1498             :                 return 0;
    1499             :         }
    1500             : 
    1501          55 :         gf_mx_p(sjs->event_mx);
    1502         110 :         while (gf_list_count(sjs->event_queue)) {
    1503           0 :                 GF_Event *an_evt = (GF_Event *) gf_list_pop_front(sjs->event_queue);
    1504           0 :                 gjs_event_filter_process(sjs, an_evt);
    1505           0 :                 gf_free(an_evt);
    1506             :         }
    1507          55 :         gf_mx_v(sjs->event_mx);
    1508             : 
    1509          55 :         res = gjs_event_filter_process(sjs, evt);
    1510             : 
    1511          55 :         gf_mx_v(sjs->compositor->mx);
    1512          55 :         gf_js_lock(sjs->c, 0);
    1513          55 :         return res;
    1514             : }
    1515             : 
    1516           3 : static JSValue scenejs_set_event_filter(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    1517             : {
    1518           3 :         GF_SCENEJSExt *sjs = JS_GetOpaque(this_val, scene_class_id);
    1519           3 :         if (!sjs || !argc)
    1520           0 :                 return JS_EXCEPTION;
    1521             : 
    1522           6 :         if (!JS_IsNull(argv[0]) && !JS_IsUndefined(argv[0]) && !JS_IsFunction(ctx, argv[0]))
    1523           0 :                 return JS_EXCEPTION;
    1524             : 
    1525           3 :         JS_FreeValue(sjs->c, sjs->evt_fun);
    1526           3 :         sjs->evt_fun = JS_DupValue(ctx, argv[0]);
    1527           3 :         sjs->evt_filter_obj = this_val;
    1528           3 :         sjs->c = ctx;
    1529           3 :         sjs->evt_filter.udta = sjs;
    1530           3 :         sjs->evt_filter.on_event = gjs_event_filter;
    1531             : 
    1532           3 :         gf_filter_add_event_listener(sjs->compositor->filter, &sjs->evt_filter);
    1533           3 :         return JS_UNDEFINED;
    1534             : }
    1535             : 
    1536             : GF_Node *gf_sg_js_get_node(struct JSContext *c, JSValue obj);
    1537             : 
    1538          19 : static JSValue scenejs_set_focus(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    1539             : {
    1540             :         GF_Node *elt;
    1541             :         GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val);
    1542          19 :         if (!compositor || !argc) return JS_EXCEPTION;
    1543             : 
    1544          38 :         if (JS_IsNull(argv[0])) {
    1545           0 :                 gf_sc_focus_switch_ring(compositor, 0, NULL, 0);
    1546           0 :                 return JS_UNDEFINED;
    1547             :         }
    1548             : 
    1549          19 :         if (JS_IsString(argv[0])) {
    1550             :                 const char *focus_type = JS_ToCString(ctx, argv[0]);
    1551           0 :                 if (!stricmp(focus_type, "previous")) {
    1552           0 :                         gf_sc_focus_switch_ring(compositor, 1, NULL, 0);
    1553             :                 }
    1554           0 :                 else if (!stricmp(focus_type, "next")) {
    1555           0 :                         gf_sc_focus_switch_ring(compositor, 0, NULL, 0);
    1556             :                 }
    1557           0 :                 JS_FreeCString(ctx, focus_type);
    1558          19 :         } else if (JS_IsObject(argv[0])) {
    1559          19 :                 elt = gf_sg_js_get_node(ctx, argv[0]);
    1560          19 :                 if (!elt) return JS_EXCEPTION;
    1561          19 :                 gf_sc_focus_switch_ring(compositor, 0, elt, 2);
    1562             :         }
    1563          19 :         return JS_UNDEFINED;
    1564             : }
    1565             : 
    1566             : #if 0 //unused
    1567             : static JSValue scenejs_get_scene(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    1568             : {
    1569             :         GF_Node *elt;
    1570             :         u32 w, h;
    1571             :         JSValue scene_obj;
    1572             : #ifndef GPAC_DISABLE_SCENEGRAPH
    1573             :         GF_SceneGraph *sg;
    1574             : #endif
    1575             :         GF_Scene *scene=NULL;
    1576             :         GF_SCENEJSExt *sjs = (GF_SCENEJSExt *)JS_GetOpaque(this_val, scene_class_id);
    1577             :         if (!sjs || !argc || !JS_IsObject(argv[0])) return JS_EXCEPTION;
    1578             : 
    1579             :         elt = gf_sg_js_get_node(ctx, argv[0]);
    1580             :         if (!elt) return JS_EXCEPTION;
    1581             :         switch (elt->sgprivate->tag) {
    1582             : #ifndef GPAC_DISABLE_VRML
    1583             :         case TAG_MPEG4_Inline:
    1584             :                 scene = (GF_Scene *)gf_node_get_private(elt);
    1585             :                 break;
    1586             : #endif
    1587             : 
    1588             : #ifndef GPAC_DISABLE_X3D
    1589             :         case TAG_X3D_Inline:
    1590             :                 scene = (GF_Scene *)gf_node_get_private(elt);
    1591             :                 break;
    1592             : #endif
    1593             : 
    1594             : #ifndef GPAC_DISABLE_SVG
    1595             :         case TAG_SVG_animation:
    1596             :                 sg = gf_sc_animation_get_scenegraph(elt);
    1597             :                 scene = (GF_Scene *)gf_sg_get_private(sg);
    1598             :                 break;
    1599             : #endif
    1600             :         default:
    1601             :                 return JS_EXCEPTION;
    1602             :         }
    1603             :         if (!scene) return JS_EXCEPTION;
    1604             : 
    1605             : 
    1606             :         scene_obj = JS_NewObjectClass(ctx, any_class_id);
    1607             :         if (JS_IsException(scene_obj)) return scene_obj;
    1608             :         JS_SetOpaque(scene_obj, scene);
    1609             :         gf_sg_get_scene_size_info(scene->graph, &w, &h);
    1610             :         JS_SetPropertyStr(ctx, scene_obj, "width", JS_NewInt32(ctx, w));
    1611             :         JS_SetPropertyStr(ctx, scene_obj, "height", JS_NewInt32(ctx, h));
    1612             :         JS_SetPropertyStr(ctx, scene_obj, "connected", JS_NewBool(ctx, scene->graph ? 1 : 0));
    1613             :         return scene_obj;
    1614             : }
    1615             : #endif
    1616             : 
    1617           1 : static JSValue scenejs_show_keyboard(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    1618             : {
    1619             :         GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val);
    1620           1 :         if (!compositor || !argc) return JS_EXCEPTION;
    1621             : 
    1622             :         GF_Event evt;
    1623             :         memset(&evt, 0, sizeof(GF_Event));
    1624           1 :         evt.type = JS_ToBool(ctx, argv[0]) ? GF_EVENT_TEXT_EDITING_START : GF_EVENT_TEXT_EDITING_END;
    1625           1 :         gf_sc_user_event(compositor, &evt);
    1626           1 :         return JS_UNDEFINED;
    1627             : }
    1628             : 
    1629             : static const JSCFunctionListEntry scenejs_evt_funcs[] = {
    1630             :         JS_CGETSET_MAGIC_DEF("keycode", gpacevt_getProperty, NULL, GJS_EVT_PROP_KEYCODE),
    1631             :         JS_CGETSET_MAGIC_DEF("mouse_x", gpacevt_getProperty, NULL, GJS_EVT_PROP_MOUSE_X),
    1632             :         JS_CGETSET_MAGIC_DEF("mouse_y", gpacevt_getProperty, NULL, GJS_EVT_PROP_MOUSE_Y),
    1633             :         JS_CGETSET_MAGIC_DEF("picked", gpacevt_getProperty, NULL, GJS_EVT_PROP_PICKED),
    1634             :         JS_CGETSET_MAGIC_DEF("wheel", gpacevt_getProperty, NULL, GJS_EVT_PROP_WHEEL),
    1635             :         JS_CGETSET_MAGIC_DEF("button", gpacevt_getProperty, NULL, GJS_EVT_PROP_BUTTON),
    1636             :         JS_CGETSET_MAGIC_DEF("type", gpacevt_getProperty, NULL, GJS_EVT_PROP_TYPE),
    1637             :         JS_CGETSET_MAGIC_DEF("name", gpacevt_getProperty, NULL, GJS_EVT_PROP_NAME),
    1638             :         JS_CGETSET_MAGIC_DEF("hwkey", gpacevt_getProperty, NULL, GJS_EVT_PROP_HWKEY),
    1639             :         JS_CGETSET_MAGIC_DEF("url", gpacevt_getProperty, NULL, GJS_EVT_PROP_TARGET_URL),
    1640             :         JS_CGETSET_MAGIC_DEF("dropfiles", gpacevt_getProperty, NULL, GJS_EVT_PROP_FILES),
    1641             : };
    1642             : 
    1643             : static const JSCFunctionListEntry scenejs_funcs[] = {
    1644             :         JS_CGETSET_MAGIC_DEF("fullscreen", scenejs_getProperty, scenejs_setProperty, GJS_SCENE_PROP_FULLSCREEN),
    1645             :         JS_CGETSET_MAGIC_DEF("current_path", scenejs_getProperty, NULL, GJS_SCENE_PROP_CURRENT_PATH),
    1646             :         JS_CGETSET_MAGIC_DEF("volume", scenejs_getProperty, scenejs_setProperty, GJS_SCENE_PROP_VOLUME),
    1647             :         JS_CGETSET_MAGIC_DEF("navigation", scenejs_getProperty, scenejs_setProperty, GJS_SCENE_PROP_NAVIGATION),
    1648             :         JS_CGETSET_MAGIC_DEF("navigation_type", scenejs_getProperty, scenejs_setProperty, GJS_SCENE_PROP_NAVIGATION_TYPE),
    1649             :         JS_CGETSET_MAGIC_DEF("hardware_yuv", scenejs_getProperty, NULL, GJS_SCENE_PROP_HARDWARE_YUV),
    1650             :         JS_CGETSET_MAGIC_DEF("hardware_rgb", scenejs_getProperty, NULL, GJS_SCENE_PROP_HARDWARE_RGB),
    1651             :         JS_CGETSET_MAGIC_DEF("hardware_rgba", scenejs_getProperty, NULL, GJS_SCENE_PROP_HARDWARE_RGBA),
    1652             :         JS_CGETSET_MAGIC_DEF("hardware_stretch", scenejs_getProperty, NULL, GJS_SCENE_PROP_HARDWARE_STRETCH),
    1653             :         JS_CGETSET_MAGIC_DEF("screen_width", scenejs_getProperty, NULL, GJS_SCENE_PROP_SCREEN_WIDTH),
    1654             :         JS_CGETSET_MAGIC_DEF("screen_height", scenejs_getProperty, NULL, GJS_SCENE_PROP_SCREEN_HEIGHT),
    1655             :         JS_CGETSET_MAGIC_DEF("fps", scenejs_getProperty, NULL, GJS_SCENE_PROP_FPS),
    1656             :         JS_CGETSET_MAGIC_DEF("sim_fps", scenejs_getProperty, NULL, GJS_SCENE_PROP_SIM_FPS),
    1657             :         JS_CGETSET_MAGIC_DEF("has_opengl", scenejs_getProperty, NULL, GJS_SCENE_PROP_HAS_OPENGL),
    1658             :         JS_CGETSET_MAGIC_DEF("caption", scenejs_getProperty, scenejs_setProperty, GJS_SCENE_PROP_CAPTION),
    1659             :         JS_CGETSET_MAGIC_DEF("focus_highlight", scenejs_getProperty, scenejs_setProperty, GJS_SCENE_PROP_FOCUS_HIGHLIGHT),
    1660             :         JS_CGETSET_MAGIC_DEF("dpi_x", scenejs_getProperty, NULL, GJS_SCENE_PROP_DPI_X),
    1661             :         JS_CGETSET_MAGIC_DEF("dpi_y", scenejs_getProperty, NULL, GJS_SCENE_PROP_DPI_Y),
    1662             :         JS_CGETSET_MAGIC_DEF("sensors_active", scenejs_getProperty, scenejs_setProperty, GJS_SCENE_PROP_SENSORS_ACTIVE),
    1663             :         JS_CGETSET_MAGIC_DEF("zoom", scenejs_getProperty, NULL, GJS_SCENE_PROP_ZOOM),
    1664             :         JS_CGETSET_MAGIC_DEF("text_selection", scenejs_getProperty, NULL, GJS_SCENE_PROP_TEXT_SEL),
    1665             :         JS_CFUNC_DEF("get_option", 0, scenejs_get_option),
    1666             :         JS_CFUNC_DEF("set_option", 0, scenejs_set_option),
    1667             :         JS_CFUNC_DEF("set_size", 0, scenejs_set_size),
    1668             :         JS_CFUNC_DEF("exit", 0, scenejs_exit),
    1669             :         JS_CFUNC_DEF("set_3d", 0, scenejs_set_3d),
    1670             :         JS_CFUNC_DEF("move_window", 0, scenejs_move_window),
    1671             :         JS_CFUNC_DEF("set_event_filter", 0, scenejs_set_event_filter),
    1672             :         JS_CFUNC_DEF("set_focus", 0, scenejs_set_focus),
    1673             :         JS_CFUNC_DEF("show_keyboard", 0, scenejs_show_keyboard),
    1674             :         JS_CFUNC_DEF("trigger_gc", 0, scenejs_trigger_gc),
    1675             :         JS_CFUNC_DEF("get_object_manager", 0, scenejs_get_object_manager),
    1676             :         JS_CFUNC_DEF("switch_quality", 0, scenejs_switch_quality),
    1677             :         JS_CFUNC_DEF("navigation_supported", 0, scenejs_navigation_supported),
    1678             :         JS_CFUNC_DEF("set_back_color", 0, scenejs_set_back_color)
    1679             : };
    1680             : 
    1681             : static const JSCFunctionListEntry odm_funcs[] = {
    1682             :         JS_CGETSET_MAGIC_DEF("ID", odm_getProperty, NULL, GJS_OM_PROP_ID),
    1683             :         JS_CGETSET_MAGIC_DEF("nb_resources", odm_getProperty, NULL, GJS_OM_PROP_NB_RES),
    1684             :         JS_CGETSET_MAGIC_DEF("service_url", odm_getProperty, NULL, GJS_OM_PROP_URL),
    1685             :         JS_CGETSET_MAGIC_DEF("duration", odm_getProperty, NULL, GJS_OM_PROP_DUR),
    1686             :         JS_CGETSET_MAGIC_DEF("clock_time", odm_getProperty, NULL, GJS_OM_PROP_CLOCK),
    1687             :         JS_CGETSET_MAGIC_DEF("clock_drift", odm_getProperty, NULL, GJS_OM_PROP_DRIFT),
    1688             :         JS_CGETSET_MAGIC_DEF("status", odm_getProperty, NULL, GJS_OM_PROP_STATUS),
    1689             :         JS_CGETSET_MAGIC_DEF("buffer", odm_getProperty, NULL, GJS_OM_PROP_BUFFER),
    1690             :         JS_CGETSET_MAGIC_DEF("db_unit_count", odm_getProperty, NULL, GJS_OM_PROP_DB_COUNT),
    1691             :         JS_CGETSET_MAGIC_DEF("cb_unit_count", odm_getProperty, NULL, GJS_OM_PROP_CB_COUNT),
    1692             :         JS_CGETSET_MAGIC_DEF("cb_capacity", odm_getProperty, NULL, GJS_OM_PROP_CB_CAP),
    1693             :         JS_CGETSET_MAGIC_DEF("type", odm_getProperty, NULL, GJS_OM_PROP_TYPE),
    1694             :         JS_CGETSET_MAGIC_DEF("samplerate", odm_getProperty, NULL, GJS_OM_PROP_SAMPLERATE),
    1695             :         JS_CGETSET_MAGIC_DEF("channels", odm_getProperty, NULL, GJS_OM_PROP_CHANNELS),
    1696             :         JS_CGETSET_MAGIC_DEF("lang", odm_getProperty, NULL, GJS_OM_PROP_LANG),
    1697             :         JS_CGETSET_MAGIC_DEF("width", odm_getProperty, NULL, GJS_OM_PROP_WIDTH),
    1698             :         JS_CGETSET_MAGIC_DEF("height", odm_getProperty, NULL, GJS_OM_PROP_HEIGHT),
    1699             :         JS_CGETSET_MAGIC_DEF("pixelformt", odm_getProperty, NULL, GJS_OM_PROP_PIXELFORMAT),
    1700             :         JS_CGETSET_MAGIC_DEF("par", odm_getProperty, NULL, GJS_OM_PROP_PAR),
    1701             :         JS_CGETSET_MAGIC_DEF("dec_frames", odm_getProperty, NULL, GJS_OM_PROP_DEC_FRAMES),
    1702             :         JS_CGETSET_MAGIC_DEF("drop_frames", odm_getProperty, NULL, GJS_OM_PROP_DROP_FRAMES),
    1703             :         JS_CGETSET_MAGIC_DEF("max_dec_time", odm_getProperty, NULL, GJS_OM_PROP_DEC_TIME_MAX),
    1704             :         JS_CGETSET_MAGIC_DEF("total_dec_time", odm_getProperty, NULL, GJS_OM_PROP_DEC_TIME_TOTAL),
    1705             :         JS_CGETSET_MAGIC_DEF("avg_bitrate", odm_getProperty, NULL, GJS_OM_PROP_AVG_RATE),
    1706             :         JS_CGETSET_MAGIC_DEF("max_bitrate", odm_getProperty, NULL, GJS_OM_PROP_MAX_RATE),
    1707             :         JS_CGETSET_MAGIC_DEF("service_handler", odm_getProperty, NULL, GJS_OM_PROP_SERVICE_HANDLER),
    1708             :         JS_CGETSET_MAGIC_DEF("codec", odm_getProperty, NULL, GJS_OM_PROP_CODEC),
    1709             :         JS_CGETSET_MAGIC_DEF("nb_qualities", odm_getProperty, NULL, GJS_OM_PROP_NB_QUALITIES),
    1710             :         JS_CGETSET_MAGIC_DEF("max_buffer", odm_getProperty, NULL, GJS_OM_PROP_MAX_BUFFER),
    1711             :         JS_CGETSET_MAGIC_DEF("min_buffer", odm_getProperty, NULL, GJS_OM_PROP_MIN_BUFFER),
    1712             :         JS_CGETSET_MAGIC_DEF("frame_duration", odm_getProperty, NULL, GJS_OM_PROP_FRAME_DUR),
    1713             :         JS_CGETSET_MAGIC_DEF("irap_frames", odm_getProperty, NULL, GJS_OM_PROP_NB_IRAP),
    1714             :         JS_CGETSET_MAGIC_DEF("irap_dec_time", odm_getProperty, NULL, GJS_OM_PROP_IRAP_DEC_TIME),
    1715             :         JS_CGETSET_MAGIC_DEF("irap_max_time", odm_getProperty, NULL, GJS_OM_PROP_IRAP_MAX_TIME),
    1716             :         JS_CGETSET_MAGIC_DEF("service_id", odm_getProperty, NULL, GJS_OM_PROP_SERVICE_ID),
    1717             :         JS_CGETSET_MAGIC_DEF("selected_service", odm_getProperty, NULL, GJS_OM_PROP_SELECTED_SERVICE),
    1718             :         JS_CGETSET_MAGIC_DEF("bandwidth_down", odm_getProperty, NULL, GJS_OM_PROP_BANDWIDTH_DOWN),
    1719             :         JS_CGETSET_MAGIC_DEF("nb_http", odm_getProperty, NULL, GJS_OM_PROP_NB_HTTP),
    1720             :         JS_CGETSET_MAGIC_DEF("timeshift_depth", odm_getProperty, NULL, GJS_OM_PROP_TIMESHIFT_DEPTH),
    1721             :         JS_CGETSET_MAGIC_DEF("timeshift_time", odm_getProperty, NULL, GJS_OM_PROP_TIMESHIFT_TIME),
    1722             :         JS_CGETSET_MAGIC_DEF("is_addon", odm_getProperty, NULL, GJS_OM_PROP_IS_ADDON),
    1723             :         JS_CGETSET_MAGIC_DEF("main_addon_on", odm_getProperty, NULL, GJS_OM_PROP_MAIN_ADDON_ON),
    1724             :         JS_CGETSET_MAGIC_DEF("is_over", odm_getProperty, NULL, GJS_OM_PROP_IS_OVER),
    1725             :         JS_CGETSET_MAGIC_DEF("dynamic_scene", odm_getProperty, NULL, GJS_OM_PROP_DYNAMIC_SCENE),
    1726             :         JS_CGETSET_MAGIC_DEF("service_name", odm_getProperty, NULL, GJS_OM_PROP_SERVICE_NAME),
    1727             :         JS_CGETSET_MAGIC_DEF("ntp_diff", odm_getProperty, NULL, GJS_OM_PROP_NTP_DIFF),
    1728             :         JS_CGETSET_MAGIC_DEF("ntp_sender_diff", odm_getProperty, NULL, GJS_OM_PROP_NTP_SENDER_DIFF),
    1729             :         JS_CGETSET_MAGIC_DEF("main_addon_url", odm_getProperty, NULL, GJS_OM_PROP_MAIN_ADDON_URL),
    1730             :         JS_CGETSET_MAGIC_DEF("reverse_playback_supported", odm_getProperty, NULL, GJS_OM_PROP_REVERSE_PLAYBACK),
    1731             :         JS_CGETSET_MAGIC_DEF("scalable_enhancement", odm_getProperty, NULL, GJS_OM_PROP_SCALABLE_ENHANCEMENT),
    1732             :         JS_CGETSET_MAGIC_DEF("main_addon_media_time", odm_getProperty, NULL, GJS_OM_PROP_MAIN_ADDON_MEDIATIME),
    1733             :         JS_CGETSET_MAGIC_DEF("dependent_groups", odm_getProperty, NULL, GJS_OM_PROP_DEPENDENT_GROUPS),
    1734             :         JS_CGETSET_MAGIC_DEF("vr_scene", odm_getProperty, NULL, GJS_OM_PROP_IS_VR_SCENE),
    1735             :         JS_CGETSET_MAGIC_DEF("disabled", odm_getProperty, NULL, GJS_OM_PROP_DISABLED),
    1736             :         JS_CGETSET_MAGIC_DEF("buffering", odm_getProperty, NULL, GJS_OM_PROP_BUFFERING),
    1737             : 
    1738             :         JS_CFUNC_DEF("declare_addon", 0, gjs_odm_declare_addon),
    1739             :         JS_CFUNC_DEF("enable_addon", 0, gjs_odm_enable_addon),
    1740             :         JS_CFUNC_DEF("addon_layout", 0, gjs_odm_addon_layout),
    1741             :         JS_CFUNC_DEF("get_resource", 0, gjs_odm_get_resource),
    1742             :         JS_CFUNC_DEF("get_quality", 0, gjs_odm_get_quality),
    1743             :         JS_CFUNC_DEF("select_service", 0, gjs_odm_select_service),
    1744             :         JS_CFUNC_DEF("select_quality", 0, gjs_odm_select_quality),
    1745             :         JS_CFUNC_DEF("disable_main_addon", 0, gjs_odm_disable_main_addon),
    1746             :         JS_CFUNC_DEF("select", 0, gjs_odm_select),
    1747             :         JS_CFUNC_DEF("get_srd", 0, gjs_odm_get_srd),
    1748             :         JS_CFUNC_DEF("in_parent_chain", 0, gjs_odm_in_parent_chain),
    1749             : };
    1750             : 
    1751             : #include "../filter_core/filter_session.h"
    1752             : 
    1753           4 : static void scenejs_finalize(JSRuntime *rt, JSValue obj)
    1754             : {
    1755           4 :         GF_SCENEJSExt *sjs = JS_GetOpaque(obj, scene_class_id);
    1756           4 :         if (!sjs) return;
    1757             : 
    1758           4 :         JS_SetOpaque(obj, NULL);
    1759             : 
    1760           8 :         while (gf_list_count(sjs->storages)) {
    1761           0 :                 GF_Config *cfg = (GF_Config *) gf_list_pop_back(sjs->storages);
    1762           0 :                 gf_cfg_discard_changes(cfg);
    1763           0 :                 gf_cfg_del(cfg);
    1764             :         }
    1765           4 :         gf_list_del(sjs->storages);
    1766             : 
    1767           8 :         while (gf_list_count(sjs->event_queue)) {
    1768           0 :                 GF_Event *evt = (GF_Event *) gf_list_pop_back(sjs->event_queue);
    1769           0 :                 gf_free(evt);
    1770             :         }
    1771           4 :         gf_list_del(sjs->event_queue);
    1772           4 :         gf_mx_del(sjs->event_mx);
    1773             : 
    1774           4 :         if (sjs->compositor && sjs->compositor->filter) {
    1775           4 :                 gf_fs_unload_script(sjs->compositor->filter->session, NULL);
    1776             :         }
    1777             :         /*if we destroy the script context holding the gpac event filter (only one for the time being), remove the filter*/
    1778             :         JS_FreeValueRT(rt, sjs->evt_fun);
    1779           4 :         if (sjs->evt_filter.udta) {
    1780           3 :                 if (sjs->compositor)
    1781           3 :                         gf_filter_remove_event_listener(sjs->compositor->filter, &sjs->evt_filter);
    1782           3 :                 sjs->evt_filter.udta = NULL;
    1783             :         }
    1784             : 
    1785           4 :         gf_free(sjs);
    1786             : }
    1787             : 
    1788           4 : static int js_scene_init(JSContext *c, JSModuleDef *m)
    1789             : {
    1790             :         GF_JSAPIParam par;
    1791             :         GF_SCENEJSExt *sjs;
    1792           4 :         GF_SAFEALLOC(sjs, GF_SCENEJSExt);
    1793             :         GF_SceneGraph *scene;
    1794           4 :         if (!sjs) {
    1795             :                 return -1;
    1796             :         }
    1797           4 :         sjs->storages = gf_list_new();
    1798           4 :         sjs->event_queue = gf_list_new();
    1799           4 :         sjs->event_mx = gf_mx_new("GPACJSEvt");
    1800           4 :         sjs->evt_fun = JS_UNDEFINED;
    1801           4 :         sjs->scene_obj = JS_UNDEFINED;
    1802           4 :         sjs->evt_obj = JS_UNDEFINED;
    1803             : 
    1804           4 :         scene = JS_GetContextOpaque(c);
    1805           4 :         if (!scene) return -1;
    1806           4 :         if (scene->__reserved_null) {
    1807           4 :                 GF_Node *n = JS_GetContextOpaque(c);
    1808           4 :                 scene = n->sgprivate->scenegraph;
    1809             :         }
    1810             : 
    1811           4 :         if (!scene_class_id) {
    1812           4 :                 JS_NewClassID(&scene_class_id);
    1813           4 :                 JS_NewClass(JS_GetRuntime(c), scene_class_id, &sceneClass);
    1814             : 
    1815           4 :                 JS_NewClassID(&odm_class_id);
    1816           4 :                 JS_NewClass(JS_GetRuntime(c), odm_class_id, &odmClass);
    1817             :         }
    1818           4 :         JSValue proto = JS_NewObjectClass(c, odm_class_id);
    1819           4 :         JS_SetPropertyFunctionList(c, proto, odm_funcs, countof(odm_funcs));
    1820           4 :         JS_SetClassProto(c, odm_class_id, proto);
    1821             : 
    1822           4 :         JS_NewClassID(&gpacevt_class_id);
    1823           4 :         JS_NewClass(JS_GetRuntime(c), gpacevt_class_id, &gpacEvtClass);
    1824             : 
    1825           4 :         JS_NewClassID(&any_class_id);
    1826           4 :         JS_NewClass(JS_GetRuntime(c), any_class_id, &anyClass);
    1827             : 
    1828           4 :         JSValue global = JS_GetGlobalObject(c);
    1829             : 
    1830           4 :         sjs->scene_obj = JS_NewObjectClass(c, scene_class_id);
    1831           4 :         JS_SetPropertyFunctionList(c, sjs->scene_obj, scenejs_funcs, countof(scenejs_funcs));
    1832           4 :         JS_SetOpaque(sjs->scene_obj, sjs);
    1833             : //      JS_SetPropertyStr(c, global, "gpac", sjs->scene_obj);
    1834             : 
    1835           4 :         if (scene->script_action) {
    1836           4 :                 if (scene->script_action(scene->script_action_cbck, GF_JSAPI_OP_GET_COMPOSITOR, scene->RootNode, &par)) {
    1837           4 :                         sjs->compositor = par.compositor;
    1838             :                 }
    1839             :         }
    1840           4 :         if (sjs->compositor && sjs->compositor->filter) {
    1841             :                 GF_Err gf_fs_load_js_api(JSContext *c, GF_FilterSession *fs);
    1842           4 :                 GF_FilterSession *fs = sjs->compositor->filter->session;
    1843             : 
    1844             :                 //don't check error code, this may fail if global JS has been set but the script may still run
    1845           4 :                 if (gf_fs_load_js_api(c, fs) == GF_OK) {
    1846           4 :                         scene->attached_session = fs;
    1847             :                 }
    1848             :         }
    1849             : 
    1850           4 :         sjs->evt_obj = JS_NewObjectClass(c, gpacevt_class_id);
    1851           4 :         JS_SetPropertyFunctionList(c, sjs->evt_obj, scenejs_evt_funcs, countof(scenejs_evt_funcs));
    1852           4 :         JS_SetOpaque(sjs->evt_obj, NULL);
    1853           4 :         JS_SetPropertyStr(c, global, "gpacevt", sjs->evt_obj);
    1854             : 
    1855             : #define DECLARE_CONST(_val) \
    1856             :         JS_SetPropertyStr(c, global, #_val, JS_NewInt32(c, _val));
    1857             : 
    1858           4 :         DECLARE_CONST(GF_EVENT_CLICK);
    1859           4 :         DECLARE_CONST(GF_EVENT_MOUSEUP);
    1860           4 :         DECLARE_CONST(GF_EVENT_MOUSEDOWN);
    1861           4 :         DECLARE_CONST(GF_EVENT_MOUSEMOVE);
    1862           4 :         DECLARE_CONST(GF_EVENT_MOUSEWHEEL);
    1863           4 :         DECLARE_CONST(GF_EVENT_DBLCLICK);
    1864           4 :         DECLARE_CONST(GF_EVENT_KEYUP);
    1865           4 :         DECLARE_CONST(GF_EVENT_KEYDOWN);
    1866           4 :         DECLARE_CONST(GF_EVENT_TEXTINPUT);
    1867           4 :         DECLARE_CONST(GF_EVENT_CONNECT);
    1868           4 :         DECLARE_CONST(GF_EVENT_NAVIGATE_INFO);
    1869           4 :         DECLARE_CONST(GF_EVENT_NAVIGATE);
    1870           4 :         DECLARE_CONST(GF_EVENT_DROPFILE);
    1871           4 :         DECLARE_CONST(GF_EVENT_ADDON_DETECTED);
    1872           4 :         DECLARE_CONST(GF_EVENT_QUALITY_SWITCHED);
    1873           4 :         DECLARE_CONST(GF_EVENT_TIMESHIFT_DEPTH);
    1874           4 :         DECLARE_CONST(GF_EVENT_TIMESHIFT_UPDATE);
    1875           4 :         DECLARE_CONST(GF_EVENT_TIMESHIFT_OVERFLOW);
    1876           4 :         DECLARE_CONST(GF_EVENT_TIMESHIFT_UNDERRUN);
    1877           4 :         DECLARE_CONST(GF_EVENT_QUIT);
    1878           4 :         DECLARE_CONST(GF_EVENT_MAIN_ADDON_STATE);
    1879           4 :         DECLARE_CONST(GF_EVENT_SCENE_SIZE);
    1880             : 
    1881           4 :         DECLARE_CONST(GF_NAVIGATE_NONE);
    1882           4 :         DECLARE_CONST(GF_NAVIGATE_WALK);
    1883           4 :         DECLARE_CONST(GF_NAVIGATE_FLY);
    1884           4 :         DECLARE_CONST(GF_NAVIGATE_PAN);
    1885           4 :         DECLARE_CONST(GF_NAVIGATE_GAME);
    1886           4 :         DECLARE_CONST(GF_NAVIGATE_SLIDE);
    1887           4 :         DECLARE_CONST(GF_NAVIGATE_EXAMINE);
    1888           4 :         DECLARE_CONST(GF_NAVIGATE_ORBIT);
    1889           4 :         DECLARE_CONST(GF_NAVIGATE_VR);
    1890             : 
    1891           4 :         DECLARE_CONST(GF_NAVIGATE_TYPE_NONE);
    1892           4 :         DECLARE_CONST(GF_NAVIGATE_TYPE_2D);
    1893           4 :         DECLARE_CONST(GF_NAVIGATE_TYPE_3D);
    1894             : 
    1895             :         JS_FreeValue(c, global);
    1896             : 
    1897           4 :     JS_SetModuleExport(c, m, "scene", sjs->scene_obj);
    1898           4 :         return 0;
    1899             : }
    1900             : 
    1901             : 
    1902         404 : void qjs_module_init_scenejs(JSContext *ctx)
    1903             : {
    1904             :         JSModuleDef *m;
    1905         404 :         m = JS_NewCModule(ctx, "scenejs", js_scene_init);
    1906         404 :         if (!m) return;
    1907             : 
    1908         404 :         JS_AddModuleExport(ctx, m, "scene");
    1909         404 :         return;
    1910             : }
    1911             : 
    1912             : 
    1913             : #endif
    1914             : 

Generated by: LCOV version 1.13