📄 terminal.c
字号:
/* * GPAC - Multimedia Framework C SDK * * Copyright (c) Jean Le Feuvre 2000-2005 * All rights reserved * * This file is part of GPAC / Media terminal sub-project * * GPAC is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * GPAC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */#include <gpac/internal/terminal_dev.h>#include <gpac/internal/renderer_dev.h>#include <gpac/constants.h>#include <gpac/options.h>#include <gpac/network.h>/*textual command processing*/#include <gpac/scene_manager.h>u32 gf_term_get_time(GF_Terminal *term){ assert(term); return gf_sr_get_clock(term->renderer);}void gf_term_invalidate_renderer(GF_Terminal *term){ gf_sr_invalidate(term->renderer, NULL);}static Bool check_user(GF_User *user){ if (!user->config) return 0; if (!user->modules) return 0; if (!user->opaque) return 0; return 1;}static Bool term_script_action(void *opaque, u32 type, GF_Node *n, GF_JSAPIParam *param){ Bool ret; GF_Terminal *term = (GF_Terminal *) opaque; if (type==GF_JSAPI_OP_MESSAGE) { gf_term_message(term, term->root_scene->root_od->net_service->url, param->info.msg, param->info.e); return 1; } if (type==GF_JSAPI_OP_GET_OPT) { param->gpac_cfg.key_val = gf_cfg_get_key(term->user->config, param->gpac_cfg.section, param->gpac_cfg.key); return 1; } if (type==GF_JSAPI_OP_SET_OPT) { gf_cfg_set_key(term->user->config, param->gpac_cfg.section, param->gpac_cfg.key, param->gpac_cfg.key_val); return 1; } if (type==GF_JSAPI_OP_GET_DOWNLOAD_MANAGER) { param->dnld_man = term->downloader; return 1; } if (type==GF_JSAPI_OP_GET_SCENE_URI) { GF_InlineScene *is = (GF_InlineScene *)gf_sg_get_private(gf_node_get_graph(n)); param->uri.url = is->root_od->net_service->url; param->uri.nb_params = 0; return 1; } ret = 0; if (term->renderer->visual_renderer->ScriptAction) ret = term->renderer->visual_renderer->ScriptAction(term->renderer->visual_renderer, type, n, param); if (ret) return ret; if (type==GF_JSAPI_OP_LOAD_URL) { if (gf_sg_get_private(gf_node_get_graph(n)) == term->root_scene) { GF_Event evt; if (!term->user->EventProc) return 0; evt.type = GF_EVENT_NAVIGATE; evt.navigate.to_url = param->uri.url; evt.navigate.parameters = param->uri.params; evt.navigate.param_count = param->uri.nb_params; return term->user->EventProc(term->user->opaque, &evt); } else { /*TODO*/ return 0; } } return 0;}static void gf_term_reload_cfg(GF_Terminal *term){ const char *sOpt; Double fps; u32 mode; s32 prio; if (!term) return; /*reload term part*/ sOpt = gf_cfg_get_key(term->user->config, "Systems", "AlwaysDrawBIFS"); if (sOpt && !stricmp(sOpt, "yes")) term->flags &= ~GF_TERM_SYSDEC_RESYNC; else term->flags |= GF_TERM_SYSDEC_RESYNC; sOpt = gf_cfg_get_key(term->user->config, "Systems", "ForceSingleClock"); if (sOpt && !stricmp(sOpt, "yes")) term->flags |= GF_TERM_SINGLE_CLOCK; else term->flags &= ~GF_TERM_SINGLE_CLOCK; sOpt = gf_cfg_get_key(term->user->config, "Rendering", "FrameRate"); if (sOpt) { fps = atof(sOpt); term->frame_duration = (u32) (1000/fps); gf_sr_set_fps(term->renderer, fps); } if (term->user->init_flags & GF_TERM_NO_VISUAL_THREAD){ //gf_term_set_threading(term->mediaman, 1); } else { prio = GF_THREAD_PRIORITY_NORMAL; sOpt = gf_cfg_get_key(term->user->config, "Systems", "Priority"); if (sOpt) { if (!stricmp(sOpt, "low")) prio = GF_THREAD_PRIORITY_LOWEST; else if (!stricmp(sOpt, "normal")) prio = GF_THREAD_PRIORITY_NORMAL; else if (!stricmp(sOpt, "high")) prio = GF_THREAD_PRIORITY_HIGHEST; else if (!stricmp(sOpt, "real-time")) prio = GF_THREAD_PRIORITY_REALTIME; } else { gf_cfg_set_key(term->user->config, "Systems", "Priority", "normal"); } gf_term_set_priority(term, prio); sOpt = gf_cfg_get_key(term->user->config, "Systems", "ThreadingPolicy"); if (sOpt) { mode = GF_TERM_THREAD_FREE; if (!stricmp(sOpt, "Single")) mode = GF_TERM_THREAD_SINGLE; else if (!stricmp(sOpt, "Multi")) mode = GF_TERM_THREAD_MULTI; gf_term_set_threading(term, mode); } } /*default data timeout is 20 sec*/ term->net_data_timeout = 20000; sOpt = gf_cfg_get_key(term->user->config, "Network", "DataTimeout"); if (sOpt) term->net_data_timeout = atoi(sOpt); if (term->root_scene) gf_is_set_duration(term->root_scene); /*reload renderer config*/ gf_sr_set_option(term->renderer, GF_OPT_RELOAD_CONFIG, 1);}static Bool gf_term_get_user_pass(void *usr_cbk, const char *site_url, char *usr_name, char *password){ GF_Event evt; GF_Terminal *term = (GF_Terminal *)usr_cbk; evt.auth.site_url = site_url; evt.auth.user = usr_name; evt.auth.password = password; return term->user->EventProc(term->user->opaque, &evt);}GF_EXPORTGF_Terminal *gf_term_new(GF_User *user){ GF_Terminal *tmp; const char *cf; if (!check_user(user)) return NULL; GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Creating terminal\n")); tmp = (GF_Terminal*)malloc(sizeof(GF_Terminal)); if (!tmp) return NULL; memset(tmp, 0, sizeof(GF_Terminal)); /*just for safety*/ gf_sys_init(); tmp->user = user; /*this is not changeable at runtime*/ if (user->init_flags & GF_TERM_NO_VISUAL_THREAD) { tmp->flags |= GF_TERM_RENDER_FRAME; } else { cf = gf_cfg_get_key(user->config, "Systems", "NoVisualThread"); if (!cf || !stricmp(cf, "no")) { tmp->flags &= ~GF_TERM_RENDER_FRAME; } else { tmp->flags |= GF_TERM_RENDER_FRAME; } } /*setup scene renderer*/ tmp->renderer = gf_sr_new(user, !(tmp->flags & GF_TERM_RENDER_FRAME) , tmp); if (!tmp->renderer) { free(tmp); return NULL; } GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] renderer loaded\n")); gf_sr_set_fps(tmp->renderer, 30.0); tmp->frame_duration = (u32) (1000/30); tmp->downloader = gf_dm_new(user->config); gf_dm_set_auth_callback(tmp->downloader, gf_term_get_user_pass, tmp); GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] downloader loaded\n")); tmp->net_services = gf_list_new(); tmp->net_services_to_remove = gf_list_new(); tmp->channels_pending = gf_list_new(); tmp->media_queue = gf_list_new(); tmp->net_mx = gf_mx_new(); tmp->input_streams = gf_list_new(); tmp->x3d_sensors = gf_list_new(); /*mode is changed when reloading cfg*/ gf_term_init_scheduler(tmp, GF_TERM_THREAD_FREE); GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Terminal created - loading config\n")); gf_term_reload_cfg(tmp); return tmp;}GF_EXPORTGF_Err gf_term_del(GF_Terminal * term){ GF_Err e; u32 timeout; if (!term) return GF_BAD_PARAM; GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Destroying terminal\n")); /*close main service*/ gf_term_disconnect(term); GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] main service disconnected\n")); /*wait for destroy*/ e = GF_IO_ERR; timeout = 1000; while (term->root_scene || gf_list_count(term->net_services) || gf_list_count(term->net_services_to_remove)) { gf_sleep(30); /*this shouldn't be needed but unfortunately there's a bug hanging around there...*/ timeout--; if (!timeout) break; } if (timeout) { assert(!gf_list_count(term->net_services)); assert(!gf_list_count(term->net_services_to_remove)); e = GF_OK; } GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] All network services deleted\n")); /*stop the media manager */ gf_term_stop_scheduler(term); /*delete renderer before the input sensor stacks to avoid recieving events from the renderer when destroying these stacks*/ gf_sr_del(term->renderer); gf_list_del(term->net_services); gf_list_del(term->net_services_to_remove); gf_list_del(term->input_streams); gf_list_del(term->x3d_sensors); assert(!gf_list_count(term->channels_pending)); gf_list_del(term->channels_pending); assert(!gf_list_count(term->media_queue)); assert(!term->nodes_pending); gf_list_del(term->media_queue); if (term->downloader) gf_dm_del(term->downloader); gf_mx_del(term->net_mx); gf_sys_close(); free(term); GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Terminal destroyed\n")); return e;}void gf_term_message(GF_Terminal *term, const char *service, const char *message, GF_Err error){ if (!term || !term->user) return; GF_USER_MESSAGE(term->user, service, message, error);}static void gf_term_set_play_state(GF_Terminal *term, u32 PlayState, Bool reset_audio, Bool pause_clocks){ u32 i, j; GF_ClientService *ns; /*only play/pause if connected*/ if (!term || !term->root_scene) return; /*and if not already paused/playing*/ if (!term->play_state && !PlayState) return; if (term->play_state && (PlayState==GF_STATE_PAUSED)) return; /*pause renderer*/ if ((PlayState==GF_STATE_PLAYING) && reset_audio) gf_sr_set_option(term->renderer, GF_OPT_PLAY_STATE, 0xFF); else gf_sr_set_option(term->renderer, GF_OPT_PLAY_STATE, PlayState); if (PlayState==GF_STATE_STEP_PAUSE) PlayState = term->play_state ? GF_STATE_PLAYING : GF_STATE_PAUSED; if (term->play_state == PlayState) return; term->play_state = PlayState; if (!pause_clocks) return; /*pause all clocks on all services*/ i=0; while ( (ns = (GF_ClientService*)gf_list_enum(term->net_services, &i)) ) { GF_Clock *ck; j=0; while ( (ck = (GF_Clock *)gf_list_enum(ns->Clocks, &j)) ) { if (PlayState) gf_clock_pause(ck); else gf_clock_resume(ck); } }}GF_Err gf_term_step_clocks(GF_Terminal * term, u32 ms_diff){ u32 i, j; GF_ClientService *ns; /*only play/pause if connected*/ if (!term || !term->root_scene || !term->root_scene->root_od) return GF_BAD_PARAM; if (!term->play_state) return GF_BAD_PARAM; gf_sr_lock(term->renderer, 1); i=0; while ( (ns = (GF_ClientService*)gf_list_enum(term->net_services, &i)) ) { GF_Clock *ck; j=0; while ( (ck = (GF_Clock *)gf_list_enum(ns->Clocks, &j)) ) { ck->init_time += ms_diff; } } term->renderer->step_mode = 1; term->renderer->draw_next_frame = 1; gf_sr_lock(term->renderer, 0); return GF_OK;}GF_EXPORTvoid gf_term_connect_from_time(GF_Terminal * term, const char *URL, u64 startTime, Bool pause_at_first_frame){ GF_InlineScene *is; GF_ObjectManager *odm; const char *main_url; if (!URL || !strlen(URL)) return; if (term->root_scene) { if (term->root_scene->root_od && term->root_scene->root_od->net_service) { main_url = term->root_scene->root_od->net_service->url; if (main_url && !strcmp(main_url, URL)) { gf_term_play_from_time(term, 0, pause_at_first_frame); return; } } /*disconnect*/ gf_term_disconnect(term); } GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Connecting to %s\n", URL)); gf_term_lock_net(term, 1); GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Creating new root scene\n", URL)); /*create a new scene*/ is = gf_is_new(NULL); gf_sg_set_script_action(is->graph, term_script_action, term); odm = gf_odm_new(); is->root_od = odm; term->root_scene = is; odm->parentscene = NULL; odm->subscene = is; odm->term = term; GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] root scene created\n", URL)); gf_term_lock_net(term, 0); odm->media_start_time = startTime; /*render first visual frame and pause*/ if (pause_at_first_frame) gf_term_set_play_state(term, GF_STATE_STEP_PAUSE, 0, 0); /*connect - we don't have any parentID */ gf_term_connect_object(term, odm, (char *) URL, NULL);}GF_EXPORTvoid gf_term_connect(GF_Terminal * term, const char *URL){ gf_term_connect_from_time(term, URL, 0, 0);}GF_EXPORTvoid gf_term_disconnect(GF_Terminal *term){ if (!term->root_scene) return; /*resume*/ if (term->play_state) gf_term_set_play_state(term, GF_STATE_PLAYING, 1, 1); gf_odm_disconnect(term->root_scene->root_od, 1); while (term->root_scene || gf_list_count(term->net_services_to_remove)) { gf_term_handle_services(term); gf_sleep(10); }}GF_EXPORTconst char *gf_term_get_url(GF_Terminal *term){ if (!term || !term->root_scene) return NULL; return term->root_scene->root_od->net_service->url;}static GF_Err gf_term_set_cache_state(GF_Terminal *term, u32 state){ GF_Err e; if (!term) return GF_BAD_PARAM; if (term->enable_cache && (state==GF_MEDIA_CACHE_ENABLED)) return GF_OK; if (!term->enable_cache && (state!=GF_MEDIA_CACHE_ENABLED)) return GF_OK; term->enable_cache = !term->enable_cache; /*not connected, nothing to do*/ if (!term->root_scene) return GF_OK; gf_term_lock_net(term, 1); if (term->enable_cache) { /*otherwise start cache*/ e = gf_term_service_cache_load(term->root_scene->root_od->net_service); } else { e = gf_term_service_cache_close(term->root_scene->root_od->net_service, (state==GF_MEDIA_CACHE_DISCARD) ? 1 : 0); } gf_term_lock_net(term, 0); return e;}/*set rendering option*/GF_EXPORTGF_Err gf_term_set_option(GF_Terminal * term, u32 type, u32 value){ if (!term) return GF_BAD_PARAM; switch (type) { case GF_OPT_PLAY_STATE: gf_term_set_play_state(term, value, 0, 1); return GF_OK; case GF_OPT_RELOAD_CONFIG: gf_term_reload_cfg(term); return GF_OK; case GF_OPT_MEDIA_CACHE: gf_term_set_cache_state(term, value); return GF_OK; default: return gf_sr_set_option(term->renderer, type, value); }}GF_EXPORTGF_Err gf_term_set_simulation_frame_rate(GF_Terminal * term, Double frame_rate){ if (!term) return GF_BAD_PARAM; term->frame_duration = (u32) (1000.0 / frame_rate); gf_sr_set_fps(term->renderer, frame_rate); return GF_OK;}GF_EXPORTDouble gf_term_get_simulation_frame_rate(GF_Terminal *term){ return term ? term->renderer->frame_rate : 0.0;}/*returns 0 if any of the clock still hasn't seen EOS*/u32 Term_CheckClocks(GF_ClientService *ns, GF_InlineScene *is){ GF_Clock *ck; u32 i; if (is) { GF_ObjectManager *odm; if (is->root_od->net_service != ns) { if (!Term_CheckClocks(is->root_od->net_service, is)) return 0; } i=0; while ( (odm = (GF_ObjectManager*)gf_list_enum(is->ODlist, &i)) ) { if (odm->net_service != ns) { if (!Term_CheckClocks(odm->net_service, NULL)) return 0; } } } i=0; while ( (ck = (GF_Clock *)gf_list_enum(ns->Clocks, &i) ) ) { if (!ck->has_seen_eos) return 0; } return 1;}u32 Term_CheckIsOver(GF_Terminal *term){ if (!term->root_scene) return 1; /*if input sensors consider the scene runs forever*/ if (gf_list_count(term->input_streams)) return 0; if (gf_list_count(term->x3d_sensors)) return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -