1 /*
2 * GPAC - Multimedia Framework C SDK
3 *
4 * Authors: Cyril Concolato
5 * Copyright (c) Telecom ParisTech 2007-2012
6 * All rights reserved
7 *
8 * This file is part of GPAC / Scene Graph sub-project
9 *
10 * GPAC is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
14 *
15 * GPAC is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; see the file COPYING. If not, write to
22 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25
26
27 #include <gpac/internal/scenegraph_dev.h>
28
29 /*base SVG type*/
30 #include <gpac/nodes_svg.h>
31 /*dom events*/
32 #include <gpac/events.h>
33 /*dom text event*/
34 #include <gpac/utf.h>
35
36 #include <gpac/download.h>
37 #include <gpac/network.h>
38 #include <gpac/xml.h>
39
40 #ifndef GPAC_DISABLE_SVG
41
42
43 #ifdef GPAC_CONFIG_ANDROID
44 #ifndef XP_UNIX
45 #define XP_UNIX
46 #endif /* XP_UNIX */
47 #endif
48
49 #ifdef GPAC_HAS_SPIDERMONKEY
50
51 #include <gpac/internal/smjs_api.h>
52 #include <gpac/html5_media.h>
53
54 /***********************************************************
55 *
56 * DOM generic functions being used
57 *
58 ***********************************************************/
59 #ifdef USE_FFDEV_15
60 void dom_element_finalize(JSFreeOp *fop, JSObject *obj);
61 void dom_document_finalize(JSFreeOp *fop, JSObject *obj);
62 #else
63 void dom_element_finalize(JSContext *c, JSObject *obj);
64 void dom_document_finalize(JSContext *c, JSObject *obj);
65 #endif
66
67 void gf_svg_set_attributeNS(GF_Node *n, u32 ns_code, char *name, char *val);
68
69 #define JSVAL_CHECK_STRING(_v) (JSVAL_IS_STRING(_v) || JSVAL_IS_NULL(_v))
70
71 /* including terminal dev for MediaObject */
72
73 /* including Compositor dev for TextureHandler and AudioInput */
74 #include <gpac/internal/compositor_dev.h>
75
76 static GF_HTML_MediaRuntime *html_media_rt = NULL;
77
78 /* Enumeration of the property codes for use in JS Bindings for the the HTML5 MediaError interface */
79 typedef enum
80 {
81 MEDIA_ERROR_PROP_ABORTED = -1,
82 MEDIA_ERROR_PROP_NETWORK = -2,
83 MEDIA_ERROR_PROP_DECODE = -3,
84 MEDIA_ERROR_PROP_SRC_NOT_SUPPORTED = -4,
85 MEDIA_ERROR_PROP_CODE = -5
86 } GF_HTML_MediaErrorPropEnum;
87
88 /* Enumeration of the property codes for use in JS Bindings for the the HTML5 HTMLMediaElement interface */
89 typedef enum
90 {
91 HTML_MEDIA_PROP_SRC = -1,
92 HTML_MEDIA_PROP_CURRENTSRC = -2,
93 HTML_MEDIA_PROP_CROSSORIGIN = -3,
94 HTML_MEDIA_PROP_NETWORKSTATE = -4,
95 HTML_MEDIA_PROP_PRELOAD = -5,
96 HTML_MEDIA_PROP_BUFFERED = -6,
97 HTML_MEDIA_PROP_READYSTATE = -7,
98 HTML_MEDIA_PROP_SEEKING = -8,
99 HTML_MEDIA_PROP_CURRENTTIME = -9,
100 HTML_MEDIA_PROP_DURATION = -10,
101 HTML_MEDIA_PROP_STARTDATE = -11,
102 HTML_MEDIA_PROP_PAUSED = -12,
103 HTML_MEDIA_PROP_DEFAULTPLAYBACKRATE = -13,
104 HTML_MEDIA_PROP_PLAYBACKRATE = -14,
105 HTML_MEDIA_PROP_PLAYED = -15,
106 HTML_MEDIA_PROP_SEEKABLE = -16,
107 HTML_MEDIA_PROP_ENDED = -17,
108 HTML_MEDIA_PROP_AUTOPLAY = -18,
109 HTML_MEDIA_PROP_LOOP = -19,
110 HTML_MEDIA_PROP_MEDIAGROUP = -20,
111 HTML_MEDIA_PROP_CONTROLLER = -21,
112 HTML_MEDIA_PROP_CONTROLS = -22,
113 HTML_MEDIA_PROP_VOLUME = -23,
114 HTML_MEDIA_PROP_MUTED = -24,
115 HTML_MEDIA_PROP_DEFAULTMUTED = -25,
116 HTML_MEDIA_PROP_AUDIOTRACKS = -26,
117 HTML_MEDIA_PROP_VIDEOTRACKS = -27,
118 HTML_MEDIA_PROP_TEXTTRACKS = -28,
119 HTML_MEDIA_PROP_NETWORK_EMPTY = -29,
120 HTML_MEDIA_PROP_NETWORK_IDLE = -30,
121 HTML_MEDIA_PROP_NETWORK_LOADING = -31,
122 HTML_MEDIA_PROP_NETWORK_NO_SOURCE = -32,
123 HTML_MEDIA_PROP_HAVE_NOTHING = -33,
124 HTML_MEDIA_PROP_HAVE_METADATA = -34,
125 HTML_MEDIA_PROP_HAVE_CURRENT_DATA = -35,
126 HTML_MEDIA_PROP_HAVE_FUTURE_DATA = -36,
127 HTML_MEDIA_PROP_HAVE_ENOUGH_DATA = -37,
128 HTML_MEDIA_PROP_ERROR = -38
129 } GF_HTML_MediaEltPropEnum;
130
131 /* Enumeration of the property codes for use in JS Bindings for the the HTML5 HTMLVideoElement interface */
132 typedef enum {
133 HTML_VIDEO_PROP_WIDTH = -1,
134 HTML_VIDEO_PROP_HEIGHT = -2,
135 HTML_VIDEO_PROP_VIDEOWIDTH = -3,
136 HTML_VIDEO_PROP_VIDEOHEIGHT = -4,
137 HTML_VIDEO_PROP_POSTER = -5
138 } GF_HTML_VideoEltPropEnum;
139
140 /* Enumeration of the property codes for use in JS Bindings for the the HTML5 HTMLTrackElement interface */
141 typedef enum {
142 HTML_TRACK_PROP_ID = -1,
143 HTML_TRACK_PROP_KIND = -2,
144 HTML_TRACK_PROP_LABEL = -3,
145 HTML_TRACK_PROP_LANGUAGE = -4,
146 HTML_TRACK_PROP_SELECTED = -5,
147 HTML_TRACK_PROP_ENABLED = -6,
148 HTML_TRACK_PROP_INBANDTYPE = -7
149 } GF_HTML_TrackPropEnum;
150
151 #define HTML_MEDIA_JS_CHECK GF_HTML_MediaElement *me; \
152 GF_Node *n = (GF_Node *)SMJS_GET_PRIVATE(c, obj); \
153 if (!n || (n->sgprivate->tag != TAG_SVG_video && n->sgprivate->tag != TAG_SVG_audio)) \
154 { \
155 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR); \
156 } \
157 me = (GF_HTML_MediaElement *)html_media_element_get_from_node(c, n); \
158 if (!me) { \
159 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR); \
160 }
161
162 #define HTML_MEDIA_JS_START HTML_MEDIA_JS_CHECK
163
164 /* Structure copied over from svg_media.c
165 TODO : fix this - remove the copy */
166 typedef struct
167 {
168 GF_TextureHandler txh;
169 struct _drawable *graph;
170 MFURL txurl;
171 Bool first_frame_fetched;
172 GF_Node *audio;
173 Bool audio_dirty;
174 Bool stop_requested;
175 } SVG_video_stack;
176
177 /* Structure copied over from svg_media.c
178 TODO : fix this - remove the copy */
179 typedef struct
180 {
181 GF_AudioInput input;
182 Bool is_active, is_error;
183 MFURL aurl;
184 } SVG_audio_stack;
185
186 /* Gets the GF_MediaObject associated with this node
187 *
188 * \param n the node either an SVG/HTML5 video or audio element
189 * \return the GF_MediaObject attached to this node or null if none
190 */
gf_html_media_object(GF_Node * n)191 static GF_MediaObject *gf_html_media_object(GF_Node *n)
192 {
193 GF_MediaObject *mo = NULL;
194 if (n->sgprivate->tag == TAG_SVG_video)
195 {
196 SVG_video_stack *video_stack;
197 video_stack = (SVG_video_stack *)n->sgprivate->UserPrivate;
198 mo = video_stack->txh.stream;
199 }
200 else if (n->sgprivate->tag == TAG_SVG_audio)
201 {
202 SVG_audio_stack *audio_stack;
203 audio_stack = (SVG_audio_stack *)n->sgprivate->UserPrivate;
204 mo = audio_stack->input.stream;
205 }
206 return mo;
207 }
208
gf_html_track_init_js(GF_HTML_Track * track,JSContext * c,GF_HTML_TrackList * tracklist)209 static void gf_html_track_init_js(GF_HTML_Track *track, JSContext *c, GF_HTML_TrackList *tracklist)
210 {
211 JSObject *track_obj;
212 track->c = c;
213 if (track->type == HTML_MEDIA_TRACK_TYPE_AUDIO)
214 {
215 track_obj = JS_NewObject(c, &html_media_rt->audioTrackClass._class, NULL, tracklist->_this);
216 }
217 else if (track->type == HTML_MEDIA_TRACK_TYPE_VIDEO)
218 {
219 track_obj = JS_NewObject(c, &html_media_rt->videoTrackClass._class, NULL, tracklist->_this);
220 }
221 else if (track->type == HTML_MEDIA_TRACK_TYPE_TEXT)
222 {
223 track_obj = JS_NewObject(c, &html_media_rt->textTrackClass._class, NULL, tracklist->_this);
224 }
225 else
226 {
227 return;
228 }
229 tracklist->onchange = JSVAL_NULL;
230 tracklist->onaddtrack = JSVAL_NULL;
231 tracklist->onremovetrack = JSVAL_NULL;
232 track->_this = track_obj;
233 SMJS_SET_PRIVATE(c, track->_this, track);
234 }
235
236 /* Not yet used, removed for GCC warning
237 static void gf_html_media_controller_init_js(GF_HTML_MediaController *mc, JSContext *c)
238 {
239 mc->c = c;
240 mc->_this = JS_NewObject(c, &html_media_rt->mediaControllerClass._class, NULL, NULL);
241 SMJS_SET_PRIVATE(c, mc->_this, mc);
242 mc->buffered->c = c;
243 mc->buffered->_this = JS_NewObject(c, &html_media_rt->timeRangesClass._class, NULL, mc->_this);
244 SMJS_SET_PRIVATE(c, mc->buffered->_this, mc->buffered);
245 mc->played->c = c;
246 mc->played->_this = JS_NewObject(c, &html_media_rt->timeRangesClass._class, NULL, mc->_this);
247 SMJS_SET_PRIVATE(c, mc->played->_this, mc->played);
248 mc->seekable->c = c;
249 mc->seekable->_this = JS_NewObject(c, &html_media_rt->timeRangesClass._class, NULL, mc->_this);
250 SMJS_SET_PRIVATE(c, mc->seekable->_this, mc->seekable);
251 }
252 */
253
gf_html_media_element_init_js(GF_HTML_MediaElement * me,JSContext * c,JSObject * node_obj)254 static void gf_html_media_element_init_js(GF_HTML_MediaElement *me, JSContext *c, JSObject *node_obj)
255 {
256 me->c = c;
257 me->_this = JS_NewObject(c, &html_media_rt->htmlMediaElementClass._class, NULL, node_obj);
258 SMJS_SET_PRIVATE(c, me->_this, me);
259
260 me->error.c = c;
261 me->error._this = JS_NewObject(c, &html_media_rt->mediaErrorClass._class, NULL, me->_this);
262 SMJS_SET_PRIVATE(c, me->error._this, &me->error);
263
264 me->audioTracks.c = c;
265 me->audioTracks._this = JS_NewObject(c, &html_media_rt->audioTrackListClass._class, NULL, me->_this);
266 SMJS_SET_PRIVATE(c, me->audioTracks._this, &me->audioTracks);
267
268 me->videoTracks.c = c;
269 me->videoTracks._this = JS_NewObject(c, &html_media_rt->videoTrackListClass._class, NULL, me->_this);
270 SMJS_SET_PRIVATE(c, me->videoTracks._this, &me->videoTracks);
271
272 me->textTracks.c = c;
273 me->textTracks._this = JS_NewObject(c, &html_media_rt->textTrackListClass._class, NULL, me->_this);
274 SMJS_SET_PRIVATE(c, me->textTracks._this, &me->textTracks);
275
276 me->buffered->c = c;
277 me->buffered->_this = JS_NewObject(c, &html_media_rt->timeRangesClass._class, NULL, me->_this);
278 SMJS_SET_PRIVATE(c, me->buffered->_this, me->buffered);
279
280 me->played->c = c;
281 me->played->_this = JS_NewObject(c, &html_media_rt->timeRangesClass._class, NULL, me->_this);
282 SMJS_SET_PRIVATE(c, me->played->_this, me->played);
283
284 me->seekable->c = c;
285 me->seekable->_this = JS_NewObject(c, &html_media_rt->timeRangesClass._class, NULL, me->_this);
286 SMJS_SET_PRIVATE(c, me->seekable->_this, me->seekable);
287 }
288
289 /* Function to browse the tracks in the MediaObject associated with the Media Element and to create appropriate HTML Track objects
290 *
291 * \param c The JavaScript Context to create the new JS object
292 * \param me the HTMLMediaElement to update
293 */
html_media_element_populate_tracks(JSContext * c,GF_HTML_MediaElement * me)294 static void html_media_element_populate_tracks(JSContext *c, GF_HTML_MediaElement *me)
295 {
296 GF_HTML_Track *track;
297 GF_MediaObject *mo;
298 u32 source_id=0;
299 GF_Scene *scene;
300 u32 i, count;
301
302 if (!me || !me->node) {
303 return;
304 }
305 mo = gf_html_media_object(me->node);
306 if (!mo || !mo->odm || !mo->odm->parentscene) {
307 return;
308 }
309 source_id = mo->odm->source_id;
310 scene = mo->odm->parentscene;
311
312 count = gf_list_count(scene->resources);
313 for (i=0; i<count; i++) {
314 char id[256];
315 char *lang = "";
316 GF_ObjectManager *odm = (GF_ObjectManager *)gf_list_get(scene->resources, i);
317 if (odm->source_id != source_id) continue;
318 if (!odm->mo) continue;
319
320 sprintf(id, "track%d", odm->ID);
321 #ifdef FILTER_FIXME
322 #error "set track ID and lang"
323 #endif
324 switch(odm->mo->type) {
325 case GF_MEDIA_OBJECT_VIDEO:
326 if (!html_media_tracklist_has_track(&me->videoTracks, id)) {
327 track = html_media_add_new_track_to_list(&me->videoTracks, HTML_MEDIA_TRACK_TYPE_VIDEO,
328 "video/mp4", GF_TRUE, id, "myKind", "myLabel", lang);
329 gf_html_track_init_js(track, c, &me->videoTracks);
330 }
331 break;
332 case GF_MEDIA_OBJECT_AUDIO:
333 if (!html_media_tracklist_has_track(&me->audioTracks, id)) {
334 track = html_media_add_new_track_to_list(&me->audioTracks, HTML_MEDIA_TRACK_TYPE_AUDIO,
335 "audio/mp4", GF_TRUE, id, "myKind", "myLabel", lang);
336 gf_html_track_init_js(track, c, &me->audioTracks);
337 }
338 break;
339 case GF_MEDIA_OBJECT_TEXT:
340 if (!html_media_tracklist_has_track(&me->textTracks, id)) {
341 track = html_media_add_new_track_to_list(&me->textTracks, HTML_MEDIA_TRACK_TYPE_TEXT,
342 "text/plain", GF_TRUE, id, "myKind", "myLabel", lang);
343 gf_html_track_init_js(track, c, &me->textTracks);
344 }
345 break;
346 }
347 }
348 }
349
350 /* Used to retrieve the structure implementing the GF_HTML_MediaElement interface associated with this node
351 * Usually this is done with the private stack of the node (see gf_node_get_private), but in this case,
352 * the stack already contains the rendering stack SVG_video_stack.
353 * So, we store the structure implementing the GF_HTML_MediaElement interface in the JavaScript context of this node,
354 * as a non enumeratable property named 'gpac_me_impl'
355 *
356 * \param c the global JavaScript context
357 * \param n the audio or video node
358 * \return the GF_HTML_MediaElement associated with this node in the given context
359 */
html_media_element_get_from_node(JSContext * c,GF_Node * n)360 static GF_HTML_MediaElement *html_media_element_get_from_node(JSContext *c, GF_Node *n)
361 {
362 jsval vp;
363 JSObject *me_obj;
364 JSObject *node_obj;
365 GF_HTML_MediaElement *me = NULL;
366
367 if ((n->sgprivate->tag == TAG_SVG_video || n->sgprivate->tag == TAG_SVG_audio) && n->sgprivate->interact && n->sgprivate->interact->js_binding) {
368 node_obj = (JSObject *)n->sgprivate->interact->js_binding->node;
369 if (node_obj) {
370 JS_GetProperty(c, node_obj, "gpac_me_impl", &vp);
371 me_obj = JSVAL_TO_OBJECT(vp);
372 me = (GF_HTML_MediaElement *)SMJS_GET_PRIVATE(c, me_obj);
373 }
374 }
375 return me;
376 }
377
SMJS_FUNCTION(html_media_load)378 static JSBool SMJS_FUNCTION(html_media_load)
379 {
380 SMJS_OBJ
381 if (GF_JS_InstanceOf(c, obj, &html_media_rt->htmlAudioElementClass, NULL) ||
382 GF_JS_InstanceOf(c, obj, &html_media_rt->htmlVideoElementClass, NULL) ||
383 GF_JS_InstanceOf(c, obj, &html_media_rt->htmlMediaElementClass, NULL))
384 {
385 MFURL mfurl;
386 GF_Node *n = (GF_Node *)SMJS_GET_PRIVATE(c, obj);
387 GF_HTML_MediaElement *me = html_media_element_get_from_node(c, n);
388 mfurl.count = 1;
389 mfurl.vals = (SFURL *)gf_malloc(sizeof(SFURL));
390 mfurl.vals[0].url = me->currentSrc;
391 mfurl.vals[0].OD_ID = GF_MEDIA_EXTERNAL_ID;
392 gf_mo_register(n, &mfurl, GF_FALSE, GF_FALSE);
393 gf_free(mfurl.vals);
394 return JS_TRUE;
395 } else {
396 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
397 }
398 }
399
SMJS_FUNCTION(html_media_canPlayType)400 static JSBool SMJS_FUNCTION(html_media_canPlayType)
401 {
402 SMJS_OBJ
403 if (GF_JS_InstanceOf(c, obj, &html_media_rt->htmlAudioElementClass, NULL) ||
404 GF_JS_InstanceOf(c, obj, &html_media_rt->htmlVideoElementClass, NULL) ||
405 GF_JS_InstanceOf(c, obj, &html_media_rt->htmlMediaElementClass, NULL))
406 {
407 return JS_TRUE;
408 } else {
409 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
410 }
411 }
412
SMJS_FUNCTION(html_media_fastSeek)413 static JSBool SMJS_FUNCTION(html_media_fastSeek)
414 {
415 SMJS_OBJ
416 if (GF_JS_InstanceOf(c, obj, &html_media_rt->htmlAudioElementClass, NULL) ||
417 GF_JS_InstanceOf(c, obj, &html_media_rt->htmlVideoElementClass, NULL) ||
418 GF_JS_InstanceOf(c, obj, &html_media_rt->htmlMediaElementClass, NULL))
419 {
420 return JS_TRUE;
421 } else {
422 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
423 }
424 }
425
SMJS_FUNCTION(html_media_addTextTrack)426 static JSBool SMJS_FUNCTION(html_media_addTextTrack)
427 {
428 SMJS_OBJ
429 if (GF_JS_InstanceOf(c, obj, &html_media_rt->htmlAudioElementClass, NULL) ||
430 GF_JS_InstanceOf(c, obj, &html_media_rt->htmlVideoElementClass, NULL) ||
431 GF_JS_InstanceOf(c, obj, &html_media_rt->htmlMediaElementClass, NULL))
432 {
433 return JS_TRUE;
434 } else {
435 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
436 }
437 }
438
html_get_element_class(GF_Node * n)439 void *html_get_element_class(GF_Node *n)
440 {
441 if (!n || !html_media_rt)
442 {
443 return NULL;
444 } else if (n->sgprivate->tag == TAG_SVG_video) {
445 return &html_media_rt->htmlVideoElementClass;
446 } else if (n->sgprivate->tag == TAG_SVG_audio) {
447 return &html_media_rt->htmlAudioElementClass;
448 } else {
449 return NULL;
450 }
451 }
452
453 /* Creates the GF_HTML_MediaElement structure for this node
454 * Store it in the JavaScript context of this node, as a non enumeratable property named 'gpac_me_impl'
455 * see \ref html_media_element_get_from_node for retrieving it
456 *
457 * \param c the global JavaScript context
458 * \param n the audio or video node
459 * \param node_obj the JavaScript object representing the audio or video node
460 */
html_media_element_js_init(JSContext * c,JSObject * node_obj,GF_Node * n)461 void html_media_element_js_init(JSContext *c, JSObject *node_obj, GF_Node *n)
462 {
463 if (n->sgprivate->tag == TAG_SVG_video || n->sgprivate->tag == TAG_SVG_audio) {
464 GF_HTML_MediaElement *me;
465 me = gf_html_media_element_new(n, NULL);
466 gf_html_media_element_init_js(me, c, node_obj);
467 /* To be able to retrieve the Media Element interface from the Node element */
468 JS_DefineProperty(c, node_obj, "gpac_me_impl", OBJECT_TO_JSVAL(me->_this), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
469 }
470 }
471
472 /* Deletes the GF_HTML_MediaElement structure for this node
473 *
474 * \param c the global JavaScript context
475 * \param n the audio or video node
476 */
html_media_element_js_finalize(JSContext * c,GF_Node * n)477 void html_media_element_js_finalize(JSContext *c, GF_Node *n)
478 {
479 GF_HTML_MediaElement *me = html_media_element_get_from_node(c, n);
480 /* TODO: finalize child objects */
481 gf_html_media_element_del(me);
482 }
483
484 /* Gets The EventTarget and SceneGraph from the GF_HTML_MediaElement structure for a audio/video JS node
485 *
486 * \param c the global JavaScript context
487 * \param obj the JS object representing the HTMLAudioElement or HTMLVideoElement
488 * \param target the GF_DOMEventTarget object
489 * \param sg the scene graph
490 */
gf_html_media_get_event_target(JSContext * c,JSObject * obj,GF_DOMEventTarget ** target,GF_SceneGraph ** sg)491 void gf_html_media_get_event_target(JSContext *c, JSObject *obj, GF_DOMEventTarget **target, GF_SceneGraph **sg) {
492 if (!sg || !target || !html_media_rt) return;
493 if (GF_JS_InstanceOf(c, obj, &html_media_rt->htmlVideoElementClass, NULL) ||
494 GF_JS_InstanceOf(c, obj, &html_media_rt->htmlAudioElementClass, NULL) ) {
495 GF_Node *n = (GF_Node *)SMJS_GET_PRIVATE(c, obj);
496 *target = gf_dom_event_get_target_from_node(n);
497 *sg = n->sgprivate->scenegraph;
498 } else {
499 *target = NULL;
500 *sg = NULL;
501 }
502 }
503
504 static SMJS_FUNC_PROP_GET(html_media_error_get_code)
505 if (GF_JS_InstanceOf(c, obj, &html_media_rt->mediaErrorClass, NULL))
506 {
507 GF_HTML_MediaError *error = (GF_HTML_MediaError*)SMJS_GET_PRIVATE(c, obj);
508 if (!SMJS_ID_IS_INT(id)) {
509 return JS_TRUE;
510 }
511 switch (SMJS_ID_TO_INT(id))
512 {
513 case MEDIA_ERROR_PROP_ABORTED:
514 *vp = INT_TO_JSVAL(MEDIA_ERR_ABORTED);
515 return JS_TRUE;
516 case MEDIA_ERROR_PROP_NETWORK:
517 *vp = INT_TO_JSVAL(MEDIA_ERR_NETWORK);
518 return JS_TRUE;
519 case MEDIA_ERROR_PROP_DECODE:
520 *vp = INT_TO_JSVAL(MEDIA_ERR_DECODE);
521 return JS_TRUE;
522 case MEDIA_ERROR_PROP_SRC_NOT_SUPPORTED:
523 *vp = INT_TO_JSVAL(MEDIA_ERR_SRC_NOT_SUPPORTED);
524 return JS_TRUE;
525 case MEDIA_ERROR_PROP_CODE:
526 *vp = INT_TO_JSVAL(error->code);
527 return JS_TRUE;
528 }
529 return JS_TRUE;
530 } else {
531 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
532 }
533 }
534
535 static SMJS_FUNC_PROP_GET(html_media_get_error)
536 HTML_MEDIA_JS_START
537 *vp = OBJECT_TO_JSVAL(me->error._this);
538 return JS_TRUE;
539 }
540
541 static SMJS_FUNC_PROP_GET(html_media_get_src)
542 GF_FieldInfo info;
543 HTML_MEDIA_JS_START
544 if (gf_node_get_attribute_by_name(n, "href", GF_XMLNS_XLINK, GF_TRUE, GF_TRUE, &info)==GF_OK) {
545 char *szAtt = gf_svg_dump_attribute(n, &info);
546 *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, szAtt) );
547 if (szAtt) gf_free(szAtt);
548 }
549 return JS_TRUE;
550 }
551
552 static SMJS_FUNC_PROP_SET(html_media_set_src)
553 HTML_MEDIA_JS_START
554 if (JSVAL_CHECK_STRING(*vp)) {
555 char *str = SMJS_CHARS(c, *vp);
556 gf_svg_set_attributeNS(n, GF_XMLNS_XLINK, "href", str);
557 return JS_TRUE;
558 } else {
559 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
560 }
561 }
562
563 static SMJS_FUNC_PROP_GET(html_media_get_cors)
564 GF_FieldInfo info;
565 HTML_MEDIA_JS_START
566 if (gf_node_get_attribute_by_name(n, "crossorigin", GF_XMLNS_SVG, GF_TRUE, GF_TRUE, &info)==GF_OK) {
567 char *szAtt = gf_svg_dump_attribute(n, &info);
568 *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, szAtt) );
569 if (szAtt) gf_free(szAtt);
570 }
571 return JS_TRUE;
572 }
573
574 static SMJS_FUNC_PROP_SET(html_media_set_cors)
575 HTML_MEDIA_JS_START
576 if (JSVAL_CHECK_STRING(*vp)) {
577 char *str = SMJS_CHARS(c, *vp);
578 gf_svg_set_attributeNS(n, GF_XMLNS_SVG, "crossorigin", str);
579 return JS_TRUE;
580 } else {
581 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
582 }
583 }
584
585 void gf_odm_collect_buffer_info(void *net, GF_ObjectManager *odm, GF_DOMMediaEvent *media_event, u32 *min_time, u32 *min_buffer);
586
587 static void html_media_get_states(GF_MediaObject *mo, GF_HTML_MediaReadyState *readyState, GF_HTML_NetworkState *networkState)
588 {
589 GF_DOMMediaEvent media_event;
590 #ifdef FILTER_FIXME
591 u32 min_time=0;
592 #endif
593 u32 min_buffer=0;
594 if (!mo) return;
595
596 if (!mo->odm) {
597 *readyState = HAVE_NOTHING;
598 *networkState = NETWORK_EMPTY;
599 return;
600 }
601
602 memset(&media_event, 0, sizeof(GF_DOMMediaEvent));
603 #ifdef FILTER_FIXME
604 gf_odm_collect_buffer_info(mo->odm->net_service, mo->odm, &media_event, &min_time, &min_buffer);
605 #endif
606 //GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTMLMediaAPI] min_time: %d, min_buffer: %d loaded: "LLD"/"LLD"\n",
607 // min_time, min_buffer, media_event.loaded_size, media_event.total_size));
608 if (min_buffer == 0)
609 {
610 *readyState = HAVE_NOTHING;
611 *networkState = NETWORK_EMPTY;
612 }
613 else
614 {
615 if (media_event.loaded_size != 0)
616 {
617 *readyState = HAVE_METADATA;
618 }
619 else if (media_event.loaded_size == media_event.total_size)
620 {
621 *readyState = HAVE_ENOUGH_DATA;
622 }
623 }
624 }
625
626 static SMJS_FUNC_PROP_GET(html_media_get_ready_state)
627 GF_HTML_MediaReadyState readyState = HAVE_NOTHING;
628 GF_HTML_NetworkState networkState = NETWORK_EMPTY;
629 GF_MediaObject *mo = NULL;
630 HTML_MEDIA_JS_START
631 html_media_element_populate_tracks(c, me);
632 mo = gf_html_media_object(n);
633 html_media_get_states(mo, &readyState, &networkState);
634 *vp = INT_TO_JSVAL( readyState );
635 return JS_TRUE;
636 }
637
638 static SMJS_FUNC_PROP_GET(html_media_get_network_state)
639 GF_HTML_MediaReadyState readyState = HAVE_NOTHING;
640 GF_HTML_NetworkState networkState = NETWORK_EMPTY;
641 GF_MediaObject *mo = NULL;
642 HTML_MEDIA_JS_START
643 mo = gf_html_media_object(n);
644 html_media_get_states(mo, &readyState, &networkState);
645 *vp = INT_TO_JSVAL( networkState );
646 return JS_TRUE;
647 }
648
649 static SMJS_FUNC_PROP_GET(html_media_get_const)
650 u32 v = 0;
651 if (GF_JS_InstanceOf(c, obj, &html_media_rt->htmlMediaElementClass, NULL) ||
652 GF_JS_InstanceOf(c, obj, &html_media_rt->htmlVideoElementClass, NULL) ||
653 GF_JS_InstanceOf(c, obj, &html_media_rt->htmlAudioElementClass, NULL))
654 {
655 if (!SMJS_ID_IS_INT(id)) {
656 return JS_TRUE;
657 }
658 switch (SMJS_ID_TO_INT(id)) {
659 case HTML_MEDIA_PROP_NETWORK_EMPTY:
660 v = NETWORK_EMPTY;
661 break;
662 case HTML_MEDIA_PROP_NETWORK_IDLE:
663 v = NETWORK_IDLE;
664 break;
665 case HTML_MEDIA_PROP_NETWORK_LOADING:
666 v = NETWORK_LOADING;
667 break;
668 case HTML_MEDIA_PROP_NETWORK_NO_SOURCE:
669 v = NETWORK_NO_SOURCE;
670 break;
671 case HTML_MEDIA_PROP_HAVE_NOTHING:
672 v = HAVE_NOTHING;
673 break;
674 case HTML_MEDIA_PROP_HAVE_METADATA:
675 v = HAVE_METADATA;
676 break;
677 case HTML_MEDIA_PROP_HAVE_CURRENT_DATA:
678 v = HAVE_CURRENT_DATA;
679 break;
680 case HTML_MEDIA_PROP_HAVE_FUTURE_DATA:
681 v = HAVE_FUTURE_DATA;
682 break;
683 case HTML_MEDIA_PROP_HAVE_ENOUGH_DATA:
684 v = HAVE_ENOUGH_DATA;
685 break;
686 default:
687 return JS_TRUE;
688 }
689 *vp = INT_TO_JSVAL( v );
690 return JS_TRUE;
691 } else {
692 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
693 }
694 }
695
696 static SMJS_FUNC_PROP_GET(html_media_get_preload)
697 GF_FieldInfo info;
698 HTML_MEDIA_JS_START
699 if (gf_node_get_attribute_by_name(n, "preload", GF_XMLNS_SVG, GF_TRUE, GF_TRUE, &info)==GF_OK) {
700 char *szAtt = gf_svg_dump_attribute(n, &info);
701 *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, szAtt) );
702 if (szAtt) gf_free(szAtt);
703 }
704 return JS_TRUE;
705 }
706
707 static SMJS_FUNC_PROP_SET(html_media_set_preload)
708 HTML_MEDIA_JS_START
709 if (JSVAL_CHECK_STRING(*vp)) {
710 char *str = SMJS_CHARS(c, *vp);
711 gf_svg_set_attributeNS(n, GF_XMLNS_SVG, "preload", str);
712 return JS_TRUE;
713 } else {
714 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
715 }
716 }
717
718 static SMJS_FUNC_PROP_GET(html_media_get_buffered)
719 HTML_MEDIA_JS_START
720 *vp = OBJECT_TO_JSVAL( me->buffered->_this );
721 return JS_TRUE;
722 }
723
724 static SMJS_FUNC_PROP_GET(html_media_get_seeking)
725 HTML_MEDIA_JS_START
726 /* TODO: implement it for real */
727 *vp = BOOLEAN_TO_JSVAL( (me->seeking ? JS_TRUE : JS_FALSE) );
728 return JS_TRUE;
729 }
730
731 static SMJS_FUNC_PROP_GET(html_media_get_current_time)
732 u32 time_ms = 0;
733 Double time_s;
734 GF_MediaObject *mo = NULL;
735 HTML_MEDIA_JS_START
736 mo = gf_html_media_object(n);
737 gf_mo_get_object_time(mo, &time_ms);
738 time_s = time_ms / 1000.0;
739 *vp = JS_MAKE_DOUBLE(c, time_s );
740 return JS_TRUE;
741 }
742
743 static GFINLINE Bool ScriptAction(GF_SceneGraph *scene, u32 type, GF_Node *node, GF_JSAPIParam *param)
744 {
745 if (scene->script_action)
746 {
747 return scene->script_action(scene->script_action_cbck, type, node, param);
748 }
749 return GF_FALSE;
750 }
751
752 static SMJS_FUNC_PROP_SET(html_media_set_current_time)
753 double d;
754 GF_JSAPIParam par;
755 HTML_MEDIA_JS_START
756 if (!JSVAL_IS_NUMBER(*vp)) {
757 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
758 }
759 JS_ValueToNumber(c, *vp, &d);
760 par.time = d;
761 ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_TIME, (GF_Node *)n, &par);
762 return JS_TRUE;
763 }
764
765 static SMJS_FUNC_PROP_GET(html_media_get_duration)
766 Double dur;
767 GF_MediaObject *mo = NULL;
768 HTML_MEDIA_JS_START
769 mo = gf_html_media_object(n);
770 dur = gf_mo_get_duration(mo);
771 if (dur <= 0) {
772 *vp = JS_GetNaNValue(c);
773 } else {
774 *vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, dur));
775 }
776 return JS_TRUE;
777 }
778
779 static SMJS_FUNC_PROP_GET(html_media_get_start_date)
780 HTML_MEDIA_JS_START
781 // SpiderMonkey 1.8.5 JSObject * JS_NewDateObject(JSContext *cx, int year, int mon, int mday, int hour, int min, int sec);
782 *vp = JSVAL_NULL;
783 /* TODO */
784 return JS_TRUE;
785 }
786
787 static SMJS_FUNC_PROP_GET(html_media_get_paused)
788 HTML_MEDIA_JS_START
789 *vp =( BOOLEAN_TO_JSVAL( (me->paused ? JS_TRUE : JS_FALSE) ) );
790 return JS_TRUE;
791 }
792
793 static SMJS_FUNC_PROP_GET(html_media_get_default_playback_rate)
794 HTML_MEDIA_JS_START
795 *vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, me->defaultPlaybackRate));
796 return JS_TRUE;
797 }
798
799 static SMJS_FUNC_PROP_SET(html_media_set_default_playback_rate)
800 jsdouble d;
801 HTML_MEDIA_JS_START
802 if (!JSVAL_IS_NUMBER(*vp)) {
803 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
804 }
805 JS_ValueToNumber(c, *vp, &d);
806 me->defaultPlaybackRate = d;
807 return JS_TRUE;
808 }
809
810 static SMJS_FUNC_PROP_GET(html_media_get_playback_rate)
811 Double speed;
812 GF_Node *n = (GF_Node *)SMJS_GET_PRIVATE(c, obj);
813 GF_MediaObject *mo = gf_html_media_object(n);
814 speed = gf_mo_get_current_speed(mo);
815 *vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, speed));
816 return JS_TRUE;
817 }
818
819 static SMJS_FUNC_PROP_SET(html_media_set_playback_rate)
820 jsdouble d;
821 Fixed speed;
822 GF_MediaObject *mo;
823 HTML_MEDIA_JS_START
824 mo = gf_html_media_object(n);
825 if (!JSVAL_IS_NUMBER(*vp)) {
826 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
827 }
828 JS_ValueToNumber(c, *vp, &d);
829 speed = FLT2FIX(d);
830 gf_mo_set_speed(mo, speed);
831 return JS_TRUE;
832 }
833
834 static SMJS_FUNC_PROP_GET(html_media_get_played)
835 HTML_MEDIA_JS_START
836 *vp =( OBJECT_TO_JSVAL( me->played->_this ) );
837 return JS_TRUE;
838 }
839
840 static SMJS_FUNC_PROP_GET(html_media_get_seekable)
841 HTML_MEDIA_JS_START
842 *vp =( OBJECT_TO_JSVAL( me->seekable->_this ) );
843 return JS_TRUE;
844 }
845
846 static SMJS_FUNC_PROP_GET(html_media_get_ended)
847 GF_MediaObject *mo;
848 HTML_MEDIA_JS_START
849 mo = gf_html_media_object(n);
850 *vp = BOOLEAN_TO_JSVAL( gf_mo_is_done(mo) ? JS_TRUE : JS_FALSE);
851 return JS_TRUE;
852 }
853
854 static SMJS_FUNC_PROP_GET(html_media_get_autoplay)
855 GF_FieldInfo info;
856 HTML_MEDIA_JS_START
857 if (gf_node_get_attribute_by_name(n, "autoplay", GF_XMLNS_SVG, GF_TRUE, GF_TRUE, &info)==GF_OK) {
858 char *szAtt = gf_svg_dump_attribute(n, &info);
859 if (!strcmp(szAtt, "true") || !strcmp(szAtt, "TRUE")) {
860 *vp =( BOOLEAN_TO_JSVAL( JS_TRUE ) );
861 } else {
862 *vp =( BOOLEAN_TO_JSVAL( JS_FALSE ) );
863 }
864 if (szAtt) gf_free(szAtt);
865 }
866 return JS_TRUE;
867 }
868
869 static SMJS_FUNC_PROP_SET(html_media_set_autoplay)
870 HTML_MEDIA_JS_START
871 if (JSVAL_CHECK_STRING(*vp)) {
872 char *str = SMJS_CHARS(c, *vp);
873 gf_svg_set_attributeNS(n, GF_XMLNS_SVG, "autoplay", str);
874 return JS_TRUE;
875 } else {
876 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
877 }
878 }
879
880 static SMJS_FUNC_PROP_GET(html_media_get_loop)
881 GF_FieldInfo info;
882 HTML_MEDIA_JS_START
883 /* TODO: gf_mo_get_loop */
884 if (gf_node_get_attribute_by_name(n, "loop", GF_XMLNS_SVG, GF_TRUE, GF_TRUE, &info)==GF_OK) {
885 char *szAtt = gf_svg_dump_attribute(n, &info);
886 if (!strcmp(szAtt, "true") || !strcmp(szAtt, "TRUE")) {
887 *vp =( BOOLEAN_TO_JSVAL( JS_TRUE ) );
888 } else {
889 *vp =( BOOLEAN_TO_JSVAL( JS_FALSE ) );
890 }
891 if (szAtt) gf_free(szAtt);
892 }
893 return JS_TRUE;
894 }
895
896 static SMJS_FUNC_PROP_SET(html_media_set_loop)
897 HTML_MEDIA_JS_START
898 if (JSVAL_CHECK_STRING(*vp)) {
899 char *str = SMJS_CHARS(c, *vp);
900 gf_svg_set_attributeNS(n, GF_XMLNS_SVG, "loop", str);
901 //TODO: use gf_mo_get_loop
902 return JS_TRUE;
903 } else {
904 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
905 }
906 }
907
908 static SMJS_FUNC_PROP_GET(html_media_get_mediagroup)
909 GF_FieldInfo info;
910 HTML_MEDIA_JS_START
911 if (gf_node_get_attribute_by_name(n, "mediagroup", GF_XMLNS_SVG, GF_TRUE, GF_TRUE, &info)==GF_OK) {
912 char *szAtt = gf_svg_dump_attribute(n, &info);
913 *vp =( STRING_TO_JSVAL( JS_NewStringCopyZ(c, szAtt) ) );
914 if (szAtt) gf_free(szAtt);
915 }
916 return JS_TRUE;
917 }
918
919 static SMJS_FUNC_PROP_SET(html_media_set_mediagroup)
920 HTML_MEDIA_JS_START
921 if (JSVAL_CHECK_STRING(*vp)) {
922 char *str = SMJS_CHARS(c, *vp);
923 gf_svg_set_attributeNS(n, GF_XMLNS_SVG, "mediagroup", str);
924 return JS_TRUE;
925 } else {
926 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
927 }
928 }
929
930 static SMJS_FUNC_PROP_GET(html_media_get_controller)
931 HTML_MEDIA_JS_START
932 if (me->controller) {
933 *vp = OBJECT_TO_JSVAL( me->controller->_this );
934 } else {
935 *vp = JSVAL_NULL;
936 }
937 return JS_TRUE;
938 }
939
940 static SMJS_FUNC_PROP_SET(html_media_set_controller)
941 HTML_MEDIA_JS_START
942 if (JSVAL_IS_OBJECT(*vp)) {
943 me->controller = (GF_HTML_MediaController *)SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(*vp));
944 return JS_TRUE;
945 } else {
946 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
947 }
948 }
949
950 static SMJS_FUNC_PROP_GET(html_media_get_controls)
951 GF_FieldInfo info;
952 HTML_MEDIA_JS_START
953 if (gf_node_get_attribute_by_name(n, "controls", GF_XMLNS_SVG, GF_TRUE, GF_TRUE, &info)==GF_OK) {
954 char *szAtt = gf_svg_dump_attribute(n, &info);
955 if (!strcmp(szAtt, "true") || !strcmp(szAtt, "TRUE")) {
956 *vp =( BOOLEAN_TO_JSVAL( JS_TRUE ) );
957 } else {
958 *vp =( BOOLEAN_TO_JSVAL( JS_FALSE ) );
959 }
960 if (szAtt) gf_free(szAtt);
961 }
962 return JS_TRUE;
963 }
964
965 static SMJS_FUNC_PROP_SET(html_media_set_controls)
966 HTML_MEDIA_JS_START
967 if (JSVAL_CHECK_STRING(*vp)) {
968 char *str = SMJS_CHARS(c, *vp);
969 gf_svg_set_attributeNS(n, GF_XMLNS_SVG, "controls", str);
970 return JS_TRUE;
971 } else {
972 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
973 }
974 }
975
976 static SVG_audio_stack *html_media_get_audio_stack(GF_Node *n) {
977 SVG_video_stack *video_stack = NULL;
978 SVG_audio_stack *audio_stack = NULL;
979 if (n->sgprivate->tag == TAG_SVG_video) {
980 video_stack = (SVG_video_stack *)n->sgprivate->UserPrivate;
981 if (video_stack->audio) {
982 audio_stack = (SVG_audio_stack *)video_stack->audio->sgprivate->UserPrivate;
983 }
984 } else if (n->sgprivate->tag == TAG_SVG_audio) {
985 audio_stack = (SVG_audio_stack *)n->sgprivate->UserPrivate;
986 }
987 return audio_stack;
988 }
989
990 static SMJS_FUNC_PROP_GET(html_media_get_volume)
991 Fixed volume = FIX_ONE;
992 SVG_audio_stack *audio_stack = NULL;
993 HTML_MEDIA_JS_CHECK
994 audio_stack = html_media_get_audio_stack(n);
995 if (audio_stack) {
996 audio_stack->input.input_ifce.GetChannelVolume(&audio_stack->input.input_ifce.callback, &volume);
997 }
998 *vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT(volume)));
999 return JS_TRUE;
1000 }
1001
1002 static SMJS_FUNC_PROP_SET(html_media_set_volume)
1003 jsdouble volume;
1004 SVG_audio_stack *audio_stack = NULL;
1005 HTML_MEDIA_JS_CHECK
1006 audio_stack = html_media_get_audio_stack(n);
1007 if (audio_stack) {
1008 JS_ValueToNumber(c, *vp, &volume);
1009 audio_stack->input.intensity = FLT2FIX(volume);
1010 return JS_TRUE;
1011 } else {
1012 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1013 }
1014 }
1015
1016 static SMJS_FUNC_PROP_GET(html_media_get_muted)
1017 SVG_audio_stack *audio_stack = NULL;
1018 HTML_MEDIA_JS_CHECK
1019 audio_stack = html_media_get_audio_stack(n);
1020 if (audio_stack) {
1021 *vp = BOOLEAN_TO_JSVAL(audio_stack->input.is_muted ? JS_TRUE : JS_FALSE);
1022 } else {
1023 *vp =( BOOLEAN_TO_JSVAL( JS_FALSE ) );
1024 }
1025 return JS_TRUE;
1026 }
1027
1028 static SMJS_FUNC_PROP_SET(html_media_set_muted)
1029 SVG_audio_stack *audio_stack = NULL;
1030 HTML_MEDIA_JS_CHECK
1031 audio_stack = html_media_get_audio_stack(n);
1032 if (audio_stack) {
1033 audio_stack->input.is_muted = (JSVAL_TO_BOOLEAN(*vp) == JS_TRUE ? GF_TRUE : GF_FALSE);
1034 return JS_TRUE;
1035 } else {
1036 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1037 }
1038 }
1039
1040 static SMJS_FUNC_PROP_GET(html_media_get_default_muted)
1041 GF_FieldInfo info;
1042 HTML_MEDIA_JS_START
1043 if (gf_node_get_attribute_by_name(n, "muted", GF_XMLNS_SVG, GF_TRUE, GF_TRUE, &info)==GF_OK) {
1044 char *szAtt = gf_svg_dump_attribute(n, &info);
1045 if (!strcmp(szAtt, "true") || !strcmp(szAtt, "TRUE")) {
1046 *vp =( BOOLEAN_TO_JSVAL( JS_TRUE ) );
1047 } else {
1048 *vp =( BOOLEAN_TO_JSVAL( JS_FALSE ) );
1049 }
1050 if (szAtt) gf_free(szAtt);
1051 } else {
1052 *vp =( BOOLEAN_TO_JSVAL( JS_FALSE ) );
1053 }
1054 return JS_TRUE;
1055 }
1056
1057 static SMJS_FUNC_PROP_SET(html_media_set_default_muted)
1058 HTML_MEDIA_JS_START
1059 if (JSVAL_CHECK_STRING(*vp)) {
1060 char *str = SMJS_CHARS(c, *vp);
1061 gf_svg_set_attributeNS(n, GF_XMLNS_SVG, "muted", str);
1062 return JS_TRUE;
1063 } else {
1064 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1065 }
1066 }
1067
1068 static SMJS_FUNC_PROP_GET(html_media_get_audio_tracks)
1069 HTML_MEDIA_JS_START
1070 *vp =( OBJECT_TO_JSVAL( me->audioTracks._this ) );
1071 return JS_TRUE;
1072 }
1073
1074 static SMJS_FUNC_PROP_GET(html_media_get_video_tracks)
1075 HTML_MEDIA_JS_START
1076 *vp =( OBJECT_TO_JSVAL( me->videoTracks._this ) );
1077 return JS_TRUE;
1078 }
1079
1080 static SMJS_FUNC_PROP_GET(html_media_get_text_tracks)
1081 HTML_MEDIA_JS_START
1082 *vp =( OBJECT_TO_JSVAL( me->textTracks._this ) );
1083 return JS_TRUE;
1084 }
1085
1086 static SMJS_FUNC_PROP_GET(html_time_ranges_get_length)
1087 GF_HTML_MediaTimeRanges *timeranges;
1088 if (!GF_JS_InstanceOf(c, obj, &html_media_rt->timeRangesClass, NULL)) {
1089 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1090 }
1091 timeranges = (GF_HTML_MediaTimeRanges *)SMJS_GET_PRIVATE(c, obj);
1092 *vp = INT_TO_JSVAL( gf_list_count(timeranges->times)/2);
1093 return JS_TRUE;
1094 }
1095
1096 static JSBool SMJS_FUNCTION(html_time_ranges_start)
1097 {
1098 GF_HTML_MediaTimeRanges *timeranges;
1099 SMJS_OBJ
1100 SMJS_ARGS
1101 if ((argc!=1) || !GF_JS_InstanceOf(c, obj, &html_media_rt->timeRangesClass, NULL)) {
1102 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1103 }
1104 timeranges = (GF_HTML_MediaTimeRanges *)SMJS_GET_PRIVATE(c, obj);
1105 if (!timeranges) {
1106 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1107 }
1108 if (JSVAL_IS_INT(argv[0])) {
1109 u32 i = JSVAL_TO_INT(argv[0]);
1110 u64 *start_value = (u64 *)gf_list_get(timeranges->times, 2*i);
1111 if (!start_value) {
1112 return dom_throw_exception(c, GF_DOM_EXC_INDEX_SIZE_ERR);
1113 } else {
1114 SMJS_SET_RVAL(DOUBLE_TO_JSVAL(JS_NewDouble(c, (*start_value)*1.0/timeranges->timescale)));
1115 }
1116 } else {
1117 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1118 }
1119 return JS_TRUE;
1120 }
1121
1122 static JSBool SMJS_FUNCTION(html_time_ranges_end)
1123 {
1124 GF_HTML_MediaTimeRanges *timeranges;
1125 SMJS_OBJ
1126 SMJS_ARGS
1127 if ((argc!=1) || !GF_JS_InstanceOf(c, obj, &html_media_rt->timeRangesClass, NULL)) {
1128 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1129 }
1130 timeranges = (GF_HTML_MediaTimeRanges *)SMJS_GET_PRIVATE(c, obj);
1131 if (!timeranges) {
1132 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1133 }
1134 if (JSVAL_IS_INT(argv[0])) {
1135 u32 i = JSVAL_TO_INT(argv[0]);
1136 u64 *end_value = (u64 *)gf_list_get(timeranges->times, 2*i+1);
1137 if (!end_value) {
1138 return dom_throw_exception(c, GF_DOM_EXC_INDEX_SIZE_ERR);
1139 } else {
1140 SMJS_SET_RVAL(DOUBLE_TO_JSVAL(JS_NewDouble(c, (*end_value)*1.0/timeranges->timescale)));
1141 }
1142 } else {
1143 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1144 }
1145 return JS_TRUE;
1146 }
1147
1148 static Bool html_is_track_list(JSContext *c, JSObject *obj) {
1149 if (GF_JS_InstanceOf(c, obj, &html_media_rt->videoTrackListClass, NULL)) return GF_TRUE;
1150 if (GF_JS_InstanceOf(c, obj, &html_media_rt->audioTrackListClass, NULL)) return GF_TRUE;
1151 if (GF_JS_InstanceOf(c, obj, &html_media_rt->textTrackListClass, NULL)) return GF_TRUE;
1152 return GF_FALSE;
1153 }
1154
1155 static SMJS_FUNC_PROP_GET(html_track_list_get_length)
1156 GF_HTML_TrackList *tracklist;
1157 if (html_is_track_list(c, obj)) {
1158 tracklist = (GF_HTML_TrackList *)SMJS_GET_PRIVATE(c, obj);
1159 if (tracklist) {
1160 *vp = INT_TO_JSVAL( gf_list_count(tracklist->tracks) );
1161 return JS_TRUE;
1162 } else {
1163 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1164 }
1165 } else {
1166 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1167 }
1168 }
1169
1170 static JSBool SMJS_FUNCTION(html_track_list_get_track_by_id)
1171 {
1172 SMJS_OBJ
1173 SMJS_ARGS
1174 if (html_is_track_list(c, obj)) {
1175 GF_HTML_TrackList *tracklist;
1176 GF_HTML_Track *track;
1177 char *str;
1178 tracklist = (GF_HTML_TrackList *)SMJS_GET_PRIVATE(c, obj);
1179 str = SMJS_CHARS_FROM_STRING(c, JS_ValueToString(c, argv[0]) );
1180 track = html_media_tracklist_get_track(tracklist, str);
1181 SMJS_FREE(c, str);
1182 if (track) {
1183 SMJS_SET_RVAL(OBJECT_TO_JSVAL(track->_this));
1184 return JS_TRUE;
1185 } else {
1186 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1187 }
1188 } else {
1189 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1190 }
1191 }
1192
1193 static SMJS_FUNC_PROP_GET(html_track_list_get_property)
1194 if (html_is_track_list(c, obj)) {
1195 GF_HTML_TrackList *tracklist;
1196 s32 index;
1197
1198 tracklist = (GF_HTML_TrackList *)SMJS_GET_PRIVATE(c, obj);
1199 index = SMJS_ID_TO_INT(id);
1200 if (index >= 0 && (u32)index < gf_list_count(tracklist->tracks)) {
1201 GF_HTML_Track *track = (GF_HTML_Track *)gf_list_get(tracklist->tracks, (u32)index);
1202 *vp = OBJECT_TO_JSVAL(track->_this);
1203 }
1204 return JS_TRUE;
1205 } else {
1206 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1207 }
1208 }
1209
1210 static SMJS_FUNC_PROP_GET(html_track_list_get_selected_index)
1211 if (html_is_track_list(c, obj)) {
1212 GF_HTML_TrackList *tracklist = (GF_HTML_TrackList *)SMJS_GET_PRIVATE(c, obj);
1213 *vp = INT_TO_JSVAL(tracklist->selected_index);
1214 return JS_TRUE;
1215 } else {
1216 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1217 }
1218 }
1219
1220 static SMJS_FUNC_PROP_GET(html_track_list_get_onchange)
1221 if (html_is_track_list(c, obj)) {
1222 GF_HTML_TrackList *tracklist = (GF_HTML_TrackList *)SMJS_GET_PRIVATE(c, obj);
1223 if (! JSVAL_IS_NULL(tracklist->onchange)) {
1224 *vp = tracklist->onchange;
1225 } else {
1226 *vp = JSVAL_NULL;
1227 }
1228 return JS_TRUE;
1229 } else {
1230 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1231 }
1232 }
1233
1234 JSBool gf_set_js_eventhandler(JSContext *c, jsval vp, jsval *callbackfuncval);
1235
1236 static SMJS_FUNC_PROP_SET(html_track_list_set_onchange)
1237 if (html_is_track_list(c, obj)) {
1238 GF_HTML_TrackList *tracklist = (GF_HTML_TrackList *)SMJS_GET_PRIVATE(c, obj);
1239 gf_set_js_eventhandler(c, *vp, &tracklist->onchange);
1240 return JS_TRUE;
1241 } else {
1242 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1243 }
1244 }
1245
1246 static SMJS_FUNC_PROP_GET(html_track_list_get_onaddtrack)
1247 if (html_is_track_list(c, obj)) {
1248 GF_HTML_TrackList *tracklist = (GF_HTML_TrackList *)SMJS_GET_PRIVATE(c, obj);
1249 if (! JSVAL_IS_NULL(tracklist->onaddtrack)) {
1250 *vp = tracklist->onaddtrack;
1251 } else {
1252 *vp = JSVAL_NULL;
1253 }
1254 return JS_TRUE;
1255 } else {
1256 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1257 }
1258 }
1259
1260 static SMJS_FUNC_PROP_SET(html_track_list_set_onaddtrack)
1261 if (html_is_track_list(c, obj)) {
1262 GF_HTML_TrackList *tracklist = (GF_HTML_TrackList *)SMJS_GET_PRIVATE(c, obj);
1263 gf_set_js_eventhandler(c, *vp, &tracklist->onaddtrack);
1264 return JS_TRUE;
1265 } else {
1266 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1267 }
1268 }
1269
1270 static SMJS_FUNC_PROP_GET(html_track_list_get_onremovetrack)
1271 if (html_is_track_list(c, obj)) {
1272 GF_HTML_TrackList *tracklist = (GF_HTML_TrackList *)SMJS_GET_PRIVATE(c, obj);
1273 if (! JSVAL_IS_NULL(tracklist->onremovetrack) ) {
1274 *vp = tracklist->onremovetrack;
1275 } else {
1276 *vp = JSVAL_NULL;
1277 }
1278 return JS_TRUE;
1279 } else {
1280 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1281 }
1282 }
1283
1284 static SMJS_FUNC_PROP_SET(html_track_list_set_onremovetrack)
1285 if (html_is_track_list(c, obj)) {
1286 GF_HTML_TrackList *tracklist = (GF_HTML_TrackList *)SMJS_GET_PRIVATE(c, obj);
1287 gf_set_js_eventhandler(c, *vp, &tracklist->onremovetrack);
1288 return JS_TRUE;
1289 } else {
1290 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1291 }
1292 }
1293
1294 static SMJS_FUNC_PROP_GET(html_track_get_property)
1295 if (html_is_track_list(c, obj)) {
1296 GF_HTML_Track *track = (GF_HTML_Track *)SMJS_GET_PRIVATE(c, obj);
1297 if (!SMJS_ID_IS_INT(id)) {
1298 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1299 }
1300 switch (SMJS_ID_TO_INT(id)) {
1301 case HTML_TRACK_PROP_ID:
1302 *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, track->id ? track->id : "") );
1303 return JS_TRUE;
1304 case HTML_TRACK_PROP_KIND:
1305 *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, track->kind ? track->kind : "") );
1306 return JS_TRUE;
1307 case HTML_TRACK_PROP_LABEL:
1308 *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, track->label ? track->label : "") );
1309 return JS_TRUE;
1310 case HTML_TRACK_PROP_LANGUAGE:
1311 *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, track->language ? track->language : "") );
1312 return JS_TRUE;
1313 case HTML_TRACK_PROP_INBANDTYPE:
1314 if (GF_JS_InstanceOf(c, obj, &html_media_rt->textTrackClass, NULL)) {
1315 *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, track->mime ? track->mime : "") );
1316 return JS_TRUE;
1317 }
1318 case HTML_TRACK_PROP_SELECTED:
1319 if (GF_JS_InstanceOf(c, obj, &html_media_rt->videoTrackClass, NULL)) {
1320 *vp = BOOLEAN_TO_JSVAL(track->enabled_or_selected ? JS_TRUE : JS_FALSE);
1321 }
1322 return JS_TRUE;
1323 case HTML_TRACK_PROP_ENABLED:
1324 if (GF_JS_InstanceOf(c, obj, &html_media_rt->audioTrackClass, NULL)) {
1325 *vp = BOOLEAN_TO_JSVAL(track->enabled_or_selected ? JS_TRUE : JS_FALSE);
1326 }
1327 return JS_TRUE;
1328 }
1329 return JS_TRUE;
1330 } else {
1331 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1332 }
1333 }
1334
1335 static SMJS_FUNC_PROP_SET(html_track_set_property)
1336 if (html_is_track_list(c, obj)) {
1337 GF_HTML_Track *track = (GF_HTML_Track *)SMJS_GET_PRIVATE(c, obj);
1338 if (!SMJS_ID_IS_INT(id)) {
1339 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1340 }
1341 switch (SMJS_ID_TO_INT(id)) {
1342 case HTML_TRACK_PROP_SELECTED:
1343 if (GF_JS_InstanceOf(c, obj, &html_media_rt->videoTrackClass, NULL))
1344 {
1345 track->enabled_or_selected = (JSVAL_TO_BOOLEAN(*vp) == JS_TRUE ? GF_TRUE : GF_FALSE);
1346 }
1347 return JS_TRUE;
1348 case HTML_TRACK_PROP_ENABLED:
1349 if (GF_JS_InstanceOf(c, obj, &html_media_rt->audioTrackClass, NULL))
1350 {
1351 track->enabled_or_selected = (JSVAL_TO_BOOLEAN(*vp) == JS_TRUE ? GF_TRUE : GF_FALSE);
1352 }
1353 return JS_TRUE;
1354 }
1355 return JS_TRUE;
1356 } else {
1357 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1358 }
1359 }
1360
1361 static SMJS_FUNC_PROP_GET(html_video_get_property)
1362 if (GF_JS_InstanceOf(c, obj, &html_media_rt->htmlVideoElementClass, NULL))
1363 {
1364 GF_FieldInfo info;
1365 SVG_video_stack *video;
1366 GF_Node *n = (GF_Node *)SMJS_GET_PRIVATE(c, obj);
1367 if (!n) return JS_TRUE;
1368
1369 video = (SVG_video_stack *)n->sgprivate->UserPrivate;
1370 if (!SMJS_ID_IS_INT(id)) {
1371 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1372 }
1373 switch (SMJS_ID_TO_INT(id)) {
1374 case HTML_VIDEO_PROP_WIDTH:
1375 if (gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_width, GF_TRUE, GF_TRUE, &info)==GF_OK) {
1376 SVG_Length *length;
1377 length = (SVG_Length *)info.far_ptr;
1378 *vp = INT_TO_JSVAL( FIX2FLT(length->value) );
1379 }
1380 return JS_TRUE;
1381 case HTML_VIDEO_PROP_HEIGHT:
1382 if (gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_height, GF_TRUE, GF_TRUE, &info)==GF_OK) {
1383 SVG_Length *length;
1384 length = (SVG_Length *)info.far_ptr;
1385 *vp = INT_TO_JSVAL( FIX2FLT(length->value) );
1386 }
1387 return JS_TRUE;
1388 case HTML_VIDEO_PROP_VIDEOWIDTH:
1389 *vp = INT_TO_JSVAL( video->txh.width );
1390 return JS_TRUE;
1391 case HTML_VIDEO_PROP_VIDEOHEIGHT:
1392 *vp = INT_TO_JSVAL( video->txh.height );
1393 return JS_TRUE;
1394 case HTML_VIDEO_PROP_POSTER:
1395 if (gf_node_get_attribute_by_name(n, "poster", GF_XMLNS_SVG, GF_TRUE, GF_TRUE, &info)==GF_OK) {
1396 char *szAtt = gf_svg_dump_attribute(n, &info);
1397 *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, szAtt) );
1398 if (szAtt) gf_free(szAtt);
1399 }
1400 return JS_TRUE;
1401 }
1402 return JS_TRUE;
1403 } else {
1404 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1405 }
1406 }
1407
1408 static SMJS_FUNC_PROP_SET(html_video_set_property)
1409 if (GF_JS_InstanceOf(c, obj, &html_media_rt->htmlVideoElementClass, NULL))
1410 {
1411 GF_Node *n = (GF_Node *)SMJS_GET_PRIVATE(c, obj);
1412 if (!SMJS_ID_IS_INT(id)) {
1413 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1414 }
1415 switch (SMJS_ID_TO_INT(id)) {
1416 case HTML_VIDEO_PROP_WIDTH:
1417 /* TODO: change value and dirty node */
1418 return JS_TRUE;
1419 case HTML_VIDEO_PROP_HEIGHT:
1420 /* TODO: change value and dirty node */
1421 return JS_TRUE;
1422 case HTML_VIDEO_PROP_POSTER:
1423 {
1424 char *str = SMJS_CHARS_FROM_STRING(c, JS_ValueToString(c, *vp) );
1425 gf_svg_set_attributeNS(n, GF_XMLNS_SVG, "poster", str);
1426 SMJS_FREE(c, str);
1427 return JS_TRUE;
1428 }
1429 }
1430 return JS_TRUE;
1431 } else {
1432 return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
1433 }
1434 }
1435
1436 static JSBool SMJS_FUNCTION(html_media_event_add_listener)
1437 {
1438 JSBool SMJS_FUNCTION_EXT(gf_sg_js_event_add_listener, GF_Node *vrml_node);
1439 return gf_sg_js_event_add_listener(SMJS_CALL_ARGS, NULL);
1440 }
1441
1442 static JSBool SMJS_FUNCTION(html_media_event_remove_listener)
1443 {
1444 JSBool SMJS_FUNCTION_EXT(gf_sg_js_event_remove_listener, GF_Node *vrml_node);
1445 return gf_sg_js_event_remove_listener(SMJS_CALL_ARGS, NULL);
1446 }
1447
1448 static JSBool SMJS_FUNCTION(html_media_event_dispatch)
1449 {
1450 return JS_TRUE;
1451 }
1452
1453 void html_media_source_init_js_api(JSContext *js_ctx, JSObject *global, GF_HTML_MediaRuntime *html_media_rt);
1454
1455 void html_media_js_api_del() {
1456 if (html_media_rt) {
1457 gf_free(html_media_rt);
1458 html_media_rt = NULL;
1459 }
1460 }
1461
1462 void html_media_init_js_api(GF_SceneGraph *scene) {
1463 /* HTML Media Element */
1464 if (!html_media_rt) {
1465 GF_SAFEALLOC(html_media_rt, GF_HTML_MediaRuntime);
1466
1467 /* Setting up MediaError class (no constructor, no finalizer) */
1468 JS_SETUP_CLASS(html_media_rt->mediaErrorClass, "MediaError", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub_forSetter, JS_FinalizeStub);
1469 {
1470 JSPropertySpec mediaErrorClassProps[] = {
1471 {"MEDIA_ERR_ABORTED", MEDIA_ERROR_PROP_ABORTED, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_error_get_code, 0},
1472 {"MEDIA_ERR_NETWORK", MEDIA_ERROR_PROP_NETWORK, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_error_get_code, 0},
1473 {"MEDIA_ERR_DECODE", MEDIA_ERROR_PROP_DECODE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_error_get_code, 0},
1474 {"MEDIA_ERR_SRC_NOT_SUPPORTED", MEDIA_ERROR_PROP_SRC_NOT_SUPPORTED, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_error_get_code, 0},
1475 {"code", MEDIA_ERROR_PROP_CODE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_error_get_code, 0},
1476 {0, 0, 0, 0, 0}
1477 };
1478 JSFunctionSpec mediaErrorClassFuncs[] = {
1479 SMJS_FUNCTION_SPEC(0, 0, 0)
1480 };
1481 GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &html_media_rt->mediaErrorClass, 0, 0, mediaErrorClassProps, mediaErrorClassFuncs, 0, 0);
1482 GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTMLMediaAPI] MediaError class initialized\n"));
1483 }
1484
1485 /* Setting up TimeRanges class (no constructor, no finalizer) */
1486 JS_SETUP_CLASS(html_media_rt->timeRangesClass, "TimeRanges", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub_forSetter, JS_FinalizeStub);
1487 {
1488 JSPropertySpec timeRangesClassProps[] = {
1489 {"length", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_time_ranges_get_length, 0},
1490 {0, 0, 0, 0, 0}
1491 };
1492 JSFunctionSpec timeRangesClassFuncs[] = {
1493 SMJS_FUNCTION_SPEC("start", html_time_ranges_start, 1),
1494 SMJS_FUNCTION_SPEC("end", html_time_ranges_end, 1),
1495 SMJS_FUNCTION_SPEC(0, 0, 0)
1496 };
1497 GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &html_media_rt->timeRangesClass, 0, 0, timeRangesClassProps, timeRangesClassFuncs, 0, 0);
1498 GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTMLMediaAPI] TimeRanges class initialized\n"));
1499 }
1500
1501 JS_SETUP_CLASS(html_media_rt->audioTrackClass, "AudioTrack", JSCLASS_HAS_PRIVATE, html_track_get_property, html_track_set_property, JS_FinalizeStub);
1502 {
1503 JSPropertySpec audioTrackClassProps[] = {
1504 {"id", HTML_TRACK_PROP_ID, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0},
1505 {"kind", HTML_TRACK_PROP_KIND, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0},
1506 {"label", HTML_TRACK_PROP_LABEL, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0},
1507 {"language", HTML_TRACK_PROP_LANGUAGE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0},
1508 {"enabled", HTML_TRACK_PROP_SELECTED, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0},
1509 {0, 0, 0, 0, 0}
1510 };
1511 JSFunctionSpec audioTrackClassFuncs[] = {
1512 SMJS_FUNCTION_SPEC(0, 0, 0)
1513 };
1514 GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &html_media_rt->audioTrackClass, 0, 0, audioTrackClassProps, audioTrackClassFuncs, 0, 0);
1515 GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTMLMediaAPI] AudioTrack class initialized\n"));
1516 }
1517
1518 JS_SETUP_CLASS(html_media_rt->videoTrackClass, "VideoTrack", JSCLASS_HAS_PRIVATE, html_track_get_property, html_track_set_property, JS_FinalizeStub);
1519 {
1520 JSPropertySpec videoTrackClassProps[] = {
1521 {"id", HTML_TRACK_PROP_ID, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0},
1522 {"kind", HTML_TRACK_PROP_KIND, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0},
1523 {"label", HTML_TRACK_PROP_LABEL, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0},
1524 {"language", HTML_TRACK_PROP_LANGUAGE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0},
1525 {"selected", HTML_TRACK_PROP_SELECTED, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0},
1526 {0, 0, 0, 0, 0}
1527 };
1528 JSFunctionSpec videoTrackClassFuncs[] = {
1529 SMJS_FUNCTION_SPEC(0, 0, 0)
1530 };
1531 GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &html_media_rt->videoTrackClass, 0, 0, videoTrackClassProps, videoTrackClassFuncs, 0, 0);
1532 GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTMLMediaAPI] VideoTrack class initialized\n"));
1533 }
1534
1535 JS_SETUP_CLASS(html_media_rt->textTrackClass, "TextTrack", JSCLASS_HAS_PRIVATE, html_track_get_property, html_track_set_property, JS_FinalizeStub);
1536 {
1537 JSPropertySpec textTrackClassProps[] = {
1538 {"id", HTML_TRACK_PROP_ID, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0},
1539 {"kind", HTML_TRACK_PROP_KIND, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0},
1540 {"label", HTML_TRACK_PROP_LABEL, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0},
1541 {"language", HTML_TRACK_PROP_LANGUAGE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0},
1542 {"inBandMetadataTrackDispatchType", HTML_TRACK_PROP_INBANDTYPE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0},
1543 {0, 0, 0, 0, 0}
1544 };
1545 JSFunctionSpec textTrackClassFuncs[] = {
1546 SMJS_FUNCTION_SPEC(0, 0, 0)
1547 };
1548 GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &html_media_rt->textTrackClass, 0, 0, textTrackClassProps, textTrackClassFuncs, 0, 0);
1549 GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTMLMediaAPI] TextTrack class initialized\n"));
1550 }
1551
1552 JS_SETUP_CLASS(html_media_rt->audioTrackListClass, "AudioTrackList", JSCLASS_HAS_PRIVATE, html_track_list_get_property, JS_PropertyStub_forSetter, JS_FinalizeStub);
1553 {
1554 JSPropertySpec audioTrackListClassProps[] = {
1555 {"length", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_track_list_get_length, 0},
1556 {"onchange", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_track_list_get_onchange, html_track_list_set_onchange},
1557 {"onaddtrack", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_track_list_get_onaddtrack, html_track_list_set_onaddtrack},
1558 {"onremovetrack", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_track_list_get_onremovetrack, html_track_list_set_onremovetrack},
1559 {0, 0, 0, 0, 0}
1560 };
1561 JSFunctionSpec audioTrackListClassFuncs[] = {
1562 SMJS_FUNCTION_SPEC("getTrackById", html_track_list_get_track_by_id, 1),
1563 SMJS_FUNCTION_SPEC(0, 0, 0)
1564 };
1565 GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &html_media_rt->audioTrackListClass, 0, 0, audioTrackListClassProps, audioTrackListClassFuncs, 0, 0);
1566 GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTMLMediaAPI] AudioTrackList class initialized\n"));
1567 }
1568
1569 JS_SETUP_CLASS(html_media_rt->videoTrackListClass, "VideoTrackList", JSCLASS_HAS_PRIVATE, html_track_list_get_property, JS_PropertyStub_forSetter, JS_FinalizeStub);
1570 {
1571 JSPropertySpec videoTrackListClassProps[] = {
1572 {"selectedIndex", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_track_list_get_selected_index, 0},
1573 {"length", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_track_list_get_length, 0},
1574 {"onchange", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_track_list_get_onchange, html_track_list_set_onchange},
1575 {"onaddtrack", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_track_list_get_onaddtrack, html_track_list_set_onaddtrack},
1576 {"onremovetrack", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_track_list_get_onremovetrack, html_track_list_set_onremovetrack},
1577 {0, 0, 0, 0, 0}
1578 };
1579 JSFunctionSpec videoTrackListClassFuncs[] = {
1580 SMJS_FUNCTION_SPEC("getTrackById", html_track_list_get_track_by_id, 1),
1581 SMJS_FUNCTION_SPEC(0, 0, 0)
1582 };
1583 GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &html_media_rt->videoTrackListClass, 0, 0, videoTrackListClassProps, videoTrackListClassFuncs, 0, 0);
1584 GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTMLMediaAPI] VideoTrackList class initialized\n"));
1585 }
1586
1587 JS_SETUP_CLASS(html_media_rt->textTrackListClass, "TextTrackList", JSCLASS_HAS_PRIVATE, html_track_list_get_property, JS_PropertyStub_forSetter, JS_FinalizeStub);
1588 {
1589 JSPropertySpec textTrackListClassProps[] = {
1590 {"length", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_track_list_get_length, 0},
1591 {"onaddtrack", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_track_list_get_onaddtrack, html_track_list_set_onaddtrack},
1592 {"onremovetrack", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_track_list_get_onremovetrack, html_track_list_set_onremovetrack},
1593 {0, 0, 0, 0, 0}
1594 };
1595 JSFunctionSpec textTrackListClassFuncs[] = {
1596 SMJS_FUNCTION_SPEC("getTrackById", html_track_list_get_track_by_id, 1),
1597 SMJS_FUNCTION_SPEC(0, 0, 0)
1598 };
1599 GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &html_media_rt->textTrackListClass, 0, 0, textTrackListClassProps, textTrackListClassFuncs, 0, 0);
1600 GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTMLMediaAPI] TextTrackList class initialized\n"));
1601 }
1602
1603 JS_SETUP_CLASS(html_media_rt->mediaControllerClass, "MediaController", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub_forSetter, JS_FinalizeStub);
1604 {
1605 JSPropertySpec mediaControllerClassProps[] = {
1606 {0, 0, 0, 0, 0}
1607 };
1608 JSFunctionSpec mediaControllerClassFuncs[] = {
1609 SMJS_FUNCTION_SPEC(0, 0, 0)
1610 };
1611 GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &html_media_rt->mediaControllerClass, 0, 0, mediaControllerClassProps, mediaControllerClassFuncs, 0, 0);
1612 GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTMLMediaAPI] MediaController class initialized\n"));
1613 }
1614
1615 JS_SETUP_CLASS(html_media_rt->htmlMediaElementClass, "HTMLMediaElement", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub_forSetter, JS_FinalizeStub);
1616 {
1617 JSPropertySpec htmlMediaElementClassProps[] = {
1618 {"error", HTML_MEDIA_PROP_ERROR, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_error, 0},
1619 {"src", HTML_MEDIA_PROP_SRC, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED , html_media_get_src, html_media_set_src},
1620 {"currentSrc", HTML_MEDIA_PROP_CURRENTSRC, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_src, 0},
1621 {"crossOrigin", HTML_MEDIA_PROP_CROSSORIGIN, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_media_get_cors, html_media_set_cors},
1622 {"NETWORK_EMPTY", HTML_MEDIA_PROP_NETWORK_EMPTY, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_const, 0},
1623 {"NETWORK_IDLE", HTML_MEDIA_PROP_NETWORK_IDLE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_const, 0},
1624 {"NETWORK_LOADING", HTML_MEDIA_PROP_NETWORK_LOADING, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_const, 0},
1625 {"NETWORK_NO_SOURCE", HTML_MEDIA_PROP_NETWORK_NO_SOURCE,JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_const, 0},
1626 {"networkState", HTML_MEDIA_PROP_NETWORKSTATE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_network_state, 0},
1627 {"preload", HTML_MEDIA_PROP_PRELOAD, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_media_get_preload, html_media_set_preload},
1628 {"buffered", HTML_MEDIA_PROP_BUFFERED, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_buffered, 0},
1629 {"HAVE_NOTHING", HTML_MEDIA_PROP_HAVE_NOTHING, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_const, 0},
1630 {"HAVE_METADATA", HTML_MEDIA_PROP_HAVE_METADATA, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_const, 0},
1631 {"HAVE_CURRENT_DATA", HTML_MEDIA_PROP_HAVE_CURRENT_DATA, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_const, 0},
1632 {"HAVE_FUTURE_DATA", HTML_MEDIA_PROP_HAVE_FUTURE_DATA, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_const, 0},
1633 {"HAVE_ENOUGH_DATA", HTML_MEDIA_PROP_HAVE_ENOUGH_DATA, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_const, 0},
1634 {"readyState", HTML_MEDIA_PROP_READYSTATE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_ready_state, 0},
1635 {"seeking", HTML_MEDIA_PROP_SEEKING, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_seeking, 0},
1636 {"currentTime", HTML_MEDIA_PROP_CURRENTTIME, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_media_get_current_time, html_media_set_current_time},
1637 {"duration", HTML_MEDIA_PROP_DURATION, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_duration, 0},
1638 {"startDate", HTML_MEDIA_PROP_STARTDATE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_start_date, 0},
1639 {"paused", HTML_MEDIA_PROP_PAUSED, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_paused, 0},
1640 {"defaultPlaybackRate", HTML_MEDIA_PROP_DEFAULTPLAYBACKRATE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_media_get_default_playback_rate, html_media_set_default_playback_rate},
1641 {"playbackRate", HTML_MEDIA_PROP_PLAYBACKRATE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_media_get_playback_rate, html_media_set_playback_rate},
1642 {"played", HTML_MEDIA_PROP_PLAYED, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_played, 0},
1643 {"seekable", HTML_MEDIA_PROP_SEEKABLE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_seekable, 0},
1644 {"ended", HTML_MEDIA_PROP_ENDED, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_ended, 0},
1645 {"autoplay", HTML_MEDIA_PROP_AUTOPLAY, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_media_get_autoplay, html_media_set_autoplay},
1646 {"loop", HTML_MEDIA_PROP_LOOP, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_media_get_loop, html_media_set_loop},
1647 {"mediaGroup", HTML_MEDIA_PROP_MEDIAGROUP, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_media_get_mediagroup, html_media_set_mediagroup},
1648 {"controller", HTML_MEDIA_PROP_CONTROLLER, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_media_get_controller, html_media_set_controller},
1649 {"controls", HTML_MEDIA_PROP_CONTROLS, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_media_get_controls, html_media_set_controls},
1650 {"volume", HTML_MEDIA_PROP_VOLUME, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_media_get_volume, html_media_set_volume},
1651 {"muted", HTML_MEDIA_PROP_MUTED, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_media_get_muted, html_media_set_muted},
1652 {"defaultMuted", HTML_MEDIA_PROP_DEFAULTMUTED, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, html_media_get_default_muted, html_media_set_default_muted},
1653 {"audioTracks", HTML_MEDIA_PROP_AUDIOTRACKS, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_audio_tracks, 0},
1654 {"videoTracks", HTML_MEDIA_PROP_VIDEOTRACKS, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_video_tracks, 0},
1655 {"textTracks", HTML_MEDIA_PROP_TEXTTRACKS, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, html_media_get_text_tracks, 0},
1656 {0, 0, 0, 0, 0}
1657 };
1658 JSBool SMJS_FUNCTION(svg_udom_smil_pause);
1659 JSBool SMJS_FUNCTION(svg_udom_smil_begin);
1660 JSBool SMJS_FUNCTION(svg_udom_smil_end);
1661 JSFunctionSpec htmlMediaElementClassFuncs[] = {
1662 SMJS_FUNCTION_SPEC("load", html_media_load, 0),
1663 SMJS_FUNCTION_SPEC("canPlayType", html_media_canPlayType, 1),
1664 SMJS_FUNCTION_SPEC("fastSeek", html_media_fastSeek, 1),
1665 SMJS_FUNCTION_SPEC("play", svg_udom_smil_begin, 0),
1666 SMJS_FUNCTION_SPEC("pause", svg_udom_smil_pause, 0),
1667 SMJS_FUNCTION_SPEC("addTextTrack", html_media_addTextTrack, 1),
1668 SMJS_FUNCTION_SPEC("addEventListener", html_media_event_add_listener, 3),
1669 SMJS_FUNCTION_SPEC("removeEventListener", html_media_event_remove_listener, 3),
1670 SMJS_FUNCTION_SPEC("dispatchEvent", html_media_event_dispatch, 1),
1671 SMJS_FUNCTION_SPEC(0, 0, 0)
1672 };
1673 JSObject *elt_proto = dom_js_get_element_proto(scene->svg_js->js_ctx);
1674 GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, elt_proto, &html_media_rt->htmlMediaElementClass, 0, 0, htmlMediaElementClassProps, htmlMediaElementClassFuncs, 0, 0);
1675 GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTMLMediaAPI] HTMLMediaElement class initialized\n"));
1676 }
1677
1678 JS_SETUP_CLASS(html_media_rt->htmlVideoElementClass, "HTMLVideoElement", JSCLASS_HAS_PRIVATE, html_video_get_property, html_video_set_property, dom_element_finalize);
1679 {
1680 JSPropertySpec htmlVideoElementClassProps[] = {
1681 {"width", HTML_VIDEO_PROP_WIDTH, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED , 0, 0},
1682 {"height", HTML_VIDEO_PROP_HEIGHT, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED , 0, 0},
1683 {"videoWidth", HTML_VIDEO_PROP_VIDEOWIDTH, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0},
1684 {"videoHeight", HTML_VIDEO_PROP_VIDEOHEIGHT, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0},
1685 {"poster", HTML_VIDEO_PROP_POSTER, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED , 0, 0},
1686 {0, 0, 0, 0, 0}
1687 };
1688 JSFunctionSpec htmlVideoElementClassFuncs[] = {
1689 SMJS_FUNCTION_SPEC(0, 0, 0)
1690 };
1691 GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, html_media_rt->htmlMediaElementClass._proto, &html_media_rt->htmlVideoElementClass, 0, 0, htmlVideoElementClassProps, htmlVideoElementClassFuncs, 0, 0);
1692 GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTMLMediaAPI] HTMLVideoElement class initialized\n"));
1693 }
1694
1695 JS_SETUP_CLASS(html_media_rt->htmlAudioElementClass, "HTMLAudioElement", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub_forSetter, dom_element_finalize);
1696 {
1697 JSPropertySpec htmlAudioElementClassProps[] = {
1698 {0, 0, 0, 0, 0}
1699 };
1700 JSFunctionSpec htmlAudioElementClassFuncs[] = {
1701 SMJS_FUNCTION_SPEC(0, 0, 0)
1702 };
1703 GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, html_media_rt->htmlMediaElementClass._proto, &html_media_rt->htmlAudioElementClass, 0, 0, htmlAudioElementClassProps, htmlAudioElementClassFuncs, 0, 0);
1704 GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTMLMediaAPI] HTMLAudioElement class initialized\n"));
1705 }
1706 html_media_source_init_js_api(scene->svg_js->js_ctx, scene->svg_js->global, html_media_rt);
1707 }
1708 }
1709 #endif /*GPAC_HAS_SPIDERMONKEY*/
1710
1711 #endif /*GPAC_DISABLE_SVG*/
1712