| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2011-2012 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / Sampe On-Scvreen Display 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/modules/term_ext.h> | |
| #include <gpac/internal/terminal_dev.h> | |
| #include <gpac/internal/compositor_dev.h> | |
| #include <gpac/nodes_mpeg4.h> | |
| typedef struct | |
| { | |
| GF_ObjectManager *odm; | |
| GF_Terminal *term; | |
| GF_TermEventFilter evt_filter; | |
| /*some of our nodes*/ | |
| M_Switch *visible; | |
| M_Transform2D *transform; | |
| M_CompositeTexture2D *ct2d; | |
| M_Text *text; | |
| char statBuffer[100]; | |
| u32 refresh_time_ms; | |
| GF_SystemRTInfo rti; | |
| } GF_OSD; | |
| #if 0 | |
| static GFINLINE GF_Node *create_node(GF_OSD *osd, u32 tag, GF_Node *par) | |
| { | |
| GF_Node *n = gf_node_new(osd->odm->subscene->graph, tag); | |
| if (n) { | |
| gf_node_init(n); | |
| if (par) { | |
| gf_node_list_add_child( & ((GF_ParentNode *)par)->children, n); | |
| gf_node_register(n, par); | |
| } | |
| } | |
| return n; | |
| } | |
| #endif | |
| const char *osd_scene_graph = "\ | |
| EXTERNPROTO Untransform [\ | |
| exposedField MFNode children []\ | |
| ]\ | |
| [ \"urn:inet:gpac:builtin:Untransform\"]\ | |
| OrderedGroup {\ | |
| children [\ | |
| Untransform {\ | |
| children [\ | |
| DEF N1 Switch {\ | |
| whichChoice 0\ | |
| choice [\ | |
| DEF N2 Transform2D {\ | |
| children [\ | |
| Shape {\ | |
| appearance Appearance {\ | |
| material Material2D {\ | |
| transparency 0\ | |
| filled TRUE\ | |
| }\ | |
| texture DEF N3 CompositeTexture2D {\ | |
| pixelWidth 256\ | |
| pixelHeight 16\ | |
| children [\ | |
| Background2D {backColor 0.6 0.6 0.6}\ | |
| Shape {\ | |
| appearance Appearance {\ | |
| material Material2D {\ | |
| emissiveColor 0 0 0\ | |
| filled TRUE\ | |
| }\ | |
| }\ | |
| geometry DEF N4 Text {\ | |
| string [\"My Sample Text !\"]\ | |
| fontStyle FontStyle {\ | |
| size 12\ | |
| justify [\"MIDDLE\", \"MIDDLE\"]\ | |
| family [\"SANS\"]\ | |
| }\ | |
| }\ | |
| }\ | |
| ]\ | |
| }\ | |
| }\ | |
| geometry Bitmap {}\ | |
| }\ | |
| ]\ | |
| }\ | |
| ]\ | |
| }\ | |
| ]\ | |
| }\ | |
| ]\ | |
| }\ | |
| "; | |
| void osd_on_resize(GF_Node *hdl, GF_DOM_Event *event, GF_Node *observer) | |
| { | |
| GF_DOMHandler *the_hdl = (GF_DOMHandler *) hdl; | |
| GF_OSD *osd = the_hdl->evt_listen_obj; | |
| if (osd->ct2d) { | |
| //osd->ct2d->pixelWidth = FIX2INT(event->screen_rect.width); | |
| gf_node_dirty_set((GF_Node *) osd->ct2d, GF_SG_NODE_DIRTY, 1); | |
| if (osd->transform) { | |
| osd->transform->translation.y = INT2FIX( (FIX2INT(event->screen_rect.height) - osd->ct2d->pixelHeight) / 2 ) ; | |
| gf_node_dirty_set((GF_Node *) osd->transform, GF_SG_NODE_DIRTY, 1); | |
| } | |
| } | |
| } | |
| Bool osd_load_scene(GF_OSD *osd) | |
| { | |
| GF_Node *n; | |
| GF_List *nodes; | |
| const char *opt; | |
| GF_DOMHandler *hdl; | |
| /*BT/VRML from string*/ | |
| GF_List *gf_sm_load_bt_from_string(GF_SceneGraph *in_scene, const char *node_str, Bool force_wrl); | |
| /*create a new scene*/ | |
| osd->odm = gf_odm_new(); | |
| osd->odm->term = osd->term; | |
| osd->odm->subscene = gf_scene_new(NULL); | |
| osd->odm->subscene->root_od = osd->odm; | |
| gf_sg_set_scene_size_info(osd->odm->subscene->graph, 0, 0, 1); | |
| /*create a scene graph*/ | |
| nodes = gf_sm_load_bt_from_string(osd->odm->subscene->graph, osd_scene_graph, 0); | |
| n = gf_list_get(nodes, 0); | |
| gf_list_del(nodes); | |
| if (!n) return 0; | |
| gf_sg_set_root_node(osd->odm->subscene->graph, n); | |
| gf_sg_set_scene_size_info(osd->odm->subscene->graph, 0, 0, 1); | |
| hdl = gf_dom_listener_build(n, GF_EVENT_RESIZE, 0); | |
| hdl->handle_event = osd_on_resize; | |
| hdl->evt_listen_obj = osd; | |
| osd->visible = (M_Switch *)gf_sg_find_node_by_name(osd->odm->subscene->graph, "N1"); | |
| osd->transform = (M_Transform2D *)gf_sg_find_node_by_name(osd->odm->subscene->graph, "N2"); | |
| osd->ct2d = (M_CompositeTexture2D *)gf_sg_find_node_by_name(osd->odm->subscene->graph, "N3"); | |
| osd->text = (M_Text *)gf_sg_find_node_by_name(osd->odm->subscene->graph, "N4"); | |
| if (osd->text->string.vals[0]) { | |
| gf_free(osd->text->string.vals[0]); | |
| osd->text->string.vals[0] = NULL; | |
| } | |
| strcpy(osd->statBuffer, "Hello World !"); | |
| osd->text->string.vals[0] = osd->statBuffer; | |
| opt = gf_opts_get_key("OSD", "Visible"); | |
| if (!opt || strcmp(opt, "yes")) osd->visible->whichChoice = -1; | |
| return 1; | |
| } | |
| Bool osd_on_event_play(void *udta, GF_Event *event, Bool consumed_by_compositor) | |
| { | |
| GF_OSD* osd = (GF_OSD*)udta; | |
| switch (event->type) { | |
| case GF_EVENT_SCENE_SIZE: | |
| gf_sg_set_scene_size_info(osd->odm->subscene->graph, event->size.width, event->size.height, 1); | |
| break; | |
| case GF_EVENT_KEYUP: | |
| if ( (event->key.key_code == GF_KEY_I) && (event->key.flags & GF_KEY_MOD_CTRL)) { | |
| if (osd->visible->whichChoice==0) { | |
| osd->visible->whichChoice = -1; | |
| gf_cfg_set_key(osd->term->user->config, "OSD", "Visible", "no"); | |
| } else { | |
| osd->visible->whichChoice = 0; | |
| gf_cfg_set_key(osd->term->user->config, "OSD", "Visible", "yes"); | |
| } | |
| gf_node_dirty_set((GF_Node *) osd->visible, GF_SG_NODE_DIRTY, 1); | |
| } | |
| break; | |
| } | |
| return 0; | |
| } | |
| static Bool osd_process(GF_TermExt *termext, u32 action, void *param) | |
| { | |
| const char *opt; | |
| GF_OSD *osd = termext->udta; | |
| switch (action) { | |
| case GF_TERM_EXT_START: | |
| osd->term = (GF_Terminal *) param; | |
| opt = gf_opts_get_key("OSD", "Enabled"); | |
| if (!opt || strcmp(opt, "yes")) return 0; | |
| /*load scene*/ | |
| if (! osd_load_scene(osd)) return 0; | |
| /*attach scene to compositor*/ | |
| gf_sc_register_extra_graph(osd->term->compositor, osd->odm->subscene->graph, 0); | |
| /*we are not threaded*/ | |
| termext->caps |= GF_TERM_EXTENSION_NOT_THREADED; | |
| osd->refresh_time_ms = 500; | |
| osd->evt_filter.on_event = osd_on_event_play; | |
| osd->evt_filter.udta = osd; | |
| gf_term_add_event_filter(osd->term, &osd->evt_filter); | |
| return 1; | |
| case GF_TERM_EXT_STOP: | |
| osd->text->string.vals[0] = NULL; | |
| /*remove scene to compositor*/ | |
| gf_sc_register_extra_graph(osd->term->compositor, osd->odm->subscene->graph, 1); | |
| gf_odm_disconnect(osd->odm, 1); | |
| osd->odm = NULL; | |
| gf_term_remove_event_filter(osd->term, &osd->evt_filter); | |
| osd->term = NULL; | |
| break; | |
| case GF_TERM_EXT_PROCESS: | |
| /*flush all events until current time if reached*/ | |
| if ((osd->visible->whichChoice==0) && gf_sys_get_rti(osd->refresh_time_ms, &osd->rti, 0)) { | |
| sprintf(osd->statBuffer, "CPU %02d - FPS %02.2f - MEM "LLU" KB", osd->rti.process_cpu_usage, gf_sc_get_fps(osd->term->compositor, 0), osd->rti.process_memory/1000); | |
| gf_node_dirty_set((GF_Node *) osd->text, GF_SG_NODE_DIRTY, 1); | |
| } | |
| break; | |
| } | |
| return 0; | |
| } | |
| GF_TermExt *osd_new() | |
| { | |
| GF_TermExt *dr; | |
| GF_OSD *osd; | |
| dr = (GF_TermExt*)gf_malloc(sizeof(GF_TermExt)); | |
| memset(dr, 0, sizeof(GF_TermExt)); | |
| GF_REGISTER_MODULE_INTERFACE(dr, GF_TERM_EXT_INTERFACE, "GPAC OnScreen Display", "gpac distribution"); | |
| GF_SAFEALLOC(osd, GF_OSD); | |
| dr->process = osd_process; | |
| dr->udta = osd; | |
| return dr; | |
| } | |
| void osd_delete(GF_BaseInterface *ifce) | |
| { | |
| GF_TermExt *dr = (GF_TermExt *) ifce; | |
| GF_OSD *osd = dr->udta; | |
| gf_free(osd); | |
| gf_free(dr); | |
| } | |
| GPAC_MODULE_EXPORT | |
| const u32 *QueryInterfaces() | |
| { | |
| static u32 si [] = { | |
| GF_TERM_EXT_INTERFACE, | |
| 0 | |
| }; | |
| return si; | |
| } | |
| GPAC_MODULE_EXPORT | |
| GF_BaseInterface *LoadInterface(u32 InterfaceType) | |
| { | |
| if (InterfaceType == GF_TERM_EXT_INTERFACE) return (GF_BaseInterface *)osd_new(); | |
| return NULL; | |
| } | |
| GPAC_MODULE_EXPORT | |
| void ShutdownInterface(GF_BaseInterface *ifce) | |
| { | |
| switch (ifce->InterfaceType) { | |
| case GF_TERM_EXT_INTERFACE: | |
| osd_delete(ifce); | |
| break; | |
| } | |
| } | |
| GPAC_MODULE_STATIC_DECLARATION( osd ) |