📄 network_service.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/network.h>#define GET_TERM() GF_Terminal *term = (GF_Terminal *) user_priv; if (!term) return;static GFINLINE GF_Channel *gf_term_get_channel(GF_ClientService *service, LPNETCHANNEL ns){ GF_Channel *ch = (GF_Channel *)ns; if (!service || !ch) return NULL; if (ch->chan_id != (u32) ch) return NULL; if (ch->service != service) return NULL; return ch;}static void term_on_message(void *user_priv, GF_ClientService *service, GF_Err error, const char *message){ GET_TERM(); /*check for UDP timeout*/ if (error==GF_IP_UDP_TIMEOUT) { const char *sOpt = gf_cfg_get_key(term->user->config, "Network", "AutoReconfigUDP"); if (sOpt && !stricmp(sOpt, "yes")) { sOpt = gf_cfg_get_key(term->user->config, "Network", "UDPNotAvailable"); /*if option is already set don't bother try reconfig*/ if (!sOpt || stricmp(sOpt, "yes")) { char szMsg[1024]; sprintf(szMsg, "!! UDP down (%s) - Retrying with TCP !!\n", message); gf_term_message(term, service->url, szMsg, GF_OK); /*reload scene*/ if (term->reload_url) free(term->reload_url); term->reload_state = 1; term->reload_url = strdup(term->root_scene->root_od->net_service->url); gf_cfg_set_key(term->user->config, "Network", "UDPNotAvailable", "yes"); return; } } } gf_term_message(term, service->url, message, error);}static void term_on_connect(void *user_priv, GF_ClientService *service, LPNETCHANNEL netch, GF_Err err){ GF_Channel *ch; GF_ObjectManager *root; GET_TERM(); root = service->owner; if (root && (root->net_service != service)) { gf_term_message(term, service->url, "Incomaptible module type", GF_SERVICE_ERROR); return; } /*this is service connection*/ if (!netch) { if (err) { char msg[5000]; sprintf(msg, "Cannot open %s", service->url); gf_term_message(term, service->url, msg, err); /*destroy service only if attached*/ if (root) { gf_term_lock_net(term, 1); service->ifce->CloseService(service->ifce); root->net_service = NULL; service->owner = NULL; /*depends on module: some module could forget to call gf_term_on_disconnect */ if ( gf_list_del_item(term->net_services, service) >= 0) { /*and queue for destroy*/ gf_list_add(term->net_services_to_remove, service); } gf_term_lock_net(term, 0); if (!root->parentscene) { GF_Event evt; evt.type = GF_EVENT_CONNECT; evt.connect.is_connected = 0; GF_USER_SENDEVENT(term->user, &evt); } else { /*try to reinsert OD for VRML/X3D with multiple URLs: 1- first remove from parent scene without destroying object, this will trigger a re-setup if other URLs are present 2- then destroy object*/ gf_is_remove_object(root->parentscene, root, 0); gf_odm_disconnect(root, 1); } return; } } if (!root) { /*channel service connect*/ u32 i; GF_ChannelSetup *cs; GF_List *ODs = gf_list_new(); gf_term_lock_net(term, 1); i=0; while ((cs = (GF_ChannelSetup*)gf_list_enum(term->channels_pending, &i))) { if (cs->ch->service != service) continue; gf_list_rem(term->channels_pending, i-1); i--; /*even if error do setup (channel needs to be deleted)*/ if (gf_odm_post_es_setup(cs->ch, cs->dec, err) == GF_OK) { if (cs->ch->odm && (gf_list_find(ODs, cs->ch->odm)==-1) ) gf_list_add(ODs, cs->ch->odm); } free(cs); } gf_term_lock_net(term, 0); /*finally setup all ODs concerned (we do this later in case of scalability)*/ while (gf_list_count(ODs)) { GF_ObjectManager *odm = (GF_ObjectManager*)gf_list_get(ODs, 0); gf_list_rem(ODs, 0); /*force re-setup*/ gf_is_setup_object(odm->parentscene, odm); } gf_list_del(ODs); } else { /*setup od*/ gf_odm_setup_entry_point(root, NULL); } /*load cache if requested*/ if (!err && term->enable_cache) { err = gf_term_service_cache_load(service); /*not a fatal error*/ if (err) gf_term_message(term, "GPAC Cache", "Cannot load cache", err); } } /*this is channel connection*/ ch = gf_term_get_channel(service, netch); if (!ch) return; /*confirm channel connection even if error - this allow playback of objects even if not all streams are setup */ gf_term_lock_net(term, 1); gf_es_on_connect(ch); gf_term_lock_net(term, 0); if (err) { gf_term_message(term, service->url, "Channel Connection Failed", err); ch->es_state = GF_ESM_ES_UNAVAILABLE;// return; } /*Plays request are skiped until all channels are connected. We send a PLAY on the objecy in case 1-OD user has requested a play 2-this is a channel of the root OD */ if ( (ch->odm->mo && ch->odm->mo->num_open) || !ch->odm->parentscene ) { gf_odm_start(ch->odm); }#if 0 else if (ch->odm->codec && ch->odm->codec->ck && ch->odm->codec->ck->no_time_ctrl) { gf_odm_play(ch->odm); }#endif}static void term_on_disconnect(void *user_priv, GF_ClientService *service, LPNETCHANNEL netch, GF_Err response){ GF_ObjectManager *root; GF_Channel *ch; GET_TERM(); /*may be null upon destroy*/ root = service->owner; if (root && (root->net_service != service)) { if (root->net_service) gf_term_message(term, service->url, "Incompatible module type", GF_SERVICE_ERROR); return; } /*this is service disconnect*/ if (!netch) { gf_term_lock_net(term, 1); /*unregister from valid services*/ if (gf_list_del_item(term->net_services, service)>=0) { /*and queue for destroy*/ gf_list_add(term->net_services_to_remove, service); } gf_term_lock_net(term, 0); return; } /*this is channel disconnect*/ /*no notif in case of failure for disconnection*/ ch = gf_term_get_channel(service, netch); if (!ch) return; /*signal channel state*/ ch->es_state = GF_ESM_ES_DISCONNECTED;}static void term_on_slp_recieved(void *user_priv, GF_ClientService *service, LPNETCHANNEL netch, char *data, u32 data_size, GF_SLHeader *hdr, GF_Err reception_status){ GF_Channel *ch; GET_TERM(); ch = gf_term_get_channel(service, netch); if (!ch) return; if (reception_status==GF_EOS) { gf_es_on_eos(ch); return; } /*otherwise dispatch with error code*/ gf_es_receive_sl_packet(service, ch, data, data_size, hdr, reception_status);}static void term_on_media_add(void *user_priv, GF_ClientService *service, GF_Descriptor *media_desc, Bool no_scene_check){ GF_InlineScene *is; GF_ObjectManager *odm, *root; GF_ObjectDescriptor *od; GET_TERM(); root = service->owner; is = root->subscene ? root->subscene : root->parentscene; GF_LOG(GF_LOG_DEBUG, GF_LOG_SERVICE, ("[Service %s] %s\n", service->url, media_desc ? "Adding new media object" : "Regenerating scene graph")); if (!media_desc) { if (!no_scene_check) gf_is_regenerate(is); return; } switch (media_desc->tag) { case GF_ODF_OD_TAG: case GF_ODF_IOD_TAG: if (root && (root->net_service == service)) { od = (GF_ObjectDescriptor *) media_desc; break; } default: gf_odf_desc_del(media_desc); return; } gf_term_lock_net(term, 1); odm = gf_is_find_odm(is, od->objectDescriptorID); /*remove the old OD*/ if (odm) gf_odm_disconnect(odm, 1); odm = gf_odm_new(); odm->OD = od; odm->term = term; odm->parentscene = is; odm->flags |= GF_ODM_NOT_IN_OD_STREAM; gf_list_add(is->ODlist, odm); gf_term_lock_net(term, 0); gf_odm_setup_object(odm, service); /*OD inserted by service: resetup scene*/ if (!no_scene_check && is->is_dynamic_scene) gf_is_regenerate(is);}static void term_on_command(void *user_priv, GF_ClientService *service, GF_NetworkCommand *com, GF_Err response){ GF_Channel *ch; GET_TERM(); if (com->command_type==GF_NET_BUFFER_QUERY) { GF_List *od_list; u32 i; GF_ObjectManager *odm; com->buffer.max = 0; com->buffer.min = com->buffer.occupancy = (u32) -1; if (!service->owner) return; od_list = NULL; if (service->owner->parentscene) od_list = service->owner->parentscene->ODlist; else if (service->owner->subscene) od_list = service->owner->subscene->ODlist; if (!od_list) return; i=0; while ((odm = (GF_ObjectManager*)gf_list_enum(od_list, &i))) { u32 j, count; count = gf_list_count(odm->channels); for (j=0; j<count; j++) { GF_Channel *ch = (GF_Channel *)gf_list_get(odm->channels, j); if (ch->service != service) continue; if (ch->IsEndOfStream) continue; if (ch->clock->Buffering) continue; if (ch->es_state != GF_ESM_ES_RUNNING) continue; if (ch->MaxBuffer>com->buffer.max) com->buffer.max = ch->MaxBuffer; if (ch->MinBuffer<com->buffer.min) com->buffer.min = ch->MinBuffer; if ((ch->AU_Count > 2) && ((u32) ch->BufferTime<com->buffer.occupancy)) com->buffer.occupancy = ch->BufferTime; } } if (com->buffer.occupancy==(u32) -1) com->buffer.occupancy = 0; return; } if (com->command_type==GF_NET_SERVICE_INFO) { GF_Event evt; evt.type = GF_EVENT_METADATA; GF_USER_SENDEVENT(term->user, &evt); return; } if (!com->base.on_channel) return; ch = gf_term_get_channel(service, com->base.on_channel); if (!ch) return; switch (com->command_type) { /*SL reconfiguration*/ case GF_NET_CHAN_RECONFIG: gf_term_lock_net(term, 1); gf_es_reconfig_sl(ch, &com->cfg.sl_config); gf_term_lock_net(term, 0); return; /*time mapping (TS to media-time)*/ case GF_NET_CHAN_MAP_TIME: ch->seed_ts = com->map_time.timestamp; ch->ts_offset = (u32) (com->map_time.media_time*1000); /* if (gf_es_owns_clock(ch)) { ch->ts_offset = (u32) (com->map_time.media_time*1000); } else { ch->ts_offset = gf_clock_time(ch->clock); } */ gf_es_map_time(ch, com->map_time.reset_buffers); break; /*duration changed*/ case GF_NET_CHAN_DURATION: gf_odm_set_duration(ch->odm, ch, (u32) (1000*com->duration.duration)); break; case GF_NET_CHAN_BUFFER_QUERY: if (ch->IsEndOfStream) { com->buffer.max = com->buffer.min = com->buffer.occupancy = 0; } else { com->buffer.max = ch->MaxBuffer; com->buffer.min = ch->MinBuffer; com->buffer.occupancy = ch->BufferTime; } break; case GF_NET_CHAN_DRM_CFG: gf_term_lock_net(term, 1); gf_es_config_drm(ch, &com->drm_cfg); gf_term_lock_net(term, 0); return; case GF_NET_CHAN_GET_ESD: gf_term_lock_net(term, 1); com->cache_esd.esd = ch->esd; com->cache_esd.is_iod_stream = (ch->odm->subscene /*&& (ch->odm->subscene->root_od==ch->odm)*/) ? 1 : 0; gf_term_lock_net(term, 0); return; default: return; }}Bool net_check_interface(GF_InputService *ifce){ if (!ifce->CanHandleURL) return 0; if (!ifce->ConnectService) return 0; if (!ifce->CloseService) return 0; if (!ifce->ConnectChannel) return 0; if (!ifce->DisconnectChannel) return 0; if (!ifce->GetServiceDescriptor) return 0; if (!ifce->ServiceCommand) return 0; return 1;}static void fetch_mime_io(void *dnld, GF_NETIO_Parameter *parameter){ /*this is correct, however some shoutcast servers don't understand HEAD and don't reply at all so don't use HEAD*/#if 0 /*only get the content type*/ if (parameter->msg_type==GF_NETIO_GET_METHOD) parameter->name = "HEAD";#endif}static char *get_mime_type(GF_Terminal *term, const char *url, GF_Err *ret_code){ char *mime_type; GF_DownloadSession * sess; (*ret_code) = GF_OK; if (strnicmp(url, "http", 4)) return NULL; sess = gf_dm_sess_new(term->downloader, (char *) url, GF_NETIO_SESSION_NOT_THREADED, fetch_mime_io, NULL, ret_code); if (!sess) { if (strstr(url, "rtsp://") || strstr(url, "rtp://") || strstr(url, "udp://") || strstr(url, "tcp://") ) (*ret_code) = GF_OK; return NULL; } mime_type = (char *) gf_dm_sess_mime_type(sess); if (mime_type) mime_type = strdup(mime_type); else *ret_code = gf_dm_sess_last_error(sess); gf_dm_sess_del(sess); return mime_type;}static Bool check_extension(char *szExtList, char *szExt){ char szExt2[50]; if (szExtList[0] != '"') return 0; szExtList += 1; while (1) { u32 i = 0; while ((szExtList[0] != ' ') && (szExtList[0] != '"')) { szExt2[i] = szExtList[0]; i++; szExtList++; } szExt2[i] = 0; if (!strncmp(szExt, szExt2, strlen(szExt2))) return 1; if (szExtList[0]=='"') break; else szExtList++; } return 0;}static GF_InputService *gf_term_can_handle_service(GF_Terminal *term, const char *url, const char *parent_url, Bool no_mime_check, char **out_url, GF_Err *ret_code){ u32 i; GF_Err e; char *sURL, *ext, *mime_type; char szExt[50]; GF_InputService *ifce; GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Looking for plugin for URL %s\n", url)); *out_url = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -