This source file includes following definitions.
- gf_mse_is_mse_object
- mediasource_get_scenegraph
- gf_mse_get_event_target
- mediasource_sourceBuffer_initjs
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNC_PROP_GET
- SMJS_FUNC_PROP_GET
- SMJS_FUNC_PROP_GET
- SMJS_FUNC_PROP_GET
- SMJS_FUNC_PROP_SET
- SMJS_FUNC_PROP_GET
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- JSVAL_CHECK_STRING
- stricmp
- stricmp
- stricmp
- gf_list_find
- SMJS_FUNC_PROP_SET
- SMJS_FUNC_PROP_SET
- SMJS_FUNCTION
- gf_arraybuffer_new
- gf_arraybuffer_js_new
- gf_arraybuffer_del
- SMJS_FUNCTION
- SMJS_FUNC_PROP_GET
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- html_media_source_init_js_api
#include <gpac/setup.h>
#ifndef GPAC_DISABLE_SVG
#ifdef GPAC_HAS_SPIDERMONKEY
#ifdef GPAC_ANDROID
#ifndef XP_UNIX
#define XP_UNIX
#endif
#endif
#include <gpac/html5_mse.h>
#define JSVAL_CHECK_STRING(_v) (JSVAL_IS_STRING(_v) || JSVAL_IS_NULL(_v))
typedef enum
{
HTML_MEDIASOURCE_PROP_SOURCEBUFFERS = -1,
HTML_MEDIASOURCE_PROP_ACTIVESOURCEBUFFERS = -2,
HTML_MEDIASOURCE_PROP_DURATION = -3,
HTML_MEDIASOURCE_PROP_READYSTATE = -4,
HTML_SOURCEBUFFER_PROP_MODE = -5,
HTML_SOURCEBUFFER_PROP_UPDATING = -6,
HTML_SOURCEBUFFER_PROP_BUFFERED = -7,
HTML_SOURCEBUFFER_PROP_TIMESTAMPOFFSET = -8,
HTML_SOURCEBUFFER_PROP_TIMESCALE = -9,
HTML_SOURCEBUFFER_PROP_AUDIOTRACKS = -10,
HTML_SOURCEBUFFER_PROP_VIDEOTRACKS = -11,
HTML_SOURCEBUFFER_PROP_TEXTTRACKS = -12,
HTML_SOURCEBUFFER_PROP_APPENDWINDOWSTART = -13,
HTML_SOURCEBUFFER_PROP_APPENDWINDOWEND = -14,
HTML_SOURCEBUFFERLIST_PROP_LENGTH = -15,
} GF_HTML_MediaSourcePropEnum;
static GF_HTML_MediaRuntime *html_media_rt = NULL;
Bool gf_mse_is_mse_object(JSContext *c, JSObject *obj) {
if (GF_JS_InstanceOf(c, obj, &html_media_rt->mediaSourceClass, NULL) ) return GF_TRUE;
if (GF_JS_InstanceOf(c, obj, &html_media_rt->sourceBufferClass, NULL) ) return GF_TRUE;
if (GF_JS_InstanceOf(c, obj, &html_media_rt->sourceBufferListClass, NULL) ) return GF_TRUE;
return GF_FALSE;
}
static GFINLINE GF_SceneGraph *mediasource_get_scenegraph(JSContext *c)
{
GF_SceneGraph *scene;
JSObject *global = JS_GetGlobalObject(c);
assert(global);
scene = (GF_SceneGraph *)SMJS_GET_PRIVATE(c, global);
assert(scene);
return scene;
}
void gf_mse_get_event_target(JSContext *c, JSObject *obj, GF_DOMEventTarget **target, GF_SceneGraph **sg) {
if (!sg || !target) return;
*sg = mediasource_get_scenegraph(c);
if (GF_JS_InstanceOf(c, obj, &html_media_rt->mediaSourceClass, NULL) ) {
GF_HTML_MediaSource *ms = NULL;
ms = (GF_HTML_MediaSource *)SMJS_GET_PRIVATE(c, obj);
*target = ms->evt_target;
} else if (GF_JS_InstanceOf(c, obj, &html_media_rt->sourceBufferClass, NULL) ) {
GF_HTML_SourceBuffer *sb = NULL;
sb = (GF_HTML_SourceBuffer *)SMJS_GET_PRIVATE(c, obj);
*target = sb->evt_target;
} else if (GF_JS_InstanceOf(c, obj, &html_media_rt->sourceBufferListClass, NULL) ) {
GF_HTML_SourceBufferList *sbl = NULL;
sbl = (GF_HTML_SourceBufferList *)SMJS_GET_PRIVATE(c, obj);
*target = sbl->evt_target;
} else {
*target = NULL;
*sg = NULL;
}
}
static void mediasource_sourceBuffer_initjs(JSContext *c, JSObject *ms_obj, GF_HTML_SourceBuffer *sb)
{
sb->_this = JS_NewObject(c, &html_media_rt->sourceBufferClass._class, 0, 0);
SMJS_SET_PRIVATE(c, sb->_this, sb);
sb->buffered->_this = JS_NewObject(c, &html_media_rt->timeRangesClass._class, NULL, sb->_this);
SMJS_SET_PRIVATE(c, sb->buffered->_this, sb->buffered);
}
#include <gpac/internal/terminal_dev.h>
Bool gf_term_is_type_supported(GF_Terminal *term, const char* mime);
static JSBool SMJS_FUNCTION(mediasource_is_type_supported)
{
SMJS_ARGS
GF_SceneGraph *sg;
GF_JSAPIParam par;
Bool isSupported = GF_TRUE;
char *mime;
if (!argc || !JSVAL_CHECK_STRING(argv[0])) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
mime = SMJS_CHARS(c, argv[0]);
sg = mediasource_get_scenegraph(c);
assert(sg);
if (!strlen(mime)) {
isSupported = GF_FALSE;
} else {
sg->script_action(sg->script_action_cbck, GF_JSAPI_OP_GET_TERM, NULL, &par);
isSupported = gf_term_is_type_supported((GF_Terminal *)par.term, mime);
}
SMJS_SET_RVAL(BOOLEAN_TO_JSVAL(isSupported ? JS_TRUE : JS_FALSE));
SMJS_FREE(c, mime);
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(mediasource_addSourceBuffer)
{
SMJS_OBJ
SMJS_ARGS
GF_HTML_SourceBuffer *sb;
GF_HTML_MediaSource *ms;
const char *mime;
GF_Err e;
u32 exception = 0;
if (!GF_JS_InstanceOf(c, obj, &html_media_rt->mediaSourceClass, NULL) ) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
if (!argc || !JSVAL_CHECK_STRING(argv[0]))
{
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
mime = SMJS_CHARS(c, argv[0]);
if (!strlen(mime)) {
exception = GF_DOM_EXC_INVALID_ACCESS_ERR;
goto exit;
}
ms = (GF_HTML_MediaSource *)SMJS_GET_PRIVATE(c, obj);
if (!ms) {
exception = GF_DOM_EXC_INVALID_ACCESS_ERR;
goto exit;
} else if (ms->readyState != MEDIA_SOURCE_READYSTATE_OPEN) {
exception = GF_DOM_EXC_INVALID_STATE_ERR;
goto exit;
}
assert(ms->service);
sb = gf_mse_source_buffer_new(ms);
assert(sb);
e = gf_mse_source_buffer_load_parser(sb, mime);
if (e == GF_OK) {
gf_mse_mediasource_add_source_buffer(ms, sb);
mediasource_sourceBuffer_initjs(c, obj, sb);
SMJS_SET_RVAL( OBJECT_TO_JSVAL(sb->_this) );
} else {
gf_mse_source_buffer_del(sb);
exception = GF_DOM_EXC_NOT_SUPPORTED_ERR;
}
exit:
if (mime) {
SMJS_FREE(c, (void *)mime);
}
if (exception) {
return dom_throw_exception(c, exception);
} else {
return JS_TRUE;
}
}
static JSBool SMJS_FUNCTION(mediasource_removeSourceBuffer)
{
SMJS_OBJ
SMJS_ARGS
GF_HTML_MediaSource *ms;
GF_HTML_SourceBuffer *sb;
JSObject *sb_obj;
if (!GF_JS_InstanceOf(c, obj, &html_media_rt->mediaSourceClass, NULL) ) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
ms = (GF_HTML_MediaSource *)SMJS_GET_PRIVATE(c, obj);
if (!ms) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
if (!argc || JSVAL_IS_NULL(argv[0]) || !JSVAL_IS_OBJECT(argv[0])) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
sb_obj = JSVAL_TO_OBJECT(argv[0]);
if (!GF_JS_InstanceOf(c, sb_obj, &html_media_rt->sourceBufferClass, NULL) ) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
sb = (GF_HTML_SourceBuffer *)SMJS_GET_PRIVATE(c, sb_obj);
if (!sb) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
} else {
GF_Err e = gf_mse_remove_source_buffer(ms, sb);
if (e == GF_NOT_FOUND) {
return dom_throw_exception(c, GF_DOM_EXC_NOT_FOUND_ERR);
}
}
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(mediasource_endOfStream)
{
SMJS_OBJ
SMJS_ARGS
GF_HTML_MediaSource *ms;
u32 i;
if (!GF_JS_InstanceOf(c, obj, &html_media_rt->mediaSourceClass, NULL) ) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
ms = (GF_HTML_MediaSource *)SMJS_GET_PRIVATE(c, obj);
if (!ms) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
if (ms->readyState != MEDIA_SOURCE_READYSTATE_OPEN) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_STATE_ERR);
}
for (i = 0; i < gf_list_count(ms->sourceBuffers.list); i++) {
GF_HTML_SourceBuffer *sb = (GF_HTML_SourceBuffer *)gf_list_get(ms->sourceBuffers.list, i);
if (sb->updating) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_STATE_ERR);
}
}
if (argc > 0) {
char *error = NULL;
if (!JSVAL_CHECK_STRING(argv[0])) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
error = SMJS_CHARS(c, argv[0]);
if (strcmp(error, "decode") && strcmp(error, "network")) {
SMJS_FREE(c, error);
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
SMJS_FREE(c, error);
}
gf_mse_mediasource_end(ms);
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(media_source_constructor)
{
GF_HTML_MediaSource *p;
SMJS_OBJ_CONSTRUCTOR(&html_media_rt->mediaSourceClass)
if (!GF_JS_InstanceOf(c, obj, &html_media_rt->mediaSourceClass, NULL) ) return JS_TRUE;
p = gf_mse_media_source_new();
p->c = c;
p->_this = obj;
SMJS_SET_PRIVATE(c, obj, p);
p->sourceBuffers._this = JS_NewObject(c, &html_media_rt->sourceBufferListClass._class, 0, 0);
SMJS_SET_PRIVATE(c, p->sourceBuffers._this, &p->sourceBuffers);
p->activeSourceBuffers._this = JS_NewObject(c, &html_media_rt->sourceBufferListClass._class, 0, 0);
SMJS_SET_PRIVATE(c, p->activeSourceBuffers._this, &p->activeSourceBuffers);
SMJS_SET_RVAL( OBJECT_TO_JSVAL(obj) );
return JS_TRUE;
}
static DECL_FINALIZE(media_source_finalize)
GF_HTML_MediaSource *p;
if (!GF_JS_InstanceOf(c, obj, &html_media_rt->mediaSourceClass, NULL) ) return;
p = (GF_HTML_MediaSource *)SMJS_GET_PRIVATE(c, obj);
gf_mse_mediasource_del(p, GF_TRUE);
}
static SMJS_FUNC_PROP_GET(media_source_get_source_buffers)
GF_HTML_MediaSource *p;
if (!GF_JS_InstanceOf(c, obj, &html_media_rt->mediaSourceClass, NULL) ) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
p = (GF_HTML_MediaSource *)SMJS_GET_PRIVATE(c, obj);
if (!p) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
} else {
*vp = OBJECT_TO_JSVAL(p->sourceBuffers._this);
return JS_TRUE;
}
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET(media_source_get_active_source_buffers)
GF_HTML_MediaSource *p;
if (!GF_JS_InstanceOf(c, obj, &html_media_rt->mediaSourceClass, NULL) ) {
return JS_TRUE;
}
p = (GF_HTML_MediaSource *)SMJS_GET_PRIVATE(c, obj);
if (p) {
*vp = OBJECT_TO_JSVAL(p->activeSourceBuffers._this);
return JS_TRUE;
}
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET(media_source_get_ready_state)
GF_HTML_MediaSource *p;
if (!GF_JS_InstanceOf(c, obj, &html_media_rt->mediaSourceClass, NULL) ) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
p = (GF_HTML_MediaSource *)SMJS_GET_PRIVATE(c, obj);
if (!p) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
} else {
switch (p->readyState)
{
case MEDIA_SOURCE_READYSTATE_CLOSED:
*vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, "closed"));
break;
case MEDIA_SOURCE_READYSTATE_OPEN:
*vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, "open"));
break;
case MEDIA_SOURCE_READYSTATE_ENDED:
*vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, "ended"));
break;
}
}
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET(media_source_get_duration)
GF_HTML_MediaSource *p;
if (!GF_JS_InstanceOf(c, obj, &html_media_rt->mediaSourceClass, NULL) ) {
return JS_TRUE;
}
p = (GF_HTML_MediaSource *)SMJS_GET_PRIVATE(c, obj);
if (p) {
if (p->readyState == MEDIA_SOURCE_READYSTATE_CLOSED || p->durationType == DURATION_NAN) {
*vp = JS_GetNaNValue(c);
} else if (p->durationType == DURATION_INFINITY) {
*vp = JS_GetPositiveInfinityValue(c);
} else {
*vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, p->duration));
}
return JS_TRUE;
}
return JS_TRUE;
}
static SMJS_FUNC_PROP_SET(media_source_set_duration)
GF_HTML_MediaSource *ms;
if (!GF_JS_InstanceOf(c, obj, &html_media_rt->mediaSourceClass, NULL)) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
ms = (GF_HTML_MediaSource *)SMJS_GET_PRIVATE(c, obj);
if (!ms) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
} else {
if (ms->readyState != MEDIA_SOURCE_READYSTATE_OPEN) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_STATE_ERR);
} else if (!JSVAL_IS_NUMBER(*vp)) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
} else {
u32 i, count;
count = gf_list_count(ms->sourceBuffers.list);
for (i = 0; i < count; i++) {
GF_HTML_SourceBuffer *sb = (GF_HTML_SourceBuffer *)gf_list_get(ms->sourceBuffers.list, i);
if (sb->updating) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_STATE_ERR);
}
}
{
jsdouble durationValue;
JS_ValueToNumber(c, *vp, &durationValue);
if (durationValue < 0) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
} else {
ms->duration = durationValue;
ms->durationType = DURATION_VALUE;
}
}
}
}
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET( sourcebufferlist_getProperty)
GF_HTML_SourceBufferList *p;
u32 count;
s32 idx;
if (!GF_JS_InstanceOf(c, obj, &html_media_rt->sourceBufferListClass, NULL) )
{
return JS_TRUE;
}
p = (GF_HTML_SourceBufferList *)SMJS_GET_PRIVATE(c, obj);
if (p) {
count = gf_list_count(p->list);
if (!SMJS_ID_IS_INT(id)) return JS_TRUE;
switch (SMJS_ID_TO_INT(id)) {
case HTML_SOURCEBUFFERLIST_PROP_LENGTH:
*vp = INT_TO_JSVAL(count);
break;
default:
idx = SMJS_ID_TO_INT(id);
if ((idx<0) || ((u32) idx>=count)) {
*vp = JSVAL_VOID;
return JS_TRUE;
} else {
GF_HTML_SourceBuffer *sb;
sb = (GF_HTML_SourceBuffer *)gf_list_get(p->list, idx);
*vp = OBJECT_TO_JSVAL(sb->_this);
return JS_TRUE;
}
}
}
return JS_TRUE;
}
#define SB_BASIC_CHECK \
GF_HTML_SourceBuffer *sb; \
if (!GF_JS_InstanceOf(c, obj, &html_media_rt->sourceBufferClass, NULL) ) \
{ \
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR); \
}\
sb = (GF_HTML_SourceBuffer *)SMJS_GET_PRIVATE(c, obj);\
if (!sb)\
{\
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR); \
}
#define SB_UPDATING_CHECK \
SB_BASIC_CHECK \
\
if (gf_list_find(sb->mediasource->sourceBuffers.list, sb) < 0)\
{\
return dom_throw_exception(c, GF_DOM_EXC_INVALID_STATE_ERR); \
} \
if (sb->updating)\
{\
return dom_throw_exception(c, GF_DOM_EXC_INVALID_STATE_ERR); \
}
static JSBool SMJS_FUNCTION(sourcebuffer_appendBuffer)
{
SMJS_OBJ
SMJS_ARGS
GF_HTML_ArrayBuffer *ab;
JSObject *js_ab;
SB_UPDATING_CHECK
if (sb->mediasource->readyState == MEDIA_SOURCE_READYSTATE_CLOSED) {
return JS_TRUE;
} else if (sb->mediasource->readyState == MEDIA_SOURCE_READYSTATE_ENDED) {
gf_mse_mediasource_open(sb->mediasource, NULL);
}
if (!argc || JSVAL_IS_NULL(argv[0]) || !JSVAL_IS_OBJECT(argv[0])) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
js_ab = JSVAL_TO_OBJECT(argv[0]);
if (!GF_JS_InstanceOf(c, js_ab, &html_media_rt->arrayBufferClass, NULL) )
{
return dom_throw_exception(c, GF_DOM_EXC_TYPE_MISMATCH_ERR);
}
ab = (GF_HTML_ArrayBuffer *)SMJS_GET_PRIVATE(c, js_ab);
if (!ab->length)
{
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
gf_mse_source_buffer_append_arraybuffer(sb, ab);
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(sourcebuffer_appendStream)
{
SMJS_OBJ
SB_UPDATING_CHECK
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(sourcebuffer_abort)
{
SMJS_OBJ
SB_BASIC_CHECK
if (gf_list_find(sb->mediasource->sourceBuffers.list, sb) < 0) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_STATE_ERR);
}
if (sb->mediasource->readyState != MEDIA_SOURCE_READYSTATE_OPEN) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_STATE_ERR);
}
if (gf_mse_source_buffer_abort(sb) != GF_OK) {
return JS_TRUE;
}
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(sourcebuffer_remove)
{
SMJS_OBJ
SMJS_ARGS
jsdouble start, end;
SB_UPDATING_CHECK
if (argc < 2 || !JSVAL_IS_NUMBER(argv[0]) || !JSVAL_IS_NUMBER(argv[1])) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
JS_ValueToNumber(c, argv[0], &start);
JS_ValueToNumber(c, argv[1], &end);
if (start < 0 || start >= end) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
if (sb->mediasource->readyState != MEDIA_SOURCE_READYSTATE_OPEN) {
gf_mse_mediasource_open(sb->mediasource, NULL);
}
gf_mse_remove(sb, start, end);
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET(sourceBuffer_get_mode)
SB_BASIC_CHECK
if (sb->append_mode == MEDIA_SOURCE_APPEND_MODE_SEGMENTS) {
*vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, "segments"));
} else if (sb->append_mode == MEDIA_SOURCE_APPEND_MODE_SEQUENCE) {
*vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, "sequence"));
}
return JS_TRUE;
}
static SMJS_FUNC_PROP_SET(sourceBuffer_set_mode)
char *smode = NULL;
GF_HTML_MediaSource_AppendMode mode = MEDIA_SOURCE_APPEND_MODE_SEGMENTS;
SB_BASIC_CHECK
if (!JSVAL_CHECK_STRING(*vp)) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
smode = SMJS_CHARS(c, *vp);
if (stricmp(smode, "segments") && stricmp(smode, "sequence")) {
SMJS_FREE(c, smode);
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
if (!stricmp(smode, "segments")) {
mode = MEDIA_SOURCE_APPEND_MODE_SEGMENTS;
} else if (!stricmp(smode, "sequence")) {
mode = MEDIA_SOURCE_APPEND_MODE_SEQUENCE;
}
SMJS_FREE(c, smode);
if (gf_list_find(sb->mediasource->sourceBuffers.list, sb) < 0) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_STATE_ERR);
}
if (sb->updating) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_STATE_ERR);
}
if (sb->mediasource->readyState == MEDIA_SOURCE_READYSTATE_ENDED) {
gf_mse_mediasource_open(sb->mediasource, NULL);
}
if (sb->append_state == MEDIA_SOURCE_APPEND_STATE_PARSING_MEDIA_SEGMENT) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_STATE_ERR);
}
sb->append_mode = mode;
if (sb->append_mode == MEDIA_SOURCE_APPEND_MODE_SEQUENCE) {
sb->group_start_timestamp_flag = GF_TRUE;
sb->group_start_timestamp = sb->group_end_timestamp;
}
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET(sourceBuffer_get_updating)
SB_BASIC_CHECK
*vp = BOOLEAN_TO_JSVAL(sb->updating ? JS_TRUE : JS_FALSE);
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET(sourceBuffer_get_timestampOffset)
SB_BASIC_CHECK
*vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, sb->timestampOffset*1.0/sb->timescale));
return JS_TRUE;
}
static SMJS_FUNC_PROP_SET(sourceBuffer_set_timestampOffset)
jsdouble d;
SB_UPDATING_CHECK
if (sb->mediasource->readyState == MEDIA_SOURCE_READYSTATE_ENDED) {
gf_mse_mediasource_open(sb->mediasource, NULL);
}
if (sb->append_state == MEDIA_SOURCE_APPEND_STATE_PARSING_MEDIA_SEGMENT) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_STATE_ERR);
}
JS_ValueToNumber(c, *vp, &d);
gf_mse_source_buffer_set_timestampOffset(sb, d);
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET(sourceBuffer_get_timescale)
SB_BASIC_CHECK
*vp = INT_TO_JSVAL(sb->timescale);
return JS_TRUE;
}
static SMJS_FUNC_PROP_SET(sourceBuffer_set_timescale)
SB_BASIC_CHECK
gf_mse_source_buffer_set_timescale(sb, JSVAL_TO_INT(*vp));
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET(sourceBuffer_get_appendWindowStart)
SB_BASIC_CHECK
*vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, sb->appendWindowStart));
return JS_TRUE;
}
static SMJS_FUNC_PROP_SET(sourceBuffer_set_appendWindowStart)
jsdouble d;
SB_UPDATING_CHECK
if (!JSVAL_IS_NUMBER(*vp)) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
JS_ValueToNumber(c, *vp, &d);
if (d < 0 || d >= sb->appendWindowEnd) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
sb->appendWindowStart = d;
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET(sourceBuffer_get_appendWindowEnd)
SB_BASIC_CHECK
if (sb->appendWindowEnd == GF_MAX_DOUBLE) {
*vp = JS_GetPositiveInfinityValue(c);
} else {
*vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, sb->appendWindowEnd));
}
return JS_TRUE;
}
static SMJS_FUNC_PROP_SET(sourceBuffer_set_appendWindowEnd)
jsdouble d;
SB_UPDATING_CHECK
if (!JSVAL_IS_NUMBER(*vp)) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
JS_ValueToNumber(c, *vp, &d);
if (d <= sb->appendWindowStart) {
return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
sb->appendWindowEnd = d;
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET(sourceBuffer_get_buffered)
SB_BASIC_CHECK
gf_mse_source_buffer_update_buffered(sb);
*vp = OBJECT_TO_JSVAL(sb->buffered->_this);
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET(sourceBuffer_get_tracks)
SB_BASIC_CHECK
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(html_url_createObjectURL)
{
SMJS_ARGS
JSObject *js_ms;
GF_HTML_MediaSource *ms;
char blobURI[256];
SMJS_SET_RVAL(JSVAL_NULL);
if (!argc || JSVAL_IS_NULL(argv[0]) || !JSVAL_IS_OBJECT(argv[0])) {
return JS_TRUE;
}
js_ms = JSVAL_TO_OBJECT(argv[0]);
if (!GF_JS_InstanceOf(c, js_ms, &html_media_rt->mediaSourceClass, NULL) )
{
return JS_TRUE;
}
ms = (GF_HTML_MediaSource *)SMJS_GET_PRIVATE(c, js_ms);
sprintf(blobURI, "blob:%p", ms);
ms->blobURI = gf_strdup(blobURI);
SMJS_SET_RVAL(STRING_TO_JSVAL( JS_NewStringCopyZ(c, blobURI)));
return JS_TRUE;
}
GF_HTML_ArrayBuffer *gf_arraybuffer_new(char *data, u32 length)
{
GF_HTML_ArrayBuffer *ab = NULL;
GF_SAFEALLOC(ab, GF_HTML_ArrayBuffer);
if (!ab) return NULL;
if (length > 0) {
ab->data = data;
ab->length = length;
}
ab->reference_count = 0;
return ab;
}
JSObject *gf_arraybuffer_js_new(JSContext *c, char *data, u32 length, JSObject *parent)
{
JSObject *obj;
GF_HTML_ArrayBuffer *p;
if (!data) return NULL;
p = gf_arraybuffer_new(data, length);
obj = JS_NewObject(c, &html_media_rt->arrayBufferClass._class, NULL, parent);
if (!parent) {
p->reference_count = 0;
}
SMJS_SET_PRIVATE(c, obj, p);
p->c = c;
p->_this = obj;
return obj;
}
void xhr_del_array_buffer(void *udta);
void gf_arraybuffer_del(GF_HTML_ArrayBuffer *buffer, Bool del_js)
{
if (buffer) {
if (del_js && buffer->_this) {
JSObject *xhro = JS_GetParent(buffer->c, buffer->_this);
if (xhro) {
void *xhr_ctx = SMJS_GET_PRIVATE(buffer->c, xhro);
xhr_del_array_buffer(xhr_ctx);
}
buffer->c = NULL;
buffer->_this = NULL;
}
if(!del_js && buffer->reference_count) {
buffer->reference_count--;
}
if (!buffer->reference_count && !buffer->_this) {
gf_free(buffer->data);
if (buffer->url) gf_free(buffer->url);
gf_free(buffer);
}
}
}
static JSBool SMJS_FUNCTION(arraybuffer_constructor)
{
SMJS_ARGS
u32 length = 0;
SMJS_OBJ_CONSTRUCTOR(&html_media_rt->arrayBufferClass)
if (!GF_JS_InstanceOf(c, obj, &html_media_rt->arrayBufferClass, NULL))
{
return JS_TRUE;
}
if (argc && !JSVAL_IS_NULL(argv[0]) && JSVAL_IS_INT(argv[0])) {
length = JSVAL_TO_INT(argv[0]);
}
if (length > 0) {
gf_arraybuffer_js_new(c, (char *)gf_malloc(length), length, NULL);
}
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET(arraybuffer_get_byteLength)
GF_HTML_ArrayBuffer *p;
if (!GF_JS_InstanceOf(c, obj, &html_media_rt->arrayBufferClass, NULL) ) {
return JS_TRUE;
}
p = (GF_HTML_ArrayBuffer *)SMJS_GET_PRIVATE(c, obj);
if (p) {
*vp = INT_TO_JSVAL(p->length);
}
return JS_TRUE;
}
static DECL_FINALIZE(arraybuffer_finalize)
GF_HTML_ArrayBuffer *p;
if (!GF_JS_InstanceOf(c, obj, &html_media_rt->arrayBufferClass, NULL) ) return;
p = (GF_HTML_ArrayBuffer *)SMJS_GET_PRIVATE(c, obj);
if (!p) return;
gf_arraybuffer_del(p, GF_TRUE);
}
static JSBool SMJS_FUNCTION(mse_event_add_listener)
{
JSBool SMJS_FUNCTION_EXT(gf_sg_js_event_add_listener, GF_Node *vrml_node);
return gf_sg_js_event_add_listener(SMJS_CALL_ARGS, NULL);
}
static JSBool SMJS_FUNCTION(mse_event_remove_listener)
{
JSBool SMJS_FUNCTION_EXT(gf_sg_js_event_remove_listener, GF_Node *vrml_node);
return gf_sg_js_event_remove_listener(SMJS_CALL_ARGS, NULL);
}
static JSBool SMJS_FUNCTION(mse_event_dispatch)
{
return JS_TRUE;
}
void html_media_source_init_js_api(JSContext *js_ctx, JSObject *global, GF_HTML_MediaRuntime *_html_media_rt)
{
if (_html_media_rt) {
html_media_rt = _html_media_rt;
JS_SETUP_CLASS(html_media_rt->arrayBufferClass, "ArrayBuffer", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub_forSetter, arraybuffer_finalize);
{
JSPropertySpec arrayBufferClassProps[] = {
{"byteLength", -1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, arraybuffer_get_byteLength, 0},
{0, 0, 0, 0, 0}
};
GF_JS_InitClass(js_ctx, global, 0, &html_media_rt->arrayBufferClass, arraybuffer_constructor, 0, arrayBufferClassProps, 0, 0, 0);
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTML Media Source API] ArrayBuffer class initialized\n"));
}
JS_SETUP_CLASS(html_media_rt->mediaSourceClass, "MediaSource", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub_forSetter, media_source_finalize);
{
JSPropertySpec htmlMediaSourceClassProps[] = {
{"sourceBuffers", HTML_MEDIASOURCE_PROP_SOURCEBUFFERS, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, media_source_get_source_buffers, 0},
{"activeSourceBuffers", HTML_MEDIASOURCE_PROP_ACTIVESOURCEBUFFERS, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, media_source_get_active_source_buffers, 0},
{"readyState", HTML_MEDIASOURCE_PROP_READYSTATE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, media_source_get_ready_state, 0},
{"duration", HTML_MEDIASOURCE_PROP_DURATION, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, media_source_get_duration, media_source_set_duration},
{0, 0, 0, 0, 0}
};
JSFunctionSpec htmlMediaSourceClassFuncs[] = {
SMJS_FUNCTION_SPEC("addSourceBuffer", mediasource_addSourceBuffer, 1),
SMJS_FUNCTION_SPEC("removeSourceBuffer", mediasource_removeSourceBuffer, 1),
SMJS_FUNCTION_SPEC("endOfStream", mediasource_endOfStream, 1),
SMJS_FUNCTION_SPEC("addEventListener", mse_event_add_listener, 3),
SMJS_FUNCTION_SPEC("removeEventListener", mse_event_remove_listener, 3),
SMJS_FUNCTION_SPEC("dispatchEvent", mse_event_dispatch, 1),
SMJS_FUNCTION_SPEC(0, 0, 0)
};
JSFunctionSpec htmlMediaSourceClassStaticFuncs[] = {
SMJS_FUNCTION_SPEC("isTypeSupported", mediasource_is_type_supported, 1),
SMJS_FUNCTION_SPEC(0, 0, 0)
};
GF_JS_InitClass(js_ctx, global, 0, &html_media_rt->mediaSourceClass, media_source_constructor, 0, htmlMediaSourceClassProps, htmlMediaSourceClassFuncs, 0, htmlMediaSourceClassStaticFuncs);
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTML Media Source API] MediaSource class initialized\n"));
}
JS_SETUP_CLASS(html_media_rt->sourceBufferClass, "SourceBuffer", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub_forSetter, JS_PropertyStub);
{
JSPropertySpec SourceBufferClassProps[] = {
{"mode", HTML_SOURCEBUFFER_PROP_MODE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, sourceBuffer_get_mode, sourceBuffer_set_mode},
{"updating", HTML_SOURCEBUFFER_PROP_UPDATING, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, sourceBuffer_get_updating, 0},
{"buffered", HTML_SOURCEBUFFER_PROP_BUFFERED, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, sourceBuffer_get_buffered, 0},
{"timestampOffset", HTML_SOURCEBUFFER_PROP_TIMESTAMPOFFSET, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, sourceBuffer_get_timestampOffset, sourceBuffer_set_timestampOffset},
{"timescale", HTML_SOURCEBUFFER_PROP_TIMESCALE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, sourceBuffer_get_timescale, sourceBuffer_set_timescale},
{"appendWindowStart", HTML_SOURCEBUFFER_PROP_APPENDWINDOWSTART,JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, sourceBuffer_get_appendWindowStart, sourceBuffer_set_appendWindowStart},
{"appendWindowEnd", HTML_SOURCEBUFFER_PROP_APPENDWINDOWEND, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, sourceBuffer_get_appendWindowEnd, sourceBuffer_set_appendWindowEnd},
{"audioTracks", HTML_SOURCEBUFFER_PROP_AUDIOTRACKS, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, sourceBuffer_get_tracks, 0},
{"videoTracks", HTML_SOURCEBUFFER_PROP_VIDEOTRACKS, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, sourceBuffer_get_tracks, 0},
{"textTracks", HTML_SOURCEBUFFER_PROP_TEXTTRACKS, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, sourceBuffer_get_tracks, 0},
{0, 0, 0, 0, 0}
};
JSFunctionSpec SourceBufferClassFuncs[] = {
SMJS_FUNCTION_SPEC("appendBuffer", sourcebuffer_appendBuffer, 1),
SMJS_FUNCTION_SPEC("appendStream", sourcebuffer_appendStream, 1),
SMJS_FUNCTION_SPEC("abort", sourcebuffer_abort, 0),
SMJS_FUNCTION_SPEC("remove", sourcebuffer_remove, 2),
SMJS_FUNCTION_SPEC("addEventListener", mse_event_add_listener, 3),
SMJS_FUNCTION_SPEC("removeEventListener", mse_event_add_listener, 3),
SMJS_FUNCTION_SPEC("dispatchEvent", mse_event_add_listener, 1),
SMJS_FUNCTION_SPEC(0, 0, 0)
};
GF_JS_InitClass(js_ctx, global, 0, &html_media_rt->sourceBufferClass, 0, 0, SourceBufferClassProps, SourceBufferClassFuncs, 0, 0);
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTML Media Source API] SourceBuffer class initialized\n"));
}
JS_SETUP_CLASS(html_media_rt->sourceBufferListClass, "SourceBufferList", JSCLASS_HAS_PRIVATE, sourcebufferlist_getProperty, JS_PropertyStub_forSetter, JS_PropertyStub);
{
JSPropertySpec SourceBufferListClassProps[] = {
{"length", HTML_SOURCEBUFFERLIST_PROP_LENGTH, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0},
{0, 0, 0, 0, 0}
};
JSFunctionSpec SourceBufferListClassFuncs[] = {
SMJS_FUNCTION_SPEC("addEventListener", mse_event_add_listener, 3),
SMJS_FUNCTION_SPEC("removeEventListener", mse_event_add_listener, 3),
SMJS_FUNCTION_SPEC("dispatchEvent", mse_event_add_listener, 1),
SMJS_FUNCTION_SPEC(0, 0, 0)
};
GF_JS_InitClass(js_ctx, global, 0, &html_media_rt->sourceBufferListClass, 0, 0, SourceBufferListClassProps, SourceBufferListClassFuncs, 0, 0);
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTML Media Source API] SourceBufferList class initialized\n"));
}
JS_SETUP_CLASS(html_media_rt->URLClass, "URL", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub_forSetter, JS_PropertyStub);
{
JSPropertySpec URLClassProps[] = {
{0, 0, 0, 0, 0}
};
JSFunctionSpec URLClassFuncs[] = {
SMJS_FUNCTION_SPEC("createObjectURL", html_url_createObjectURL, 1),
SMJS_FUNCTION_SPEC(0, 0, 0)
};
GF_JS_InitClass(js_ctx, global, 0, &html_media_rt->URLClass, 0, 0, URLClassProps, 0, 0, URLClassFuncs);
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[HTML Media Source API] URL class initialized\n"));
}
}
}
#endif
#endif