| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2021-2025 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / NodeJS module | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terfsess of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include "gpac_napi.h" | |
| #include <gpac/thread.h> | |
| #include <gpac/dash.h> | |
| #include <gpac/mpd.h> | |
| #include <gpac/network.h> | |
| #ifndef GPAC_DISABLE_SVG | |
| #include <gpac/scenegraph_svg.h> | |
| #endif | |
| typedef struct | |
| { | |
| Bool is_init; | |
| u32 str_buf_alloc; | |
| char *str_buf; | |
| u32 nb_args; | |
| char **argv; | |
| u32 main_thid; | |
| napi_async_context rmt_ctx; | |
| GF_List *rmt_new_clients; | |
| GF_List* rmt_task_calls; | |
| GF_List* rmt_delete_calls; | |
| struct _napi_session *fs_rmt_handler; | |
| } GPAC_NAPI; | |
| #define GF_SETUP_ERROR 0 | |
| #define GF_NOTIF_ERROR 1 | |
| #define GF_NOTIF_ERROR_AND_DISCONNECT 2 | |
| typedef struct _napi_session | |
| { | |
| GF_FilterSession *fs; | |
| napi_async_context async_ctx; | |
| napi_env env; | |
| napi_ref ref; | |
| GF_List *defer_filters; | |
| Bool blocking; | |
| } NAPI_Session; | |
| typedef struct | |
| { | |
| napi_ref ref; | |
| GF_FilterPacket *pck; | |
| u8 is_src, readonly, dangling; | |
| napi_ref shared_ab_ref, shared_destructor_ref; | |
| GF_List *pid_refs; | |
| } NAPI_FilterPacket; | |
| typedef struct | |
| { | |
| GF_Filter *f; | |
| GF_FilterSession *fs; | |
| napi_ref ref; | |
| napi_env env; | |
| Bool is_custom; | |
| napi_async_context async_ctx; | |
| napi_ref binding_ref; | |
| void *binding_stack; | |
| } NAPI_Filter; | |
| /*for custom filters only*/ | |
| typedef struct | |
| { | |
| GF_Filter *f; | |
| GF_FilterPid *pid; | |
| napi_ref ref; | |
| NAPI_FilterPacket *napi_pck; | |
| GF_List *pck_refs; | |
| } NAPI_FilterPid; | |
| typedef struct | |
| { | |
| napi_ref ref; | |
| napi_env env; | |
| napi_ref groups; | |
| } NAPI_Dash; | |
| #define NAPI_GPAC \ | |
| GPAC_NAPI *gpac;\ | |
| if ( napi_get_instance_data(env, (void **) &gpac) != napi_ok) { \ | |
| napi_throw_error(env, NULL, "Cannot get GPAC NAPI instance"); \ | |
| return NULL;\ | |
| } | |
| napi_value filter_enum_pid_props(napi_env env, napi_value cbk_fun, GF_FilterPid *pid); | |
| napi_value filter_get_pid_property(napi_env env, char *pname, GF_FilterPid *pid, Bool is_info); | |
| napi_value fs_wrap_filter(napi_env env, GF_FilterSession *fs, GF_Filter *filter); | |
| napi_status prop_from_napi(napi_env env, napi_value prop, u32 p4cc, char *pname, u32 custom_type, GF_PropertyValue *p); | |
| napi_value prop_to_napi(napi_env env, const GF_PropertyValue *p, u32 p4cc); | |
| napi_value frac_to_napi(napi_env env, const GF_Fraction *frac); | |
| napi_value frac64_to_napi(napi_env env, const GF_Fraction64 *frac); | |
| NAPI_FilterPacket *wrap_filter_packet(napi_env env, GF_FilterPacket *pck, Bool is_src); | |
| napi_value wrap_filterevent(napi_env env, GF_FilterEvent *evt, napi_value *for_val); | |
| napi_type_tag filtersession_tag = {0x475041434e4f4445, 0x2053455353494f4e}; | |
| napi_type_tag filter_tag = {0x475041434e4f4445, 0x2046494c54455220}; | |
| napi_type_tag filterpid_tag = {0x475041434e4f4445, 0x46696c7472506964}; | |
| napi_type_tag filterpck_tag = {0x475041434e4f4445, 0x47465041434b4554}; | |
| napi_type_tag filterevent_tag = {0x475041434e4f4445, 0x4746464556454e54}; | |
| napi_type_tag dashbind_tag = {0x475041434e4f4445, 0x44415348494e4a53}; | |
| napi_type_tag fileio_tag = {0x475041434e4f4445, 0x474646494c45494f}; | |
| napi_type_tag httpsess_tag = {0x475041434e4f4445, 0x474646494cf0df5c}; | |
| napi_type_tag rmtclient_tag = {0x475041434e4f4445, 0x174646494cf0df5c}; | |
| napi_value dashin_bind(napi_env env, napi_callback_info info); | |
| void dashin_detach_binding(NAPI_Filter *napi_f); | |
| napi_value httpout_bind(napi_env env, napi_callback_info info); | |
| void httpout_detach_binding(NAPI_Filter *napi_f); | |
| void dummy_finalize(napi_env env, void* finalize_data, void* finalize_hint) { | |
| } | |
| napi_value gpac_init(napi_env env, napi_callback_info info) | |
| { | |
| NAPI_GPAC | |
| NARG_ARGS(2, 0) | |
| NARG_U32(mem_track, 0, 0); | |
| NARG_STR(profile, 1, NULL); | |
| if (!gpac->is_init) { | |
| //copy profile as we will destroy gpac->str_buf | |
| if (profile) profile = strdup(profile); | |
| if (gpac->str_buf) gf_free(gpac->str_buf); | |
| gpac->str_buf = NULL; | |
| gpac->str_buf_alloc = 0; | |
| gf_sys_close(); | |
| gf_sys_init(mem_track, profile); | |
| gpac->is_init = GF_TRUE; | |
| if (profile) free(profile); | |
| } else { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[NAPI] libgpac already initialized, ignoring init(%d, %s)\n", mem_track, profile ? profile : "NULL")); | |
| } | |
| return NULL; | |
| } | |
| napi_value gpac_e2s(napi_env env, napi_callback_info info) | |
| { | |
| napi_value val; | |
| NARG_ARGS(1, 1) | |
| NARG_S32(e, 0, 0); | |
| NAPI_CALL( napi_create_string_utf8(env, gf_error_to_string(e), NAPI_AUTO_LENGTH, &val) ); | |
| return val; | |
| } | |
| napi_value gpac_set_logs(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS(2, 1) | |
| NARG_STR(logs, 0, NULL); | |
| NARG_BOOL(reset, 1, GF_FALSE); | |
| gf_log_set_tools_levels(logs, reset); | |
| return NULL; | |
| } | |
| napi_value gpac_sys_clock(napi_env env, napi_callback_info info) | |
| { | |
| napi_status status; | |
| napi_value val; | |
| NAPI_CALL( napi_create_uint32(env, gf_sys_clock(), &val) ); | |
| return val; | |
| } | |
| napi_value gpac_sys_clock_high_res(napi_env env, napi_callback_info info) | |
| { | |
| napi_status status; | |
| napi_value val; | |
| NAPI_CALL( napi_create_bigint_uint64(env, gf_sys_clock_high_res(), &val) ); | |
| return val; | |
| } | |
| napi_value gpac_sys_keyname(napi_env env, napi_callback_info info) | |
| { | |
| napi_value val; | |
| #ifndef GPAC_DISABLE_SVG | |
| const char *gf_dom_get_friendly_name(u32 key_identifier); | |
| NARG_ARGS(1, 1) | |
| NARG_S32(code, 0, 0); | |
| NAPI_CALL( napi_create_string_utf8(env, gf_dom_get_friendly_name(code), NAPI_AUTO_LENGTH, &val) ); | |
| #else | |
| NAPI_CALL( napi_create_string_utf8(env, "unknown", NAPI_AUTO_LENGTH, &val) ); | |
| #endif | |
| return val; | |
| } | |
| static u32 RMT_CLIENT_PROP_PEER_ADDRESS = 0; | |
| static u32 RMT_CLIENT_PROP_ON_DATA = 1; | |
| static u32 RMT_CLIENT_PROP_ON_CLOSE = 2; | |
| #define RMT_CLIENT\ | |
| RMT_ClientCtx* client=NULL;\ | |
| bool _tag;\ | |
| NAPI_CALL( napi_check_object_type_tag(env, this_val, &rmtclient_tag, &_tag) );\ | |
| if (!_tag) {\ | |
| napi_throw_error(env, NULL, "Not a RMTClient object");\ | |
| return NULL;\ | |
| }\ | |
| NAPI_CALL( napi_unwrap(env, this_val, (void**) &client) );\ | |
| napi_value rmt_client_getter(napi_env env, napi_callback_info info) { | |
| void *magic; | |
| napi_status status; | |
| napi_value this_val, ret; | |
| NAPI_CALL(napi_get_cb_info(env, info, NULL, NULL, &this_val, &magic) ); | |
| RMT_CLIENT | |
| if (!client) | |
| return NULL; | |
| if (magic == &RMT_CLIENT_PROP_PEER_ADDRESS) { | |
| const char *peer = gf_rmt_get_peer_address(client); | |
| if (!peer) return NULL; | |
| NAPI_CALL( napi_create_string_utf8(env, peer, NAPI_AUTO_LENGTH, &ret) ); | |
| return ret; | |
| } | |
| return NULL; | |
| } | |
| typedef struct { | |
| u32 type; | |
| napi_env env; | |
| napi_ref _this; | |
| napi_ref fun; | |
| void* client; | |
| } NAPI_RMT_Task ; | |
| typedef struct { | |
| NAPI_RMT_Task* task; | |
| napi_value arg; | |
| } NAPI_RMT_Task_Call ; | |
| void napi_rmt_task_del(napi_env env, NAPI_RMT_Task* task) { | |
| if (task && task->type == RMT_CALLBACK_NODE) { | |
| uint32_t res; | |
| if (task->_this) { | |
| napi_reference_unref(env, task->_this, &res); | |
| napi_delete_reference(env, task->_this); | |
| } | |
| task->_this = NULL; | |
| if (task->fun) { | |
| napi_reference_unref(env, task->fun, &res); | |
| napi_delete_reference(env, task->fun); | |
| } | |
| task->fun = NULL; | |
| gf_free(task); | |
| } | |
| } | |
| Bool fs_flush_rmt(GF_FilterSession *fsess, void *callback, u32 *reschedule_ms); | |
| void gpac_rmt_client_on_delete(void *udta) { | |
| NAPI_RMT_Task* task = udta; | |
| if (!task || task->type != RMT_CALLBACK_NODE ) | |
| return; | |
| napi_env env = task->env; | |
| GPAC_NAPI *gpac; | |
| if ( napi_get_instance_data(env, (void **) &gpac) != napi_ok) { | |
| napi_throw_error(env, NULL, "Cannot get GPAC NAPI instance"); | |
| return; | |
| } | |
| if (task->client) { | |
| RMT_ClientCtx* client = task->client; | |
| NAPI_RMT_Task* datatask = gf_rmt_client_get_on_data_task(client); | |
| gf_rmt_client_set_on_data_cbk(client, NULL, NULL); | |
| napi_rmt_task_del(env, datatask); | |
| gf_rmt_client_set_on_del_cbk(client, NULL, NULL); | |
| } | |
| if (!gpac->rmt_delete_calls) gpac->rmt_delete_calls = gf_list_new(); | |
| gf_list_add(gpac->rmt_delete_calls, task ); | |
| if (gpac->fs_rmt_handler) { | |
| gf_fs_post_user_task(gpac->fs_rmt_handler->fs, fs_flush_rmt, gpac->fs_rmt_handler, "RMTClientOnData"); | |
| } | |
| else { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_RMTWS, ("gpac_rmt_client_on_delete with no fs_rmt_handler\n")); | |
| } | |
| } | |
| void gpac_rmt_client_on_data(void *udta, const u8* payload, u64 size, Bool is_binary) { | |
| NAPI_RMT_Task* task = udta; | |
| if (!task || task->type != RMT_CALLBACK_NODE ) | |
| return; | |
| napi_env env = task->env; | |
| GPAC_NAPI *gpac; | |
| if ( napi_get_instance_data(env, (void **) &gpac) != napi_ok) { | |
| napi_throw_error(env, NULL, "Cannot get GPAC NAPI instance"); | |
| return; | |
| } | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_RMTWS, ("gpac_rmt_client_on_data binary %d size %llu: %.*s\n", is_binary, size, size, payload)); | |
| napi_value arg; | |
| // napi_status status; //TODO: check status | |
| if (is_binary) { | |
| void* abdata; | |
| /*status = */napi_create_arraybuffer(env, size, &abdata, &arg); | |
| memcpy(abdata, payload, size); | |
| } | |
| else { | |
| /*status = */napi_create_string_utf8(env, (const char*)payload, size, &arg); | |
| } | |
| NAPI_RMT_Task_Call* on_data_call; | |
| GF_SAFEALLOC(on_data_call, NAPI_RMT_Task_Call); | |
| on_data_call->task = task; | |
| on_data_call->arg = arg; | |
| if (!gpac->rmt_task_calls) gpac->rmt_task_calls = gf_list_new(); | |
| gf_list_add(gpac->rmt_task_calls, on_data_call ); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_RMTWS, ("gpac_rmt_client_on_data added call %p\n", on_data_call)); | |
| if (gpac->fs_rmt_handler) { | |
| gf_fs_post_user_task(gpac->fs_rmt_handler->fs, fs_flush_rmt, gpac->fs_rmt_handler, "RMTClientOnData"); | |
| } | |
| else { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_RMTWS, ("gpac_rmt_client_on_data with no fs_rmt_handler\n")); | |
| } | |
| } | |
| napi_value rmt_client_setter(napi_env env, napi_callback_info info) { | |
| NAPI_GPAC | |
| NARG_ARGS_THIS_MAGIC(1, 1) | |
| RMT_CLIENT | |
| if (magic == &RMT_CLIENT_PROP_ON_DATA) { | |
| napi_valuetype rtype; | |
| napi_typeof(env, argv[0], &rtype); | |
| if (rtype == napi_null || rtype == napi_undefined ) { | |
| NAPI_RMT_Task* task = gf_rmt_client_get_on_data_task(client); | |
| napi_rmt_task_del(env, task); | |
| gf_rmt_client_set_on_data_cbk(client, NULL, NULL); | |
| } else if (rtype == napi_function) { | |
| if (!gpac->rmt_ctx) { | |
| napi_value global, name; | |
| NAPI_CALL( napi_get_global(env, &global) ); | |
| NAPI_CALL( napi_create_string_utf8(env, "GPAC_RMTCallback", NAPI_AUTO_LENGTH, &name) ); | |
| NAPI_CALL( napi_async_init(env, global, name, &gpac->rmt_ctx) ) | |
| } | |
| NAPI_RMT_Task *task; | |
| GF_SAFEALLOC(task, NAPI_RMT_Task); | |
| task->type = RMT_CALLBACK_NODE; | |
| task->env = env; | |
| task->client = client; | |
| napi_create_reference(env, this_val, 1, &task->_this); | |
| napi_create_reference(env, argv[0], 1, &task->fun); | |
| gf_rmt_client_set_on_data_cbk(client, task, gpac_rmt_client_on_data); | |
| } | |
| } | |
| else if (magic == &RMT_CLIENT_PROP_ON_CLOSE) { | |
| napi_valuetype rtype; | |
| napi_typeof(env, argv[0], &rtype); | |
| if (rtype == napi_null || rtype == napi_undefined ) { | |
| NAPI_RMT_Task* task = gf_rmt_client_get_on_del_task(client); | |
| if (task && task->type == RMT_CALLBACK_NODE) { | |
| // reset the js function but keep the ref to the client for on_delete | |
| uint32_t res; | |
| napi_reference_unref(env, task->fun, &res); | |
| napi_delete_reference(env, task->fun); | |
| task->fun = NULL; | |
| } | |
| } else if (rtype == napi_function) { | |
| if (!gpac->rmt_ctx) { | |
| napi_value global, name; | |
| NAPI_CALL( napi_get_global(env, &global) ); | |
| NAPI_CALL( napi_create_string_utf8(env, "GPAC_RMTCallback", NAPI_AUTO_LENGTH, &name) ); | |
| NAPI_CALL( napi_async_init(env, global, name, &gpac->rmt_ctx) ) | |
| } | |
| NAPI_RMT_Task *task = gf_rmt_client_get_on_del_task(client); | |
| if (!task) | |
| GF_SAFEALLOC(task, NAPI_RMT_Task); | |
| task->type = RMT_CALLBACK_NODE; | |
| task->env = env; | |
| task->client = client; | |
| napi_create_reference(env, this_val, 1, &task->_this); | |
| napi_create_reference(env, argv[0], 1, &task->fun); | |
| gf_rmt_client_set_on_del_cbk(client, task, gpac_rmt_client_on_delete); | |
| } | |
| } | |
| return NULL; | |
| } | |
| napi_value rmt_client_send(napi_env env, napi_callback_info info) { | |
| NARG_ARGS_THIS(1,1) | |
| RMT_CLIENT | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_RMTWS, ("rmt_client_send got client %p\n", client)); | |
| bool is_arraybuffer = 0; | |
| NAPI_CALL( napi_is_arraybuffer(env, argv[0], &is_arraybuffer) ); | |
| if (is_arraybuffer) { | |
| u8* data=NULL; | |
| size_t len=0; | |
| NAPI_CALL(napi_get_arraybuffer_info(env, argv[0], (void **)&data, &len) ); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_RMTWS, ("rmt_client_send got bin data of size %d\n", len)); | |
| gf_rmt_client_send_to_ws(client, (const char*)data, len, GF_TRUE); | |
| //free(data); //freed elsewhere??? | |
| } | |
| else { | |
| NARG_STR_ALLOC(data, 0, NULL); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_RMTWS, ("rmt_client_send got string data %s\n", data)); | |
| gf_rmt_client_send_to_ws(client, data, strlen(data), GF_FALSE); | |
| gf_free(data); | |
| } | |
| return NULL; | |
| } | |
| napi_value grap_wrap_rmt_new_client(napi_env env, RMT_ClientCtx* client) { | |
| napi_status status; | |
| napi_value obj; | |
| napi_property_descriptor rmt_client_properties[] = { | |
| { "peer_address", NULL, NULL, rmt_client_getter, NULL, NULL, napi_enumerable, &RMT_CLIENT_PROP_PEER_ADDRESS}, | |
| { "on_data", NULL, NULL, NULL, rmt_client_setter, NULL, napi_enumerable, &RMT_CLIENT_PROP_ON_DATA}, | |
| { "on_close", NULL, NULL, NULL, rmt_client_setter, NULL, napi_enumerable, &RMT_CLIENT_PROP_ON_CLOSE}, | |
| { "send", NULL, rmt_client_send, NULL, NULL, NULL, napi_enumerable, NULL}, | |
| }; | |
| NAPI_CALL( napi_create_object(env, &obj) ); | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(rmt_client_properties)/sizeof(napi_property_descriptor), rmt_client_properties) ); | |
| NAPI_CALL( napi_type_tag_object(env, obj, &rmtclient_tag) ); | |
| NAPI_CALL( napi_wrap(env, obj, client, dummy_finalize, NULL, NULL) ); | |
| NAPI_RMT_Task *task; | |
| GF_SAFEALLOC(task, NAPI_RMT_Task); | |
| task->type = RMT_CALLBACK_NODE; | |
| task->env = env; | |
| task->client = client; | |
| napi_create_reference(env, obj, 1, &task->_this); | |
| gf_rmt_client_set_on_del_cbk(client, task, gpac_rmt_client_on_delete); | |
| return obj; | |
| } | |
| Bool fs_flush_rmt(GF_FilterSession *fsess, void *callback, u32 *reschedule_ms) | |
| { | |
| NAPI_Session *napi_fs = callback; | |
| napi_env env = napi_fs->env; | |
| GPAC_NAPI *gpac=NULL; | |
| napi_get_instance_data(env, (void **) &gpac); | |
| if (!gpac) return GF_FALSE; | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_RMTWS, ("%s:%d fs_flush_rmt clients %d tasks %d deletes %d gpac->rmt_ctx %p\n", __FILE__, __LINE__, gf_list_count(gpac->rmt_new_clients), gf_list_count(gpac->rmt_task_calls), gf_list_count(gpac->rmt_delete_calls), gpac->rmt_ctx)); | |
| while (gf_list_count(gpac->rmt_delete_calls)) { | |
| napi_value res; | |
| NAPI_RMT_Task* task = gf_list_pop_front(gpac->rmt_delete_calls); | |
| napi_value _this; | |
| napi_get_reference_value(env, task->_this, &_this); | |
| RMT_ClientCtx* client; | |
| napi_remove_wrap(env, _this, (void**) &client); | |
| napi_wrap(env, _this, NULL, NULL, NULL, NULL); | |
| if (task->fun) { | |
| napi_value fun; | |
| napi_get_reference_value(env, task->fun, &fun); | |
| napi_make_callback(env, gpac->rmt_ctx, _this, fun, 0, NULL, &res); | |
| } | |
| napi_rmt_task_del(env, task); | |
| } | |
| while (gf_list_count(gpac->rmt_new_clients)) { | |
| void* client = gf_list_pop_front(gpac->rmt_new_clients); | |
| RMT_WS* rmt = gf_rmt_client_get_rmt((RMT_ClientCtx*) client); | |
| NAPI_RMT_Task* task = gf_rmt_get_on_new_client_task(rmt); | |
| if (task && gpac->rmt_ctx) { | |
| napi_value res, _this, fun; | |
| // napi_status status; //TODO: check status | |
| napi_get_reference_value(env, task->_this, &_this); | |
| napi_get_reference_value(env, task->fun, &fun); | |
| napi_value jsclient = grap_wrap_rmt_new_client(env, client); | |
| napi_valuetype rtype; | |
| napi_typeof(env, jsclient, &rtype); | |
| /*status = */napi_make_callback(env, gpac->rmt_ctx, _this, fun, 1, &jsclient, &res); | |
| } | |
| } | |
| while (gf_list_count(gpac->rmt_task_calls)) { | |
| NAPI_RMT_Task_Call* call = gf_list_pop_front(gpac->rmt_task_calls); | |
| if (call && call->task) { | |
| if (gpac->rmt_ctx) { | |
| napi_value res, _this, fun; | |
| // napi_status status; //TODO: check status | |
| napi_get_reference_value(env, call->task->_this, &_this); | |
| napi_get_reference_value(env, call->task->fun, &fun); | |
| /*status = */napi_make_callback(env, gpac->rmt_ctx, _this, fun, 1, &call->arg, &res); | |
| // else { | |
| // napi_extended_error_info* error_info = NULL; | |
| // napi_get_last_error_info(env, &error_info); | |
| // const char* err_message = error_info->error_message; | |
| // fprintf(stderr, "ERR cb status %d err: %s\n", status, err_message); | |
| // } | |
| } | |
| gf_free(call); | |
| } | |
| } | |
| if (!gf_list_count(gpac->rmt_task_calls) && !gf_list_count(gpac->rmt_new_clients)) { | |
| return GF_FALSE; | |
| } | |
| *reschedule_ms = 10; | |
| return GF_TRUE; | |
| } | |
| napi_value gpac_enable_rmtws(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS(1, 1) | |
| NARG_BOOL(enable, 0, GF_TRUE); | |
| NAPI_GPAC | |
| gpac->is_init = GF_TRUE; | |
| gf_sys_enable_rmtws(enable); | |
| return NULL; | |
| } | |
| napi_value gpac_enable_userws(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS(1, 1) | |
| NARG_BOOL(enable, 0, GF_TRUE); | |
| NAPI_GPAC | |
| gpac->is_init = GF_TRUE; | |
| gf_sys_enable_userws(enable); | |
| return NULL; | |
| } | |
| napi_value gpac_set_args(napi_env env, napi_callback_info info) | |
| { | |
| NAPI_GPAC | |
| bool is_array; | |
| u32 i, nb_items=0; | |
| napi_value val; | |
| NARG_ARGS(1, 1) | |
| gpac->is_init = GF_TRUE; | |
| NAPI_CALL( napi_is_array(env, argv[0], &is_array) ); | |
| if (!is_array) { | |
| napi_throw_error(env, NULL, "NAPI call failed"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_get_array_length(env, argv[0], &nb_items) ); | |
| if (!nb_items) return NULL; | |
| if (gpac->argv) { | |
| for (i=0; i<gpac->nb_args; i++) { | |
| gf_free(gpac->argv[i]); | |
| } | |
| gf_free(gpac->argv); | |
| } | |
| gpac->argv = gf_malloc(sizeof(char *) * nb_items); | |
| gpac->nb_args = nb_items; | |
| for (i=0; i<gpac->nb_args; i++) { | |
| size_t size; | |
| NAPI_CALL( napi_get_element(env, argv[0], i, &val) ); | |
| NAPI_CALL( napi_get_value_string_utf8(env, val, NULL, 0, &size) ); | |
| gpac->argv[i] = gf_malloc(sizeof(char) * (size+1) ); | |
| NAPI_CALL( napi_get_value_string_utf8(env, val, gpac->argv[i], size+1, &size) ); | |
| gpac->argv[i][size]=0; | |
| } | |
| gf_sys_set_args(gpac->nb_args, (const char **) gpac->argv); | |
| return NULL; | |
| } | |
| static void gpac_napi_finalize(napi_env env, void* finalize_data, void* finalize_hint) | |
| { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_RMTWS, ("gpac_napi_finalize() \n")); | |
| GPAC_NAPI *inst = finalize_data; | |
| if (inst->nb_args) { | |
| u32 i; | |
| for (i=0; i<inst->nb_args; i++) { | |
| gf_free(inst->argv[i]); | |
| } | |
| gf_free(inst->argv); | |
| } | |
| if (inst->str_buf) gf_free(inst->str_buf); | |
| RMT_WS* rmt = (RMT_WS*) gf_sys_get_rmtws(); | |
| NAPI_RMT_Task* task = gf_rmt_get_on_new_client_task(rmt); | |
| gf_rmt_set_on_new_client_cbk(rmt, NULL, NULL); | |
| napi_rmt_task_del(env, task); | |
| rmt = (RMT_WS*) gf_sys_get_userws(); | |
| task = gf_rmt_get_on_new_client_task(rmt); | |
| gf_rmt_set_on_new_client_cbk(rmt, NULL, NULL); | |
| napi_rmt_task_del(env, task); | |
| if (inst->rmt_new_clients) { | |
| gf_list_del(inst->rmt_new_clients); | |
| inst->rmt_new_clients = NULL; | |
| } | |
| if (inst->rmt_delete_calls) { | |
| gf_list_del(inst->rmt_delete_calls); | |
| inst->rmt_delete_calls = NULL; | |
| } | |
| if (inst->rmt_task_calls) { | |
| while (gf_list_count(inst->rmt_task_calls)) { | |
| NAPI_RMT_Task_Call *call = gf_list_pop_back(inst->rmt_task_calls); | |
| gf_free(call); | |
| } | |
| gf_list_del(inst->rmt_task_calls); | |
| } | |
| if (inst->rmt_ctx) { | |
| napi_async_destroy(env, inst->rmt_ctx); | |
| inst->rmt_ctx = NULL; | |
| } | |
| //close gpac | |
| gf_sys_close(); | |
| #if defined(GPAC_MEMORY_TRACKING) | |
| if (gf_memory_size() || gf_file_handles_count()) { | |
| gf_log_set_tools_levels("mem@info", GF_FALSE); | |
| gf_memory_print(); | |
| } | |
| #endif | |
| //not allocated using gf_malloc !! | |
| free(inst); | |
| } | |
| #define FILTERPCK\ | |
| NAPI_FilterPacket *napi_pck=NULL;\ | |
| GF_FilterPacket *pck=NULL;\ | |
| bool _tag;\ | |
| NAPI_CALL( napi_check_object_type_tag(env, this_val, &filterpck_tag, &_tag) );\ | |
| if (!_tag) {\ | |
| napi_throw_error(env, NULL, "Not a FilterPacket object");\ | |
| return NULL;\ | |
| }\ | |
| NAPI_CALL( napi_unwrap(env, this_val, (void**) &napi_pck) );\ | |
| pck = napi_pck->pck;\ | |
| if (!pck) {\ | |
| napi_throw_error(env, NULL, napi_pck->is_src ? "Detached packet, already drop" : "Detached packet, already sent");\ | |
| return NULL;\ | |
| }\ | |
| static u32 FS_PROP_PCK_DTS = 0; | |
| static u32 FS_PROP_PCK_CTS = 1; | |
| static u32 FS_PROP_PCK_SAP = 2; | |
| static u32 FS_PROP_PCK_DUR = 3; | |
| static u32 FS_PROP_PCK_SIZE = 4; | |
| static u32 FS_PROP_PCK_DATA = 5; | |
| static u32 FS_PROP_PCK_START = 6; | |
| static u32 FS_PROP_PCK_END = 7; | |
| static u32 FS_PROP_PCK_TIMESCALE = 8; | |
| static u32 FS_PROP_PCK_INTERLACED = 9; | |
| static u32 FS_PROP_PCK_CORRUPTED = 10; | |
| static u32 FS_PROP_PCK_SEEK = 11; | |
| static u32 FS_PROP_PCK_BYTE_OFFSET = 12; | |
| static u32 FS_PROP_PCK_ROLL = 13; | |
| static u32 FS_PROP_PCK_CRYPT = 14; | |
| static u32 FS_PROP_PCK_CLOCK = 15; | |
| static u32 FS_PROP_PCK_CAROUSEL = 16; | |
| static u32 FS_PROP_PCK_SEQNUM = 17; | |
| static u32 FS_PROP_PCK_DEPS = 18; | |
| static u32 FS_PROP_PCK_FRAME_IFCE = 19; | |
| static u32 FS_PROP_PCK_BLOCKING = 20; | |
| napi_value filterpck_getter(napi_env env, napi_callback_info info) | |
| { | |
| void *magic; | |
| napi_status status; | |
| napi_value this_val, ret; | |
| NAPI_CALL(napi_get_cb_info(env, info, NULL, NULL, &this_val, &magic) ); | |
| FILTERPCK | |
| if (magic == &FS_PROP_PCK_DTS) { | |
| u64 dts = gf_filter_pck_get_dts(pck); | |
| if (dts==GF_FILTER_NO_TS) { | |
| NAPI_CALL( napi_get_null(env, &ret) ); | |
| } else { | |
| NAPI_CALL( napi_create_int64(env, (s64) dts, &ret) ); | |
| } | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_CTS) { | |
| u64 cts = gf_filter_pck_get_cts(pck); | |
| if (cts==GF_FILTER_NO_TS) { | |
| NAPI_CALL( napi_get_null(env, &ret) ); | |
| } else { | |
| NAPI_CALL( napi_create_int64(env, (s64) cts, &ret) ); | |
| } | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_SAP) { | |
| NAPI_CALL( napi_create_uint32(env, gf_filter_pck_get_sap(pck), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_DUR) { | |
| NAPI_CALL( napi_create_uint32(env, gf_filter_pck_get_duration(pck), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_SIZE) { | |
| u32 size; | |
| gf_filter_pck_get_data(pck, &size); | |
| NAPI_CALL( napi_create_uint32(env, size, &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_DATA) { | |
| u32 size; | |
| u8 *data = (u8 *) gf_filter_pck_get_data(pck, &size); | |
| if (!data) { | |
| NAPI_CALL( napi_get_null(env, &ret) ); | |
| } else { | |
| //TODO, mark as readonly using napi_pck->readonly !! | |
| NAPI_CALL(napi_create_external_arraybuffer(env, data, size, dummy_finalize, NULL, &ret) ); | |
| } | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_START) { | |
| Bool s, e; | |
| gf_filter_pck_get_framing(pck, &s, &e); | |
| NAPI_CALL( napi_get_boolean(env, s, &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_END) { | |
| Bool s, e; | |
| gf_filter_pck_get_framing(pck, &s, &e); | |
| NAPI_CALL( napi_get_boolean(env, e, &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_TIMESCALE) { | |
| NAPI_CALL( napi_create_uint32(env, gf_filter_pck_get_timescale(pck), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_INTERLACED) { | |
| NAPI_CALL( napi_get_boolean(env, gf_filter_pck_get_interlaced(pck), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_CORRUPTED) { | |
| NAPI_CALL( napi_get_boolean(env, gf_filter_pck_get_corrupted(pck), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_SEEK) { | |
| NAPI_CALL( napi_get_boolean(env, gf_filter_pck_get_seek_flag(pck), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_BYTE_OFFSET) { | |
| u64 bo = gf_filter_pck_get_byte_offset(pck); | |
| if (bo==GF_FILTER_NO_BO) { | |
| NAPI_CALL( napi_get_null(env, &ret) ); | |
| } else { | |
| NAPI_CALL( napi_create_int64(env, (s64) bo, &ret) ); | |
| } | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_ROLL) { | |
| NAPI_CALL( napi_create_int32(env, gf_filter_pck_get_roll_info(pck), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_CRYPT) { | |
| NAPI_CALL( napi_create_uint32(env, gf_filter_pck_get_crypt_flags(pck), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_CLOCK) { | |
| NAPI_CALL( napi_create_uint32(env, gf_filter_pck_get_clock_type(pck), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_CAROUSEL) { | |
| NAPI_CALL( napi_create_uint32(env, gf_filter_pck_get_carousel_version(pck), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_SEQNUM) { | |
| NAPI_CALL( napi_create_uint32(env, gf_filter_pck_get_seq_num(pck), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_DEPS) { | |
| NAPI_CALL( napi_create_uint32(env, gf_filter_pck_get_dependency_flags(pck), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_FRAME_IFCE) { | |
| NAPI_CALL( napi_get_boolean(env, (gf_filter_pck_get_frame_interface(pck)!=NULL), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PCK_BLOCKING) { | |
| NAPI_CALL( napi_get_boolean(env, gf_filter_pck_is_blocking_ref(pck), &ret) ); | |
| return ret; | |
| } | |
| return NULL; | |
| } | |
| napi_value filterpck_setter(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS_MAGIC(1, 1) | |
| FILTERPCK | |
| if (magic && napi_pck->is_src) { | |
| napi_throw_error(env, NULL, "Cannot set property on input packet"); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_DTS) { | |
| u64 dts; | |
| napi_valuetype rtype; | |
| napi_typeof(env, argv[0], &rtype); | |
| if (rtype == napi_null) { | |
| dts = GF_FILTER_NO_TS; | |
| } else { | |
| NAPI_CALL( napi_get_value_int64(env, argv[0], (s64 *) &dts) ); | |
| } | |
| gf_filter_pck_set_dts(pck, dts); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_CTS) { | |
| u64 cts; | |
| napi_valuetype rtype; | |
| napi_typeof(env, argv[0], &rtype); | |
| if (rtype == napi_null) { | |
| cts = GF_FILTER_NO_TS; | |
| } else { | |
| NAPI_CALL( napi_get_value_int64(env, argv[0], (s64 *) &cts) ); | |
| } | |
| gf_filter_pck_set_dts(pck, cts); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_SAP) { | |
| NARG_U32(sap, 0, 0) | |
| gf_filter_pck_set_sap(pck, sap); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_DUR) { | |
| NARG_U32(dur, 0, 0) | |
| gf_filter_pck_set_duration(pck, dur); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_SIZE) { | |
| napi_throw_error(env, NULL, "Cannot set size property"); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_DATA) { | |
| napi_throw_error(env, NULL, "Cannot set data property"); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_START) { | |
| NARG_BOOL(start, 0, 0); | |
| Bool s, e; | |
| gf_filter_pck_get_framing(pck, &s, &e); | |
| gf_filter_pck_set_framing(pck, start, e); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_END) { | |
| NARG_BOOL(end, 0, 0); | |
| Bool s, e; | |
| gf_filter_pck_get_framing(pck, &s, &e); | |
| gf_filter_pck_set_framing(pck, s, end); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_TIMESCALE) { | |
| napi_throw_error(env, NULL, "Cannot set timescale on output packet"); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_INTERLACED) { | |
| NARG_BOOL(ilced, 0, 0); | |
| gf_filter_pck_set_interlaced(pck, ilced); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_CORRUPTED) { | |
| NARG_BOOL(corr, 0, 0); | |
| gf_filter_pck_set_interlaced(pck, corr); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_SEEK) { | |
| NARG_BOOL(seek, 0, 0); | |
| gf_filter_pck_set_interlaced(pck, seek); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_BYTE_OFFSET) { | |
| u64 bo; | |
| napi_valuetype rtype; | |
| napi_typeof(env, argv[0], &rtype); | |
| if (rtype == napi_null) { | |
| bo = GF_FILTER_NO_BO; | |
| } else { | |
| NAPI_CALL( napi_get_value_int64(env, argv[0], (s64 *) &bo) ); | |
| } | |
| gf_filter_pck_set_byte_offset(pck, bo); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_ROLL) { | |
| NARG_U32(roll, 0, 0); | |
| gf_filter_pck_set_interlaced(pck, roll); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_CRYPT) { | |
| NARG_U32(crypt, 0, 0); | |
| gf_filter_pck_set_interlaced(pck, crypt); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_CLOCK) { | |
| NARG_U32(clock, 0, 0); | |
| gf_filter_pck_set_clock_type(pck, clock); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_CAROUSEL) { | |
| NARG_U32(carv, 0, 0); | |
| gf_filter_pck_set_carousel_version(pck, carv); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_SEQNUM) { | |
| NARG_U32(seqnum, 0, 0); | |
| gf_filter_pck_set_seq_num(pck, seqnum); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PCK_DEPS) { | |
| NARG_U32(deps, 0, 0); | |
| gf_filter_pck_set_dependency_flags(pck, deps); | |
| return NULL; | |
| } | |
| return NULL; | |
| } | |
| napi_value filterpck_enum_props(napi_env env, napi_callback_info info) | |
| { | |
| napi_value global; | |
| NARG_ARGS_THIS(1, 1) | |
| FILTERPCK | |
| napi_valuetype rtype; | |
| napi_typeof(env, argv[0], &rtype); | |
| if (rtype != napi_function) { | |
| napi_throw_error(env, NULL, "property enumerator is not a function"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_get_global(env, &global) ); | |
| u32 prop_idx=0; | |
| while (1) { | |
| napi_handle_scope scope_h; | |
| u32 p4cc=0; | |
| napi_value fun_args[3], res; | |
| const char *pname = NULL; | |
| const GF_PropertyValue *prop = gf_filter_pck_enum_properties(pck, &prop_idx, &p4cc, &pname); | |
| if (!prop) break; | |
| NAPI_CALL(napi_open_handle_scope(env, &scope_h) ); | |
| if (p4cc) { | |
| pname = gf_props_4cc_get_name(p4cc); | |
| if (!pname) pname = gf_4cc_to_str(p4cc); | |
| } | |
| NAPI_CALL( napi_create_string_utf8(env, pname, NAPI_AUTO_LENGTH, &fun_args[0]) ); | |
| fun_args[1] = prop_to_napi(env, prop, p4cc); | |
| NAPI_CALL( napi_create_int32(env, prop->type, &fun_args[2]) ); | |
| NAPI_CALL(napi_call_function(env, global, argv[0], 3, fun_args, &res) ); | |
| NAPI_CALL(napi_close_handle_scope(env, scope_h) ); | |
| } | |
| return NULL; | |
| } | |
| napi_value filterpck_get_prop(napi_env env, napi_callback_info info) | |
| { | |
| const GF_PropertyValue *prop = NULL; | |
| NARG_ARGS_THIS(1, 1) | |
| FILTERPCK | |
| NARG_STR(pname, 0, NULL); | |
| u32 p4cc = gf_props_get_id(pname); | |
| if (p4cc) | |
| prop = gf_filter_pck_get_property(pck, p4cc); | |
| else | |
| prop = gf_filter_pck_get_property_str(pck, pname); | |
| if (!prop) { | |
| napi_value res; | |
| NAPI_CALL(napi_get_null(env, &res) ); | |
| return res; | |
| } | |
| return prop_to_napi(env, prop, p4cc); | |
| } | |
| napi_value filterpck_ref(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTERPCK | |
| //if dangling, no ref | |
| if (napi_pck->dangling) return NULL; | |
| napi_pck->pck = gf_filter_pck_ref_ex(napi_pck->pck); | |
| //add ref | |
| u32 nbrefs; | |
| napi_reference_ref(env, napi_pck->ref, &nbrefs); | |
| return NULL; | |
| } | |
| napi_value filterpck_unref(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTERPCK | |
| //if dangling, no ref | |
| if (napi_pck->dangling) return NULL; | |
| gf_filter_pck_unref(pck); | |
| //remove ref | |
| u32 nbrefs; | |
| napi_reference_unref(env, napi_pck->ref, &nbrefs); | |
| if (!nbrefs) | |
| napi_pck->pck = NULL; | |
| return NULL; | |
| } | |
| napi_value filterpck_discard(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTERPCK | |
| //if dangling, accept discard as a way to free memory | |
| if (napi_pck->is_src && !napi_pck->dangling) { | |
| napi_throw_error(env, NULL, "Cannot discard an input packet"); | |
| return NULL; | |
| } | |
| gf_filter_pck_discard(pck); | |
| //detach | |
| u32 rcount; | |
| napi_reference_unref(env, napi_pck->ref, &rcount); | |
| napi_delete_reference(env, napi_pck->ref); | |
| napi_pck->ref = NULL; | |
| napi_pck->pck = NULL; | |
| return NULL; | |
| } | |
| napi_value filterpck_clone(napi_env env, napi_callback_info info) | |
| { | |
| napi_value ret; | |
| NARG_ARGS_THIS(1, 0) | |
| FILTERPCK | |
| GF_FilterPacket *cached_pck=NULL; | |
| NAPI_FilterPacket *napi_cached_pck=NULL; | |
| GF_FilterPacket *clone=NULL; | |
| if (argc>=1) { | |
| napi_valuetype rtype; | |
| napi_typeof(env, argv[0], &rtype); | |
| if (rtype == napi_object) { | |
| NAPI_CALL( napi_check_object_type_tag(env, argv[0], &filterpck_tag, &_tag) ); | |
| if (!_tag) { | |
| napi_throw_error(env, NULL, "Cached packet is not a FilterPacket object"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_unwrap(env, argv[0], (void**) &napi_cached_pck) ); | |
| cached_pck = napi_cached_pck->pck; | |
| } | |
| } | |
| clone = gf_filter_pck_dangling_copy(pck, cached_pck); | |
| if (!clone) { | |
| napi_throw_error(env, NULL, "Failed to clone packet"); | |
| return NULL; | |
| } | |
| //same packet used, return same object | |
| if (clone == cached_pck) { | |
| NAPI_CALL( napi_get_reference_value(env, napi_cached_pck->ref, &ret) ); | |
| return ret; | |
| } | |
| //discard previous napi_cached_pck | |
| if (napi_cached_pck) { | |
| u32 nbrefs; | |
| napi_reference_unref(env, napi_cached_pck->ref, &nbrefs); | |
| napi_delete_reference(env, napi_cached_pck->ref); | |
| napi_cached_pck->ref = NULL; | |
| //napi_cached_pck will be freed when GC collects previous cached_pck | |
| } | |
| NAPI_FilterPacket *napi_cloned = wrap_filter_packet(env, clone, GF_FALSE); | |
| if (!napi_cloned) { | |
| napi_throw_error(env, NULL, "Failed to clone packet"); | |
| return NULL; | |
| } | |
| napi_cloned->readonly = 0; | |
| napi_cloned->dangling = 1; | |
| NAPI_CALL( napi_get_reference_value(env, napi_cloned->ref, &ret) ); | |
| return ret; | |
| } | |
| napi_value filterpck_readonly(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTERPCK | |
| if (napi_pck->is_src) { | |
| napi_throw_error(env, NULL, "Cannot set readonly mode for input packet"); | |
| return NULL; | |
| } | |
| gf_filter_pck_set_readonly(pck); | |
| return NULL; | |
| } | |
| napi_value filterpck_send(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTERPCK | |
| if (napi_pck->is_src) { | |
| napi_throw_error(env, NULL, "Cannot send an input packet"); | |
| return NULL; | |
| } | |
| gf_filter_pck_send(pck); | |
| //detach | |
| u32 rcount; | |
| napi_reference_unref(env, napi_pck->ref, &rcount); | |
| if (!rcount) { | |
| napi_delete_reference(env, napi_pck->ref); | |
| napi_pck->ref = NULL; | |
| } | |
| napi_pck->pck = NULL; | |
| return NULL; | |
| } | |
| napi_value filterpck_copy_props(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1, 1) | |
| FILTERPCK | |
| GF_FilterPacket *from_pck=NULL; | |
| NAPI_FilterPacket *napi_from_pck=NULL; | |
| if (napi_pck->is_src) { | |
| napi_throw_error(env, NULL, "Cannot copy properties on input packet"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_check_object_type_tag(env, argv[0], &filterpck_tag, &_tag) ); | |
| if (!_tag) { | |
| napi_throw_error(env, NULL, "No packet to copy properties from"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_unwrap(env, argv[0], (void**) &napi_from_pck) ); | |
| from_pck = napi_from_pck->pck; | |
| gf_filter_pck_merge_properties(from_pck, pck); | |
| return NULL; | |
| } | |
| napi_value filterpck_set_prop(napi_env env, napi_callback_info info) | |
| { | |
| Bool is_null = GF_FALSE; | |
| u32 p4cc; | |
| GF_Err e; | |
| GF_PropertyValue p; | |
| NARG_ARGS_THIS(3, 1) | |
| FILTERPCK | |
| if (napi_pck->is_src) { | |
| napi_throw_error(env, NULL, "Cannot set property on input packet"); | |
| return NULL; | |
| } | |
| NARG_STR(pname, 0, NULL); | |
| if (!pname) return NULL; | |
| p4cc = gf_props_get_id(pname); | |
| if (argc==1) { | |
| is_null = GF_TRUE; | |
| } else { | |
| napi_valuetype rtype; | |
| napi_typeof(env, argv[1], &rtype); | |
| if (rtype == napi_null) | |
| is_null = GF_TRUE; | |
| else { | |
| NARG_U32(custom_type,2,0) | |
| status = prop_from_napi(env, argv[1], p4cc, pname, custom_type, &p); | |
| if (status != napi_ok) { | |
| napi_throw_error(env, NULL, "Cannot set packet property, invalid property value"); | |
| return NULL; | |
| } | |
| } | |
| } | |
| if (p4cc) { | |
| e = gf_filter_pck_set_property(pck, p4cc, is_null ? NULL : &p); | |
| } else { | |
| e = gf_filter_pck_set_property_dyn(pck, pname, is_null ? NULL : &p); | |
| } | |
| if (e) { | |
| napi_throw_error(env, gf_error_to_string(e), "Cannot set packet property"); | |
| } | |
| if (!is_null) { | |
| gf_props_reset_single(&p); | |
| } | |
| return NULL; | |
| } | |
| napi_value filterpck_truncate(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1, 1) | |
| FILTERPCK | |
| if (napi_pck->is_src) { | |
| napi_throw_error(env, NULL, "Cannot truncate an input packet"); | |
| return NULL; | |
| } | |
| NARG_U32(size, 0, 0) | |
| gf_filter_pck_truncate(pck, size); | |
| return NULL; | |
| } | |
| static u32 FS_PROP_PID_NAME = 0; | |
| static u32 FS_PROP_PID_FILTERNAME = 1; | |
| static u32 FS_PROP_PID_EOS = 2; | |
| static u32 FS_PROP_PID_HAS_SEEN_EOS = 3; | |
| static u32 FS_PROP_PID_HAS_RECEIVED = 4; | |
| static u32 FS_PROP_PID_WOULD_BLOCK = 5; | |
| static u32 FS_PROP_PID_MAX_BUFFER = 6; | |
| static u32 FS_PROP_PID_BUFFER = 7; | |
| static u32 FS_PROP_PID_BUFFER_FULL = 8; | |
| static u32 FS_PROP_PID_FIRST_EMPTY = 9; | |
| static u32 FS_PROP_PID_FIRST_CTS = 10; | |
| static u32 FS_PROP_PID_NB_PCK_QUEUED = 11; | |
| static u32 FS_PROP_PID_TIMESCALE = 12; | |
| static u32 FS_PROP_PID_MIN_PCK_DUR = 13; | |
| static u32 FS_PROP_PID_PLAYING = 14; | |
| static u32 FS_PROP_PID_NEXT_TS = 15; | |
| static u32 FS_PROP_PID_SPARSE = 16; | |
| static u32 FS_PROP_PID_HAS_DECODER = 17; | |
| #define FILTERPID\ | |
| GF_FilterPid *pid=NULL;\ | |
| bool _tag;\ | |
| NAPI_CALL( napi_check_object_type_tag(env, this_val, &filterpid_tag, &_tag) );\ | |
| if (!_tag) {\ | |
| napi_throw_error(env, NULL, "Not a FilterPid object");\ | |
| return NULL;\ | |
| }\ | |
| NAPI_CALL( napi_unwrap(env, this_val, (void**) &pid) );\ | |
| napi_value filterpid_getter(napi_env env, napi_callback_info info) | |
| { | |
| void *magic; | |
| napi_status status; | |
| napi_value this_val, ret; | |
| NAPI_CALL(napi_get_cb_info(env, info, NULL, NULL, &this_val, &magic) ); | |
| FILTERPID | |
| if (magic == &FS_PROP_PID_NAME) { | |
| const char *fname = gf_filter_pid_get_name(pid); | |
| if (fname) { | |
| NAPI_CALL( napi_create_string_utf8(env, fname, NAPI_AUTO_LENGTH, &ret) ); | |
| } else { | |
| NAPI_CALL( napi_get_null(env, &ret) ); | |
| } | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_FILTERNAME) { | |
| const char *fname = gf_filter_pid_get_filter_name(pid); | |
| if (fname) { | |
| NAPI_CALL( napi_create_string_utf8(env, fname, NAPI_AUTO_LENGTH, &ret) ); | |
| } else { | |
| NAPI_CALL( napi_get_null(env, &ret) ); | |
| } | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_EOS) { | |
| NAPI_CALL( napi_get_boolean(env, gf_filter_pid_is_eos(pid), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_HAS_SEEN_EOS) { | |
| NAPI_CALL( napi_get_boolean(env, gf_filter_pid_has_seen_eos(pid), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_HAS_RECEIVED) { | |
| NAPI_CALL( napi_get_boolean(env, gf_filter_pid_eos_received(pid), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_WOULD_BLOCK) { | |
| NAPI_CALL( napi_get_boolean(env, gf_filter_pid_would_block(pid), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_SPARSE) { | |
| NAPI_CALL( napi_get_boolean(env, gf_filter_pid_is_sparse(pid), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_MAX_BUFFER) { | |
| NAPI_CALL( napi_create_uint32(env, gf_filter_pid_get_max_buffer(pid), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_BUFFER) { | |
| NAPI_CALL( napi_create_int64(env, (s64) gf_filter_pid_query_buffer_duration(pid, GF_FALSE), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_BUFFER_FULL) { | |
| u64 dur = gf_filter_pid_query_buffer_duration(pid, GF_TRUE); | |
| NAPI_CALL( napi_get_boolean(env, (dur ? 1 : 0), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_FIRST_EMPTY) { | |
| NAPI_CALL( napi_get_boolean(env, gf_filter_pid_first_packet_is_empty(pid), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_FIRST_CTS) { | |
| u64 cts; | |
| if (!gf_filter_pid_get_first_packet_cts(pid, &cts)) { | |
| NAPI_CALL( napi_get_null(env, &ret) ); | |
| } else if (cts == GF_FILTER_NO_TS) { | |
| NAPI_CALL( napi_get_null(env, &ret) ); | |
| } else { | |
| NAPI_CALL( napi_create_int64(env, (s64) cts, &ret) ); | |
| } | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_NB_PCK_QUEUED) { | |
| NAPI_CALL( napi_create_uint32(env, gf_filter_pid_get_packet_count(pid), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_TIMESCALE) { | |
| NAPI_CALL( napi_create_uint32(env, gf_filter_pid_get_timescale(pid), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_MIN_PCK_DUR) { | |
| NAPI_CALL( napi_create_uint32(env, gf_filter_pid_get_min_pck_duration(pid), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_PLAYING) { | |
| NAPI_CALL( napi_get_boolean(env, gf_filter_pid_is_playing(pid), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_NEXT_TS) { | |
| u64 cts = gf_filter_pid_get_next_ts(pid); | |
| if (cts == GF_FILTER_NO_TS) { | |
| NAPI_CALL( napi_get_null(env, &ret) ); | |
| } else { | |
| NAPI_CALL( napi_create_int64(env, (s64) cts, &ret) ); | |
| } | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_PID_HAS_DECODER) { | |
| NAPI_CALL( napi_get_boolean(env, gf_filter_pid_has_decoder(pid), &ret) ); | |
| return ret; | |
| } | |
| return NULL; | |
| } | |
| napi_value filterpid_setter(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS_MAGIC(1, 1) | |
| FILTERPID | |
| if (magic == &FS_PROP_PID_NAME) { | |
| NARG_STR(name, 0, NULL) | |
| gf_filter_pid_set_name(pid, name); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PID_EOS) { | |
| NARG_BOOL(val, 0, GF_FALSE) | |
| if (val) gf_filter_pid_set_eos(pid); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PID_MAX_BUFFER) { | |
| NARG_U32(val, 0, 0) | |
| gf_filter_pid_set_max_buffer(pid, val); | |
| return NULL; | |
| } | |
| if (magic == &FS_PROP_PID_BUFFER) { | |
| NARG_U32(val, 0, 0) | |
| gf_filter_pid_set_max_buffer(pid, val); | |
| return NULL; | |
| } | |
| return NULL; | |
| } | |
| napi_value filterpid_remove(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTERPID | |
| gf_filter_pid_remove(pid); | |
| return NULL; | |
| } | |
| napi_value filterpid_enum_props(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1,1) | |
| FILTERPID | |
| return filter_enum_pid_props(env, argv[0], pid); | |
| } | |
| napi_value filterpid_get_prop(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1,1) | |
| FILTERPID | |
| NARG_STR(pname, 0, NULL) | |
| return filter_get_pid_property(env, pname, pid, GF_FALSE); | |
| } | |
| napi_value filterpid_get_info(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1,1) | |
| FILTERPID | |
| NARG_STR(pname, 0, NULL) | |
| return filter_get_pid_property(env, pname, pid, GF_TRUE); | |
| } | |
| napi_value filterpid_copy_props(napi_env env, napi_callback_info info) | |
| { | |
| GF_Err e; | |
| NARG_ARGS_THIS(1,1) | |
| FILTERPID | |
| GF_FilterPid *ipid=NULL; | |
| NAPI_CALL( napi_check_object_type_tag(env, argv[0], &filterpid_tag, &_tag) ); | |
| if (!_tag) { | |
| napi_throw_error(env, NULL, "Not a FilterPid object"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_unwrap(env, argv[0], (void**) &ipid) ); | |
| e = gf_filter_pid_copy_properties(pid, ipid); | |
| if (e) { | |
| napi_throw_error(env, gf_error_to_string(e), "Cannot copy PID properties"); | |
| } | |
| return NULL; | |
| } | |
| napi_value filterpid_reset_props(napi_env env, napi_callback_info info) | |
| { | |
| GF_Err e; | |
| NARG_ARGS_THIS(1,1) | |
| FILTERPID | |
| e = gf_filter_pid_reset_properties(pid); | |
| if (e) { | |
| napi_throw_error(env, gf_error_to_string(e), "Cannot reset PID properties"); | |
| } | |
| return NULL; | |
| } | |
| napi_value filterpid_set_prop_internal(napi_env env, napi_callback_info info, Bool is_info) | |
| { | |
| GF_PropertyValue p; | |
| GF_Err e; | |
| u32 p4cc=0; | |
| Bool is_null=GF_FALSE; | |
| NARG_ARGS_THIS(3,1) | |
| FILTERPID | |
| NARG_STR(pname, 0, NULL); | |
| if (!pname) return NULL; | |
| p4cc = gf_props_get_id(pname); | |
| if (argc==1) { | |
| is_null = GF_TRUE; | |
| } else { | |
| napi_valuetype rtype; | |
| napi_typeof(env, argv[1], &rtype); | |
| if (rtype == napi_null) | |
| is_null = GF_TRUE; | |
| else { | |
| NARG_U32(custom_type,2,0) | |
| status = prop_from_napi(env, argv[1], p4cc, pname, custom_type, &p); | |
| if (status != napi_ok) { | |
| napi_throw_error(env, NULL, "Cannot set PID property, invalid property value"); | |
| return NULL; | |
| } | |
| } | |
| } | |
| if (p4cc) { | |
| if (is_info) { | |
| e = gf_filter_pid_set_info(pid, p4cc, is_null ? NULL : &p); | |
| } else { | |
| e = gf_filter_pid_set_property(pid, p4cc, is_null ? NULL : &p); | |
| } | |
| } else { | |
| if (is_info) { | |
| e = gf_filter_pid_set_info_dyn(pid, pname, is_null ? NULL : &p); | |
| } else { | |
| e = gf_filter_pid_set_property_dyn(pid, pname, is_null ? NULL : &p); | |
| } | |
| } | |
| if (e) { | |
| napi_throw_error(env, gf_error_to_string(e), "Cannot set PID property"); | |
| } | |
| if (!is_null) { | |
| gf_props_reset_single(&p); | |
| } | |
| return NULL; | |
| } | |
| napi_value filterpid_set_prop(napi_env env, napi_callback_info info) | |
| { | |
| return filterpid_set_prop_internal(env, info, GF_FALSE); | |
| } | |
| napi_value filterpid_set_info(napi_env env, napi_callback_info info) | |
| { | |
| return filterpid_set_prop_internal(env, info, GF_TRUE); | |
| } | |
| napi_value filterpid_clear_eos(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1,0) | |
| FILTERPID | |
| NARG_BOOL(all_pids, 0, GF_FALSE); | |
| gf_filter_pid_clear_eos(pid, all_pids); | |
| return NULL; | |
| } | |
| napi_value filterpid_check_caps(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTERPID | |
| gf_filter_pid_check_caps(pid); | |
| return NULL; | |
| } | |
| napi_value filterpid_discard_block(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTERPID | |
| gf_filter_pid_discard_block(pid); | |
| return NULL; | |
| } | |
| napi_value filterpid_allow_direct_dispatch(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTERPID | |
| gf_filter_pid_allow_direct_dispatch(pid); | |
| return NULL; | |
| } | |
| napi_value filterpid_get_clock_type(napi_env env, napi_callback_info info) | |
| { | |
| napi_value ret; | |
| NARG_THIS | |
| FILTERPID | |
| NAPI_CALL( napi_create_uint32(env, gf_filter_pid_get_clock_info(pid, NULL, NULL), &ret) ); | |
| return ret; | |
| } | |
| napi_value filterpid_get_clock_timestamp(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTERPID | |
| u64 ts; | |
| u32 scale; | |
| gf_filter_pid_get_clock_info(pid, &ts, &scale); | |
| GF_Fraction64 frac = {ts, scale}; | |
| return frac64_to_napi(env, &frac); | |
| } | |
| napi_value filterpid_is_filter_in_parents(napi_env env, napi_callback_info info) | |
| { | |
| napi_value ret; | |
| NARG_ARGS_THIS(1,1) | |
| FILTERPID | |
| GF_Filter *f=NULL; | |
| NAPI_CALL( napi_check_object_type_tag(env, argv[0], &filter_tag, &_tag) ); | |
| if (!_tag) { | |
| napi_throw_error(env, NULL, "Not a Filter object"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_unwrap(env, argv[0], (void**) &f) ); | |
| NAPI_CALL( napi_get_boolean(env, gf_filter_pid_is_filter_in_parents(pid, f), &ret) ); | |
| return ret; | |
| } | |
| napi_value filterpid_get_buffer_occupancy(napi_env env, napi_callback_info info) | |
| { | |
| napi_property_descriptor p_desc = {.attributes=napi_enumerable}; | |
| napi_value obj; | |
| NARG_THIS | |
| FILTERPID | |
| u32 max_units = 0, nb_pck = 0, max_dur = 0, dur = 0; | |
| Bool in_final_flush = gf_filter_pid_get_buffer_occupancy(pid, &max_units, &nb_pck, &max_dur, &dur); | |
| in_final_flush = ! in_final_flush; | |
| NAPI_CALL(napi_create_object(env, &obj) ); | |
| #define SET_VAL(_val) \ | |
| p_desc.utf8name = #_val; \ | |
| NAPI_CALL(napi_create_uint32(env, _val, &p_desc.value) ); \ | |
| NAPI_CALL(napi_define_properties(env, obj, 1, &p_desc) ); \ | |
| SET_VAL(max_units); | |
| SET_VAL(nb_pck); | |
| SET_VAL(max_dur); | |
| SET_VAL(dur); | |
| p_desc.utf8name = "is_final_flush"; | |
| NAPI_CALL(napi_get_boolean(env, in_final_flush, &p_desc.value) ); | |
| NAPI_CALL(napi_define_properties(env, obj, 1, &p_desc) ); | |
| return obj; | |
| } | |
| napi_value filterpid_loose_connect(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTERPID | |
| gf_filter_pid_set_loose_connect(pid); | |
| return NULL; | |
| } | |
| napi_value filterpid_set_framing(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1,0) | |
| FILTERPID | |
| NARG_BOOL(framed, 0, GF_TRUE); | |
| gf_filter_pid_set_framing_mode(pid, framed); | |
| return NULL; | |
| } | |
| napi_value filterpid_set_clock_mode(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1,1) | |
| FILTERPID | |
| NARG_U32(ck_mode, 0, 0); | |
| gf_filter_pid_set_clock_mode(pid, ck_mode); | |
| return NULL; | |
| } | |
| napi_value filterpid_set_discard(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1,1) | |
| FILTERPID | |
| NARG_BOOL(do_discard, 0, GF_TRUE); | |
| gf_filter_pid_set_discard(pid, do_discard); | |
| return NULL; | |
| } | |
| napi_value filterpid_require_source_id(napi_env env, napi_callback_info info) | |
| { | |
| GF_Err e; | |
| NARG_THIS | |
| FILTERPID | |
| e = gf_filter_pid_require_source_id(pid); | |
| if (e) { | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to require sourceID for PID"); | |
| } | |
| return NULL; | |
| } | |
| napi_value filterpid_recompute_dts(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1,1) | |
| FILTERPID | |
| NARG_BOOL(do_recompute, 0, GF_TRUE); | |
| gf_filter_pid_recompute_dts(pid, do_recompute); | |
| return NULL; | |
| } | |
| napi_value filterpid_resolve_template(napi_env env, napi_callback_info info) | |
| { | |
| GF_Err e; | |
| char szTpl[GF_MAX_PATH]; | |
| NARG_ARGS_THIS(3,1) | |
| napi_value ret; | |
| FILTERPID | |
| NARG_U32(file_idx, 1, 0); | |
| NARG_STR(suffix, 2, NULL); | |
| if (suffix) suffix = gf_strdup(suffix); | |
| NARG_STR(template, 0, NULL); | |
| if (!template) { | |
| napi_throw_error(env, NULL, "No template provided"); | |
| if (suffix) gf_free(suffix); | |
| return NULL; | |
| } | |
| e = gf_filter_pid_resolve_file_template(pid, szTpl, template, file_idx, suffix); | |
| if (suffix) gf_free(suffix); | |
| if (e) { | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to resolve template for PID"); | |
| } | |
| NAPI_CALL( napi_create_string_utf8(env, szTpl, NAPI_AUTO_LENGTH, &ret) ); | |
| return ret; | |
| } | |
| napi_value filterpid_query_cap(napi_env env, napi_callback_info info) | |
| { | |
| napi_value ret; | |
| u32 p4cc; | |
| const GF_PropertyValue *cap=NULL; | |
| NARG_ARGS_THIS(1,1) | |
| FILTERPID | |
| NARG_STR(pname, 0, NULL); | |
| if (!pname) return NULL; | |
| p4cc = gf_props_get_id(pname); | |
| if (p4cc) { | |
| cap = gf_filter_pid_caps_query(pid, p4cc); | |
| } else { | |
| cap = gf_filter_pid_caps_query_str(pid, pname); | |
| } | |
| if (cap) return prop_to_napi(env, cap, p4cc); | |
| NAPI_CALL(napi_get_null(env, &ret)); | |
| return ret; | |
| } | |
| napi_value filterpid_negotiate_cap(napi_env env, napi_callback_info info) | |
| { | |
| u32 p4cc; | |
| GF_Err e; | |
| GF_PropertyValue p; | |
| NARG_ARGS_THIS(3,2) | |
| FILTERPID | |
| NARG_STR(pname, 0, NULL); | |
| if (!pname) return NULL; | |
| p4cc = gf_props_get_id(pname); | |
| NARG_U32(custom_type, 2, 0); | |
| NAPI_CALL( prop_from_napi(env, argv[1], p4cc, pname, custom_type, &p) ); | |
| if (p4cc) { | |
| e = gf_filter_pid_negotiate_property(pid, p4cc, &p); | |
| } else { | |
| e = gf_filter_pid_negotiate_property_dyn(pid, pname, &p); | |
| } | |
| if (e) { | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to negotiate cap for PID"); | |
| } | |
| gf_props_reset_single(&p); | |
| return NULL; | |
| } | |
| void napi_pck_del(napi_env env, void *finalize_data, void* finalize_hint) | |
| { | |
| NAPI_FilterPacket *napi_pck = (NAPI_FilterPacket *)finalize_data; | |
| //output packet not sent or dangling packet, discard it | |
| if (napi_pck->pck && (!napi_pck->is_src || napi_pck->dangling) ) | |
| gf_filter_pck_discard(napi_pck->pck); | |
| //we still have a ref and a packet, unref - this cleans up calls to pck.ref() without pck.unref() | |
| //not true for dangling copy | |
| if (!napi_pck->dangling && napi_pck->ref && napi_pck->pck) { | |
| gf_filter_pck_unref(napi_pck->pck); | |
| } | |
| //if pid is still alive, remove ourselves | |
| //this is needed because the GC for this packet might happen before the GC for the session/custom filter | |
| //so we need to make sure we won't call the shared destructor callback | |
| if (napi_pck->pid_refs) { | |
| gf_list_del_item(napi_pck->pid_refs, napi_pck); | |
| } | |
| gf_free(napi_pck); | |
| } | |
| NAPI_FilterPacket *wrap_filter_packet(napi_env env, GF_FilterPacket *pck, Bool is_src) | |
| { | |
| napi_value pck_obj; | |
| NAPI_FilterPacket *napi_pck; | |
| GF_SAFEALLOC(napi_pck, NAPI_FilterPacket); | |
| if (!napi_pck) { | |
| return NULL; | |
| } | |
| napi_pck->pck = pck; | |
| napi_pck->is_src = is_src ? 1 : 0; | |
| //all packets are read-only by default | |
| napi_pck->readonly = 1; | |
| napi_create_object(env, &pck_obj); | |
| napi_create_reference(env, pck_obj, 1, &napi_pck->ref); | |
| napi_type_tag_object(env, pck_obj, &filterpck_tag); | |
| //define all props for custom filter | |
| napi_property_descriptor filterpck_properties[] = { | |
| { "dts", NULL, NULL, filterpck_getter, filterpck_setter, NULL, napi_enumerable, &FS_PROP_PCK_DTS}, | |
| { "cts", NULL, NULL, filterpck_getter, filterpck_setter, NULL, napi_enumerable, &FS_PROP_PCK_CTS}, | |
| { "sap", NULL, NULL, filterpck_getter, filterpck_setter, NULL, napi_enumerable, &FS_PROP_PCK_SAP}, | |
| { "dur", NULL, NULL, filterpck_getter, filterpck_setter, NULL, napi_enumerable, &FS_PROP_PCK_DUR}, | |
| { "size", NULL, NULL, filterpck_getter, NULL, NULL, napi_enumerable, &FS_PROP_PCK_SIZE}, | |
| { "data", NULL, NULL, filterpck_getter, NULL, NULL, napi_enumerable, &FS_PROP_PCK_DATA}, | |
| { "start", NULL, NULL, filterpck_getter, filterpck_setter, NULL, napi_enumerable, &FS_PROP_PCK_START}, | |
| { "end", NULL, NULL, filterpck_getter, filterpck_setter, NULL, napi_enumerable, &FS_PROP_PCK_END}, | |
| { "timescale", NULL, NULL, filterpck_getter, NULL, NULL, napi_enumerable, &FS_PROP_PCK_TIMESCALE}, | |
| { "interlaced", NULL, NULL, filterpck_getter, filterpck_setter, NULL, napi_enumerable, &FS_PROP_PCK_INTERLACED}, | |
| { "corrupted", NULL, NULL, filterpck_getter, filterpck_setter, NULL, napi_enumerable, &FS_PROP_PCK_CORRUPTED}, | |
| { "seek", NULL, NULL, filterpck_getter, filterpck_setter, NULL, napi_enumerable, &FS_PROP_PCK_SEEK}, | |
| { "byte_offset", NULL, NULL, filterpck_getter, filterpck_setter, NULL, napi_enumerable, &FS_PROP_PCK_BYTE_OFFSET}, | |
| { "roll", NULL, NULL, filterpck_getter, filterpck_setter, NULL, napi_enumerable, &FS_PROP_PCK_ROLL}, | |
| { "crypt", NULL, NULL, filterpck_getter, filterpck_setter, NULL, napi_enumerable, &FS_PROP_PCK_CRYPT}, | |
| { "clock", NULL, NULL, filterpck_getter, filterpck_setter, NULL, napi_enumerable, &FS_PROP_PCK_CLOCK}, | |
| { "carousel", NULL, NULL, filterpck_getter, filterpck_setter, NULL, napi_enumerable, &FS_PROP_PCK_CAROUSEL}, | |
| { "seqnum", NULL, NULL, filterpck_getter, filterpck_setter, NULL, napi_enumerable, &FS_PROP_PCK_SEQNUM}, | |
| { "deps", NULL, NULL, filterpck_getter, filterpck_setter, NULL, napi_enumerable, &FS_PROP_PCK_DEPS}, | |
| { "frame_ifce", NULL, NULL, filterpck_getter, NULL, NULL, napi_enumerable, &FS_PROP_PCK_FRAME_IFCE}, | |
| { "blocking", NULL, NULL, filterpck_getter, NULL, NULL, napi_enumerable, &FS_PROP_PCK_BLOCKING}, | |
| { "enum_props", 0, filterpck_enum_props, 0, 0, 0, napi_enumerable, 0 }, | |
| { "get_prop", 0, filterpck_get_prop, 0, 0, 0, napi_enumerable, 0 }, | |
| { "ref", 0, filterpck_ref, 0, 0, 0, napi_enumerable, 0 }, | |
| { "unref", 0, filterpck_unref, 0, 0, 0, napi_enumerable, 0 }, | |
| { "discard", 0, filterpck_discard, 0, 0, 0, napi_enumerable, 0 }, | |
| { "clone", 0, filterpck_clone, 0, 0, 0, napi_enumerable, 0 }, | |
| { "readonly", 0, filterpck_readonly, 0, 0, 0, napi_enumerable, 0 }, | |
| { "send", 0, filterpck_send, 0, 0, 0, napi_enumerable, 0 }, | |
| { "copy_props", 0, filterpck_copy_props, 0, 0, 0, napi_enumerable, 0 }, | |
| { "set_prop", 0, filterpck_set_prop, 0, 0, 0, napi_enumerable, 0 }, | |
| { "truncate", 0, filterpck_truncate, 0, 0, 0, napi_enumerable, 0 }, | |
| }; | |
| napi_define_properties(env, pck_obj, sizeof(filterpck_properties)/sizeof(napi_property_descriptor), filterpck_properties); | |
| napi_wrap(env, pck_obj, napi_pck, napi_pck_del, NULL, NULL); | |
| return napi_pck; | |
| } | |
| napi_value filterpid_get_packet(napi_env env, napi_callback_info info) | |
| { | |
| napi_value ret; | |
| NARG_THIS | |
| FILTERPID | |
| GF_FilterPacket *pck; | |
| NAPI_FilterPid *napi_pid = gf_filter_pid_get_udta(pid); | |
| if (napi_pid->napi_pck) { | |
| NAPI_CALL( napi_get_reference_value(env, napi_pid->napi_pck->ref, &ret) ); | |
| return ret; | |
| } | |
| pck = gf_filter_pid_get_packet(pid); | |
| if (!pck) { | |
| NAPI_CALL(napi_get_null(env, &ret)); | |
| return ret; | |
| } | |
| //create pck object | |
| napi_pid->napi_pck = wrap_filter_packet(env, pck, GF_TRUE); | |
| NAPI_CALL( napi_get_reference_value(env, napi_pid->napi_pck->ref, &ret) ); | |
| return ret; | |
| } | |
| napi_value filterpid_drop_packet(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTERPID | |
| NAPI_FilterPid *napi_pid = gf_filter_pid_get_udta(pid); | |
| if (!napi_pid->napi_pck) { | |
| return NULL; | |
| } | |
| u32 rcount; | |
| napi_reference_unref(env, napi_pid->napi_pck->ref, &rcount); | |
| if (!rcount) { | |
| napi_delete_reference(env, napi_pid->napi_pck->ref); | |
| napi_pid->napi_pck->ref = NULL; | |
| napi_pid->napi_pck->pck = NULL; | |
| } | |
| napi_pid->napi_pck = NULL; | |
| gf_filter_pid_drop_packet(pid); | |
| return NULL; | |
| } | |
| napi_value filterpid_send_event(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1,1) | |
| FILTERPID | |
| GF_FilterEvent *evt=NULL; | |
| NAPI_CALL( napi_check_object_type_tag(env, argv[0], &filterevent_tag, &_tag) ); | |
| if (!_tag) { | |
| napi_throw_error(env, NULL, "Not a FilterEvent object"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_unwrap(env, argv[0], (void**) &evt) ); | |
| evt->base.on_pid = pid; | |
| gf_filter_pid_send_event(pid, evt); | |
| return NULL; | |
| } | |
| napi_value filterpid_forward(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1,1) | |
| FILTERPID | |
| NAPI_FilterPacket *napi_pck=NULL; | |
| NAPI_CALL( napi_check_object_type_tag(env, argv[0], &filterpck_tag, &_tag) ); | |
| if (!_tag) { | |
| napi_throw_error(env, NULL, "Not a FilterPacket object"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_unwrap(env, argv[0], (void**) &napi_pck) ); | |
| if (napi_pck->pck) { | |
| GF_Err e = gf_filter_pck_forward(napi_pck->pck, pid); | |
| if (e) { | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to forward packet"); | |
| } | |
| } | |
| return NULL; | |
| } | |
| napi_value filterpid_new_pck(napi_env env, napi_callback_info info) | |
| { | |
| napi_value ret; | |
| NARG_ARGS_THIS(1,0) | |
| FILTERPID | |
| NARG_U32(size, 0, 0) | |
| GF_FilterPacket *pck = gf_filter_pck_new_alloc(pid, size, NULL); | |
| if (!pck) { | |
| napi_throw_error(env, NULL, "Failed to create new packet"); | |
| return NULL; | |
| } | |
| NAPI_FilterPacket *napi_pck = wrap_filter_packet(env, pck, GF_FALSE); | |
| if (!napi_pck) { | |
| napi_throw_error(env, NULL, "Failed to create new packet"); | |
| return NULL; | |
| } | |
| napi_pck->readonly = 0; | |
| NAPI_CALL( napi_get_reference_value(env, napi_pck->ref, &ret) ); | |
| return ret; | |
| } | |
| napi_value filterpid_new_pck_ref(napi_env env, napi_callback_info info) | |
| { | |
| napi_value ret; | |
| NARG_ARGS_THIS(3,1) | |
| FILTERPID | |
| NAPI_FilterPacket *napi_pck=NULL; | |
| GF_FilterPacket *ipck=NULL; | |
| NAPI_CALL( napi_check_object_type_tag(env, argv[0], &filterpck_tag, &_tag) ); | |
| if (!_tag) { | |
| napi_throw_error(env, NULL, "Not a FilterPacket object"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_unwrap(env, argv[0], (void**) &napi_pck) ); | |
| ipck = napi_pck->pck; | |
| if (!ipck) { | |
| napi_throw_error(env, NULL, napi_pck->is_src ? "Detached packet, already drop" : "Detached packet, already sent"); | |
| return NULL; | |
| } | |
| NARG_U32(size, 1, 0) | |
| NARG_U32(offset, 2, 0) | |
| GF_FilterPacket *pck = gf_filter_pck_new_ref(pid, offset, size, ipck); | |
| if (!pck) { | |
| napi_throw_error(env, NULL, "Failed to create new packet"); | |
| return NULL; | |
| } | |
| napi_pck = wrap_filter_packet(env, pck, GF_FALSE); | |
| if (!napi_pck) { | |
| napi_throw_error(env, NULL, "Failed to create new packet"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_get_reference_value(env, napi_pck->ref, &ret) ); | |
| return ret; | |
| } | |
| void filter_cbk_release_packet(GF_Filter *filter, GF_FilterPid *pid, GF_FilterPacket *pck) | |
| { | |
| NAPI_Filter *napi_f = gf_filter_get_rt_udta(filter); | |
| NAPI_FilterPacket *napi_pck = NULL; | |
| NAPI_FilterPid *napi_pid = gf_filter_pid_get_udta(pid); | |
| napi_pck = gf_list_pop_front(napi_pid->pck_refs); | |
| if (!napi_pck) return; | |
| if (napi_pck->shared_destructor_ref) { | |
| napi_value fun, obj, res; | |
| napi_get_reference_value(napi_f->env, napi_pck->shared_destructor_ref, &fun); | |
| napi_get_global(napi_f->env, &obj); | |
| napi_make_callback(napi_f->env, napi_f->async_ctx, obj, fun, 0, NULL, &res); | |
| } | |
| u32 rcount; | |
| //unref arraybuffer | |
| napi_reference_unref(napi_f->env, napi_pck->shared_ab_ref, &rcount); | |
| napi_delete_reference(napi_f->env, napi_pck->shared_ab_ref); | |
| //unref destructor | |
| if (napi_pck->shared_destructor_ref) { | |
| napi_reference_unref(napi_f->env, napi_pck->shared_ab_ref, &rcount); | |
| napi_delete_reference(napi_f->env, napi_pck->shared_ab_ref); | |
| } | |
| //unref packet | |
| napi_reference_unref(napi_f->env, napi_pck->ref, &rcount); | |
| if (rcount==0) { | |
| napi_delete_reference(napi_f->env, napi_pck->ref); | |
| napi_pck->ref = NULL; | |
| napi_pck->pck = NULL; | |
| } | |
| } | |
| napi_value filterpid_new_pck_shared(napi_env env, napi_callback_info info) | |
| { | |
| Bool has_destructor=GF_FALSE; | |
| napi_value ret; | |
| GF_Err e; | |
| NARG_ARGS_THIS(2,1) | |
| FILTERPID | |
| if (argc>1) { | |
| napi_valuetype rtype; | |
| napi_typeof(env, argv[1], &rtype); | |
| if (rtype != napi_function) { | |
| napi_throw_error(env, NULL, "destructor is not a function"); | |
| return NULL; | |
| } | |
| has_destructor = GF_TRUE; | |
| } | |
| NAPI_FilterPid *napi_pid = gf_filter_pid_get_udta(pid); | |
| Bool is_ab=GF_FALSE; | |
| NAPI_CALL(napi_is_arraybuffer(env, argv[0], (bool*) &is_ab) ); | |
| if (!is_ab) { | |
| napi_throw_error(env, NULL, "not an array buffer"); | |
| return NULL; | |
| } | |
| u8 *data_p=NULL; | |
| size_t data_len=0; | |
| NAPI_CALL(napi_get_arraybuffer_info(env, argv[0], (void **)&data_p, &data_len) ); | |
| GF_FilterPacket *pck = gf_filter_pck_new_shared(pid, data_p, data_len, filter_cbk_release_packet); | |
| if (!pck) { | |
| napi_throw_error(env, NULL, "Failed to create new packet"); | |
| return NULL; | |
| } | |
| NAPI_FilterPacket *napi_pck = wrap_filter_packet(env, pck, GF_FALSE); | |
| if (!napi_pck) goto err_exit; | |
| if (!napi_pid->pck_refs) { | |
| napi_pid->pck_refs = gf_list_new(); | |
| if (!napi_pid->pck_refs) goto err_exit; | |
| } | |
| e = gf_list_add(napi_pid->pck_refs, napi_pck); | |
| if (e) goto err_exit; | |
| //ref packet to avoid destruction | |
| u32 nbrefs; | |
| status = napi_reference_ref(env, napi_pck->ref, &nbrefs); | |
| if (status!=napi_ok) goto err_exit; | |
| //ref array buffer | |
| status = napi_create_reference(env, argv[0], 1, &napi_pck->shared_ab_ref); | |
| if (status!=napi_ok) goto err_exit; | |
| if (has_destructor) { | |
| status = napi_create_reference(env, argv[1], 1, &napi_pck->shared_destructor_ref); | |
| if (status!=napi_ok) goto err_exit; | |
| } | |
| napi_pck->readonly = 0; | |
| status = napi_get_reference_value(env, napi_pck->ref, &ret); | |
| if (status == napi_ok) { | |
| napi_pck->pid_refs = napi_pid->pck_refs; | |
| return ret; | |
| } | |
| err_exit: | |
| if (pck) gf_filter_pck_discard(pck); | |
| if (napi_pck) gf_list_del_item(napi_pid->pck_refs, napi_pck); | |
| napi_throw_error(env, NULL, "Failed to create new packet"); | |
| return NULL; | |
| } | |
| napi_value filterpid_new_pck_copy_clone(napi_env env, napi_callback_info info, Bool is_clone) | |
| { | |
| napi_value ret; | |
| NARG_ARGS_THIS(1,1) | |
| FILTERPID | |
| NAPI_FilterPacket *napi_pck=NULL; | |
| GF_FilterPacket *ipck=NULL; | |
| NAPI_CALL( napi_check_object_type_tag(env, argv[0], &filterpck_tag, &_tag) ); | |
| if (!_tag) { | |
| napi_throw_error(env, NULL, "Not a FilterPacket object"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_unwrap(env, argv[0], (void**) &napi_pck) ); | |
| ipck = napi_pck ? napi_pck->pck : NULL; | |
| if (!ipck) { | |
| napi_throw_error(env, NULL, napi_pck->is_src ? "Detached packet, already drop" : "Detached packet, already sent"); | |
| return NULL; | |
| } | |
| GF_FilterPacket *pck; | |
| if (is_clone) | |
| pck = gf_filter_pck_new_clone(pid, ipck, NULL); | |
| else | |
| pck = gf_filter_pck_new_copy(pid, ipck, NULL); | |
| if (!pck) { | |
| napi_throw_error(env, NULL, "Failed to create new packet"); | |
| return NULL; | |
| } | |
| napi_pck = wrap_filter_packet(env, pck, GF_FALSE); | |
| if (!napi_pck) { | |
| napi_throw_error(env, NULL, "Failed to create new packet"); | |
| return NULL; | |
| } | |
| napi_pck->readonly = 0; | |
| NAPI_CALL( napi_get_reference_value(env, napi_pck->ref, &ret) ); | |
| return ret; | |
| } | |
| napi_value filterpid_new_pck_copy(napi_env env, napi_callback_info info) | |
| { | |
| return filterpid_new_pck_copy_clone(env, info, GF_FALSE); | |
| } | |
| napi_value filterpid_new_pck_clone(napi_env env, napi_callback_info info) | |
| { | |
| return filterpid_new_pck_copy_clone(env, info, GF_TRUE); | |
| } | |
| static u32 FILTER_PROP_NAME = 0; | |
| static u32 FILTER_PROP_ID = 1; | |
| static u32 FILTER_PROP_IPIDS = 2; | |
| static u32 FILTER_PROP_OPIDS = 3; | |
| static u32 FILTER_PROP_STATUS = 4; | |
| static u32 FILTER_PROP_BYTES_DONE = 5; | |
| #define FILTER\ | |
| GF_Filter *f=NULL;\ | |
| bool _tag;\ | |
| NAPI_CALL( napi_check_object_type_tag(env, this_val, &filter_tag, &_tag) );\ | |
| if (!_tag) {\ | |
| napi_throw_error(env, NULL, "Not a Filter object");\ | |
| return NULL;\ | |
| }\ | |
| NAPI_CALL( napi_unwrap(env, this_val, (void**) &f) );\ | |
| #define FILTER_CUST\ | |
| NAPI_Filter *napi_f = (NAPI_Filter *) gf_filter_get_rt_udta(f);\ | |
| if (!napi_f->is_custom) {\ | |
| napi_throw_error(env, NULL, "Not a custom Filter object");\ | |
| return NULL;\ | |
| }\ | |
| #define FILTER_ARG(_name, _argidx)\ | |
| GF_Filter *_name=NULL;\ | |
| bool _fa_tag;\ | |
| NAPI_CALL( napi_check_object_type_tag(env, argv[_argidx], &filter_tag, &_fa_tag) );\ | |
| if (!_fa_tag) {\ | |
| napi_throw_error(env, NULL, "Not a Filter object");\ | |
| return NULL;\ | |
| }\ | |
| NAPI_CALL( napi_unwrap(env, argv[_argidx], (void**) &_name) );\ | |
| napi_value filter_getter(napi_env env, napi_callback_info info) | |
| { | |
| void *magic; | |
| napi_status status; | |
| napi_value this_val, ret; | |
| NAPI_CALL(napi_get_cb_info(env, info, NULL, NULL, &this_val, &magic) ); | |
| FILTER | |
| if (magic == &FILTER_PROP_NAME) { | |
| const char *fname = gf_filter_get_name(f); | |
| if (!fname) return NULL; | |
| NAPI_CALL( napi_create_string_utf8(env, fname, NAPI_AUTO_LENGTH, &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FILTER_PROP_ID) { | |
| const char *fid = gf_filter_get_name(f); | |
| if (!fid) return NULL; | |
| NAPI_CALL( napi_create_string_utf8(env, fid, NAPI_AUTO_LENGTH, &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FILTER_PROP_IPIDS) { | |
| NAPI_CALL( napi_create_uint32(env, gf_filter_get_ipid_count(f), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FILTER_PROP_OPIDS) { | |
| NAPI_CALL( napi_create_uint32(env, gf_filter_get_opid_count(f), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FILTER_PROP_STATUS) { | |
| const char* statusstr = gf_filter_get_status(f); | |
| NAPI_CALL( napi_create_string_utf8(env, statusstr, NAPI_AUTO_LENGTH, &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FILTER_PROP_BYTES_DONE) { | |
| u64 bytes_dones = gf_filter_get_bytes_done(f); | |
| NAPI_CALL( napi_create_int64(env, bytes_dones, &ret) ); | |
| return ret; | |
| } | |
| return NULL; | |
| } | |
| napi_value filter_setter(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS_MAGIC(1, 1) | |
| FILTER | |
| if (magic == &FILTER_PROP_NAME) { | |
| NARG_STR(name, 0, NULL); | |
| gf_filter_set_name(f, name); | |
| return NULL; | |
| } | |
| return NULL; | |
| } | |
| napi_value filter_remove(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTER | |
| gf_filter_remove(f); | |
| return NULL; | |
| } | |
| napi_value filter_update(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(3, 2) | |
| FILTER | |
| NARG_STR(name, 0, NULL); | |
| NARG_STR_ALLOC(value, 1, NULL); | |
| NARG_U32(mask, 2, 0) | |
| gf_fs_send_update(NULL, NULL, f, name, value, mask); | |
| if (value) gf_free(value); | |
| return NULL; | |
| } | |
| napi_value filter_set_source_internal(napi_env env, napi_callback_info info, Bool is_restricted) | |
| { | |
| GF_Err e; | |
| NARG_ARGS_THIS(2, 1) | |
| FILTER | |
| FILTER_ARG(src_f, 0) | |
| NARG_STR(link_ext, 1, NULL); | |
| if (is_restricted) | |
| e = gf_filter_set_source_restricted(f, src_f, link_ext); | |
| else | |
| e = gf_filter_set_source(f, src_f, link_ext); | |
| if (e) | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to set source"); | |
| return NULL; | |
| } | |
| napi_value filter_set_source(napi_env env, napi_callback_info info) | |
| { | |
| return filter_set_source_internal(env, info, GF_FALSE); | |
| } | |
| napi_value filter_set_source_restricted(napi_env env, napi_callback_info info) | |
| { | |
| return filter_set_source_internal(env, info, GF_TRUE); | |
| } | |
| napi_value filter_insert(napi_env env, napi_callback_info info) | |
| { | |
| GF_FilterPid *opid=NULL; | |
| s32 offset=1; | |
| GF_Err e; | |
| NARG_ARGS_THIS(3, 1) | |
| FILTER | |
| FILTER_ARG(ins_f, 0) | |
| //check if 2nd param is int, get opid | |
| if (argc>1) { | |
| s32 idx = -1; | |
| if (napi_get_value_int32(env, argv[1], &idx) == napi_ok) { | |
| offset = 2; | |
| if (idx>=0) { | |
| opid = gf_filter_get_opid(f, (u32) idx); | |
| if (!opid) { | |
| napi_throw_error(env, NULL, "Invalid output PID index"); | |
| return NULL; | |
| } | |
| } | |
| } | |
| } | |
| NARG_STR(link_ext, (u32)offset, NULL); | |
| e = gf_filter_set_source(f, ins_f, link_ext); | |
| if (e) { | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to set source"); | |
| return NULL; | |
| } | |
| e = gf_filter_reconnect_output(f, opid); | |
| if (e) | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to reconnect output"); | |
| return NULL; | |
| } | |
| napi_value filter_reconnect(napi_env env, napi_callback_info info) | |
| { | |
| GF_FilterPid *opid=NULL; | |
| GF_Err e; | |
| NARG_ARGS_THIS(1, 0) | |
| FILTER | |
| //check if 2nd param is int, get opid | |
| if (argc) { | |
| s32 idx = -1; | |
| if (napi_get_value_int32(env, argv[0], &idx) == napi_ok) { | |
| if (idx>=0) { | |
| opid = gf_filter_get_opid(f, (u32) idx); | |
| if (!opid) { | |
| napi_throw_error(env, NULL, "Invalid output PID index"); | |
| return NULL; | |
| } | |
| } | |
| } | |
| } | |
| e = gf_filter_reconnect_output(f, opid); | |
| if (e) | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to reconnect output"); | |
| return NULL; | |
| } | |
| napi_value filter_probe_link(napi_env env, napi_callback_info info) | |
| { | |
| GF_Err e; | |
| char *res=NULL; | |
| s32 idx = -1; | |
| s32 offset=1; | |
| NARG_ARGS_THIS(2, 1) | |
| FILTER | |
| //check if 2nd param is int, get opid | |
| if (argc>1) { | |
| if (napi_get_value_int32(env, argv[0], &idx) == napi_ok) { | |
| offset=2; | |
| } | |
| } | |
| NARG_STR(fdesc, (u32)offset, NULL); | |
| e = gf_filter_probe_link(f, idx, fdesc, &res); | |
| if (e) { | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to probe links"); | |
| return NULL; | |
| } | |
| napi_value val; | |
| NAPI_CALL( napi_create_string_utf8(env, res, NAPI_AUTO_LENGTH, &val) ); | |
| gf_free(res); | |
| return val; | |
| } | |
| napi_value filter_get_destinations(napi_env env, napi_callback_info info) | |
| { | |
| GF_Err e; | |
| char *res=NULL; | |
| s32 idx = -1; | |
| NARG_ARGS_THIS(1, 0) | |
| FILTER | |
| //check if 2nd param is int, get opid | |
| if (argc && (napi_get_value_int32(env, argv[0], &idx) == napi_ok)) { | |
| } | |
| e = gf_filter_get_possible_destinations(f, idx, &res); | |
| if (e) { | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to get destinations"); | |
| return NULL; | |
| } | |
| napi_value val; | |
| NAPI_CALL( napi_create_string_utf8(env, res, NAPI_AUTO_LENGTH, &val) ); | |
| gf_free(res); | |
| return val; | |
| } | |
| napi_value filter_require_source_id(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTER | |
| gf_filter_require_source_id(f); | |
| return NULL; | |
| } | |
| napi_value filter_bind(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1,1) | |
| FILTER | |
| napi_valuetype rtype; | |
| napi_typeof(env, argv[0], &rtype); | |
| if (rtype != napi_object) { | |
| napi_throw_error(env, NULL, "Binding not an object"); | |
| return NULL; | |
| } | |
| const char *fname = gf_filter_get_name(f); | |
| if (fname && !strcmp(fname, "dashin")) { | |
| return dashin_bind(env, info); | |
| } | |
| if (fname && !strcmp(fname, "httpout")) { | |
| return httpout_bind(env, info); | |
| } | |
| napi_throw_error(env, NULL, "Failed to bind to filter, not dashin filter"); | |
| return NULL; | |
| } | |
| napi_value frac_to_napi(napi_env env, const GF_Fraction *frac) | |
| { | |
| napi_value val, res; | |
| napi_status status; | |
| NAPI_CALL(napi_create_object(env, &res) ); | |
| NAPI_CALL(napi_create_int32(env, frac->num, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "num", val) ); | |
| NAPI_CALL(napi_create_uint32(env, frac->den, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "den", val) ); | |
| return res; | |
| } | |
| napi_value frac64_to_napi(napi_env env, const GF_Fraction64 *frac) | |
| { | |
| napi_value val, res; | |
| napi_status status; | |
| NAPI_CALL(napi_create_object(env, &res) ); | |
| NAPI_CALL(napi_create_int64(env, frac->num, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "num", val) ); | |
| NAPI_CALL(napi_create_int64(env, frac->den, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "den", val) ); | |
| return res; | |
| } | |
| napi_status frac_from_napi(napi_env env, napi_value prop, GF_Fraction *frac) | |
| { | |
| napi_status status; | |
| Bool has_p; | |
| napi_value v; | |
| GET_PROP_FIELD_S32("num", "Fraction", frac->num) | |
| GET_PROP_FIELD_U32("den", "Fraction", frac->den) | |
| return napi_ok; | |
| } | |
| napi_status frac64_from_napi(napi_env env, napi_value prop, GF_Fraction64 *frac) | |
| { | |
| napi_status status; | |
| napi_value v; | |
| Bool has_p; | |
| GET_PROP_FIELD_S64("num", "Fraction64", frac->num) | |
| GET_PROP_FIELD_U64("den", "Fraction64", frac->den) | |
| return napi_ok; | |
| } | |
| #define SET_BOOL(_val) \ | |
| NAPI_CALL(napi_get_boolean(env, stats._val ? true : false, &val) ); \ | |
| NAPI_CALL(napi_set_named_property(env, res, #_val, val) ); | |
| #define SET_U32(_val) \ | |
| NAPI_CALL(napi_create_uint32(env, stats._val, &val) ); \ | |
| NAPI_CALL(napi_set_named_property(env, res, #_val, val) ); | |
| #define SET_U64(_val) \ | |
| NAPI_CALL(napi_create_int64(env, stats._val, &val) ); \ | |
| NAPI_CALL(napi_set_named_property(env, res, #_val, val) ); | |
| #define SET_S64(_val) \ | |
| NAPI_CALL(napi_create_int64(env, stats._val, &val) ); \ | |
| NAPI_CALL(napi_set_named_property(env, res, #_val, val) ); | |
| #define SET_S32(_val) \ | |
| NAPI_CALL(napi_create_int32(env, stats._val, &val) ); \ | |
| NAPI_CALL(napi_set_named_property(env, res, #_val, val) ); | |
| #define SET_STR(_val) \ | |
| if (stats._val) {\ | |
| NAPI_CALL( napi_create_string_utf8(env, stats._val, NAPI_AUTO_LENGTH, &val) );\ | |
| } else {\ | |
| napi_get_null(env, &val);\ | |
| }\ | |
| NAPI_CALL(napi_set_named_property(env, res, #_val, val) ); | |
| napi_value filter_get_statistics(napi_env env, napi_callback_info info) | |
| { | |
| GF_Err e; | |
| GF_FilterStats stats; | |
| napi_value res, val; | |
| NARG_THIS | |
| FILTER | |
| e = gf_filter_get_stats(f, &stats); | |
| if (e) { | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to get filter stats"); | |
| return NULL; | |
| } | |
| NAPI_CALL(napi_create_object(env, &res) ); | |
| NAPI_CALL(napi_get_boolean(env, stats.filter_alias ? true : false, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "filter_alias", val) ); | |
| if (stats.filter_alias) { | |
| return res; | |
| } | |
| SET_U64(nb_tasks_done) | |
| SET_U64(nb_pck_processed) | |
| SET_U64(nb_bytes_processed) | |
| SET_U64(nb_pck_sent) | |
| SET_U64(nb_hw_pck_sent) | |
| SET_U32(nb_errors) | |
| SET_U32(nb_current_errors) | |
| SET_U64(nb_bytes_sent) | |
| SET_U64(time_process) | |
| SET_S32(percent) | |
| SET_STR(status) | |
| SET_BOOL(report_updated) | |
| SET_STR(name) | |
| SET_STR(reg_name) | |
| SET_STR(filter_id) | |
| SET_BOOL(done) | |
| SET_U32(nb_pid_in) | |
| SET_U64(nb_in_pck) | |
| SET_U32(nb_pid_out) | |
| SET_U64(nb_out_pck) | |
| SET_BOOL(in_eos) | |
| SET_U32(type) | |
| NAPI_CALL( napi_create_string_utf8(env, gf_stream_type_name(stats.stream_type), NAPI_AUTO_LENGTH, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "streamtype", val) ); | |
| char *sep = NULL; | |
| const char *codecid = gf_codecid_file_ext(stats.codecid); | |
| if (codecid) sep = strchr(codecid, '|'); | |
| if (sep) { | |
| NAPI_CALL( napi_create_string_utf8(env, codecid, sep-codecid, &val) ); | |
| } else { | |
| NAPI_CALL( napi_create_string_utf8(env, codecid, NAPI_AUTO_LENGTH, &val) ); | |
| } | |
| NAPI_CALL(napi_set_named_property(env, res, "codecid", val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "last_ts_sent", frac64_to_napi(env, &stats.last_ts_sent)) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "last_ts_drop", frac64_to_napi(env, &stats.last_ts_drop)) ); | |
| SET_U32(last_task_time) | |
| return res; | |
| } | |
| napi_value prop_to_napi(napi_env env, const GF_PropertyValue *p, u32 p4cc) | |
| { | |
| napi_value res, val; | |
| u32 i; | |
| napi_status status; | |
| if (gf_props_type_is_enum(p->type)) { | |
| NAPI_CALL( napi_create_string_utf8(env, gf_props_enum_name(p->type, p->value.uint), NAPI_AUTO_LENGTH, &res) ); | |
| return res; | |
| } | |
| switch (p->type) { | |
| case GF_PROP_BOOL: | |
| NAPI_CALL(napi_get_boolean(env, p->value.boolean ? true : false, &res) ); | |
| return res; | |
| case GF_PROP_SINT: | |
| NAPI_CALL(napi_create_int32(env, p->value.sint, &res) ); | |
| return res; | |
| case GF_PROP_UINT: | |
| if (p4cc== GF_PROP_PID_STREAM_TYPE) { | |
| NAPI_CALL( napi_create_string_utf8(env, gf_stream_type_name(p->value.uint), NAPI_AUTO_LENGTH, &res) ); | |
| return res; | |
| } | |
| if (p4cc== GF_PROP_PID_PIXFMT) { | |
| NAPI_CALL( napi_create_string_utf8(env, gf_pixel_fmt_sname(p->value.uint), NAPI_AUTO_LENGTH, &res) ); | |
| return res; | |
| } | |
| if (p4cc== GF_PROP_PID_CODECID) { | |
| char *sep=NULL; | |
| const char *codecid = gf_codecid_file_ext(p->value.uint); | |
| if (codecid) sep = strchr(codecid, '|'); | |
| if (sep) { | |
| NAPI_CALL( napi_create_string_utf8(env, codecid, sep-codecid, &res) ); | |
| } else { | |
| NAPI_CALL( napi_create_string_utf8(env, codecid, NAPI_AUTO_LENGTH, &res) ); | |
| } | |
| return res; | |
| } | |
| NAPI_CALL(napi_create_uint32(env, p->value.uint, &res) ); | |
| return res; | |
| case GF_PROP_LSINT: | |
| NAPI_CALL(napi_create_int64(env, p->value.longsint, &res) ); | |
| return res; | |
| case GF_PROP_LUINT: | |
| NAPI_CALL(napi_create_int64(env, p->value.longuint, &res) ); | |
| return res; | |
| case GF_PROP_FRACTION: | |
| return frac_to_napi(env, &p->value.frac); | |
| case GF_PROP_FRACTION64: | |
| return frac64_to_napi(env, &p->value.lfrac); | |
| case GF_PROP_FLOAT: | |
| NAPI_CALL(napi_create_double(env, (double) FIX2FLT(p->value.fnumber), &res) ); | |
| return res; | |
| case GF_PROP_DOUBLE: | |
| NAPI_CALL(napi_create_double(env, p->value.number, &res) ); | |
| return res; | |
| case GF_PROP_STRING: | |
| case GF_PROP_NAME: | |
| if (p->value.string) { | |
| NAPI_CALL( napi_create_string_utf8(env, p->value.string, NAPI_AUTO_LENGTH, &res) ); | |
| } else { | |
| napi_get_null(env, &res); | |
| } | |
| return res; | |
| case GF_PROP_4CC: | |
| NAPI_CALL( napi_create_string_utf8(env, gf_4cc_to_str(p->value.uint), NAPI_AUTO_LENGTH, &res) ); | |
| return res; | |
| case GF_PROP_VEC2I: | |
| NAPI_CALL(napi_create_object(env, &res) ); | |
| NAPI_CALL(napi_create_int32(env, p->value.vec2i.x, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "x", val) ); | |
| NAPI_CALL(napi_create_int32(env, p->value.vec2i.y, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "y", val) ); | |
| return res; | |
| case GF_PROP_VEC2: | |
| NAPI_CALL(napi_create_object(env, &res) ); | |
| NAPI_CALL(napi_create_double(env, p->value.vec2.x, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "x", val) ); | |
| NAPI_CALL(napi_create_double(env, p->value.vec2.y, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "y", val) ); | |
| return res; | |
| case GF_PROP_VEC3I: | |
| NAPI_CALL(napi_create_object(env, &res) ); | |
| NAPI_CALL(napi_create_int32(env, p->value.vec3i.x, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "x", val) ); | |
| NAPI_CALL(napi_create_int32(env, p->value.vec3i.y, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "y", val) ); | |
| NAPI_CALL(napi_create_int32(env, p->value.vec3i.z, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "z", val) ); | |
| return res; | |
| case GF_PROP_VEC4I: | |
| NAPI_CALL(napi_create_object(env, &res) ); | |
| NAPI_CALL(napi_create_int32(env, p->value.vec3i.x, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "x", val) ); | |
| NAPI_CALL(napi_create_int32(env, p->value.vec3i.y, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "y", val) ); | |
| NAPI_CALL(napi_create_int32(env, p->value.vec3i.z, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "z", val) ); | |
| NAPI_CALL(napi_create_int32(env, p->value.vec3i.z, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "w", val) ); | |
| return res; | |
| case GF_PROP_DATA: | |
| case GF_PROP_DATA_NO_COPY: | |
| case GF_PROP_CONST_DATA: | |
| if (p->value.data.ptr) { | |
| #if 0 | |
| NAPI_CALL(napi_create_external_arraybuffer(env, p->value.data.ptr, p->value.data.size, dummy_finalize, NULL, &res) ); | |
| #else | |
| u8 *output; | |
| NAPI_CALL(napi_create_buffer(env, p->value.data.size, (void**) &output, &res) ); | |
| memcpy(output, p->value.data.ptr, p->value.data.size); | |
| #endif | |
| } else { | |
| napi_get_null(env, &res); | |
| } | |
| return res; | |
| case GF_PROP_POINTER: | |
| NAPI_CALL( napi_create_string_utf8(env, "INTERNAL_POINTER", NAPI_AUTO_LENGTH, &res) ); | |
| return res; | |
| case GF_PROP_UINT_LIST: | |
| NAPI_CALL(napi_create_array_with_length(env, p->value.uint_list.nb_items, &res) ); | |
| for (i=0; i<p->value.uint_list.nb_items; i++) { | |
| NAPI_CALL(napi_create_uint32(env, p->value.uint_list.vals[i], &val) ); | |
| NAPI_CALL(napi_set_element(env, res, i, val) ); | |
| } | |
| return res; | |
| case GF_PROP_SINT_LIST: | |
| NAPI_CALL(napi_create_array_with_length(env, p->value.sint_list.nb_items, &res) ); | |
| for (i=0; i<p->value.uint_list.nb_items; i++) { | |
| NAPI_CALL(napi_create_int32(env, p->value.sint_list.vals[i], &val) ); | |
| NAPI_CALL(napi_set_element(env, res, i, val) ); | |
| } | |
| return res; | |
| case GF_PROP_STRING_LIST: | |
| NAPI_CALL(napi_create_array_with_length(env, p->value.string_list.nb_items, &res) ); | |
| for (i=0; i<p->value.string_list.nb_items; i++) { | |
| if (p->value.string_list.vals[i]) { | |
| NAPI_CALL( napi_create_string_utf8(env, p->value.string_list.vals[i], NAPI_AUTO_LENGTH, &val) ); | |
| } else { | |
| napi_get_null(env, &val); | |
| } | |
| NAPI_CALL(napi_set_element(env, res, i, val) ); | |
| } | |
| return res; | |
| case GF_PROP_4CC_LIST: | |
| NAPI_CALL(napi_create_array_with_length(env, p->value.uint_list.nb_items, &res) ); | |
| for (i=0; i<p->value.uint_list.nb_items; i++) { | |
| NAPI_CALL( napi_create_string_utf8(env, gf_4cc_to_str(p->value.uint_list.vals[i]), NAPI_AUTO_LENGTH, &val) ); | |
| NAPI_CALL(napi_set_element(env, res, i, val) ); | |
| } | |
| return res; | |
| case GF_PROP_VEC2I_LIST: | |
| NAPI_CALL(napi_create_array_with_length(env, p->value.v2i_list.nb_items, &res) ); | |
| for (i=0; i<p->value.v2i_list.nb_items; i++) { | |
| napi_value item; | |
| NAPI_CALL(napi_create_object(env, &item) ); | |
| NAPI_CALL(napi_create_int32(env, p->value.v2i_list.vals[i].x, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, item, "x", val) ); | |
| NAPI_CALL(napi_create_int32(env, p->value.v2i_list.vals[i].y, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, item, "y", val) ); | |
| NAPI_CALL(napi_set_element(env, res, i, item) ); | |
| } | |
| return res; | |
| default: | |
| napi_throw_error(env, NULL, "unknown property type"); | |
| return NULL; | |
| } | |
| } | |
| napi_status napi_get_str(napi_env env, napi_value val, char **ostr) | |
| { | |
| GPAC_NAPI *gpac; | |
| size_t s; | |
| napi_status status; | |
| status = napi_get_instance_data(env, (void **) &gpac); | |
| if (status != napi_ok) return status; | |
| napi_valuetype vtype; | |
| status = napi_typeof(env, val, &vtype); | |
| if (status != napi_ok) return status; | |
| if (vtype == napi_null) { | |
| *ostr = NULL; | |
| return napi_ok; | |
| } | |
| status = napi_get_value_string_utf8(env, val, NULL, 0, &s); | |
| if (status != napi_ok) return status; | |
| if (s+1 > gpac->str_buf_alloc) { | |
| gpac->str_buf = gf_realloc(gpac->str_buf, sizeof(char) * (s+1)); | |
| if (!gpac->str_buf) { | |
| napi_throw_error(env, NULL, "Out of memory"); | |
| return napi_generic_failure; | |
| } | |
| gpac->str_buf_alloc = s+1; | |
| } | |
| status = napi_get_value_string_utf8(env, val, gpac->str_buf, gpac->str_buf_alloc, &s); | |
| if (status != napi_ok) return status; | |
| gpac->str_buf[s]=0; | |
| *ostr = gpac->str_buf; | |
| return napi_ok; | |
| } | |
| napi_status prop_from_napi(napi_env env, napi_value prop, u32 p4cc, char *pname, u32 custom_type, GF_PropertyValue *p) | |
| { | |
| char *str; | |
| napi_status status; | |
| Bool has_p; | |
| napi_value v; | |
| memset(p, 0, sizeof(GF_PropertyValue)); | |
| if (!p4cc) { | |
| if (!custom_type) { | |
| p->type = GF_PROP_STRING; | |
| status = napi_get_str(env, prop, &str); | |
| if (status != napi_ok) return status; | |
| p->value.string = str ? gf_strdup(str) : NULL; | |
| return napi_ok; | |
| } | |
| p->type = custom_type; | |
| } else { | |
| p->type = gf_props_4cc_get_type(p4cc); | |
| } | |
| if (p4cc == GF_PROP_PID_STREAM_TYPE) { | |
| status = napi_get_str(env, prop, &str); | |
| if (status != napi_ok) return status; | |
| if (!str) { | |
| napi_throw_error(env, NULL, "Invalid StreamType"); | |
| return napi_generic_failure; | |
| } | |
| p->value.uint = gf_stream_type_by_name( str ); | |
| return napi_ok; | |
| } | |
| if (p4cc == GF_PROP_PID_CODECID) { | |
| status = napi_get_str(env, prop, &str); | |
| if (status != napi_ok) return status; | |
| if (!str) { | |
| napi_throw_error(env, NULL, "Invalid CodecID"); | |
| return napi_generic_failure; | |
| } | |
| p->value.uint = gf_codecid_parse( str ); | |
| return napi_ok; | |
| } | |
| if (gf_props_type_is_enum(p->type)) { | |
| status = napi_get_str(env, prop, &str); | |
| if (status != napi_ok) return status; | |
| if (!str) { | |
| napi_throw_error(env, NULL, "Invalid Enum"); | |
| return napi_generic_failure; | |
| } | |
| p->value.uint = gf_props_parse_enum(p->type, str); | |
| return napi_ok; | |
| } | |
| if (p->type==GF_PROP_SINT) { | |
| return napi_get_value_int32(env, prop, &p->value.sint); | |
| } | |
| if (p->type==GF_PROP_UINT) { | |
| return napi_get_value_uint32(env, prop, &p->value.uint); | |
| } | |
| if (p->type==GF_PROP_4CC) { | |
| status = napi_get_str(env, prop, &str); | |
| if (status != napi_ok) return status; | |
| if (!str) { | |
| napi_throw_error(env, NULL, "Invalid 4CC"); | |
| return napi_generic_failure; | |
| } | |
| p->value.uint = gf_4cc_parse(str); | |
| return napi_ok; | |
| } | |
| if (p->type==GF_PROP_LSINT) { | |
| return napi_get_value_int64(env, prop, &p->value.longsint); | |
| } | |
| if (p->type==GF_PROP_LUINT) { | |
| return napi_get_value_int64(env, prop, (s64 *) &p->value.longuint); | |
| } | |
| if (p->type==GF_PROP_BOOL) { | |
| bool val; | |
| status = napi_get_value_bool(env, prop, &val); | |
| p->value.boolean = val ? GF_TRUE : GF_FALSE; | |
| return status; | |
| } | |
| if (p->type==GF_PROP_FRACTION) { | |
| GET_PROP_FIELD_S32("num", "Fraction", p->value.frac.num) | |
| GET_PROP_FIELD_U32("den", "Fraction", p->value.frac.den) | |
| return napi_ok; | |
| } | |
| if (p->type==GF_PROP_FRACTION64) { | |
| GET_PROP_FIELD_S64("num", "Fraction64", p->value.lfrac.num) | |
| GET_PROP_FIELD_U64("den", "Fraction64", p->value.lfrac.den) | |
| return napi_ok; | |
| } | |
| if (p->type==GF_PROP_FLOAT) { | |
| Double n; | |
| status = napi_get_value_double(env, prop, &n); | |
| p->value.fnumber = FLT2FIX(n); | |
| return status; | |
| } | |
| if (p->type==GF_PROP_DOUBLE) { | |
| return napi_get_value_double(env, prop, &p->value.number); | |
| } | |
| if (p->type==GF_PROP_VEC2I) { | |
| GET_PROP_FIELD_S32("x", "Vec2i", p->value.vec2i.x) | |
| GET_PROP_FIELD_S32("y", "Vec2i", p->value.vec2i.y) | |
| return napi_ok; | |
| } | |
| if (p->type==GF_PROP_VEC2) { | |
| GET_PROP_FIELD_FLT("x", "Vec2", p->value.vec2.x) | |
| GET_PROP_FIELD_FLT("y", "Vec2", p->value.vec2.y) | |
| return napi_ok; | |
| } | |
| if (p->type==GF_PROP_VEC3I) { | |
| GET_PROP_FIELD_S32("x", "Vec3i", p->value.vec3i.x) | |
| GET_PROP_FIELD_S32("y", "Vec3i", p->value.vec3i.y) | |
| GET_PROP_FIELD_S32("z", "Vec3i", p->value.vec3i.z) | |
| return napi_ok; | |
| } | |
| if (p->type==GF_PROP_VEC4I) { | |
| GET_PROP_FIELD_S32("x", "Vec4i", p->value.vec4i.x) | |
| GET_PROP_FIELD_S32("y", "Vec4i", p->value.vec4i.y) | |
| GET_PROP_FIELD_S32("z", "Vec4i", p->value.vec4i.z) | |
| GET_PROP_FIELD_S32("w", "Vec4i", p->value.vec4i.w) | |
| return napi_ok; | |
| } | |
| if ((p->type==GF_PROP_STRING) || (p->type==GF_PROP_STRING_NO_COPY) || (p->type==GF_PROP_NAME)) { | |
| status = napi_get_str(env, prop, &str); | |
| if (status != napi_ok) return status; | |
| p->value.string = str ? gf_strdup(p->value.string) : NULL; | |
| return napi_ok; | |
| } | |
| if ((p->type==GF_PROP_DATA) || (p->type==GF_PROP_DATA_NO_COPY) || (p->type==GF_PROP_CONST_DATA)) { | |
| napi_throw_error(env, NULL, "Setting data property from JS not yet implemented"); | |
| return napi_generic_failure; | |
| } | |
| if (p->type==GF_PROP_POINTER) { | |
| napi_throw_error(env, NULL, "Setting pointer property from JS not supported"); | |
| return napi_generic_failure; | |
| } | |
| if ((p->type==GF_PROP_UINT_LIST) || (p->type==GF_PROP_4CC_LIST) | |
| || (p->type==GF_PROP_SINT_LIST) || (p->type==GF_PROP_VEC2I_LIST) | |
| || (p->type==GF_PROP_STRING_LIST) | |
| ) { | |
| Bool is_array; | |
| u32 i, nb_items; | |
| size_t size; | |
| status = napi_is_array(env, prop, (bool *) &is_array); | |
| if (!is_array) { | |
| napi_throw_error(env, NULL, "Property value not an array"); | |
| return napi_generic_failure; | |
| } | |
| //use uint list for nb_items | |
| status = napi_get_array_length(env, prop, &nb_items); | |
| p->value.uint_list.nb_items = nb_items; | |
| if (!p->value.uint_list.nb_items) { | |
| p->value.uint_list.vals = NULL; | |
| return status; | |
| } | |
| if (p->type==GF_PROP_STRING_LIST) size = sizeof(char *) * nb_items; | |
| else if (p->type==GF_PROP_VEC2I_LIST) size = sizeof(GF_PropVec2i) * nb_items; | |
| else size = sizeof(u32) * nb_items; | |
| p->value.uint_list.vals = gf_malloc(size); | |
| if (!p->value.uint_list.vals) { | |
| napi_throw_error(env, NULL, "Failed to allocate property"); | |
| return napi_generic_failure; | |
| } | |
| memset(p->value.uint_list.vals, 0, size); | |
| for (i=0; i<nb_items; i++) { | |
| napi_value val; | |
| status = napi_get_element(env, prop, i, &val); | |
| if (status != napi_ok) return status; | |
| if (p->type==GF_PROP_STRING_LIST) { | |
| napi_valuetype vtype; | |
| napi_typeof(env, val, &vtype); | |
| if (vtype == napi_null) { | |
| p->value.string_list.vals[i] = NULL; | |
| } else { | |
| status = napi_get_value_string_utf8(env, val, NULL, 0, &size); | |
| if (status != napi_ok) return status; | |
| p->value.string_list.vals[i] = gf_malloc(sizeof(char) * (size+1) ); | |
| if (!p->value.string_list.vals[i]) return napi_generic_failure; | |
| status = napi_get_value_string_utf8(env, val, p->value.string_list.vals[i], size+1, &size); | |
| if (status != napi_ok) return status; | |
| p->value.string_list.vals[i] [size]=0; | |
| } | |
| } | |
| else if (p->type==GF_PROP_VEC2I_LIST) { | |
| GET_PROP_FIELD_S32("x", "Vec2i", p->value.v2i_list.vals[i].x) | |
| GET_PROP_FIELD_S32("y", "Vec2i", p->value.v2i_list.vals[i].y) | |
| } else if (p->type==GF_PROP_SINT_LIST) { | |
| status = napi_get_value_int32(env, val, &p->value.sint_list.vals[i]); | |
| if (status != napi_ok) return status; | |
| } else if (p->type==GF_PROP_UINT_LIST) { | |
| status = napi_get_value_uint32(env, val, &p->value.uint_list.vals[i]); | |
| if (status != napi_ok) return status; | |
| } else if (p->type==GF_PROP_4CC_LIST) { | |
| status = napi_get_str(env, prop, &str); | |
| if (status != napi_ok) return status; | |
| if (str && (strlen(str)==4)) { | |
| p->value.uint_list.vals[i] = GF_4CC(str[0], str[1], str[2], str[3]); | |
| } else { | |
| napi_throw_error(env, NULL, "Invalid 4CC, needs 4 characters"); | |
| return napi_generic_failure; | |
| } | |
| if (status != napi_ok) return status; | |
| } | |
| } | |
| } | |
| napi_throw_error(env, NULL, "Unsupported property type"); | |
| return napi_generic_failure; | |
| } | |
| napi_value filter_enum_pid_props(napi_env env, napi_value cbk_fun, GF_FilterPid *pid) | |
| { | |
| napi_value res, global; | |
| napi_status status; | |
| napi_valuetype rtype; | |
| napi_typeof(env, cbk_fun, &rtype); | |
| if (rtype != napi_function) { | |
| napi_throw_error(env, NULL, "property enumerator is not a function"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_get_global(env, &global) ); | |
| u32 prop_idx=0; | |
| while (1) { | |
| napi_handle_scope scope_h; | |
| u32 p4cc=0; | |
| napi_value fun_args[3]; | |
| const char *pname = NULL; | |
| const GF_PropertyValue *prop = gf_filter_pid_enum_properties(pid, &prop_idx, &p4cc, &pname); | |
| if (!prop) break; | |
| NAPI_CALL(napi_open_handle_scope(env, &scope_h) ); | |
| if (p4cc) { | |
| pname = gf_props_4cc_get_name(p4cc); | |
| if (!pname) pname = gf_4cc_to_str(p4cc); | |
| } | |
| NAPI_CALL( napi_create_string_utf8(env, pname, NAPI_AUTO_LENGTH, &fun_args[0]) ); | |
| fun_args[1] = prop_to_napi(env, prop, p4cc); | |
| NAPI_CALL( napi_create_int32(env, prop->type, &fun_args[2]) ); | |
| NAPI_CALL(napi_call_function(env, global, cbk_fun, 3, fun_args, &res) ); | |
| NAPI_CALL(napi_close_handle_scope(env, scope_h) ); | |
| } | |
| return NULL; | |
| } | |
| napi_value filter_iopid_enum_props(napi_env env, napi_callback_info info, Bool is_opid) | |
| { | |
| GF_FilterPid *pid=NULL; | |
| NARG_ARGS_THIS(2, 2) | |
| FILTER | |
| NARG_U32(idx, 0, 0) | |
| if (is_opid) { | |
| pid = gf_filter_get_opid(f, idx); | |
| } else { | |
| pid = gf_filter_get_ipid(f, idx); | |
| } | |
| if (!pid) { | |
| napi_throw_error(env, NULL, "Invalid PID index"); | |
| return NULL; | |
| } | |
| return filter_enum_pid_props(env, argv[1], pid); | |
| } | |
| napi_value filter_ipid_enum_props(napi_env env, napi_callback_info info) | |
| { | |
| return filter_iopid_enum_props(env, info, GF_FALSE); | |
| } | |
| napi_value filter_opid_enum_props(napi_env env, napi_callback_info info) | |
| { | |
| return filter_iopid_enum_props(env, info, GF_TRUE); | |
| } | |
| napi_value filter_get_pid_property(napi_env env, char *pname, GF_FilterPid *pid, Bool is_info) | |
| { | |
| napi_status status; | |
| napi_value res; | |
| GF_PropertyEntry *pe=NULL; | |
| u32 p4cc = gf_props_get_id(pname); | |
| const GF_PropertyValue *prop = NULL; | |
| if (is_info) { | |
| if (p4cc) | |
| prop = gf_filter_pid_get_info(pid, p4cc, &pe); | |
| else | |
| prop = gf_filter_pid_get_info_str(pid, pname, &pe); | |
| } else { | |
| if (p4cc) | |
| prop = gf_filter_pid_get_property(pid, p4cc); | |
| else | |
| prop = gf_filter_pid_get_property_str(pid, pname); | |
| } | |
| if (!prop) { | |
| if (!strcmp(pname, "buffer")) { | |
| NAPI_CALL( napi_create_int64(env, (s64) gf_filter_pid_query_buffer_duration(pid, GF_FALSE), &res) ); | |
| return res; | |
| } | |
| if (!strcmp(pname, "buffer_total")) { | |
| NAPI_CALL( napi_create_int64(env, (s64) gf_filter_pid_query_buffer_duration(pid, GF_TRUE), &res) ); | |
| return res; | |
| } | |
| if (!strcmp(pname, "name")) { | |
| const char *fname = gf_filter_pid_get_name(pid); | |
| if (fname) { | |
| NAPI_CALL( napi_create_string_utf8(env, fname, NAPI_AUTO_LENGTH, &res) ); | |
| } else { | |
| NAPI_CALL( napi_get_null(env, &res) ); | |
| } | |
| return res; | |
| } | |
| if (!strcmp(pname, "eos")) { | |
| NAPI_CALL( napi_get_boolean(env, gf_filter_pid_is_eos(pid), &res) ); | |
| return res; | |
| } | |
| napi_value res; | |
| NAPI_CALL(napi_get_null(env, &res) ); | |
| return res; | |
| } | |
| res = prop_to_napi(env, prop, p4cc); | |
| gf_filter_release_property(pe); | |
| return res; | |
| } | |
| napi_value filter_iopid_prop(napi_env env, napi_callback_info info, Bool is_opid) | |
| { | |
| GF_FilterPid *pid=NULL; | |
| NARG_ARGS_THIS(2, 2) | |
| FILTER | |
| NARG_U32(idx, 0, 0) | |
| NARG_STR(name, 1, 0) | |
| if (is_opid) { | |
| pid = gf_filter_get_opid(f, idx); | |
| } else { | |
| pid = gf_filter_get_ipid(f, idx); | |
| } | |
| if (!pid) { | |
| napi_throw_error(env, NULL, "Invalid PID index"); | |
| return NULL; | |
| } | |
| return filter_get_pid_property(env, name, pid, GF_FALSE); | |
| } | |
| napi_value filter_ipid_prop(napi_env env, napi_callback_info info) | |
| { | |
| return filter_iopid_prop(env, info, GF_FALSE); | |
| } | |
| napi_value filter_opid_prop(napi_env env, napi_callback_info info) | |
| { | |
| return filter_iopid_prop(env, info, GF_TRUE); | |
| } | |
| napi_value filter_iopid_stats(napi_env env, napi_callback_info info, Bool is_opid) | |
| { | |
| GF_Err e; | |
| napi_value res, val; | |
| GF_FilterPidStatistics stats; | |
| GF_FilterPid *pid=NULL; | |
| NARG_ARGS_THIS(2, 1) | |
| FILTER | |
| NARG_U32(idx, 0, 0) | |
| NARG_U32(mode, 1, 0) | |
| if (is_opid) { | |
| pid = gf_filter_get_opid(f, idx); | |
| } else { | |
| pid = gf_filter_get_ipid(f, idx); | |
| } | |
| if (!pid) { | |
| napi_throw_error(env, NULL, "Invalid PID index"); | |
| return NULL; | |
| } | |
| e = gf_filter_pid_get_statistics(pid, &stats, mode); | |
| if (e) { | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to get filter stats"); | |
| return NULL; | |
| } | |
| NAPI_CALL(napi_create_object(env, &res) ); | |
| SET_BOOL(disconnected) | |
| SET_U32(average_process_rate) | |
| SET_U32(max_process_rate) | |
| SET_U32(average_bitrate) | |
| SET_U32(max_bitrate) | |
| SET_U32(nb_processed) | |
| SET_U32(max_process_time) | |
| SET_U64(total_process_time) | |
| SET_U64(first_process_time) | |
| SET_U64(last_process_time) | |
| SET_U32(min_frame_dur) | |
| SET_U32(nb_saps) | |
| SET_U32(max_sap_process_time) | |
| SET_U64(total_sap_process_time) | |
| SET_U64(max_buffer_time) | |
| SET_U64(max_playout_time) | |
| SET_U64(min_playout_time) | |
| SET_U64(buffer_time) | |
| SET_U32(nb_buffer_units) | |
| SET_U64(last_rt_report) | |
| SET_U32(rtt) | |
| SET_U32(jitter) | |
| SET_U32(loss_rate) | |
| NAPI_CALL(napi_set_named_property(env, res, "last_ts_sent", frac64_to_napi(env, &stats.last_ts_sent)) ); | |
| NAPI_CALL(napi_set_named_property(env, res, "last_ts_drop", frac64_to_napi(env, &stats.last_ts_drop)) ); | |
| return res; | |
| } | |
| napi_value filter_ipid_stats(napi_env env, napi_callback_info info) | |
| { | |
| return filter_iopid_stats(env, info, GF_FALSE); | |
| } | |
| napi_value filter_opid_stats(napi_env env, napi_callback_info info) | |
| { | |
| return filter_iopid_stats(env, info, GF_TRUE); | |
| } | |
| napi_value filter_ipid_source(napi_env env, napi_callback_info info) | |
| { | |
| GF_FilterPid *pid=NULL; | |
| NARG_ARGS_THIS(1, 1) | |
| FILTER | |
| NARG_U32(idx, 0, 0) | |
| pid = gf_filter_get_ipid(f, idx); | |
| if (!pid) { | |
| napi_throw_error(env, NULL, "Invalid PID index"); | |
| return NULL; | |
| } | |
| GF_Filter *f_src = gf_filter_pid_get_source_filter(pid); | |
| NAPI_Filter *napi_f = (NAPI_Filter *) gf_filter_get_rt_udta(f); | |
| return fs_wrap_filter(env, napi_f->fs, f_src); | |
| } | |
| napi_value filter_opid_sinks(napi_env env, napi_callback_info info) | |
| { | |
| napi_value res; | |
| u32 i; | |
| GF_FilterPid *pid=NULL; | |
| NARG_ARGS_THIS(1, 1) | |
| FILTER | |
| NARG_U32(idx, 0, 0) | |
| pid = gf_filter_get_opid(f, idx); | |
| if (!pid) { | |
| napi_throw_error(env, NULL, "Invalid PID index"); | |
| return NULL; | |
| } | |
| NAPI_CALL(napi_create_array(env, &res) ); | |
| NAPI_Filter *napi_f = (NAPI_Filter *) gf_filter_get_rt_udta(f); | |
| i=0; | |
| while (1) { | |
| GF_Filter *f_dst = gf_filter_pid_enum_destinations(pid, i); | |
| if (!f_dst) break; | |
| NAPI_CALL(napi_set_element(env, res, i, fs_wrap_filter(env, napi_f->fs, f_dst) ) ); | |
| i++; | |
| } | |
| return res; | |
| } | |
| napi_value filter_get_info(napi_env env, napi_callback_info info) | |
| { | |
| napi_value res; | |
| u32 p4cc; | |
| NARG_ARGS_THIS(1, 1) | |
| FILTER | |
| NARG_STR(prop_name, 0, NULL); | |
| const GF_PropertyValue *prop; | |
| GF_PropertyEntry *pe=NULL; | |
| p4cc =gf_props_get_id(prop_name); | |
| if (p4cc) { | |
| prop = gf_filter_get_info(f, p4cc, &pe); | |
| } else { | |
| prop = gf_filter_get_info_str(f, prop_name, &pe); | |
| } | |
| if (prop) { | |
| res = prop_to_napi(env, prop, p4cc); | |
| } else { | |
| NAPI_CALL( napi_get_null(env, &res) ); | |
| } | |
| gf_filter_release_property(pe); | |
| return res; | |
| } | |
| napi_value filter_all_args(napi_env env, napi_callback_info info) | |
| { | |
| napi_value res; | |
| u32 a_idx; | |
| NARG_THIS | |
| FILTER | |
| NAPI_CALL(napi_create_array(env, &res) ); | |
| a_idx=0; | |
| while (1) { | |
| napi_value n_arg, val; | |
| const GF_FilterArgs *arg = gf_filter_enumerate_args(f, a_idx); | |
| if (!arg) break; | |
| NAPI_CALL(napi_create_object(env, &n_arg) ); | |
| NAPI_CALL( napi_create_string_utf8(env, arg->arg_name, NAPI_AUTO_LENGTH, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, n_arg, "name", val) ); | |
| NAPI_CALL( napi_create_string_utf8(env, arg->arg_desc, NAPI_AUTO_LENGTH, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, n_arg, "desc", val) ); | |
| NAPI_CALL( napi_create_int32(env, arg->arg_type, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, n_arg, "type", val) ); | |
| if (arg->arg_default_val) { | |
| NAPI_CALL( napi_create_string_utf8(env, arg->arg_default_val, NAPI_AUTO_LENGTH, &val) ); | |
| } else { | |
| NAPI_CALL( napi_get_null(env, &val) ); | |
| } | |
| NAPI_CALL(napi_set_named_property(env, n_arg, "default", val) ); | |
| if (arg->min_max_enum) { | |
| NAPI_CALL( napi_create_string_utf8(env, arg->min_max_enum, NAPI_AUTO_LENGTH, &val) ); | |
| } else { | |
| NAPI_CALL( napi_get_null(env, &val) ); | |
| } | |
| NAPI_CALL(napi_set_named_property(env, n_arg, "min_max_enum", val) ); | |
| NAPI_CALL( napi_create_int32(env, arg->flags, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, n_arg, "flags", val) ); | |
| char argval[GF_PROP_DUMP_ARG_SIZE]; | |
| gf_filter_get_arg_str(f, arg->arg_name, argval); | |
| NAPI_CALL( napi_create_string_utf8(env, argval, NAPI_AUTO_LENGTH, &val) ); | |
| NAPI_CALL(napi_set_named_property(env, n_arg, "value", val) ); | |
| NAPI_CALL(napi_set_element(env, res, a_idx, n_arg) ); | |
| a_idx++; | |
| } | |
| return res; | |
| } | |
| /* custom filters*/ | |
| static GF_Err napi_filter_cbk_process(GF_Filter *filter) | |
| { | |
| napi_status status; | |
| napi_value obj, fun_val, res; | |
| NAPI_Filter *napi_f = gf_filter_get_rt_udta(filter); | |
| if (!napi_f) return GF_SERVICE_ERROR; | |
| napi_get_reference_value(napi_f->env, napi_f->ref, &obj); | |
| Bool has_fun = GF_FALSE; | |
| napi_has_named_property(napi_f->env, obj, "process", (bool *) &has_fun); | |
| if (!has_fun) return GF_EOS; | |
| status = napi_get_named_property(napi_f->env, obj, "process", &fun_val); | |
| if (status != napi_ok) return GF_BAD_PARAM; | |
| status = napi_make_callback(napi_f->env, napi_f->async_ctx, obj, fun_val, 0, NULL, &res); | |
| GF_Err e = GF_OK; | |
| napi_get_value_int32(napi_f->env, res, &e); | |
| return e; | |
| } | |
| void filter_inputpid_finalize(napi_env env, void* finalize_data, void* finalize_hint) | |
| { | |
| //finalize_hint contains napi_pid | |
| NAPI_FilterPid *napi_pid = (NAPI_FilterPid *)finalize_hint; | |
| gf_free(napi_pid); | |
| } | |
| static NAPI_FilterPid *wrap_filter_pid(napi_env env, GF_Filter *filter, GF_FilterPid *pid, Bool is_input) | |
| { | |
| NAPI_FilterPid *napi_pid; | |
| napi_value pid_obj; | |
| GF_SAFEALLOC(napi_pid, NAPI_FilterPid); | |
| if (!napi_pid) { | |
| return NULL; | |
| } | |
| gf_filter_pid_set_udta(pid, napi_pid); | |
| napi_pid->f = filter; | |
| napi_pid->pid = pid; | |
| napi_create_object(env, &pid_obj); | |
| napi_create_reference(env, pid_obj, 1, &napi_pid->ref); | |
| napi_type_tag_object(env, pid_obj, &filterpid_tag); | |
| napi_wrap(env, pid_obj, pid, is_input ? filter_inputpid_finalize : dummy_finalize, napi_pid, NULL); | |
| //define all props for custom filter | |
| napi_property_descriptor filterpid_properties[] = { | |
| { "name", NULL, NULL, filterpid_getter, filterpid_setter, NULL, napi_enumerable, &FS_PROP_PID_NAME}, | |
| { "filter_name", NULL, NULL, filterpid_getter, NULL, NULL, napi_enumerable, &FS_PROP_PID_FILTERNAME}, | |
| { "eos", NULL, NULL, filterpid_getter, filterpid_setter, NULL, napi_enumerable, &FS_PROP_PID_EOS}, | |
| { "has_seen_eos", NULL, NULL, filterpid_getter, NULL, NULL, napi_enumerable, &FS_PROP_PID_HAS_SEEN_EOS}, | |
| { "eos_received", NULL, NULL, filterpid_getter, NULL, NULL, napi_enumerable, &FS_PROP_PID_HAS_RECEIVED}, | |
| { "would_block", NULL, NULL, filterpid_getter, NULL, NULL, napi_enumerable, &FS_PROP_PID_WOULD_BLOCK}, | |
| { "sparse", NULL, NULL, filterpid_getter, NULL, NULL, napi_enumerable, &FS_PROP_PID_SPARSE}, | |
| { "max_buffer", NULL, NULL, filterpid_getter, filterpid_setter, NULL, napi_enumerable, &FS_PROP_PID_MAX_BUFFER}, | |
| { "buffer", NULL, NULL, filterpid_getter, NULL, NULL, napi_enumerable, &FS_PROP_PID_BUFFER}, | |
| { "buffer_full", NULL, NULL, filterpid_getter, NULL, NULL, napi_enumerable, &FS_PROP_PID_BUFFER_FULL}, | |
| { "first_empty", NULL, NULL, filterpid_getter, NULL, NULL, napi_enumerable, &FS_PROP_PID_FIRST_EMPTY}, | |
| { "first_cts", NULL, NULL, filterpid_getter, NULL, NULL, napi_enumerable, &FS_PROP_PID_FIRST_CTS}, | |
| { "nb_pck_queued", NULL, NULL, filterpid_getter, NULL, NULL, napi_enumerable, &FS_PROP_PID_NB_PCK_QUEUED}, | |
| { "timescale", NULL, NULL, filterpid_getter, NULL, NULL, napi_enumerable, &FS_PROP_PID_TIMESCALE}, | |
| { "min_pck_dur", NULL, NULL, filterpid_getter, NULL, NULL, napi_enumerable, &FS_PROP_PID_MIN_PCK_DUR}, | |
| { "playing", NULL, NULL, filterpid_getter, NULL, NULL, napi_enumerable, &FS_PROP_PID_PLAYING}, | |
| { "next_ts", NULL, NULL, filterpid_getter, NULL, NULL, napi_enumerable, &FS_PROP_PID_NEXT_TS}, | |
| { "has_decoder", NULL, NULL, filterpid_getter, NULL, NULL, napi_enumerable, &FS_PROP_PID_HAS_DECODER}, | |
| { "remove", 0, filterpid_remove, 0, 0, 0, napi_enumerable, 0 }, | |
| { "enum_props", 0, filterpid_enum_props, 0, 0, 0, napi_enumerable, 0 }, | |
| { "get_prop", 0, filterpid_get_prop, 0, 0, 0, napi_enumerable, 0 }, | |
| { "get_info", 0, filterpid_get_info, 0, 0, 0, napi_enumerable, 0 }, | |
| { "copy_props", 0, filterpid_copy_props, 0, 0, 0, napi_enumerable, 0 }, | |
| { "reset_props", 0, filterpid_reset_props, 0, 0, 0, napi_enumerable, 0 }, | |
| { "set_prop", 0, filterpid_set_prop, 0, 0, 0, napi_enumerable, 0 }, | |
| { "set_info", 0, filterpid_set_info, 0, 0, 0, napi_enumerable, 0 }, | |
| { "clear_eos", 0, filterpid_clear_eos, 0, 0, 0, napi_enumerable, 0 }, | |
| { "check_caps", 0, filterpid_check_caps, 0, 0, 0, napi_enumerable, 0 }, | |
| { "discard_block", 0, filterpid_discard_block, 0, 0, 0, napi_enumerable, 0 }, | |
| { "allow_direct_dispatch", 0, filterpid_allow_direct_dispatch, 0, 0, 0, napi_enumerable, 0 }, | |
| { "get_clock_type", 0, filterpid_get_clock_type, 0, 0, 0, napi_enumerable, 0 }, | |
| { "get_clock_timestamp", 0, filterpid_get_clock_timestamp, 0, 0, 0, napi_enumerable, 0 }, | |
| { "is_filter_in_parents", 0, filterpid_is_filter_in_parents, 0, 0, 0, napi_enumerable, 0 }, | |
| { "get_buffer_occupancy", 0, filterpid_get_buffer_occupancy, 0, 0, 0, napi_enumerable, 0 }, | |
| { "loose_connect", 0, filterpid_loose_connect, 0, 0, 0, napi_enumerable, 0 }, | |
| { "set_framing", 0, filterpid_set_framing, 0, 0, 0, napi_enumerable, 0 }, | |
| { "set_clock_mode", 0, filterpid_set_clock_mode, 0, 0, 0, napi_enumerable, 0 }, | |
| { "set_discard", 0, filterpid_set_discard, 0, 0, 0, napi_enumerable, 0 }, | |
| { "require_source_id", 0, filterpid_require_source_id, 0, 0, 0, napi_enumerable, 0 }, | |
| { "recompute_dts", 0, filterpid_recompute_dts, 0, 0, 0, napi_enumerable, 0 }, | |
| { "resolve_template", 0, filterpid_resolve_template, 0, 0, 0, napi_enumerable, 0 }, | |
| { "query_cap", 0, filterpid_query_cap, 0, 0, 0, napi_enumerable, 0 }, | |
| { "negotiate_cap", 0, filterpid_negotiate_cap, 0, 0, 0, napi_enumerable, 0 }, | |
| { "get_packet", 0, filterpid_get_packet, 0, 0, 0, napi_enumerable, 0 }, | |
| { "drop_packet", 0, filterpid_drop_packet, 0, 0, 0, napi_enumerable, 0 }, | |
| { "send_event", 0, filterpid_send_event, 0, 0, 0, napi_enumerable, 0 }, | |
| { "forward", 0, filterpid_forward, 0, 0, 0, napi_enumerable, 0 }, | |
| { "new_pck", 0, filterpid_new_pck, 0, 0, 0, napi_enumerable, 0 }, | |
| { "new_pck_ref", 0, filterpid_new_pck_ref, 0, 0, 0, napi_enumerable, 0 }, | |
| { "new_pck_shared", 0, filterpid_new_pck_shared, 0, 0, 0, napi_enumerable, 0 }, | |
| { "new_pck_copy", 0, filterpid_new_pck_copy, 0, 0, 0, napi_enumerable, 0 }, | |
| { "new_pck_clone", 0, filterpid_new_pck_clone, 0, 0, 0, napi_enumerable, 0 }, | |
| }; | |
| napi_define_properties(env, pid_obj, sizeof(filterpid_properties)/sizeof(napi_property_descriptor), filterpid_properties); | |
| return napi_pid; | |
| } | |
| static GF_Err napi_filter_cbk_configure(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove) | |
| { | |
| GF_Err e=GF_OK; | |
| napi_value args[2]; | |
| napi_status status; | |
| napi_value obj, fun_val, res; | |
| NAPI_Filter *napi_f = gf_filter_get_rt_udta(filter); | |
| Bool has_fun = GF_FALSE; | |
| if (!napi_f) return GF_SERVICE_ERROR; | |
| napi_get_reference_value(napi_f->env, napi_f->ref, &obj); | |
| NAPI_FilterPid *napi_pid = gf_filter_pid_get_udta(pid); | |
| napi_has_named_property(napi_f->env, obj, "configure_pid", (bool *) &has_fun); | |
| if (has_fun) { | |
| status = napi_get_named_property(napi_f->env, obj, "configure_pid", &fun_val); | |
| if (status != napi_ok) has_fun = GF_FALSE; | |
| } | |
| if (is_remove) { | |
| if (napi_pid) { | |
| if (has_fun) { | |
| napi_get_reference_value(napi_f->env, napi_pid->ref, &args[0]); | |
| napi_get_boolean(napi_f->env, (bool) 1, &args[1]); | |
| status = napi_make_callback(napi_f->env, napi_f->async_ctx, obj, fun_val, 2, args, &res); | |
| status = napi_get_value_int32(napi_f->env, res, &e); | |
| } | |
| u32 rcount; | |
| napi_reference_unref(napi_f->env, napi_pid->ref, &rcount); | |
| napi_delete_reference(napi_f->env, napi_pid->ref); | |
| napi_pid->pid = NULL; | |
| gf_filter_pid_set_udta(pid, NULL); | |
| } | |
| return e; | |
| } | |
| if (!napi_pid) { | |
| napi_pid = wrap_filter_pid(napi_f->env, filter, pid, GF_TRUE); | |
| if (!napi_pid) return GF_OUT_OF_MEM; | |
| } | |
| if (has_fun) { | |
| napi_get_reference_value(napi_f->env, napi_pid->ref, &args[0]); | |
| napi_get_boolean(napi_f->env, (bool) 0, &args[1]); | |
| status = napi_make_callback(napi_f->env, napi_f->async_ctx, obj, fun_val, 2, args, &res); | |
| status = napi_get_value_int32(napi_f->env, res, &e); | |
| } | |
| return e; | |
| } | |
| static GF_Err napi_filter_cbk_reconfigure_output(GF_Filter *filter, GF_FilterPid *pid) | |
| { | |
| GF_Err e=GF_OK; | |
| napi_value args[1]; | |
| napi_status status; | |
| napi_value obj, fun_val, res; | |
| NAPI_Filter *napi_f = gf_filter_get_rt_udta(filter); | |
| Bool has_fun = GF_FALSE; | |
| if (!napi_f) return GF_SERVICE_ERROR; | |
| napi_get_reference_value(napi_f->env, napi_f->ref, &obj); | |
| NAPI_FilterPid *napi_pid = gf_filter_pid_get_udta(pid); | |
| if (!napi_pid) return GF_SERVICE_ERROR; | |
| napi_has_named_property(napi_f->env, obj, "reconfigure_output", (bool *) &has_fun); | |
| if (has_fun) { | |
| status = napi_get_named_property(napi_f->env, obj, "reconfigure_output", &fun_val); | |
| if (status != napi_ok) has_fun = GF_FALSE; | |
| } | |
| //by default return not supported | |
| e = GF_NOT_SUPPORTED; | |
| if (has_fun) { | |
| napi_get_reference_value(napi_f->env, napi_pid->ref, &args[0]); | |
| status = napi_make_callback(napi_f->env, napi_f->async_ctx, obj, fun_val, 1, args, &res); | |
| status = napi_get_value_int32(napi_f->env, res, &e); | |
| } | |
| return e; | |
| } | |
| static Bool napi_filter_cbk_process_event(GF_Filter *filter, const GF_FilterEvent *evt) | |
| { | |
| napi_status status; | |
| napi_value obj, fun_val, arg, res; | |
| NAPI_Filter *napi_f = gf_filter_get_rt_udta(filter); | |
| if (!napi_f) return GF_FALSE; | |
| napi_get_reference_value(napi_f->env, napi_f->ref, &obj); | |
| Bool has_fun = GF_FALSE; | |
| napi_has_named_property(napi_f->env, obj, "process_event", (bool *) &has_fun); | |
| if (!has_fun) return GF_FALSE; | |
| status = napi_get_named_property(napi_f->env, obj, "process_event", &fun_val); | |
| if (status != napi_ok) return GF_FALSE; | |
| arg = wrap_filterevent(napi_f->env, (GF_FilterEvent *)evt, NULL); | |
| status = napi_make_callback(napi_f->env, napi_f->async_ctx, obj, fun_val, 1, &arg, &res); | |
| bool ret = GF_FALSE; | |
| napi_get_value_bool(napi_f->env, res, &ret); | |
| return ret ? GF_TRUE : GF_FALSE; | |
| } | |
| static u32 FS_PROP_CUST_BLOCK_ENABLED = 0; | |
| static u32 FS_PROP_CUST_OUTPUT_BUFFER = 1; | |
| static u32 FS_PROP_CUST_PLAYOUT_BUFFER = 2; | |
| static u32 FS_PROP_CUST_SINKS_DONE = 3; | |
| static u32 FS_PROP_CUST_EVTS_QUEUED = 4; | |
| static u32 FS_PROP_CUST_CK_HINT_TIME = 5; | |
| static u32 FS_PROP_CUST_CK_HINT_MEDIATIME = 6; | |
| static u32 FS_PROP_CUST_CONNECTIONS_PENDING = 7; | |
| napi_value filter_cust_getter(napi_env env, napi_callback_info info) | |
| { | |
| void *magic; | |
| napi_status status; | |
| napi_value this_val, ret; | |
| NAPI_CALL(napi_get_cb_info(env, info, NULL, NULL, &this_val, &magic) ); | |
| FILTER | |
| FILTER_CUST | |
| if (magic == &FS_PROP_CUST_BLOCK_ENABLED) { | |
| NAPI_CALL( napi_get_boolean(env, (bool) gf_filter_block_enabled(f), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_CUST_OUTPUT_BUFFER) { | |
| u32 val; | |
| gf_filter_get_output_buffer_max(f, &val, NULL); | |
| NAPI_CALL( napi_create_uint32(env, val, &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_CUST_PLAYOUT_BUFFER) { | |
| u32 val; | |
| gf_filter_get_output_buffer_max(f, NULL, &val); | |
| NAPI_CALL( napi_create_uint32(env, val, &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_CUST_SINKS_DONE) { | |
| NAPI_CALL( napi_get_boolean(env, gf_filter_all_sinks_done(f), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_CUST_EVTS_QUEUED) { | |
| NAPI_CALL( napi_create_uint32(env, gf_filter_get_num_events_queued(f), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_CUST_CK_HINT_TIME) { | |
| u64 val; | |
| gf_filter_get_clock_hint(f, &val, NULL); | |
| NAPI_CALL( napi_create_int64(env, val, &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_CUST_CK_HINT_MEDIATIME) { | |
| GF_Fraction64 val; | |
| gf_filter_get_clock_hint(f, NULL, &val); | |
| ret = frac64_to_napi(env, &val); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_CUST_CONNECTIONS_PENDING) { | |
| NAPI_CALL( napi_get_boolean(env, gf_filter_connections_pending(f), &ret) ); | |
| return ret; | |
| } | |
| return NULL; | |
| } | |
| napi_value filter_push_cap(napi_env env, napi_callback_info info) | |
| { | |
| GF_Err e; | |
| char *pname=NULL; | |
| u32 p4cc=0; | |
| GF_PropertyValue prop; | |
| NARG_ARGS_THIS(5, 3) | |
| FILTER | |
| FILTER_CUST | |
| napi_valuetype rtype; | |
| napi_typeof(env, argv[0], &rtype); | |
| if (rtype == napi_string) { | |
| NARG_STR(prop_name, 0, NULL) | |
| pname = prop_name; | |
| p4cc = gf_props_get_id(pname); | |
| } else { | |
| NARG_U32(pcode, 0, 0) | |
| p4cc = pcode; | |
| } | |
| NARG_U32(flags, 2, 0) | |
| NARG_U32(priority, 3, 0) | |
| NARG_U32(custom_type, 4, 0) | |
| if (p4cc) pname = NULL; | |
| //clone prop name as prop from napi may reuse the global arg buffer | |
| if (pname) pname = gf_strdup(pname); | |
| //convert napi_prop to prop | |
| status = prop_from_napi(env, argv[1], p4cc, pname, custom_type, &prop); | |
| if (status == napi_ok) { | |
| e = gf_filter_push_caps(f, p4cc, &prop, pname, flags, priority); | |
| } else { | |
| e = GF_BAD_PARAM; | |
| } | |
| if (pname) gf_free(pname); | |
| //do not reset property, it is now owned by filter | |
| if (e) { | |
| napi_throw_error(env, gf_error_to_string(e), "Error assigning cap"); | |
| } | |
| return NULL; | |
| } | |
| napi_value filter_update_status(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(2, 1) | |
| FILTER | |
| FILTER_CUST | |
| NARG_STR(fstatus, 0, NULL) | |
| NARG_U32(percent, 1, 0) | |
| gf_filter_update_status(f, percent, fstatus); | |
| return NULL; | |
| } | |
| napi_value filter_reschedule(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1, 0) | |
| FILTER | |
| FILTER_CUST | |
| NARG_U32(when, 0, 0) | |
| if (when) | |
| gf_filter_ask_rt_reschedule(f, when); | |
| else | |
| gf_filter_post_process_task(f); | |
| return NULL; | |
| } | |
| napi_value filter_notify_failure(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(2, 1) | |
| FILTER | |
| FILTER_CUST | |
| NARG_S32(err, 0, 0) | |
| NARG_U32(type, 1, GF_SETUP_ERROR) | |
| if (type==GF_NOTIF_ERROR) | |
| gf_filter_notification_failure(f, err, GF_FALSE); | |
| else if (type==GF_NOTIF_ERROR_AND_DISCONNECT) | |
| gf_filter_notification_failure(f, err, GF_TRUE); | |
| else | |
| gf_filter_setup_failure(f, err); | |
| return NULL; | |
| } | |
| napi_value filter_make_sticky(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTER | |
| FILTER_CUST | |
| gf_filter_make_sticky(f); | |
| return NULL; | |
| } | |
| napi_value filter_prevent_blocking(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1, 1) | |
| FILTER | |
| FILTER_CUST | |
| NARG_BOOL(enable, 0, 0) | |
| gf_filter_prevent_blocking(f, enable); | |
| return NULL; | |
| } | |
| napi_value filter_block_eos(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1, 1) | |
| FILTER | |
| FILTER_CUST | |
| NARG_BOOL(enable, 0, 0) | |
| gf_filter_block_eos(f, enable); | |
| return NULL; | |
| } | |
| napi_value filter_set_max_pids(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1, 1) | |
| FILTER | |
| FILTER_CUST | |
| NARG_U32(max_extra_pids, 0, 0) | |
| gf_filter_set_max_extra_input_pids(f, max_extra_pids); | |
| return NULL; | |
| } | |
| napi_value filter_hint_clock(napi_env env, napi_callback_info info) | |
| { | |
| GF_Fraction64 frac; | |
| NARG_ARGS_THIS(2, 2) | |
| FILTER | |
| FILTER_CUST | |
| NARG_U64(clock_us, 0, 0) | |
| NAPI_CALL(frac64_from_napi(env, argv[1], &frac) ); | |
| gf_filter_hint_single_clock(f, clock_us, frac); | |
| return NULL; | |
| } | |
| napi_value filter_new_pid(napi_env env, napi_callback_info info) | |
| { | |
| napi_value ret; | |
| NARG_THIS | |
| FILTER | |
| FILTER_CUST | |
| GF_FilterPid *opid = gf_filter_pid_new(f); | |
| if (!opid) { | |
| napi_throw_error(env, NULL, "Failed to create new output PID"); | |
| return NULL; | |
| } | |
| NAPI_FilterPid *napi_pid = wrap_filter_pid(env, f, opid, GF_FALSE); | |
| if (!napi_pid) { | |
| napi_throw_error(env, NULL, "Failed to create new output PID"); | |
| return NULL; | |
| } | |
| napi_get_reference_value(env, napi_pid->ref, &ret); | |
| return ret; | |
| } | |
| napi_value filter_print_connections(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTER | |
| FILTER_CUST | |
| gf_filter_print_all_connections(f, NULL); | |
| return NULL; | |
| } | |
| #define FILTER_SESSION\ | |
| GF_FilterSession *fs;\ | |
| bool _tag;\ | |
| NAPI_CALL( napi_unwrap(env, this_val, (void**) &fs) );\ | |
| NAPI_CALL( napi_check_object_type_tag(env, this_val, &filtersession_tag, &_tag) );\ | |
| if (!_tag) {\ | |
| napi_throw_error(env, NULL, "Not a FilterSession object");\ | |
| return NULL;\ | |
| }\ | |
| static u32 FS_PROP_LAST_TASK = 0; | |
| static u32 FS_PROP_NB_FILTERS = 1; | |
| static u32 FS_PROP_HTTP_RATE = 2; | |
| static u32 FS_PROP_MAX_HTTP_RATE = 3; | |
| napi_value fs_getter(napi_env env, napi_callback_info info) | |
| { | |
| void *magic; | |
| napi_status status; | |
| napi_value this_val, ret; | |
| NAPI_CALL(napi_get_cb_info(env, info, NULL, NULL, &this_val, &magic) ); | |
| FILTER_SESSION | |
| if (magic == &FS_PROP_LAST_TASK) { | |
| NAPI_CALL( napi_get_boolean(env, (bool) gf_fs_is_last_task(fs), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_NB_FILTERS) { | |
| NAPI_CALL( napi_create_uint32(env, gf_fs_get_filters_count(fs), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_HTTP_RATE) { | |
| NAPI_CALL( napi_create_uint32(env, gf_fs_get_http_rate(fs), &ret) ); | |
| return ret; | |
| } | |
| if (magic == &FS_PROP_MAX_HTTP_RATE) { | |
| NAPI_CALL( napi_create_uint32(env, gf_fs_get_http_max_rate(fs), &ret) ); | |
| return ret; | |
| } | |
| return NULL; | |
| } | |
| napi_value fs_setter(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS_MAGIC(1, 1) | |
| FILTER_SESSION | |
| if (magic == &FS_PROP_MAX_HTTP_RATE) { | |
| NARG_U32(new_rate, 0, 0); | |
| gf_fs_set_http_max_rate(fs, new_rate); | |
| return NULL; | |
| } | |
| return NULL; | |
| } | |
| napi_value fs_run(napi_env env, napi_callback_info info) | |
| { | |
| GF_Err e; | |
| NARG_THIS | |
| FILTER_SESSION | |
| NAPI_GPAC | |
| NAPI_Session *napi_fs = gf_fs_get_rt_udta(fs); | |
| gpac->fs_rmt_handler = napi_fs; | |
| if (gf_list_count(gpac->rmt_new_clients) || gf_list_count(gpac->rmt_task_calls) || gf_list_count(gpac->rmt_delete_calls)) { | |
| gf_fs_post_user_task(fs, fs_flush_rmt, napi_fs, "RMTFlush"); | |
| } | |
| e = gf_fs_run(fs); | |
| //TODO: run flush rmt again if not blocking? | |
| if (e>=GF_OK) { | |
| e = gf_fs_get_last_connect_error(fs); | |
| if (e>=GF_OK) | |
| e = gf_fs_get_last_process_error(fs); | |
| } | |
| if (e<0) | |
| napi_throw_error(env, gf_error_to_string(e), "Error running session");\ | |
| while (gf_list_count(napi_fs->defer_filters)) { | |
| GF_Filter *filter = gf_list_pop_front(napi_fs->defer_filters); | |
| const char *fname = "on_filter_new"; | |
| Bool has_fun = GF_FALSE; | |
| napi_has_named_property(env, this_val, fname, (bool *) &has_fun); | |
| if (has_fun) { | |
| napi_value fun_val; | |
| status = napi_get_named_property(env, this_val, fname, &fun_val); | |
| if (status == napi_ok) { | |
| napi_value res, arg = fs_wrap_filter(env, napi_fs->fs, filter); | |
| status = napi_make_callback(env, napi_fs->async_ctx, this_val, fun_val, 1, &arg, &res); | |
| } | |
| } | |
| } | |
| return NULL; | |
| } | |
| napi_value fs_wrap_filter(napi_env env, GF_FilterSession *fs, GF_Filter *filter) | |
| { | |
| napi_status status; | |
| napi_value obj; | |
| NAPI_Filter *napi_f = gf_filter_get_rt_udta(filter); | |
| if (!filter) { | |
| NAPI_CALL( napi_get_null(env, &obj) ); | |
| return obj; | |
| } | |
| napi_property_descriptor filter_properties[] = { | |
| { "name", NULL, NULL, filter_getter, filter_setter, NULL, napi_enumerable, &FILTER_PROP_NAME}, | |
| { "ID", NULL, NULL, filter_getter, NULL, NULL, napi_enumerable, &FILTER_PROP_ID}, | |
| { "nb_ipid", NULL, NULL, filter_getter, NULL, NULL, napi_enumerable, &FILTER_PROP_IPIDS}, | |
| { "nb_opid", NULL, NULL, filter_getter, NULL, NULL, napi_enumerable, &FILTER_PROP_OPIDS}, | |
| { "status", NULL, NULL, filter_getter, NULL, NULL, napi_enumerable, &FILTER_PROP_STATUS}, | |
| { "bytes_done", NULL, NULL, filter_getter, NULL, NULL, napi_enumerable, &FILTER_PROP_BYTES_DONE}, | |
| { "remove", 0, filter_remove, 0, 0, 0, napi_enumerable, 0 }, | |
| { "update", 0, filter_update, 0, 0, 0, napi_enumerable, 0 }, | |
| { "set_source", 0, filter_set_source, 0, 0, 0, napi_enumerable, 0 }, | |
| { "set_source_restricted", 0, filter_set_source_restricted, 0, 0, 0, napi_enumerable, 0 }, | |
| { "insert", 0, filter_insert, 0, 0, 0, napi_enumerable, 0 }, | |
| { "reconnect", 0, filter_reconnect, 0, 0, 0, napi_enumerable, 0 }, | |
| { "ipid_prop", 0, filter_ipid_prop, 0, 0, 0, napi_enumerable, 0 }, | |
| { "ipid_enum_props", 0, filter_ipid_enum_props, 0, 0, 0, napi_enumerable, 0 }, | |
| { "opid_prop", 0, filter_opid_prop, 0, 0, 0, napi_enumerable, 0 }, | |
| { "opid_enum_props", 0, filter_opid_enum_props, 0, 0, 0, napi_enumerable, 0 }, | |
| { "ipid_source", 0, filter_ipid_source, 0, 0, 0, napi_enumerable, 0 }, | |
| { "opid_sinks", 0, filter_opid_sinks, 0, 0, 0, napi_enumerable, 0 }, | |
| { "all_args", 0, filter_all_args, 0, 0, 0, napi_enumerable, 0 }, | |
| { "get_info", 0, filter_get_info, 0, 0, 0, napi_enumerable, 0 }, | |
| { "get_statistics", 0, filter_get_statistics, 0, 0, 0, napi_enumerable, 0 }, | |
| { "require_source_id", 0, filter_require_source_id, 0, 0, 0, napi_enumerable, 0 }, | |
| { "bind", 0, filter_bind, 0, 0, 0, napi_enumerable, 0 }, | |
| { "ipid_stats", 0, filter_ipid_stats, 0, 0, 0, napi_enumerable, 0 }, | |
| { "opid_stats", 0, filter_opid_stats, 0, 0, 0, napi_enumerable, 0 }, | |
| { "probe_link", 0, filter_probe_link, 0, 0, 0, napi_enumerable, 0 }, | |
| { "get_destinations", 0, filter_get_destinations, 0, 0, 0, napi_enumerable, 0 }, | |
| }; | |
| if (napi_f) { | |
| NAPI_CALL ( napi_get_reference_value(env, napi_f->ref, &obj) ); | |
| return obj; | |
| } | |
| GF_SAFEALLOC(napi_f, NAPI_Filter); | |
| napi_f->f = filter; | |
| napi_f->fs = fs; | |
| napi_f->env = env; | |
| NAPI_CALL( napi_create_object(env, &obj) ); | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(filter_properties)/sizeof(napi_property_descriptor), filter_properties) ); | |
| NAPI_CALL( napi_create_reference(env, obj, 1, &napi_f->ref) ); | |
| NAPI_CALL( napi_type_tag_object(env, obj, &filter_tag) ); | |
| NAPI_CALL( napi_wrap(env, obj, filter, dummy_finalize, NULL, NULL) ); | |
| gf_filter_set_rt_udta(filter, napi_f); | |
| return obj; | |
| } | |
| napi_value fs_load_src_dst(napi_env env, napi_callback_info info, Bool is_source) | |
| { | |
| GF_Err e; | |
| NARG_ARGS_THIS(2, 1) | |
| FILTER_SESSION | |
| NARG_STR(url, 0, NULL) | |
| if (!url) { | |
| napi_throw_error(env, NULL, "Missing URL"); | |
| return NULL; | |
| } | |
| NARG_STR_ALLOC(parent_url, 1, NULL); | |
| GF_Filter *filter; | |
| if (is_source) | |
| filter = gf_fs_load_source(fs, url, NULL, parent_url, &e); | |
| else | |
| filter = gf_fs_load_destination(fs, url, NULL, parent_url, &e); | |
| if (parent_url) gf_free(parent_url); | |
| if (e<0) | |
| napi_throw_error(env, gf_error_to_string(e), is_source ? "Failed to load source filter" : "Failed to load destination filter"); | |
| return fs_wrap_filter(env, fs, filter); | |
| } | |
| napi_value fs_load_src(napi_env env, napi_callback_info info) | |
| { | |
| return fs_load_src_dst(env, info, GF_TRUE); | |
| } | |
| napi_value fs_load_dst(napi_env env, napi_callback_info info) | |
| { | |
| return fs_load_src_dst(env, info, GF_FALSE); | |
| } | |
| napi_value fs_load_filter(napi_env env, napi_callback_info info) | |
| { | |
| GF_Err e; | |
| NARG_ARGS_THIS(1, 1) | |
| FILTER_SESSION | |
| NARG_STR(url, 0, NULL) | |
| GF_Filter *filter = gf_fs_load_filter(fs, url, &e); | |
| if (e<0) | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to load filter"); | |
| return fs_wrap_filter(env, fs, filter); | |
| } | |
| typedef struct | |
| { | |
| napi_async_context ctx; | |
| napi_ref ref; | |
| napi_env env; | |
| NAPI_Session *napi_fs; | |
| } NAPI_Task; | |
| Bool fs_task_exec(GF_FilterSession *fsess, void *callback, u32 *reschedule_ms) | |
| { | |
| Bool ret = GF_TRUE; | |
| napi_status status; | |
| napi_value res, obj_val, fun_val; | |
| NAPI_Task *task = (NAPI_Task *) callback; | |
| napi_env env = (napi_env) task->env; | |
| status = napi_get_reference_value(env, task->ref, &obj_val); | |
| if (status != napi_ok) goto exit; | |
| status = napi_get_named_property(env, obj_val, "execute", &fun_val); | |
| if (status != napi_ok) goto exit; | |
| status = napi_make_callback(env, task->napi_fs->async_ctx, obj_val, fun_val, 0, NULL, &res); | |
| if (status != napi_ok) goto exit; | |
| napi_valuetype rtype; | |
| napi_typeof(env, res, &rtype); | |
| if (rtype == napi_boolean) { | |
| bool val; | |
| status = napi_get_value_bool(env, res, &val); | |
| ret = val ? GF_TRUE : GF_FALSE; | |
| } else if ((rtype==napi_number) || (rtype==napi_bigint)) { | |
| status = napi_get_value_uint32(env, res, reschedule_ms); | |
| if (status == napi_ok) | |
| ret = GF_TRUE; | |
| } else { | |
| ret = GF_FALSE; | |
| } | |
| exit: | |
| if (status != napi_ok) { | |
| napi_throw_error(env, NULL, "Failed to execute task"); | |
| } | |
| if (gf_fs_in_final_flush(fsess)) | |
| ret = GF_FALSE; | |
| if (!ret) { | |
| u32 rcount; | |
| napi_reference_unref(env, task->ref, &rcount); | |
| napi_delete_reference(env, task->ref); | |
| gf_free(task); | |
| } | |
| return ret; | |
| } | |
| napi_value fs_post_task(napi_env env, napi_callback_info info) | |
| { | |
| napi_value global, fun_name, fun_val; | |
| NARG_ARGS_THIS(2, 1) | |
| FILTER_SESSION | |
| NARG_STR(log_name, 1, "NapiTask"); | |
| NAPI_CALL( napi_create_string_utf8(env, "execute", NAPI_AUTO_LENGTH, &fun_name) ); | |
| status = napi_get_property(env, argv[0], fun_name, &fun_val); | |
| if (status != napi_ok) { | |
| napi_throw_error(env, NULL, "No execute method on object"); | |
| return NULL; | |
| } | |
| napi_valuetype rtype; | |
| napi_typeof(env, fun_val, &rtype); | |
| if (rtype != napi_function) { | |
| napi_throw_error(env, NULL, "property execute is not a function"); | |
| return NULL; | |
| } | |
| NAPI_Task *task; | |
| GF_SAFEALLOC(task, NAPI_Task); | |
| if (!task) { | |
| napi_throw_error(env, NULL, "Failed to allocate task"); | |
| return NULL; | |
| } | |
| task->napi_fs = gf_fs_get_rt_udta(fs); | |
| NAPI_CALL( napi_get_global(env, &global) ); | |
| NAPI_CALL( napi_create_string_utf8(env, "TaskExecute", NAPI_AUTO_LENGTH, &fun_name) ); | |
| status = napi_create_reference(env, argv[0], 1, &task->ref); | |
| task->env = env; | |
| gf_fs_post_user_task(fs, fs_task_exec, task, log_name); | |
| return NULL; | |
| } | |
| napi_value fs_abort(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1, 0) | |
| FILTER_SESSION | |
| NARG_U32(flush, 0, 0); | |
| gf_fs_abort(fs, flush); | |
| return NULL; | |
| } | |
| napi_value fs_lock(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1, 1) | |
| FILTER_SESSION | |
| NARG_BOOL(lock, 0, 0); | |
| gf_fs_lock_filters(fs, lock); | |
| return NULL; | |
| } | |
| napi_value fs_reporting(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1, 1) | |
| FILTER_SESSION | |
| NARG_BOOL(do_report, 0, 0); | |
| gf_fs_enable_reporting(fs, do_report); | |
| return NULL; | |
| } | |
| napi_value fs_print_stats(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTER_SESSION | |
| gf_fs_print_stats(fs); | |
| return NULL; | |
| } | |
| napi_value fs_print_graph(napi_env env, napi_callback_info info) | |
| { | |
| NARG_THIS | |
| FILTER_SESSION | |
| gf_fs_print_connections(fs); | |
| return NULL; | |
| } | |
| napi_value fs_get_filter(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS(1, 1) | |
| FILTER_SESSION | |
| NARG_U32(fidx, 0, 0); | |
| GF_Filter *f = gf_fs_get_filter(fs, fidx); | |
| if (!f) { | |
| napi_throw_error(env, NULL, "Invalid filter index"); | |
| return NULL; | |
| } | |
| return fs_wrap_filter(env, fs, f); | |
| } | |
| napi_value fs_fire_event(napi_env env, napi_callback_info info) | |
| { | |
| Bool res; | |
| napi_value ret; | |
| GF_Filter *f = NULL; | |
| GF_FilterEvent *evt = NULL; | |
| NARG_ARGS_THIS(3, 1) | |
| FILTER_SESSION | |
| NAPI_CALL( napi_check_object_type_tag(env, argv[0], &filterevent_tag, &_tag) ); | |
| if (!_tag) { | |
| napi_throw_error(env, NULL, "Not a FilterEvent object"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_unwrap(env, argv[0], (void**) &evt) ); | |
| if (argc>1) { | |
| NAPI_CALL( napi_check_object_type_tag(env, argv[1], &filter_tag, &_tag) ); | |
| if (!_tag) { | |
| napi_throw_error(env, NULL, "Not a Filter object"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_unwrap(env, argv[1], (void**) &f) ); | |
| } | |
| NARG_BOOL(upstream, 2, GF_FALSE); | |
| res = gf_fs_fire_event(fs, f, evt, upstream); | |
| NAPI_CALL(napi_get_boolean(env, res, &ret) ); | |
| return ret; | |
| } | |
| napi_value fs_is_supported_mime(napi_env env, napi_callback_info info) | |
| { | |
| napi_value val; | |
| NARG_ARGS_THIS(1, 1) | |
| FILTER_SESSION | |
| NARG_STR(mime, 0, NULL); | |
| NAPI_CALL( napi_get_boolean(env, gf_fs_is_supported_mime(fs, mime), &val) ); | |
| return val; | |
| } | |
| napi_value fs_is_supported_source(napi_env env, napi_callback_info info) | |
| { | |
| bool is_ok; | |
| napi_value val; | |
| NARG_ARGS_THIS(2, 1) | |
| FILTER_SESSION | |
| NARG_STR(url, 0, NULL); | |
| NARG_STR_ALLOC(parent_url, 1, NULL); | |
| is_ok = gf_fs_is_supported_source(fs, url, parent_url); | |
| if (parent_url) gf_free(parent_url); | |
| NAPI_CALL( napi_get_boolean(env, is_ok, &val) ); | |
| return val; | |
| } | |
| napi_value fs_new_filter(napi_env env, napi_callback_info info) | |
| { | |
| NAPI_Filter *napi_f=NULL; | |
| GF_Err e=GF_OK; | |
| napi_value obj, async_name; | |
| NARG_ARGS_THIS(2, 0) | |
| FILTER_SESSION | |
| NARG_STR(fname, 0, "Custom"); | |
| NARG_U32(flags, 1, 0); | |
| GF_Filter *new_f = gf_fs_new_filter(fs, fname, flags | GF_FS_REG_MAIN_THREAD, &e); | |
| if (!new_f) { | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to create custom filter"); | |
| return NULL; | |
| } | |
| //inherit from regular filter | |
| obj = fs_wrap_filter(env, fs, new_f); | |
| napi_f = gf_filter_get_rt_udta(new_f); | |
| napi_f->is_custom = GF_TRUE; | |
| //define all props for custom filter | |
| napi_property_descriptor curstom_filter_properties[] = { | |
| { "block_enabled", NULL, NULL, filter_cust_getter, NULL, NULL, napi_enumerable, &FS_PROP_CUST_BLOCK_ENABLED}, | |
| { "output_buffer", NULL, NULL, filter_cust_getter, NULL, NULL, napi_enumerable, &FS_PROP_CUST_OUTPUT_BUFFER}, | |
| { "playout_buffer", NULL, NULL, filter_cust_getter, NULL, NULL, napi_enumerable, &FS_PROP_CUST_PLAYOUT_BUFFER}, | |
| { "sinks_done", NULL, NULL, filter_cust_getter, NULL, NULL, napi_enumerable, &FS_PROP_CUST_SINKS_DONE}, | |
| { "nb_evts_queued", NULL, NULL, filter_cust_getter, NULL, NULL, napi_enumerable, &FS_PROP_CUST_EVTS_QUEUED}, | |
| { "clock_hint_time", NULL, NULL, filter_cust_getter, NULL, NULL, napi_enumerable, &FS_PROP_CUST_CK_HINT_TIME}, | |
| { "clock_hint_mediatime", NULL, NULL, filter_cust_getter, NULL, NULL, napi_enumerable, &FS_PROP_CUST_CK_HINT_MEDIATIME}, | |
| { "connections_pending", NULL, NULL, filter_cust_getter, NULL, NULL, napi_enumerable, &FS_PROP_CUST_CONNECTIONS_PENDING}, | |
| { "push_cap", 0, filter_push_cap, 0, 0, 0, napi_enumerable, 0 }, | |
| { "update_status", 0, filter_update_status, 0, 0, 0, napi_enumerable, 0 }, | |
| { "reschedule", 0, filter_reschedule, 0, 0, 0, napi_enumerable, 0 }, | |
| { "notify_failure", 0, filter_notify_failure, 0, 0, 0, napi_enumerable, 0 }, | |
| { "make_sticky", 0, filter_make_sticky, 0, 0, 0, napi_enumerable, 0 }, | |
| { "prevent_blocking", 0, filter_prevent_blocking, 0, 0, 0, napi_enumerable, 0 }, | |
| { "block_eos", 0, filter_block_eos, 0, 0, 0, napi_enumerable, 0 }, | |
| { "set_max_pids", 0, filter_set_max_pids, 0, 0, 0, napi_enumerable, 0 }, | |
| { "hint_clock", 0, filter_hint_clock, 0, 0, 0, napi_enumerable, 0 }, | |
| { "new_pid", 0, filter_new_pid, 0, 0, 0, napi_enumerable, 0 }, | |
| { "print_connections", 0, filter_print_connections, 0, 0, 0, napi_enumerable, 0 }, | |
| }; | |
| NAPI_CALL(napi_define_properties(env, obj, sizeof(curstom_filter_properties)/sizeof(napi_property_descriptor), curstom_filter_properties) ); | |
| //setup callbacks | |
| e = gf_filter_set_configure_ckb(new_f, napi_filter_cbk_configure); | |
| if (!e) e = gf_filter_set_process_ckb(new_f, napi_filter_cbk_process); | |
| if (!e) e = gf_filter_set_process_event_ckb(new_f, napi_filter_cbk_process_event); | |
| if (!e) e = gf_filter_set_reconfigure_output_ckb(new_f, napi_filter_cbk_reconfigure_output); | |
| if (e) { | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to assign custom filter callbacks"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_create_string_utf8(env, "AsyncFilterTask", NAPI_AUTO_LENGTH, &async_name) ); | |
| NAPI_CALL( napi_async_init(env, obj, async_name, &napi_f->async_ctx) ); | |
| return obj; | |
| } | |
| void del_fs(napi_env env, void* finalize_data, void* finalize_hint) | |
| { | |
| GF_FilterSession *fs = (GF_FilterSession *)finalize_data; | |
| NAPI_Session *napi_fs = gf_fs_get_rt_udta(fs); | |
| if (napi_fs) { | |
| u32 rcount; | |
| napi_reference_unref(env, napi_fs->ref, &rcount); | |
| napi_delete_reference(env, napi_fs->ref); | |
| napi_async_destroy(env, napi_fs->async_ctx); | |
| napi_fs->ref = NULL; | |
| napi_fs->async_ctx = NULL; | |
| } | |
| gf_fs_del(fs); | |
| if (napi_fs) { | |
| if (napi_fs->defer_filters) gf_list_del(napi_fs->defer_filters); | |
| gf_free(napi_fs); | |
| } | |
| } | |
| void fs_on_filter_creation(void *udta, GF_Filter *filter, Bool is_destroy) | |
| { | |
| napi_value fs_val, fun_val; | |
| napi_status status; | |
| NAPI_Session *napi_fs = (NAPI_Session *)udta; | |
| NAPI_Filter *napi_f = (NAPI_Filter *) gf_filter_get_rt_udta(filter); | |
| napi_env env = napi_fs->env; | |
| //set to NULL means final nodeJS addon shutdown, do not notify | |
| if (napi_fs->async_ctx != NULL) { | |
| GPAC_NAPI *gpac; | |
| if ( napi_get_instance_data(env, (void **) &gpac) != napi_ok) { | |
| return; | |
| } | |
| //creation callback are not guaranteed to run in main thread, defer notif to end of fs.run() | |
| if (!is_destroy && (gpac->main_thid != gf_th_id() ) ) { | |
| if (!napi_fs->defer_filters) napi_fs->defer_filters = gf_list_new(); | |
| gf_list_add(napi_fs->defer_filters, filter); | |
| return; | |
| } | |
| //destroy callbacks are always scheduled on main thread, but remove filter if in pending list | |
| if (is_destroy) | |
| gf_list_del_item(napi_fs->defer_filters, filter); | |
| status = napi_get_reference_value(env, napi_fs->ref, &fs_val); | |
| if (status == napi_ok) { | |
| const char *fname = is_destroy ? "on_filter_del" : "on_filter_new"; | |
| Bool has_fun = GF_FALSE; | |
| napi_has_named_property(env, fs_val, fname, (bool *) &has_fun); | |
| if (has_fun) { | |
| status = napi_get_named_property(env, fs_val, fname, &fun_val); | |
| if (status == napi_ok) { | |
| napi_value res, arg = fs_wrap_filter(env, napi_fs->fs, filter); | |
| status = napi_make_callback(env, napi_fs->async_ctx, fs_val, fun_val, 1, &arg, &res); | |
| } | |
| } | |
| } | |
| } | |
| if (is_destroy && napi_f) { | |
| u32 rcount; | |
| gf_filter_set_rt_udta(filter, NULL); | |
| napi_reference_unref(napi_f->env, napi_f->ref, &rcount); | |
| napi_delete_reference(napi_f->env, napi_f->ref); | |
| //extenral object was bound, destroy ref | |
| if (napi_f->binding_ref != NULL) { | |
| const char *fname = gf_filter_get_name(filter); | |
| if (fname && !strcmp(fname, "dashin")) | |
| dashin_detach_binding(napi_f); | |
| if (fname && !strcmp(fname, "httpour")) | |
| httpout_detach_binding(napi_f); | |
| napi_reference_unref(napi_f->env, napi_f->binding_ref, &rcount); | |
| napi_delete_reference(napi_f->env, napi_f->binding_ref); | |
| } | |
| //cleanup our bindings, we don't use finalizer as we don't know the order of destruction | |
| if (napi_f->is_custom) { | |
| u32 i, pids = gf_filter_get_opid_count(filter); | |
| for (i=0; i<pids; i++) { | |
| GF_FilterPid *pid = gf_filter_get_opid(filter, i); | |
| NAPI_FilterPid *napi_pid = (NAPI_FilterPid *) gf_filter_pid_get_udta(pid); | |
| if (napi_pid) { | |
| if (napi_pid->pck_refs) { | |
| while (gf_list_count(napi_pid->pck_refs)) { | |
| NAPI_FilterPacket *napi_pck = gf_list_pop_back(napi_pid->pck_refs); | |
| napi_pck->pid_refs = NULL; | |
| } | |
| gf_list_del(napi_pid->pck_refs); | |
| } | |
| gf_free(napi_pid); | |
| } | |
| } | |
| } | |
| gf_free(napi_f); | |
| } | |
| } | |
| Bool napi_gpac_event_proc(void *udta, GF_Event *event) | |
| { | |
| napi_status status; | |
| bool has_fun; | |
| GF_FilterEvent fevt; | |
| napi_value fs_val, fun_val, evt, res; | |
| GPAC_NAPI *gpac; | |
| NAPI_Session *napi_fs = (NAPI_Session *) udta; | |
| status = napi_get_instance_data(napi_fs->env, (void **) &gpac); | |
| if (status != napi_ok) return GF_FALSE; | |
| if (gpac->main_thid != gf_th_id()) { | |
| #ifndef GPAC_DISABLE_SVG | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_SCRIPT, ("[NodeJS] GPAC main event callback from different thread not implemented, ignoring event %s\n", gf_dom_event_get_name(event->type) )); | |
| #else | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_SCRIPT, ("[NodeJS] GPAC main event callback from different thread not implemented, ignoring event %d\n", event->type)); | |
| #endif | |
| return GF_FALSE; | |
| } | |
| status = napi_get_reference_value(napi_fs->env, napi_fs->ref, &fs_val); | |
| if (status != napi_ok) return GF_FALSE; | |
| napi_has_named_property(napi_fs->env, fs_val, "on_event", (bool *) &has_fun); | |
| if (!has_fun) return GF_FALSE; | |
| status = napi_get_named_property(napi_fs->env, fs_val, "on_event", &fun_val); | |
| if (status != napi_ok) return GF_FALSE; | |
| fevt.base.type = GF_FEVT_USER; | |
| fevt.base.on_pid = NULL; | |
| fevt.user_event.event = *event; | |
| evt = wrap_filterevent(napi_fs->env, &fevt, NULL); | |
| //make event | |
| status = napi_make_callback(napi_fs->env, napi_fs->async_ctx, fs_val, fun_val, 1, &evt, &res); | |
| if (status != napi_ok) return GF_FALSE; | |
| napi_get_value_bool(napi_fs->env, res, &has_fun); | |
| return has_fun ? GF_TRUE : GF_FALSE; | |
| } | |
| napi_value new_fs(napi_env env, napi_callback_info info) | |
| { | |
| NAPI_Session *napi_fs; | |
| napi_value fun_name; | |
| NARG_ARGS_THIS(1, 0); | |
| NARG_U32(flags, 0, 0); | |
| NAPI_GPAC | |
| gpac->is_init = GF_TRUE; | |
| GF_SAFEALLOC(napi_fs, NAPI_Session); | |
| if (!napi_fs) { | |
| napi_throw_error(env, NULL, "Failed to allocate filter session"); | |
| return NULL; | |
| } | |
| GF_FilterSession *fs = gf_fs_new_defaults(flags); | |
| if (!fs) { | |
| gf_free(napi_fs); | |
| napi_throw_error(env, NULL, "Failed to allocate filter session"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_wrap(env, this_val, fs, del_fs, NULL, NULL) ); | |
| NAPI_CALL( napi_type_tag_object(env, this_val, &filtersession_tag) ); | |
| napi_fs->fs = fs; | |
| napi_fs->env = env; | |
| //we force all setup tasks on main thread, async IO with node is not implemented yet here | |
| gf_fs_set_filter_creation_callback(fs, fs_on_filter_creation, napi_fs, GF_TRUE); | |
| NAPI_CALL( napi_create_reference(env, this_val, 1, &napi_fs->ref) ); | |
| NAPI_CALL( napi_create_string_utf8(env, "AsyncFilterSessionTask", NAPI_AUTO_LENGTH, &fun_name) ); | |
| NAPI_CALL( napi_async_init(env, this_val, fun_name, &napi_fs->async_ctx) ); | |
| gf_fs_set_ui_callback(fs, napi_gpac_event_proc, napi_fs); | |
| if (! (flags & GF_FS_FLAG_NON_BLOCKING)) | |
| napi_fs->blocking = GF_TRUE; | |
| return this_val; | |
| } | |
| napi_status init_fs_class(napi_env env, napi_value exports, GPAC_NAPI *inst) | |
| { | |
| napi_status status; | |
| napi_property_descriptor fs_properties[] = { | |
| { "last_task", NULL, NULL, fs_getter, NULL, NULL, napi_enumerable, &FS_PROP_LAST_TASK}, | |
| { "nb_filters", NULL, NULL, fs_getter, NULL, NULL, napi_enumerable, &FS_PROP_NB_FILTERS}, | |
| { "http_bitrate", NULL, NULL, fs_getter, NULL, NULL, napi_enumerable, &FS_PROP_HTTP_RATE}, | |
| { "http_max_bitrate", NULL, NULL, fs_getter, fs_setter, NULL, napi_enumerable, &FS_PROP_MAX_HTTP_RATE}, | |
| { "run", 0, fs_run, 0, 0, 0, napi_enumerable, 0 }, | |
| { "load_src", 0, fs_load_src, 0, 0, 0, napi_enumerable, 0 }, | |
| { "load_dst", 0, fs_load_dst, 0, 0, 0, napi_enumerable, 0 }, | |
| { "load", 0, fs_load_filter, 0, 0, 0, napi_enumerable, 0 }, | |
| { "post_task", 0, fs_post_task, 0, 0, 0, napi_enumerable, 0 }, | |
| { "abort", 0, fs_abort, 0, 0, 0, napi_enumerable, 0 }, | |
| { "lock", 0, fs_lock, 0, 0, 0, napi_enumerable, 0 }, | |
| //TODO move as attrib | |
| { "reporting", 0, fs_reporting, 0, 0, 0, napi_enumerable, 0 }, | |
| { "print_stats", 0, fs_print_stats, 0, 0, 0, napi_enumerable, 0 }, | |
| { "print_graph", 0, fs_print_graph, 0, 0, 0, napi_enumerable, 0 }, | |
| { "get_filter", 0, fs_get_filter, 0, 0, 0, napi_enumerable, 0 }, | |
| { "fire_event", 0, fs_fire_event, 0, 0, 0, napi_enumerable, 0 }, | |
| { "is_supported_mime", 0, fs_is_supported_mime, 0, 0, 0, napi_enumerable, 0 }, | |
| { "is_supported_source", 0, fs_is_supported_source, 0, 0, 0, napi_enumerable, 0 }, | |
| { "new_filter", 0, fs_new_filter, 0, 0, 0, napi_enumerable, 0 }, | |
| }; | |
| napi_value cons; | |
| status = napi_define_class(env, "FilterSession", NAPI_AUTO_LENGTH , new_fs, NULL, sizeof(fs_properties)/sizeof(napi_property_descriptor), fs_properties, &cons); | |
| if (status != napi_ok) return status; | |
| status = napi_set_named_property(env, exports, "FilterSession", cons); | |
| if (status != napi_ok) return status; | |
| return napi_ok; | |
| } | |
| static u32 FEVT_PROP_TYPE = 0; | |
| static u32 FEVT_PROP_START_RANGE = 1; | |
| static u32 FEVT_PROP_END_RANGE = 2; | |
| static u32 FEVT_PROP_SPEED = 3; | |
| static u32 FEVT_PROP_FROM_PCK = 4; | |
| static u32 FEVT_PROP_HW_BUFFER_RESET = 5; | |
| static u32 FEVT_PROP_INITIAL_BCAST = 6; | |
| static u32 FEVT_PROP_TS_BASED = 7; | |
| static u32 FEVT_PROP_FULL_FILE_ONLY = 8; | |
| static u32 FEVT_PROP_FORCED_DASH_SWITCH = 9; | |
| static u32 FEVT_PROP_DROP_NON_REF = 10; | |
| static u32 FEVT_PROP_NO_BYTERANGE = 11; | |
| static u32 FEVT_PROP_START_OFFSET = 12; | |
| static u32 FEVT_PROP_END_OFFSET = 13; | |
| static u32 FEVT_PROP_HINT_BLOCK_SIZE = 14; | |
| static u32 FEVT_PROP_SOURCE_SWITCH = 15; | |
| static u32 FEVT_PROP_SWITCH_IS_INIT_SEG = 16; | |
| static u32 FEVT_PROP_SKIP_CACHE_EXP = 17; | |
| static u32 FEVT_PROP_SSIZE_URL = 18; | |
| static u32 FEVT_PROP_SSIZE_MEDIA_START = 19; | |
| static u32 FEVT_PROP_SSIZE_MEDIA_END = 20; | |
| static u32 FEVT_PROP_SSIZE_IDX_START = 21; | |
| static u32 FEVT_PROP_SSIZE_IDX_END = 22; | |
| static u32 FEVT_PROP_SSIZE_IS_INIT = 23; | |
| static u32 FEVT_PROP_SSIZE_IS_SHIFT = 24; | |
| static u32 FEVT_PROP_QS_UP = 25; | |
| static u32 FEVT_PROP_QS_DEP_GROUP_IDX = 26; | |
| static u32 FEVT_PROP_QS_QIDX = 27; | |
| static u32 FEVT_PROP_QS_TILEMODE = 28; | |
| static u32 FEVT_PROP_QS_DEGRADATION = 29; | |
| static u32 FEVT_PROP_MIN_X = 30; | |
| static u32 FEVT_PROP_MIN_Y = 31; | |
| static u32 FEVT_PROP_MAX_X = 32; | |
| static u32 FEVT_PROP_MAX_Y = 33; | |
| static u32 FEVT_PROP_IS_GAZE = 34; | |
| static u32 FEVT_PROP_MAX_BUFFER = 35; | |
| static u32 FEVT_PROP_MAX_PLAYOUT = 36; | |
| static u32 FEVT_PROP_MIN_PLAYOUT = 37; | |
| static u32 FEVT_PROP_PID_ONLY = 38; | |
| static u32 FEVT_PROP_UI_MOUSE_X = 39; | |
| static u32 FEVT_PROP_UI_MOUSE_Y = 40; | |
| static u32 FEVT_PROP_UI_MOUSE_WHEEL = 41; | |
| static u32 FEVT_PROP_UI_MOUSE_BUTTON = 42; | |
| static u32 FEVT_PROP_UI_MT_ROTATE = 43; | |
| static u32 FEVT_PROP_UI_MT_PINCH = 44; | |
| static u32 FEVT_PROP_UI_MT_FINGERS = 45; | |
| static u32 FEVT_PROP_UI_KEY_CODE = 46; | |
| static u32 FEVT_PROP_UI_KEY_NAME = 47; | |
| static u32 FEVT_PROP_UI_KEY_MODS = 48; | |
| static u32 FEVT_PROP_UI_KEY_HW = 49; | |
| static u32 FEVT_PROP_UI_SHOWTYPE = 50; | |
| static u32 FEVT_PROP_UI_CLIPBOARD = 51; | |
| static u32 FEVT_PROP_UI_WIDTH = 52; | |
| static u32 FEVT_PROP_UI_HEIGHT = 53; | |
| static u32 FEVT_PROP_UI_MOVE_X = 54; | |
| static u32 FEVT_PROP_UI_MOVE_Y = 55; | |
| static u32 FEVT_PROP_UI_MOVE_RELATIVE = 56; | |
| static u32 FEVT_PROP_UI_MOVE_ALIGNX = 57; | |
| static u32 FEVT_PROP_UI_MOVE_ALIGNY = 58; | |
| static u32 FEVT_PROP_UI_CAPTION = 59; | |
| static u32 FEVT_PROP_FILE_DEL = 60; | |
| static u32 FEVT_PROP_UI_TYPE = 61; | |
| static u32 FEVT_PROP_NTP_REF = 62; | |
| static u32 FEVT_PROP_NAME = 63; | |
| static u32 FEVT_PROP_UI_NAME = 64; | |
| static u32 FEVT_PROP_PID = 65; | |
| static u32 FEVT_PROP_TO_PCK = 66; | |
| static u32 FEVT_PROP_ORIG_DELAY = 67; | |
| static u32 FEVT_PROP_HINT_FIRST_DTS = 68; | |
| static u32 FEVT_PROP_HINT_START_OFFSET = 69; | |
| static u32 FEVT_PROP_HINT_END_OFFSET = 70; | |
| #define FILTEREVENT\ | |
| GF_FilterEvent *evt=NULL;\ | |
| bool _tag;\ | |
| NAPI_CALL( napi_check_object_type_tag(env, this_val, &filterevent_tag, &_tag) );\ | |
| if (!_tag) {\ | |
| napi_throw_error(env, NULL, "Not a FilterEvent object");\ | |
| return NULL;\ | |
| }\ | |
| NAPI_CALL( napi_unwrap(env, this_val, (void**) &evt) );\ | |
| #define ALL_EVENT_FIELDS \ | |
| EVT_DBL(FEVT_PROP_START_RANGE, evt->play.start_range);\ | |
| EVT_DBL(FEVT_PROP_END_RANGE, evt->play.end_range);\ | |
| EVT_DBL(FEVT_PROP_SPEED, evt->play.speed);\ | |
| EVT_U32(FEVT_PROP_FROM_PCK, evt->play.from_pck);\ | |
| EVT_BOOL(FEVT_PROP_HW_BUFFER_RESET, evt->play.hw_buffer_reset);\ | |
| EVT_BOOL(FEVT_PROP_INITIAL_BCAST, evt->play.initial_broadcast_play);\ | |
| EVT_U32(FEVT_PROP_TS_BASED, evt->play.timestamp_based);\ | |
| EVT_BOOL(FEVT_PROP_FULL_FILE_ONLY, evt->play.full_file_only);\ | |
| EVT_BOOL(FEVT_PROP_FORCED_DASH_SWITCH, evt->play.forced_dash_segment_switch);\ | |
| EVT_BOOL(FEVT_PROP_DROP_NON_REF, evt->play.drop_non_ref);\ | |
| EVT_BOOL(FEVT_PROP_NO_BYTERANGE, evt->play.no_byterange_forward);\ | |
| EVT_U32(FEVT_PROP_TO_PCK, evt->play.to_pck);\ | |
| EVT_U32(FEVT_PROP_ORIG_DELAY, evt->play.orig_delay);\ | |
| EVT_U64(FEVT_PROP_HINT_FIRST_DTS, evt->play.hint_first_dts);\ | |
| EVT_U64(FEVT_PROP_HINT_START_OFFSET, evt->play.hint_start_offset);\ | |
| EVT_U64(FEVT_PROP_HINT_END_OFFSET, evt->play.hint_end_offset);\ | |
| EVT_U64(FEVT_PROP_START_OFFSET, evt->seek.start_offset);\ | |
| EVT_U64(FEVT_PROP_END_OFFSET, evt->seek.end_offset);\ | |
| EVT_U32(FEVT_PROP_HINT_BLOCK_SIZE, evt->seek.hint_block_size);\ | |
| EVT_STR(FEVT_PROP_SOURCE_SWITCH, evt->seek.source_switch);\ | |
| EVT_BOOL(FEVT_PROP_SWITCH_IS_INIT_SEG, evt->seek.is_init_segment);\ | |
| EVT_BOOL(FEVT_PROP_SKIP_CACHE_EXP, evt->seek.skip_cache_expiration);\ | |
| EVT_STR(FEVT_PROP_SSIZE_URL, evt->seg_size.seg_url);\ | |
| EVT_U64(FEVT_PROP_SSIZE_MEDIA_START, evt->seg_size.media_range_start);\ | |
| EVT_U64(FEVT_PROP_SSIZE_MEDIA_END, evt->seg_size.media_range_end);\ | |
| EVT_U64(FEVT_PROP_SSIZE_IDX_START, evt->seg_size.idx_range_start);\ | |
| EVT_U64(FEVT_PROP_SSIZE_IDX_END, evt->seg_size.idx_range_end);\ | |
| EVT_BOOL(FEVT_PROP_SSIZE_IS_INIT, evt->seg_size.is_init);\ | |
| EVT_BOOL(FEVT_PROP_SSIZE_IS_SHIFT, evt->seg_size.is_shift);\ | |
| EVT_BOOL(FEVT_PROP_QS_UP, evt->quality_switch.up);\ | |
| EVT_U32(FEVT_PROP_QS_DEP_GROUP_IDX, evt->quality_switch.dependent_group_index);\ | |
| EVT_S32(FEVT_PROP_QS_QIDX, evt->quality_switch.q_idx);\ | |
| EVT_U32(FEVT_PROP_QS_TILEMODE, evt->quality_switch.set_tile_mode_plus_one);\ | |
| EVT_U32(FEVT_PROP_QS_DEGRADATION, evt->quality_switch.quality_degradation);\ | |
| EVT_U32(FEVT_PROP_MIN_X, evt->visibility_hint.min_x);\ | |
| EVT_U32(FEVT_PROP_MIN_Y, evt->visibility_hint.min_y);\ | |
| EVT_U32(FEVT_PROP_MAX_X, evt->visibility_hint.max_x);\ | |
| EVT_U32(FEVT_PROP_MAX_Y, evt->visibility_hint.max_y);\ | |
| EVT_BOOL(FEVT_PROP_IS_GAZE, evt->visibility_hint.is_gaze);\ | |
| EVT_U32(FEVT_PROP_MAX_BUFFER, evt->buffer_req.max_buffer_us);\ | |
| EVT_U32(FEVT_PROP_MAX_PLAYOUT, evt->buffer_req.max_playout_us);\ | |
| EVT_U32(FEVT_PROP_MIN_PLAYOUT, evt->buffer_req.min_playout_us);\ | |
| EVT_BOOL(FEVT_PROP_PID_ONLY, evt->buffer_req.pid_only);\ | |
| EVT_STR(FEVT_PROP_FILE_DEL, evt->file_del.url);\ | |
| EVT_U64(FEVT_PROP_NTP_REF, evt->ntp.ntp);\ | |
| EVT_U32(FEVT_PROP_UI_TYPE, evt->user_event.event.type);\ | |
| if (evt->user_event.event.type==GF_EVENT_MULTITOUCH) {\ | |
| EVT_FIX(FEVT_PROP_UI_MOUSE_X, evt->user_event.event.mtouch.x);\ | |
| EVT_FIX(FEVT_PROP_UI_MOUSE_Y, evt->user_event.event.mtouch.y);\ | |
| } else {\ | |
| EVT_S32(FEVT_PROP_UI_MOUSE_X, evt->user_event.event.mouse.x);\ | |
| EVT_S32(FEVT_PROP_UI_MOUSE_Y, evt->user_event.event.mouse.y);\ | |
| }\ | |
| EVT_FIX(FEVT_PROP_UI_MOUSE_WHEEL, evt->user_event.event.mouse.wheel_pos);\ | |
| EVT_U32(FEVT_PROP_UI_MOUSE_BUTTON, evt->user_event.event.mouse.button);\ | |
| if (evt->user_event.event.type<=GF_EVENT_MOUSEWHEEL) {\ | |
| EVT_U32(FEVT_PROP_UI_KEY_MODS, evt->user_event.event.mouse.key_states);\ | |
| } else {\ | |
| EVT_U32(FEVT_PROP_UI_KEY_MODS, evt->user_event.event.key.flags);\ | |
| }\ | |
| EVT_FIX(FEVT_PROP_UI_MT_ROTATE, evt->user_event.event.mtouch.rotation);\ | |
| EVT_FIX(FEVT_PROP_UI_MT_PINCH, evt->user_event.event.mtouch.pinch);\ | |
| EVT_U32(FEVT_PROP_UI_MT_FINGERS, evt->user_event.event.mtouch.num_fingers);\ | |
| EVT_U32(FEVT_PROP_UI_KEY_CODE, evt->user_event.event.key.key_code);\ | |
| EVT_U32(FEVT_PROP_UI_KEY_HW, evt->user_event.event.key.hw_code);\ | |
| EVT_STR(FEVT_PROP_UI_CLIPBOARD, evt->user_event.event.clipboard.text);\ | |
| EVT_U32(FEVT_PROP_UI_WIDTH, evt->user_event.event.size.width);\ | |
| EVT_U32(FEVT_PROP_UI_HEIGHT, evt->user_event.event.size.height);\ | |
| EVT_U32(FEVT_PROP_UI_SHOWTYPE, evt->user_event.event.show.show_type);\ | |
| EVT_S32(FEVT_PROP_UI_MOVE_X, evt->user_event.event.move.x);\ | |
| EVT_S32(FEVT_PROP_UI_MOVE_Y, evt->user_event.event.move.y);\ | |
| EVT_U32(FEVT_PROP_UI_MOVE_RELATIVE, evt->user_event.event.move.relative);\ | |
| EVT_U32(FEVT_PROP_UI_MOVE_ALIGNX, evt->user_event.event.move.align_x);\ | |
| EVT_U32(FEVT_PROP_UI_MOVE_ALIGNY, evt->user_event.event.move.align_y);\ | |
| EVT_STR(FEVT_PROP_UI_CAPTION, evt->user_event.event.caption.caption);\ | |
| napi_value fevt_getter(napi_env env, napi_callback_info info) | |
| { | |
| void *magic; | |
| napi_status status; | |
| napi_value this_val, ret; | |
| NAPI_CALL(napi_get_cb_info(env, info, NULL, NULL, &this_val, &magic) ); | |
| FILTEREVENT | |
| #define EVT_DBL(_magic_val, _field) \ | |
| if (magic == &_magic_val) {\ | |
| NAPI_CALL( napi_create_double(env, _field, &ret) );\ | |
| return ret;\ | |
| } | |
| #define EVT_FIX(_magic_val, _field) \ | |
| if (magic == &_magic_val) {\ | |
| NAPI_CALL( napi_create_double(env, FIX2FLT(_field), &ret) );\ | |
| return ret;\ | |
| } | |
| #define EVT_U32(_magic_val, _field) \ | |
| if (magic == &_magic_val) {\ | |
| NAPI_CALL( napi_create_uint32(env, (u32) _field, &ret) );\ | |
| return ret;\ | |
| } | |
| #define EVT_S32(_magic_val, _field) \ | |
| if (magic == &_magic_val) {\ | |
| NAPI_CALL( napi_create_int32(env, (s32) _field, &ret) );\ | |
| return ret;\ | |
| } | |
| #define EVT_STR(_magic_val, _field) \ | |
| if (magic == &_magic_val) {\ | |
| if (_field) {\ | |
| NAPI_CALL( napi_create_string_utf8(env, _field, NAPI_AUTO_LENGTH, &ret) );\ | |
| } else {\ | |
| NAPI_CALL( napi_get_null(env, &ret) );\ | |
| }\ | |
| return ret;\ | |
| } | |
| #define EVT_U64(_magic_val, _field) \ | |
| if (magic == &_magic_val) {\ | |
| NAPI_CALL( napi_create_int64(env, (s64) _field, &ret) );\ | |
| return ret;\ | |
| } | |
| #define EVT_BOOL(_magic_val, _field) \ | |
| if (magic == &_magic_val) {\ | |
| NAPI_CALL( napi_get_boolean(env, (bool) _field, &ret) );\ | |
| return ret;\ | |
| } | |
| //read-only props | |
| EVT_U32(FEVT_PROP_TYPE, evt->base.type); | |
| EVT_STR(FEVT_PROP_NAME, gf_filter_event_name(evt->base.type) ); | |
| #ifndef GPAC_DISABLE_SVG | |
| EVT_STR(FEVT_PROP_UI_NAME, gf_dom_event_get_name(evt->user_event.event.type) ); | |
| #endif | |
| //all read/write props | |
| ALL_EVENT_FIELDS | |
| //custom fileds | |
| #ifndef GPAC_DISABLE_SVG | |
| EVT_STR(FEVT_PROP_UI_KEY_NAME, gf_dom_get_key_name(evt->user_event.event.key.key_code)); | |
| #endif | |
| if (magic == &FEVT_PROP_PID) { | |
| NAPI_FilterPid *napi_pid = evt->base.on_pid ? gf_filter_pid_get_udta(evt->base.on_pid) : NULL; | |
| if (napi_pid) { | |
| napi_get_reference_value(env, napi_pid->ref, &ret); | |
| } else { | |
| NAPI_CALL( napi_get_null(env, &ret) ); | |
| } | |
| return ret; | |
| } | |
| #undef EVT_DBL | |
| #undef EVT_FIX | |
| #undef EVT_U32 | |
| #undef EVT_S32 | |
| #undef EVT_STR | |
| #undef EVT_U64 | |
| #undef EVT_BOOL | |
| return NULL; | |
| } | |
| napi_value fevt_setter(napi_env env, napi_callback_info info) | |
| { | |
| NARG_ARGS_THIS_MAGIC(1, 1) | |
| FILTEREVENT | |
| #define EVT_U32(_magic_val, _field)\ | |
| if (magic == &_magic_val) {\ | |
| NARG_U32(val, 0, 0);\ | |
| _field = val;\ | |
| return NULL;\ | |
| } | |
| #define EVT_S32(_magic_val, _field)\ | |
| if (magic == &_magic_val) {\ | |
| NARG_S32(val, 0, 0);\ | |
| _field = val;\ | |
| return NULL;\ | |
| } | |
| #define EVT_U64(_magic_val, _field)\ | |
| if (magic == &_magic_val) {\ | |
| NARG_U64(val, 0, 0);\ | |
| _field = val;\ | |
| return NULL;\ | |
| } | |
| #define EVT_BOOL(_magic_val, _field)\ | |
| if (magic == &_magic_val) {\ | |
| NARG_BOOL(val, 0, 0);\ | |
| _field = val;\ | |
| return NULL;\ | |
| } | |
| #define EVT_DBL(_magic_val, _field)\ | |
| if (magic == &_magic_val) {\ | |
| NARG_DBL(val, 0, 0);\ | |
| _field = val;\ | |
| return NULL;\ | |
| } | |
| #define EVT_FIX(_magic_val, _field)\ | |
| if (magic == &_magic_val) {\ | |
| NARG_DBL(val, 0, 0);\ | |
| _field = FLT2FIX(val);\ | |
| return NULL;\ | |
| } | |
| #define EVT_STR(_magic_val, _field)\ | |
| if (magic == &_magic_val) {\ | |
| NARG_STR(val, 0, 0);\ | |
| _field = val;\ | |
| return NULL;\ | |
| } | |
| //all read/write props | |
| ALL_EVENT_FIELDS | |
| //custom fields | |
| #ifndef GPAC_DISABLE_SVG | |
| if (magic == &FEVT_PROP_UI_KEY_NAME) { | |
| NARG_STR(val, 0, 0); | |
| evt->user_event.event.key.key_code = gf_dom_get_key_type(val); | |
| return NULL; | |
| } | |
| #endif | |
| return NULL; | |
| } | |
| void del_evt(napi_env env, void* finalize_data, void* finalize_hint) | |
| { | |
| GF_FilterEvent *fevt = (GF_FilterEvent *)finalize_data; | |
| gf_free(fevt); | |
| } | |
| napi_value wrap_filterevent(napi_env env, GF_FilterEvent *evt, napi_value *for_val) | |
| { | |
| napi_status status; | |
| napi_value obj; | |
| if (for_val) { | |
| obj = *for_val; | |
| } else { | |
| NAPI_CALL(napi_create_object(env, &obj) ); | |
| } | |
| napi_callback evt_set = for_val ? fevt_setter : NULL; | |
| napi_property_descriptor fevt_base_properties[] = { | |
| { "type", NULL, NULL, fevt_getter, NULL, NULL, napi_enumerable, &FEVT_PROP_TYPE}, | |
| { "pid", NULL, NULL, fevt_getter, NULL, NULL, napi_enumerable, &FEVT_PROP_PID}, | |
| { "name", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_NAME}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_base_properties)/sizeof(napi_property_descriptor), fevt_base_properties) ); | |
| //define properties based on event type | |
| if (evt->base.type == GF_FEVT_PLAY) { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "start_range", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_START_RANGE}, | |
| { "end_range", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_END_RANGE}, | |
| { "speed", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_SPEED}, | |
| { "from_pck", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_FROM_PCK}, | |
| { "hw_buffer_reset", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_HW_BUFFER_RESET}, | |
| { "initial_broadcast_play", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_INITIAL_BCAST}, | |
| { "timestamp_based", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_TS_BASED}, | |
| { "full_file_only", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_FULL_FILE_ONLY}, | |
| { "forced_dash_segment_switch", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_FORCED_DASH_SWITCH}, | |
| { "drop_non_ref", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_DROP_NON_REF}, | |
| { "no_byterange_forward", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_NO_BYTERANGE}, | |
| { "to_pck", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_TO_PCK}, | |
| { "orig_delay", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_ORIG_DELAY}, | |
| { "hint_first_dts", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_HINT_FIRST_DTS}, | |
| { "hint_start_offset", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_HINT_START_OFFSET}, | |
| { "hint_end_offset", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_HINT_END_OFFSET}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| else if (evt->base.type == GF_FEVT_SET_SPEED) { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "speed", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_SPEED}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| else if ((evt->base.type == GF_FEVT_STOP) || (evt->base.type == GF_FEVT_PLAY_HINT)) { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "forced_dash_segment_switch", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_FORCED_DASH_SWITCH}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| else if (evt->base.type == GF_FEVT_SOURCE_SEEK) { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "start_offset", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_START_OFFSET}, | |
| { "end_offset", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_END_OFFSET}, | |
| { "hint_block_size", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_HINT_BLOCK_SIZE}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| else if (evt->base.type == GF_FEVT_SOURCE_SWITCH) { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "start_offset", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_START_OFFSET}, | |
| { "end_offset", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_END_OFFSET}, | |
| { "source_switch", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_SOURCE_SWITCH}, | |
| { "is_init_segment", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_SWITCH_IS_INIT_SEG}, | |
| { "skip_cache_expiration", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_SKIP_CACHE_EXP}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| else if (evt->base.type == GF_FEVT_SEGMENT_SIZE) { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "seg_url", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_SSIZE_URL}, | |
| { "media_range_start", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_SSIZE_MEDIA_START}, | |
| { "media_range_end", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_SSIZE_MEDIA_END}, | |
| { "idx_range_start", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_SSIZE_IDX_START}, | |
| { "idx_range_end", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_SSIZE_IDX_END}, | |
| { "is_init", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_SSIZE_IS_INIT}, | |
| { "is_shift", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_SSIZE_IS_SHIFT}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| else if (evt->base.type == GF_FEVT_QUALITY_SWITCH) { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "up", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_QS_UP}, | |
| { "dependent_group_index", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_QS_DEP_GROUP_IDX}, | |
| { "q_idx", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_QS_QIDX}, | |
| { "set_tile_mode_plus_one", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_QS_TILEMODE}, | |
| { "quality_degradation", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_QS_DEGRADATION}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| else if (evt->base.type == GF_FEVT_VISIBILITY_HINT) { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "min_x", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_MIN_X}, | |
| { "max_x", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_MIN_Y}, | |
| { "min_y", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_MAX_X}, | |
| { "max_y", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_MAX_Y}, | |
| { "is_gaze", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_IS_GAZE}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| else if (evt->base.type == GF_FEVT_BUFFER_REQ) { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "max_buffer_us", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_MAX_BUFFER}, | |
| { "max_playout_us", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_MAX_PLAYOUT}, | |
| { "min_playout_us", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_MIN_PLAYOUT}, | |
| { "pid_only", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_PID_ONLY}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| else if (evt->base.type == GF_FEVT_USER) { | |
| u32 ui_type = evt->user_event.event.type; | |
| napi_property_descriptor fevt_ui_properties[] = { | |
| { "ui_type", NULL, NULL, fevt_getter, NULL, NULL, napi_enumerable, &FEVT_PROP_UI_TYPE}, | |
| #ifndef GPAC_DISABLE_SVG | |
| { "ui_name", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_NAME}, | |
| #endif | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_ui_properties)/sizeof(napi_property_descriptor), fevt_ui_properties) ); | |
| switch (ui_type) { | |
| case GF_EVENT_CLICK: | |
| case GF_EVENT_MOUSEUP: | |
| case GF_EVENT_MOUSEDOWN: | |
| case GF_EVENT_MOUSEOVER: | |
| case GF_EVENT_MOUSEOUT: | |
| case GF_EVENT_MOUSEMOVE: | |
| case GF_EVENT_MOUSEWHEEL: | |
| { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "mouse_x", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_MOUSE_X}, | |
| { "mouse_y", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_MOUSE_Y}, | |
| { "wheel", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_MOUSE_WHEEL}, | |
| { "button", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_MOUSE_BUTTON}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| break; | |
| case GF_EVENT_MULTITOUCH: | |
| { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "mt_x", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_MOUSE_X}, | |
| { "mt_y", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_MOUSE_Y}, | |
| { "mt_rotate", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_MT_ROTATE}, | |
| { "mt_pinch", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_MT_PINCH}, | |
| { "mt_fingers", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_MT_FINGERS}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| break; | |
| case GF_EVENT_KEYUP: | |
| case GF_EVENT_KEYDOWN: | |
| case GF_EVENT_LONGKEYPRESS: | |
| case GF_EVENT_TEXTINPUT: | |
| { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "keycode", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_KEY_CODE}, | |
| #ifndef GPAC_DISABLE_SVG | |
| { "keyname", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_KEY_NAME}, | |
| #endif | |
| { "keymods", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_KEY_MODS}, | |
| { "hwkey", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_KEY_HW}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| break; | |
| case GF_EVENT_PASTE_TEXT: | |
| case GF_EVENT_COPY_TEXT: | |
| { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "clipboard", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_CLIPBOARD}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| break; | |
| case GF_EVENT_SIZE: | |
| { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "width", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_WIDTH}, | |
| { "height", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_HEIGHT}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| break; | |
| case GF_EVENT_MOVE: | |
| { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "move_x", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_MOVE_X}, | |
| { "move_y", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_MOVE_Y}, | |
| { "showtype", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_SHOWTYPE}, | |
| { "move_relative", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_MOVE_RELATIVE}, | |
| { "move_alignx", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_MOVE_ALIGNX}, | |
| { "move_aligny", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_MOVE_ALIGNY}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| break; | |
| case GF_EVENT_SET_CAPTION: | |
| { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "caption", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_UI_CAPTION}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| else if (evt->base.type == GF_FEVT_PLAY_HINT) { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "full_file_only", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_FULL_FILE_ONLY}, | |
| { "forced_dash_segment_switch", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_FORCED_DASH_SWITCH}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| else if (evt->base.type == GF_FEVT_FILE_DELETE) { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "url", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_FILE_DEL}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| else if (evt->base.type == GF_FEVT_NTP_REF) { | |
| napi_property_descriptor fevt_properties[] = { | |
| { "ntp", NULL, NULL, fevt_getter, evt_set, NULL, napi_enumerable, &FEVT_PROP_NTP_REF}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, obj, sizeof(fevt_properties)/sizeof(napi_property_descriptor), fevt_properties) ); | |
| } | |
| //other events don't have any attributes | |
| NAPI_CALL( napi_wrap(env, obj, evt, for_val ? del_evt : NULL, NULL, NULL) ); | |
| NAPI_CALL( napi_type_tag_object(env, obj, &filterevent_tag) ); | |
| return obj; | |
| } | |
| napi_value new_fevt(napi_env env, napi_callback_info info) | |
| { | |
| GF_FilterEvent *evt; | |
| NARG_ARGS_THIS(2, 1); | |
| NARG_U32(type, 0, 0); | |
| NAPI_GPAC | |
| gpac->is_init = GF_TRUE; | |
| if (type==GF_FEVT_USER) { | |
| if (argc<2) { | |
| napi_throw_error(env, NULL, "User event type must be specified"); | |
| return NULL; | |
| } | |
| } | |
| GF_SAFEALLOC(evt, GF_FilterEvent) | |
| if (!evt) { | |
| napi_throw_error(env, NULL, "Failed to allocate filter event"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_get_value_uint32(env, argv[0], &evt->base.type) ); | |
| if (type==GF_FEVT_USER) { | |
| u32 val; | |
| NAPI_CALL( napi_get_value_uint32(env, argv[1], &val) ); | |
| evt->user_event.event.type = (u8) val; | |
| } | |
| return wrap_filterevent(env, evt, &this_val); | |
| } | |
| napi_status init_fevt_class(napi_env env, napi_value exports, GPAC_NAPI *inst) | |
| { | |
| napi_status status; | |
| napi_value cons; | |
| status = napi_define_class(env, "FilterEvent", NAPI_AUTO_LENGTH , new_fevt, NULL, 0, NULL, &cons); | |
| if (status != napi_ok) return status; | |
| return napi_set_named_property(env, exports, "FilterEvent", cons); | |
| } | |
| static napi_status InitConstants(napi_env env, napi_value exports); | |
| napi_status init_fio_class(napi_env env, napi_value exports, GPAC_NAPI *inst); | |
| static u32 NODEGPAC_PROP_RMT_ON_NEW_CLIENT = 0; | |
| static u32 NODEGPAC_PROP_USERWS_ON_NEW_CLIENT = 1; | |
| void gpac_rmt_on_new_client(void *udta, void* new_client) { | |
| NAPI_RMT_Task* task = udta; | |
| if (!task || task->type != RMT_CALLBACK_NODE ) | |
| return; | |
| napi_env env = task->env; | |
| GPAC_NAPI *gpac; | |
| if ( napi_get_instance_data(env, (void **) &gpac) != napi_ok) { | |
| napi_throw_error(env, NULL, "Cannot get GPAC NAPI instance"); | |
| return; | |
| } | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_RMTWS, ("gpac_rmt_on_new_client %p\n", new_client)); | |
| if (!gpac->rmt_new_clients) gpac->rmt_new_clients = gf_list_new(); | |
| gf_list_add(gpac->rmt_new_clients, new_client ); | |
| if (gpac->fs_rmt_handler) { | |
| gf_fs_post_user_task(gpac->fs_rmt_handler->fs, fs_flush_rmt, gpac->fs_rmt_handler, "RMTOnNewClient"); | |
| } | |
| } | |
| napi_value nodegpac_setter(napi_env env, napi_callback_info info) | |
| { | |
| NAPI_GPAC | |
| NARG_ARGS_THIS_MAGIC(1, 1) | |
| gpac->is_init = GF_TRUE; | |
| if (magic == &NODEGPAC_PROP_RMT_ON_NEW_CLIENT || magic == &NODEGPAC_PROP_USERWS_ON_NEW_CLIENT) { | |
| napi_valuetype rtype; | |
| napi_typeof(env, argv[0], &rtype); | |
| RMT_WS* rmt = NULL; | |
| if (magic == &NODEGPAC_PROP_RMT_ON_NEW_CLIENT) rmt = (RMT_WS*) gf_sys_get_rmtws(); | |
| if (magic == &NODEGPAC_PROP_USERWS_ON_NEW_CLIENT) rmt = (RMT_WS*) gf_sys_get_userws(); | |
| if (!rmt) | |
| return NULL; | |
| if (rtype == napi_null || rtype == napi_undefined ) { | |
| if (gpac->rmt_ctx) { | |
| napi_async_destroy(env, gpac->rmt_ctx); | |
| gpac->rmt_ctx = NULL; | |
| } | |
| NAPI_RMT_Task* task = gf_rmt_get_on_new_client_task(rmt); | |
| napi_rmt_task_del(env, task); | |
| gf_rmt_set_on_new_client_cbk(rmt, NULL, NULL); | |
| } else if (rtype == napi_function) { | |
| if (!gpac->rmt_ctx) { | |
| napi_value global, name; | |
| NAPI_CALL( napi_get_global(env, &global) ); | |
| NAPI_CALL( napi_create_string_utf8(env, "GPAC_RMTCallback", NAPI_AUTO_LENGTH, &name) ); | |
| NAPI_CALL( napi_async_init(env, global, name, &gpac->rmt_ctx) ) | |
| } | |
| NAPI_RMT_Task *task; | |
| GF_SAFEALLOC(task, NAPI_RMT_Task); | |
| task->type = RMT_CALLBACK_NODE; | |
| task->env = env; | |
| napi_create_reference(env, this_val, 1, &task->_this); | |
| napi_create_reference(env, argv[0], 1, &task->fun); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_RMTWS, ("setting on new client cb %p\n", gpac_rmt_on_new_client)); | |
| gf_rmt_set_on_new_client_cbk(rmt, task, gpac_rmt_on_new_client); | |
| } | |
| } | |
| return NULL; | |
| } | |
| napi_value Init(napi_env env, napi_value exports) | |
| { | |
| GPAC_NAPI *inst; | |
| napi_status status; | |
| napi_property_descriptor p_desc = {.attributes=napi_enumerable}; | |
| //don't use gf_malloc here !! | |
| inst = malloc(sizeof(GPAC_NAPI)); | |
| if (!inst) { | |
| napi_throw_error(env, NULL, "Failed to allocate GPAC NAPI context"); | |
| return NULL; | |
| } | |
| memset(inst, 0, sizeof(GPAC_NAPI)); | |
| NAPI_CALL( napi_set_instance_data(env, inst, gpac_napi_finalize, NULL) ); | |
| NAPI_CALL(InitConstants(env, exports) ); | |
| #define DEF_FUN(_name, _fun) \ | |
| p_desc.utf8name = _name; \ | |
| NAPI_CALL( napi_create_function(env, NULL, 0, _fun, NULL, &p_desc.value) ); \ | |
| NAPI_CALL( napi_define_properties(env, exports, 1, &p_desc) ); | |
| /*function exports*/ | |
| DEF_FUN("init", gpac_init); | |
| DEF_FUN("e2s", gpac_e2s); | |
| DEF_FUN("set_logs", gpac_set_logs); | |
| DEF_FUN("set_args", gpac_set_args); | |
| DEF_FUN("sys_clock", gpac_sys_clock); | |
| DEF_FUN("sys_clock_high_res", gpac_sys_clock_high_res); | |
| DEF_FUN("sys_keyname", gpac_sys_keyname); | |
| DEF_FUN("enable_rmtws", gpac_enable_rmtws); | |
| napi_property_descriptor rmtws_properties[] = { | |
| { "rmt_on_new_client", NULL, NULL, NULL, nodegpac_setter, NULL, napi_enumerable, &NODEGPAC_PROP_RMT_ON_NEW_CLIENT}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, exports, sizeof(rmtws_properties)/sizeof(napi_property_descriptor), rmtws_properties) ); | |
| DEF_FUN("enable_userws", gpac_enable_userws); | |
| napi_property_descriptor userws_properties[] = { | |
| { "userws_on_new_client", NULL, NULL, NULL, nodegpac_setter, NULL, napi_enumerable, &NODEGPAC_PROP_USERWS_ON_NEW_CLIENT}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, exports, sizeof(userws_properties)/sizeof(napi_property_descriptor), userws_properties) ); | |
| status = init_fs_class(env, exports, inst); | |
| if (status != napi_ok) return NULL; | |
| status = init_fevt_class(env, exports, inst); | |
| if (status != napi_ok) return NULL; | |
| status = init_fio_class(env, exports, inst); | |
| if (status != napi_ok) return NULL; | |
| inst->main_thid = gf_th_id(); | |
| //default init | |
| gf_sys_init(0, NULL); | |
| return exports; | |
| } | |
| NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) | |
| static napi_status InitConstants(napi_env env, napi_value exports) | |
| { | |
| napi_status status; | |
| napi_property_descriptor p_desc = {.attributes=napi_enumerable}; | |
| #define DEF_CONST_STR(_name, _fun) \ | |
| p_desc.utf8name = _name; \ | |
| status = napi_create_string_utf8(env, _fun, NAPI_AUTO_LENGTH, &p_desc.value);\ | |
| if (status != napi_ok) return status;\ | |
| status = napi_define_properties(env, exports, 1, &p_desc); \ | |
| if (status != napi_ok) return status;\ | |
| #define DEF_CONST_INT(_name, _fun) \ | |
| p_desc.utf8name = _name; \ | |
| status = napi_create_int32(env, _fun, &p_desc.value); \ | |
| if (status != napi_ok) return status;\ | |
| status = napi_define_properties(env, exports, 1, &p_desc);\ | |
| if (status != napi_ok) return status;\ | |
| #define DEF_CONST(_const) \ | |
| p_desc.utf8name = #_const; \ | |
| status = napi_create_int32(env, _const, &p_desc.value); \ | |
| if (status != napi_ok) return status;\ | |
| status = napi_define_properties(env, exports, 1, &p_desc);\ | |
| if (status != napi_ok) return status;\ | |
| /*symbol exports*/ | |
| DEF_CONST_STR("version", gf_gpac_version() ); | |
| DEF_CONST_STR("copyright", gf_gpac_copyright() ); | |
| DEF_CONST_STR("copyright_cite", gf_gpac_copyright_cite() ); | |
| DEF_CONST_INT("abi_major", gf_gpac_abi_major() ); | |
| DEF_CONST_INT("abi_minor", gf_gpac_abi_minor() ); | |
| DEF_CONST_INT("abi_micro", gf_gpac_abi_micro() ); | |
| DEF_CONST(GF_LOG_ERROR) | |
| DEF_CONST(GF_LOG_WARNING) | |
| DEF_CONST(GF_LOG_INFO) | |
| DEF_CONST(GF_LOG_DEBUG) | |
| DEF_CONST(GF_PROP_FORBIDDEN) | |
| DEF_CONST(GF_PROP_BOOL) | |
| DEF_CONST(GF_PROP_UINT) | |
| DEF_CONST(GF_PROP_SINT) | |
| DEF_CONST(GF_PROP_LUINT) | |
| DEF_CONST(GF_PROP_LSINT) | |
| DEF_CONST(GF_PROP_FRACTION) | |
| DEF_CONST(GF_PROP_FRACTION64) | |
| DEF_CONST(GF_PROP_FLOAT) | |
| DEF_CONST(GF_PROP_DOUBLE) | |
| DEF_CONST(GF_PROP_VEC2I) | |
| DEF_CONST(GF_PROP_VEC2) | |
| DEF_CONST(GF_PROP_VEC3I) | |
| DEF_CONST(GF_PROP_VEC4I) | |
| DEF_CONST(GF_PROP_STRING) | |
| DEF_CONST(GF_PROP_STRING_NO_COPY) | |
| DEF_CONST(GF_PROP_DATA) | |
| DEF_CONST(GF_PROP_NAME) | |
| DEF_CONST(GF_PROP_DATA_NO_COPY) | |
| DEF_CONST(GF_PROP_CONST_DATA) | |
| DEF_CONST(GF_PROP_POINTER) | |
| DEF_CONST(GF_PROP_STRING_LIST) | |
| DEF_CONST(GF_PROP_UINT_LIST) | |
| DEF_CONST(GF_PROP_SINT_LIST) | |
| DEF_CONST(GF_PROP_VEC2I_LIST) | |
| DEF_CONST(GF_PROP_4CC) | |
| DEF_CONST(GF_PROP_4CC_LIST) | |
| DEF_CONST(GF_PROP_FIRST_ENUM) | |
| DEF_CONST(GF_PROP_PIXFMT) | |
| DEF_CONST(GF_PROP_PCMFMT) | |
| DEF_CONST(GF_PROP_CICP_COL_PRIM) | |
| DEF_CONST(GF_PROP_CICP_COL_TFC) | |
| DEF_CONST(GF_PROP_CICP_COL_MX) | |
| DEF_CONST(GF_PROP_CICP_LAYOUT) | |
| DEF_CONST(GF_PROP_FIRST_ENUM) | |
| DEF_CONST(GF_FEVT_PLAY) | |
| DEF_CONST(GF_FEVT_SET_SPEED) | |
| DEF_CONST(GF_FEVT_STOP) | |
| DEF_CONST(GF_FEVT_PAUSE) | |
| DEF_CONST(GF_FEVT_RESUME) | |
| DEF_CONST(GF_FEVT_SOURCE_SEEK) | |
| DEF_CONST(GF_FEVT_SOURCE_SWITCH) | |
| DEF_CONST(GF_FEVT_SEGMENT_SIZE) | |
| DEF_CONST(GF_FEVT_QUALITY_SWITCH) | |
| DEF_CONST(GF_FEVT_VISIBILITY_HINT) | |
| DEF_CONST(GF_FEVT_INFO_UPDATE) | |
| DEF_CONST(GF_FEVT_BUFFER_REQ) | |
| DEF_CONST(GF_FEVT_CAPS_CHANGE) | |
| DEF_CONST(GF_FEVT_CONNECT_FAIL) | |
| DEF_CONST(GF_FEVT_USER) | |
| DEF_CONST(GF_FEVT_PLAY_HINT) | |
| DEF_CONST(GF_FEVT_FILE_DELETE) | |
| DEF_CONST(GF_STATS_LOCAL) | |
| DEF_CONST(GF_STATS_LOCAL_INPUTS) | |
| DEF_CONST(GF_STATS_DECODER_SINK) | |
| DEF_CONST(GF_STATS_DECODER_SOURCE) | |
| DEF_CONST(GF_STATS_ENCODER_SINK) | |
| DEF_CONST(GF_STATS_ENCODER_SOURCE) | |
| DEF_CONST(GF_STATS_SINK) | |
| DEF_CONST(GF_FILTER_CLOCK_NONE) | |
| DEF_CONST(GF_FILTER_CLOCK_PCR) | |
| DEF_CONST(GF_FILTER_CLOCK_PCR_DISC) | |
| DEF_CONST(GF_FILTER_SAP_NONE) | |
| DEF_CONST(GF_FILTER_SAP_1) | |
| DEF_CONST(GF_FILTER_SAP_2) | |
| DEF_CONST(GF_FILTER_SAP_3) | |
| DEF_CONST(GF_FILTER_SAP_4) | |
| DEF_CONST(GF_FILTER_SAP_4_PROL) | |
| DEF_CONST(GF_EOS); | |
| DEF_CONST(GF_OK) | |
| DEF_CONST(GF_BAD_PARAM) | |
| DEF_CONST(GF_OUT_OF_MEM) | |
| DEF_CONST(GF_IO_ERR) | |
| DEF_CONST(GF_NOT_SUPPORTED) | |
| DEF_CONST(GF_CORRUPTED_DATA) | |
| DEF_CONST(GF_SG_UNKNOWN_NODE) | |
| DEF_CONST(GF_SG_INVALID_PROTO) | |
| DEF_CONST(GF_SCRIPT_ERROR) | |
| DEF_CONST(GF_BUFFER_TOO_SMALL) | |
| DEF_CONST(GF_NON_COMPLIANT_BITSTREAM) | |
| DEF_CONST(GF_FILTER_NOT_FOUND) | |
| DEF_CONST(GF_URL_ERROR) | |
| DEF_CONST(GF_SERVICE_ERROR) | |
| DEF_CONST(GF_REMOTE_SERVICE_ERROR) | |
| DEF_CONST(GF_STREAM_NOT_FOUND) | |
| DEF_CONST(GF_ISOM_INVALID_FILE) | |
| DEF_CONST(GF_ISOM_INCOMPLETE_FILE) | |
| DEF_CONST(GF_ISOM_INVALID_MEDIA) | |
| DEF_CONST(GF_ISOM_INVALID_MODE) | |
| DEF_CONST(GF_ISOM_UNKNOWN_DATA_REF) | |
| DEF_CONST(GF_ODF_INVALID_DESCRIPTOR) | |
| DEF_CONST(GF_ODF_FORBIDDEN_DESCRIPTOR) | |
| DEF_CONST(GF_ODF_INVALID_COMMAND) | |
| DEF_CONST(GF_BIFS_UNKNOWN_VERSION) | |
| DEF_CONST(GF_IP_ADDRESS_NOT_FOUND) | |
| DEF_CONST(GF_IP_CONNECTION_FAILURE) | |
| DEF_CONST(GF_IP_NETWORK_FAILURE) | |
| DEF_CONST(GF_IP_CONNECTION_CLOSED) | |
| DEF_CONST(GF_IP_NETWORK_EMPTY) | |
| DEF_CONST(GF_IP_UDP_TIMEOUT) | |
| DEF_CONST(GF_AUTHENTICATION_FAILURE) | |
| DEF_CONST(GF_NOT_READY) | |
| DEF_CONST(GF_INVALID_CONFIGURATION) | |
| DEF_CONST(GF_NOT_FOUND) | |
| DEF_CONST(GF_PROFILE_NOT_SUPPORTED) | |
| DEF_CONST(GF_REQUIRES_NEW_INSTANCE) | |
| DEF_CONST(GF_FILTER_NOT_SUPPORTED) | |
| DEF_CONST(GF_FILTER_UPDATE_DOWNSTREAM) | |
| DEF_CONST(GF_FILTER_UPDATE_UPSTREAM) | |
| DEF_CONST(GF_EVENT_CLICK) | |
| DEF_CONST(GF_EVENT_MOUSEUP) | |
| DEF_CONST(GF_EVENT_MOUSEDOWN) | |
| DEF_CONST(GF_EVENT_MOUSEMOVE) | |
| DEF_CONST(GF_EVENT_MOUSEWHEEL) | |
| DEF_CONST(GF_EVENT_DBLCLICK) | |
| DEF_CONST(GF_EVENT_MULTITOUCH) | |
| DEF_CONST(GF_EVENT_KEYUP) | |
| DEF_CONST(GF_EVENT_KEYDOWN) | |
| DEF_CONST(GF_EVENT_TEXTINPUT) | |
| DEF_CONST(GF_EVENT_DROPFILE) | |
| DEF_CONST(GF_EVENT_TIMESHIFT_DEPTH) | |
| DEF_CONST(GF_EVENT_TIMESHIFT_UPDATE) | |
| DEF_CONST(GF_EVENT_TIMESHIFT_OVERFLOW) | |
| DEF_CONST(GF_EVENT_TIMESHIFT_UNDERRUN) | |
| DEF_CONST(GF_EVENT_PASTE_TEXT) | |
| DEF_CONST(GF_EVENT_COPY_TEXT) | |
| DEF_CONST(GF_EVENT_SIZE) | |
| DEF_CONST(GF_EVENT_SHOWHIDE) | |
| DEF_CONST(GF_EVENT_MOVE) | |
| DEF_CONST(GF_EVENT_SET_CAPTION) | |
| DEF_CONST(GF_EVENT_REFRESH) | |
| DEF_CONST(GF_EVENT_QUIT) | |
| DEF_CONST(GF_EVENT_CODEC_SLOW) | |
| DEF_CONST(GF_EVENT_CODEC_OK) | |
| DEF_CONST(GF_KEY_MOD_SHIFT) | |
| DEF_CONST(GF_KEY_MOD_CTRL) | |
| DEF_CONST(GF_KEY_MOD_ALT) | |
| DEF_CONST(GF_KEY_EXT_NUMPAD) | |
| DEF_CONST(GF_KEY_EXT_LEFT) | |
| DEF_CONST(GF_KEY_EXT_RIGHT) | |
| DEF_CONST(GF_MemTrackerNone) | |
| DEF_CONST(GF_MemTrackerSimple) | |
| DEF_CONST(GF_MemTrackerBackTrace) | |
| DEF_CONST(GF_FS_SCHEDULER_LOCK_FREE) | |
| DEF_CONST(GF_FS_SCHEDULER_LOCK) | |
| DEF_CONST(GF_FS_SCHEDULER_LOCK_FREE_X) | |
| DEF_CONST(GF_FS_SCHEDULER_LOCK_FORCE) | |
| DEF_CONST(GF_FS_SCHEDULER_DIRECT) | |
| DEF_CONST(GF_FS_FLAG_LOAD_META) | |
| DEF_CONST(GF_FS_FLAG_NON_BLOCKING) | |
| DEF_CONST(GF_FS_FLAG_NO_GRAPH_CACHE) | |
| DEF_CONST(GF_FS_FLAG_NO_REGULATION) | |
| DEF_CONST(GF_FS_FLAG_NO_PROBE) | |
| DEF_CONST(GF_FS_FLAG_NO_REASSIGN) | |
| DEF_CONST(GF_FS_FLAG_PRINT_CONNECTIONS) | |
| DEF_CONST(GF_FS_FLAG_NO_ARG_CHECK) | |
| DEF_CONST(GF_FS_FLAG_NO_RESERVOIR) | |
| DEF_CONST(GF_FS_FLAG_FULL_LINK) | |
| DEF_CONST(GF_FS_FLAG_NO_IMPLICIT) | |
| DEF_CONST(GF_FS_FLAG_REQUIRE_SOURCE_ID) | |
| DEF_CONST(GF_FS_FLAG_FORCE_DEFER_LINK) | |
| DEF_CONST(GF_FS_FLAG_PREVENT_PLAY) | |
| DEF_CONST(GF_FS_ARG_HINT_NORMAL) | |
| DEF_CONST(GF_FS_ARG_HINT_ADVANCED) | |
| DEF_CONST(GF_FS_ARG_HINT_EXPERT) | |
| DEF_CONST(GF_FS_ARG_HINT_HIDE) | |
| DEF_CONST(GF_FS_ARG_UPDATE) | |
| DEF_CONST(GF_FS_ARG_META) | |
| DEF_CONST(GF_FS_ARG_META_ALLOC) | |
| DEF_CONST(GF_FS_ARG_SINK_ALIAS) | |
| DEF_CONST(GF_CAPFLAG_IN_BUNDLE) | |
| DEF_CONST(GF_CAPFLAG_INPUT) | |
| DEF_CONST(GF_CAPFLAG_OUTPUT) | |
| DEF_CONST(GF_CAPFLAG_EXCLUDED) | |
| DEF_CONST(GF_CAPFLAG_LOADED_FILTER) | |
| DEF_CONST(GF_CAPFLAG_STATIC) | |
| DEF_CONST(GF_CAPFLAG_OPTIONAL) | |
| DEF_CONST(GF_CAPFLAG_RECONFIG) | |
| DEF_CONST(GF_CAPS_INPUT) | |
| DEF_CONST(GF_CAPS_INPUT_OPT) | |
| DEF_CONST(GF_CAPS_INPUT_STATIC) | |
| DEF_CONST(GF_CAPS_INPUT_STATIC_OPT) | |
| DEF_CONST(GF_CAPS_INPUT_EXCLUDED) | |
| DEF_CONST(GF_CAPS_INPUT_LOADED_FILTER) | |
| DEF_CONST(GF_CAPS_OUTPUT) | |
| DEF_CONST(GF_CAPS_OUTPUT_LOADED_FILTER) | |
| DEF_CONST(GF_CAPS_OUTPUT_EXCLUDED) | |
| DEF_CONST(GF_CAPS_OUTPUT_STATIC) | |
| DEF_CONST(GF_CAPS_OUTPUT_STATIC_EXCLUDED) | |
| DEF_CONST(GF_CAPS_INPUT_OUTPUT) | |
| DEF_CONST(GF_CAPS_INPUT_OUTPUT_OPT) | |
| DEF_CONST(GF_FS_FLUSH_NONE) | |
| DEF_CONST(GF_FS_FLUSH_ALL) | |
| DEF_CONST(GF_FS_FLUSH_FAST) | |
| DEF_CONST(GF_SETUP_ERROR) | |
| DEF_CONST(GF_NOTIF_ERROR) | |
| DEF_CONST(GF_NOTIF_ERROR_AND_DISCONNECT) | |
| return napi_ok; | |
| } | |
| void dashbind_finalize(napi_env env, void* finalize_data, void* finalize_hint) | |
| { | |
| NAPI_Dash *napi_dash = (NAPI_Dash *)finalize_data; | |
| return; | |
| u32 rcount; | |
| napi_reference_unref(env, napi_dash->groups, &rcount); | |
| napi_delete_reference(env, napi_dash->groups); | |
| napi_dash->groups = NULL; | |
| napi_reference_unref(env, napi_dash->ref, &rcount); | |
| napi_delete_reference(env, napi_dash->ref); | |
| napi_dash->ref = NULL; | |
| /*destruction not done here*/ | |
| } | |
| void dashbind_period_reset(void *udta, u32 type) | |
| { | |
| napi_status status; | |
| napi_value obj, fun_val, res; | |
| NAPI_Dash *napi_dash = (NAPI_Dash *)udta; | |
| napi_get_reference_value(napi_dash->env, napi_dash->ref, &obj); | |
| Bool has_fun = GF_FALSE; | |
| napi_has_named_property(napi_dash->env, obj, "on_period_reset", (bool *) &has_fun); | |
| if (has_fun) { | |
| status = napi_get_named_property(napi_dash->env, obj, "on_period_reset", &fun_val); | |
| if (status!=napi_ok) has_fun = GF_FALSE; | |
| } | |
| if (!has_fun) return; | |
| napi_value arg; | |
| napi_create_uint32(napi_dash->env, type, &arg); | |
| napi_call_function(napi_dash->env, obj, fun_val, 1, &arg, &res); | |
| } | |
| void dashbind_new_group(void *udta, u32 group_idx, void *_dash) | |
| { | |
| napi_property_descriptor p_desc = {.attributes=napi_enumerable}; | |
| napi_status status; | |
| napi_value obj, group, groups, qualities, fun_val, res; | |
| NAPI_Dash *napi_dash = (NAPI_Dash *)udta; | |
| napi_env env = napi_dash->env; | |
| GF_DashClient *dash = (GF_DashClient *) _dash; | |
| napi_create_object(env, &group); | |
| napi_get_reference_value(napi_dash->env, napi_dash->groups, &groups); | |
| napi_set_element(env, groups, group_idx, group); | |
| p_desc.utf8name = "idx"; | |
| napi_create_uint32(env, group_idx, &p_desc.value); | |
| napi_define_properties(env, group, 1, &p_desc); | |
| p_desc.utf8name = "duration"; | |
| napi_create_int64(env, (s64) gf_dash_get_period_duration(dash), &p_desc.value); | |
| napi_define_properties(env, group, 1, &p_desc); | |
| p_desc.utf8name = "qualities"; | |
| napi_create_array(env, &p_desc.value); | |
| napi_define_properties(env, group, 1, &p_desc); | |
| qualities = p_desc.value; | |
| u32 i, nb_qualities = gf_dash_group_get_num_qualities(dash, group_idx); | |
| for (i=0; i<nb_qualities; i++) { | |
| napi_value q, segs; | |
| GF_DASHQualityInfo qinfo; | |
| gf_dash_group_get_quality_info(dash, group_idx, i, &qinfo); | |
| napi_create_object(env, &q); | |
| #define QSET_U32(_val) \ | |
| p_desc.utf8name = #_val;\ | |
| napi_create_uint32(env, qinfo._val, &p_desc.value); \ | |
| napi_define_properties(env, q, 1, &p_desc); | |
| #define QSET_BOOL(_val) \ | |
| p_desc.utf8name = #_val;\ | |
| napi_get_boolean(env, qinfo._val ? true : false, &p_desc.value); \ | |
| napi_define_properties(env, q, 1, &p_desc); | |
| #define QSET_STR(_val) \ | |
| p_desc.utf8name = #_val;\ | |
| if (qinfo._val) {\ | |
| napi_create_string_utf8(env, qinfo._val, NAPI_AUTO_LENGTH, &p_desc.value);\ | |
| } else {\ | |
| napi_get_null(env, &p_desc.value);\ | |
| }\ | |
| napi_define_properties(env, q, 1, &p_desc); | |
| #define QSET_DOUBLE(_val) \ | |
| p_desc.utf8name = #_val;\ | |
| napi_create_double(env, qinfo._val ? true : false, &p_desc.value); \ | |
| napi_define_properties(env, q, 1, &p_desc); | |
| QSET_U32(bandwidth) | |
| QSET_STR(ID) | |
| QSET_STR(mime) | |
| QSET_STR(codec) | |
| QSET_U32(width) | |
| QSET_U32(height) | |
| QSET_BOOL(interlaced) | |
| p_desc.utf8name = "fps"; | |
| GF_Fraction frac = {qinfo.fps_num, qinfo.fps_den}; | |
| p_desc.value = frac_to_napi(env, &frac); | |
| napi_define_properties(env, q, 1, &p_desc); | |
| p_desc.utf8name = "sar"; | |
| frac.num = qinfo.par_num; | |
| frac.den = qinfo.par_den; | |
| p_desc.value = frac_to_napi(env, &frac); | |
| napi_define_properties(env, q, 1, &p_desc); | |
| QSET_U32(sample_rate) | |
| QSET_U32(nb_channels) | |
| QSET_BOOL(disabled) | |
| QSET_BOOL(is_selected) | |
| QSET_DOUBLE(ast_offset) | |
| QSET_DOUBLE(average_duration) | |
| QSET_STR(hls_variant_url) | |
| QSET_U32(ssr) | |
| p_desc.utf8name = "sizes"; | |
| napi_create_array(env, &p_desc.value); | |
| napi_define_properties(env, q, 1, &p_desc); | |
| segs = p_desc.value; | |
| /*! list of segmentURLs if known, NULL otherwise. Used for onDemand profile to get segment sizes*/ | |
| if (qinfo.seg_urls) { | |
| u32 j, nb_segs=gf_list_count(qinfo.seg_urls); | |
| for (j=0; j<nb_segs; j++) { | |
| napi_value si; | |
| GF_MPD_SegmentURL *surl = gf_list_get((GF_List *) qinfo.seg_urls, j); | |
| napi_create_object(env, &si); | |
| napi_set_element(env, segs, j, si); | |
| p_desc.utf8name = "size"; | |
| napi_create_int64(env, (1 + surl->media_range->end_range - surl->media_range->start_range), &p_desc.value); | |
| napi_define_properties(env, si, 1, &p_desc); | |
| p_desc.utf8name = "duration"; | |
| napi_create_uint32(env, surl->duration, &p_desc.value); | |
| napi_define_properties(env, si, 1, &p_desc); | |
| } | |
| } | |
| napi_set_element(env, qualities, i, q); | |
| } | |
| //create srd | |
| p_desc.utf8name = "SRD"; | |
| u32 id, x, y, w, h, fw, fh; | |
| if (gf_dash_group_get_srd_info(dash, group_idx, &id, &x, &y, &w, &h, &fw, &fh)) { | |
| napi_value srd, val; | |
| napi_create_object(env, &srd); | |
| p_desc.value = srd; | |
| #define SRD_U32(_val) \ | |
| napi_create_uint32(env, _val, &val);\ | |
| napi_set_named_property(env, srd, #_val, val); \ | |
| SRD_U32(id) | |
| SRD_U32(x) | |
| SRD_U32(y) | |
| SRD_U32(w) | |
| SRD_U32(h) | |
| SRD_U32(fw) | |
| SRD_U32(fh) | |
| } else { | |
| napi_get_null(env, &p_desc.value); | |
| } | |
| napi_define_properties(env, group, 1, &p_desc); | |
| napi_get_reference_value(napi_dash->env, napi_dash->ref, &obj); | |
| Bool has_fun = GF_FALSE; | |
| napi_has_named_property(napi_dash->env, obj, "on_new_group", (bool *) &has_fun); | |
| if (has_fun) { | |
| status = napi_get_named_property(napi_dash->env, obj, "on_new_group", &fun_val); | |
| if (status!=napi_ok) has_fun = GF_FALSE; | |
| } | |
| if (has_fun) { | |
| napi_call_function(napi_dash->env, obj, fun_val, 1, &group, &res); | |
| } | |
| } | |
| s32 dashbind_rate_adaptation(void *udta, u32 group_idx, u32 base_group_idx, Bool force_low_complex, void *_stats) | |
| { | |
| napi_status status; | |
| napi_value obj, fun_val, groups, res, st, val; | |
| GF_DASHCustomAlgoInfo *stats = (GF_DASHCustomAlgoInfo *)_stats; | |
| NAPI_Dash *napi_dash = (NAPI_Dash *)udta; | |
| napi_env env = napi_dash->env; | |
| napi_get_reference_value(napi_dash->env, napi_dash->ref, &obj); | |
| Bool has_fun = GF_FALSE; | |
| napi_has_named_property(napi_dash->env, obj, "on_rate_adaptation", (bool *) &has_fun); | |
| if (has_fun) { | |
| status = napi_get_named_property(napi_dash->env, obj, "on_rate_adaptation", &fun_val); | |
| if (status!=napi_ok) has_fun = GF_FALSE; | |
| } | |
| if (!has_fun) return -1; | |
| u32 i, nb_groups; | |
| napi_get_reference_value(env, napi_dash->groups, &groups); | |
| napi_get_named_property(env, groups, "length", &res); | |
| napi_get_value_uint32(env, res, &nb_groups); | |
| napi_value args[4]; | |
| napi_get_null(env, &args[0]); | |
| napi_get_null(env, &args[1]); | |
| for (i=0; i<nb_groups;i++) { | |
| u32 g_idx=0; | |
| napi_value group; | |
| napi_get_element(env, groups, i, &group); | |
| napi_get_named_property(env, group, "idx", &res); | |
| napi_get_value_uint32(env, res, &g_idx); | |
| if (g_idx==group_idx) args[0] = group; | |
| else if (g_idx==base_group_idx) args[1] = group; | |
| } | |
| napi_get_boolean(env, force_low_complex ? true : false, &args[2]); | |
| napi_create_object(env, &st); | |
| args[3] = st; | |
| #define SSET_U32(_val) \ | |
| napi_create_uint32(env, stats->_val, &val);\ | |
| napi_set_named_property(env, st, #_val, val); \ | |
| #define SSET_DOUBLE(_val) \ | |
| napi_create_double(env, stats->_val, &val);\ | |
| napi_set_named_property(env, st, #_val, val); \ | |
| SSET_U32(download_rate); | |
| SSET_U32(file_size); | |
| SSET_DOUBLE(speed); | |
| SSET_DOUBLE(max_available_speed); | |
| SSET_U32(display_width); | |
| SSET_U32(display_height); | |
| SSET_U32(active_quality_idx); | |
| SSET_U32(buffer_min_ms); | |
| SSET_U32(buffer_max_ms); | |
| SSET_U32(buffer_occupancy_ms); | |
| SSET_U32(quality_degradation_hint); | |
| SSET_U32(total_rate); | |
| napi_call_function(napi_dash->env, obj, fun_val, 4, args, &res); | |
| s32 ret_val=-1; | |
| napi_get_value_int32(env, res, &ret_val); | |
| return ret_val; | |
| } | |
| typedef struct | |
| { | |
| u32 bits_per_sec; | |
| u64 total_bytes; | |
| u64 bytes_done; | |
| u64 us_since_start; | |
| u32 buffer_dur; | |
| u32 current_seg_dur; | |
| } GF_DASHDownloadStats; | |
| s32 dashbind_download_monitor(void *udta, u32 group_idx, void *_stats) | |
| { | |
| napi_status status; | |
| napi_value obj, fun_val, groups, res, st, val; | |
| GF_DASHDownloadStats *stats = (GF_DASHDownloadStats *)_stats; | |
| NAPI_Dash *napi_dash = (NAPI_Dash *)udta; | |
| napi_env env = napi_dash->env; | |
| napi_get_reference_value(napi_dash->env, napi_dash->ref, &obj); | |
| Bool has_fun = GF_FALSE; | |
| napi_has_named_property(napi_dash->env, obj, "on_download_monitor", (bool *) &has_fun); | |
| if (has_fun) { | |
| status = napi_get_named_property(napi_dash->env, obj, "on_download_monitor", &fun_val); | |
| if (status!=napi_ok) has_fun = GF_FALSE; | |
| } | |
| if (!has_fun) return -1; | |
| u32 i, nb_groups; | |
| napi_get_reference_value(env, napi_dash->groups, &groups); | |
| napi_get_named_property(env, groups, "length", &res); | |
| napi_get_value_uint32(env, res, &nb_groups); | |
| napi_value args[2]; | |
| napi_get_null(env, &args[0]); | |
| for (i=0; i<nb_groups;i++) { | |
| u32 g_idx=0; | |
| napi_value group; | |
| napi_get_element(env, groups, i, &group); | |
| napi_get_named_property(env, group, "idx", &res); | |
| napi_get_value_uint32(env, res, &g_idx); | |
| if (g_idx==group_idx) { | |
| args[0] = group; | |
| break; | |
| } | |
| } | |
| napi_create_object(env, &st); | |
| args[1] = st; | |
| #define DSSET_U32(_val) \ | |
| napi_create_uint32(env, stats->_val, &val);\ | |
| napi_set_named_property(env, st, #_val, val); \ | |
| #define DSSET_U64(_val) \ | |
| napi_create_int64(env, (s64) stats->_val, &val);\ | |
| napi_set_named_property(env, st, #_val, val); \ | |
| DSSET_U32(bits_per_sec); | |
| DSSET_U64(total_bytes); | |
| DSSET_U64(bytes_done); | |
| DSSET_U64(us_since_start); | |
| DSSET_U32(buffer_dur); | |
| DSSET_U32(current_seg_dur); | |
| napi_call_function(napi_dash->env, obj, fun_val, 2, args, &res); | |
| s32 ret_val=-1; | |
| napi_get_value_int32(env, res, &ret_val); | |
| return ret_val; | |
| } | |
| /*currently not exposed by filter.h*/ | |
| GF_Err gf_filter_bind_dash_algo_callbacks(GF_Filter *filter, void *udta, | |
| void (*period_reset)(void *rate_adaptation, u32 type), | |
| void (*new_group)(void *udta, u32 group_idx, void *dash), | |
| s32 (*rate_adaptation)(void *udta, u32 group_idx, u32 base_group_idx, Bool force_low_complex, void *stats), | |
| s32 (*download_monitor)(void *udta, u32 group_idx, void *stats) | |
| ); | |
| napi_value dashin_bind(napi_env env, napi_callback_info info) | |
| { | |
| GF_Err e; | |
| napi_value val; | |
| NAPI_Dash *napi_dash; | |
| NARG_ARGS_THIS(1,1) | |
| FILTER | |
| GF_SAFEALLOC(napi_dash, NAPI_Dash); | |
| if (!napi_dash) { | |
| napi_throw_error(env, NULL, "Failed to allocate dashin binding"); | |
| return NULL; | |
| } | |
| NAPI_Filter *napi_f = (NAPI_Filter *) gf_filter_get_rt_udta(f); | |
| napi_create_reference(env, argv[0], 1, &napi_f->binding_ref); | |
| napi_dash->ref = napi_f->binding_ref; | |
| napi_dash->env = env; | |
| napi_f->binding_stack = napi_dash; | |
| napi_create_array(env, &val); | |
| napi_create_reference(env, val, 1, &napi_dash->groups); | |
| napi_type_tag_object(env, argv[0], &dashbind_tag); | |
| NAPI_CALL( napi_wrap(env, argv[0], napi_dash, dashbind_finalize, NULL, NULL) ); | |
| Bool has_fun = GF_FALSE; | |
| napi_has_named_property(napi_f->env, argv[0], "on_download_monitor", (bool *) &has_fun); | |
| e = gf_filter_bind_dash_algo_callbacks(f, napi_dash, dashbind_period_reset, dashbind_new_group, dashbind_rate_adaptation, has_fun ? dashbind_download_monitor : NULL); | |
| if (e) { | |
| u32 rcount; | |
| napi_reference_unref(env, napi_f->binding_ref, &rcount); | |
| napi_delete_reference(env, napi_f->binding_ref); | |
| napi_f->binding_ref = NULL; | |
| napi_f->binding_stack = NULL; | |
| gf_free(napi_dash); | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to bind dashin callbacks"); | |
| return NULL; | |
| } | |
| //tag dasher filter to run on main thread only | |
| gf_filter_force_main_thread(napi_f->f, GF_TRUE); | |
| return NULL; | |
| } | |
| void dashin_detach_binding(NAPI_Filter *napi_f) | |
| { | |
| NAPI_Dash *napi_dash = (NAPI_Dash *) napi_f->binding_stack; | |
| if (!napi_dash) return; | |
| gf_free(napi_dash); | |
| } | |
| typedef struct | |
| { | |
| napi_ref ref; | |
| napi_env env; | |
| } NAPI_HTTPOut; | |
| void httpoutbind_finalize(napi_env env, void* finalize_data, void* finalize_hint) | |
| { | |
| } | |
| typedef struct | |
| { | |
| void *httpout_session; | |
| napi_env env; | |
| napi_ref req_ref; | |
| } NAPI_HTTPSession; | |
| void napi_http_del(napi_env env, void *finalize_data, void* finalize_hint) | |
| { | |
| NAPI_HTTPSession *napi_http = (NAPI_HTTPSession *)finalize_data; | |
| if (napi_http) gf_free(napi_http); | |
| } | |
| static u32 httpout_throttle_cbk(void *udta, u64 done, u64 total) | |
| { | |
| napi_value req; | |
| napi_value fun_val, args[2], res; | |
| u32 retval=0; | |
| NAPI_HTTPSession *napi_sess = (NAPI_HTTPSession *)udta; | |
| //call | |
| napi_get_reference_value(napi_sess->env, napi_sess->req_ref, &req); | |
| napi_get_named_property(napi_sess->env, req, "throttle", &fun_val); | |
| napi_create_int64(napi_sess->env, done, &args[0]); | |
| napi_create_int64(napi_sess->env, total, &args[1]); | |
| napi_call_function(napi_sess->env, req, fun_val, 2, args, &res); | |
| napi_get_value_uint32(napi_sess->env, res, &retval); | |
| return retval; | |
| } | |
| static s32 httpout_read_cbk(void *udta, u8 *buffer, u32 buffer_size) | |
| { | |
| napi_value req; | |
| napi_value fun_val, arg, ab, res; | |
| u32 nb_bytes=0; | |
| napi_status status; | |
| NAPI_HTTPSession *napi_sess = (NAPI_HTTPSession *)udta; | |
| //call | |
| napi_get_reference_value(napi_sess->env, napi_sess->req_ref, &req); | |
| napi_get_named_property(napi_sess->env, req, "read", &fun_val); | |
| //create arg | |
| #if NAPI_VERSION >= 7 | |
| status = napi_create_external_arraybuffer(napi_sess->env, buffer, buffer_size, NULL, NULL, &ab); | |
| #else | |
| void *buf_data=NULL; | |
| status = napi_create_arraybuffer(napi_sess->env, buffer_size, &buf_data, &ab); | |
| #endif | |
| if (status==napi_ok) | |
| status = napi_create_typedarray(napi_sess->env, napi_uint8_array, buffer_size, ab, 0, &arg); | |
| //call | |
| if (status==napi_ok) | |
| status = napi_call_function(napi_sess->env, req, fun_val, 1, &arg, &res); | |
| if (status==napi_ok) | |
| status = napi_get_value_uint32(napi_sess->env, res, &nb_bytes); | |
| #if NAPI_VERSION >= 7 | |
| //detach array buffer | |
| napi_detach_arraybuffer(napi_sess->env, ab); | |
| #else | |
| if ((status==napi_ok) && buf_data && nb_bytes) | |
| memcpy(buffer, buf_data, nb_bytes); | |
| #endif | |
| return nb_bytes; | |
| } | |
| static u32 httpout_write_cbk(void *udta, const u8 *buffer, u32 buffer_size) | |
| { | |
| napi_value req; | |
| napi_value fun_val, arg, ab, res; | |
| u32 nb_bytes=0; | |
| napi_status status; | |
| NAPI_HTTPSession *napi_sess = (NAPI_HTTPSession *)udta; | |
| //call | |
| napi_get_reference_value(napi_sess->env, napi_sess->req_ref, &req); | |
| napi_get_named_property(napi_sess->env, req, "write", &fun_val); | |
| //create arg | |
| #if NAPI_VERSION >= 7 | |
| status = napi_create_external_arraybuffer(napi_sess->env, (void *)buffer, buffer_size, NULL, NULL, &ab); | |
| #else | |
| void *buf_data=NULL; | |
| status = napi_create_arraybuffer(napi_sess->env, buffer_size, &buf_data, &ab); | |
| if (status == napi_ok) { | |
| memcpy(buf_data, buffer, buffer_size); | |
| } | |
| #endif | |
| if (status==napi_ok) | |
| status = napi_create_typedarray(napi_sess->env, napi_uint8_array, buffer_size, ab, 0, &arg); | |
| //call | |
| if (status==napi_ok) | |
| status = napi_call_function(napi_sess->env, req, fun_val, 1, &arg, &res); | |
| if (status==napi_ok) | |
| status = napi_get_value_uint32(napi_sess->env, res, &nb_bytes); | |
| #if NAPI_VERSION >= 7 | |
| //detach arraybuffer | |
| napi_detach_arraybuffer(napi_sess->env, ab); | |
| #endif | |
| if (status!=napi_ok) return 0; | |
| return nb_bytes; | |
| } | |
| static void httpout_on_close_cbk(void *udta, GF_Err reason) | |
| { | |
| napi_value req; | |
| NAPI_HTTPSession *napi_sess = (NAPI_HTTPSession *)udta; | |
| //call | |
| napi_get_reference_value(napi_sess->env, napi_sess->req_ref, &req); | |
| Bool has_p = GF_FALSE; | |
| napi_has_named_property(napi_sess->env, req, "close", (bool *) &has_p); | |
| if (has_p) { | |
| napi_value fun_val, arg, res; | |
| napi_get_named_property(napi_sess->env, req, "close", &fun_val); | |
| napi_create_uint32(napi_sess->env, reason, &arg); | |
| napi_call_function(napi_sess->env, req, fun_val, 1, &arg, &res); | |
| } | |
| napi_delete_reference(napi_sess->env, napi_sess->req_ref); | |
| } | |
| GF_Err gf_httpout_send_request(void *sess, void *udta, | |
| u32 reply, | |
| char *body_or_file, | |
| u32 nb_headers, | |
| const char **headers, | |
| u32 (*throttle)(void *udta, u64 done, u64 total), | |
| s32 (*read)(void *udta, u8 *buffer, u32 buffer_size), | |
| u32 (*write)(void *udta, const u8 *buffer, u32 buffer_size), | |
| void (*close)(void *udta, GF_Err reason) | |
| ); | |
| napi_value httpout_send_cbk(napi_env env, napi_callback_info info) | |
| { | |
| napi_value val; | |
| u32 i, reply=0; | |
| size_t size; | |
| char *body_or_file=NULL; | |
| u32 nb_headers=0; | |
| char **headers=NULL; | |
| Bool use_throttle=GF_FALSE; | |
| Bool use_read=GF_FALSE; | |
| Bool use_write=GF_FALSE; | |
| NARG_THIS | |
| NAPI_HTTPSession *napi_hsess; | |
| bool _tag; | |
| NAPI_CALL( napi_check_object_type_tag(env, this_val, &httpsess_tag, &_tag) ); | |
| if (!_tag) { | |
| napi_throw_error(env, NULL, "Not a HTTP session object"); | |
| return NULL; | |
| } | |
| NAPI_CALL( napi_unwrap(env, this_val, (void**) &napi_hsess) ); | |
| napi_get_named_property(env, this_val, "reply", &val); | |
| napi_get_value_uint32(env, val, &reply); | |
| Bool has_p = GF_FALSE; | |
| napi_has_named_property(env, this_val, "body", (bool *) &has_p); | |
| if (has_p) { | |
| napi_get_named_property(env, this_val, "body", &val); | |
| napi_get_value_string_utf8(env, val, NULL, 0, &size); | |
| body_or_file = gf_malloc(sizeof(char) * (size+1) ); | |
| napi_get_value_string_utf8(env, val, body_or_file, size+1, &size); | |
| body_or_file[size]=0; | |
| } | |
| napi_has_named_property(env, this_val, "headers_out", (bool *) &has_p); | |
| if (has_p) { | |
| napi_value hdrs; | |
| u32 nb_items; | |
| napi_get_named_property(env, this_val, "headers_out", &hdrs); | |
| napi_get_array_length(env, hdrs, &nb_items); | |
| for (i=0; i<nb_items; i++) { | |
| napi_value h; | |
| napi_get_element(env, hdrs, i, &h); | |
| napi_has_named_property(env, h, "name", (bool *) &has_p); | |
| if (!has_p) continue; | |
| napi_has_named_property(env, h, "value", (bool *) &has_p); | |
| if (!has_p) continue; | |
| headers = gf_realloc(headers, sizeof(char *) * (nb_headers+2)); | |
| napi_get_named_property(env, h, "name", &val); | |
| napi_get_value_string_utf8(env, val, NULL, 0, &size); | |
| headers[nb_headers] = gf_malloc(sizeof(char) * (size+1) ); | |
| napi_get_value_string_utf8(env, val, headers[nb_headers], size+1, &size); | |
| headers[nb_headers][size]=0; | |
| napi_get_named_property(env, h, "value", &val); | |
| napi_get_value_string_utf8(env, val, NULL, 0, &size); | |
| headers[nb_headers+1] = gf_malloc(sizeof(char) * (size+1) ); | |
| napi_get_value_string_utf8(env, val, headers[nb_headers+1], size+1, &size); | |
| headers[nb_headers+1][size]=0; | |
| nb_headers += 2; | |
| } | |
| } | |
| napi_has_named_property(env, this_val, "throttle", (bool *) &has_p); | |
| if (has_p) use_throttle = GF_TRUE; | |
| napi_has_named_property(env, this_val, "read", (bool *) &has_p); | |
| if (has_p) use_read = GF_TRUE; | |
| napi_has_named_property(env, this_val, "write", (bool *) &has_p); | |
| if (has_p) use_write = GF_TRUE; | |
| gf_httpout_send_request(napi_hsess->httpout_session, napi_hsess, reply, body_or_file, nb_headers, (const char**) headers, | |
| use_throttle ? httpout_throttle_cbk : NULL, | |
| use_read ? httpout_read_cbk : NULL, | |
| use_write ? httpout_write_cbk : NULL, | |
| httpout_on_close_cbk); | |
| if (body_or_file) gf_free(body_or_file); | |
| if (headers) { | |
| for (i=0; i<nb_headers; i++) { | |
| gf_free(headers[i]); | |
| } | |
| gf_free(headers); | |
| } | |
| return NULL; | |
| } | |
| s32 httpout_on_request(void *udta, void *session, const char *method, const char *url, u32 auth_code, u32 nb_hdrs, const char **hdrs) | |
| { | |
| napi_status status; | |
| napi_value req_obj; | |
| u32 i; | |
| napi_value obj, fun_val, res; | |
| NAPI_HTTPOut *napi_http = (NAPI_HTTPOut *)udta; | |
| NAPI_HTTPSession *napi_sess; | |
| //create request object | |
| GF_SAFEALLOC(napi_sess, NAPI_HTTPSession); | |
| if (!napi_sess) return 1; | |
| napi_sess->httpout_session = session; | |
| napi_sess->env = napi_http->env; | |
| napi_create_object(napi_http->env, &req_obj); | |
| napi_create_reference(napi_http->env, req_obj, 1, &napi_sess->req_ref); | |
| napi_type_tag_object(napi_http->env, req_obj, &httpsess_tag); | |
| napi_wrap(napi_http->env, req_obj, napi_sess, napi_http_del, NULL, NULL); | |
| //set properties | |
| napi_value val; | |
| status = napi_create_string_utf8(napi_http->env, method, NAPI_AUTO_LENGTH, &val); | |
| if (status==napi_ok) status = napi_set_named_property(napi_http->env, req_obj, "method", val); | |
| if (status!=napi_ok) return 1; | |
| status = napi_create_string_utf8(napi_http->env, url, NAPI_AUTO_LENGTH, &val); | |
| if (status==napi_ok) status = napi_set_named_property(napi_http->env, req_obj, "url", val); | |
| if (status!=napi_ok) return 1; | |
| status = napi_create_uint32(napi_http->env, auth_code, &val); | |
| if (status==napi_ok) status = napi_set_named_property(napi_http->env, req_obj, "auth_code", val); | |
| if (status!=napi_ok) return 1; | |
| status = napi_create_uint32(napi_http->env, 0, &val); | |
| if (status==napi_ok) status = napi_set_named_property(napi_http->env, req_obj, "reply", val); | |
| if (status!=napi_ok) return 1; | |
| status = napi_create_function(napi_http->env, "send", NAPI_AUTO_LENGTH, httpout_send_cbk, napi_http, &val); | |
| if (status==napi_ok) status = napi_set_named_property(napi_http->env, req_obj, "send", val); | |
| if (status!=napi_ok) return 1; | |
| napi_value in_headers; | |
| napi_create_array(napi_http->env, &in_headers); | |
| for (i=0; i<nb_hdrs;i+=2) { | |
| napi_value h; | |
| napi_create_object(napi_http->env, &h); | |
| napi_create_string_utf8(napi_http->env, hdrs[i], NAPI_AUTO_LENGTH, &val); | |
| napi_set_named_property(napi_http->env, h, "name", val); | |
| napi_create_string_utf8(napi_http->env, hdrs[i+1], NAPI_AUTO_LENGTH, &val); | |
| napi_set_named_property(napi_http->env, h, "value", val); | |
| napi_set_element(napi_http->env, in_headers, i/2, h); | |
| } | |
| napi_set_named_property(napi_http->env, req_obj, "headers_in", in_headers); | |
| napi_value out_headers; | |
| napi_create_array(napi_http->env, &out_headers); | |
| napi_set_named_property(napi_http->env, req_obj, "headers_out", out_headers); | |
| //call | |
| napi_get_reference_value(napi_http->env, napi_http->ref, &obj); | |
| status = napi_get_named_property(napi_http->env, obj, "on_request", &fun_val); | |
| if (status!=napi_ok) return 1; | |
| napi_call_function(napi_http->env, obj, fun_val, 1, &req_obj, &res); | |
| return 0; | |
| } | |
| GF_Err gf_filter_bind_httpout_callbacks(GF_Filter *filter, void *udta, | |
| s32 (*on_request)(void *udta, void *session, const char *method, const char *url, u32 auth_code, u32 nb_hdrs, const char **hdrs) | |
| ); | |
| napi_value httpout_bind(napi_env env, napi_callback_info info) | |
| { | |
| GF_Err e; | |
| NAPI_HTTPOut *napi_http; | |
| NARG_ARGS_THIS(1,1) | |
| FILTER | |
| NAPI_Filter *napi_f = (NAPI_Filter *) gf_filter_get_rt_udta(f); | |
| Bool has_fun = GF_FALSE; | |
| napi_has_named_property(napi_f->env, argv[0], "on_request", (bool *) &has_fun); | |
| if (!has_fun) { | |
| napi_throw_error(env, NULL, "Invalid httpout binding object, missing on_request callback"); | |
| return NULL; | |
| } | |
| GF_SAFEALLOC(napi_http, NAPI_HTTPOut); | |
| if (!napi_http) { | |
| napi_throw_error(env, NULL, "Failed to allocate httpout binding"); | |
| return NULL; | |
| } | |
| napi_create_reference(env, argv[0], 1, &napi_f->binding_ref); | |
| napi_http->ref = napi_f->binding_ref; | |
| napi_http->env = env; | |
| napi_f->binding_stack = napi_http; | |
| napi_type_tag_object(env, argv[0], &dashbind_tag); | |
| NAPI_CALL( napi_wrap(env, argv[0], napi_http, httpoutbind_finalize, NULL, NULL) ); | |
| e = gf_filter_bind_httpout_callbacks(f, napi_http, httpout_on_request); | |
| if (e) { | |
| u32 rcount; | |
| napi_reference_unref(env, napi_f->binding_ref, &rcount); | |
| napi_delete_reference(env, napi_f->binding_ref); | |
| napi_f->binding_ref = NULL; | |
| napi_f->binding_stack = NULL; | |
| gf_free(napi_http); | |
| napi_throw_error(env, gf_error_to_string(e), "Failed to bind httpout callbacks"); | |
| return NULL; | |
| } | |
| //tag httpout filter to run on main thread only | |
| gf_filter_force_main_thread(napi_f->f, GF_TRUE); | |
| return NULL; | |
| } | |
| void httpout_detach_binding(NAPI_Filter *napi_f) | |
| { | |
| NAPI_HTTPOut *napi_http = (NAPI_HTTPOut *) napi_f->binding_stack; | |
| if (!napi_http) return; | |
| gf_free(napi_http); | |
| } | |
| typedef struct _napi_fio | |
| { | |
| //points to factory object (passed in FileIO constructor) | |
| struct _napi_fio *factory; | |
| GF_FileIO *gfio; | |
| napi_ref ref; | |
| u32 nb_refs; | |
| //for root only | |
| napi_env env; | |
| GF_List *pending_urls; | |
| //GF_List *gc_exclude; | |
| u32 all_refs; | |
| napi_ref root_ref; | |
| napi_async_context async_ctx; | |
| Bool direct_mem; | |
| } NAPI_FileIO; | |
| static u32 FIO_PROP_URL = 0; | |
| #define FILEIO\ | |
| NAPI_FileIO *napi_fio=NULL;\ | |
| bool _tag;\ | |
| NAPI_CALL( napi_check_object_type_tag(env, this_val, &fileio_tag, &_tag) );\ | |
| if (!_tag) {\ | |
| napi_throw_error(env, NULL, "Not a FileIO object");\ | |
| return NULL;\ | |
| }\ | |
| NAPI_CALL( napi_unwrap(env, this_val, (void**) &napi_fio) );\ | |
| napi_value fio_getter(napi_env env, napi_callback_info info) | |
| { | |
| void *magic; | |
| napi_status status; | |
| napi_value this_val, ret; | |
| NAPI_CALL(napi_get_cb_info(env, info, NULL, NULL, &this_val, &magic) ); | |
| FILEIO | |
| if (magic == &FIO_PROP_URL) { | |
| NAPI_CALL( napi_create_string_utf8(env, gf_fileio_url(napi_fio->gfio), NAPI_AUTO_LENGTH, &ret) ); | |
| return ret; | |
| } | |
| return NULL; | |
| } | |
| static GF_Err gfio_seek(GF_FileIO *fileio, u64 offset, s32 whence) | |
| { | |
| napi_status status; | |
| s32 rval; | |
| napi_value args[2], fact_obj, obj, fun_val, res; | |
| NAPI_FileIO *napi_fio = gf_fileio_get_udta(fileio); | |
| napi_env env = napi_fio->factory->env; | |
| //get fun and target this | |
| napi_get_reference_value(env, napi_fio->factory->root_ref, &fact_obj); | |
| status = napi_get_named_property(env, fact_obj, "seek", &fun_val); | |
| if (status!=napi_ok) { | |
| return GF_NOT_SUPPORTED; | |
| } | |
| napi_get_reference_value(env, napi_fio->ref, &obj); | |
| //create args | |
| status = napi_create_int64(env, (s64) offset, &args[0]); | |
| if (status != napi_ok) { | |
| return -1; | |
| } | |
| status = napi_create_uint32(env, whence, &args[1]); | |
| if (status != napi_ok) { | |
| return -1; | |
| } | |
| //call | |
| status = napi_make_callback(env, napi_fio->factory->async_ctx, obj, fun_val, 2, args, &res); | |
| if (status!=napi_ok) { | |
| return -1; | |
| } | |
| status = napi_get_value_int32(env, res, &rval); | |
| if (status!=napi_ok) { | |
| return -1; | |
| } | |
| return rval; | |
| } | |
| /* | |
| ultimately we would like node to read/write directly to/from our buffer, | |
| however this currently crashes node | |
| https://github.com/nodejs/node/pull/33321#issuecomment-630905537 | |
| "If you use napi_create_external_buffer() a second time on the same pointer without waiting for the buffer created from the first time to be released, you’ll get a crash" | |
| the same applies with napi_create_external_arraybuffer(), and we are usually called | |
| with the same internal buffer pointer for read or write methods | |
| until fixed, we create a different buffer and do a memcpy... | |
| */ | |
| static u32 gfio_read(GF_FileIO *fileio, u8 *buffer, u32 bytes) | |
| { | |
| napi_status status; | |
| u32 nb_bytes; | |
| void *buf_data=NULL; | |
| napi_value arg, ab, fact_obj, obj, fun_val, res; | |
| NAPI_FileIO *napi_fio = gf_fileio_get_udta(fileio); | |
| napi_env env = napi_fio->factory->env; | |
| //get fun and target this | |
| status = napi_get_reference_value(env, napi_fio->factory->root_ref, &fact_obj); | |
| if (status!=napi_ok) return 0; | |
| status = napi_get_named_property(env, fact_obj, "read", &fun_val); | |
| if (status!=napi_ok) return 0; | |
| status = napi_get_reference_value(env, napi_fio->ref, &obj); | |
| if (status!=napi_ok) return 0; | |
| //create arg | |
| #if NAPI_VERSION >= 7 | |
| if (napi_fio->factory->direct_mem) | |
| status = napi_create_external_arraybuffer(env, buffer, bytes, NULL, NULL, &ab); | |
| else | |
| #endif | |
| status = napi_create_arraybuffer(env, bytes, &buf_data, &ab); | |
| if (status==napi_ok) | |
| status = napi_create_typedarray(env, napi_uint8_array, bytes, ab, 0, &arg); | |
| //call | |
| if (status==napi_ok) | |
| status = napi_make_callback(env, napi_fio->factory->async_ctx, obj, fun_val, 1, &arg, &res); | |
| if (status==napi_ok) | |
| status = napi_get_value_uint32(env, res, &nb_bytes); | |
| if ((status==napi_ok) && !napi_fio->factory->direct_mem && buf_data && nb_bytes) | |
| memcpy(buffer, buf_data, nb_bytes); | |
| #if NAPI_VERSION >= 7 | |
| //detach array buffer | |
| if (napi_fio->factory->direct_mem) | |
| napi_detach_arraybuffer(env, ab); | |
| #endif | |
| if (status!=napi_ok) return 0; | |
| return nb_bytes; | |
| } | |
| static u32 gfio_write(GF_FileIO *fileio, u8 *buffer, u32 bytes) | |
| { | |
| napi_status status; | |
| u32 nb_bytes; | |
| napi_value arg, ab, fact_obj, obj, fun_val, res; | |
| NAPI_FileIO *napi_fio = gf_fileio_get_udta(fileio); | |
| napi_env env = napi_fio->factory->env; | |
| //get fun and target this | |
| status = napi_get_reference_value(env, napi_fio->factory->root_ref, &fact_obj); | |
| if (status!=napi_ok) return 0; | |
| status = napi_get_named_property(env, fact_obj, "write", &fun_val); | |
| if (status!=napi_ok) return 0; | |
| status = napi_get_reference_value(env, napi_fio->ref, &obj); | |
| if (status!=napi_ok) return 0; | |
| //create arg | |
| #if NAPI_VERSION >= 7 | |
| if (napi_fio->factory->direct_mem) { | |
| status = napi_create_external_arraybuffer(env, buffer, bytes, NULL, NULL, &ab); | |
| } else | |
| #endif | |
| { | |
| void *buf_data=NULL; | |
| status = napi_create_arraybuffer(env, bytes, &buf_data, &ab); | |
| if (status == napi_ok) { | |
| memcpy(buf_data, buffer, bytes); | |
| } | |
| } | |
| if (status==napi_ok) | |
| status = napi_create_typedarray(env, napi_uint8_array, bytes, ab, 0, &arg); | |
| //call | |
| if (status==napi_ok) | |
| status = napi_make_callback(env, napi_fio->factory->async_ctx, obj, fun_val, 1, &arg, &res); | |
| if (status==napi_ok) | |
| status = napi_get_value_uint32(env, res, &nb_bytes); | |
| #if NAPI_VERSION >= 7 | |
| //detach arraybuffer | |
| if (napi_fio->factory->direct_mem) | |
| napi_detach_arraybuffer(env, ab); | |
| #endif | |
| if (status!=napi_ok) return 0; | |
| return nb_bytes; | |
| } | |
| static s64 gfio_tell(GF_FileIO *fileio) | |
| { | |
| napi_status status; | |
| s64 pos; | |
| napi_value fact_obj, obj, fun_val, res; | |
| NAPI_FileIO *napi_fio = gf_fileio_get_udta(fileio); | |
| napi_env env = napi_fio->factory->env; | |
| //get fun and target this | |
| napi_get_reference_value(env, napi_fio->factory->root_ref, &fact_obj); | |
| status = napi_get_named_property(env, fact_obj, "tell", &fun_val); | |
| if (status!=napi_ok) { | |
| return -1; | |
| } | |
| napi_get_reference_value(env, napi_fio->ref, &obj); | |
| //call | |
| status = napi_make_callback(env, napi_fio->factory->async_ctx, obj, fun_val, 0, NULL, &res); | |
| if (status!=napi_ok) { | |
| return -1; | |
| } | |
| status = napi_get_value_int64(env, res, &pos); | |
| if (status!=napi_ok) { | |
| return -1; | |
| } | |
| return pos; | |
| } | |
| static Bool gfio_eof(GF_FileIO *fileio) | |
| { | |
| napi_status status; | |
| bool is_eof; | |
| napi_value fact_obj, obj, fun_val, res; | |
| NAPI_FileIO *napi_fio = gf_fileio_get_udta(fileio); | |
| napi_env env = napi_fio->factory->env; | |
| //get fun and target this | |
| napi_get_reference_value(env, napi_fio->factory->root_ref, &fact_obj); | |
| status = napi_get_named_property(env, fact_obj, "eof", &fun_val); | |
| if (status!=napi_ok) { | |
| return GF_FALSE; | |
| } | |
| napi_get_reference_value(env, napi_fio->ref, &obj); | |
| //call | |
| status = napi_make_callback(env, napi_fio->factory->async_ctx, obj, fun_val, 0, NULL, &res); | |
| if (status!=napi_ok) { | |
| return GF_FALSE; | |
| } | |
| status = napi_get_value_bool(env, res, &is_eof); | |
| if (status!=napi_ok) { | |
| return -1; | |
| } | |
| return is_eof ? GF_TRUE : GF_FALSE; | |
| } | |
| static GF_FileIO *gfio_open(GF_FileIO *fileio_ref, const char *url, const char *mode, GF_Err *out_error) | |
| { | |
| napi_status status; | |
| char *fio_url = NULL; | |
| Bool do_delete = GF_FALSE; | |
| napi_value args[3], fact_obj, obj, fun_val, res; | |
| NAPI_FileIO *napi_fio = gf_fileio_get_udta(fileio_ref); | |
| napi_env env = napi_fio->factory->env; | |
| NAPI_GPAC | |
| *out_error = GF_OK; | |
| //make sure we are called from main thread | |
| if (gpac->main_thid != gf_th_id()) { | |
| *out_error = GF_NOT_SUPPORTED; | |
| return NULL; | |
| } | |
| if (!strcmp(mode, "url")) { | |
| NAPI_FileIO *new_gfio; | |
| char *path = gf_url_concatenate(gf_fileio_resource_url(fileio_ref), url); | |
| GF_SAFEALLOC(new_gfio, NAPI_FileIO); | |
| if (!new_gfio) { | |
| *out_error = GF_OUT_OF_MEM; | |
| return NULL; | |
| } | |
| new_gfio->factory = napi_fio->factory; | |
| new_gfio->gfio = gf_fileio_new(path, new_gfio, gfio_open, gfio_seek, gfio_read, gfio_write, gfio_tell, gfio_eof, NULL); | |
| gf_free(path); | |
| if (!new_gfio->gfio) { | |
| gf_free(new_gfio); | |
| *out_error = GF_OUT_OF_MEM; | |
| return NULL; | |
| } | |
| gf_fileio_tag_main_thread(new_gfio->gfio); | |
| gf_list_add(napi_fio->factory->pending_urls, new_gfio); | |
| //fio_ref.factory.gc_exclude.append(new_gfio) | |
| napi_fio->factory->all_refs += 1; | |
| return new_gfio->gfio; | |
| } | |
| if (!strcmp(mode, "ref")) { | |
| napi_fio->nb_refs += 1; | |
| napi_fio->factory->all_refs += 1; | |
| return fileio_ref; | |
| } | |
| if (!strcmp(mode, "probe")) { | |
| bool exists; | |
| //get fun and target this | |
| napi_get_reference_value(env, napi_fio->factory->root_ref, &fact_obj); | |
| status = napi_get_named_property(env, fact_obj, "exists", &fun_val); | |
| if (status!=napi_ok) return NULL; | |
| //create arg | |
| status = napi_create_string_utf8(env, url, NAPI_AUTO_LENGTH, &args[0]); | |
| if (status!=napi_ok) return NULL; | |
| //call | |
| status = napi_make_callback(env, napi_fio->factory->async_ctx, fact_obj, fun_val, 1, &args[0], &res); | |
| if (status!=napi_ok) return NULL; | |
| status = napi_get_value_bool(env, res, &exists); | |
| if (status!=napi_ok) return NULL; | |
| if (!exists) *out_error = GF_URL_ERROR; | |
| return NULL; | |
| } | |
| if (!strcmp(mode, "unref")) { | |
| napi_fio->nb_refs -= 1; | |
| napi_fio->factory->all_refs -= 1; | |
| if (napi_fio->nb_refs) return fileio_ref; | |
| do_delete = GF_TRUE; | |
| } | |
| if (!strcmp(mode, "close")) { | |
| if (napi_fio->ref != NULL) { | |
| //get fun and target this | |
| napi_get_reference_value(env, napi_fio->factory->root_ref, &fact_obj); | |
| status = napi_get_named_property(env, fact_obj, "close", &fun_val); | |
| napi_get_reference_value(env, napi_fio->ref, &obj); | |
| status = napi_make_callback(env, napi_fio->factory->async_ctx, obj, fun_val, 0, NULL, &res); | |
| napi_fio->factory->all_refs -= 1; | |
| } | |
| if (napi_fio->nb_refs) return NULL; | |
| do_delete = GF_TRUE; | |
| } | |
| if (do_delete) { | |
| uint32_t res; | |
| NAPI_FileIO *factory = napi_fio->factory; | |
| //file no longer used, can now be garbage collected | |
| //fio_ref.factory.gc_exclude.remove(fio_ref) | |
| if (napi_fio->factory != napi_fio) { | |
| gf_fileio_del(napi_fio->gfio); | |
| napi_get_reference_value(env, napi_fio->ref, &obj); | |
| napi_reference_unref(env, napi_fio->ref, &res); | |
| napi_delete_reference(env, napi_fio->ref); | |
| gf_free(napi_fio); | |
| } | |
| //final cleanup | |
| if (!factory->all_refs && !gf_list_count(factory->pending_urls)) { // and not len(fio_ref.factory.gc_exclude): | |
| gf_fileio_del(factory->gfio); | |
| napi_get_reference_value(env, factory->ref, &obj); | |
| napi_reference_unref(env, factory->ref, &res); | |
| napi_delete_reference(env, factory->ref); | |
| napi_get_reference_value(env, factory->root_ref, &obj); | |
| napi_wrap(env, obj, NULL, NULL, NULL, NULL); | |
| napi_reference_unref(env, factory->root_ref, &res); | |
| napi_delete_reference(env, factory->root_ref); | |
| gf_list_del(factory->pending_urls); | |
| gf_free(factory); | |
| } | |
| return NULL; | |
| } | |
| //this is an open() call, check if associated object exists, if not so we can use this instance | |
| NAPI_FileIO *fio = NULL; | |
| if (napi_fio->ref == NULL) { | |
| fio = napi_fio; | |
| if (gf_list_find(napi_fio->factory->pending_urls, napi_fio)>=0) { | |
| napi_fio->factory->all_refs -= 1; | |
| gf_list_del_item(napi_fio->factory->pending_urls, napi_fio); | |
| } | |
| } | |
| //check in pending urls | |
| if (fio == NULL) { | |
| u32 i, count = gf_list_count(napi_fio->factory->pending_urls); | |
| for (i=0; i<count; i++) { | |
| NAPI_FileIO *afio = gf_list_get(napi_fio->factory->pending_urls, i); | |
| const char *a_url = gf_fileio_resource_url(afio->gfio); | |
| if (!strcmp(a_url, url)) { | |
| napi_fio->factory->all_refs -= 1; | |
| gf_list_rem(napi_fio->factory->pending_urls, i); | |
| fio = afio; | |
| break; | |
| } | |
| } | |
| } | |
| //we need to create a new FileIO | |
| Bool created = (fio==NULL) ? GF_TRUE : GF_FALSE; | |
| //read url, translate | |
| if (!strncmp(url, "gfio://", 7)) { | |
| url = gf_fileio_translate_url(url); | |
| } | |
| //if we create a new FileIO, we need to resolve url, against parent URL | |
| else if (created) { | |
| fio_url = gf_url_concatenate(gf_fileio_resource_url(napi_fio->gfio), url); | |
| url = fio_url; | |
| } | |
| //create new FileIO | |
| if (fio==NULL) { | |
| GF_SAFEALLOC(fio, NAPI_FileIO); | |
| if (!fio) { | |
| *out_error = GF_OUT_OF_MEM; | |
| gf_free(fio_url); | |
| return NULL; | |
| } | |
| fio->factory = napi_fio->factory; | |
| fio->gfio = gf_fileio_new((char *) url, fio, gfio_open, gfio_seek, gfio_read, gfio_write, gfio_tell, gfio_eof, NULL); | |
| if (!fio->gfio) { | |
| gf_free(fio); | |
| gf_free(fio_url); | |
| *out_error = GF_OUT_OF_MEM; | |
| return NULL; | |
| } | |
| gf_fileio_tag_main_thread(fio->gfio); | |
| } | |
| //create object, direct copy of the object passed at construction | |
| napi_create_object(env, &obj); | |
| napi_create_reference(env, obj, 1, &fio->ref); | |
| //get fun and target this | |
| napi_get_reference_value(env, napi_fio->factory->root_ref, &fact_obj); | |
| status = napi_get_named_property(env, fact_obj, "open", &fun_val); | |
| if (status==napi_ok) { | |
| //create arg | |
| status = napi_create_string_utf8(env, url, NAPI_AUTO_LENGTH, &args[0]); | |
| } | |
| if (status==napi_ok) { | |
| status = napi_create_string_utf8(env, mode, NAPI_AUTO_LENGTH, &args[1]); | |
| } | |
| args[2] = fact_obj; | |
| //call | |
| if (status==napi_ok) { | |
| status = napi_make_callback(env, napi_fio->factory->async_ctx, obj, fun_val, 3, args, &res); | |
| } | |
| bool open_res = false; | |
| if (status==napi_ok) { | |
| status = napi_get_value_bool(env, res, &open_res); | |
| } | |
| if (fio_url) gf_free(fio_url); | |
| if (open_res) { | |
| //if not fio in fio.factory.gc_exclude: | |
| // fio.factory.gc_exclude.append(fio) | |
| fio->factory->all_refs += 1; | |
| return fio->gfio; | |
| } | |
| //failure to open a new created object, remove FileIO and let the object be garbage collected | |
| if (created) { | |
| gf_fileio_del(fio->gfio); | |
| fio->gfio = NULL; | |
| } | |
| *out_error = GF_URL_ERROR; | |
| return NULL; | |
| } | |
| void del_fio(napi_env env, void* finalize_data, void* finalize_hint) | |
| { | |
| } | |
| napi_value new_fio(napi_env env, napi_callback_info info) | |
| { | |
| napi_value fact_obj, fun_val; | |
| NAPI_FileIO *napi_fio; | |
| NARG_ARGS_THIS(3, 2); | |
| NARG_STR(url, 0, NULL); | |
| NARG_BOOL(direct_mem, 2, GF_TRUE); | |
| NAPI_GPAC | |
| gpac->is_init = GF_TRUE; | |
| #if NAPI_VERSION < 7 | |
| direct_mem = GF_FALSE; | |
| #endif | |
| if (!url) { | |
| napi_throw_error(env, NULL, "FileIO must be constructed with a non-null URL"); | |
| return NULL; | |
| } | |
| napi_valuetype rtype; | |
| fact_obj = argv[1]; | |
| napi_typeof(env, fact_obj, &rtype); | |
| if (rtype != napi_object) { | |
| napi_throw_error(env, NULL, "factory is not an object"); | |
| return NULL; | |
| } | |
| status = napi_get_named_property(env, fact_obj, "open", &fun_val); | |
| if (status != napi_ok) { | |
| napi_throw_error(env, NULL, "factory has no open function"); | |
| return NULL; | |
| } | |
| status = napi_get_named_property(env, fact_obj, "close", &fun_val); | |
| if (status != napi_ok) { | |
| napi_throw_error(env, NULL, "factory has no close function"); | |
| return NULL; | |
| } | |
| GF_SAFEALLOC(napi_fio, NAPI_FileIO); | |
| if (!napi_fio) { | |
| napi_throw_error(env, NULL, "Failed to allocate FileIO"); | |
| return NULL; | |
| } | |
| napi_fio->gfio = gf_fileio_new(url, napi_fio, gfio_open, gfio_seek, gfio_read, gfio_write, gfio_tell, gfio_eof, NULL); | |
| if (!napi_fio->gfio) { | |
| gf_free(napi_fio); | |
| napi_throw_error(env, NULL, "Failed to allocate FileIO"); | |
| return NULL; | |
| } | |
| gf_fileio_tag_main_thread(napi_fio->gfio); | |
| napi_create_reference(env, fact_obj, 1, &napi_fio->root_ref); | |
| napi_fio->env = env; | |
| napi_fio->factory = napi_fio; | |
| napi_fio->pending_urls = gf_list_new(); | |
| napi_fio->direct_mem = direct_mem; | |
| napi_value async_name; | |
| napi_create_string_utf8(env, "AsyncFileIO", NAPI_AUTO_LENGTH, &async_name); | |
| napi_async_init(env, this_val, async_name, &napi_fio->async_ctx); | |
| napi_property_descriptor fio_base_properties[] = { | |
| { "url", NULL, NULL, fio_getter, NULL, NULL, napi_enumerable, &FIO_PROP_URL}, | |
| }; | |
| NAPI_CALL( napi_define_properties(env, this_val, sizeof(fio_base_properties)/sizeof(napi_property_descriptor), fio_base_properties) ); | |
| NAPI_CALL( napi_wrap(env, this_val, napi_fio, del_fio, NULL, NULL) ); | |
| NAPI_CALL( napi_type_tag_object(env, this_val, &fileio_tag) ); | |
| return this_val; | |
| } | |
| napi_status init_fio_class(napi_env env, napi_value exports, GPAC_NAPI *inst) | |
| { | |
| napi_status status; | |
| napi_value cons; | |
| status = napi_define_class(env, "FileIO", NAPI_AUTO_LENGTH , new_fio, NULL, 0, NULL, &cons); | |
| if (status != napi_ok) return status; | |
| return napi_set_named_property(env, exports, "FileIO", cons); | |
| } |