Coverage Report

Created: 2025-12-05 07:06

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