| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2007-2025 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / JavaScript Compositor extensions | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include <gpac/setup.h> | |
| #if defined(GPAC_HAS_QJS) && !defined(GPAC_DISABLE_COMPOSITOR) | |
| /*base SVG type*/ | |
| #include <gpac/nodes_svg.h> | |
| #include <gpac/nodes_mpeg4.h> | |
| #include <gpac/nodes_x3d.h> | |
| /*dom events*/ | |
| #include <gpac/events.h> | |
| #include <gpac/download.h> | |
| #include <gpac/network.h> | |
| #include <gpac/xml.h> | |
| #include <gpac/internal/scenegraph_dev.h> | |
| #include "../scenegraph/qjs_common.h" | |
| #include <gpac/internal/compositor_dev.h> | |
| typedef struct | |
| { | |
| GF_Compositor *compositor; | |
| JSValue evt_fun; | |
| GF_FSEventListener evt_filter; | |
| GF_Event *evt; | |
| JSContext *c; | |
| JSValue evt_filter_obj, evt_obj, scene_obj; | |
| GF_SystemRTInfo rti; | |
| //list of config files for storage | |
| GF_List *storages; | |
| GF_List *event_queue; | |
| GF_Mutex *event_mx; | |
| Bool owns_fs_api; | |
| } GF_SCENEJSExt; | |
| enum { | |
| GJS_OM_PROP_ID=1, | |
| GJS_OM_PROP_NB_RES, | |
| GJS_OM_PROP_URL, | |
| GJS_OM_PROP_DUR, | |
| GJS_OM_PROP_CLOCK, | |
| GJS_OM_PROP_DRIFT, | |
| GJS_OM_PROP_STATUS, | |
| GJS_OM_PROP_BUFFER, | |
| GJS_OM_PROP_DB_COUNT, | |
| GJS_OM_PROP_CB_COUNT, | |
| GJS_OM_PROP_CB_CAP, | |
| GJS_OM_PROP_TYPE, | |
| GJS_OM_PROP_SAMPLERATE, | |
| GJS_OM_PROP_CHANNELS, | |
| GJS_OM_PROP_LANG, | |
| GJS_OM_PROP_WIDTH, | |
| GJS_OM_PROP_HEIGHT, | |
| GJS_OM_PROP_PIXELFORMAT, | |
| GJS_OM_PROP_PAR, | |
| GJS_OM_PROP_DEC_FRAMES, | |
| GJS_OM_PROP_DROP_FRAMES, | |
| GJS_OM_PROP_DEC_TIME_MAX, | |
| GJS_OM_PROP_DEC_TIME_TOTAL, | |
| GJS_OM_PROP_AVG_RATE, | |
| GJS_OM_PROP_MAX_RATE, | |
| GJS_OM_PROP_SERVICE_HANDLER, | |
| GJS_OM_PROP_CODEC, | |
| GJS_OM_PROP_NB_VIEWS, | |
| GJS_OM_PROP_NB_QUALITIES, | |
| GJS_OM_PROP_MAX_BUFFER, | |
| GJS_OM_PROP_MIN_BUFFER, | |
| GJS_OM_PROP_FRAME_DUR, | |
| GJS_OM_PROP_NB_IRAP, | |
| GJS_OM_PROP_IRAP_DEC_TIME, | |
| GJS_OM_PROP_IRAP_MAX_TIME, | |
| GJS_OM_PROP_SERVICE_ID, | |
| GJS_OM_PROP_SELECTED_SERVICE, | |
| GJS_OM_PROP_BANDWIDTH_DOWN, | |
| GJS_OM_PROP_NB_HTTP, | |
| GJS_OM_PROP_TIMESHIFT_DEPTH, | |
| GJS_OM_PROP_TIMESHIFT_TIME, | |
| GJS_OM_PROP_IS_ADDON, | |
| GJS_OM_PROP_MAIN_ADDON_ON, | |
| GJS_OM_PROP_IS_OVER, | |
| GJS_OM_PROP_IS_VR_SCENE, | |
| GJS_OM_PROP_DYNAMIC_SCENE, | |
| GJS_OM_PROP_SERVICE_NAME, | |
| GJS_OM_PROP_NTP_DIFF, | |
| GJS_OM_PROP_MAIN_ADDON_URL, | |
| GJS_OM_PROP_REVERSE_PLAYBACK, | |
| GJS_OM_PROP_SCALABLE_ENHANCEMENT, | |
| GJS_OM_PROP_MAIN_ADDON_MEDIATIME, | |
| GJS_OM_PROP_DEPENDENT_GROUPS, | |
| GJS_OM_PROP_DISABLED, | |
| GJS_OM_PROP_NTP_SENDER_DIFF, | |
| GJS_OM_PROP_BUFFERING, | |
| GJS_OM_PROP_FORCED_SUB, | |
| }; | |
| enum { | |
| GJS_SCENE_PROP_FULLSCREEN=1, | |
| GJS_SCENE_PROP_CURRENT_PATH, | |
| GJS_SCENE_PROP_VOLUME, | |
| GJS_SCENE_PROP_NAVIGATION, | |
| GJS_SCENE_PROP_NAVIGATION_TYPE, | |
| GJS_SCENE_PROP_HARDWARE_YUV, | |
| GJS_SCENE_PROP_HARDWARE_RGB, | |
| GJS_SCENE_PROP_HARDWARE_RGBA, | |
| GJS_SCENE_PROP_HARDWARE_STRETCH, | |
| GJS_SCENE_PROP_SCREEN_WIDTH, | |
| GJS_SCENE_PROP_SCREEN_HEIGHT, | |
| GJS_SCENE_PROP_FPS, | |
| GJS_SCENE_PROP_CAPTION, | |
| GJS_SCENE_PROP_FOCUS_HIGHLIGHT, | |
| GJS_SCENE_PROP_DPI_X, | |
| GJS_SCENE_PROP_DPI_Y, | |
| GJS_SCENE_PROP_SENSORS_ACTIVE, | |
| GJS_SCENE_PROP_SIM_FPS, | |
| GJS_SCENE_PROP_HAS_OPENGL, | |
| GJS_SCENE_PROP_ZOOM, | |
| GJS_SCENE_PROP_TEXT_SEL, | |
| GJS_SCENE_PROP_DISP_ORIENTATION, | |
| }; | |
| enum { | |
| GJS_EVT_PROP_KEYCODE = 1, | |
| GJS_EVT_PROP_MOUSE_X, | |
| GJS_EVT_PROP_MOUSE_Y, | |
| GJS_EVT_PROP_PICKED, | |
| GJS_EVT_PROP_WHEEL, | |
| GJS_EVT_PROP_BUTTON, | |
| GJS_EVT_PROP_TYPE, | |
| GJS_EVT_PROP_NAME, | |
| GJS_EVT_PROP_HWKEY, | |
| GJS_EVT_PROP_TARGET_URL, | |
| GJS_EVT_PROP_FILES, | |
| }; | |
| static JSClassID scene_class_id = 0; | |
| static JSClassID odm_class_id = 0; | |
| static JSClassID gpacevt_class_id = 0; | |
| static JSClassID any_class_id = 0; | |
| static void scenejs_finalize(JSRuntime *rt, JSValue obj); | |
| static void scenejs_gc_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) | |
| { | |
| GF_SCENEJSExt *ext = JS_GetOpaque(val, scene_class_id); | |
| if (ext) { | |
| JS_MarkValue(rt, ext->evt_fun, mark_func); | |
| } | |
| } | |
| JSClassDef sceneClass = { | |
| "JSSCENE", | |
| .finalizer = scenejs_finalize, | |
| .gc_mark = scenejs_gc_mark | |
| }; | |
| JSClassDef gpacEvtClass = { | |
| "GPACEVT" | |
| }; | |
| JSClassDef odmClass = { | |
| "MediaObject" | |
| }; | |
| JSClassDef anyClass = { | |
| "GPACOBJECT" | |
| }; | |
| static GF_Compositor *scenejs_get_compositor(JSContext *c, JSValue obj) | |
| { | |
| GF_SCENEJSExt *ext = JS_GetOpaque(obj, scene_class_id); | |
| return ext ? ext->compositor : NULL; | |
| } | |
| static JSValue scenejs_getProperty(JSContext *ctx, JSValueConst this_val, int prop_id) | |
| { | |
| Bool bval; | |
| JSValue ret; | |
| char *str; | |
| GF_SCENEJSExt *ext = JS_GetOpaque(this_val, scene_class_id); | |
| GF_Compositor *compositor = ext ? ext->compositor : NULL; | |
| if (!ext || !compositor) return GF_JS_EXCEPTION(ctx); | |
| switch (prop_id) { | |
| case GJS_SCENE_PROP_FULLSCREEN: | |
| return JS_NewBool(ctx, compositor->fullscreen); | |
| case GJS_SCENE_PROP_CURRENT_PATH: | |
| str = gf_url_concatenate(compositor->root_scene->root_od->scene_ns->url, ""); | |
| if (!str) str = gf_strdup(""); | |
| ret = JS_NewString(ctx, str); | |
| gf_free(str); | |
| return ret; | |
| case GJS_SCENE_PROP_VOLUME: | |
| return JS_NewInt32(ctx, gf_sc_get_option(compositor, GF_OPT_AUDIO_VOLUME)); | |
| case GJS_SCENE_PROP_NAVIGATION: | |
| return JS_NewInt32(ctx, gf_sc_get_option(compositor, GF_OPT_NAVIGATION)); | |
| case GJS_SCENE_PROP_NAVIGATION_TYPE: | |
| return JS_NewInt32(ctx, gf_sc_get_option(compositor, GF_OPT_NAVIGATION_TYPE) ); | |
| case GJS_SCENE_PROP_HARDWARE_YUV: | |
| return JS_NewBool(ctx, (compositor->video_out->hw_caps & GF_VIDEO_HW_HAS_YUV) ? 1 : 0 ); | |
| case GJS_SCENE_PROP_HARDWARE_RGB: | |
| return JS_NewBool(ctx, (compositor->video_out->hw_caps & GF_VIDEO_HW_HAS_RGB) ? 1 : 0 ); | |
| case GJS_SCENE_PROP_HARDWARE_RGBA: | |
| bval = (compositor->video_out->hw_caps & GF_VIDEO_HW_HAS_RGBA) ? 1 : 0; | |
| #ifndef GPAC_DISABLE_3D | |
| if (compositor->hybrid_opengl || compositor->is_opengl) bval = 1; | |
| #endif | |
| return JS_NewBool(ctx, bval); | |
| case GJS_SCENE_PROP_HARDWARE_STRETCH: | |
| return JS_NewBool(ctx, (compositor->video_out->hw_caps & GF_VIDEO_HW_HAS_STRETCH) ? 1 : 0 ); | |
| case GJS_SCENE_PROP_SCREEN_WIDTH: | |
| if (compositor->osize.x) | |
| return JS_NewInt32(ctx, compositor->osize.x); | |
| return JS_NewInt32(ctx, compositor->video_out->max_screen_width); | |
| case GJS_SCENE_PROP_SCREEN_HEIGHT: | |
| if (compositor->osize.y) | |
| return JS_NewInt32(ctx, compositor->osize.y); | |
| return JS_NewInt32(ctx, compositor->video_out->max_screen_height); | |
| case GJS_SCENE_PROP_FPS: | |
| return JS_NewFloat64(ctx, gf_sc_get_fps(compositor, 0) ); | |
| case GJS_SCENE_PROP_SIM_FPS: | |
| return JS_NewFloat64(ctx, ((Double)compositor->fps.den)/compositor->fps.num); | |
| case GJS_SCENE_PROP_HAS_OPENGL: | |
| return JS_NewBool(ctx, (compositor->ogl != GF_SC_GLMODE_OFF) ? 1 : 0); | |
| case GJS_SCENE_PROP_DPI_X: | |
| return JS_NewInt32(ctx, compositor->video_out->dpi_x); | |
| case GJS_SCENE_PROP_DPI_Y: | |
| return JS_NewInt32(ctx, compositor->video_out->dpi_y); | |
| case GJS_SCENE_PROP_SENSORS_ACTIVE: | |
| return JS_NewBool(ctx, compositor->orientation_sensors_active); | |
| case GJS_SCENE_PROP_FOCUS_HIGHLIGHT: | |
| return JS_NewBool(ctx, !compositor->disable_focus_highlight); | |
| case GJS_SCENE_PROP_ZOOM: | |
| if (compositor->root_scene && compositor->root_scene->graph->script_action) { | |
| GF_JSAPIParam jspar; | |
| memset(&jspar, 0, sizeof(GF_JSAPIParam)); | |
| compositor->root_scene->graph->script_action(compositor->root_scene->graph->script_action_cbck, GF_JSAPI_OP_GET_SCALE, NULL, &jspar); | |
| #ifdef GPAC_ENABLE_COVERAGE | |
| if (gf_sys_is_cov_mode()) { | |
| compositor->root_scene->graph->script_action(compositor->root_scene->graph->script_action_cbck, GF_JSAPI_OP_GET_VIEWPORT, NULL, &jspar); | |
| } | |
| #endif | |
| return JS_NewFloat64(ctx, FIX2FLT(jspar.val) ); | |
| } else { | |
| return JS_NewFloat64(ctx, 1.0 ); | |
| } | |
| break; | |
| case GJS_SCENE_PROP_TEXT_SEL: | |
| str = (char *) gf_sc_get_selected_text(compositor); | |
| if (!str) str = ""; | |
| return JS_NewString(ctx, str); | |
| case GJS_SCENE_PROP_DISP_ORIENTATION: | |
| return JS_NewInt32(ctx, compositor->disp_ori); | |
| } | |
| return JS_UNDEFINED; | |
| } | |
| static JSValue scenejs_setProperty(JSContext *ctx, JSValueConst this_val, JSValueConst value, int magic) | |
| { | |
| #ifndef GPAC_CONFIG_IOS | |
| Bool bval; | |
| #endif | |
| s32 ival; | |
| Double dval; | |
| const char *prop_val; | |
| GF_SCENEJSExt *ext = JS_GetOpaque(this_val, scene_class_id); | |
| GF_Compositor *compositor = ext ? ext->compositor : NULL; | |
| if (!ext || !compositor) return GF_JS_EXCEPTION(ctx); | |
| switch (magic) { | |
| case GJS_SCENE_PROP_CAPTION: | |
| { | |
| GF_Event evt; | |
| if (!JS_IsString(value)) return GF_JS_EXCEPTION(ctx); | |
| prop_val = JS_ToCString(ctx, value); | |
| evt.type = GF_EVENT_SET_CAPTION; | |
| if (prop_val && !strnicmp(prop_val, "gpac://", 7)) { | |
| evt.caption.caption = prop_val + 7; | |
| } else { | |
| evt.caption.caption = prop_val; | |
| } | |
| gf_sc_user_event(compositor, &evt); | |
| JS_FreeCString(ctx, prop_val); | |
| } | |
| break; | |
| case GJS_SCENE_PROP_FULLSCREEN: | |
| /*no fullscreen for iOS (always on)*/ | |
| #ifndef GPAC_CONFIG_IOS | |
| bval = JS_ToBool(ctx, value); | |
| if (compositor->fullscreen != bval) { | |
| gf_sc_set_option(compositor, GF_OPT_FULLSCREEN, bval); | |
| } | |
| #endif | |
| break; | |
| case GJS_SCENE_PROP_VOLUME: | |
| if (!JS_ToFloat64(ctx, &dval, value)) { | |
| gf_sc_set_option(compositor, GF_OPT_AUDIO_VOLUME, (u32) dval); | |
| } else if (!JS_ToInt32(ctx, &ival, value)) { | |
| gf_sc_set_option(compositor, GF_OPT_AUDIO_VOLUME, (u32) ival); | |
| } | |
| break; | |
| case GJS_SCENE_PROP_NAVIGATION: | |
| if (JS_ToInt32(ctx, &ival, value)) return GF_JS_EXCEPTION(ctx); | |
| gf_sc_set_option(compositor, GF_OPT_NAVIGATION, (u32) ival); | |
| break; | |
| case GJS_SCENE_PROP_NAVIGATION_TYPE: | |
| gf_sc_set_option(compositor, GF_OPT_NAVIGATION_TYPE, 0); | |
| break; | |
| case GJS_SCENE_PROP_FOCUS_HIGHLIGHT: | |
| compositor->disable_focus_highlight = !JS_ToBool(ctx, value); | |
| break; | |
| case GJS_SCENE_PROP_SENSORS_ACTIVE: | |
| { | |
| GF_Event evt; | |
| compositor->orientation_sensors_active = JS_ToBool(ctx, value); | |
| //send activation for sensors | |
| memset(&evt, 0, sizeof(GF_Event)); | |
| evt.type = GF_EVENT_SENSOR_REQUEST; | |
| evt.activate_sensor.activate = compositor->orientation_sensors_active; | |
| evt.activate_sensor.sensor_type = GF_EVENT_SENSOR_ORIENTATION; | |
| if (gf_sc_send_event(compositor, &evt) == GF_FALSE) { | |
| compositor->orientation_sensors_active = GF_FALSE; | |
| } | |
| } | |
| break; | |
| case GJS_SCENE_PROP_DISP_ORIENTATION: | |
| if (JS_ToInt32(ctx, &ival, value)) return GF_JS_EXCEPTION(ctx); | |
| if (compositor->video_out && compositor->video_out->ProcessEvent) { | |
| GF_Event evt; | |
| memset(&evt, 0, sizeof(GF_Event)); | |
| evt.size.type = GF_EVENT_SET_ORIENTATION; | |
| evt.size.orientation = ival; | |
| compositor->video_out->ProcessEvent(compositor->video_out, &evt); | |
| } | |
| break; | |
| } | |
| return JS_UNDEFINED; | |
| } | |
| static JSValue scenejs_get_option(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| const char *opt = NULL; | |
| const char *sec_name, *key_name; | |
| char arg_val[GF_PROP_DUMP_ARG_SIZE]; | |
| s32 idx = -1; | |
| GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val); | |
| if (!compositor) return GF_JS_EXCEPTION(ctx); | |
| if (argc < 2) return GF_JS_EXCEPTION(ctx); | |
| if (!JS_IsString(argv[0])) return GF_JS_EXCEPTION(ctx); | |
| if (!JS_IsString(argv[1]) && !JS_IsNumber(argv[1])) return GF_JS_EXCEPTION(ctx); | |
| sec_name = JS_ToCString(ctx, argv[0]); | |
| key_name = NULL; | |
| if (JS_IsNumber(argv[1])) { | |
| JS_ToInt32(ctx, &idx, argv[1]); | |
| } else if (JS_IsString(argv[1]) ) { | |
| key_name = JS_ToCString(ctx, argv[1]); | |
| } | |
| if (sec_name && !stricmp(sec_name, "core") && key_name && !strcmp(key_name, "version")) { | |
| opt = gf_gpac_version(); | |
| } else if (sec_name && key_name) { | |
| if (!strcmp(sec_name, "Compositor")) { | |
| opt = gf_filter_get_arg_str(compositor->filter, key_name, arg_val); | |
| } else { | |
| opt = gf_opts_get_key(sec_name, key_name); | |
| } | |
| } else if (sec_name && (idx>=0)) { | |
| opt = gf_opts_get_key_name(sec_name, idx); | |
| } else { | |
| opt = NULL; | |
| } | |
| JS_FreeCString(ctx, key_name); | |
| JS_FreeCString(ctx, sec_name); | |
| if (opt) { | |
| return JS_NewString(ctx, opt); | |
| } | |
| return JS_NULL; | |
| } | |
| static JSValue scenejs_set_option(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| const char *sec_name, *key_name, *key_val; | |
| GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val); | |
| if (!compositor) return GF_JS_EXCEPTION(ctx); | |
| if (argc < 3) return GF_JS_EXCEPTION(ctx); | |
| if (!JS_IsString(argv[0])) return GF_JS_EXCEPTION(ctx); | |
| if (!JS_IsString(argv[1])) return GF_JS_EXCEPTION(ctx); | |
| sec_name = JS_ToCString(ctx, argv[0]); | |
| key_name = JS_ToCString(ctx, argv[1]); | |
| key_val = NULL; | |
| if (JS_IsString(argv[2])) | |
| key_val = JS_ToCString(ctx, argv[2]); | |
| if (!strcmp(sec_name, "Compositor")) { | |
| gf_filter_send_update(compositor->filter, NULL, key_name, key_val, 0); | |
| } else { | |
| gf_opts_set_key(sec_name, key_name, key_val); | |
| } | |
| JS_FreeCString(ctx, sec_name); | |
| JS_FreeCString(ctx, key_name); | |
| if (key_val) { | |
| JS_FreeCString(ctx, key_val); | |
| } | |
| return JS_UNDEFINED; | |
| } | |
| static JSValue scenejs_set_back_color(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| u32 r, g, b, a, i; | |
| GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val); | |
| if (!compositor) return GF_JS_EXCEPTION(ctx); | |
| if (argc < 3) return GF_JS_EXCEPTION(ctx); | |
| r = g = b = 0; | |
| a = 255; | |
| for (i=0; i<(u32) argc; i++) { | |
| Double d; | |
| u32 v; | |
| if (! JS_ToFloat64(ctx, &d, argv[i])) { | |
| } else { | |
| return GF_JS_EXCEPTION(ctx); | |
| } | |
| d*=255; | |
| v = 0; | |
| if (d>0) { | |
| v = (u32) d; | |
| if (v>255) v = 255; | |
| } | |
| if (i==0) r = v; | |
| else if (i==1) g = v; | |
| else if (i==2) b = v; | |
| else if (i==3) a = v; | |
| } | |
| compositor->back_color = compositor->bc = GF_COL_ARGB(a, r, g, b); | |
| gf_sc_invalidate(compositor, NULL); | |
| return JS_UNDEFINED; | |
| } | |
| static JSValue scenejs_switch_quality(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val); | |
| if (!compositor) return GF_JS_EXCEPTION(ctx); | |
| if (argc < 1) return GF_JS_EXCEPTION(ctx); | |
| if (!JS_IsBool(argv[0])) | |
| return GF_JS_EXCEPTION(ctx); | |
| Bool up = JS_ToBool(ctx, argv[0]) ? GF_TRUE : GF_FALSE; | |
| gf_scene_switch_quality(compositor->root_scene, up); | |
| return JS_UNDEFINED; | |
| } | |
| #if 0 //unused | |
| static JSValue scenejs_reload(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| GF_Event evt; | |
| GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val); | |
| if (!compositor) return GF_JS_EXCEPTION(ctx); | |
| memset(&evt, 0, sizeof(GF_Event)); | |
| evt.type = GF_EVENT_RELOAD; | |
| gf_filter_ui_event(compositor->filter, &evt); | |
| return JS_UNDEFINED; | |
| } | |
| #endif | |
| static JSValue scenejs_navigation_supported(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| u32 type; | |
| GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val); | |
| if (!compositor) return GF_JS_EXCEPTION(ctx); | |
| if (argc < 1) return GF_JS_EXCEPTION(ctx); | |
| if (! JS_IsInteger(argv[0]) ) { | |
| return JS_NewBool(ctx, 0); | |
| } | |
| if (JS_ToInt32(ctx, &type, argv[0])) | |
| return GF_JS_EXCEPTION(ctx); | |
| return JS_NewBool(ctx, gf_sc_navigation_supported(compositor, type) ? 1 : 0 ); | |
| } | |
| static JSValue scenejs_set_size(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| Bool override_size_info = 0; | |
| u32 w, h; | |
| GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val); | |
| if (!compositor) return GF_JS_EXCEPTION(ctx); | |
| if (argc<2) return GF_JS_EXCEPTION(ctx); | |
| w = h = 0; | |
| if (JS_ToInt32(ctx, &w, argv[0])) | |
| return GF_JS_EXCEPTION(ctx); | |
| if (JS_ToInt32(ctx, &h, argv[1])) | |
| return GF_JS_EXCEPTION(ctx); | |
| if (argc > 2) | |
| override_size_info = JS_ToBool(ctx, argv[2]); | |
| if (w && h) { | |
| GF_Event evt; | |
| if (override_size_info) { | |
| compositor->scene_width = w; | |
| compositor->scene_height = h; | |
| compositor->has_size_info = 1; | |
| compositor->recompute_ar = 1; | |
| return JS_UNDEFINED; | |
| } | |
| if (compositor->os_wnd) { | |
| evt.type = GF_EVENT_SCENE_SIZE; | |
| evt.size.width = w; | |
| evt.size.height = h; | |
| gf_sc_send_event(compositor, &evt); | |
| } else { | |
| gf_sc_set_size(compositor, w, h); | |
| } | |
| } else if (override_size_info) { | |
| compositor->has_size_info = 0; | |
| compositor->recompute_ar = 1; | |
| } | |
| return JS_UNDEFINED; | |
| } | |
| static JSValue scenejs_exit(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| GF_Event evt; | |
| GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val); | |
| memset(&evt, 0, sizeof(GF_Event)); | |
| evt.type = GF_EVENT_QUIT; | |
| if (argc) | |
| JS_ToInt32(ctx, (s32 *) &evt.message.error, argv[0]); | |
| gf_sc_send_event(compositor, &evt); | |
| return JS_UNDEFINED; | |
| } | |
| static JSValue scenejs_set_3d(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| Bool type_3d; | |
| GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val); | |
| if (!argc) return GF_JS_EXCEPTION(ctx); | |
| type_3d = JS_ToBool(ctx, argv[0]); | |
| if (compositor->inherit_type_3d != type_3d) { | |
| compositor->inherit_type_3d = type_3d; | |
| compositor->root_visual_setup = 0; | |
| gf_sc_reset_graphics(compositor); | |
| } | |
| return JS_UNDEFINED; | |
| } | |
| static JSValue scenejs_move_window(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| GF_Event evt; | |
| GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val); | |
| if (argc < 2) return GF_JS_EXCEPTION(ctx); | |
| evt.type = GF_EVENT_MOVE; | |
| evt.move.relative = 1; | |
| if (JS_ToInt32(ctx, &evt.move.x, argv[0])) | |
| return GF_JS_EXCEPTION(ctx); | |
| if (JS_ToInt32(ctx, &evt.move.y, argv[1])) | |
| return GF_JS_EXCEPTION(ctx); | |
| if (argc ==3) { | |
| evt.move.relative = JS_ToBool(ctx, argv[2]); | |
| } | |
| compositor->video_out->ProcessEvent(compositor->video_out, &evt); | |
| return JS_UNDEFINED; | |
| } | |
| #if 0 //unused | |
| static JSValue scenejs_get_scene_time(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| GF_SceneGraph *sg = NULL; | |
| GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val); | |
| if (!compositor) return GF_JS_EXCEPTION(ctx); | |
| if (!argc || !JS_IsObject(argv[0]) ) { | |
| sg = compositor->root_scene->graph; | |
| } else { | |
| GF_Node *n = gf_sg_js_get_node(c, JSVAL_TO_OBJECT(argv[0])); | |
| sg = n ? n->sgprivate->scenegraph : compositor->root_scene->graph; | |
| } | |
| return JS_NewFloat64(ctx, sg->GetSceneTime(sg->userpriv) ); | |
| } | |
| #endif | |
| static JSValue scenejs_trigger_gc(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| GF_SceneGraph *sg; | |
| GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val); | |
| sg = compositor->root_scene->graph; | |
| sg->trigger_gc = GF_TRUE; | |
| return JS_UNDEFINED; | |
| } | |
| static GF_FilterPid *gjs_enum_pids(void *udta, u32 *idx) | |
| { | |
| GF_Scene *scene = (GF_Scene *)udta; | |
| GF_ObjectManager *odm = gf_list_get(scene->resources, *idx); | |
| if (odm) return odm->pid; | |
| return NULL; | |
| } | |
| static JSValue odm_getProperty(JSContext *ctx, JSValueConst this_val, int magic) | |
| { | |
| GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id); | |
| GF_MediaInfo odi; | |
| char *str; | |
| GF_Scene *scene; | |
| Double dval; | |
| u32 i, count; | |
| switch (magic) { | |
| case GJS_OM_PROP_ID: | |
| return JS_NewInt32(ctx, odm->ID); | |
| case GJS_OM_PROP_NB_RES: | |
| return JS_NewInt32(ctx, odm->subscene ? gf_list_count(odm->subscene->resources) : 0); | |
| case GJS_OM_PROP_URL: | |
| if (odm->scene_ns) | |
| return JS_NewString(ctx, odm->scene_ns->url); | |
| return JS_NULL; | |
| case GJS_OM_PROP_DUR: | |
| return JS_NewFloat64(ctx, odm->duration / 1000.0); | |
| case GJS_OM_PROP_CLOCK: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewFloat64(ctx, odi.current_time); | |
| case GJS_OM_PROP_DRIFT: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, odi.clock_drift); | |
| case GJS_OM_PROP_STATUS: | |
| gf_odm_get_object_info(odm, &odi); | |
| if (odi.status==0) str = "Stopped"; | |
| else if (odi.status==1) str = "Playing"; | |
| else if (odi.status==2) str = "Paused"; | |
| else if (odi.status==3) str = "Not Setup"; | |
| else str = "Setup Failed"; | |
| return JS_NewString(ctx, str); | |
| case GJS_OM_PROP_BUFFER: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, odi.buffer); | |
| case GJS_OM_PROP_DB_COUNT: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, odi.db_unit_count); | |
| case GJS_OM_PROP_CB_COUNT: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, odi.cb_unit_count); | |
| case GJS_OM_PROP_CB_CAP: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, odi.cb_max_count); | |
| case GJS_OM_PROP_TYPE: | |
| if (odm->type==GF_STREAM_SCENE) str = "Scene"; | |
| else if (odm->type==GF_STREAM_OD) str = "OD"; | |
| else if (odm->type==GF_STREAM_VISUAL) { | |
| //handle all sparse video as text (we could check the original stream type) | |
| if (odm->flags & GF_ODM_IS_SPARSE) str = "Text"; | |
| else str = "Video"; | |
| } | |
| else if (odm->type==GF_STREAM_AUDIO) str = "Audio"; | |
| else if (odm->type==GF_STREAM_TEXT) str = "Text"; | |
| else if (odm->subscene) str = "Subscene"; | |
| else str = (char*) gf_stream_type_name(odm->type); | |
| return JS_NewString(ctx, str); | |
| case GJS_OM_PROP_SAMPLERATE: | |
| return JS_NewInt32(ctx, odm->mo ? odm->mo->sample_rate : 0); | |
| case GJS_OM_PROP_CHANNELS: | |
| return JS_NewInt32(ctx, odm->mo ? odm->mo->num_channels : 0); | |
| case GJS_OM_PROP_LANG: | |
| gf_odm_get_object_info(odm, &odi); | |
| if (odi.lang_code) return JS_NewString(ctx, odi.lang_code); | |
| else if (odi.lang) return JS_NewString(ctx, gf_4cc_to_str(odi.lang) ); | |
| return JS_NewString(ctx, "und"); | |
| case GJS_OM_PROP_WIDTH: | |
| return JS_NewInt32(ctx, odm->mo ? odm->mo->width : 0); | |
| case GJS_OM_PROP_HEIGHT: | |
| return JS_NewInt32(ctx, odm->mo ? odm->mo->height : 0); | |
| case GJS_OM_PROP_PIXELFORMAT: | |
| return JS_NewString(ctx, odm->mo ? gf_4cc_to_str(odm->mo->pixelformat) : "none"); | |
| case GJS_OM_PROP_PAR: | |
| if (odm->mo && odm->mo->pixel_ar) { | |
| char szPar[50]; | |
| sprintf(szPar, "%d:%d", (odm->mo->pixel_ar>>16)&0xFF, (odm->mo->pixel_ar)&0xFF ); | |
| return JS_NewString(ctx, szPar); | |
| } else if (odm->mo && odm->mo->width) { | |
| return JS_NewString(ctx, "1:1"); | |
| } | |
| return JS_NULL; | |
| case GJS_OM_PROP_DEC_FRAMES: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, odi.nb_dec_frames); | |
| case GJS_OM_PROP_DROP_FRAMES: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, odi.nb_dropped); | |
| case GJS_OM_PROP_DEC_TIME_MAX: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, odi.max_dec_time); | |
| case GJS_OM_PROP_DEC_TIME_TOTAL: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt64(ctx, odi.total_dec_time); | |
| case GJS_OM_PROP_AVG_RATE: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, odi.avg_bitrate); | |
| case GJS_OM_PROP_MAX_RATE: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, odi.max_bitrate); | |
| case GJS_OM_PROP_SERVICE_HANDLER: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewString(ctx, odi.service_handler ? odi.service_handler : "unloaded"); | |
| case GJS_OM_PROP_CODEC: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewString(ctx, odi.codec_name ? odi.codec_name : "unloaded"); | |
| case GJS_OM_PROP_NB_VIEWS: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, (odi.nb_views>1) ? odi.nb_views : 0); | |
| case GJS_OM_PROP_NB_QUALITIES: | |
| //use HAS qualities | |
| if (odm->pid) { | |
| u32 nb_qualities = 0; | |
| GF_PropertyEntry *pe=NULL; | |
| const GF_PropertyValue *prop = gf_filter_pid_get_info_str(odm->pid, "has:qualities", &pe); | |
| if (prop) nb_qualities = prop->value.string_list.nb_items; | |
| gf_filter_release_property(pe); | |
| if (nb_qualities) | |
| return JS_NewInt32(ctx, nb_qualities); | |
| } | |
| //use input channels | |
| if (odm->extra_pids) { | |
| return JS_NewInt32(ctx, 1 + gf_list_count(odm->extra_pids)); | |
| } | |
| //use number of scalable addons | |
| if (odm->upper_layer_odm) { | |
| count = 0; | |
| while (odm) { | |
| odm = odm->upper_layer_odm; | |
| count++; | |
| } | |
| return JS_NewInt32(ctx, count); | |
| } | |
| return JS_NewInt32(ctx, 1); | |
| case GJS_OM_PROP_MAX_BUFFER: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, odi.max_buffer); | |
| case GJS_OM_PROP_MIN_BUFFER: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, odi.min_buffer); | |
| case GJS_OM_PROP_FRAME_DUR: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, odi.au_duration); | |
| case GJS_OM_PROP_NB_IRAP: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, odi.nb_iraps); | |
| case GJS_OM_PROP_IRAP_DEC_TIME: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt64(ctx, odi.irap_total_dec_time); | |
| case GJS_OM_PROP_IRAP_MAX_TIME: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, odi.irap_max_dec_time); | |
| case GJS_OM_PROP_SERVICE_ID: | |
| return JS_NewInt32(ctx, odm->ServiceID); | |
| case GJS_OM_PROP_SELECTED_SERVICE: | |
| return JS_NewInt32(ctx, (!odm->addon && odm->subscene) ? odm->subscene->selected_service_id : odm->parentscene->selected_service_id); | |
| break; | |
| case GJS_OM_PROP_BANDWIDTH_DOWN: | |
| if (odm->scene_ns->source_filter) { | |
| JSValue ret; | |
| GF_PropertyEntry *pe=NULL; | |
| const GF_PropertyValue *prop = gf_filter_get_info(odm->scene_ns->source_filter, GF_PROP_PID_DOWN_RATE, &pe); | |
| ret = JS_NewInt32(ctx, prop ? prop->value.uint/1000 : 0); | |
| gf_filter_release_property(pe); | |
| return ret; | |
| } | |
| return JS_NewInt32(ctx, 0); | |
| case GJS_OM_PROP_NB_HTTP: | |
| if (odm->scene_ns->source_filter) { | |
| 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 ) ); | |
| } | |
| return JS_NewInt32(ctx, 0); | |
| case GJS_OM_PROP_TIMESHIFT_DEPTH: | |
| if ((s32) odm->timeshift_depth > 0) { | |
| return JS_NewFloat64(ctx, ((Double) odm->timeshift_depth) / 1000.0 ); | |
| } | |
| return JS_NewFloat64(ctx, 0.0); | |
| case GJS_OM_PROP_TIMESHIFT_TIME: | |
| dval = 0.0; | |
| if (!odm->timeshift_depth) { | |
| return JS_NewInt32(ctx, 0); | |
| } | |
| scene = odm->subscene ? odm->subscene : odm->parentscene; | |
| //we may need to check the main addon for timeshifting | |
| if (scene->main_addon_selected) { | |
| count = gf_list_count(scene->resources); | |
| for (i=0; i < count; i++) { | |
| GF_ObjectManager *an_odm = gf_list_get(scene->resources, i); | |
| if (an_odm && an_odm->addon && (an_odm->addon->addon_type==GF_ADDON_TYPE_MAIN)) { | |
| odm = an_odm; | |
| } | |
| } | |
| } | |
| if (odm->timeshift_depth) { | |
| GF_FilterPid *pid = odm->pid; | |
| if (!pid && odm->subscene) { | |
| odm = gf_list_get(odm->subscene->resources, 0); | |
| pid = odm->pid; | |
| } | |
| if (pid) { | |
| GF_PropertyEntry *pe=NULL; | |
| const GF_PropertyValue *p = gf_filter_pid_get_info(pid, GF_PROP_PID_TIMESHIFT_TIME, &pe); | |
| if (p) dval = p->value.number; | |
| gf_filter_release_property(pe); | |
| } | |
| } else if (scene->main_addon_selected) { | |
| GF_Clock *ck = scene->root_od->ck; | |
| if (ck) { | |
| u64 now = gf_clock_time_absolute(ck) ; | |
| u64 live = scene->obj_clock_at_main_activation + gf_sys_clock() - scene->sys_clock_at_main_activation; | |
| dval = ((Double) live) / 1000.0; | |
| dval -= ((Double) now) / 1000.0; | |
| if (dval<0) { | |
| GF_Event evt; | |
| memset(&evt, 0, sizeof(evt)); | |
| evt.type = GF_EVENT_TIMESHIFT_UNDERRUN; | |
| gf_sc_send_event(scene->compositor, &evt); | |
| dval=0; | |
| } else if (dval && dval*1000 > scene->timeshift_depth) { | |
| GF_Event evt; | |
| memset(&evt, 0, sizeof(evt)); | |
| evt.type = GF_EVENT_TIMESHIFT_OVERFLOW; | |
| gf_sc_send_event(scene->compositor, &evt); | |
| dval=scene->timeshift_depth; | |
| dval/=1000; | |
| } | |
| } | |
| } | |
| return JS_NewFloat64(ctx, dval); | |
| case GJS_OM_PROP_IS_ADDON: | |
| return JS_NewBool(ctx, (odm->addon || (!odm->subscene && odm->parentscene->root_od->addon)) ); | |
| case GJS_OM_PROP_MAIN_ADDON_ON: | |
| scene = odm->subscene ? odm->subscene : odm->parentscene; | |
| return JS_NewBool(ctx, scene->main_addon_selected ); | |
| case GJS_OM_PROP_IS_OVER: | |
| scene = odm->subscene ? odm->subscene : odm->parentscene; | |
| return JS_NewBool(ctx, gf_sc_is_over(scene->compositor, scene->graph)); | |
| case GJS_OM_PROP_DYNAMIC_SCENE: | |
| return JS_NewBool(ctx, odm->subscene && odm->subscene->is_dynamic_scene); | |
| case GJS_OM_PROP_SERVICE_NAME: | |
| if (odm->pid) { | |
| GF_PropertyEntry *pe=NULL; | |
| JSValue ret = JS_NULL; | |
| const GF_PropertyValue *p = gf_filter_pid_get_info(odm->pid, GF_PROP_PID_SERVICE_NAME, &pe); | |
| if (p && p->value.string) | |
| ret = JS_NewString(ctx, p->value.string); | |
| gf_filter_release_property(pe); | |
| return ret; | |
| } | |
| return JS_NULL; | |
| case GJS_OM_PROP_NTP_DIFF: | |
| gf_odm_get_object_info(odm, &odi); | |
| return JS_NewInt32(ctx, odi.ntp_diff); | |
| case GJS_OM_PROP_NTP_SENDER_DIFF: | |
| if (odm->last_drawn_frame_ntp_sender && odm->last_drawn_frame_ntp_receive) { | |
| s32 diff = gf_net_ntp_diff_ms(odm->last_drawn_frame_ntp_receive, odm->last_drawn_frame_ntp_sender); | |
| return JS_NewInt32(ctx, diff); | |
| } | |
| return JS_NULL; | |
| case GJS_OM_PROP_MAIN_ADDON_URL: | |
| scene = odm->subscene ? odm->subscene : odm->parentscene; | |
| count = gf_list_count(scene->resources); | |
| for (i=0; i < count; i++) { | |
| GF_ObjectManager *an_odm = gf_list_get(scene->resources, i); | |
| if (an_odm && an_odm->addon && (an_odm->addon->addon_type==GF_ADDON_TYPE_MAIN)) { | |
| if (!strstr(an_odm->addon->url, "://")) { | |
| char szURL[GF_MAX_PATH]; | |
| sprintf(szURL, "gpac://%s", an_odm->addon->url); | |
| return JS_NewString(ctx, szURL); | |
| } else { | |
| return JS_NewString(ctx, an_odm->addon->url); | |
| } | |
| } | |
| } | |
| return JS_NULL; | |
| case GJS_OM_PROP_REVERSE_PLAYBACK: | |
| if (odm->pid) { | |
| const GF_PropertyValue *p = gf_filter_pid_get_property(odm->pid, GF_PROP_PID_PLAYBACK_MODE); | |
| if (p && p->value.uint==GF_PLAYBACK_MODE_REWIND) | |
| return JS_NewBool(ctx, GF_TRUE); | |
| } | |
| return JS_NewBool(ctx, GF_FALSE ); | |
| case GJS_OM_PROP_SCALABLE_ENHANCEMENT: | |
| if (odm->lower_layer_odm) return JS_TRUE; | |
| if (odm->subscene && !odm->pid && odm->addon && !gf_list_count(odm->subscene->resources)) | |
| return JS_TRUE; | |
| return JS_FALSE; | |
| case GJS_OM_PROP_MAIN_ADDON_MEDIATIME: | |
| scene = odm->subscene ? odm->subscene : odm->parentscene; | |
| count = gf_list_count(scene->resources); | |
| if (! scene->main_addon_selected) count = 0; | |
| for (i=0; i < count; i++) { | |
| GF_ObjectManager *an_odm = gf_list_get(scene->resources, i); | |
| if (an_odm && an_odm->addon && (an_odm->addon->addon_type==GF_ADDON_TYPE_MAIN)) { | |
| if (an_odm->duration) { | |
| Double now = gf_clock_time_absolute(scene->root_od->ck) / 1000.0; | |
| now -= ((Double) an_odm->addon->media_pts) / 90000.0; | |
| now += ((Double) an_odm->addon->media_timestamp) / an_odm->addon->media_timescale; | |
| return JS_NewFloat64(ctx, now); | |
| } | |
| } | |
| } | |
| return JS_NewFloat64(ctx, -1); | |
| case GJS_OM_PROP_DEPENDENT_GROUPS: | |
| if (odm->pid) { | |
| GF_PropertyEntry *pe=NULL; | |
| const GF_PropertyValue *p = gf_filter_pid_get_info_str(odm->pid, "has:group_deps", &pe); | |
| if (p) { | |
| u32 v = p->value.uint; | |
| gf_filter_release_property(pe); | |
| return JS_NewInt32(ctx, v); | |
| } | |
| } | |
| return JS_NewInt32(ctx, 0); | |
| case GJS_OM_PROP_IS_VR_SCENE: | |
| return JS_NewBool(ctx, odm->subscene && odm->subscene->vr_type); | |
| case GJS_OM_PROP_DISABLED: | |
| return JS_NewBool(ctx, odm->redirect_url ? 1 : 0); | |
| case GJS_OM_PROP_BUFFERING: | |
| if (odm->parentscene) | |
| return JS_NewBool(ctx, odm->parentscene->nb_buffering ? 1 : 0); | |
| return JS_NewBool(ctx, odm->subscene->nb_buffering ? 1 : 0); | |
| case GJS_OM_PROP_FORCED_SUB: | |
| if (odm->pid) { | |
| if (odm->flags & GF_ODM_SUB_FORCED) return JS_NewInt32(ctx, 3); | |
| const GF_PropertyValue *p = gf_filter_pid_get_property(odm->pid, GF_PROP_PID_FORCED_SUB); | |
| if (p) return JS_NewInt32(ctx, p->value.uint); | |
| } | |
| return JS_NewInt32(ctx, 0); | |
| } | |
| return JS_UNDEFINED; | |
| } | |
| static JSValue gjs_odm_get_quality(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id); | |
| const GF_PropertyValue *prop; | |
| GF_PropertyEntry *pe=NULL; | |
| char *qdesc; | |
| const char *id="", *mime="", *codec=""; | |
| 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; | |
| Bool ilced=GF_FALSE, disabled=GF_FALSE, selected=GF_FALSE, automatic=GF_FALSE; | |
| Double fps=30.0; | |
| u32 ssr = 0; | |
| s32 idx; | |
| s32 dep_idx=0; | |
| if (!odm) return GF_JS_EXCEPTION(ctx); | |
| if (argc<1) return GF_JS_EXCEPTION(ctx); | |
| if (JS_ToInt32(ctx, &idx, argv[0])) | |
| return GF_JS_EXCEPTION(ctx); | |
| if ((argc>=2) && JS_ToInt32(ctx, &dep_idx, argv[1])) | |
| return GF_JS_EXCEPTION(ctx); | |
| if (!odm->pid) return JS_NULL; | |
| if (dep_idx) { | |
| char szName[100]; | |
| sprintf(szName, "has:deps_%d_qualities", dep_idx); | |
| prop = gf_filter_pid_get_info_str(odm->pid, szName, &pe); | |
| } else { | |
| prop = gf_filter_pid_get_info_str(odm->pid, "has:qualities", &pe); | |
| } | |
| if (!prop || (prop->type!=GF_PROP_STRING_LIST)) { | |
| gf_filter_release_property(pe); | |
| return JS_NULL; | |
| } | |
| qdesc = NULL; | |
| if (idx < (s32) prop->value.string_list.nb_items) | |
| qdesc = prop->value.string_list.vals[idx]; | |
| if (!qdesc) { | |
| gf_filter_release_property(pe); | |
| return JS_NULL; | |
| } | |
| JSValue a = JS_NewObject(ctx); | |
| if (JS_IsException(a)) | |
| return a; | |
| while (qdesc) { | |
| char *sep=strstr(qdesc, "::"); | |
| if (sep) sep[0]=0; | |
| if (!strncmp(qdesc, "id=", 3)) { | |
| id = NULL; | |
| JS_SetPropertyStr(ctx, a, "ID", JS_NewString(ctx, qdesc+3) ); | |
| } else if (!strncmp(qdesc, "mime=", 5)) { | |
| mime = NULL; | |
| JS_SetPropertyStr(ctx, a, "mime", JS_NewString(ctx, qdesc+5)); | |
| } else if (!strncmp(qdesc, "codec=", 6)) { | |
| codec = NULL; | |
| JS_SetPropertyStr(ctx, a, "codec", JS_NewString(ctx, qdesc+6)); | |
| } | |
| else if (!strncmp(qdesc, "bw=", 3)) bw = atoi(qdesc+3); | |
| else if (!strncmp(qdesc, "w=", 2)) w = atoi(qdesc+2); | |
| else if (!strncmp(qdesc, "h=", 2)) h = atoi(qdesc+2); | |
| else if (!strncmp(qdesc, "sr=", 3)) sr = atoi(qdesc+3); | |
| else if (!strncmp(qdesc, "ch=", 3)) ch = atoi(qdesc+3); | |
| else if (!strcmp(qdesc, "interlaced")) ilced = GF_TRUE; | |
| else if (!strcmp(qdesc, "disabled")) disabled = GF_TRUE; | |
| else if (!strcmp(qdesc, "selected")) selected = GF_TRUE; | |
| else if (!strncmp(qdesc, "fps=", 4)) { | |
| u32 fd=25, fn=1; | |
| if (sscanf(qdesc, "fps=%d/%d", &fn, &fd) != 2) { | |
| fd=1; | |
| sscanf(qdesc, "fps=%d", &fn); | |
| } | |
| fps = ((Double)fn) / fd; | |
| } | |
| else if (!strncmp(qdesc, "sar=", 4)) { | |
| sscanf(qdesc, "sar=%d/%d", &par_n, &par_d); | |
| } | |
| else if (!strncmp(qdesc, "ssr=", 4)) { | |
| sscanf(qdesc, "ssr=%u", &ssr); | |
| } | |
| if (!sep) break; | |
| sep[0]=':'; | |
| qdesc = sep+2; | |
| } | |
| if (!dep_idx) { | |
| prop = gf_filter_pid_get_info_str(odm->pid, "has:selected", &pe); | |
| if (prop && (prop->value.uint==idx)) | |
| selected = GF_TRUE; | |
| prop = gf_filter_pid_get_info_str(odm->pid, "has:auto", &pe); | |
| if (prop && prop->value.boolean) | |
| automatic = GF_TRUE; | |
| prop = gf_filter_pid_get_info_str(odm->pid, "has:tilemode", &pe); | |
| if (prop) tile_adaptation_mode = prop->value.uint; | |
| } else if (dep_idx>0) { | |
| prop = gf_filter_pid_get_info_str(odm->pid, "has:deps_selected", &pe); | |
| if (prop && (prop->type==GF_PROP_SINT_LIST)) { | |
| if ((u32) dep_idx<=prop->value.sint_list.nb_items) { | |
| s32 sel = prop->value.sint_list.vals[dep_idx-1]; | |
| if ((sel>=0) && (idx==sel)) { | |
| selected = GF_TRUE; | |
| } | |
| } | |
| } | |
| } | |
| gf_filter_release_property(pe); | |
| if (id) | |
| JS_SetPropertyStr(ctx, a, "ID", JS_NewString(ctx, id)); | |
| if (mime) | |
| JS_SetPropertyStr(ctx, a, "mime", JS_NewString(ctx, mime)); | |
| if (codec) | |
| JS_SetPropertyStr(ctx, a, "codec", JS_NewString(ctx, codec)); | |
| JS_SetPropertyStr(ctx, a, "width", JS_NewInt32(ctx, w)); | |
| JS_SetPropertyStr(ctx, a, "height", JS_NewInt32(ctx, h)); | |
| JS_SetPropertyStr(ctx, a, "bandwidth", JS_NewInt32(ctx, bw)); | |
| JS_SetPropertyStr(ctx, a, "interlaced", JS_NewBool(ctx, ilced)); | |
| JS_SetPropertyStr(ctx, a, "fps", JS_NewFloat64(ctx, fps) ); | |
| JS_SetPropertyStr(ctx, a, "samplerate", JS_NewInt32(ctx, sr)); | |
| JS_SetPropertyStr(ctx, a, "channels", JS_NewInt32(ctx, ch)); | |
| JS_SetPropertyStr(ctx, a, "par_num", JS_NewInt32(ctx, par_n)); | |
| JS_SetPropertyStr(ctx, a, "par_den", JS_NewInt32(ctx, par_d)); | |
| JS_SetPropertyStr(ctx, a, "ssr", JS_NewInt32(ctx, ssr)); | |
| JS_SetPropertyStr(ctx, a, "disabled", JS_NewBool(ctx, disabled)); | |
| JS_SetPropertyStr(ctx, a, "is_selected", JS_NewBool(ctx, selected)); | |
| JS_SetPropertyStr(ctx, a, "automatic", JS_NewBool(ctx, automatic)); | |
| if (!dep_idx) { | |
| JS_SetPropertyStr(ctx, a, "tile_mode", JS_NewInt32(ctx, tile_adaptation_mode)); | |
| JS_SetPropertyStr(ctx, a, "dependent_groups", JS_NewInt32(ctx, dependent_group_index)); | |
| } | |
| return a; | |
| } | |
| static JSValue gjs_odm_get_srd(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id); | |
| s32 x, y, w, h; | |
| if (!odm) return GF_JS_EXCEPTION(ctx); | |
| x = y = w = h = 0; | |
| if (argc) { | |
| u32 srdw, srdh; | |
| const GF_PropertyValue *p; | |
| GF_PropertyEntry *pe=NULL; | |
| s32 dep_idx; | |
| if (JS_ToInt32(ctx, &dep_idx, argv[0]) ) | |
| return GF_JS_EXCEPTION(ctx); | |
| if (dep_idx<=0) | |
| return GF_JS_EXCEPTION(ctx); | |
| dep_idx--; | |
| p = gf_filter_pid_get_info_str(odm->pid, "has:groups_srd", &pe); | |
| if (p && (p->type==GF_PROP_STRING_LIST)) { | |
| char *srd; | |
| if ((u32) dep_idx>=p->value.string_list.nb_items) { | |
| gf_filter_release_property(pe); | |
| return GF_JS_EXCEPTION(ctx); | |
| } | |
| srd = p->value.string_list.vals[dep_idx]; | |
| sscanf(srd, "%dx%dx%dx%d@%dx%d", &x, &y, &w, &h, &srdw, &srdh); | |
| } | |
| gf_filter_release_property(pe); | |
| } else if (odm && odm->mo && odm->mo->srd_w && odm->mo->srd_h) { | |
| x = odm->mo->srd_x; | |
| y = odm->mo->srd_y; | |
| w = odm->mo->srd_w; | |
| h = odm->mo->srd_h; | |
| } | |
| if (w && h) { | |
| JSValue a = JS_NewObject(ctx); | |
| if (JS_IsException(a)) return a; | |
| JS_SetPropertyStr(ctx, a, "x", JS_NewInt32(ctx, x)); | |
| JS_SetPropertyStr(ctx, a, "y", JS_NewInt32(ctx, y)); | |
| JS_SetPropertyStr(ctx, a, "w", JS_NewInt32(ctx, w)); | |
| JS_SetPropertyStr(ctx, a, "h", JS_NewInt32(ctx, h)); | |
| return a; | |
| } | |
| return JS_NULL; | |
| } | |
| GF_Filter *jsff_get_filter(JSContext *c, JSValue this_val); | |
| static JSValue gjs_odm_in_parent_chain(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| Bool res; | |
| GF_Filter *f; | |
| GF_Scene *scene; | |
| GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id); | |
| if (!odm || !argc) return GF_JS_EXCEPTION(ctx); | |
| f = jsff_get_filter(ctx, argv[0]); | |
| if (!f) return GF_JS_EXCEPTION(ctx); | |
| scene = odm->subscene ? odm->subscene : odm->parentscene; | |
| if (!scene) return GF_JS_EXCEPTION(ctx); | |
| if (gf_filter_in_parent_chain(f, scene->compositor->filter)) | |
| return JS_FALSE; | |
| if (odm->pid) { | |
| res = gf_filter_pid_is_filter_in_parents(odm->pid, f); | |
| } else { | |
| res = gf_filter_in_parent_chain(f, odm->scene_ns->source_filter); | |
| } | |
| return res ? JS_TRUE : JS_FALSE; | |
| } | |
| static JSValue gjs_odm_select_quality(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| s32 idx = -1; | |
| s32 tile_mode = -1; | |
| u32 dep_idx = 0; | |
| GF_FilterEvent evt; | |
| GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id); | |
| if (!odm || !odm->pid) return GF_JS_EXCEPTION(ctx); | |
| if ((argc>=1) && JS_IsString(argv[0])) { | |
| const char *ID = JS_ToCString(ctx, argv[0]); | |
| if (!strcmp(ID, "auto")) { | |
| idx = -1; | |
| } else { | |
| idx = atoi(ID); | |
| } | |
| JS_FreeCString(ctx, ID); | |
| if (argc>=2) { | |
| if (JS_ToInt32(ctx, &dep_idx, argv[1])) | |
| return GF_JS_EXCEPTION(ctx); | |
| } | |
| } | |
| else if ((argc==1) && JS_IsInteger(argv[0])) { | |
| if (JS_ToInt32(ctx, &tile_mode, argv[0])) | |
| return GF_JS_EXCEPTION(ctx); | |
| if (tile_mode<0) | |
| return JS_UNDEFINED; | |
| } | |
| GF_FEVT_INIT(evt, GF_FEVT_QUALITY_SWITCH, odm->pid); | |
| if (tile_mode>=0) { | |
| evt.quality_switch.set_tile_mode_plus_one = 1 + tile_mode; | |
| } else { | |
| evt.quality_switch.dependent_group_index = dep_idx; | |
| evt.quality_switch.q_idx = idx; | |
| } | |
| gf_filter_pid_send_event(odm->pid, &evt); | |
| return JS_UNDEFINED; | |
| } | |
| static JSValue gjs_odm_disable_main_addon(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id); | |
| if (!odm) return GF_JS_EXCEPTION(ctx); | |
| if (!odm->subscene || !odm->subscene->main_addon_selected) return JS_UNDEFINED; | |
| gf_scene_resume_live(odm->subscene); | |
| return JS_UNDEFINED; | |
| } | |
| static JSValue gjs_odm_select_service(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| u32 sid; | |
| GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id); | |
| if (!odm) return GF_JS_EXCEPTION(ctx); | |
| if (argc<1) return GF_JS_EXCEPTION(ctx); | |
| if (JS_ToInt32(ctx, &sid, argv[0])) | |
| return GF_JS_EXCEPTION(ctx); | |
| gf_scene_set_service_id(odm->subscene ? odm->subscene : odm->parentscene, sid); | |
| return JS_UNDEFINED; | |
| } | |
| static JSValue gjs_odm_select(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id); | |
| if (!odm) return GF_JS_EXCEPTION(ctx); | |
| #ifndef GPAC_DISABLE_COMPOSITOR | |
| gf_scene_select_object(odm->parentscene, odm); | |
| #endif | |
| return JS_UNDEFINED; | |
| } | |
| static JSValue gjs_odm_get_resource(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| GF_ObjectManager *an_odm = NULL; | |
| u32 idx; | |
| GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id); | |
| if (!odm) return GF_JS_EXCEPTION(ctx); | |
| if (argc<1) return GF_JS_EXCEPTION(ctx); | |
| if (JS_ToInt32(ctx, &idx, argv[0])) return GF_JS_EXCEPTION(ctx); | |
| if (odm->subscene) { | |
| an_odm = gf_list_get(odm->subscene->resources, idx); | |
| } | |
| if (an_odm && an_odm->scene_ns) { | |
| JSValue anobj = JS_NewObjectClass(ctx, odm_class_id); | |
| JS_SetOpaque(anobj, an_odm); | |
| return anobj; | |
| } | |
| return JS_NULL; | |
| } | |
| static JSValue gjs_odm_addon_layout(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| u32 pos, size; | |
| GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id); | |
| if (!odm) return GF_JS_EXCEPTION(ctx); | |
| if (argc<2) return GF_JS_EXCEPTION(ctx); | |
| if (JS_ToInt32(ctx, &pos, argv[0])) return GF_JS_EXCEPTION(ctx); | |
| if (JS_ToInt32(ctx, &size, argv[1])) return GF_JS_EXCEPTION(ctx); | |
| if (odm->subscene) | |
| gf_scene_set_addon_layout_info(odm->subscene, pos, size); | |
| return JS_UNDEFINED; | |
| } | |
| static void do_enable_addon(GF_ObjectManager *odm, char *addon_url, Bool enable_if_defined, Bool disable_if_defined ) | |
| { | |
| if (addon_url) { | |
| GF_AssociatedContentLocation addon_info; | |
| memset(&addon_info, 0, sizeof(GF_AssociatedContentLocation)); | |
| addon_info.external_URL = addon_url; | |
| addon_info.timeline_id = -100; | |
| addon_info.enable_if_defined = enable_if_defined; | |
| addon_info.disable_if_defined = disable_if_defined; | |
| gf_scene_register_associated_media(odm->subscene ? odm->subscene : odm->parentscene, &addon_info); | |
| } | |
| } | |
| static JSValue gjs_odm_enable_addon(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| Bool do_disable = GF_FALSE; | |
| const char *addon_url = NULL; | |
| GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id); | |
| if (!odm || !argc) return GF_JS_EXCEPTION(ctx); | |
| if (! JS_IsString(argv[0]) ) { | |
| #ifdef GPAC_ENABLE_COVERAGE | |
| if (gf_sys_is_cov_mode()) { | |
| do_enable_addon(odm, NULL, GF_TRUE, GF_FALSE); | |
| } | |
| #endif | |
| return JS_UNDEFINED; | |
| } | |
| if (argc==2) | |
| do_disable = JS_ToBool(ctx, argv[1]); | |
| addon_url = JS_ToCString(ctx, argv[0]); | |
| if (addon_url) { | |
| do_enable_addon(odm, (char *) addon_url, GF_TRUE, do_disable); | |
| } | |
| JS_FreeCString(ctx, addon_url); | |
| return JS_UNDEFINED; | |
| } | |
| static JSValue gjs_odm_declare_addon(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| const char *addon_url = NULL; | |
| GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id); | |
| if (!odm || !argc) return GF_JS_EXCEPTION(ctx); | |
| if (! JS_IsString(argv[0]) ) return GF_JS_EXCEPTION(ctx); | |
| addon_url = JS_ToCString(ctx, argv[0]); | |
| if (addon_url && strcmp(addon_url, "gtest")) { | |
| do_enable_addon(odm, (char *)addon_url, GF_FALSE, GF_FALSE); | |
| } | |
| JS_FreeCString(ctx, addon_url); | |
| return JS_UNDEFINED; | |
| } | |
| static JSValue gjs_odm_get_chapters(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| GF_ObjectManager *odm = JS_GetOpaque(this_val, odm_class_id); | |
| if (!odm) return GF_JS_EXCEPTION(ctx); | |
| if (odm->subscene) odm = gf_list_get(odm->subscene->resources, 0); | |
| if (!odm || ! odm->pid) return JS_NULL; | |
| const GF_PropertyValue *times = gf_filter_pid_get_property(odm->pid, GF_PROP_PID_CHAP_TIMES); | |
| const GF_PropertyValue *names = gf_filter_pid_get_property(odm->pid, GF_PROP_PID_CHAP_NAMES); | |
| if (!times || !names || (times->value.uint_list.nb_items!=times->value.string_list.nb_items)) | |
| return JS_NULL; | |
| JSValue ret = JS_NewArray(ctx); | |
| u32 i, count=times->value.uint_list.nb_items; | |
| for (i=0; i<count; i++) { | |
| char *name; | |
| JSValue obj = JS_NewObject(ctx); | |
| JS_SetPropertyStr(ctx, obj, "start", JS_NewInt32(ctx, times->value.uint_list.vals[i])); | |
| name = names->value.string_list.vals[i]; | |
| if (!name) name = ""; | |
| JS_SetPropertyStr(ctx, obj, "name", JS_NewString(ctx, name)); | |
| JS_SetPropertyUint32(ctx, ret, i, obj); | |
| } | |
| return ret; | |
| } | |
| static JSValue scenejs_get_object_manager(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| JSValue anobj; | |
| u32 i, count; | |
| GF_ObjectManager *odm = NULL; | |
| const char *service_url = NULL; | |
| GF_SCENEJSExt *sjs = JS_GetOpaque(this_val, scene_class_id); | |
| GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val); | |
| GF_Scene *scene = compositor->root_scene; | |
| if (!sjs) return GF_JS_EXCEPTION(ctx); | |
| if (JS_IsString(argv[0]) ) { | |
| const char *url; | |
| char *an_url; | |
| u32 url_len; | |
| url = service_url = JS_ToCString(ctx, argv[0]); | |
| if (!service_url) { | |
| return JS_NULL; | |
| } | |
| if (!strncmp(service_url, "gpac://", 7)) url = service_url + 7; | |
| if (!strncmp(service_url, "file://", 7)) url = service_url + 7; | |
| url_len = (u32) strlen(url); | |
| an_url = strchr(url, '#'); | |
| if (an_url) url_len -= (u32) strlen(an_url); | |
| count = gf_list_count(scene->resources); | |
| for (i=0; i<count; i++) { | |
| odm = gf_list_get(scene->resources, i); | |
| if (odm->scene_ns) { | |
| an_url = odm->scene_ns->url; | |
| if (!strncmp(an_url, "gpac://", 7)) an_url = an_url + 7; | |
| if (!strncmp(an_url, "file://", 7)) an_url = an_url + 7; | |
| if (!strncmp(an_url, url, url_len)) | |
| break; | |
| if (strstr(an_url, "://")) { | |
| char *pc_url = gf_url_percent_encode(an_url); | |
| if (!strncmp(pc_url, url, url_len)) { | |
| gf_free(pc_url); | |
| break; | |
| } | |
| gf_free(pc_url); | |
| } | |
| } | |
| odm = NULL; | |
| } | |
| } | |
| JS_FreeCString(ctx, service_url); | |
| if (!odm) return JS_NULL; | |
| anobj = JS_NewObjectClass(ctx, odm_class_id); | |
| if (JS_IsException(anobj)) return anobj; | |
| JS_SetOpaque(anobj, odm); | |
| return anobj; | |
| } | |
| static JSValue gpacevt_getProperty(JSContext *ctx, JSValueConst this_val, int magic) | |
| { | |
| GF_SCENEJSExt *sjs = JS_GetOpaque(this_val, gpacevt_class_id); | |
| if (!sjs || !sjs->evt) return GF_JS_EXCEPTION(ctx); | |
| GF_Event *evt = sjs->evt; | |
| switch (magic) { | |
| case GJS_EVT_PROP_KEYCODE: | |
| #ifndef GPAC_DISABLE_SVG | |
| return JS_NewString(ctx, gf_dom_get_key_name(evt->key.key_code) ); | |
| #else | |
| return JS_NULL; | |
| #endif | |
| case GJS_EVT_PROP_MOUSE_X: | |
| return JS_NewInt32(ctx, evt->mouse.x); | |
| case GJS_EVT_PROP_MOUSE_Y: | |
| return JS_NewInt32(ctx, evt->mouse.y); | |
| case GJS_EVT_PROP_PICKED: | |
| if (sjs->compositor->hit_appear) return JS_NewBool(ctx, 1); | |
| else if (gf_list_count(sjs->compositor->previous_sensors) ) return JS_NewBool(ctx, 1); | |
| else if (sjs->compositor->text_selection) return JS_NewBool(ctx, 1); | |
| else return JS_NewBool(ctx, 0); | |
| case GJS_EVT_PROP_WHEEL: | |
| return JS_NewFloat64(ctx, FIX2FLT(evt->mouse.wheel_pos)); | |
| case GJS_EVT_PROP_BUTTON: | |
| return JS_NewInt32(ctx, evt->mouse.button); | |
| case GJS_EVT_PROP_TYPE: | |
| return JS_NewInt32(ctx, evt->type); | |
| case GJS_EVT_PROP_NAME: | |
| #ifndef GPAC_DISABLE_SVG | |
| return JS_NewString(ctx, gf_dom_event_get_name(evt->type) ); | |
| #else | |
| return JS_NULL; | |
| #endif | |
| case GJS_EVT_PROP_HWKEY: | |
| return JS_NewInt32(ctx, evt->key.hw_code); | |
| case GJS_EVT_PROP_TARGET_URL: | |
| return JS_NewString(ctx, evt->navigate.to_url); | |
| case GJS_EVT_PROP_FILES: | |
| { | |
| u32 i, idx; | |
| JSValue files_array = JS_NewArray(ctx); | |
| idx=0; | |
| for (i=0; i<evt->open_file.nb_files; i++) { | |
| if (evt->open_file.files[i]) { | |
| JS_SetPropertyUint32(ctx, files_array, idx, JS_NewString(ctx, evt->open_file.files[i]) ); | |
| idx++; | |
| } | |
| } | |
| return files_array; | |
| } | |
| } | |
| return JS_UNDEFINED; | |
| } | |
| static Bool gjs_event_filter_process(GF_SCENEJSExt *sjs, GF_Event *evt) | |
| { | |
| s32 res; | |
| JSValue rval; | |
| sjs->evt = evt; | |
| JS_SetOpaque(sjs->evt_obj, sjs); | |
| rval = JS_Call(sjs->c, sjs->evt_fun, sjs->evt_filter_obj, 1, &sjs->evt_obj); | |
| JS_SetOpaque(sjs->evt_obj, NULL); | |
| sjs->evt = NULL; | |
| res = 0; | |
| if (JS_IsBool(rval)) | |
| res = JS_ToBool(sjs->c, rval); | |
| else if (JS_IsNumber(rval)) | |
| JS_ToInt32(sjs->c, &res, rval); | |
| JS_FreeValue(sjs->c, rval); | |
| return res ? GF_TRUE : GF_FALSE; | |
| } | |
| static Bool gjs_event_filter(void *udta, GF_Event *evt, Bool consumed_by_compositor) | |
| { | |
| u32 lock_fail; | |
| Bool res; | |
| GF_SCENEJSExt *sjs = (GF_SCENEJSExt *)udta; | |
| if (consumed_by_compositor) return 0; | |
| if (sjs->evt != NULL) return 0; | |
| lock_fail=0; | |
| res = gf_mx_try_lock(sjs->compositor->mx); | |
| if (!res) { | |
| lock_fail=1; | |
| } else { | |
| res = gf_js_try_lock(sjs->c); | |
| if (!res) lock_fail=2; | |
| } | |
| if (lock_fail) { | |
| GF_Event *evt_clone; | |
| gf_mx_p(sjs->event_mx); | |
| evt_clone = gf_malloc(sizeof(GF_Event)); | |
| memcpy(evt_clone, evt, sizeof(GF_Event)); | |
| gf_list_add(sjs->event_queue, evt_clone); | |
| GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[SCENEJS] Couldn't lock % mutex, queuing event\n", (lock_fail==2) ? "JavaScript" : "Compositor")); | |
| gf_mx_v(sjs->event_mx); | |
| if (lock_fail==2){ | |
| gf_mx_v(sjs->compositor->mx); | |
| } | |
| return 0; | |
| } | |
| gf_mx_p(sjs->event_mx); | |
| while (gf_list_count(sjs->event_queue)) { | |
| GF_Event *an_evt = (GF_Event *) gf_list_pop_front(sjs->event_queue); | |
| gjs_event_filter_process(sjs, an_evt); | |
| gf_free(an_evt); | |
| } | |
| gf_mx_v(sjs->event_mx); | |
| res = gjs_event_filter_process(sjs, evt); | |
| gf_mx_v(sjs->compositor->mx); | |
| gf_js_lock(sjs->c, 0); | |
| return res; | |
| } | |
| static JSValue scenejs_set_event_filter(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| GF_SCENEJSExt *sjs = JS_GetOpaque(this_val, scene_class_id); | |
| if (!sjs || !argc) | |
| return GF_JS_EXCEPTION(ctx); | |
| if (!JS_IsNull(argv[0]) && !JS_IsUndefined(argv[0]) && !JS_IsFunction(ctx, argv[0])) | |
| return GF_JS_EXCEPTION(ctx); | |
| JS_FreeValue(sjs->c, sjs->evt_fun); | |
| sjs->evt_fun = JS_DupValue(ctx, argv[0]); | |
| sjs->evt_filter_obj = this_val; | |
| sjs->c = ctx; | |
| sjs->evt_filter.udta = sjs; | |
| sjs->evt_filter.on_event = gjs_event_filter; | |
| gf_filter_add_event_listener(sjs->compositor->filter, &sjs->evt_filter); | |
| return JS_UNDEFINED; | |
| } | |
| GF_Node *gf_sg_js_get_node(struct JSContext *c, JSValue obj); | |
| static JSValue scenejs_set_focus(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| GF_Node *elt; | |
| GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val); | |
| if (!compositor || !argc) return GF_JS_EXCEPTION(ctx); | |
| if (JS_IsNull(argv[0])) { | |
| gf_sc_focus_switch_ring(compositor, 0, NULL, 0); | |
| return JS_UNDEFINED; | |
| } | |
| if (JS_IsString(argv[0])) { | |
| const char *focus_type = JS_ToCString(ctx, argv[0]); | |
| if (!stricmp(focus_type, "previous")) { | |
| gf_sc_focus_switch_ring(compositor, 1, NULL, 0); | |
| } | |
| else if (!stricmp(focus_type, "next")) { | |
| gf_sc_focus_switch_ring(compositor, 0, NULL, 0); | |
| } | |
| JS_FreeCString(ctx, focus_type); | |
| } else if (JS_IsObject(argv[0])) { | |
| elt = gf_sg_js_get_node(ctx, argv[0]); | |
| if (!elt) return GF_JS_EXCEPTION(ctx); | |
| gf_sc_focus_switch_ring(compositor, 0, elt, 2); | |
| } | |
| return JS_UNDEFINED; | |
| } | |
| #if 0 //unused | |
| static JSValue scenejs_get_scene(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| GF_Node *elt; | |
| u32 w, h; | |
| JSValue scene_obj; | |
| #ifndef GPAC_DISABLE_SCENEGRAPH | |
| GF_SceneGraph *sg; | |
| #endif | |
| GF_Scene *scene=NULL; | |
| GF_SCENEJSExt *sjs = (GF_SCENEJSExt *)JS_GetOpaque(this_val, scene_class_id); | |
| if (!sjs || !argc || !JS_IsObject(argv[0])) return GF_JS_EXCEPTION(ctx); | |
| elt = gf_sg_js_get_node(ctx, argv[0]); | |
| if (!elt) return GF_JS_EXCEPTION(ctx); | |
| switch (elt->sgprivate->tag) { | |
| #ifndef GPAC_DISABLE_VRML | |
| case TAG_MPEG4_Inline: | |
| scene = (GF_Scene *)gf_node_get_private(elt); | |
| break; | |
| #endif | |
| #ifndef GPAC_DISABLE_X3D | |
| case TAG_X3D_Inline: | |
| scene = (GF_Scene *)gf_node_get_private(elt); | |
| break; | |
| #endif | |
| #ifndef GPAC_DISABLE_SVG | |
| case TAG_SVG_animation: | |
| sg = gf_sc_animation_get_scenegraph(elt); | |
| scene = (GF_Scene *)gf_sg_get_private(sg); | |
| break; | |
| #endif | |
| default: | |
| return GF_JS_EXCEPTION(ctx); | |
| } | |
| if (!scene) return GF_JS_EXCEPTION(ctx); | |
| scene_obj = JS_NewObjectClass(ctx, any_class_id); | |
| if (JS_IsException(scene_obj)) return scene_obj; | |
| JS_SetOpaque(scene_obj, scene); | |
| gf_sg_get_scene_size_info(scene->graph, &w, &h); | |
| JS_SetPropertyStr(ctx, scene_obj, "width", JS_NewInt32(ctx, w)); | |
| JS_SetPropertyStr(ctx, scene_obj, "height", JS_NewInt32(ctx, h)); | |
| JS_SetPropertyStr(ctx, scene_obj, "connected", JS_NewBool(ctx, scene->graph ? 1 : 0)); | |
| return scene_obj; | |
| } | |
| #endif | |
| static JSValue scenejs_show_keyboard(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) | |
| { | |
| GF_Compositor *compositor = scenejs_get_compositor(ctx, this_val); | |
| if (!compositor || !argc) return GF_JS_EXCEPTION(ctx); | |
| GF_Event evt; | |
| memset(&evt, 0, sizeof(GF_Event)); | |
| evt.type = JS_ToBool(ctx, argv[0]) ? GF_EVENT_TEXT_EDITING_START : GF_EVENT_TEXT_EDITING_END; | |
| gf_sc_user_event(compositor, &evt); | |
| return JS_UNDEFINED; | |
| } | |
| static const JSCFunctionListEntry scenejs_evt_funcs[] = { | |
| JS_CGETSET_MAGIC_DEF("keycode", gpacevt_getProperty, NULL, GJS_EVT_PROP_KEYCODE), | |
| JS_CGETSET_MAGIC_DEF("mouse_x", gpacevt_getProperty, NULL, GJS_EVT_PROP_MOUSE_X), | |
| JS_CGETSET_MAGIC_DEF("mouse_y", gpacevt_getProperty, NULL, GJS_EVT_PROP_MOUSE_Y), | |
| JS_CGETSET_MAGIC_DEF("picked", gpacevt_getProperty, NULL, GJS_EVT_PROP_PICKED), | |
| JS_CGETSET_MAGIC_DEF("wheel", gpacevt_getProperty, NULL, GJS_EVT_PROP_WHEEL), | |
| JS_CGETSET_MAGIC_DEF("button", gpacevt_getProperty, NULL, GJS_EVT_PROP_BUTTON), | |
| JS_CGETSET_MAGIC_DEF("type", gpacevt_getProperty, NULL, GJS_EVT_PROP_TYPE), | |
| JS_CGETSET_MAGIC_DEF("name", gpacevt_getProperty, NULL, GJS_EVT_PROP_NAME), | |
| JS_CGETSET_MAGIC_DEF("hwkey", gpacevt_getProperty, NULL, GJS_EVT_PROP_HWKEY), | |
| JS_CGETSET_MAGIC_DEF("url", gpacevt_getProperty, NULL, GJS_EVT_PROP_TARGET_URL), | |
| JS_CGETSET_MAGIC_DEF("dropfiles", gpacevt_getProperty, NULL, GJS_EVT_PROP_FILES), | |
| }; | |
| static const JSCFunctionListEntry scenejs_funcs[] = { | |
| JS_CGETSET_MAGIC_DEF("fullscreen", scenejs_getProperty, scenejs_setProperty, GJS_SCENE_PROP_FULLSCREEN), | |
| JS_CGETSET_MAGIC_DEF("current_path", scenejs_getProperty, NULL, GJS_SCENE_PROP_CURRENT_PATH), | |
| JS_CGETSET_MAGIC_DEF("volume", scenejs_getProperty, scenejs_setProperty, GJS_SCENE_PROP_VOLUME), | |
| JS_CGETSET_MAGIC_DEF("navigation", scenejs_getProperty, scenejs_setProperty, GJS_SCENE_PROP_NAVIGATION), | |
| JS_CGETSET_MAGIC_DEF("navigation_type", scenejs_getProperty, scenejs_setProperty, GJS_SCENE_PROP_NAVIGATION_TYPE), | |
| JS_CGETSET_MAGIC_DEF("hardware_yuv", scenejs_getProperty, NULL, GJS_SCENE_PROP_HARDWARE_YUV), | |
| JS_CGETSET_MAGIC_DEF("hardware_rgb", scenejs_getProperty, NULL, GJS_SCENE_PROP_HARDWARE_RGB), | |
| JS_CGETSET_MAGIC_DEF("hardware_rgba", scenejs_getProperty, NULL, GJS_SCENE_PROP_HARDWARE_RGBA), | |
| JS_CGETSET_MAGIC_DEF("hardware_stretch", scenejs_getProperty, NULL, GJS_SCENE_PROP_HARDWARE_STRETCH), | |
| JS_CGETSET_MAGIC_DEF("screen_width", scenejs_getProperty, NULL, GJS_SCENE_PROP_SCREEN_WIDTH), | |
| JS_CGETSET_MAGIC_DEF("screen_height", scenejs_getProperty, NULL, GJS_SCENE_PROP_SCREEN_HEIGHT), | |
| JS_CGETSET_MAGIC_DEF("fps", scenejs_getProperty, NULL, GJS_SCENE_PROP_FPS), | |
| JS_CGETSET_MAGIC_DEF("sim_fps", scenejs_getProperty, NULL, GJS_SCENE_PROP_SIM_FPS), | |
| JS_CGETSET_MAGIC_DEF("has_opengl", scenejs_getProperty, NULL, GJS_SCENE_PROP_HAS_OPENGL), | |
| JS_CGETSET_MAGIC_DEF("caption", scenejs_getProperty, scenejs_setProperty, GJS_SCENE_PROP_CAPTION), | |
| JS_CGETSET_MAGIC_DEF("focus_highlight", scenejs_getProperty, scenejs_setProperty, GJS_SCENE_PROP_FOCUS_HIGHLIGHT), | |
| JS_CGETSET_MAGIC_DEF("dpi_x", scenejs_getProperty, NULL, GJS_SCENE_PROP_DPI_X), | |
| JS_CGETSET_MAGIC_DEF("dpi_y", scenejs_getProperty, NULL, GJS_SCENE_PROP_DPI_Y), | |
| JS_CGETSET_MAGIC_DEF("sensors_active", scenejs_getProperty, scenejs_setProperty, GJS_SCENE_PROP_SENSORS_ACTIVE), | |
| JS_CGETSET_MAGIC_DEF("zoom", scenejs_getProperty, NULL, GJS_SCENE_PROP_ZOOM), | |
| JS_CGETSET_MAGIC_DEF("text_selection", scenejs_getProperty, NULL, GJS_SCENE_PROP_TEXT_SEL), | |
| JS_CGETSET_MAGIC_DEF("orientation", scenejs_getProperty, scenejs_setProperty, GJS_SCENE_PROP_DISP_ORIENTATION), | |
| JS_CFUNC_DEF("get_option", 0, scenejs_get_option), | |
| JS_CFUNC_DEF("set_option", 0, scenejs_set_option), | |
| JS_CFUNC_DEF("set_size", 0, scenejs_set_size), | |
| JS_CFUNC_DEF("exit", 0, scenejs_exit), | |
| JS_CFUNC_DEF("set_3d", 0, scenejs_set_3d), | |
| JS_CFUNC_DEF("move_window", 0, scenejs_move_window), | |
| JS_CFUNC_DEF("set_event_filter", 0, scenejs_set_event_filter), | |
| JS_CFUNC_DEF("set_focus", 0, scenejs_set_focus), | |
| JS_CFUNC_DEF("show_keyboard", 0, scenejs_show_keyboard), | |
| JS_CFUNC_DEF("trigger_gc", 0, scenejs_trigger_gc), | |
| JS_CFUNC_DEF("get_object_manager", 0, scenejs_get_object_manager), | |
| JS_CFUNC_DEF("switch_quality", 0, scenejs_switch_quality), | |
| JS_CFUNC_DEF("navigation_supported", 0, scenejs_navigation_supported), | |
| JS_CFUNC_DEF("set_back_color", 0, scenejs_set_back_color) | |
| }; | |
| static const JSCFunctionListEntry odm_funcs[] = { | |
| JS_CGETSET_MAGIC_DEF("ID", odm_getProperty, NULL, GJS_OM_PROP_ID), | |
| JS_CGETSET_MAGIC_DEF("nb_resources", odm_getProperty, NULL, GJS_OM_PROP_NB_RES), | |
| JS_CGETSET_MAGIC_DEF("service_url", odm_getProperty, NULL, GJS_OM_PROP_URL), | |
| JS_CGETSET_MAGIC_DEF("duration", odm_getProperty, NULL, GJS_OM_PROP_DUR), | |
| JS_CGETSET_MAGIC_DEF("clock_time", odm_getProperty, NULL, GJS_OM_PROP_CLOCK), | |
| JS_CGETSET_MAGIC_DEF("clock_drift", odm_getProperty, NULL, GJS_OM_PROP_DRIFT), | |
| JS_CGETSET_MAGIC_DEF("status", odm_getProperty, NULL, GJS_OM_PROP_STATUS), | |
| JS_CGETSET_MAGIC_DEF("buffer", odm_getProperty, NULL, GJS_OM_PROP_BUFFER), | |
| JS_CGETSET_MAGIC_DEF("db_unit_count", odm_getProperty, NULL, GJS_OM_PROP_DB_COUNT), | |
| JS_CGETSET_MAGIC_DEF("cb_unit_count", odm_getProperty, NULL, GJS_OM_PROP_CB_COUNT), | |
| JS_CGETSET_MAGIC_DEF("cb_capacity", odm_getProperty, NULL, GJS_OM_PROP_CB_CAP), | |
| JS_CGETSET_MAGIC_DEF("type", odm_getProperty, NULL, GJS_OM_PROP_TYPE), | |
| JS_CGETSET_MAGIC_DEF("samplerate", odm_getProperty, NULL, GJS_OM_PROP_SAMPLERATE), | |
| JS_CGETSET_MAGIC_DEF("channels", odm_getProperty, NULL, GJS_OM_PROP_CHANNELS), | |
| JS_CGETSET_MAGIC_DEF("lang", odm_getProperty, NULL, GJS_OM_PROP_LANG), | |
| JS_CGETSET_MAGIC_DEF("width", odm_getProperty, NULL, GJS_OM_PROP_WIDTH), | |
| JS_CGETSET_MAGIC_DEF("height", odm_getProperty, NULL, GJS_OM_PROP_HEIGHT), | |
| JS_CGETSET_MAGIC_DEF("pixelformt", odm_getProperty, NULL, GJS_OM_PROP_PIXELFORMAT), | |
| JS_CGETSET_MAGIC_DEF("par", odm_getProperty, NULL, GJS_OM_PROP_PAR), | |
| JS_CGETSET_MAGIC_DEF("dec_frames", odm_getProperty, NULL, GJS_OM_PROP_DEC_FRAMES), | |
| JS_CGETSET_MAGIC_DEF("drop_frames", odm_getProperty, NULL, GJS_OM_PROP_DROP_FRAMES), | |
| JS_CGETSET_MAGIC_DEF("max_dec_time", odm_getProperty, NULL, GJS_OM_PROP_DEC_TIME_MAX), | |
| JS_CGETSET_MAGIC_DEF("total_dec_time", odm_getProperty, NULL, GJS_OM_PROP_DEC_TIME_TOTAL), | |
| JS_CGETSET_MAGIC_DEF("avg_bitrate", odm_getProperty, NULL, GJS_OM_PROP_AVG_RATE), | |
| JS_CGETSET_MAGIC_DEF("max_bitrate", odm_getProperty, NULL, GJS_OM_PROP_MAX_RATE), | |
| JS_CGETSET_MAGIC_DEF("service_handler", odm_getProperty, NULL, GJS_OM_PROP_SERVICE_HANDLER), | |
| JS_CGETSET_MAGIC_DEF("codec", odm_getProperty, NULL, GJS_OM_PROP_CODEC), | |
| JS_CGETSET_MAGIC_DEF("nb_views", odm_getProperty, NULL, GJS_OM_PROP_NB_VIEWS), | |
| JS_CGETSET_MAGIC_DEF("nb_qualities", odm_getProperty, NULL, GJS_OM_PROP_NB_QUALITIES), | |
| JS_CGETSET_MAGIC_DEF("max_buffer", odm_getProperty, NULL, GJS_OM_PROP_MAX_BUFFER), | |
| JS_CGETSET_MAGIC_DEF("min_buffer", odm_getProperty, NULL, GJS_OM_PROP_MIN_BUFFER), | |
| JS_CGETSET_MAGIC_DEF("frame_duration", odm_getProperty, NULL, GJS_OM_PROP_FRAME_DUR), | |
| JS_CGETSET_MAGIC_DEF("irap_frames", odm_getProperty, NULL, GJS_OM_PROP_NB_IRAP), | |
| JS_CGETSET_MAGIC_DEF("irap_dec_time", odm_getProperty, NULL, GJS_OM_PROP_IRAP_DEC_TIME), | |
| JS_CGETSET_MAGIC_DEF("irap_max_time", odm_getProperty, NULL, GJS_OM_PROP_IRAP_MAX_TIME), | |
| JS_CGETSET_MAGIC_DEF("service_id", odm_getProperty, NULL, GJS_OM_PROP_SERVICE_ID), | |
| JS_CGETSET_MAGIC_DEF("selected_service", odm_getProperty, NULL, GJS_OM_PROP_SELECTED_SERVICE), | |
| JS_CGETSET_MAGIC_DEF("bandwidth_down", odm_getProperty, NULL, GJS_OM_PROP_BANDWIDTH_DOWN), | |
| JS_CGETSET_MAGIC_DEF("nb_http", odm_getProperty, NULL, GJS_OM_PROP_NB_HTTP), | |
| JS_CGETSET_MAGIC_DEF("timeshift_depth", odm_getProperty, NULL, GJS_OM_PROP_TIMESHIFT_DEPTH), | |
| JS_CGETSET_MAGIC_DEF("timeshift_time", odm_getProperty, NULL, GJS_OM_PROP_TIMESHIFT_TIME), | |
| JS_CGETSET_MAGIC_DEF("is_addon", odm_getProperty, NULL, GJS_OM_PROP_IS_ADDON), | |
| JS_CGETSET_MAGIC_DEF("main_addon_on", odm_getProperty, NULL, GJS_OM_PROP_MAIN_ADDON_ON), | |
| JS_CGETSET_MAGIC_DEF("is_over", odm_getProperty, NULL, GJS_OM_PROP_IS_OVER), | |
| JS_CGETSET_MAGIC_DEF("dynamic_scene", odm_getProperty, NULL, GJS_OM_PROP_DYNAMIC_SCENE), | |
| JS_CGETSET_MAGIC_DEF("service_name", odm_getProperty, NULL, GJS_OM_PROP_SERVICE_NAME), | |
| JS_CGETSET_MAGIC_DEF("ntp_diff", odm_getProperty, NULL, GJS_OM_PROP_NTP_DIFF), | |
| JS_CGETSET_MAGIC_DEF("ntp_sender_diff", odm_getProperty, NULL, GJS_OM_PROP_NTP_SENDER_DIFF), | |
| JS_CGETSET_MAGIC_DEF("main_addon_url", odm_getProperty, NULL, GJS_OM_PROP_MAIN_ADDON_URL), | |
| JS_CGETSET_MAGIC_DEF("reverse_playback_supported", odm_getProperty, NULL, GJS_OM_PROP_REVERSE_PLAYBACK), | |
| JS_CGETSET_MAGIC_DEF("scalable_enhancement", odm_getProperty, NULL, GJS_OM_PROP_SCALABLE_ENHANCEMENT), | |
| JS_CGETSET_MAGIC_DEF("main_addon_media_time", odm_getProperty, NULL, GJS_OM_PROP_MAIN_ADDON_MEDIATIME), | |
| JS_CGETSET_MAGIC_DEF("dependent_groups", odm_getProperty, NULL, GJS_OM_PROP_DEPENDENT_GROUPS), | |
| JS_CGETSET_MAGIC_DEF("vr_scene", odm_getProperty, NULL, GJS_OM_PROP_IS_VR_SCENE), | |
| JS_CGETSET_MAGIC_DEF("disabled", odm_getProperty, NULL, GJS_OM_PROP_DISABLED), | |
| JS_CGETSET_MAGIC_DEF("buffering", odm_getProperty, NULL, GJS_OM_PROP_BUFFERING), | |
| JS_CGETSET_MAGIC_DEF("forced", odm_getProperty, NULL, GJS_OM_PROP_FORCED_SUB), | |
| JS_CFUNC_DEF("declare_addon", 0, gjs_odm_declare_addon), | |
| JS_CFUNC_DEF("enable_addon", 0, gjs_odm_enable_addon), | |
| JS_CFUNC_DEF("addon_layout", 0, gjs_odm_addon_layout), | |
| JS_CFUNC_DEF("get_resource", 0, gjs_odm_get_resource), | |
| JS_CFUNC_DEF("get_quality", 0, gjs_odm_get_quality), | |
| JS_CFUNC_DEF("select_service", 0, gjs_odm_select_service), | |
| JS_CFUNC_DEF("select_quality", 0, gjs_odm_select_quality), | |
| JS_CFUNC_DEF("disable_main_addon", 0, gjs_odm_disable_main_addon), | |
| JS_CFUNC_DEF("select", 0, gjs_odm_select), | |
| JS_CFUNC_DEF("get_srd", 0, gjs_odm_get_srd), | |
| JS_CFUNC_DEF("in_parent_chain", 0, gjs_odm_in_parent_chain), | |
| JS_CFUNC_DEF("get_chapters", 0, gjs_odm_get_chapters), | |
| }; | |
| #include "../filter_core/filter_session.h" | |
| //we must unload API before the session is destroyed, so not in the finalizer which can be called in gf_sys_close when runtime is destroyeds | |
| void scenejs_unload(JSContext *c, JSValue global_obj) | |
| { | |
| JSValue js_sess = JS_GetPropertyStr(c, global_obj, "__scene_js"); | |
| GF_SCENEJSExt *sjs = JS_GetOpaque(js_sess, scene_class_id); | |
| if (sjs) { | |
| if (sjs->owns_fs_api && sjs->compositor && sjs->compositor->filter) { | |
| gf_fs_unload_script(sjs->compositor->filter->session, NULL); | |
| } | |
| if (sjs->evt_filter.udta) { | |
| if (sjs->owns_fs_api && sjs->compositor) | |
| gf_filter_remove_event_listener(sjs->compositor->filter, &sjs->evt_filter); | |
| sjs->evt_filter.udta = NULL; | |
| } | |
| } | |
| JS_FreeValue(c, js_sess); | |
| } | |
| static void scenejs_finalize(JSRuntime *rt, JSValue obj) | |
| { | |
| GF_SCENEJSExt *sjs = JS_GetOpaque(obj, scene_class_id); | |
| if (!sjs) return; | |
| JS_SetOpaque(obj, NULL); | |
| while (gf_list_count(sjs->storages)) { | |
| GF_Config *cfg = (GF_Config *) gf_list_pop_back(sjs->storages); | |
| gf_cfg_discard_changes(cfg); | |
| gf_cfg_del(cfg); | |
| } | |
| gf_list_del(sjs->storages); | |
| while (gf_list_count(sjs->event_queue)) { | |
| GF_Event *evt = (GF_Event *) gf_list_pop_back(sjs->event_queue); | |
| gf_free(evt); | |
| } | |
| gf_list_del(sjs->event_queue); | |
| gf_mx_del(sjs->event_mx); | |
| /*if we destroy the script context holding the gpac event filter (only one for the time being), remove the filter*/ | |
| JS_FreeValueRT(rt, sjs->evt_fun); | |
| gf_free(sjs); | |
| } | |
| static int js_scene_init(JSContext *c, JSModuleDef *m) | |
| { | |
| GF_JSAPIParam par; | |
| GF_SCENEJSExt *sjs; | |
| GF_SAFEALLOC(sjs, GF_SCENEJSExt); | |
| GF_SceneGraph *scene; | |
| if (!sjs) { | |
| return -1; | |
| } | |
| sjs->storages = gf_list_new(); | |
| sjs->event_queue = gf_list_new(); | |
| sjs->event_mx = gf_mx_new("GPACJSEvt"); | |
| sjs->evt_fun = JS_UNDEFINED; | |
| sjs->scene_obj = JS_UNDEFINED; | |
| sjs->evt_obj = JS_UNDEFINED; | |
| scene = JS_GetContextOpaque(c); | |
| if (!scene) return -1; | |
| if (scene->__reserved_null) { | |
| GF_Node *n = JS_GetContextOpaque(c); | |
| scene = n->sgprivate->scenegraph; | |
| } | |
| if (!scene_class_id) { | |
| JS_NewClassID(&scene_class_id); | |
| JS_NewClass(JS_GetRuntime(c), scene_class_id, &sceneClass); | |
| JS_NewClassID(&odm_class_id); | |
| JS_NewClass(JS_GetRuntime(c), odm_class_id, &odmClass); | |
| } | |
| JSValue proto = JS_NewObjectClass(c, odm_class_id); | |
| JS_SetPropertyFunctionList(c, proto, odm_funcs, countof(odm_funcs)); | |
| JS_SetClassProto(c, odm_class_id, proto); | |
| JS_NewClassID(&gpacevt_class_id); | |
| JS_NewClass(JS_GetRuntime(c), gpacevt_class_id, &gpacEvtClass); | |
| JS_NewClassID(&any_class_id); | |
| JS_NewClass(JS_GetRuntime(c), any_class_id, &anyClass); | |
| JSValue global = JS_GetGlobalObject(c); | |
| sjs->scene_obj = JS_NewObjectClass(c, scene_class_id); | |
| JS_SetPropertyFunctionList(c, sjs->scene_obj, scenejs_funcs, countof(scenejs_funcs)); | |
| JS_SetOpaque(sjs->scene_obj, sjs); | |
| JS_SetPropertyStr(c, global, "__scene_js", JS_DupValue(c, sjs->scene_obj) ); | |
| if (scene->script_action) { | |
| if (scene->script_action(scene->script_action_cbck, GF_JSAPI_OP_GET_COMPOSITOR, scene->RootNode, &par)) { | |
| sjs->compositor = par.compositor; | |
| } | |
| } | |
| if (sjs->compositor && sjs->compositor->filter) { | |
| GF_Err gf_fs_load_js_api(JSContext *c, GF_FilterSession *fs); | |
| GF_FilterSession *fs = sjs->compositor->filter->session; | |
| //don't check error code, this may fail if global JS has been set but the script may still run | |
| if (gf_fs_load_js_api(c, fs) == GF_OK) { | |
| scene->attached_session = fs; | |
| sjs->owns_fs_api = GF_TRUE; | |
| } | |
| } | |
| sjs->evt_obj = JS_NewObjectClass(c, gpacevt_class_id); | |
| JS_SetPropertyFunctionList(c, sjs->evt_obj, scenejs_evt_funcs, countof(scenejs_evt_funcs)); | |
| JS_SetOpaque(sjs->evt_obj, NULL); | |
| JS_SetPropertyStr(c, global, "gpacevt", sjs->evt_obj); | |
| #define DECLARE_CONST(_val) \ | |
| JS_SetPropertyStr(c, global, #_val, JS_NewInt32(c, _val)); | |
| DECLARE_CONST(GF_EVENT_CLICK); | |
| DECLARE_CONST(GF_EVENT_MOUSEUP); | |
| DECLARE_CONST(GF_EVENT_MOUSEDOWN); | |
| DECLARE_CONST(GF_EVENT_MOUSEMOVE); | |
| DECLARE_CONST(GF_EVENT_MOUSEWHEEL); | |
| DECLARE_CONST(GF_EVENT_DBLCLICK); | |
| DECLARE_CONST(GF_EVENT_KEYUP); | |
| DECLARE_CONST(GF_EVENT_KEYDOWN); | |
| DECLARE_CONST(GF_EVENT_TEXTINPUT); | |
| DECLARE_CONST(GF_EVENT_CONNECT); | |
| DECLARE_CONST(GF_EVENT_NAVIGATE_INFO); | |
| DECLARE_CONST(GF_EVENT_NAVIGATE); | |
| DECLARE_CONST(GF_EVENT_DROPFILE); | |
| DECLARE_CONST(GF_EVENT_ADDON_DETECTED); | |
| DECLARE_CONST(GF_EVENT_QUALITY_SWITCHED); | |
| DECLARE_CONST(GF_EVENT_TIMESHIFT_DEPTH); | |
| DECLARE_CONST(GF_EVENT_TIMESHIFT_UPDATE); | |
| DECLARE_CONST(GF_EVENT_TIMESHIFT_OVERFLOW); | |
| DECLARE_CONST(GF_EVENT_TIMESHIFT_UNDERRUN); | |
| DECLARE_CONST(GF_EVENT_QUIT); | |
| DECLARE_CONST(GF_EVENT_MAIN_ADDON_STATE); | |
| DECLARE_CONST(GF_EVENT_SCENE_SIZE); | |
| DECLARE_CONST(GF_NAVIGATE_NONE); | |
| DECLARE_CONST(GF_NAVIGATE_WALK); | |
| DECLARE_CONST(GF_NAVIGATE_FLY); | |
| DECLARE_CONST(GF_NAVIGATE_PAN); | |
| DECLARE_CONST(GF_NAVIGATE_GAME); | |
| DECLARE_CONST(GF_NAVIGATE_SLIDE); | |
| DECLARE_CONST(GF_NAVIGATE_EXAMINE); | |
| DECLARE_CONST(GF_NAVIGATE_ORBIT); | |
| DECLARE_CONST(GF_NAVIGATE_VR); | |
| DECLARE_CONST(GF_NAVIGATE_TYPE_NONE); | |
| DECLARE_CONST(GF_NAVIGATE_TYPE_2D); | |
| DECLARE_CONST(GF_NAVIGATE_TYPE_3D); | |
| JS_FreeValue(c, global); | |
| JS_SetModuleExport(c, m, "scene", sjs->scene_obj); | |
| return 0; | |
| } | |
| void qjs_module_init_scenejs(JSContext *ctx) | |
| { | |
| JSModuleDef *m; | |
| m = JS_NewCModule(ctx, "scenejs", js_scene_init); | |
| if (!m) return; | |
| JS_AddModuleExport(ctx, m, "scene"); | |
| return; | |
| } | |
| #endif | |